@bagelink/vue 0.0.1102 → 0.0.1109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/form/inputs/DatePick.vue.d.ts +23 -0
- package/dist/components/form/inputs/DatePick.vue.d.ts.map +1 -0
- package/dist/components/form/inputs/NumberInput.vue.d.ts +1 -0
- package/dist/components/form/inputs/NumberInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/index.d.ts +1 -0
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/composables/useFormField.d.ts +11 -0
- package/dist/composables/useFormField.d.ts.map +1 -0
- package/dist/index.cjs +564 -190
- package/dist/index.mjs +564 -190
- package/dist/style.css +187 -6
- package/package.json +1 -1
- package/src/components/form/BglForm.vue +1 -1
- package/src/components/form/inputs/DatePick.vue +344 -250
- package/src/components/form/inputs/NumberInput.vue +17 -5
- package/src/components/form/inputs/index.ts +1 -1
- package/src/composables/useFormField.ts +35 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { Btn,
|
|
3
|
-
import {
|
|
2
|
+
import { Btn, NumberInput } from '@bagelink/vue'
|
|
3
|
+
import { Dropdown as VDropdown } from 'floating-vue'
|
|
4
4
|
|
|
5
5
|
const props = withDefaults(
|
|
6
6
|
defineProps<{
|
|
@@ -10,293 +10,337 @@ const props = withDefaults(
|
|
|
10
10
|
small?: boolean
|
|
11
11
|
enableTime?: boolean
|
|
12
12
|
modelValue?: string | Date
|
|
13
|
-
defaultValue?: string | Date
|
|
14
|
-
rtl?: boolean
|
|
15
13
|
min?: string | Date
|
|
16
14
|
max?: string | Date
|
|
17
|
-
|
|
18
|
-
timezone?: string // e.g. 'America/New_York', 'Europe/London'
|
|
15
|
+
timezone?: string
|
|
19
16
|
}>(),
|
|
20
17
|
{
|
|
21
18
|
enableTime: false,
|
|
22
19
|
editMode: true,
|
|
23
20
|
small: false,
|
|
24
|
-
rtl: false,
|
|
25
|
-
showInput: true,
|
|
26
21
|
timezone: 'UTC'
|
|
27
22
|
},
|
|
28
23
|
)
|
|
29
24
|
|
|
30
25
|
const emit = defineEmits(['update:modelValue'])
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
let isOpen = $ref(false)
|
|
27
|
+
let currentMonth = $ref(new Date())
|
|
28
|
+
type ViewMode = 'days' | 'months' | 'years'
|
|
29
|
+
let currentView = $ref<ViewMode>('days')
|
|
33
30
|
|
|
34
|
-
const inputType = computed(() => props.enableTime ? 'datetime-local' : 'date')
|
|
31
|
+
const inputType = $computed(() => props.enableTime ? 'datetime-local' : 'date')
|
|
35
32
|
|
|
36
|
-
function
|
|
37
|
-
if (!date) return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
} catch (e) {
|
|
41
|
-
console.warn(`Invalid timezone: ${props.timezone}, falling back to UTC`)
|
|
42
|
-
return date
|
|
43
|
-
}
|
|
33
|
+
function formatDate(date: Date | string | undefined): string {
|
|
34
|
+
if (!date) return ''
|
|
35
|
+
const dateObj = typeof date === 'string' ? new Date(date) : date
|
|
36
|
+
return props.enableTime ? dateObj.toISOString().slice(0, 16) : dateObj.toISOString().split('T')[0]
|
|
44
37
|
}
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const localDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }))
|
|
50
|
-
const tzDate = new Date(date.toLocaleString('en-US', { timeZone: props.timezone }))
|
|
51
|
-
const diff = localDate.getTime() - tzDate.getTime()
|
|
52
|
-
return new Date(date.getTime() + diff)
|
|
53
|
-
} catch (e) {
|
|
54
|
-
console.warn(`Invalid timezone: ${props.timezone}, falling back to UTC`)
|
|
55
|
-
return date
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const formattedValue = computed(() => {
|
|
60
|
-
if (!props.modelValue) return ''
|
|
61
|
-
const date = typeof props.modelValue === 'string' ? new Date(props.modelValue) : props.modelValue
|
|
62
|
-
const localDate = toLocalDate(date)
|
|
63
|
-
|
|
64
|
-
if (props.enableTime) {
|
|
65
|
-
return localDate.toISOString().slice(0, 16) // Format: YYYY-MM-DDTHH:mm
|
|
66
|
-
}
|
|
67
|
-
return localDate.toISOString().split('T')[0] // Format: YYYY-MM-DD
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const formattedMin = computed(() => {
|
|
71
|
-
if (!props.min) return undefined
|
|
72
|
-
const date = typeof props.min === 'string' ? new Date(props.min) : props.min
|
|
73
|
-
const localDate = toLocalDate(date)
|
|
74
|
-
return props.enableTime ? localDate.toISOString().slice(0, 16) : localDate.toISOString().split('T')[0]
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const formattedMax = computed(() => {
|
|
78
|
-
if (!props.max) return undefined
|
|
79
|
-
const date = typeof props.max === 'string' ? new Date(props.max) : props.max
|
|
80
|
-
const localDate = toLocalDate(date)
|
|
81
|
-
return props.enableTime ? localDate.toISOString().slice(0, 16) : localDate.toISOString().split('T')[0]
|
|
82
|
-
})
|
|
39
|
+
const formattedValue = $computed(() => formatDate(props.modelValue))
|
|
40
|
+
const formattedMin = $computed(() => formatDate(props.min))
|
|
41
|
+
const formattedMax = $computed(() => formatDate(props.max))
|
|
83
42
|
|
|
84
|
-
const selectedDate = computed(() => {
|
|
43
|
+
const selectedDate = $computed(() => {
|
|
85
44
|
if (!props.modelValue) return null
|
|
86
|
-
|
|
87
|
-
return toLocalDate(date)
|
|
45
|
+
return typeof props.modelValue === 'string' ? new Date(props.modelValue) : props.modelValue
|
|
88
46
|
})
|
|
89
47
|
|
|
90
|
-
const currentMonthDays = computed(() => {
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const month = localCurrentMonth.getMonth()
|
|
48
|
+
const currentMonthDays = $computed(() => {
|
|
49
|
+
const year = currentMonth.getFullYear()
|
|
50
|
+
const month = currentMonth.getMonth()
|
|
94
51
|
const firstDay = new Date(year, month, 1)
|
|
95
52
|
const lastDay = new Date(year, month + 1, 0)
|
|
96
53
|
const days = []
|
|
97
54
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
for (let i = 0; i < firstDayOfWeek; i++) {
|
|
101
|
-
days.push(null)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Add all days of the month
|
|
105
|
-
for (let i = 1; i <= lastDay.getDate(); i++) {
|
|
106
|
-
days.push(new Date(year, month, i))
|
|
107
|
-
}
|
|
55
|
+
for (let i = 0; i < firstDay.getDay(); i++) days.push(null)
|
|
56
|
+
for (let i = 1; i <= lastDay.getDate(); i++) days.push(new Date(year, month, i))
|
|
108
57
|
|
|
109
58
|
return days
|
|
110
59
|
})
|
|
111
60
|
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
timeZone: props.timezone
|
|
118
|
-
}
|
|
61
|
+
const currentMonthValue = $computed(() => ({
|
|
62
|
+
month: currentMonth.getMonth(),
|
|
63
|
+
year: currentMonth.getFullYear(),
|
|
64
|
+
formatted: {
|
|
65
|
+
month: currentMonth.toLocaleString('default', { month: 'long', timeZone: props.timezone }),
|
|
66
|
+
year: currentMonth.toLocaleString('default', { year: 'numeric', timeZone: props.timezone })
|
|
67
|
+
}
|
|
68
|
+
}))
|
|
69
|
+
|
|
70
|
+
const months = $computed(() => Array.from({ length: 12 }, (_, i) => {
|
|
71
|
+
const date = new Date(currentMonthValue.year, i, 1)
|
|
72
|
+
return {
|
|
73
|
+
name: date.toLocaleString('default', { month: 'short' }),
|
|
74
|
+
value: i,
|
|
75
|
+
disabled: isDateDisabled(date)
|
|
76
|
+
}
|
|
119
77
|
})
|
|
78
|
+
)
|
|
120
79
|
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
80
|
+
const years = $computed(() => {
|
|
81
|
+
const startYear = currentMonthValue.year - 10
|
|
82
|
+
return Array.from({ length: 21 }, (_, i) => ({
|
|
83
|
+
value: startYear + i,
|
|
84
|
+
disabled: isYearDisabled(startYear + i)
|
|
85
|
+
}))
|
|
86
|
+
})
|
|
127
87
|
|
|
128
|
-
|
|
129
|
-
|
|
88
|
+
function isDateDisabled(date: Date | null) {
|
|
89
|
+
if (!date) return true
|
|
90
|
+
const minDate = props.min ? new Date(props.min) : null
|
|
91
|
+
const maxDate = props.max ? new Date(props.max) : null
|
|
130
92
|
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
93
|
+
if (minDate && date < minDate) return true
|
|
94
|
+
if (maxDate && date > maxDate) return true
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isYearDisabled(year: number) {
|
|
99
|
+
const minDate = props.min ? new Date(props.min) : null
|
|
100
|
+
const maxDate = props.max ? new Date(props.max) : null
|
|
101
|
+
|
|
102
|
+
if (minDate && year < minDate.getFullYear()) return true
|
|
103
|
+
if (maxDate && year > maxDate.getFullYear()) return true
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function selectMonth(monthIndex: number) {
|
|
108
|
+
currentMonth = new Date(currentMonth.getFullYear(), monthIndex, 1)
|
|
109
|
+
currentView = 'days'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function selectYear(year: number) {
|
|
113
|
+
currentMonth = new Date(year, currentMonth.getMonth(), 1)
|
|
114
|
+
currentView = 'months'
|
|
136
115
|
}
|
|
137
116
|
|
|
138
117
|
function previousMonth() {
|
|
139
|
-
|
|
140
|
-
currentMonth.value = new Date(
|
|
141
|
-
localCurrentMonth.getFullYear(),
|
|
142
|
-
localCurrentMonth.getMonth() - 1,
|
|
143
|
-
1
|
|
144
|
-
)
|
|
118
|
+
currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
|
|
145
119
|
}
|
|
146
120
|
|
|
147
121
|
function nextMonth() {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
122
|
+
currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function previousYear() {
|
|
126
|
+
const offset = currentView === 'months' ? 1 : 21
|
|
127
|
+
currentMonth = new Date(currentMonth.getFullYear() - offset, currentMonth.getMonth(), 1)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function nextYear() {
|
|
131
|
+
const offset = currentView === 'months' ? 1 : 21
|
|
132
|
+
currentMonth = new Date(currentMonth.getFullYear() + offset, currentMonth.getMonth(), 1)
|
|
154
133
|
}
|
|
155
134
|
|
|
156
135
|
function selectDate(date: Date | null) {
|
|
157
136
|
if (!date || !props.editMode) return
|
|
158
137
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
138
|
+
// Create date at start of day in the target timezone
|
|
139
|
+
const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0)
|
|
140
|
+
|
|
141
|
+
// If time is enabled, preserve existing time or set to current time
|
|
142
|
+
if (props.enableTime) {
|
|
143
|
+
const currentHours = selectedDate?.getHours() ?? new Date().getHours()
|
|
144
|
+
const currentMinutes = selectedDate?.getMinutes() ?? new Date().getMinutes()
|
|
145
|
+
newDate.setHours(currentHours)
|
|
146
|
+
newDate.setMinutes(currentMinutes)
|
|
147
|
+
emit('update:modelValue', newDate.toISOString())
|
|
148
|
+
} else {
|
|
149
|
+
emit('update:modelValue', newDate.toISOString().split('T')[0])
|
|
150
|
+
isOpen = false
|
|
165
151
|
}
|
|
152
|
+
}
|
|
166
153
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
154
|
+
function handleInput(event: Event) {
|
|
155
|
+
const input = event.target as HTMLInputElement
|
|
156
|
+
if (!input.value) {
|
|
157
|
+
emit('update:modelValue', '')
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const date = new Date(input.value)
|
|
162
|
+
emit('update:modelValue', props.enableTime ? date.toISOString() : date.toISOString().split('T')[0])
|
|
163
|
+
}
|
|
170
164
|
|
|
171
|
-
|
|
165
|
+
const hours = $computed(() => selectedDate?.getHours() ?? 0)
|
|
166
|
+
const minutes = $computed(() => selectedDate?.getMinutes() ?? 0)
|
|
167
|
+
|
|
168
|
+
function handleHourInput(value: number) {
|
|
169
|
+
if (!selectedDate) return
|
|
170
|
+
const newDate = new Date(selectedDate)
|
|
171
|
+
newDate.setHours(value)
|
|
172
|
+
emit('update:modelValue', newDate.toISOString())
|
|
173
|
+
}
|
|
172
174
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
function handleMinuteInput(value: number) {
|
|
176
|
+
if (!selectedDate) return
|
|
177
|
+
const newDate = new Date(selectedDate)
|
|
178
|
+
newDate.setMinutes(value)
|
|
179
|
+
emit('update:modelValue', newDate.toISOString())
|
|
175
180
|
}
|
|
176
181
|
|
|
177
182
|
function isSelected(date: Date | null) {
|
|
178
|
-
if (!date || !selectedDate
|
|
179
|
-
|
|
180
|
-
return localDate.toISOString().split('T')[0] === selectedDate.value.toISOString().split('T')[0]
|
|
183
|
+
if (!date || !selectedDate) return false
|
|
184
|
+
return date.toISOString().split('T')[0] === selectedDate.toISOString().split('T')[0]
|
|
181
185
|
}
|
|
182
186
|
|
|
183
187
|
function isToday(date: Date | null) {
|
|
184
188
|
if (!date) return false
|
|
185
|
-
|
|
186
|
-
const localDate = toLocalDate(date)
|
|
187
|
-
return localDate.toISOString().split('T')[0] === today.toISOString().split('T')[0]
|
|
189
|
+
return date.toISOString().split('T')[0] === new Date().toISOString().split('T')[0]
|
|
188
190
|
}
|
|
189
191
|
|
|
190
|
-
|
|
191
|
-
if (!date) return true
|
|
192
|
-
const localDate = toLocalDate(date)
|
|
193
|
-
const minDate = props.min ? toLocalDate(new Date(props.min)) : null
|
|
194
|
-
const maxDate = props.max ? toLocalDate(new Date(props.max)) : null
|
|
195
|
-
|
|
196
|
-
if (minDate && localDate < minDate) return true
|
|
197
|
-
if (maxDate && localDate > maxDate) return true
|
|
198
|
-
return false
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Add timezone display if time is enabled
|
|
202
|
-
const timezoneDisplay = computed(() => {
|
|
192
|
+
const timezoneDisplay = $computed(() => {
|
|
203
193
|
if (!props.enableTime) return ''
|
|
204
194
|
try {
|
|
205
|
-
|
|
206
|
-
return date.toLocaleString('en-US', {
|
|
195
|
+
return new Date().toLocaleString('en-US', {
|
|
207
196
|
timeZoneName: 'short',
|
|
208
197
|
timeZone: props.timezone
|
|
209
198
|
}).split(' ').pop()
|
|
210
|
-
} catch
|
|
199
|
+
} catch {
|
|
211
200
|
return 'UTC'
|
|
212
201
|
}
|
|
213
202
|
})
|
|
214
203
|
</script>
|
|
215
204
|
|
|
216
205
|
<template>
|
|
217
|
-
<div
|
|
218
|
-
class="bagel-input"
|
|
219
|
-
:class="{ small, 'rtl-date': rtl }"
|
|
220
|
-
:title="label"
|
|
221
|
-
>
|
|
206
|
+
<div class="bagel-input" :class="{ small }" :title="label">
|
|
222
207
|
<label v-if="label">
|
|
223
208
|
{{ label }}
|
|
224
209
|
<span v-if="required" class="required">*</span>
|
|
225
210
|
</label>
|
|
226
211
|
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
<span v-if="enableTime" class="timezone-display">{{ timezoneDisplay }}</span>
|
|
212
|
+
<VDropdown
|
|
213
|
+
:shown="isOpen"
|
|
214
|
+
:triggers="[]"
|
|
215
|
+
placement="bottom-start"
|
|
216
|
+
:distance="4"
|
|
217
|
+
@apply-show="isOpen = true"
|
|
218
|
+
@apply-hide="isOpen = false"
|
|
219
|
+
>
|
|
220
|
+
<div class="date-picker-container">
|
|
221
|
+
<input
|
|
222
|
+
:type="inputType"
|
|
223
|
+
:value="formattedValue"
|
|
224
|
+
:min="formattedMin"
|
|
225
|
+
:max="formattedMax"
|
|
226
|
+
:required="required"
|
|
227
|
+
:disabled="!editMode"
|
|
228
|
+
class="date-input"
|
|
229
|
+
@input="handleInput"
|
|
230
|
+
@click="isOpen = true"
|
|
231
|
+
>
|
|
248
232
|
</div>
|
|
249
233
|
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
234
|
+
<template #popper>
|
|
235
|
+
<div class="calendar-container">
|
|
236
|
+
<div class="calendar-section">
|
|
237
|
+
<div class="calendar-header">
|
|
238
|
+
<template v-if="currentView === 'days'">
|
|
239
|
+
<Btn flat icon="chevron_left" @click="previousMonth" />
|
|
240
|
+
<div class="month-year-selector">
|
|
241
|
+
<button class="month-btn" @click="currentView = 'months'">
|
|
242
|
+
{{ currentMonthValue.formatted.month }}
|
|
243
|
+
</button>
|
|
244
|
+
<button class="year-btn" @click="currentView = 'years'">
|
|
245
|
+
{{ currentMonthValue.formatted.year }}
|
|
246
|
+
</button>
|
|
247
|
+
</div>
|
|
248
|
+
<Btn flat icon="chevron_right" @click="nextMonth" />
|
|
249
|
+
</template>
|
|
250
|
+
<template v-else>
|
|
251
|
+
<Btn flat icon="chevron_left" @click="previousYear" />
|
|
252
|
+
<span class="month-year">{{ currentMonthValue.formatted.year }}</span>
|
|
253
|
+
<Btn flat icon="chevron_right" @click="nextYear" />
|
|
254
|
+
</template>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<div v-if="currentView === 'days'" class="calendar-grid">
|
|
258
|
+
<div v-for="day in ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']" :key="day" class="weekday">
|
|
259
|
+
{{ day }}
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<button
|
|
263
|
+
v-for="(date, index) in currentMonthDays"
|
|
264
|
+
:key="index"
|
|
265
|
+
type="button"
|
|
266
|
+
class="day"
|
|
267
|
+
:class="{
|
|
268
|
+
selected: isSelected(date),
|
|
269
|
+
today: isToday(date),
|
|
270
|
+
disabled: isDateDisabled(date),
|
|
271
|
+
}"
|
|
272
|
+
:disabled="isDateDisabled(date)"
|
|
273
|
+
@click="selectDate(date)"
|
|
274
|
+
>
|
|
275
|
+
{{ date?.getDate() }}
|
|
276
|
+
</button>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
<div v-else-if="currentView === 'months'" class="month-grid">
|
|
280
|
+
<button
|
|
281
|
+
v-for="month in months"
|
|
282
|
+
:key="month.value"
|
|
283
|
+
class="month-item"
|
|
284
|
+
:class="{
|
|
285
|
+
selected: month.value === currentMonthValue.month,
|
|
286
|
+
disabled: month.disabled,
|
|
287
|
+
}"
|
|
288
|
+
:disabled="month.disabled"
|
|
289
|
+
@click="selectMonth(month.value)"
|
|
290
|
+
>
|
|
291
|
+
{{ month.name }}
|
|
292
|
+
</button>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div v-else class="year-grid">
|
|
296
|
+
<button
|
|
297
|
+
v-for="year in years"
|
|
298
|
+
:key="year.value"
|
|
299
|
+
class="year-item"
|
|
300
|
+
:class="{
|
|
301
|
+
selected: year.value === currentMonthValue.year,
|
|
302
|
+
disabled: year.disabled,
|
|
303
|
+
}"
|
|
304
|
+
:disabled="year.disabled"
|
|
305
|
+
@click="selectYear(year.value)"
|
|
306
|
+
>
|
|
307
|
+
{{ year.value }}
|
|
308
|
+
</button>
|
|
309
|
+
</div>
|
|
268
310
|
</div>
|
|
269
311
|
|
|
270
|
-
<
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
312
|
+
<div v-if="enableTime && currentView === 'days'" class="time-picker">
|
|
313
|
+
<div class="time-input-group">
|
|
314
|
+
<NumberInput
|
|
315
|
+
center
|
|
316
|
+
:model-value="hours"
|
|
317
|
+
:disabled="!selectedDate"
|
|
318
|
+
:min="0"
|
|
319
|
+
:max="23"
|
|
320
|
+
layout="vertical"
|
|
321
|
+
:padZero="2"
|
|
322
|
+
@update:model-value="handleHourInput"
|
|
323
|
+
/>
|
|
324
|
+
<span>:</span>
|
|
325
|
+
<NumberInput
|
|
326
|
+
center
|
|
327
|
+
:model-value="minutes"
|
|
328
|
+
:disabled="!selectedDate"
|
|
329
|
+
:min="0"
|
|
330
|
+
:max="59"
|
|
331
|
+
:padZero="2"
|
|
332
|
+
layout="vertical"
|
|
333
|
+
@update:model-value="handleMinuteInput"
|
|
334
|
+
/>
|
|
335
|
+
</div>
|
|
336
|
+
<span class="timezone-display">{{ timezoneDisplay }}</span>
|
|
337
|
+
<Btn v-if="selectedDate" flat @click="isOpen = false">
|
|
338
|
+
Done
|
|
339
|
+
</Btn>
|
|
340
|
+
</div>
|
|
297
341
|
</div>
|
|
298
|
-
</
|
|
299
|
-
</
|
|
342
|
+
</template>
|
|
343
|
+
</VDropdown>
|
|
300
344
|
</div>
|
|
301
345
|
</template>
|
|
302
346
|
|
|
@@ -312,8 +356,7 @@ const timezoneDisplay = computed(() => {
|
|
|
312
356
|
position: relative;
|
|
313
357
|
}
|
|
314
358
|
|
|
315
|
-
.date-input
|
|
316
|
-
.date-display {
|
|
359
|
+
.date-input {
|
|
317
360
|
padding: 0.5rem;
|
|
318
361
|
border: 1px solid #ddd;
|
|
319
362
|
border-radius: 4px;
|
|
@@ -322,17 +365,7 @@ const timezoneDisplay = computed(() => {
|
|
|
322
365
|
background: white;
|
|
323
366
|
}
|
|
324
367
|
|
|
325
|
-
.date-
|
|
326
|
-
cursor: pointer;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
.date-display.disabled {
|
|
330
|
-
background-color: #f5f5f5;
|
|
331
|
-
cursor: not-allowed;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.date-input:focus,
|
|
335
|
-
.date-display:focus {
|
|
368
|
+
.date-input:focus {
|
|
336
369
|
outline: none;
|
|
337
370
|
border-color: #4a90e2;
|
|
338
371
|
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
|
|
@@ -343,32 +376,30 @@ const timezoneDisplay = computed(() => {
|
|
|
343
376
|
cursor: not-allowed;
|
|
344
377
|
}
|
|
345
378
|
|
|
346
|
-
.small .date-input
|
|
347
|
-
.small .date-display {
|
|
379
|
+
.small .date-input {
|
|
348
380
|
padding: 0.25rem;
|
|
349
381
|
font-size: 0.875rem;
|
|
350
382
|
}
|
|
351
383
|
|
|
352
|
-
.rtl-date input {
|
|
353
|
-
direction: rtl;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
384
|
.required {
|
|
357
385
|
color: #ff4d4f;
|
|
358
386
|
margin-left: 4px;
|
|
359
387
|
}
|
|
360
388
|
|
|
361
389
|
.calendar-popup {
|
|
362
|
-
position: absolute;
|
|
363
|
-
top: 100%;
|
|
364
|
-
left: 0;
|
|
365
|
-
z-index: 1000;
|
|
366
390
|
background: white;
|
|
367
391
|
border: 1px solid #ddd;
|
|
368
392
|
border-radius: 4px;
|
|
369
393
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
370
394
|
padding: 1rem;
|
|
371
|
-
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.calendar-container {
|
|
398
|
+
display: flex;
|
|
399
|
+
gap: 1rem;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.calendar-section {
|
|
372
403
|
width: 300px;
|
|
373
404
|
}
|
|
374
405
|
|
|
@@ -379,21 +410,25 @@ const timezoneDisplay = computed(() => {
|
|
|
379
410
|
margin-bottom: 1rem;
|
|
380
411
|
}
|
|
381
412
|
|
|
382
|
-
.
|
|
413
|
+
.month-year-selector {
|
|
414
|
+
display: flex;
|
|
415
|
+
gap: 0.5rem;
|
|
416
|
+
align-items: center;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.month-btn,
|
|
420
|
+
.year-btn {
|
|
383
421
|
background: none;
|
|
384
422
|
border: none;
|
|
385
|
-
font-
|
|
423
|
+
font-weight: 500;
|
|
386
424
|
cursor: pointer;
|
|
387
|
-
padding: 0 0.5rem;
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
.nav-button:hover {
|
|
392
|
-
color: #4a90e2;
|
|
425
|
+
padding: 0.25rem 0.5rem;
|
|
426
|
+
border-radius: 4px;
|
|
393
427
|
}
|
|
394
428
|
|
|
395
|
-
.month-
|
|
396
|
-
|
|
429
|
+
.month-btn:hover,
|
|
430
|
+
.year-btn:hover {
|
|
431
|
+
background-color: #f5f5f5;
|
|
397
432
|
}
|
|
398
433
|
|
|
399
434
|
.calendar-grid {
|
|
@@ -402,6 +437,54 @@ const timezoneDisplay = computed(() => {
|
|
|
402
437
|
gap: 0.25rem;
|
|
403
438
|
}
|
|
404
439
|
|
|
440
|
+
.month-grid {
|
|
441
|
+
display: grid;
|
|
442
|
+
grid-template-columns: repeat(3, 1fr);
|
|
443
|
+
grid-template-rows: repeat(4, 1fr);
|
|
444
|
+
gap: 0.5rem;
|
|
445
|
+
padding: 0.5rem;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.year-grid {
|
|
449
|
+
display: grid;
|
|
450
|
+
grid-template-columns: repeat(3, 1fr);
|
|
451
|
+
grid-template-rows: repeat(7, 1fr);
|
|
452
|
+
gap: 0.5rem;
|
|
453
|
+
padding: 0.5rem;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.month-item,
|
|
457
|
+
.year-item {
|
|
458
|
+
display: flex;
|
|
459
|
+
align-items: center;
|
|
460
|
+
justify-content: center;
|
|
461
|
+
border: none;
|
|
462
|
+
background: none;
|
|
463
|
+
cursor: pointer;
|
|
464
|
+
border-radius: 4px;
|
|
465
|
+
font-size: 0.875rem;
|
|
466
|
+
color: #333;
|
|
467
|
+
padding: 0.5rem;
|
|
468
|
+
min-height: 2.5rem;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.month-item:hover:not(.disabled),
|
|
472
|
+
.year-item:hover:not(.disabled) {
|
|
473
|
+
background-color: #f5f5f5;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.month-item.selected,
|
|
477
|
+
.year-item.selected {
|
|
478
|
+
background-color: #4a90e2;
|
|
479
|
+
color: white;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.month-item.disabled,
|
|
483
|
+
.year-item.disabled {
|
|
484
|
+
color: #ccc;
|
|
485
|
+
cursor: not-allowed;
|
|
486
|
+
}
|
|
487
|
+
|
|
405
488
|
.weekday {
|
|
406
489
|
text-align: center;
|
|
407
490
|
font-size: 0.875rem;
|
|
@@ -442,18 +525,29 @@ const timezoneDisplay = computed(() => {
|
|
|
442
525
|
}
|
|
443
526
|
|
|
444
527
|
.timezone-display {
|
|
445
|
-
margin-left: 0.5rem;
|
|
446
528
|
color: #666;
|
|
447
529
|
font-size: 0.875rem;
|
|
530
|
+
text-align: center;
|
|
448
531
|
}
|
|
449
532
|
|
|
450
533
|
.time-picker {
|
|
451
534
|
display: flex;
|
|
535
|
+
flex-direction: column;
|
|
536
|
+
gap: 1rem;
|
|
537
|
+
border-left: 1px solid #ddd;
|
|
538
|
+
width: 100px;
|
|
452
539
|
align-items: center;
|
|
453
|
-
gap: 0.5rem;
|
|
454
540
|
}
|
|
455
541
|
|
|
456
|
-
.time-
|
|
457
|
-
|
|
542
|
+
.time-input-group {
|
|
543
|
+
display: flex;
|
|
544
|
+
align-items: center;
|
|
545
|
+
gap: 0.25rem;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.time-input-group span {
|
|
549
|
+
font-size: 1.25rem;
|
|
550
|
+
color: #666;
|
|
551
|
+
padding: 0 0.25rem;
|
|
458
552
|
}
|
|
459
553
|
</style>
|