feat: color the task color button when the task has a color set #2331

Merged
konrad merged 5 commits from feature/color-task-color-button into main 2022-09-15 12:46:13 +00:00
6 changed files with 96 additions and 28 deletions

View File

@ -56,10 +56,10 @@
class="menu-label"
v-tooltip="namespaceTitles[nk]"
>
<span
<ColorBubble
v-if="n.hexColor !== ''"
:style="{ backgroundColor: n.hexColor }"
class="color-bubble"
:color="n.hexColor"
class="mr-1"
konrad marked this conversation as resolved
Review

See comment in ColorBubble component

See comment in ColorBubble component
/>
<span class="name">{{ namespaceTitles[nk] }}</span>
<div
@ -114,11 +114,11 @@
<span class="icon handle">
<icon icon="grip-lines"/>
</span>
<span
:style="{ backgroundColor: l.hexColor }"
class="color-bubble"
v-if="l.hexColor !== ''">
</span>
<ColorBubble
v-if="l.hexColor !== ''"
:color="l.hexColor"
class="mr-1"
konrad marked this conversation as resolved
Review

See comment in ColorBubble component

See comment in ColorBubble component
/>
<span class="list-menu-title">{{ getListTitle(l) }}</span>
</BaseButton>
<BaseButton
@ -158,6 +158,7 @@ import {getListTitle} from '@/helpers/getListTitle'
import {useEventListener} from '@vueuse/core'
import type {IList} from '@/modelTypes/IList'
import type {INamespace} from '@/modelTypes/INamespace'
import ColorBubble from '@/components/misc/colorBubble.vue'
const drag = ref(false)
const dragOptions = {

View File

@ -9,9 +9,16 @@
}
]"
>
<icon :icon="icon" v-if="showIconOnly"/>
<icon
v-if="showIconOnly"
:icon="icon"
:style="{'color': iconColor !== '' ? iconColor : false}"
/>
<span class="icon is-small" v-else-if="icon !== ''">
<icon :icon="icon"/>
<icon
:icon="icon"
:style="{'color': iconColor !== '' ? iconColor : false}"
/>
</span>
<slot />
</BaseButton>
@ -42,6 +49,10 @@ const props = defineProps({
type: [String, Array],
default: '',
},
iconColor: {
Review

What I still don't like here is that it's possible to choose a color that doesn't have any contrast to the button.

While the color dot has the same problem we could fix it there by adding a slim border.
The icon on the other hand has such slim lines that the contrast can get bad very easy.

What I still don't like here is that it's possible to choose a color that doesn't have any contrast to the button. While the color dot has the same problem we could fix it there by adding a slim border. The icon on the other hand has such slim lines that the contrast can get bad very easy.
Review

So we rather shouldn't set the color for the icon?

So we rather shouldn't set the color for the icon?
Review

That's my personal opinion :)

That's my personal opinion :)
type: String,
default: '',
},
loading: {
type: Boolean,
default: false,

View File

@ -0,0 +1,24 @@
<template>
<span
:style="{backgroundColor: color }"
class="color-bubble"
></span>
</template>
<script lang="ts" setup>
import type { Color } from 'csstype'
defineProps< {
konrad marked this conversation as resolved Outdated

Picky: you could improve types here by using:

import type { Color } from "csstype"
Picky: you could improve types here by using: ``` import type { Color } from "csstype" ```

Nice one. Done.

Nice one. Done.
color: Color,
}>()
</script>
<style scoped>
.color-bubble {
display: inline-block;
border-radius: 100%;
height: 10px;
width: 10px;
dpschen marked this conversation as resolved
Review

If this margin should really be the same everywhere:

We should build this element so that the bubble has that distance included, meaning e.g. we would need to wrap two elements (the outer has an inner padding, the inner is the actual bubble).

If not:

The margin should be controlled from the outside.

If this margin should __really__ be the same everywhere: We should build this element so that the bubble has that distance included, meaning e.g. we would need to wrap two elements (the outer has an inner padding, the inner is the actual bubble). If not: The margin should be controlled from the outside.
Review

It's intended to not have any margin - the margin here is a leftover from copying the styles from the theme. I've added the margins everywhere where the ColorBubble component is included and the margins are needed.

It's intended to not have any margin - the margin here is a leftover from copying the styles from the theme. I've added the margins everywhere where the ColorBubble component is included and the margins are needed.
flex-shrink: 0;
}
</style>

View File

@ -1,7 +1,12 @@
<template>
<div class="heading">
<BaseButton @click="copyUrl"><h1 class="title task-id">{{ textIdentifier }}</h1></BaseButton>
<Done class="heading__done" :is-done="task.done" />
<Done class="heading__done" :is-done="task.done"/>
<ColorBubble
v-if="task.hexColor !== ''"
:color="task.getHexColor()"
class="mt-1 ml-2"
/>
<h1
class="title input"
:class="{'disabled': !canWrite}"
@ -42,6 +47,7 @@ import Done from '@/components/misc/Done.vue'
import {useCopyToClipboard} from '@/composables/useCopyToClipboard'
import type {ITask} from '@/modelTypes/ITask'
import ColorBubble from '@/components/misc/colorBubble.vue'
const props = defineProps({
task: {
@ -58,10 +64,11 @@ const emit = defineEmits(['update:task'])
const router = useRouter()
const copy = useCopyToClipboard()
async function copyUrl() {
const route = router.resolve({ name: 'task.detail', query: { taskId: props.task.id}})
const route = router.resolve({name: 'task.detail', query: {taskId: props.task.id}})
const absoluteURL = new URL(route.href, window.location.href).href
await copy(absoluteURL)
}
@ -95,8 +102,7 @@ async function save(title: string) {
setTimeout(() => {
showSavedMessage.value = false
}, 2000)
}
finally {
} finally {
saving.value = false
}
}
@ -106,4 +112,9 @@ async function save(title: string) {
.heading__done {
margin-left: .5rem;
}
.color-bubble {
height: .75rem;
width: .75rem;
}
</style>

View File

@ -1,12 +1,11 @@
<template>
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
<fancycheckbox :disabled="(isArchived || disabled) && !canMarkAsDone" @change="markAsDone" v-model="task.done"/>
<span
<ColorBubble
v-if="showListColor && listColor !== ''"
:style="{backgroundColor: listColor }"
class="color-bubble"
>
</span>
:color="listColor"
class="mr-1"
konrad marked this conversation as resolved
Review

See comment in ColorBubble component

See comment in ColorBubble component
/>
<router-link
:to="taskDetailRoute"
:class="{ 'done': task.done}"
@ -15,11 +14,17 @@
<router-link
:to="{ name: 'list.list', params: { listId: task.listId } }"
class="task-list"
:class="{'mr-2': task.hexColor !== ''}"
v-if="showList && $store.getters['lists/getListById'](task.listId) !== null"
v-tooltip="$t('task.detail.belongsToList', {list: $store.getters['lists/getListById'](task.listId).title})">
{{ $store.getters['lists/getListById'](task.listId).title }}
</router-link>
<ColorBubble
v-if="task.hexColor !== ''"
:color="task.getHexColor()"
class="mr-1"
konrad marked this conversation as resolved
Review

See comment in ColorBubble component

See comment in ColorBubble component
/>
<!-- Show any parent tasks to make it clear this task is a sub task of something -->
<span class="parent-tasks" v-if="typeof task.relatedTasks.parenttask !== 'undefined'">
<template v-for="(pt, i) in task.relatedTasks.parenttask">
@ -30,7 +35,7 @@
{{ task.title }}
</span>
<labels class="labels ml-2 mr-1" :labels="task.labels" v-if="task.labels.length > 0" />
<labels class="labels ml-2 mr-1" :labels="task.labels" v-if="task.labels.length > 0"/>
<user
:avatar-size="27"
:is-inline="true"
@ -111,6 +116,7 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {playPop} from '@/helpers/playPop'
import ChecklistSummary from './checklist-summary.vue'
import {formatDateSince, formatISO, formatDateLong} from '@/helpers/time/formatDate'
import ColorBubble from '@/components/misc/colorBubble.vue'
export default defineComponent({
name: 'singleTaskInList',
@ -122,6 +128,7 @@ export default defineComponent({
}
},
components: {
ColorBubble,
BaseButton,
ChecklistSummary,
DeferTask,
@ -282,11 +289,6 @@ export default defineComponent({
white-space: nowrap;
}
.color-bubble {
height: 10px;
flex: 0 0 10px;
}
.avatar {
border-radius: 50%;
vertical-align: bottom;

View File

@ -378,6 +378,7 @@
@click="setFieldActive('color')"
variant="secondary"
icon="fill-drip"
:icon-color="color"
v-shortcut="'c'"
>
{{ $t('task.detail.actions.color') }}
@ -429,7 +430,7 @@ import {defineComponent} from 'vue'
import cloneDeep from 'lodash.clonedeep'
import TaskService from '../../services/task'
import TaskModel from '@/models/task'
import TaskModel, {TASK_DEFAULT_COLOR} from '@/models/task'
import type {ITask} from '@/modelTypes/ITask'
import { PRIORITIES as priorites } from '@/constants/priorities'
@ -461,6 +462,7 @@ import { setTitle } from '@/helpers/setTitle'
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
import {getListTitle} from '@/helpers/getListTitle'
import type { IList } from '@/modelTypes/IList'
import {colorIsDark} from '@/helpers/color/colorIsDark'
function scrollIntoView(el) {
if (!el) {
@ -527,6 +529,8 @@ export default defineComponent({
showDeleteModal: false,
// Used to avoid flashing of empty elements if the task content is not yet loaded.
visible: false,
TASK_DEFAULT_COLOR,
activeFields: {
assignees: false,
@ -594,6 +598,15 @@ export default defineComponent({
shouldShowClosePopup() {
return this.$route.name.includes('kanban')
},
color() {
const color = this.task.getHexColor
? this.task.getHexColor()
: false
return color === TASK_DEFAULT_COLOR
? ''
: color
},
},
methods: {
getNamespaceTitle,
@ -745,6 +758,8 @@ export default defineComponent({
this.task = await this.taskService.update(this.task)
this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist')
},
colorIsDark,
},
})
</script>
@ -932,7 +947,11 @@ $flash-background-duration: 750ms;
.button {
width: 100%;
margin-bottom: .5rem;
justify-content: left;
justify-content: left;
&.has-light-text {
color: var(--white);
}
}
}