@bagelink/vue 0.0.1256 → 0.0.1258
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/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 +943 -598
- package/dist/index.mjs +943 -598
- package/dist/style.css +849 -593
- 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/Icon/Icon.vue +2 -2
- package/src/components/Modal.vue +2 -1
- package/src/components/form/BagelForm.vue +0 -1
- 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
|
@@ -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
|
}
|
|
@@ -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>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { IconType } from '@bagelink/vue'
|
|
3
3
|
import { Icon, Btn } from '@bagelink/vue'
|
|
4
|
-
import { watch } from 'vue'
|
|
4
|
+
import { onMounted, watch } from 'vue'
|
|
5
5
|
|
|
6
6
|
type NumberLayout = 'default' | 'vertical' | 'horizontal'
|
|
7
7
|
|
|
@@ -14,6 +14,7 @@ interface NumberInputProps {
|
|
|
14
14
|
iconStart?: IconType
|
|
15
15
|
icon?: IconType
|
|
16
16
|
label?: string
|
|
17
|
+
defaultValue?: number
|
|
17
18
|
placeholder?: string
|
|
18
19
|
disabled?: boolean
|
|
19
20
|
required?: boolean
|
|
@@ -40,11 +41,18 @@ const {
|
|
|
40
41
|
layout,
|
|
41
42
|
id,
|
|
42
43
|
padZero = 1,
|
|
44
|
+
defaultValue
|
|
43
45
|
} = defineProps<NumberInputProps>()
|
|
44
46
|
|
|
45
47
|
const emit = defineEmits(['update:modelValue'])
|
|
46
48
|
|
|
47
|
-
let numberValue = $ref(
|
|
49
|
+
let numberValue = $ref<number>()
|
|
50
|
+
|
|
51
|
+
onMounted(() => {
|
|
52
|
+
const num = modelValue !== undefined ? Number.parseFloat(`${modelValue}`) : undefined
|
|
53
|
+
const defaultNum = defaultValue !== undefined ? Number.parseFloat(`${defaultValue}`) : undefined
|
|
54
|
+
numberValue = num || defaultNum
|
|
55
|
+
})
|
|
48
56
|
|
|
49
57
|
const btnLayouts: NumberLayout[] = ['horizontal', 'vertical']
|
|
50
58
|
|
|
@@ -23,10 +23,9 @@ function handlePaste(event: ClipboardEvent, index: number) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function emitUpdate() {
|
|
26
|
+
emit('update:modelValue', digits.join(''))
|
|
26
27
|
if (isDigitsFull()) {
|
|
27
28
|
emit('complete', digits.join(''))
|
|
28
|
-
} else {
|
|
29
|
-
emit('update:modelValue', digits.join(''))
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
|
|
@@ -93,7 +92,7 @@ function isDigitsFull() {
|
|
|
93
92
|
:autofocus="ind === 0"
|
|
94
93
|
maxlength="1"
|
|
95
94
|
pattern="[0-9]*"
|
|
96
|
-
oninput="this.value = this.value.slice(0, 1)
|
|
95
|
+
oninput="this.value = this.value.slice(0, 1)"
|
|
97
96
|
@keydown="handleKeyDown($event, ind)"
|
|
98
97
|
@paste="handlePaste($event, ind)"
|
|
99
98
|
>
|