fix: use props destructuring everywhere

This commit is contained in:
kolaente 2023-06-20 14:39:59 +02:00
parent 78a268ab07
commit 3aa502e07d
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
17 changed files with 197 additions and 215 deletions

View File

@ -84,14 +84,17 @@ import ColorBubble from '@/components/misc/colorBubble.vue'
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue' import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
import {canNestProjectDeeper} from '@/helpers/canNestProjectDeeper' import {canNestProjectDeeper} from '@/helpers/canNestProjectDeeper'
const props = withDefaults(defineProps<{ const {
project,
isLoading,
canCollapse,
level = 0,
} = defineProps<{
project: IProject, project: IProject,
isLoading?: boolean, isLoading?: boolean,
canCollapse?: boolean, canCollapse?: boolean,
level?: number, level?: number,
}>(), { }>()
level: 0,
})
const projectStore = useProjectStore() const projectStore = useProjectStore()
const baseStore = useBaseStore() const baseStore = useBaseStore()
@ -104,12 +107,12 @@ const childProjects = computed(() => {
return [] return []
} }
return projectStore.getChildProjects(props.project.id) return projectStore.getChildProjects(project.id)
.filter(p => !p.isArchived) .filter(p => !p.isArchived)
.sort((a, b) => a.position - b.position) .sort((a, b) => a.position - b.position)
}) })
const canNestDeeper = computed(() => canNestProjectDeeper(props.level)) const canNestDeeper = computed(() => canNestProjectDeeper(level))
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -35,7 +35,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, toRef, watch} from 'vue' import {computed, ref, watch} from 'vue'
import {createRandomID} from '@/helpers/randomId' import {createRandomID} from '@/helpers/randomId'
import XButton from '@/components/input/button.vue' import XButton from '@/components/input/button.vue'
@ -53,22 +53,16 @@ const lastChangeTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
const defaultColors = ref(DEFAULT_COLORS) const defaultColors = ref(DEFAULT_COLORS)
const colorListID = ref(createRandomID()) const colorListID = ref(createRandomID())
const props = defineProps({ const {
modelValue: { modelValue,
type: String, } = defineProps<{
required: true, modelValue: string,
}, }>()
menuPosition: {
type: String,
default: 'top',
},
})
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const modelValue = toRef(props, 'modelValue')
watch( watch(
modelValue, () => modelValue,
(newValue) => { (newValue) => {
color.value = newValue color.value = newValue
}, },

View File

@ -75,17 +75,19 @@ import BaseButton from '@/components/base/BaseButton.vue'
import {ref, useAttrs, watchEffect} from 'vue' import {ref, useAttrs, watchEffect} from 'vue'
import {useScrollLock} from '@vueuse/core' import {useScrollLock} from '@vueuse/core'
const props = withDefaults(defineProps<{ const {
enabled = true,
overflow,
wide,
transitionName = 'modal',
variant = 'default',
} = defineProps<{
enabled?: boolean, enabled?: boolean,
overflow?: boolean, overflow?: boolean,
wide?: boolean, wide?: boolean,
transitionName?: 'modal' | 'fade', transitionName?: 'modal' | 'fade',
variant?: 'default' | 'hint-modal' | 'scrolling', variant?: 'default' | 'hint-modal' | 'scrolling',
}>(), { }>()
enabled: true,
transitionName: 'modal',
variant: 'default',
})
defineEmits(['close', 'submit']) defineEmits(['close', 'submit'])
@ -95,7 +97,7 @@ const modal = ref<HTMLElement | null>(null)
const scrollLock = useScrollLock(modal) const scrollLock = useScrollLock(modal)
watchEffect(() => { watchEffect(() => {
scrollLock.value = props.enabled scrollLock.value = enabled
}) })
</script> </script>
@ -200,10 +202,10 @@ $modal-width: 1024px;
right: $close-button-padding; right: $close-button-padding;
color: var(--grey-900); color: var(--grey-900);
font-size: 2rem; font-size: 2rem;
@media screen and (min-width: $desktop) and (max-width: calc(#{$desktop } + #{$close-button-min-space})) { @media screen and (min-width: $desktop) and (max-width: calc(#{$desktop } + #{$close-button-min-space})) {
top: calc(5px + $modal-margin); top: calc(5px + $modal-margin);
right: 50%; right: 50%;
// we align the close button to the modal until there is enough space outside for it // we align the close button to the modal until there is enough space outside for it
transform: translateX(calc((#{$modal-width} / 2) - #{$close-button-padding})); transform: translateX(calc((#{$modal-width} / 2) - #{$close-button-padding}));
} }

View File

@ -44,8 +44,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {toRef, type PropType} from 'vue'
import type {IProject} from '@/modelTypes/IProject' import type {IProject} from '@/modelTypes/IProject'
import BaseButton from '@/components/base/BaseButton.vue' import BaseButton from '@/components/base/BaseButton.vue'
@ -53,14 +51,13 @@ import BaseButton from '@/components/base/BaseButton.vue'
import {useProjectBackground} from './useProjectBackground' import {useProjectBackground} from './useProjectBackground'
import {useProjectStore} from '@/stores/projects' import {useProjectStore} from '@/stores/projects'
const props = defineProps({ const {
project: { project,
type: Object as PropType<IProject>, } = defineProps<{
required: true, project: IProject,
}, }>()
})
const {background, blurHashUrl} = useProjectBackground(toRef(props, 'project')) const {background, blurHashUrl} = useProjectBackground(project)
const projectStore = useProjectStore() const projectStore = useProjectStore()
</script> </script>

View File

@ -180,13 +180,13 @@ import {useI18n} from 'vue-i18n'
const taskStore = useTaskStore() const taskStore = useTaskStore()
const {t} = useI18n({useScope: 'global'}) const {t} = useI18n({useScope: 'global'})
const props = withDefaults(defineProps<{ const {
task,
editEnabled = true,
} = defineProps<{
task: ITask, task: ITask,
initialAttachments?: IAttachment[],
editEnabled: boolean, editEnabled: boolean,
}>(), { }>()
editEnabled: true,
})
// FIXME: this should go through the store // FIXME: this should go through the store
const emit = defineEmits(['task-changed']) const emit = defineEmits(['task-changed'])
@ -223,7 +223,7 @@ function uploadNewAttachment() {
} }
function uploadFilesToTask(files: File[] | FileList) { function uploadFilesToTask(files: File[] | FileList) {
uploadFiles(attachmentService, props.task.id, files) uploadFiles(attachmentService, task.id, files)
} }
const attachmentToDelete = ref<IAttachment | null>(null) const attachmentToDelete = ref<IAttachment | null>(null)
@ -260,11 +260,11 @@ async function viewOrDownload(attachment: IAttachment) {
const copy = useCopyToClipboard() const copy = useCopyToClipboard()
function copyUrl(attachment: IAttachment) { function copyUrl(attachment: IAttachment) {
copy(generateAttachmentUrl(props.task.id, attachment.id)) copy(generateAttachmentUrl(task.id, attachment.id))
} }
async function setCoverImage(attachment: IAttachment | null) { async function setCoverImage(attachment: IAttachment | null) {
const task = await taskStore.setCoverImage(props.task, attachment) const task = await taskStore.setCoverImage(task, attachment)
emit('task-changed', task) emit('task-changed', task)
success({message: t('task.attachment.successfullyChangedCoverImage')}) success({message: t('task.attachment.successfullyChangedCoverImage')})
} }

View File

@ -38,7 +38,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref, shallowReactive, computed, watch, onMounted, onBeforeUnmount, toRef, type PropType} from 'vue' import {ref, shallowReactive, computed, watch, onMounted, onBeforeUnmount} from 'vue'
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
import flatPickr from 'vue-flatpickr-component' import flatPickr from 'vue-flatpickr-component'
@ -46,12 +46,12 @@ import TaskService from '@/services/task'
import type {ITask} from '@/modelTypes/ITask' import type {ITask} from '@/modelTypes/ITask'
import {useAuthStore} from '@/stores/auth' import {useAuthStore} from '@/stores/auth'
const props = defineProps({ const {
modelValue: { modelValue,
type: Object as PropType<ITask>, } = defineProps<{
required: true, modelValue: ITask,
}, }>()
})
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const {t} = useI18n({useScope: 'global'}) const {t} = useI18n({useScope: 'global'})
@ -66,7 +66,7 @@ const lastValue = ref<Date | null>()
const changeInterval = ref<ReturnType<typeof setInterval>>() const changeInterval = ref<ReturnType<typeof setInterval>>()
watch( watch(
toRef(props, 'modelValue'), () => modelValue,
(value) => { (value) => {
task.value = { ...value } task.value = { ...value }
dueDate.value = value.dueDate dueDate.value = value.dueDate

View File

@ -96,14 +96,15 @@ const router = useRouter()
const loadingInternal = ref(false) const loadingInternal = ref(false)
const props = withDefaults(defineProps<{ const {
task,
loading = false,
} = defineProps<{
task: ITask, task: ITask,
loading: boolean, loading: boolean,
}>(), { }>()
loading: false,
})
const color = computed(() => getHexColor(props.task.hexColor)) const color = computed(() => getHexColor(task.hexColor))
async function toggleTaskDone(task: ITask) { async function toggleTaskDone(task: ITask) {
loadingInternal.value = true loadingInternal.value = true
@ -120,7 +121,7 @@ async function toggleTaskDone(task: ITask) {
function openTaskDetail() { function openTaskDetail() {
router.push({ router.push({
name: 'task.detail', name: 'task.detail',
params: {id: props.task.id}, params: {id: task.id},
state: {backdropView: router.currentRoute.value.fullPath}, state: {backdropView: router.currentRoute.value.fullPath},
}) })
} }
@ -128,12 +129,12 @@ function openTaskDetail() {
const coverImageBlobUrl = ref<string | null>(null) const coverImageBlobUrl = ref<string | null>(null)
async function maybeDownloadCoverImage() { async function maybeDownloadCoverImage() {
if (!props.task.coverImageAttachmentId) { if (!task.coverImageAttachmentId) {
coverImageBlobUrl.value = null coverImageBlobUrl.value = null
return return
} }
const attachment = props.task.attachments.find(a => a.id === props.task.coverImageAttachmentId) const attachment = task.attachments.find(a => a.id === task.coverImageAttachmentId)
if (!attachment || !SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.endsWith(suffix))) { if (!attachment || !SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.endsWith(suffix))) {
return return
} }
@ -143,7 +144,7 @@ async function maybeDownloadCoverImage() {
} }
watch( watch(
() => props.task.coverImageAttachmentId, () => task.coverImageAttachmentId,
maybeDownloadCoverImage, maybeDownloadCoverImage,
{immediate: true}, {immediate: true},
) )

View File

@ -65,7 +65,6 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, watch} from 'vue' import {computed, ref, watch} from 'vue'
import {toRef} from '@vueuse/core'
import {SECONDS_A_DAY, SECONDS_A_HOUR} from '@/constants/date' import {SECONDS_A_DAY, SECONDS_A_HOUR} from '@/constants/date'
import {IReminderPeriodRelativeTo, REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo' import {IReminderPeriodRelativeTo, REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo'
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
@ -84,28 +83,27 @@ import SimpleButton from '@/components/input/SimpleButton.vue'
const {t} = useI18n({useScope: 'global'}) const {t} = useI18n({useScope: 'global'})
const props = withDefaults(defineProps<{ const {
modelValue,
clearAfterUpdate = false,
defaultRelativeTo = REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE,
} = defineProps<{
modelValue?: ITaskReminder, modelValue?: ITaskReminder,
disabled?: boolean,
clearAfterUpdate?: boolean, clearAfterUpdate?: boolean,
defaultRelativeTo?: null | IReminderPeriodRelativeTo, defaultRelativeTo?: null | IReminderPeriodRelativeTo,
}>(), { }>()
disabled: false,
clearAfterUpdate: false,
defaultRelativeTo: REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE,
})
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const reminder = ref<ITaskReminder>(new TaskReminderModel()) const reminder = ref<ITaskReminder>(new TaskReminderModel())
const presets = computed<TaskReminderModel[]>(() => [ const presets = computed<TaskReminderModel[]>(() => [
{reminder: null, relativePeriod: 0, relativeTo: props.defaultRelativeTo}, {reminder: null, relativePeriod: 0, relativeTo: defaultRelativeTo},
{reminder: null, relativePeriod: -2 * SECONDS_A_HOUR, relativeTo: props.defaultRelativeTo}, {reminder: null, relativePeriod: -2 * SECONDS_A_HOUR, relativeTo: defaultRelativeTo},
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY, relativeTo: props.defaultRelativeTo}, {reminder: null, relativePeriod: -1 * SECONDS_A_DAY, relativeTo: defaultRelativeTo},
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 3, relativeTo: props.defaultRelativeTo}, {reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 3, relativeTo: defaultRelativeTo},
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 7, relativeTo: props.defaultRelativeTo}, {reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 7, relativeTo: defaultRelativeTo},
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 30, relativeTo: props.defaultRelativeTo}, {reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 30, relativeTo: defaultRelativeTo},
]) ])
const reminderDate = ref(null) const reminderDate = ref(null)
@ -114,7 +112,7 @@ type availableForms = null | 'relative' | 'absolute'
const showFormSwitch = ref<availableForms>(null) const showFormSwitch = ref<availableForms>(null)
const activeForm = computed<availableForms>(() => { const activeForm = computed<availableForms>(() => {
if (props.defaultRelativeTo === null) { if (defaultRelativeTo === null) {
return 'absolute' return 'absolute'
} }
@ -134,9 +132,8 @@ const reminderText = computed(() => {
return t('task.addReminder') return t('task.addReminder')
}) })
const modelValue = toRef(props, 'modelValue')
watch( watch(
modelValue, () => modelValue,
(newReminder) => { (newReminder) => {
reminder.value = newReminder || new TaskReminderModel() reminder.value = newReminder || new TaskReminderModel()
}, },
@ -146,7 +143,7 @@ watch(
function updateData() { function updateData() {
emit('update:modelValue', reminder.value) emit('update:modelValue', reminder.value)
if (props.clearAfterUpdate) { if (clearAfterUpdate) {
reminder.value = new TaskReminderModel() reminder.value = new TaskReminderModel()
} }
} }
@ -168,7 +165,7 @@ function setReminderFromPreset(preset, toggle) {
function updateDataAndMaybeClose(toggle) { function updateDataAndMaybeClose(toggle) {
updateData() updateData()
if (props.clearAfterUpdate) { if (clearAfterUpdate) {
toggle() toggle()
} }
} }

View File

@ -47,8 +47,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref, watch, type PropType} from 'vue' import {ref, watch} from 'vue'
import {toRef} from '@vueuse/core'
import {periodToSeconds, PeriodUnit, secondsToPeriod} from '@/helpers/time/period' import {periodToSeconds, PeriodUnit, secondsToPeriod} from '@/helpers/time/period'
@ -57,16 +56,11 @@ import TaskReminderModel from '@/models/taskReminder'
import type {ITaskReminder} from '@/modelTypes/ITaskReminder' import type {ITaskReminder} from '@/modelTypes/ITaskReminder'
import {REMINDER_PERIOD_RELATIVE_TO_TYPES, type IReminderPeriodRelativeTo} from '@/types/IReminderPeriodRelativeTo' import {REMINDER_PERIOD_RELATIVE_TO_TYPES, type IReminderPeriodRelativeTo} from '@/types/IReminderPeriodRelativeTo'
const props = defineProps({ const {
modelValue: { modelValue,
type: Object as PropType<ITaskReminder>, } = defineProps<{
required: false, modelValue?: ITaskReminder,
}, }>()
disabled: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
@ -86,9 +80,8 @@ const period = ref<PeriodInput>({
sign: -1, sign: -1,
}) })
const modelValue = toRef(props, 'modelValue')
watch( watch(
modelValue, () => modelValue,
(value) => { (value) => {
const p = secondsToPeriod(value?.relativePeriod) const p = secondsToPeriod(value?.relativePeriod)
period.value.durationUnit = p.unit period.value.durationUnit = p.unit

View File

@ -41,20 +41,20 @@ import ReminderDetail from '@/components/tasks/partials/reminder-detail.vue'
import type {ITask} from '@/modelTypes/ITask' import type {ITask} from '@/modelTypes/ITask'
import {REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo' import {REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo'
const props = withDefaults(defineProps<{ const {
modelValue,
disabled = false,
} = defineProps<{
modelValue: ITask, modelValue: ITask,
disabled?: boolean, disabled?: boolean,
}>(), { }>()
modelValue: [],
disabled: false,
})
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const reminders = ref<ITaskReminder[]>([]) const reminders = ref<ITaskReminder[]>([])
watch( watch(
() => props.modelValue.reminders, () => modelValue.reminders,
(newVal) => { (newVal) => {
reminders.value = newVal reminders.value = newVal
}, },
@ -62,19 +62,19 @@ watch(
) )
const defaultRelativeTo = computed(() => { const defaultRelativeTo = computed(() => {
if (typeof props.modelValue === 'undefined') { if (typeof modelValue === 'undefined') {
return null return null
} }
if (props.modelValue?.dueDate) { if (modelValue?.dueDate) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE return REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE
} }
if (props.modelValue.dueDate === null && props.modelValue.startDate !== null) { if (modelValue.dueDate === null && modelValue.startDate !== null) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE return REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE
} }
if (props.modelValue.dueDate === null && props.modelValue.startDate === null && props.modelValue.endDate !== null) { if (modelValue.dueDate === null && modelValue.startDate === null && modelValue.endDate !== null) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE return REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE
} }
@ -83,7 +83,7 @@ const defaultRelativeTo = computed(() => {
function updateData() { function updateData() {
emit('update:modelValue', { emit('update:modelValue', {
...props.modelValue, ...modelValue,
reminders: reminders.value, reminders: reminders.value,
}) })
} }

View File

@ -126,7 +126,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref, watch, shallowReactive, toRef, type PropType, onMounted, onBeforeUnmount, computed} from 'vue' import {ref, watch, shallowReactive, onMounted, onBeforeUnmount, computed} from 'vue'
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
import TaskModel, { getHexColor } from '@/models/task' import TaskModel, { getHexColor } from '@/models/task'
@ -153,32 +153,21 @@ import {useProjectStore} from '@/stores/projects'
import {useBaseStore} from '@/stores/base' import {useBaseStore} from '@/stores/base'
import {useTaskStore} from '@/stores/tasks' import {useTaskStore} from '@/stores/tasks'
const props = defineProps({ const {
theTask: { theTask,
type: Object as PropType<ITask>, isArchived = false,
required: true, showProject = false,
}, disabled = false,
isArchived: { showProjectColor = false,
type: Boolean, canMarkAsDone = true,
default: false, } = defineProps<{
}, theTask: ITask,
showProject: { isArchived?: boolean,
type: Boolean, showProject?: boolean,
default: false, disabled?: boolean,
}, showProjectColor?: boolean,
disabled: { canMarkAsDone?: boolean,
type: Boolean, }>()
default: false,
},
showProjectColor: {
type: Boolean,
default: true,
},
canMarkAsDone: {
type: Boolean,
default: true,
},
})
const emit = defineEmits(['task-updated']) const emit = defineEmits(['task-updated'])
@ -188,10 +177,8 @@ const taskService = shallowReactive(new TaskService())
const task = ref<ITask>(new TaskModel()) const task = ref<ITask>(new TaskModel())
const showDefer = ref(false) const showDefer = ref(false)
const theTask = toRef(props, 'theTask')
watch( watch(
theTask, () => theTask,
newVal => { newVal => {
task.value = newVal task.value = newVal
}, },

View File

@ -6,7 +6,7 @@
<template #header> <template #header>
<span>{{ $t('filters.delete.header') }}</span> <span>{{ $t('filters.delete.header') }}</span>
</template> </template>
<template #text> <template #text>
<p>{{ $t('filters.delete.text') }}</p> <p>{{ $t('filters.delete.text') }}</p>
</template> </template>
@ -14,11 +14,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {toRef} from 'vue'
import type {IProject} from '@/modelTypes/IProject' import type {IProject} from '@/modelTypes/IProject'
import {useSavedFilter} from '@/services/savedFilter' import {useSavedFilter} from '@/services/savedFilter'
const props = defineProps<{ projectId: IProject['id'] }>() const {
projectId,
} = defineProps<{
projectId: IProject['id'],
}>()
const {deleteFilter} = useSavedFilter(toRef(props, 'projectId')) const {deleteFilter} = useSavedFilter(projectId)
</script> </script>

View File

@ -54,8 +54,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {toRef} from 'vue'
import Editor from '@/components/input/AsyncEditor' import Editor from '@/components/input/AsyncEditor'
import CreateEdit from '@/components/misc/create-edit.vue' import CreateEdit from '@/components/misc/create-edit.vue'
import Filters from '@/components/project/partials/filters.vue' import Filters from '@/components/project/partials/filters.vue'
@ -64,7 +62,11 @@ import {useSavedFilter} from '@/services/savedFilter'
import type {IProject} from '@/modelTypes/IProject' import type {IProject} from '@/modelTypes/IProject'
const props = defineProps<{ projectId: IProject['id'] }>() const {
projectId,
} = defineProps<{
projectId: IProject['id'],
}>()
const { const {
saveFilterWithValidation, saveFilterWithValidation,
@ -73,5 +75,5 @@ const {
filterService, filterService,
titleValid, titleValid,
validateTitleField, validateTitleField,
} = useSavedFilter(toRef(props, 'projectId')) } = useSavedFilter(projectId)
</script> </script>

View File

@ -120,7 +120,7 @@ export default { name: 'List' }
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import {ref, computed, toRef, nextTick, onMounted, type PropType} from 'vue' import {ref, computed, nextTick, onMounted} from 'vue'
import draggable from 'zhyswan-vuedraggable' import draggable from 'zhyswan-vuedraggable'
import {useRoute, useRouter} from 'vue-router' import {useRoute, useRouter} from 'vue-router'
@ -144,12 +144,11 @@ import {useTaskStore} from '@/stores/tasks'
import type {IProject} from '@/modelTypes/IProject' import type {IProject} from '@/modelTypes/IProject'
const props = defineProps({ const {
projectId: { projectId,
type: Number as PropType<IProject['id']>, } = defineProps<{
required: true, projectId: IProject['id'],
}, }>()
})
const ctaVisible = ref(false) const ctaVisible = ref(false)
const showTaskSearch = ref(false) const showTaskSearch = ref(false)
@ -169,7 +168,7 @@ const {
searchTerm, searchTerm,
params, params,
sortByParam, sortByParam,
} = useTaskList(toRef(props, 'projectId'), {position: 'asc' }) } = useTaskList(projectId, {position: 'asc' })
const isAlphabeticalSorting = computed(() => { const isAlphabeticalSorting = computed(() => {

View File

@ -180,7 +180,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {toRef, computed, type Ref} from 'vue' import {computed, type Ref} from 'vue'
import {useStorage} from '@vueuse/core' import {useStorage} from '@vueuse/core'
@ -200,6 +200,7 @@ import {useTaskList} from '@/composables/useTaskList'
import type {SortBy} from '@/composables/useTaskList' import type {SortBy} from '@/composables/useTaskList'
import type {ITask} from '@/modelTypes/ITask' import type {ITask} from '@/modelTypes/ITask'
import type {IProject} from '@/modelTypes/IProject'
const ACTIVE_COLUMNS_DEFAULT = { const ACTIVE_COLUMNS_DEFAULT = {
index: true, index: true,
@ -217,12 +218,11 @@ const ACTIVE_COLUMNS_DEFAULT = {
createdBy: false, createdBy: false,
} }
const props = defineProps({ const {
projectId: { projectId,
type: Number, } = defineProps<{
required: true, projectId: IProject['id'],
}, }>()
})
const SORT_BY_DEFAULT: SortBy = { const SORT_BY_DEFAULT: SortBy = {
index: 'desc', index: 'desc',
@ -231,7 +231,7 @@ const SORT_BY_DEFAULT: SortBy = {
const activeColumns = useStorage('tableViewColumns', {...ACTIVE_COLUMNS_DEFAULT}) const activeColumns = useStorage('tableViewColumns', {...ACTIVE_COLUMNS_DEFAULT})
const sortBy = useStorage<SortBy>('tableViewSortBy', {...SORT_BY_DEFAULT}) const sortBy = useStorage<SortBy>('tableViewSortBy', {...SORT_BY_DEFAULT})
const taskList = useTaskList(toRef(props, 'projectId'), sortBy.value) const taskList = useTaskList(projectId, sortBy.value)
const { const {
loading, loading,

View File

@ -216,7 +216,7 @@
{{ $t('task.attributes.labels') }} {{ $t('task.attributes.labels') }}
</div> </div>
<edit-labels <edit-labels
:disabled="!canWrite" :disabled="!canWrite"
:task-id="taskId" :task-id="taskId"
:ref="e => setFieldRef('labels', e)" :ref="e => setFieldRef('labels', e)"
v-model="task.labels"/> v-model="task.labels"/>
@ -270,7 +270,7 @@
</h3> </h3>
<div class="field has-addons"> <div class="field has-addons">
<div class="control is-expanded"> <div class="control is-expanded">
<project-search <project-search
@update:modelValue="changeProject" @update:modelValue="changeProject"
:ref="e => setFieldRef('moveProject', e)" :ref="e => setFieldRef('moveProject', e)"
/> />
@ -447,7 +447,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {ref, reactive, toRef, shallowReactive, computed, watch, watchEffect, nextTick, type PropType} from 'vue' import {ref, reactive, toRef, shallowReactive, computed, watch, watchEffect, nextTick} from 'vue'
import {useRouter, type RouteLocation} from 'vue-router' import {useRouter, type RouteLocation} from 'vue-router'
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
import {unrefElement} from '@vueuse/core' import {unrefElement} from '@vueuse/core'
@ -500,15 +500,13 @@ import type {Action as MessageAction} from '@/message'
import {useProjectStore} from '@/stores/projects' import {useProjectStore} from '@/stores/projects'
import {TASK_REPEAT_MODES} from '@/types/IRepeatMode' import {TASK_REPEAT_MODES} from '@/types/IRepeatMode'
const props = defineProps({ const {
taskId: { taskId,
type: Number as PropType<ITask['id']>, backdropView,
required: true, } = defineProps<{
}, taskId: ITask['id'],
backdropView: { backdropView?: RouteLocation['fullPath'],
type: String as PropType<RouteLocation['fullPath']>, }>()
},
})
defineEmits(['close']) defineEmits(['close'])
@ -535,8 +533,6 @@ const taskColor = ref<ITask['hexColor']>('')
// Used to avoid flashing of empty elements if the task content is not yet loaded. // Used to avoid flashing of empty elements if the task content is not yet loaded.
const visible = ref(false) const visible = ref(false)
const taskId = toRef(props, 'taskId')
const project = computed(() => projectStore.projects[task.value.projectId]) const project = computed(() => projectStore.projects[task.value.projectId])
watchEffect(() => { watchEffect(() => {
if (typeof project.value === 'undefined') { if (typeof project.value === 'undefined') {
@ -559,7 +555,7 @@ const color = computed(() => {
const color = task.value.getHexColor const color = task.value.getHexColor
? task.value.getHexColor() ? task.value.getHexColor()
: undefined : undefined
return color === TASK_DEFAULT_COLOR return color === TASK_DEFAULT_COLOR
? '' ? ''
: color : color
@ -567,13 +563,14 @@ const color = computed(() => {
const hasAttachments = computed(() => attachmentStore.attachments.length > 0) const hasAttachments = computed(() => attachmentStore.attachments.length > 0)
const isModal = computed(() => Boolean(props.backdropView)) const isModal = computed(() => Boolean(backdropView))
function attachmentUpload(file: File, onSuccess?: (url: string) => void) { function attachmentUpload(file: File, onSuccess?: (url: string) => void) {
return uploadFile(taskId.value, file, onSuccess) return uploadFile(taskId, file, onSuccess)
} }
const heading = ref<HTMLElement | null>(null) const heading = ref<HTMLElement | null>(null)
async function scrollToHeading() { async function scrollToHeading() {
scrollIntoView(unrefElement(heading)) scrollIntoView(unrefElement(heading))
} }
@ -581,22 +578,24 @@ async function scrollToHeading() {
const taskService = shallowReactive(new TaskService()) const taskService = shallowReactive(new TaskService())
// load task // load task
watch(taskId, async (id) => { watch(
if (id === undefined) { () => taskId,
return async (id) => {
} if (id === undefined) {
return
}
try { try {
Object.assign(task.value, await taskService.get({id})) Object.assign(task.value, await taskService.get({id}))
attachmentStore.set(task.value.attachments) attachmentStore.set(task.value.attachments)
taskColor.value = task.value.hexColor taskColor.value = task.value.hexColor
setActiveFields() setActiveFields()
} finally { } finally {
await nextTick() await nextTick()
scrollToHeading() scrollToHeading()
visible.value = true visible.value = true
} }
}, {immediate: true}) }, {immediate: true})
type FieldType = type FieldType =
| 'assignees' | 'assignees'
@ -613,7 +612,7 @@ type FieldType =
| 'repeatAfter' | 'repeatAfter'
| 'startDate' | 'startDate'
const activeFields : {[type in FieldType]: boolean} = reactive({ const activeFields: { [type in FieldType]: boolean } = reactive({
assignees: false, assignees: false,
attachments: false, attachments: false,
color: false, color: false,
@ -648,7 +647,7 @@ function setActiveFields() {
activeFields.startDate = task.value.startDate !== null activeFields.startDate = task.value.startDate !== null
} }
const activeFieldElements : {[id in FieldType]: HTMLElement | null} = reactive({ const activeFieldElements: { [id in FieldType]: HTMLElement | null } = reactive({
assignees: null, assignees: null,
attachments: null, attachments: null,
color: null, color: null,
@ -676,7 +675,7 @@ function setFieldActive(fieldName: keyof typeof activeFields) {
if (!el) { if (!el) {
return return
} }
el.focus() el.focus()
// scroll the field to the center of the screen if not in viewport already // scroll the field to the center of the screen if not in viewport already
@ -691,7 +690,7 @@ async function saveTask(
if (currentTask === null) { if (currentTask === null) {
currentTask = klona(task.value) currentTask = klona(task.value)
} }
if (!canWrite.value) { if (!canWrite.value) {
return return
} }
@ -723,6 +722,7 @@ async function saveTask(
} }
const showDeleteModal = ref(false) const showDeleteModal = ref(false)
async function deleteTask() { async function deleteTask() {
await taskStore.delete(task.value) await taskStore.delete(task.value)
success({message: t('task.detail.deleteSuccess')}) success({message: t('task.detail.deleteSuccess')})
@ -784,32 +784,32 @@ async function removeRepeatAfter() {
// simulate sass lighten($primary, 30) by increasing lightness 30% to 73% // simulate sass lighten($primary, 30) by increasing lightness 30% to 73%
--primary-light: hsla(var(--primary-h), var(--primary-s), 73%, var(--primary-a)); --primary-light: hsla(var(--primary-h), var(--primary-s), 73%, var(--primary-a));
padding-bottom: 0; padding-bottom: 0;
@media screen and (min-width: $desktop) { @media screen and (min-width: $desktop) {
padding-bottom: 1rem; padding-bottom: 1rem;
} }
} }
.task-view { .task-view {
padding-top: 1rem; padding-top: 1rem;
padding-inline: .5rem; padding-inline: .5rem;
background-color: var(--site-background); background-color: var(--site-background);
@media screen and (min-width: $desktop) { @media screen and (min-width: $desktop) {
padding: 1rem; padding: 1rem;
} }
} }
.is-modal .task-view { .is-modal .task-view {
border-radius: $radius; border-radius: $radius;
padding: 1rem; padding: 1rem;
color: var(--text); color: var(--text);
background-color: var(--site-background) !important; background-color: var(--site-background) !important;
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
border-radius: 0; border-radius: 0;
padding-top: 2rem; padding-top: 2rem;
} }
} }
.task-view * { .task-view * {
@ -837,6 +837,7 @@ h3 .button {
.icon.is-grey { .icon.is-grey {
color: var(--grey-400); color: var(--grey-400);
} }
.date-input { .date-input {
display: flex; display: flex;
align-items: center; align-items: center;
@ -960,11 +961,11 @@ h3 .button {
top: $navbar-height + 1.5rem; top: $navbar-height + 1.5rem;
align-self: flex-start; align-self: flex-start;
} }
.button { .button {
width: 100%; width: 100%;
margin-bottom: .5rem; margin-bottom: .5rem;
justify-content: left; justify-content: left;
&.has-light-text { &.has-light-text {
color: var(--white); color: var(--white);
@ -989,8 +990,8 @@ h3 .button {
} }
.detail-content { .detail-content {
@media print { @media print {
width: 100% !important; width: 100% !important;
} }
} }
</style> </style>

View File

@ -121,6 +121,9 @@ export default defineConfig(({mode}) => {
plugins: [ plugins: [
vue({ vue({
reactivityTransform: true, reactivityTransform: true,
script: {
propsDestructure: true,
},
}), }),
legacy, legacy,
svgLoader({ svgLoader({