WIP: REFERENCE datepicker script setup alternative #1972
|
@ -1,71 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="datepicker">
|
<div class="datepicker" ref="datepicker">
|
||||||
<BaseButton @click.stop="toggleDatePopup" class="show" :disabled="disabled || undefined">
|
<BaseButton @click="toggleDatePopup" class="show" :disabled="disabled || undefined">
|
||||||
{{ date === null ? chooseDateLabel : formatDateShort(date) }}
|
{{ state.date === null ? chooseDateLabel : formatDateShort(state.date) }}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div v-if="show" class="datepicker-popup" ref="datepickerPopup">
|
<div v-if="show" class="datepicker-popup">
|
||||||
|
|
||||||
<BaseButton
|
<BaseButton
|
||||||
v-if="(new Date()).getHours() < 21"
|
v-for="({dayInterval, label, icon}) of selectDateOptions"
|
||||||
|
:key="dayInterval"
|
||||||
class="datepicker__quick-select-date"
|
class="datepicker__quick-select-date"
|
||||||
@click.stop="setDate('today')"
|
@click="setDate(dayInterval)"
|
||||||
>
|
>
|
||||||
<span class="icon"><icon :icon="['far', 'calendar-alt']"/></span>
|
<span class="icon"><icon :icon="icon"/></span>
|
||||||
<span class="text">
|
<span class="text">
|
||||||
<span>{{ $t('input.datepicker.today') }}</span>
|
<span>{{ label }}</span>
|
||||||
<span class="weekday">{{ getWeekdayFromStringInterval('today') }}</span>
|
<span class="weekday">{{ getWeekdayFromStringInterval(dayInterval) }}</span>
|
||||||
</span>
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton
|
|
||||||
class="datepicker__quick-select-date"
|
|
||||||
@click.stop="setDate('tomorrow')"
|
|
||||||
>
|
|
||||||
<span class="icon"><icon :icon="['far', 'sun']"/></span>
|
|
||||||
<span class="text">
|
|
||||||
<span>{{ $t('input.datepicker.tomorrow') }}</span>
|
|
||||||
<span class="weekday">{{ getWeekdayFromStringInterval('tomorrow') }}</span>
|
|
||||||
</span>
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton
|
|
||||||
class="datepicker__quick-select-date"
|
|
||||||
@click.stop="setDate('nextMonday')"
|
|
||||||
>
|
|
||||||
<span class="icon"><icon icon="coffee"/></span>
|
|
||||||
<span class="text">
|
|
||||||
<span>{{ $t('input.datepicker.nextMonday') }}</span>
|
|
||||||
<span class="weekday">{{ getWeekdayFromStringInterval('nextMonday') }}</span>
|
|
||||||
</span>
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton
|
|
||||||
class="datepicker__quick-select-date"
|
|
||||||
@click.stop="setDate('thisWeekend')"
|
|
||||||
>
|
|
||||||
<span class="icon"><icon icon="cocktail"/></span>
|
|
||||||
<span class="text">
|
|
||||||
<span>{{ $t('input.datepicker.thisWeekend') }}</span>
|
|
||||||
<span class="weekday">{{ getWeekdayFromStringInterval('thisWeekend') }}</span>
|
|
||||||
</span>
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton
|
|
||||||
class="datepicker__quick-select-date"
|
|
||||||
@click.stop="setDate('laterThisWeek')"
|
|
||||||
>
|
|
||||||
<span class="icon"><icon icon="chess-knight"/></span>
|
|
||||||
<span class="text">
|
|
||||||
<span>{{ $t('input.datepicker.laterThisWeek') }}</span>
|
|
||||||
<span class="weekday">{{ getWeekdayFromStringInterval('laterThisWeek') }}</span>
|
|
||||||
</span>
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton
|
|
||||||
class="datepicker__quick-select-date"
|
|
||||||
@click.stop="setDate('nextWeek')"
|
|
||||||
>
|
|
||||||
<span class="icon"><icon icon="forward"/></span>
|
|
||||||
<span class="text">
|
|
||||||
<span>{{ $t('input.datepicker.nextWeek') }}</span>
|
|
||||||
<span class="weekday">{{ getWeekdayFromStringInterval('nextWeek') }}</span>
|
|
||||||
</span>
|
</span>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
|
||||||
|
@ -78,7 +29,7 @@
|
||||||
<x-button
|
<x-button
|
||||||
class="datepicker__close-button"
|
class="datepicker__close-button"
|
||||||
:shadow="false"
|
:shadow="false"
|
||||||
@click="close"
|
@click="closeDatePopup"
|
||||||
v-cy="'closeDatepicker'"
|
v-cy="'closeDatepicker'"
|
||||||
>
|
>
|
||||||
{{ $t('misc.confirm') }}
|
{{ $t('misc.confirm') }}
|
||||||
|
@ -88,155 +39,197 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import {defineComponent} from 'vue'
|
import {ref, reactive, computed, watch, type PropType } from 'vue'
|
||||||
|
import {useI18n} from 'vue-i18n'
|
||||||
|
import {useStore} from 'vuex'
|
||||||
|
import {useNow, onClickOutside} from '@vueuse/core'
|
||||||
|
|
||||||
import flatPickr from 'vue-flatpickr-component'
|
import flatPickr from 'vue-flatpickr-component'
|
||||||
import 'flatpickr/dist/flatpickr.css'
|
import 'flatpickr/dist/flatpickr.css'
|
||||||
import {i18n} from '@/i18n'
|
|
||||||
|
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
import BaseButton from '@/components/base/BaseButton.vue'
|
||||||
|
|
||||||
import {format} from 'date-fns'
|
import {format} from 'date-fns'
|
||||||
|
|
||||||
import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
|
import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
|
||||||
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
|
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
|
||||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
|
||||||
import {createDateFromString} from '@/helpers/time/createDateFromString'
|
import {createDateFromString} from '@/helpers/time/createDateFromString'
|
||||||
|
import {formatDateShort} from '@/helpers/time/formatDate'
|
||||||
|
|
||||||
export default defineComponent({
|
function getDateFromDayInterval(dayInterval: string) {
|
||||||
name: 'datepicker',
|
const interval = calculateDayInterval(dayInterval)
|
||||||
data() {
|
const newDate = new Date()
|
||||||
return {
|
newDate.setDate(newDate.getDate() + interval)
|
||||||
date: null,
|
newDate.setHours(calculateNearestHours(newDate))
|
||||||
show: false,
|
newDate.setMinutes(0)
|
||||||
changed: false,
|
newDate.setSeconds(0)
|
||||||
}
|
return newDate
|
||||||
},
|
}
|
||||||
components: {
|
|
||||||
flatPickr,
|
|
||||||
BaseButton,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
|
|
||||||
},
|
|
||||||
chooseDateLabel: {
|
|
||||||
type: String,
|
|
||||||
default() {
|
|
||||||
return i18n.global.t('input.datepicker.chooseDate')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ['update:modelValue', 'change', 'close', 'close-on-change'],
|
|
||||||
mounted() {
|
|
||||||
document.addEventListener('click', this.hideDatePopup)
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
document.removeEventListener('click', this.hideDatePopup)
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
modelValue: {
|
|
||||||
handler: 'setDateValue',
|
|
||||||
immediate: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
flatPickerConfig() {
|
|
||||||
return {
|
|
||||||
altFormat: this.$t('date.altFormatLong'),
|
|
||||||
altInput: true,
|
|
||||||
dateFormat: 'Y-m-d H:i',
|
|
||||||
enableTime: true,
|
|
||||||
time_24hr: true,
|
|
||||||
inline: true,
|
|
||||||
locale: {
|
|
||||||
firstDayOfWeek: this.$store.state.auth.settings.weekStart,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Since flatpickr dates are strings, we need to convert them to native date objects.
|
|
||||||
// To make that work, we need a separate variable since flatpickr does not have a change event.
|
|
||||||
flatPickrDate: {
|
|
||||||
set(newValue) {
|
|
||||||
this.date = createDateFromString(newValue)
|
|
||||||
this.updateData()
|
|
||||||
},
|
|
||||||
get() {
|
|
||||||
if (!this.date) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
return format(this.date, 'yyy-LL-dd H:mm')
|
function getWeekdayFromStringInterval(dateInterval: string) {
|
||||||
dpschen marked this conversation as resolved
Outdated
|
|||||||
},
|
const interval = calculateDayInterval(dateInterval)
|
||||||
|
const newDate = new Date(now.value)
|
||||||
|
newDate.setDate(newDate.getDate() + interval)
|
||||||
|
return format(newDate, 'E')
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// FIXME: should only accept Date objects. Then we wouldn't need all the conversion
|
||||||
|
modelValue: {
|
||||||
|
type: [Date, null, String] as PropType<Date | null | string>,
|
||||||
|
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
chooseDateLabel: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
default() {
|
||||||
|
const {t} = useI18n({useScope: 'global'})
|
||||||
|
return t('input.datepicker.chooseDate')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
disabled: {
|
||||||
setDateValue(newVal) {
|
type: Boolean,
|
||||||
if (newVal === null) {
|
default: false,
|
||||||
this.date = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.date = createDateFromString(newVal)
|
|
||||||
},
|
|
||||||
updateData() {
|
|
||||||
this.changed = true
|
|
||||||
this.$emit('update:modelValue', this.date)
|
|
||||||
this.$emit('change', this.date)
|
|
||||||
},
|
|
||||||
toggleDatePopup() {
|
|
||||||
if (this.disabled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.show = !this.show
|
|
||||||
},
|
|
||||||
hideDatePopup(e) {
|
|
||||||
if (this.show) {
|
|
||||||
closeWhenClickedOutside(e, this.$refs.datepickerPopup, this.close)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
// Kind of dirty, but the timeout allows us to enter a time and click on "confirm" without
|
|
||||||
// having to click on another input field before it is actually used.
|
|
||||||
setTimeout(() => {
|
|
||||||
this.show = false
|
|
||||||
this.$emit('close', this.changed)
|
|
||||||
if (this.changed) {
|
|
||||||
this.changed = false
|
|
||||||
this.$emit('close-on-change', this.changed)
|
|
||||||
}
|
|
||||||
}, 200)
|
|
||||||
},
|
|
||||||
setDate(date) {
|
|
||||||
if (this.date === null) {
|
|
||||||
this.date = new Date()
|
|
||||||
}
|
|
||||||
|
|
||||||
const interval = calculateDayInterval(date)
|
|
||||||
const newDate = new Date()
|
|
||||||
newDate.setDate(newDate.getDate() + interval)
|
|
||||||
newDate.setHours(calculateNearestHours(newDate))
|
|
||||||
newDate.setMinutes(0)
|
|
||||||
newDate.setSeconds(0)
|
|
||||||
this.date = newDate
|
|
||||||
this.flatPickrDate = newDate
|
|
||||||
this.updateData()
|
|
||||||
},
|
|
||||||
getDayIntervalFromString(date) {
|
|
||||||
return calculateDayInterval(date)
|
|
||||||
},
|
|
||||||
getWeekdayFromStringInterval(date) {
|
|
||||||
const interval = calculateDayInterval(date)
|
|
||||||
const newDate = new Date()
|
|
||||||
newDate.setDate(newDate.getDate() + interval)
|
|
||||||
return format(newDate, 'E')
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const {t} = useI18n({useScope: 'global'})
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
const show = ref(false)
|
||||||
|
|
||||||
|
// FIXME: replace with popup
|
||||||
|
const datepicker = ref<HTMLElement | null>(null)
|
||||||
|
onClickOutside(datepicker, closeDatePopup)
|
||||||
|
|
||||||
|
function toggleDatePopup() {
|
||||||
|
if (props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
show.value = !show.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDatePopup() {
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const state : {
|
||||||
|
date: Date | null,
|
||||||
|
initialDate: Date | null,
|
||||||
|
}= reactive({
|
||||||
|
date: null,
|
||||||
|
initialDate: null,
|
||||||
|
})
|
||||||
|
// const date = ref<Date | null>(null)
|
||||||
|
// const initialDate = ref<Date | null>(null)
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(value: null | Date | string) => {
|
||||||
|
const newDate = value === null ? null : createDateFromString(value)
|
||||||
|
Object.assign(state, {
|
||||||
|
date: newDate,
|
||||||
|
initialDate: newDate,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
function isSameDate(dateA: Date | null, dateB: Date | null) {
|
||||||
|
return (dateA !== null ? new Date(dateA).toISOString() : null) ===
|
||||||
|
(dateB !== null ? new Date(dateB).toISOString() : null)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(show, (newIsShown) => {
|
||||||
|
newIsShown && console.log(state.date, state.initialDate)
|
||||||
|
if (
|
||||||
|
newIsShown === false &&
|
||||||
|
!isSameDate(state.initialDate, state.date)
|
||||||
|
) {
|
||||||
|
console.log('initialDate', state.initialDate !== null ? new Date(state.initialDate).toISOString() : null)
|
||||||
|
console.log('date', state.date !== null ? new Date(state.date).toISOString() : null)
|
||||||
|
// make copy of date
|
||||||
|
emit('update:modelValue', state.date !== null ? new Date(state.date) : null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const flatPickerConfig = computed(() => ({
|
||||||
|
altFormat: t('date.altFormatLong'),
|
||||||
|
altInput: true,
|
||||||
|
dateFormat: 'Y-m-d H:i',
|
||||||
|
enableTime: true,
|
||||||
|
time_24hr: true,
|
||||||
|
inline: true,
|
||||||
|
locale: {
|
||||||
|
firstDayOfWeek: store.state.auth.settings.weekStart,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
dpschen marked this conversation as resolved
Outdated
konrad
commented
Please remove this. Please remove this.
|
|||||||
|
// Since flatpickr dates are strings, we need to convert them to native date objects.
|
||||||
|
// To make that work, we need a separate variable since flatpickr does not have a change event.
|
||||||
|
const flatPickrDate = computed({
|
||||||
|
get() {
|
||||||
|
if (!state.date) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return format(state.date, 'yyy-LL-dd H:mm')
|
||||||
|
},
|
||||||
|
set(newValue: string) {
|
||||||
|
state.date = createDateFromString(newValue)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const now = useNow()
|
||||||
|
|
||||||
|
const selectDateOptions = computed(() => {
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
condition: now.value.getHours() < 21,
|
||||||
|
dayInterval: 'today',
|
||||||
|
icon: ['far', 'calendar-alt'],
|
||||||
|
label: t('input.datepicker.today'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dayInterval: 'tomorrow',
|
||||||
|
icon: ['far', 'sun'],
|
||||||
|
label: t('input.datepicker.tomorrow'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dayInterval: 'nextMonday',
|
||||||
dpschen marked this conversation as resolved
Outdated
konrad
commented
I think this doesn't actually work? But that doesn't really work right now either, more of a mental note to properly fix this some time. I think this doesn't actually work? But that doesn't really work right now either, more of a mental note to properly fix this some time.
|
|||||||
|
icon: 'coffee',
|
||||||
|
label: t('input.datepicker.nextMonday'),
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dayInterval: 'thisWeekend',
|
||||||
|
icon: 'cocktail',
|
||||||
|
label: t('input.datepicker.thisWeekend'),
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dayInterval: 'laterThisWeek',
|
||||||
|
icon: 'chess-knight',
|
||||||
|
label: t('input.datepicker.laterThisWeek'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dayInterval: 'nextWeek',
|
||||||
|
icon: 'forward',
|
||||||
|
label: t('input.datepicker.nextWeek'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return options.filter((option) => option.condition || option.condition === undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
function setDate(dayInterval: string) {
|
||||||
|
state.date = getDateFromDayInterval(dayInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -36,8 +36,8 @@
|
||||||
|
|
||||||
<strong>{{ $t('task.attributes.reminders') }}</strong>
|
<strong>{{ $t('task.attributes.reminders') }}</strong>
|
||||||
<reminders
|
<reminders
|
||||||
@change="editTaskSubmit()"
|
|
||||||
v-model="taskEditTask.reminderDates"
|
v-model="taskEditTask.reminderDates"
|
||||||
|
@update:model-value="editTaskSubmit()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
@ -3,22 +3,25 @@
|
||||||
<div
|
<div
|
||||||
v-for="(r, index) in reminders"
|
v-for="(r, index) in reminders"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="{ 'overdue': r < new Date()}"
|
|
||||||
class="reminder-input"
|
class="reminder-input"
|
||||||
|
:class="{ 'overdue': r < new Date()}"
|
||||||
>
|
>
|
||||||
<Datepicker
|
<Datepicker
|
||||||
v-model="reminders[index]"
|
v-model="reminders[index]"
|
||||||
|
@update:model-value="addReminderDate(index)"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@close-on-change="() => addReminderDate(index)"
|
|
||||||
/>
|
/>
|
||||||
<BaseButton @click="removeReminderByIndex(index)" v-if="!disabled" class="remove">
|
<BaseButton
|
||||||
<icon icon="times"></icon>
|
v-if="!disabled" class="remove"
|
||||||
|
@click="removeReminderByIndex(index)"
|
||||||
|
>
|
||||||
|
<icon icon="times" />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="reminder-input" v-if="!disabled">
|
<div class="reminder-input" v-if="!disabled">
|
||||||
<Datepicker
|
<Datepicker
|
||||||
v-model="newReminder"
|
v-model="newReminder"
|
||||||
@close-on-change="() => addReminderDate()"
|
@update:model-value="addReminderDate"
|
||||||
:choose-date-label="$t('task.addReminder')"
|
:choose-date-label="$t('task.addReminder')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,15 +29,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {PropType, ref, onMounted, watch} from 'vue'
|
import {type PropType, ref, toRef, watch} from 'vue'
|
||||||
|
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
import BaseButton from '@/components/base/BaseButton.vue'
|
||||||
import Datepicker from '@/components/input/datepicker.vue'
|
import Datepicker from '@/components/input/datepicker.vue'
|
||||||
|
|
||||||
type Reminder = Date | string
|
type Reminder = Date | string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Array as PropType<Reminder[]>,
|
type: Array as PropType<Reminder[]>,
|
||||||
|
@ -67,26 +68,21 @@ const emit = defineEmits(['update:modelValue', 'change'])
|
||||||
|
|
||||||
const reminders = ref<Reminder[]>([])
|
const reminders = ref<Reminder[]>([])
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
reminders.value = [...props.modelValue]
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
toRef(props, 'modelValue'),
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
for (const i in newVal) {
|
reminders.value = newVal.map(item => {
|
||||||
if (typeof newVal[i] === 'string') {
|
return typeof item === 'string'
|
||||||
newVal[i] = new Date(newVal[i])
|
? new Date(item)
|
||||||
}
|
: item
|
||||||
}
|
})
|
||||||
reminders.value = newVal
|
|
||||||
},
|
},
|
||||||
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
function updateData() {
|
function updateData() {
|
||||||
emit('update:modelValue', reminders.value)
|
emit('update:modelValue', reminders.value)
|
||||||
emit('change')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newReminder = ref(null)
|
const newReminder = ref(null)
|
||||||
|
@ -98,7 +94,7 @@ function addReminderDate(index : number | null = null) {
|
||||||
}
|
}
|
||||||
reminders.value.push(new Date(newReminder.value))
|
reminders.value.push(new Date(newReminder.value))
|
||||||
newReminder.value = null
|
newReminder.value = null
|
||||||
} else if(reminders.value[index] === null) {
|
} else if (reminders.value[index] === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,23 +108,21 @@ 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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.remove {
|
.remove {
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
padding-left: .5rem;
|
padding-left: .5rem;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,4 +1,4 @@
|
||||||
export function calculateDayInterval(date, currentDay = (new Date().getDay())) {
|
export function calculateDayInterval(date, currentDay = (new Date().getDay())): number {
|
||||||
switch (date) {
|
switch (date) {
|
||||||
case 'today':
|
case 'today':
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* @param dateString
|
* @param dateString
|
||||||
* @returns {Date}
|
* @returns {Date}
|
||||||
*/
|
*/
|
||||||
export const createDateFromString = dateString => {
|
export function createDateFromString(dateString: Date | string) : Date {
|
||||||
if (dateString instanceof Date) {
|
if (dateString instanceof Date) {
|
||||||
return dateString
|
return dateString
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<div class="date-input">
|
<div class="date-input">
|
||||||
<datepicker
|
<datepicker
|
||||||
v-model="task.dueDate"
|
v-model="task.dueDate"
|
||||||
@close-on-change="() => saveTask()"
|
@update:model-value="saveTask"
|
||||||
:choose-date-label="$t('task.detail.chooseDueDate')"
|
:choose-date-label="$t('task.detail.chooseDueDate')"
|
||||||
:disabled="taskService.loading || !canWrite"
|
:disabled="taskService.loading || !canWrite"
|
||||||
ref="dueDate"
|
ref="dueDate"
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
<div class="date-input">
|
<div class="date-input">
|
||||||
<datepicker
|
<datepicker
|
||||||
v-model="task.startDate"
|
v-model="task.startDate"
|
||||||
@close-on-change="() => saveTask()"
|
@update:model-value="saveTask"
|
||||||
:choose-date-label="$t('task.detail.chooseStartDate')"
|
:choose-date-label="$t('task.detail.chooseStartDate')"
|
||||||
:disabled="taskService.loading || !canWrite"
|
:disabled="taskService.loading || !canWrite"
|
||||||
ref="startDate"
|
ref="startDate"
|
||||||
|
@ -121,7 +121,7 @@
|
||||||
<div class="date-input">
|
<div class="date-input">
|
||||||
<datepicker
|
<datepicker
|
||||||
v-model="task.endDate"
|
v-model="task.endDate"
|
||||||
@close-on-change="() => saveTask()"
|
@update:model-value="saveTask"
|
||||||
:choose-date-label="$t('task.detail.chooseEndDate')"
|
:choose-date-label="$t('task.detail.chooseEndDate')"
|
||||||
:disabled="taskService.loading || !canWrite"
|
:disabled="taskService.loading || !canWrite"
|
||||||
ref="endDate"
|
ref="endDate"
|
||||||
|
@ -146,9 +146,10 @@
|
||||||
</div>
|
</div>
|
||||||
<reminders
|
<reminders
|
||||||
:disabled="!canWrite"
|
:disabled="!canWrite"
|
||||||
@change="saveTask"
|
|
||||||
ref="reminders"
|
ref="reminders"
|
||||||
v-model="task.reminderDates"/>
|
v-model="task.reminderDates"
|
||||||
|
@update:model-value="saveTask"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="flash-background" appear>
|
<transition name="flash-background" appear>
|
||||||
|
@ -781,10 +782,10 @@ $flash-background-duration: 750ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove {
|
.remove {
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding-left: .5rem;
|
padding-left: .5rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.datepicker) {
|
:deep(.datepicker) {
|
||||||
|
|
Reference in New Issue
Why don't you add the
useI18n
before the props declaration?That wouldn't work. Because afaik the props, events declaration and imports are extracted from script setup and the rest will be wrapped by the setup function. Meaning that if I would use
use18n
beforedefineProps
it would still be inside the setup block after beeing compiled => no access inside the props.