feat(datepicker) script setup
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
parent
3440d71e74
commit
b87436c35c
|
@ -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) {
|
||||||
},
|
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,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 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',
|
||||||
|
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