wip: base review

(cherry picked from commit 3bb64f078cf333f174d247dc404355f2c8437cfd)
This commit is contained in:
Dominik Pschenitschni 2023-03-30 12:40:19 +02:00 committed by kolaente
parent 85ffed4d9a
commit 811254e6a9
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 167 additions and 134 deletions

View File

@ -1,22 +1,29 @@
<template> <template>
<div> <div>
<ReminderPeriod v-if="showRelativeReminder" v-model="reminder" :disabled="disabled" <ReminderPeriod
@update:modelValue="() => updateData()"></ReminderPeriod> v-if="showRelativeReminder"
v-model="reminder"
:disabled="disabled"
@update:modelValue="emit('update:modelValue', reminder.value)"
/>
<Datepicker <Datepicker
v-if="showAbsoluteReminder" v-if="showAbsoluteReminder"
v-model="reminderDate" v-model="reminderDate"
:disabled="disabled" :disabled="disabled"
@close-on-change="() => setReminderDate()" @close-on-change="setReminderDate"
/> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, watch, type PropType} from 'vue'
import type {ITaskReminder} from '@/modelTypes/ITaskReminder'
import Datepicker from '@/components/input/datepicker.vue' import Datepicker from '@/components/input/datepicker.vue'
import ReminderPeriod from '@/components/tasks/partials/reminder-period.vue' import ReminderPeriod from '@/components/tasks/partials/reminder-period.vue'
import TaskReminderModel from '@/models/taskReminder' import TaskReminderModel from '@/models/taskReminder'
import type {ITaskReminder} from '@/modelTypes/ITaskReminder'
import {computed, ref, watch, type PropType} from 'vue'
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -30,35 +37,35 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const reminder = ref<ITaskReminder>() const reminder = ref<ITaskReminder>(new TaskReminderModel())
const reminderDate = ref() const reminderDate = computed({
get() {
return reminder.value?.reminder
},
set(newReminderDate) {
if (!reminderDate.value) {
return
}
reminder.value.reminder = new Date(reminderDate.value)
}
})
const showAbsoluteReminder = computed(() => !reminder.value || !reminder.value?.relativeTo) const showAbsoluteReminder = computed(() => !reminder.value || !reminder.value?.relativeTo)
const showRelativeReminder = computed(() => !reminder.value || reminder.value?.relativeTo) const showRelativeReminder = computed(() => !reminder.value || reminder.value?.relativeTo)
watch( watch(
() => props.modelValue, props.modelValue,
(value) => { (newReminder) => {
reminder.value = value reminder.value = newReminder || new TaskReminderModel()
if (reminder.value && reminder.value.reminder) {
reminderDate.value = new Date(reminder.value.reminder)
}
}, },
{immediate: true}, {immediate: true},
) )
function updateData() {
emit('update:modelValue', reminder.value)
}
function setReminderDate() { function setReminderDate() {
if (!reminderDate.value) { if (!reminderDate.value) {
return return
} }
if (!reminder.value) {
reminder.value = new TaskReminderModel()
}
reminder.value.reminder = new Date(reminderDate.value) reminder.value.reminder = new Date(reminderDate.value)
updateData() emit('update:modelValue', reminder.value)
} }
</script> </script>

View File

@ -1,78 +1,96 @@
<template> <template>
<div class="datepicker"> <div
<BaseButton :disabled="disabled" class="show" v-if="!!reminder?.relativeTo" @click.stop="togglePeriodPopup"> v-if="!!reminder?.relativeTo"
{{ formatDuration(reminder.relativePeriod) }} {{ reminder.relativePeriod <= 0 ? '&le;' : '&gt;' }} class="reminder-period"
{{ formatRelativeTo(reminder.relativeTo) }} >
</BaseButton> <Popup>
<CustomTransition name="fade"> <template #trigger="{toggle}">
<div v-if="isShowForm" class="mt-2" ref="periodPopup"> <BaseButton
<div class="control is-flex is-align-items-center"> @click="toggle"
<input :disabled="disabled"
:disabled="disabled" class="show"
class="input" >
:placeholder="$t('task.reminder.daysShort')" {{ formatDuration(reminder.relativePeriod) }} {{ reminder.relativePeriod <= 0 ? '&le;' : '&gt;' }}
v-model="periodInput.duration.days" {{ formatRelativeTo(reminder.relativeTo) }}
type="number" <span class="icon"><icon icon="chevron-down"/></span>
min="0" </BaseButton>
/> {{ $t('task.reminder.days') }} </template>
<input
<template #content>
<div class="mt-2">
<div class="control is-flex is-align-items-center">
<label>
<input
:disabled="disabled"
class="input"
:placeholder="$t('task.reminder.daysShort')"
v-model="periodInput.duration.days"
type="number"
min="0"
/> {{ $t('task.reminder.days') }}
</label>
<input
:disabled="disabled" :disabled="disabled"
class="input" class="input"
:placeholder="$t('task.reminder.hoursShort')" :placeholder="$t('task.reminder.hoursShort')"
v-model="periodInput.duration.hours" v-model="periodInput.duration.hours"
type="number" type="number"
min="0" min="0"
/>: />:
<input <input
:disabled="disabled" :disabled="disabled"
class="input" class="input"
:placeholder="$t('task.reminder.minutesShort')" :placeholder="$t('task.reminder.minutesShort')"
v-model="periodInput.duration.minutes" v-model="periodInput.duration.minutes"
type="number" type="number"
min="0" min="0"
/> />
<div class="select">
<select :disabled="disabled" v-model="periodInput.sign" id="sign"> <div class="select">
<option value="-1">&le;</option> <select :disabled="disabled" v-model.number="periodInput.sign">
<option value="1">&gt;</option> <option value="-1">&le;</option>
</select> <option value="1">&gt;</option>
</select>
</div>
<div class="select">
<select :disabled="disabled" v-model="periodInput.relativeTo">
<option :value="REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE">{{ $t('task.attributes.dueDate') }}</option>
<option :value="REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE">{{ $t('task.attributes.startDate')}}</option>
<option :value="REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE">{{ $t('task.attributes.endDate') }}</option>
</select>
</div>
</div> </div>
<div class="select">
<select :disabled="disabled" v-model="periodInput.relativeTo" id="relativeTo"> <div class="control">
<option :value="REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE">{{ $t('task.attributes.dueDate') }}</option> <x-button
<option :value="REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE">{{
$t('task.attributes.startDate')
}}
</option>
<option :value="REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE">{{ $t('task.attributes.endDate') }}</option>
</select>
</div>
</div>
<div class="control">
<x-button
:disabled="disabled" :disabled="disabled"
class="close-button" class="close-button"
:shadow="false" :shadow="false"
@click="submitForm" @click="submitForm"
> >
{{ $t('misc.confirm') }} {{ $t('misc.confirm') }}
</x-button> </x-button>
</div>
</div> </div>
</div> </template>
</CustomTransition> </Popup>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {reactive, ref, watch, type PropType, computed} from 'vue'
import {useI18n} from 'vue-i18n'
import BaseButton from '@/components/base/BaseButton.vue' import BaseButton from '@/components/base/BaseButton.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue' import Popup from '@/components/misc/popup.vue'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {periodToSeconds, secondsToPeriod} from '@/helpers/time/period' import {periodToSeconds, secondsToPeriod} from '@/helpers/time/period'
import TaskReminderModel from '@/models/taskReminder' 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'
import {onMounted, onBeforeUnmount, reactive, ref, watch, type PropType} from 'vue'
import {useI18n} from 'vue-i18n'
const {t} = useI18n({useScope: 'global'}) const {t} = useI18n({useScope: 'global'})
@ -87,22 +105,23 @@ const props = defineProps({
}, },
}) })
const emit = defineEmits(['update:modelValue', 'close', 'close-on-change']) const emit = defineEmits(['update:modelValue'])
const reminder = ref<ITaskReminder>() const reminder = ref<ITaskReminder>()
const isShowForm = ref(false)
const periodInput = reactive({ const periodInput = reactive({
duration: {days: 0, hours: 0, minutes: 0, seconds: 0}, duration: {
days: 0,
hours: 0,
minutes: 0,
seconds: 0
},
relativeTo: REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE, relativeTo: REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE,
sign: -1, sign: -1,
}) })
onMounted(() => document.addEventListener('click', hidePeriodPopup))
onBeforeUnmount(() => document.removeEventListener('click', hidePeriodPopup))
watch( watch(
() => props.modelValue, props.modelValue,
(value) => { (value) => {
reminder.value = value reminder.value = value
if (value && value.relativeTo != null) { if (value && value.relativeTo != null) {
@ -121,28 +140,13 @@ watch(
function updateData() { function updateData() {
changed.value = true changed.value = true
if (reminder.value) { if (reminder.value) {
reminder.value.relativePeriod = parseInt(periodInput.sign) * periodToSeconds(periodInput.duration.days, periodInput.duration.hours, periodInput.duration.minutes, 0) reminder.value.relativePeriod = periodInput.sign * periodToSeconds(periodInput.duration.days, periodInput.duration.hours, periodInput.duration.minutes, 0)
reminder.value.relativeTo = periodInput.relativeTo reminder.value.relativeTo = periodInput.relativeTo
reminder.value.reminder = null reminder.value.reminder = null
} }
emit('update:modelValue', reminder.value) emit('update:modelValue', reminder.value)
} }
function togglePeriodPopup() {
if (props.disabled) {
return
}
isShowForm.value = !isShowForm.value
}
const periodPopup = ref<HTMLElement | null>(null)
function hidePeriodPopup(e: MouseEvent) {
if (isShowForm.value) {
closeWhenClickedOutside(e, periodPopup.value, close)
}
}
function submitForm() { function submitForm() {
updateData() updateData()
close() close()
@ -153,10 +157,8 @@ const changed = ref(false)
function close() { function close() {
setTimeout(() => { setTimeout(() => {
isShowForm.value = false isShowForm.value = false
emit('close', changed.value)
if (changed.value) { if (changed.value) {
changed.value = false changed.value = false
emit('close-on-change', changed.value)
} }
}, 200) }, 200)
} }
@ -171,6 +173,16 @@ function formatDuration(reminderPeriod: number): string {
('' + duration.minutes).padStart(2, '0') ('' + duration.minutes).padStart(2, '0')
} }
const relativeToOptions = {
[REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE]: t('task.attributes.dueDate'),
[REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE]: t('task.attributes.startDate'),
[REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE]: t('task.attributes.endDate'),
} as const
const relativeTo = computed(() => relativeToOptions[periodInput.relativeTo]))
function formatRelativeTo(relativeTo: IReminderPeriodRelativeTo | null): string | null { function formatRelativeTo(relativeTo: IReminderPeriodRelativeTo | null): string | null {
switch (relativeTo) { switch (relativeTo) {
case REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE: case REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE:

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
import reminders from './reminders.vue'
</script>
<template>
<Story>
<Variant title="Default">
<reminders />
</Variant>
</Story>
</template>

View File

@ -1,32 +1,41 @@
<template> <template>
<div class="reminders"> <div class="reminders">
<div v-for="(r, index) in reminders" :key="index" :class="{ 'overdue': r.reminder < new Date() }" class="reminder-input"> <div
v-for="(r, index) in reminders"
:key="index"
:class="{ 'overdue': r.reminder < new Date() }"
class="reminder-input"
>
<div class="reminder-detail"> <div class="reminder-detail">
<ReminderDetail :disabled="disabled" v-model="reminders[index]" @update:modelValue="() => editReminder(index)"/> <ReminderDetail :disabled="disabled" v-model="reminders[index]" />
</div> </div>
<div> <div>
<BaseButton @click="removeReminderByIndex(index)" v-if="!disabled" class="remove"> <BaseButton v-if="!disabled" @click="removeReminderByIndex(index)" class="remove">
<icon icon="times"></icon> <icon icon="times" />
</BaseButton> </BaseButton>
</div> </div>
</div> </div>
<div class="reminder-input">
<BaseButton @click.stop="toggleAddReminder" v-if="!disabled"> <div v-if="!disabled" class="reminder-input">
<BaseButton @click="toggleAddReminder">
{{ $t('task.addReminder') }} {{ $t('task.addReminder') }}
</BaseButton> </BaseButton>
</div> </div>
<div class="reminder-input">
<ReminderDetail v-if="isAddReminder" :disabled="disabled" @update:modelValue="addNewReminder"/> <div v-if="isAddReminder" class="reminder-input">
<ReminderDetail :disabled="disabled" @update:modelValue="addNewReminder"/>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {reactive, ref, watch, type PropType} from 'vue'
import type { ITaskReminder } from '@/modelTypes/ITaskReminder'
import BaseButton from '@/components/base/BaseButton.vue' import BaseButton from '@/components/base/BaseButton.vue'
import ReminderDetail from '@/components/tasks/partials/reminder-detail.vue' import ReminderDetail from '@/components/tasks/partials/reminder-detail.vue'
import TaskReminderModel from '@/models/taskReminder' import TaskReminderModel from '@/models/taskReminder'
import type { ITaskReminder } from '@/modelTypes/ITaskReminder'
import { onMounted, reactive, ref, watch, type PropType } from 'vue'
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -42,15 +51,12 @@ const emit = defineEmits(['update:modelValue'])
const reminders = ref<ITaskReminder[]>([]) const reminders = ref<ITaskReminder[]>([])
onMounted(() => {
reminders.value = [...props.modelValue]
})
watch( watch(
() => props.modelValue, props.modelValue,
(newVal) => { (newVal) => {
reminders.value = newVal reminders.value = newVal
}, },
{immediate: true},
) )
const isAddReminder = ref(false) const isAddReminder = ref(false)
@ -86,28 +92,25 @@ function removeReminderByIndex(index: number) {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.reminders { .reminder-input {
.reminder-input { display: flex;
display: flex; align-items: center;
align-items: center;
&.overdue :deep(.datepicker .show) { &.overdue :deep(.datepicker .show) {
color: var(--danger); color: var(--danger);
} }
&:last-child { &::last-child {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
}
.reminder-detail {
width: 100%;
}
.remove {
color: var(--danger);
vertical-align: top;
padding-left: .5rem;
line-height: 1;
}
} }
} }
.reminder-detail {
width: 100%;
}
.remove {
color: var(--danger);
vertical-align: top;
padding-left: .5rem;
line-height: 1;
}
</style> </style>