@bagelink/vue 0.0.1262 → 0.0.1268
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/AddressSearch.vue.d.ts +6 -0
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/DropDown.vue.d.ts +51 -48
- package/dist/components/DropDown.vue.d.ts.map +1 -1
- package/dist/components/form/FieldArray.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts +4 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/PasswordInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RadioGroup.vue.d.ts +1 -1
- package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +31 -23
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts +2 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/config.d.ts +2 -1
- package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/commands.d.ts +1 -0
- package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/media.d.ts +5 -3
- package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +12 -0
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +8 -2
- package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
- package/dist/editor-7QC0nG_c.js +4 -0
- package/dist/editor-CpMNx6Eo.cjs +4 -0
- package/dist/index.cjs +1327 -756
- package/dist/index.mjs +1327 -756
- package/dist/style.css +90 -83
- package/package.json +1 -1
- package/src/components/DataTable/DataTable.vue +1 -1
- package/src/components/Dropdown.vue +5 -2
- package/src/components/form/FieldArray.vue +3 -0
- package/src/components/form/inputs/DateInput.vue +341 -162
- package/src/components/form/inputs/PasswordInput.vue +5 -1
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +2 -2
- package/src/components/form/inputs/RichText/composables/useCommands.ts +53 -97
- package/src/components/form/inputs/RichText/composables/useEditor.ts +377 -270
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +124 -58
- package/src/components/form/inputs/RichText/config.ts +27 -3
- package/src/components/form/inputs/RichText/editor.css +29 -0
- package/src/components/form/inputs/RichText/index.vue +129 -55
- package/src/components/form/inputs/RichText/richTextTypes.d.ts +35 -49
- package/src/components/form/inputs/RichText/utils/commands.ts +181 -0
- package/src/components/form/inputs/RichText/utils/media.ts +64 -3
- package/src/components/form/inputs/RichText/utils/selection.ts +40 -5
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { modeType } from '../../Calendar/typings/types'
|
|
3
3
|
import { Btn, NumberInput, Dropdown, TextInput } from '@bagelink/vue'
|
|
4
|
+
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
|
|
4
5
|
import Time, { WEEK_START_DAY } from '../../Calendar/helpers/Time'
|
|
5
6
|
|
|
7
|
+
// Types
|
|
8
|
+
type ViewMode = 'days' | 'months' | 'years'
|
|
9
|
+
|
|
10
|
+
// Props
|
|
6
11
|
const props = withDefaults(
|
|
7
12
|
defineProps<{
|
|
8
13
|
required?: boolean
|
|
@@ -18,7 +23,6 @@ const props = withDefaults(
|
|
|
18
23
|
firstDayOfWeek?: WEEK_START_DAY
|
|
19
24
|
locale?: string
|
|
20
25
|
center?: boolean
|
|
21
|
-
|
|
22
26
|
}>(),
|
|
23
27
|
{
|
|
24
28
|
enableTime: false,
|
|
@@ -26,224 +30,395 @@ const props = withDefaults(
|
|
|
26
30
|
small: false,
|
|
27
31
|
timezone: 'UTC',
|
|
28
32
|
mode: 'day',
|
|
29
|
-
firstDayOfWeek: WEEK_START_DAY.
|
|
33
|
+
firstDayOfWeek: WEEK_START_DAY.SUNDAY,
|
|
30
34
|
locale: ''
|
|
31
35
|
},
|
|
32
36
|
)
|
|
33
37
|
|
|
34
38
|
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')
|
|
39
39
|
|
|
40
|
+
// State
|
|
41
|
+
const isOpen = ref(false)
|
|
42
|
+
const currentMonth = ref(new Date())
|
|
43
|
+
const currentView = ref<ViewMode>('days')
|
|
44
|
+
|
|
45
|
+
// Time helper instance
|
|
40
46
|
const time = new Time(props.firstDayOfWeek, props.locale)
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
48
|
+
// Date formatting composable
|
|
49
|
+
function useFormatting() {
|
|
50
|
+
const formatDisplayDate = (date: Date | string | undefined): string => {
|
|
51
|
+
if (!date) return ''
|
|
52
|
+
const dateObj = typeof date === 'string' ? new Date(date) : date
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
return dateObj.toLocaleString(props.locale || undefined, {
|
|
54
|
+
const options: Intl.DateTimeFormatOptions = {
|
|
48
55
|
year: 'numeric',
|
|
49
56
|
month: 'short',
|
|
50
57
|
day: 'numeric',
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
...(props.enableTime && {
|
|
59
|
+
hour: '2-digit',
|
|
60
|
+
minute: '2-digit',
|
|
61
|
+
}),
|
|
53
62
|
timeZone: props.timezone
|
|
54
|
-
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return dateObj.toLocaleString(props.locale || undefined, options)
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
68
|
+
const formatDate = (date: Date | string | undefined): string => {
|
|
69
|
+
if (!date) return ''
|
|
70
|
+
const dateObj = typeof date === 'string' ? new Date(date) : date
|
|
71
|
+
return props.enableTime
|
|
72
|
+
? dateObj.toISOString().slice(0, 16)
|
|
73
|
+
: dateObj.toISOString().split('T')[0]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const parseUserInput = (input: string): Date | null => {
|
|
77
|
+
// Try parsing various date formats
|
|
78
|
+
const date = new Date(input)
|
|
79
|
+
if (!Number.isNaN(date.getTime())) {
|
|
80
|
+
return date
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Try DD/MM/YYYY format
|
|
84
|
+
const parts = input.split(/[/.-]/)
|
|
85
|
+
if (parts.length === 3) {
|
|
86
|
+
const [day, month, year] = parts.map(p => Number.parseInt(p, 10))
|
|
87
|
+
if (!Number.isNaN(day) && !Number.isNaN(month) && !Number.isNaN(year)) {
|
|
88
|
+
const date = new Date(year, month - 1, day)
|
|
89
|
+
if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
|
|
90
|
+
return date
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
64
97
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
98
|
+
return {
|
|
99
|
+
formatDisplayDate,
|
|
100
|
+
formatDate,
|
|
101
|
+
parseUserInput
|
|
102
|
+
}
|
|
69
103
|
}
|
|
70
104
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
105
|
+
// Date validation composable
|
|
106
|
+
function useDateValidation() {
|
|
107
|
+
const isDateDisabled = (date: Date | null) => {
|
|
108
|
+
if (!date) return true
|
|
109
|
+
const minDate = props.min ? new Date(props.min) : null
|
|
110
|
+
const maxDate = props.max ? new Date(props.max) : null
|
|
74
111
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
112
|
+
if (minDate && date < minDate) return true
|
|
113
|
+
if (maxDate && date > maxDate) return true
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
79
116
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return time.getCalendarMonthSplitInWeeks(year, month).flat()
|
|
84
|
-
})
|
|
117
|
+
const isYearDisabled = (year: number) => {
|
|
118
|
+
const minDate = props.min ? new Date(props.min) : null
|
|
119
|
+
const maxDate = props.max ? new Date(props.max) : null
|
|
85
120
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
formatted: {
|
|
90
|
-
month: time.getLocalizedNameOfMonth(currentMonth, 'long'),
|
|
91
|
-
year: time.getLocalizedDateString(currentMonth).split('/').pop() // Get just the year part
|
|
121
|
+
if (minDate && year < minDate.getFullYear()) return true
|
|
122
|
+
if (maxDate && year > maxDate.getFullYear()) return true
|
|
123
|
+
return false
|
|
92
124
|
}
|
|
93
|
-
}))
|
|
94
125
|
|
|
95
|
-
const months = $computed(() => Array.from({ length: 12 }, (_, i) => {
|
|
96
|
-
const date = new Date(currentMonthValue.year, i, 1)
|
|
97
126
|
return {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
isDateDisabled,
|
|
128
|
+
isYearDisabled
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Date state composable
|
|
133
|
+
function useDateState() {
|
|
134
|
+
const { formatDisplayDate, formatDate } = useFormatting()
|
|
135
|
+
|
|
136
|
+
const formattedDisplayValue = computed(() => formatDisplayDate(props.modelValue))
|
|
137
|
+
const formattedMin = computed(() => formatDate(props.min))
|
|
138
|
+
const formattedMax = computed(() => formatDate(props.max))
|
|
139
|
+
const selectedDate = computed(() => {
|
|
140
|
+
if (!props.modelValue) return null
|
|
141
|
+
return typeof props.modelValue === 'string' ? new Date(props.modelValue) : props.modelValue
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
formattedDisplayValue,
|
|
146
|
+
formattedMin,
|
|
147
|
+
formattedMax,
|
|
148
|
+
selectedDate
|
|
101
149
|
}
|
|
102
|
-
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Initialize core composables first
|
|
153
|
+
const { isDateDisabled, isYearDisabled } = useDateValidation()
|
|
154
|
+
const { formattedDisplayValue, formattedMin, formattedMax, selectedDate } = useDateState()
|
|
103
155
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
156
|
+
// Calendar view composable
|
|
157
|
+
function useCalendarView() {
|
|
158
|
+
const currentMonthDays = computed(() => {
|
|
159
|
+
const year = currentMonth.value.getFullYear()
|
|
160
|
+
const month = currentMonth.value.getMonth()
|
|
161
|
+
return time.getCalendarMonthSplitInWeeks(year, month).flat()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const currentMonthValue = computed(() => ({
|
|
165
|
+
month: currentMonth.value.getMonth(),
|
|
166
|
+
year: currentMonth.value.getFullYear(),
|
|
167
|
+
formatted: {
|
|
168
|
+
month: time.getLocalizedNameOfMonth(currentMonth.value, 'long'),
|
|
169
|
+
year: time.getLocalizedDateString(currentMonth.value).split('/').pop()
|
|
170
|
+
}
|
|
109
171
|
}))
|
|
110
|
-
})
|
|
111
172
|
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
173
|
+
const months = computed(() => Array.from({ length: 12 }, (_, i) => {
|
|
174
|
+
const date = new Date(currentMonthValue.value.year, i, 1)
|
|
175
|
+
return {
|
|
176
|
+
name: time.getLocalizedNameOfMonth(date, 'short'),
|
|
177
|
+
value: i,
|
|
178
|
+
disabled: isDateDisabled(date)
|
|
179
|
+
}
|
|
180
|
+
}))
|
|
181
|
+
|
|
182
|
+
const years = computed(() => {
|
|
183
|
+
const startYear = currentMonthValue.value.year - 10
|
|
184
|
+
return Array.from({ length: 21 }, (_, i) => ({
|
|
185
|
+
value: startYear + i,
|
|
186
|
+
disabled: isYearDisabled(startYear + i)
|
|
187
|
+
}))
|
|
119
188
|
})
|
|
120
|
-
})
|
|
121
189
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
190
|
+
const weekDays = computed(() => {
|
|
191
|
+
const weekStart = new Date()
|
|
192
|
+
weekStart.setDate(weekStart.getDate() - weekStart.getDay()
|
|
193
|
+
+ (props.firstDayOfWeek === WEEK_START_DAY.MONDAY ? 1 : 0))
|
|
194
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
195
|
+
const day = new Date(weekStart)
|
|
196
|
+
day.setDate(weekStart.getDate() + i)
|
|
197
|
+
return time.getLocalizedNameOfWeekday(day, 'short')
|
|
198
|
+
})
|
|
199
|
+
})
|
|
126
200
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
201
|
+
const isSelected = (date: Date | null) => {
|
|
202
|
+
if (!date || !selectedDate.value) return false
|
|
203
|
+
return date.getFullYear() === selectedDate.value.getFullYear()
|
|
204
|
+
&& date.getMonth() === selectedDate.value.getMonth()
|
|
205
|
+
&& date.getDate() === selectedDate.value.getDate()
|
|
206
|
+
}
|
|
131
207
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
208
|
+
const isToday = (date: Date | null) => {
|
|
209
|
+
if (!date) return false
|
|
210
|
+
return time.dateIsToday(date)
|
|
211
|
+
}
|
|
135
212
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
213
|
+
const isNotInMonth = (date: Date) => {
|
|
214
|
+
return time.isTrailingOrLeadingDate(date, currentMonth.value.getMonth())
|
|
215
|
+
}
|
|
140
216
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
217
|
+
return {
|
|
218
|
+
currentMonthDays,
|
|
219
|
+
currentMonthValue,
|
|
220
|
+
months,
|
|
221
|
+
years,
|
|
222
|
+
weekDays,
|
|
223
|
+
isSelected,
|
|
224
|
+
isToday,
|
|
225
|
+
isNotInMonth
|
|
226
|
+
}
|
|
144
227
|
}
|
|
145
228
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
229
|
+
// Navigation handlers
|
|
230
|
+
function useNavigation() {
|
|
231
|
+
const selectMonth = (monthIndex: number) => {
|
|
232
|
+
currentMonth.value = new Date(currentMonth.value.getFullYear(), monthIndex, 1)
|
|
233
|
+
currentView.value = 'days'
|
|
234
|
+
}
|
|
150
235
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
236
|
+
const selectYear = (year: number) => {
|
|
237
|
+
currentMonth.value = new Date(year, currentMonth.value.getMonth(), 1)
|
|
238
|
+
currentView.value = 'months'
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const previousMonth = () => {
|
|
242
|
+
currentMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() - 1, 1)
|
|
243
|
+
}
|
|
154
244
|
|
|
155
|
-
|
|
156
|
-
|
|
245
|
+
const nextMonth = () => {
|
|
246
|
+
currentMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 1)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const previousYear = () => {
|
|
250
|
+
const offset = currentView.value === 'months' ? 1 : 21
|
|
251
|
+
currentMonth.value = new Date(currentMonth.value.getFullYear() - offset, currentMonth.value.getMonth(), 1)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const nextYear = () => {
|
|
255
|
+
const offset = currentView.value === 'months' ? 1 : 21
|
|
256
|
+
currentMonth.value = new Date(currentMonth.value.getFullYear() + offset, currentMonth.value.getMonth(), 1)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
selectMonth,
|
|
261
|
+
selectYear,
|
|
262
|
+
previousMonth,
|
|
263
|
+
nextMonth,
|
|
264
|
+
previousYear,
|
|
265
|
+
nextYear
|
|
266
|
+
}
|
|
157
267
|
}
|
|
158
268
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
269
|
+
// Time handling composable
|
|
270
|
+
function useTimeHandling() {
|
|
271
|
+
const hours = computed(() => selectedDate.value?.getHours() ?? 0)
|
|
272
|
+
const minutes = computed(() => selectedDate.value?.getMinutes() ?? 0)
|
|
273
|
+
|
|
274
|
+
const handleHourInput = (value: number) => {
|
|
275
|
+
if (!selectedDate.value) return
|
|
276
|
+
const newDate = new Date(selectedDate.value)
|
|
277
|
+
newDate.setHours(value)
|
|
278
|
+
emit('update:modelValue', newDate.toISOString())
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const handleMinuteInput = (value: number) => {
|
|
282
|
+
if (!selectedDate.value) return
|
|
283
|
+
const newDate = new Date(selectedDate.value)
|
|
284
|
+
newDate.setMinutes(value)
|
|
285
|
+
emit('update:modelValue', newDate.toISOString())
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const timezoneDisplay = computed(() => {
|
|
289
|
+
if (!props.enableTime) return ''
|
|
290
|
+
try {
|
|
291
|
+
return new Date().toLocaleString('en-US', {
|
|
292
|
+
timeZoneName: 'short',
|
|
293
|
+
timeZone: props.timezone
|
|
294
|
+
}).split(' ').pop()
|
|
295
|
+
} catch {
|
|
296
|
+
return 'UTC'
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
hours,
|
|
302
|
+
minutes,
|
|
303
|
+
handleHourInput,
|
|
304
|
+
handleMinuteInput,
|
|
305
|
+
timezoneDisplay
|
|
306
|
+
}
|
|
162
307
|
}
|
|
163
308
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
309
|
+
// Input handling composable
|
|
310
|
+
function useInputHandling() {
|
|
311
|
+
const { parseUserInput } = useFormatting()
|
|
312
|
+
const inputValue = ref('')
|
|
313
|
+
|
|
314
|
+
const handleInput = (event: Event) => {
|
|
315
|
+
const input = event.target as HTMLInputElement
|
|
316
|
+
inputValue.value = input.value
|
|
317
|
+
|
|
318
|
+
const date = parseUserInput(input.value)
|
|
319
|
+
if (date) {
|
|
320
|
+
if (props.enableTime) {
|
|
321
|
+
emit('update:modelValue', date.toISOString())
|
|
322
|
+
} else {
|
|
323
|
+
const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
|
|
324
|
+
emit('update:modelValue', utcDate.toISOString().split('T')[0])
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const handleFocus = (e: FocusEvent) => {
|
|
330
|
+
e.preventDefault()
|
|
331
|
+
e.stopPropagation()
|
|
332
|
+
isOpen.value = true
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const handleClick = (e: MouseEvent) => {
|
|
336
|
+
e.preventDefault()
|
|
337
|
+
e.stopPropagation()
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const handleKeydown = (event: KeyboardEvent) => {
|
|
341
|
+
if (event.key === 'Escape') {
|
|
342
|
+
isOpen.value = false
|
|
343
|
+
} else if (event.key === 'Enter' && inputValue.value) {
|
|
344
|
+
const date = parseUserInput(inputValue.value)
|
|
345
|
+
if (date) {
|
|
346
|
+
if (props.enableTime) {
|
|
347
|
+
emit('update:modelValue', date.toISOString())
|
|
348
|
+
} else {
|
|
349
|
+
const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
|
|
350
|
+
emit('update:modelValue', utcDate.toISOString().split('T')[0])
|
|
351
|
+
}
|
|
352
|
+
isOpen.value = false
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
inputValue,
|
|
359
|
+
handleInput,
|
|
360
|
+
handleFocus,
|
|
361
|
+
handleClick,
|
|
362
|
+
handleKeydown
|
|
363
|
+
}
|
|
167
364
|
}
|
|
168
365
|
|
|
366
|
+
// Initialize input handling
|
|
367
|
+
const { inputValue, handleInput, handleFocus, handleClick, handleKeydown } = useInputHandling()
|
|
368
|
+
|
|
369
|
+
// Initialize remaining composables
|
|
370
|
+
const { currentMonthDays, currentMonthValue, months, years, weekDays, isSelected, isToday, isNotInMonth } = useCalendarView()
|
|
371
|
+
const { selectMonth, selectYear, previousMonth, nextMonth, previousYear, nextYear } = useNavigation()
|
|
372
|
+
const { hours, minutes, handleHourInput, handleMinuteInput, timezoneDisplay } = useTimeHandling()
|
|
373
|
+
|
|
374
|
+
// Date selection handler
|
|
169
375
|
function selectDate(date: Date | null) {
|
|
170
376
|
if (!date || !props.editMode) return
|
|
171
377
|
|
|
172
|
-
// Create date
|
|
378
|
+
// Create date in the target timezone
|
|
379
|
+
const tzOffset = new Date().getTimezoneOffset()
|
|
173
380
|
const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
|
174
381
|
|
|
175
|
-
// If time is enabled, preserve existing time or set to current time
|
|
176
382
|
if (props.enableTime) {
|
|
177
|
-
const currentHours = selectedDate?.getHours() ?? new Date().getHours()
|
|
178
|
-
const currentMinutes = selectedDate?.getMinutes() ?? new Date().getMinutes()
|
|
383
|
+
const currentHours = selectedDate.value?.getHours() ?? new Date().getHours()
|
|
384
|
+
const currentMinutes = selectedDate.value?.getMinutes() ?? new Date().getMinutes()
|
|
179
385
|
newDate.setHours(currentHours)
|
|
180
386
|
newDate.setMinutes(currentMinutes)
|
|
387
|
+
|
|
388
|
+
// Adjust for timezone when time is enabled
|
|
389
|
+
if (props.timezone === 'UTC') {
|
|
390
|
+
newDate.setMinutes(newDate.getMinutes() + tzOffset)
|
|
391
|
+
}
|
|
181
392
|
emit('update:modelValue', newDate.toISOString())
|
|
182
393
|
} else {
|
|
183
|
-
// For
|
|
184
|
-
|
|
185
|
-
emit('update:modelValue',
|
|
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
|
|
394
|
+
// For date-only selection, ensure we're working with UTC midnight
|
|
395
|
+
const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
|
|
396
|
+
emit('update:modelValue', utcDate.toISOString().split('T')[0])
|
|
397
|
+
isOpen.value = false
|
|
195
398
|
}
|
|
196
|
-
|
|
197
|
-
const date = new Date(input.value)
|
|
198
|
-
emit('update:modelValue', props.enableTime ? date.toISOString() : date.toISOString().split('T')[0])
|
|
199
399
|
}
|
|
200
400
|
|
|
201
|
-
const
|
|
202
|
-
const
|
|
401
|
+
const datePickerRef = ref<HTMLElement | null>(null)
|
|
402
|
+
const calendarRef = ref<HTMLElement | null>(null)
|
|
203
403
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
}
|
|
404
|
+
onMounted(() => {
|
|
405
|
+
const handleDocumentClick = (e: MouseEvent) => {
|
|
406
|
+
const target = e.target as HTMLElement
|
|
407
|
+
if (!datePickerRef.value?.contains(target) && !calendarRef.value?.contains(target)) {
|
|
408
|
+
isOpen.value = false
|
|
409
|
+
}
|
|
410
|
+
}
|
|
222
411
|
|
|
223
|
-
|
|
224
|
-
if (!date) return false
|
|
225
|
-
return time.dateIsToday(date)
|
|
226
|
-
}
|
|
412
|
+
document.addEventListener('click', handleDocumentClick)
|
|
227
413
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return new Date().toLocaleString('en-US', {
|
|
232
|
-
timeZoneName: 'short',
|
|
233
|
-
timeZone: props.timezone
|
|
234
|
-
}).split(' ').pop()
|
|
235
|
-
} catch {
|
|
236
|
-
return 'UTC'
|
|
237
|
-
}
|
|
414
|
+
onBeforeUnmount(() => {
|
|
415
|
+
document.removeEventListener('click', handleDocumentClick)
|
|
416
|
+
})
|
|
238
417
|
})
|
|
239
|
-
|
|
240
|
-
function isNotInMonth(date: Date) {
|
|
241
|
-
return time.isTrailingOrLeadingDate(date, currentMonth.getMonth())
|
|
242
|
-
}
|
|
243
418
|
</script>
|
|
244
419
|
|
|
245
420
|
<template>
|
|
246
|
-
<div class="bagel-input" :class="{ small }" :title="label">
|
|
421
|
+
<div class="bagel-input" :class="{ small }" :title="label" @focusin="handleFocus">
|
|
247
422
|
<label v-if="label">
|
|
248
423
|
{{ label }}
|
|
249
424
|
<span v-if="required" class="required">*</span>
|
|
@@ -252,11 +427,11 @@ function isNotInMonth(date: Date) {
|
|
|
252
427
|
<Dropdown
|
|
253
428
|
:shown="isOpen"
|
|
254
429
|
placement="bottom-start"
|
|
255
|
-
|
|
256
|
-
|
|
430
|
+
:autoHide="false"
|
|
431
|
+
:triggers="['click']"
|
|
257
432
|
>
|
|
258
433
|
<template #trigger>
|
|
259
|
-
<div class="date-picker-container">
|
|
434
|
+
<div ref="datePickerRef" class="date-picker-container" @mousedown.stop @click.stop>
|
|
260
435
|
<TextInput
|
|
261
436
|
:modelValue="formattedDisplayValue"
|
|
262
437
|
icon="calendar"
|
|
@@ -266,14 +441,18 @@ function isNotInMonth(date: Date) {
|
|
|
266
441
|
:disabled="!editMode"
|
|
267
442
|
class="date-input"
|
|
268
443
|
:class="{
|
|
269
|
-
'txt-center': center
|
|
270
|
-
|
|
271
|
-
|
|
444
|
+
'txt-center': center,
|
|
445
|
+
}"
|
|
446
|
+
:readonly="false"
|
|
447
|
+
@input="handleInput"
|
|
448
|
+
@focus="handleFocus"
|
|
449
|
+
@click="handleClick"
|
|
450
|
+
@keydown="handleKeydown"
|
|
272
451
|
/>
|
|
273
452
|
</div>
|
|
274
453
|
</template>
|
|
275
454
|
|
|
276
|
-
<div class="flex gap-075 p-05 m_flex-wrap calendar-container justify-content-center h-100p">
|
|
455
|
+
<div ref="calendarRef" class="ltr flex gap-075 p-05 m_flex-wrap calendar-container justify-content-center h-100p" @click.stop>
|
|
277
456
|
<div class="calendar-section m_border-none pe-05 m_p-0">
|
|
278
457
|
<div class="flex space-between pb-1">
|
|
279
458
|
<template v-if="currentView === 'days'">
|
|
@@ -43,7 +43,7 @@ const inputType = $computed(() => (showPwd.value ? 'text' : 'password'))
|
|
|
43
43
|
</script>
|
|
44
44
|
|
|
45
45
|
<template>
|
|
46
|
-
<div class="relative">
|
|
46
|
+
<div class="relative passwordInput">
|
|
47
47
|
<TextInput
|
|
48
48
|
v-model="password"
|
|
49
49
|
v-bind="props"
|
|
@@ -66,4 +66,8 @@ const inputType = $computed(() => (showPwd.value ? 'text' : 'password'))
|
|
|
66
66
|
.m-password {
|
|
67
67
|
height: var(--input-height) !important;
|
|
68
68
|
}
|
|
69
|
+
[dir='rtl'] .passwordInput input {
|
|
70
|
+
direction: ltr;
|
|
71
|
+
text-align: right;
|
|
72
|
+
}
|
|
69
73
|
</style>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { ToolbarConfig, ToolbarConfigOption, ToolbarOption } from '../richTextTypes'
|
|
3
3
|
import { Btn, Dropdown } from '@bagelink/vue'
|
|
4
|
-
import {
|
|
4
|
+
import { basicToolbarConfig, toolbarOptions } from '../config'
|
|
5
5
|
import GridBox from './gridBox.vue'
|
|
6
6
|
|
|
7
|
-
const { config =
|
|
7
|
+
const { config = basicToolbarConfig, selectedStyles } = defineProps<{
|
|
8
8
|
config?: ToolbarConfig
|
|
9
9
|
selectedStyles: Set<string>
|
|
10
10
|
}>()
|