@bagelink/vue 0.0.1256 → 0.0.1260
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/Carousel.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts +1 -0
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts +13 -7
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- 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/OTP.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +8 -0
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TextInput.vue.d.ts +2 -0
- package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
- package/dist/index.cjs +975 -625
- package/dist/index.mjs +975 -625
- package/dist/style.css +859 -604
- package/dist/utils/BagelFormUtils.d.ts +2 -2
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Carousel.vue +1 -2
- package/src/components/Icon/Icon.vue +2 -2
- package/src/components/Modal.vue +2 -1
- package/src/components/form/BagelForm.vue +15 -13
- package/src/components/form/inputs/DateInput.vue +434 -55
- package/src/components/form/inputs/NumberInput.vue +10 -2
- package/src/components/form/inputs/OTP.vue +2 -3
- package/src/components/form/inputs/TextInput.vue +4 -0
- package/src/styles/layout.css +121 -0
- package/src/styles/mobilLayout.css +121 -0
- package/src/styles/text.css +565 -562
- package/src/utils/BagelFormUtils.ts +1 -1
|
@@ -3,7 +3,7 @@ interface InputOptions {
|
|
|
3
3
|
required?: boolean;
|
|
4
4
|
placeholder?: string;
|
|
5
5
|
class?: string;
|
|
6
|
-
defaultValue?: string;
|
|
6
|
+
defaultValue?: string | number;
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
helptext?: string;
|
|
9
9
|
vIf?: boolean | ((item: any, row: any) => boolean);
|
|
@@ -69,7 +69,7 @@ export declare function uploadField(id: string, label?: string, options?: Upload
|
|
|
69
69
|
required?: boolean;
|
|
70
70
|
placeholder?: string;
|
|
71
71
|
class?: string;
|
|
72
|
-
defaultValue?: string;
|
|
72
|
+
defaultValue?: string | number;
|
|
73
73
|
disabled?: boolean;
|
|
74
74
|
helptext?: string;
|
|
75
75
|
vIf?: boolean | ((item: any, row: any) => boolean);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BagelFormUtils.d.ts","sourceRoot":"","sources":["../../src/utils/BagelFormUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAI/E,UAAU,YAAY;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"BagelFormUtils.d.ts","sourceRoot":"","sources":["../../src/utils/BagelFormUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -571,7 +571,7 @@ watch(() => activeSlideIndex, handleSlideChange)
|
|
|
571
571
|
<div
|
|
572
572
|
ref="bglSlider"
|
|
573
573
|
class="bgl-slider"
|
|
574
|
-
:class="{ dragging: isDragging, clicking: isPressed, [`slides-${itemCount}`]: true }"
|
|
574
|
+
:class="{ dragging: isDragging, clicking: isPressed, [`slides-${itemCount}`]: true, grab: freeDrag && slideCount > 1 }"
|
|
575
575
|
:style="{ '--item-count': itemCount }"
|
|
576
576
|
@mousedown="startDrag"
|
|
577
577
|
@mouseover="clearAutoplay"
|
|
@@ -615,7 +615,6 @@ watch(() => activeSlideIndex, handleSlideChange)
|
|
|
615
615
|
will-change: transform;
|
|
616
616
|
transform: translateX(0);
|
|
617
617
|
gap: 1%;
|
|
618
|
-
cursor: grab;
|
|
619
618
|
transition: none;
|
|
620
619
|
-webkit-user-select: none;
|
|
621
620
|
user-select: none;
|
|
@@ -29,9 +29,9 @@ const isFaBrand = $computed(() => FONT_AWESOME_BRANDS_ICONS.includes(iconRender)
|
|
|
29
29
|
>
|
|
30
30
|
{{ iconRender }}
|
|
31
31
|
</span>
|
|
32
|
-
<
|
|
32
|
+
<span
|
|
33
33
|
v-else-if="iconRenderType === 'font-awesome'"
|
|
34
|
-
class="fa far" :class="[`fa-${iconRender}`, { 'fa-brands': isFaBrand }]"
|
|
34
|
+
class="fa far bgl_icon-font" :class="[`fa-${iconRender}`, { 'fa-brands': isFaBrand }]"
|
|
35
35
|
:style="{ 'fontSize': `${size}rem`, color, 'font-variation-settings': `'wght' ${weight || 400}` }"
|
|
36
36
|
/>
|
|
37
37
|
</template>
|
package/src/components/Modal.vue
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import '../styles/modal.css'
|
|
17
17
|
|
|
18
18
|
const props = defineProps<{
|
|
19
|
+
thin?: boolean
|
|
19
20
|
side?: boolean
|
|
20
21
|
title?: string
|
|
21
22
|
width?: string
|
|
@@ -74,7 +75,7 @@ onUnmounted(() => {
|
|
|
74
75
|
@click="() => (dismissable ? closeModal() : '')"
|
|
75
76
|
@keydown.esc="closeModal"
|
|
76
77
|
>
|
|
77
|
-
<Card class="modal" :style="{ ...maxWidth }" @click.stop>
|
|
78
|
+
<Card class="modal" :style="{ ...maxWidth }" :thin="thin" @click.stop>
|
|
78
79
|
<header v-if="slots.toolbar || title" class="tool-bar">
|
|
79
80
|
<slot name="toolbar" />
|
|
80
81
|
<Btn
|
|
@@ -36,7 +36,6 @@ const formData = ref<T>(safeClone(props.modelValue ?? {}) as T)
|
|
|
36
36
|
const initialFormData = ref<T>(safeClone(props.modelValue ?? {}) as T)
|
|
37
37
|
|
|
38
38
|
onMounted(() => {
|
|
39
|
-
console.log('formData.value')
|
|
40
39
|
if (props.modelValue) {
|
|
41
40
|
initialFormData.value = safeClone(props.modelValue)
|
|
42
41
|
}
|
|
@@ -107,8 +106,9 @@ async function handleSubmit() {
|
|
|
107
106
|
initialFormData.value = safeClone(formData.value)
|
|
108
107
|
formState.value = 'success'
|
|
109
108
|
// Notify parent window of successful submission
|
|
110
|
-
window.parent.postMessage({ type: 'BAGEL_FORM_SUCCESS', data: formData.value }, '*')
|
|
109
|
+
window.parent.postMessage({ type: 'BAGEL_FORM_SUCCESS', data: JSON.stringify(formData.value) }, '*')
|
|
111
110
|
} catch (error) {
|
|
111
|
+
console.error('Form submission error:', error)
|
|
112
112
|
formState.value = 'error'
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -142,20 +142,22 @@ defineExpose({ form, isDirty, validateForm })
|
|
|
142
142
|
</script>
|
|
143
143
|
|
|
144
144
|
<template>
|
|
145
|
-
<
|
|
146
|
-
<
|
|
145
|
+
<template v-if="formState !== 'success' || !$slots.success">
|
|
146
|
+
<form v-if="props.tag === 'form'" ref="form" @submit.prevent="handleSubmit">
|
|
147
|
+
<template v-if="resolvedSchema">
|
|
148
|
+
<template v-for="field in (resolvedSchema as Field[])" :key="field.id">
|
|
149
|
+
<component :is="renderSchemaField(field)" />
|
|
150
|
+
</template>
|
|
151
|
+
</template>
|
|
152
|
+
<slot v-else />
|
|
153
|
+
<slot name="submit" :submit="handleSubmit" :isDirty="isDirty" :validateForm="validateForm" :formState="formState" />
|
|
154
|
+
</form>
|
|
155
|
+
<template v-else>
|
|
147
156
|
<template v-for="field in (resolvedSchema as Field[])" :key="field.id">
|
|
148
157
|
<component :is="renderSchemaField(field)" />
|
|
149
158
|
</template>
|
|
150
159
|
</template>
|
|
151
|
-
<slot v-else />
|
|
152
|
-
<slot name="submit" :submit="handleSubmit" :isDirty="isDirty" :validateForm="validateForm" :formState="formState" />
|
|
153
|
-
<slot v-if="formState === 'success'" name="success" />
|
|
154
|
-
<slot v-if="formState === 'error'" name="error" />
|
|
155
|
-
</form>
|
|
156
|
-
<template v-else>
|
|
157
|
-
<template v-for="field in (resolvedSchema as Field[])" :key="field.id">
|
|
158
|
-
<component :is="renderSchemaField(field)" />
|
|
159
|
-
</template>
|
|
160
160
|
</template>
|
|
161
|
+
<slot v-if="formState === 'success'" name="success" />
|
|
162
|
+
<slot v-if="formState === 'error'" name="error" />
|
|
161
163
|
</template>
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import '@vuepic/vue-datepicker/dist/main.css'
|
|
2
|
+
import type { modeType } from '../../Calendar/typings/types'
|
|
3
|
+
import { Btn, NumberInput, Dropdown, TextInput } from '@bagelink/vue'
|
|
4
|
+
import Time, { WEEK_START_DAY } from '../../Calendar/helpers/Time'
|
|
7
5
|
|
|
8
6
|
const props = withDefaults(
|
|
9
7
|
defineProps<{
|
|
@@ -13,84 +11,465 @@ const props = withDefaults(
|
|
|
13
11
|
small?: boolean
|
|
14
12
|
enableTime?: boolean
|
|
15
13
|
modelValue?: string | Date
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
min?: string | Date
|
|
15
|
+
max?: string | Date
|
|
16
|
+
timezone?: string
|
|
17
|
+
mode?: modeType
|
|
18
|
+
firstDayOfWeek?: WEEK_START_DAY
|
|
19
|
+
locale?: string
|
|
20
|
+
center?: boolean
|
|
21
|
+
|
|
22
22
|
}>(),
|
|
23
23
|
{
|
|
24
24
|
enableTime: false,
|
|
25
25
|
editMode: true,
|
|
26
26
|
small: false,
|
|
27
|
+
timezone: 'UTC',
|
|
28
|
+
mode: 'day',
|
|
29
|
+
firstDayOfWeek: WEEK_START_DAY.MONDAY,
|
|
30
|
+
locale: ''
|
|
27
31
|
},
|
|
28
32
|
)
|
|
29
33
|
|
|
30
34
|
const emit = defineEmits(['update:modelValue'])
|
|
35
|
+
let isOpen = $ref(false)
|
|
36
|
+
let currentMonth = $ref(new Date())
|
|
37
|
+
type ViewMode = 'days' | 'months' | 'years'
|
|
38
|
+
let currentView = $ref<ViewMode>('days')
|
|
31
39
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
const time = new Time(props.firstDayOfWeek, props.locale)
|
|
41
|
+
|
|
42
|
+
function formatDisplayDate(date: Date | string | undefined): string {
|
|
43
|
+
if (!date) return ''
|
|
44
|
+
const dateObj = typeof date === 'string' ? new Date(date) : date
|
|
45
|
+
|
|
46
|
+
if (props.enableTime) {
|
|
47
|
+
return dateObj.toLocaleString(props.locale || undefined, {
|
|
48
|
+
year: 'numeric',
|
|
49
|
+
month: 'short',
|
|
50
|
+
day: 'numeric',
|
|
51
|
+
hour: '2-digit',
|
|
52
|
+
minute: '2-digit',
|
|
53
|
+
timeZone: props.timezone
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return dateObj.toLocaleString(props.locale || undefined, {
|
|
58
|
+
year: 'numeric',
|
|
59
|
+
month: 'short',
|
|
60
|
+
day: 'numeric',
|
|
61
|
+
timeZone: props.timezone
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function formatDate(date: Date | string | undefined): string {
|
|
66
|
+
if (!date) return ''
|
|
67
|
+
const dateObj = typeof date === 'string' ? new Date(date) : date
|
|
68
|
+
return props.enableTime ? dateObj.toISOString().slice(0, 16) : dateObj.toISOString().split('T')[0]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const formattedDisplayValue = $computed(() => formatDisplayDate(props.modelValue))
|
|
72
|
+
const formattedMin = $computed(() => formatDate(props.min))
|
|
73
|
+
const formattedMax = $computed(() => formatDate(props.max))
|
|
74
|
+
|
|
75
|
+
const selectedDate = $computed(() => {
|
|
76
|
+
if (!props.modelValue) return null
|
|
77
|
+
return typeof props.modelValue === 'string' ? new Date(props.modelValue) : props.modelValue
|
|
44
78
|
})
|
|
45
79
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
|
|
80
|
+
const currentMonthDays = $computed(() => {
|
|
81
|
+
const year = currentMonth.getFullYear()
|
|
82
|
+
const month = currentMonth.getMonth()
|
|
83
|
+
return time.getCalendarMonthSplitInWeeks(year, month).flat()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const currentMonthValue = $computed(() => ({
|
|
87
|
+
month: currentMonth.getMonth(),
|
|
88
|
+
year: currentMonth.getFullYear(),
|
|
89
|
+
formatted: {
|
|
90
|
+
month: time.getLocalizedNameOfMonth(currentMonth, 'long'),
|
|
91
|
+
year: time.getLocalizedDateString(currentMonth).split('/').pop() // Get just the year part
|
|
92
|
+
}
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
const months = $computed(() => Array.from({ length: 12 }, (_, i) => {
|
|
96
|
+
const date = new Date(currentMonthValue.year, i, 1)
|
|
97
|
+
return {
|
|
98
|
+
name: time.getLocalizedNameOfMonth(date, 'short'),
|
|
99
|
+
value: i,
|
|
100
|
+
disabled: isDateDisabled(date)
|
|
51
101
|
}
|
|
102
|
+
}))
|
|
103
|
+
|
|
104
|
+
const years = $computed(() => {
|
|
105
|
+
const startYear = currentMonthValue.year - 10
|
|
106
|
+
return Array.from({ length: 21 }, (_, i) => ({
|
|
107
|
+
value: startYear + i,
|
|
108
|
+
disabled: isYearDisabled(startYear + i)
|
|
109
|
+
}))
|
|
52
110
|
})
|
|
111
|
+
|
|
112
|
+
const weekDays = $computed(() => {
|
|
113
|
+
const weekStart = new Date()
|
|
114
|
+
weekStart.setDate(weekStart.getDate() - weekStart.getDay() + (props.firstDayOfWeek === WEEK_START_DAY.MONDAY ? 1 : 0))
|
|
115
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
116
|
+
const day = new Date(weekStart)
|
|
117
|
+
day.setDate(weekStart.getDate() + i)
|
|
118
|
+
return time.getLocalizedNameOfWeekday(day, 'short')
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
function isDateDisabled(date: Date | null) {
|
|
123
|
+
if (!date) return true
|
|
124
|
+
const minDate = props.min ? new Date(props.min) : null
|
|
125
|
+
const maxDate = props.max ? new Date(props.max) : null
|
|
126
|
+
|
|
127
|
+
if (minDate && date < minDate) return true
|
|
128
|
+
if (maxDate && date > maxDate) return true
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function isYearDisabled(year: number) {
|
|
133
|
+
const minDate = props.min ? new Date(props.min) : null
|
|
134
|
+
const maxDate = props.max ? new Date(props.max) : null
|
|
135
|
+
|
|
136
|
+
if (minDate && year < minDate.getFullYear()) return true
|
|
137
|
+
if (maxDate && year > maxDate.getFullYear()) return true
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function selectMonth(monthIndex: number) {
|
|
142
|
+
currentMonth = new Date(currentMonth.getFullYear(), monthIndex, 1)
|
|
143
|
+
currentView = 'days'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function selectYear(year: number) {
|
|
147
|
+
currentMonth = new Date(year, currentMonth.getMonth(), 1)
|
|
148
|
+
currentView = 'months'
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function previousMonth() {
|
|
152
|
+
currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function nextMonth() {
|
|
156
|
+
currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function previousYear() {
|
|
160
|
+
const offset = currentView === 'months' ? 1 : 21
|
|
161
|
+
currentMonth = new Date(currentMonth.getFullYear() - offset, currentMonth.getMonth(), 1)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function nextYear() {
|
|
165
|
+
const offset = currentView === 'months' ? 1 : 21
|
|
166
|
+
currentMonth = new Date(currentMonth.getFullYear() + offset, currentMonth.getMonth(), 1)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function selectDate(date: Date | null) {
|
|
170
|
+
if (!date || !props.editMode) return
|
|
171
|
+
|
|
172
|
+
// Create date at start of day in the target timezone
|
|
173
|
+
const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
|
174
|
+
|
|
175
|
+
// If time is enabled, preserve existing time or set to current time
|
|
176
|
+
if (props.enableTime) {
|
|
177
|
+
const currentHours = selectedDate?.getHours() ?? new Date().getHours()
|
|
178
|
+
const currentMinutes = selectedDate?.getMinutes() ?? new Date().getMinutes()
|
|
179
|
+
newDate.setHours(currentHours)
|
|
180
|
+
newDate.setMinutes(currentMinutes)
|
|
181
|
+
emit('update:modelValue', newDate.toISOString())
|
|
182
|
+
} else {
|
|
183
|
+
// For dates without time, ensure time is set to midnight UTC
|
|
184
|
+
newDate.setUTCHours(0, 0, 0, 0)
|
|
185
|
+
emit('update:modelValue', newDate.toISOString().split('T')[0])
|
|
186
|
+
isOpen = false
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function handleInput(event: Event) {
|
|
191
|
+
const input = event.target as HTMLInputElement
|
|
192
|
+
if (!input.value) {
|
|
193
|
+
emit('update:modelValue', '')
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const date = new Date(input.value)
|
|
198
|
+
emit('update:modelValue', props.enableTime ? date.toISOString() : date.toISOString().split('T')[0])
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const hours = $computed(() => selectedDate?.getHours() ?? 0)
|
|
202
|
+
const minutes = $computed(() => selectedDate?.getMinutes() ?? 0)
|
|
203
|
+
|
|
204
|
+
function handleHourInput(value: number) {
|
|
205
|
+
if (!selectedDate) return
|
|
206
|
+
const newDate = new Date(selectedDate)
|
|
207
|
+
newDate.setHours(value)
|
|
208
|
+
emit('update:modelValue', newDate.toISOString())
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function handleMinuteInput(value: number) {
|
|
212
|
+
if (!selectedDate) return
|
|
213
|
+
const newDate = new Date(selectedDate)
|
|
214
|
+
newDate.setMinutes(value)
|
|
215
|
+
emit('update:modelValue', newDate.toISOString())
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function isSelected(date: Date | null) {
|
|
219
|
+
if (!date || !selectedDate) return false
|
|
220
|
+
return date.toISOString().split('T')[0] === selectedDate.toISOString().split('T')[0]
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function isToday(date: Date | null) {
|
|
224
|
+
if (!date) return false
|
|
225
|
+
return time.dateIsToday(date)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const timezoneDisplay = $computed(() => {
|
|
229
|
+
if (!props.enableTime) return ''
|
|
230
|
+
try {
|
|
231
|
+
return new Date().toLocaleString('en-US', {
|
|
232
|
+
timeZoneName: 'short',
|
|
233
|
+
timeZone: props.timezone
|
|
234
|
+
}).split(' ').pop()
|
|
235
|
+
} catch {
|
|
236
|
+
return 'UTC'
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
function isNotInMonth(date: Date) {
|
|
241
|
+
return time.isTrailingOrLeadingDate(date, currentMonth.getMonth())
|
|
242
|
+
}
|
|
53
243
|
</script>
|
|
54
244
|
|
|
55
245
|
<template>
|
|
56
|
-
<div
|
|
57
|
-
class="bagel-input"
|
|
58
|
-
:title="label"
|
|
59
|
-
:class="{ small }"
|
|
60
|
-
>
|
|
246
|
+
<div class="bagel-input" :class="{ small }" :title="label">
|
|
61
247
|
<label v-if="label">
|
|
62
248
|
{{ label }}
|
|
249
|
+
<span v-if="required" class="required">*</span>
|
|
63
250
|
</label>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
251
|
+
|
|
252
|
+
<Dropdown
|
|
253
|
+
:shown="isOpen"
|
|
254
|
+
placement="bottom-start"
|
|
255
|
+
@apply-show="isOpen = true"
|
|
256
|
+
@apply-hide="isOpen = false"
|
|
257
|
+
>
|
|
258
|
+
<template #trigger>
|
|
259
|
+
<div class="date-picker-container">
|
|
260
|
+
<TextInput
|
|
261
|
+
:modelValue="formattedDisplayValue"
|
|
262
|
+
icon="calendar"
|
|
263
|
+
:min="formattedMin"
|
|
264
|
+
:max="formattedMax"
|
|
265
|
+
:required="required"
|
|
266
|
+
:disabled="!editMode"
|
|
267
|
+
class="date-input"
|
|
268
|
+
:class="{
|
|
269
|
+
'txt-center': center }"
|
|
270
|
+
readonly
|
|
271
|
+
@click="isOpen = true"
|
|
272
|
+
/>
|
|
273
|
+
</div>
|
|
274
|
+
</template>
|
|
275
|
+
|
|
276
|
+
<div class="flex gap-075 p-05 m_flex-wrap calendar-container justify-content-center h-100p">
|
|
277
|
+
<div class="calendar-section m_border-none pe-05 m_p-0">
|
|
278
|
+
<div class="flex space-between pb-1">
|
|
279
|
+
<template v-if="currentView === 'days'">
|
|
280
|
+
<Btn flat icon="chevron_left" @click="previousMonth" />
|
|
281
|
+
<div class="flex gap-05">
|
|
282
|
+
<Btn flat class="month-btn" @click="currentView = 'months'">
|
|
283
|
+
{{ currentMonthValue.formatted.month }}
|
|
284
|
+
</Btn>
|
|
285
|
+
<Btn flat class="year-btn" @click="currentView = 'years'">
|
|
286
|
+
{{ currentMonthValue.formatted.year }}
|
|
287
|
+
</Btn>
|
|
288
|
+
</div>
|
|
289
|
+
<Btn flat icon="chevron_right" @click="nextMonth" />
|
|
290
|
+
</template>
|
|
291
|
+
<template v-else>
|
|
292
|
+
<Btn flat icon="chevron_left" @click="previousYear" />
|
|
293
|
+
<span class="month-year">{{ currentMonthValue.formatted.year }}</span>
|
|
294
|
+
<Btn flat icon="chevron_right" @click="nextYear" />
|
|
295
|
+
</template>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div v-if="currentView === 'days'" class="calendar-grid grid gap-025">
|
|
299
|
+
<div
|
|
300
|
+
v-for="day in weekDays"
|
|
301
|
+
:key="day"
|
|
302
|
+
class="txt-center txt-12 opacity-6"
|
|
303
|
+
>
|
|
304
|
+
{{ day }}
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<button
|
|
308
|
+
v-for="date in currentMonthDays"
|
|
309
|
+
:key="date?.toISOString()"
|
|
310
|
+
type="button"
|
|
311
|
+
class="day aspect-ratio-1 flex align-items-center justify-content-center pointer round txt14 p-0"
|
|
312
|
+
:class="{
|
|
313
|
+
'selected': isSelected(date),
|
|
314
|
+
'today': isToday(date),
|
|
315
|
+
'disabled': isDateDisabled(date),
|
|
316
|
+
'not-in-month': isNotInMonth(date),
|
|
317
|
+
}"
|
|
318
|
+
:disabled="isDateDisabled(date)"
|
|
319
|
+
@click="selectDate(date)"
|
|
320
|
+
>
|
|
321
|
+
{{ date?.getDate() }}
|
|
322
|
+
</button>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<div v-else-if="currentView === 'months'" class="month-grid grid gap-05 p-05">
|
|
326
|
+
<button
|
|
327
|
+
v-for="month in months"
|
|
328
|
+
:key="month.value"
|
|
329
|
+
class="month-item flex align-items-center justify-content-center pointer rounded p-05 txt14 border-none"
|
|
330
|
+
:class="{
|
|
331
|
+
selected: month.value === currentMonthValue.month,
|
|
332
|
+
disabled: month.disabled,
|
|
333
|
+
}"
|
|
334
|
+
:disabled="month.disabled"
|
|
335
|
+
@click="selectMonth(month.value)"
|
|
336
|
+
>
|
|
337
|
+
{{ month.name }}
|
|
338
|
+
</button>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<div v-else class="year-grid grid gap-05 p-0">
|
|
342
|
+
<button
|
|
343
|
+
v-for="year in years"
|
|
344
|
+
:key="year.value"
|
|
345
|
+
class="year-item flex align-items-center justify-content-center pointer rounded p-05 txt14 border-none"
|
|
346
|
+
:class="{
|
|
347
|
+
selected: year.value === currentMonthValue.year,
|
|
348
|
+
disabled: year.disabled,
|
|
349
|
+
}"
|
|
350
|
+
:disabled="year.disabled"
|
|
351
|
+
@click="selectYear(year.value)"
|
|
352
|
+
>
|
|
353
|
+
{{ year.value }}
|
|
354
|
+
</button>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
<div v-if="enableTime && currentView === 'days'" class="time-picker border-start flex column gap-1 w-120px">
|
|
359
|
+
<div class="flex gap-025">
|
|
360
|
+
<NumberInput
|
|
361
|
+
center
|
|
362
|
+
:model-value="hours"
|
|
363
|
+
:disabled="!selectedDate"
|
|
364
|
+
:min="0"
|
|
365
|
+
:max="23"
|
|
366
|
+
layout="vertical"
|
|
367
|
+
:padZero="2"
|
|
368
|
+
@update:model-value="handleHourInput"
|
|
369
|
+
/>
|
|
370
|
+
<p class="pb-075">
|
|
371
|
+
:
|
|
372
|
+
</p>
|
|
373
|
+
<NumberInput
|
|
374
|
+
center
|
|
375
|
+
:model-value="minutes"
|
|
376
|
+
:disabled="!selectedDate"
|
|
377
|
+
:min="0"
|
|
378
|
+
:max="59"
|
|
379
|
+
:padZero="2"
|
|
380
|
+
layout="vertical"
|
|
381
|
+
@update:model-value="handleMinuteInput"
|
|
382
|
+
/>
|
|
383
|
+
</div>
|
|
384
|
+
<span class="txt-center opacity-6 txt14">{{ timezoneDisplay }}</span>
|
|
385
|
+
<Btn v-if="selectedDate" flat @click="isOpen = false">
|
|
386
|
+
Done
|
|
387
|
+
</Btn>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
</Dropdown>
|
|
77
391
|
</div>
|
|
78
392
|
</template>
|
|
79
393
|
|
|
80
|
-
<style>
|
|
81
|
-
.
|
|
82
|
-
|
|
394
|
+
<style scoped>
|
|
395
|
+
.calendar-container {
|
|
396
|
+
max-width: 90vw;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.calendar-section {
|
|
400
|
+
min-width: 280px;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.calendar-grid {
|
|
404
|
+
grid-template-columns: repeat(7, 1fr);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.month-grid {
|
|
408
|
+
grid-template-columns: repeat(3, 1fr);
|
|
409
|
+
grid-template-rows: repeat(4, 1fr);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.year-grid {
|
|
413
|
+
grid-template-columns: repeat(3, 1fr);
|
|
414
|
+
grid-template-rows: repeat(7, 1fr);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.month-item,
|
|
418
|
+
.year-item {
|
|
419
|
+
background: none;
|
|
420
|
+
color: var(--bgl-text-color);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.month-item:hover:not(.disabled),
|
|
424
|
+
.year-item:hover:not(.disabled) {
|
|
425
|
+
background: var(--bgl-box-bg);
|
|
426
|
+
filter: var(--bgl-hover-filter);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.month-item:active:not(.disabled),
|
|
430
|
+
.year-item:active:not(.disabled) {
|
|
431
|
+
background: var(--bgl-box-bg);
|
|
432
|
+
filter: var(--bgl-active-filter);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.month-item.selected,
|
|
436
|
+
.year-item.selected {
|
|
437
|
+
background-color: var(--bgl-primary);
|
|
438
|
+
color: white;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.month-item.disabled,
|
|
442
|
+
.year-item.disabled {
|
|
443
|
+
opacity: 0.6;
|
|
444
|
+
filter: grayscale(0.3);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.day {
|
|
448
|
+
border: none;
|
|
449
|
+
background: none;
|
|
450
|
+
color: var(--bgl-text-color);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.day:hover:not(.disabled) {
|
|
454
|
+
background-color: var(--input-bg);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.day.selected {
|
|
458
|
+
background-color: var(--bgl-primary);
|
|
459
|
+
color: var(--bgl-white);
|
|
83
460
|
}
|
|
84
461
|
|
|
85
|
-
.
|
|
86
|
-
|
|
462
|
+
.day.today:not(.selected) {
|
|
463
|
+
border: 1px solid var(--bgl-primary);
|
|
87
464
|
}
|
|
88
465
|
|
|
89
|
-
.
|
|
90
|
-
|
|
466
|
+
.day.disabled {
|
|
467
|
+
opacity: 0.6;
|
|
468
|
+
filter: grayscale(0.3);
|
|
469
|
+
cursor: not-allowed;
|
|
91
470
|
}
|
|
92
471
|
|
|
93
|
-
.
|
|
94
|
-
|
|
472
|
+
.day.not-in-month {
|
|
473
|
+
opacity: 0.4;
|
|
95
474
|
}
|
|
96
475
|
</style>
|