@bagelink/vue 1.14.13 → 1.15.0
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 -7
- package/dist/components/Alert.vue.d.ts.map +1 -1
- package/dist/components/Avatar.vue.d.ts.map +1 -1
- package/dist/components/Badge.vue.d.ts.map +1 -1
- package/dist/components/Btn.vue.d.ts +1 -1
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/Card.vue.d.ts.map +1 -1
- package/dist/components/Carousel.vue.d.ts +0 -11
- package/dist/components/Dropdown.vue.d.ts +0 -2
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/Filter.vue.d.ts +30 -0
- package/dist/components/Filter.vue.d.ts.map +1 -0
- package/dist/components/FilterQuery.vue.d.ts +8 -3
- package/dist/components/Image.vue.d.ts.map +1 -1
- package/dist/components/ImportData.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/MapEmbed/Index.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts +0 -1
- package/dist/components/Pagination.vue.d.ts.map +1 -1
- package/dist/components/Pill.vue.d.ts.map +1 -1
- package/dist/components/QueryFilter.vue.d.ts +30 -0
- package/dist/components/QueryFilter.vue.d.ts.map +1 -0
- package/dist/components/Swiper.vue.d.ts +6 -12
- package/dist/components/Swiper.vue.d.ts.map +1 -1
- package/dist/components/Toast.vue.d.ts.map +1 -1
- package/dist/components/analytics/PieChart.vue.d.ts +2 -2
- package/dist/components/calendar/CalendarPopover.vue.d.ts +8 -4
- package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/calendar/CalendarTypes.d.ts +0 -10
- package/dist/components/calendar/Index.vue.d.ts +4 -20
- package/dist/components/calendar/views/WeekView.vue.d.ts +1 -9
- package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/inputs/ArrayInput.vue.d.ts +2 -4
- package/dist/components/form/inputs/CheckInput.vue.d.ts +1 -2
- package/dist/components/form/inputs/Checkbox.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts +0 -54
- package/dist/components/form/inputs/ColorInput.vue.d.ts +1 -3
- package/dist/components/form/inputs/DateInput.vue.d.ts +1 -2
- package/dist/components/form/inputs/DatePicker.vue.d.ts +0 -1
- package/dist/components/form/inputs/EmailInput.vue.d.ts +2 -5
- package/dist/components/form/inputs/JSONInput.vue.d.ts +1 -2
- package/dist/components/form/inputs/MarkdownEditor.vue.d.ts +2 -7
- package/dist/components/form/inputs/NumberInput.vue.d.ts +1 -2
- package/dist/components/form/inputs/OTP.vue.d.ts +1 -2
- package/dist/components/form/inputs/PasswordInput.vue.d.ts +10 -16
- package/dist/components/form/inputs/RadioGroup.vue.d.ts +1 -3
- package/dist/components/form/inputs/RangeInput.vue.d.ts +1 -6
- package/dist/components/form/inputs/RichText/index.vue.d.ts +1 -2
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectBtn.vue.d.ts +2 -2
- package/dist/components/form/inputs/SelectInput.vue.d.ts +13 -20
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SignaturePad.vue.d.ts +1 -6
- package/dist/components/form/inputs/TableField.vue.d.ts +1 -2
- package/dist/components/form/inputs/TelInput.vue.d.ts +1 -2
- package/dist/components/form/inputs/TextInput.vue.d.ts +2 -3
- package/dist/components/form/inputs/ToggleInput.vue.d.ts +1 -2
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts +6 -27
- package/dist/components/form/inputs/Upload/upload.d.ts +1 -1
- package/dist/components/form/inputs/index.d.ts +0 -1
- package/dist/components/index.d.ts +1 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts +1 -1
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.vue.d.ts +0 -2
- package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts +1 -5
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/layout/Panel.vue.d.ts.map +1 -1
- package/dist/components/layout/Resizable.vue.d.ts.map +1 -1
- package/dist/components/layout/Skeleton.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsNav.vue.d.ts +1 -12
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
- package/dist/components/layout/appLayoutContext.d.ts +24 -0
- package/dist/components/layout/appLayoutContext.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useDevice.d.ts.map +1 -1
- package/dist/composables/useEscapeKey.d.ts +12 -0
- package/dist/composables/useEscapeKey.d.ts.map +1 -0
- package/dist/composables/useSchemaField.d.ts.map +1 -1
- package/dist/composables/useTheme.d.ts.map +1 -1
- package/dist/dialog/Dialog.vue.d.ts.map +1 -1
- package/dist/dialog/DialogConfirm.vue.d.ts.map +1 -1
- package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
- package/dist/form-flow/MultiStepForm.vue.d.ts +1 -6
- package/dist/form-flow/form-flow.d.ts +1 -24
- package/dist/form-flow/form-flow.d.ts.map +1 -1
- package/dist/i18n/index.d.ts +0 -838
- package/dist/index.cjs +245 -222
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +42201 -51162
- package/dist/plugins/bagel.d.ts.map +1 -1
- package/dist/style.css +1 -2
- package/dist/types/BagelForm.d.ts +1 -10
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/types/BtnOptions.d.ts.map +1 -1
- package/dist/types/NavLink.d.ts +1 -2
- package/dist/types/TableSchema.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/BagelFormUtils.d.ts +0 -1
- package/dist/utils/calendar/dateUtils.d.ts +2 -2
- package/dist/utils/calendar/dateUtils.d.ts.map +1 -1
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/date.d.ts +116 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/fetch.d.ts +29 -0
- package/dist/utils/fetch.d.ts.map +1 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/string.d.ts +7 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/useSearch.d.ts +1 -1
- package/package.json +3 -10
- package/src/components/AccordionItem.vue +5 -5
- package/src/components/Alert.vue +37 -16
- package/src/components/Avatar.vue +2 -1
- package/src/components/Badge.vue +145 -22
- package/src/components/BglVideo.vue +4 -4
- package/src/components/Btn.vue +81 -69
- package/src/components/Card.vue +7 -6
- package/src/components/Dropdown.vue +7 -14
- package/src/components/FieldSetVue.vue +2 -2
- package/src/components/FilterQuery.vue +3 -3
- package/src/components/Image.vue +5 -3
- package/src/components/JSONSchema.vue +4 -4
- package/src/components/JsonBuilder.vue +3 -3
- package/src/components/ListItem.vue +2 -4
- package/src/components/MapEmbed/Index.vue +18 -17
- package/src/components/NavBar.vue +2 -2
- package/src/components/Spreadsheet/Index.vue +4 -4
- package/src/components/Spreadsheet/SpreadsheetTable.vue +10 -10
- package/src/components/Swiper.vue +3 -1
- package/src/components/Toast.vue +57 -36
- package/src/components/calendar/CalendarPopover.vue +1 -1
- package/src/components/calendar/Index.vue +5 -5
- package/src/components/calendar/views/AgendaView.vue +2 -2
- package/src/components/calendar/views/DayView.vue +1 -1
- package/src/components/calendar/views/MonthView.vue +8 -8
- package/src/components/dataTable/DataTable.vue +68 -10
- package/src/components/form/index.ts +0 -4
- package/src/components/form/inputs/ArrayInput.vue +1 -1
- package/src/components/form/inputs/CheckInput.vue +6 -6
- package/src/components/form/inputs/Checkbox.vue +5 -4
- package/src/components/form/inputs/CodeEditor/Index.vue +1 -1
- package/src/components/form/inputs/ColorInput.vue +5 -5
- package/src/components/form/inputs/DatePicker.vue +3 -3
- package/src/components/form/inputs/EmailInput.vue +15 -15
- package/src/components/form/inputs/NumberInput.vue +11 -11
- package/src/components/form/inputs/OTP.vue +4 -4
- package/src/components/form/inputs/PasswordInput.vue +3 -3
- package/src/components/form/inputs/RadioGroup.vue +1 -1
- package/src/components/form/inputs/RichText/editor.css +4 -4
- package/src/components/form/inputs/RichText/index.vue +39 -39
- package/src/components/form/inputs/RichText/utils/media.ts +1 -92
- package/src/components/form/inputs/RichText/utils/table.ts +4 -4
- package/src/components/form/inputs/SelectBtn.vue +1 -1
- package/src/components/form/inputs/SelectInput.vue +16 -16
- package/src/components/form/inputs/SignaturePad.vue +6 -6
- package/src/components/form/inputs/TableField.vue +7 -7
- package/src/components/form/inputs/TelInput.vue +12 -12
- package/src/components/form/inputs/TextInput.vue +11 -11
- package/src/components/form/inputs/ToggleInput.vue +11 -11
- package/src/components/form/inputs/Upload/upload.css +16 -16
- package/src/components/index.ts +2 -9
- package/src/components/layout/AppContent.vue +5 -19
- package/src/components/layout/AppLayout.vue +47 -18
- package/src/components/layout/AppSidebar.vue +19 -36
- package/src/components/layout/BottomMenu.vue +1 -1
- package/src/components/layout/Resizable.vue +5 -2
- package/src/components/layout/Skeleton.vue +5 -4
- package/src/components/layout/TabsNav.vue +23 -23
- package/src/components/layout/appLayoutContext.ts +44 -0
- package/src/components/layout/index.ts +2 -0
- package/src/components/lightbox/Lightbox.vue +3 -9
- package/src/composables/index.ts +1 -0
- package/src/composables/useDevice.ts +2 -1
- package/src/composables/useEscapeKey.ts +56 -0
- package/src/composables/useSchemaField.ts +2 -17
- package/src/composables/useTheme.ts +23 -19
- package/src/form-flow/FormFlow.vue +2 -0
- package/src/form-flow/form-flow.ts +7 -0
- package/src/index.ts +0 -3
- package/src/plugins/bagel.ts +0 -15
- package/src/styles/app-layout.css +231 -0
- package/src/styles/appearance.css +179 -21
- package/src/styles/bagel.css +103 -97
- package/src/styles/buttons.css +8 -8
- package/src/styles/colors.css +0 -103
- package/src/styles/dark.css +25 -26
- package/src/styles/input-variants.css +11 -11
- package/src/styles/inputs.css +44 -61
- package/src/styles/layout.css +445 -1258
- package/src/styles/loginCard.css +1 -1
- package/src/styles/mobilLayout.css +153 -28
- package/src/styles/text.css +500 -1508
- package/src/styles/theme.css +199 -435
- package/src/styles/transitions.css +4 -4
- package/src/types/BagelForm.ts +46 -151
- package/src/types/BtnOptions.ts +5 -3
- package/src/types/TableSchema.ts +1 -0
- package/src/types/index.ts +0 -5
- package/src/utils/calendar/dateUtils.ts +2 -3
- package/src/utils/constants.ts +7 -0
- package/src/utils/date.ts +482 -0
- package/src/utils/fetch.ts +128 -0
- package/src/utils/index.ts +54 -3
- package/src/utils/sizeParsing.ts +5 -5
- package/src/utils/string.ts +56 -0
- package/vite.config.ts +5 -1
- package/bin/generateFormSchema.ts +0 -1035
- package/bin/utils.ts +0 -223
- package/src/components/Carousel.vue +0 -724
- package/src/components/ImportData.vue +0 -1749
- package/src/components/Modal.vue +0 -184
- package/src/components/ModalConfirm.vue +0 -42
- package/src/components/ModalForm.vue +0 -102
- package/src/components/Pill.vue +0 -149
- package/src/components/Slider.vue +0 -1446
- package/src/components/Title.vue +0 -23
- package/src/components/ToolBar.vue +0 -9
- package/src/components/form/BagelForm.vue +0 -219
- package/src/components/form/BglFieldSet.vue +0 -14
- package/src/components/form/BglMultiStepForm.vue +0 -469
- package/src/components/form/FieldArray.vue +0 -422
- package/src/components/form/useBagelFormState.ts +0 -76
- package/src/composables/useFormField.ts +0 -38
- package/src/dialog/DialogOLD.vue +0 -358
- package/src/plugins/modalTypes.ts +0 -61
- package/src/plugins/useModal.ts +0 -225
- package/src/styles/modal.css +0 -120
- package/src/styles/pillColors.css +0 -0
- package/src/utils/BagelFormUtils.ts +0 -684
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { getI18n } from '../i18n'
|
|
2
|
+
|
|
3
|
+
function getLocale(): string {
|
|
4
|
+
try {
|
|
5
|
+
const { locale } = getI18n().global
|
|
6
|
+
return typeof locale === 'string' ? locale : locale.value
|
|
7
|
+
} catch {
|
|
8
|
+
if (typeof navigator === 'undefined') return 'en-US'
|
|
9
|
+
return (navigator.languages && navigator.languages.length)
|
|
10
|
+
? navigator.languages[0]
|
|
11
|
+
: navigator.language
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export type DateInput = Date | number | string
|
|
18
|
+
|
|
19
|
+
// ─── Date convention ────────────────────────────────────────────────────────
|
|
20
|
+
//
|
|
21
|
+
// Server → Client: UTC (defaults to UTC when no TZ info present)
|
|
22
|
+
// Client → Server: Local time with TZ offset, OR UTC with Z suffix
|
|
23
|
+
// Server accepts: Anything with TZ info; defaults to UTC when absent
|
|
24
|
+
// Client accepts: Anything with TZ info; defaults to UTC when absent
|
|
25
|
+
// Display: Always device-local time (browser's Intl / Date methods)
|
|
26
|
+
//
|
|
27
|
+
// ── utc() — parse server dates ──────────────────────────────────────────────
|
|
28
|
+
//
|
|
29
|
+
// utc(input) → Date (interpreted as UTC when no TZ info)
|
|
30
|
+
// utc.iso(input) → normalized ISO string with Z
|
|
31
|
+
// utc.date(input) → "YYYY-MM-DD" in UTC
|
|
32
|
+
// utc.ms(input) → epoch milliseconds
|
|
33
|
+
//
|
|
34
|
+
// Handles:
|
|
35
|
+
// • Missing "Z" suffix or TZ offset → assumes UTC
|
|
36
|
+
// • Space instead of "T" → normalizes
|
|
37
|
+
// • Strings with explicit TZ offset → respects it
|
|
38
|
+
// • Already a Date / number → passes through
|
|
39
|
+
//
|
|
40
|
+
// ── local() — format for server submission ──────────────────────────────────
|
|
41
|
+
//
|
|
42
|
+
// local.iso(date) → ISO string with local TZ offset, e.g. "2026-05-05T11:00:00.000+03:00"
|
|
43
|
+
// local.offset() → current device TZ offset string, e.g. "+03:00"
|
|
44
|
+
//
|
|
45
|
+
|
|
46
|
+
function _parseUTC(input: DateInput): Date {
|
|
47
|
+
if (input instanceof Date) return input
|
|
48
|
+
if (typeof input === 'number') return new Date(input)
|
|
49
|
+
// Normalize: "2026-05-05 08:00:00" → "2026-05-05T08:00:00Z"
|
|
50
|
+
let s = input.trim().replace(' ', 'T')
|
|
51
|
+
// If no TZ info at all, assume UTC
|
|
52
|
+
if (!s.endsWith('Z') && !s.includes('+') && !/[+-]\d{2}:\d{2}$/.test(s)) s += 'Z'
|
|
53
|
+
return new Date(s)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const utc = Object.assign(_parseUTC, {
|
|
57
|
+
/** Parse and return normalized ISO string with Z */
|
|
58
|
+
iso(input: DateInput): string {
|
|
59
|
+
return _parseUTC(input).toISOString()
|
|
60
|
+
},
|
|
61
|
+
/** Parse and return UTC date-only string "YYYY-MM-DD" */
|
|
62
|
+
date(input: DateInput): string {
|
|
63
|
+
return _parseUTC(input).toISOString().slice(0, 10)
|
|
64
|
+
},
|
|
65
|
+
/** Parse and return epoch ms */
|
|
66
|
+
ms(input: DateInput): number {
|
|
67
|
+
return _parseUTC(input).getTime()
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// ── local — format dates with device TZ for server submission ───────────────
|
|
72
|
+
|
|
73
|
+
function _tzOffset(date: Date): string {
|
|
74
|
+
const off = -date.getTimezoneOffset()
|
|
75
|
+
const sign = off >= 0 ? '+' : '-'
|
|
76
|
+
const h = String(Math.floor(Math.abs(off) / 60)).padStart(2, '0')
|
|
77
|
+
const m = String(Math.abs(off) % 60).padStart(2, '0')
|
|
78
|
+
return `${sign}${h}:${m}`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function _localISO(date: Date): string {
|
|
82
|
+
const y = date.getFullYear()
|
|
83
|
+
const mo = String(date.getMonth() + 1).padStart(2, '0')
|
|
84
|
+
const d = String(date.getDate()).padStart(2, '0')
|
|
85
|
+
const h = String(date.getHours()).padStart(2, '0')
|
|
86
|
+
const mi = String(date.getMinutes()).padStart(2, '0')
|
|
87
|
+
const s = String(date.getSeconds()).padStart(2, '0')
|
|
88
|
+
const ms = String(date.getMilliseconds()).padStart(3, '0')
|
|
89
|
+
return `${y}-${mo}-${d}T${h}:${mi}:${s}.${ms}${_tzOffset(date)}`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const local = {
|
|
93
|
+
/** Format a Date as ISO string with local TZ offset, e.g. "2026-05-05T11:00:00.000+03:00" */
|
|
94
|
+
iso(date: Date = new Date()): string {
|
|
95
|
+
return _localISO(date)
|
|
96
|
+
},
|
|
97
|
+
/** Current device TZ offset string, e.g. "+03:00" or "-05:00" */
|
|
98
|
+
offset(date: Date = new Date()): string {
|
|
99
|
+
return _tzOffset(date)
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type TimeDeltaUnit = 'days' | 'hours' | 'minutes' | 'seconds' | 'weeks' | 'months' | 'years'
|
|
104
|
+
|
|
105
|
+
export interface TimeDeltaOptions {
|
|
106
|
+
days?: number
|
|
107
|
+
hours?: number
|
|
108
|
+
minutes?: number
|
|
109
|
+
seconds?: number
|
|
110
|
+
weeks?: number
|
|
111
|
+
months?: number
|
|
112
|
+
years?: number
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type DateDiffUnit = 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months'
|
|
116
|
+
|
|
117
|
+
export type DayFormatTypes = 'DD' | 'DDD' | 'DDDD'
|
|
118
|
+
export type MonthFormatTypes = 'MM' | 'MMM' | 'MMMM'
|
|
119
|
+
export type YearFormatTypes = 'YY' | 'YYYY'
|
|
120
|
+
export type HourFormatTypes = 'HH'
|
|
121
|
+
export type MinuteFormatTypes = 'mm'
|
|
122
|
+
export type SecondFormatTypes = 'ss'
|
|
123
|
+
export type MillisecondFormatTypes = 'sss'
|
|
124
|
+
export type AmPmFormatTypes = 'AmPm'
|
|
125
|
+
export type DateFormatSeparatorTypes = '/' | '-' | ' ' | ':' | '.'
|
|
126
|
+
|
|
127
|
+
export type CommonDateFormats
|
|
128
|
+
= | `${DayFormatTypes}${DateFormatSeparatorTypes}${MonthFormatTypes}${DateFormatSeparatorTypes}${YearFormatTypes}`
|
|
129
|
+
| `${MonthFormatTypes}${DateFormatSeparatorTypes}${YearFormatTypes}`
|
|
130
|
+
| 'DD.MM.YY' | 'DD.MM.YYYY' | 'DD/MM/YY' | 'DD/MM/YYYY'
|
|
131
|
+
| 'MM.DD.YY' | 'MM.DD.YYYY' | 'MM/DD/YY' | 'MM/DD/YYYY'
|
|
132
|
+
| 'YYYY-MM-DD' | 'YY-MM-DD'
|
|
133
|
+
| 'DD MMM YYYY' | 'DD MMMM YYYY'
|
|
134
|
+
| 'DDD, DD MMM' | 'DDDD, DD MMMM'
|
|
135
|
+
| 'MMM DD' | 'MMMM DD'
|
|
136
|
+
|
|
137
|
+
export type CommonTimeFormats
|
|
138
|
+
= | 'HH:mm' | 'HH:mm:ss' | 'HH:mm:ss:sss'
|
|
139
|
+
| 'HH:mm AmPm'
|
|
140
|
+
|
|
141
|
+
export type CommonDateTimeFormats
|
|
142
|
+
= | `${CommonDateFormats} ${CommonTimeFormats}`
|
|
143
|
+
| `${CommonTimeFormats}, ${CommonDateFormats}`
|
|
144
|
+
| 'YYYY-MM-DD HH:MM'
|
|
145
|
+
|
|
146
|
+
export type NamedFormats = 'ISO' | 'ISO8601' | 'UTC' | 'RFC2822' | 'RFC3339' | 'UNIX' | 'TIMESTAMP'
|
|
147
|
+
|
|
148
|
+
export type DateTimeAcceptedFormats
|
|
149
|
+
= | CommonDateFormats
|
|
150
|
+
| CommonTimeFormats
|
|
151
|
+
| CommonDateTimeFormats
|
|
152
|
+
| DayFormatTypes
|
|
153
|
+
| MonthFormatTypes
|
|
154
|
+
| YearFormatTypes
|
|
155
|
+
| HourFormatTypes
|
|
156
|
+
| MinuteFormatTypes
|
|
157
|
+
| SecondFormatTypes
|
|
158
|
+
| MillisecondFormatTypes
|
|
159
|
+
| NamedFormats
|
|
160
|
+
|
|
161
|
+
export interface FormatDateOptions extends Partial<Pick<Intl.DateTimeFormatOptions, 'hour12'>> {
|
|
162
|
+
format?: DateTimeAcceptedFormats
|
|
163
|
+
locale?: Intl.LocalesArgument
|
|
164
|
+
tz?: string
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ─── Time constants ────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
export const MS = {
|
|
170
|
+
SECOND: 1_000,
|
|
171
|
+
MINUTE: 60_000,
|
|
172
|
+
HOUR: 3_600_000,
|
|
173
|
+
DAY: 86_400_000,
|
|
174
|
+
WEEK: 604_800_000,
|
|
175
|
+
} as const
|
|
176
|
+
|
|
177
|
+
export const { SECOND } = MS
|
|
178
|
+
export const { MINUTE } = MS
|
|
179
|
+
export const { HOUR } = MS
|
|
180
|
+
export const { DAY } = MS
|
|
181
|
+
export const { WEEK } = MS
|
|
182
|
+
|
|
183
|
+
// ─── Core date arithmetic ──────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
export function timeDelta(date: DateInput, options: TimeDeltaOptions): Date {
|
|
186
|
+
const d = new Date(date)
|
|
187
|
+
if (options.days) d.setDate(d.getDate() + options.days)
|
|
188
|
+
if (options.hours) d.setHours(d.getHours() + options.hours)
|
|
189
|
+
if (options.minutes) d.setMinutes(d.getMinutes() + options.minutes)
|
|
190
|
+
if (options.seconds) d.setSeconds(d.getSeconds() + options.seconds)
|
|
191
|
+
if (options.weeks) d.setDate(d.getDate() + options.weeks * 7)
|
|
192
|
+
if (options.months) d.setMonth(d.getMonth() + options.months)
|
|
193
|
+
if (options.years) d.setFullYear(d.getFullYear() + options.years)
|
|
194
|
+
return d
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const _MS_PER_UNIT: Record<DateDiffUnit, number> = {
|
|
198
|
+
seconds: 1_000,
|
|
199
|
+
minutes: 60_000,
|
|
200
|
+
hours: 3_600_000,
|
|
201
|
+
days: 86_400_000,
|
|
202
|
+
weeks: 604_800_000,
|
|
203
|
+
months: 0,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function dateDiff(a: DateInput, b: DateInput, unit: DateDiffUnit = 'days'): number {
|
|
207
|
+
const da = new Date(a)
|
|
208
|
+
const db = new Date(b)
|
|
209
|
+
if (unit === 'months') {
|
|
210
|
+
const years = db.getFullYear() - da.getFullYear()
|
|
211
|
+
return years * 12 + (db.getMonth() - da.getMonth())
|
|
212
|
+
}
|
|
213
|
+
return Math.floor((db.getTime() - da.getTime()) / _MS_PER_UNIT[unit])
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ─── DateChain — fluent wrapper ────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
export class DateChain {
|
|
219
|
+
private _date: Date
|
|
220
|
+
|
|
221
|
+
constructor(date: DateInput) {
|
|
222
|
+
this._date = new Date(date)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
add(n: number, unit: TimeDeltaUnit = 'days'): DateChain {
|
|
226
|
+
return new DateChain(timeDelta(this._date, { [unit]: n }))
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
subtract(n: number, unit: TimeDeltaUnit = 'days'): DateChain {
|
|
230
|
+
return new DateChain(timeDelta(this._date, { [unit]: -n }))
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
diff(other: DateInput, unit: DateDiffUnit = 'days'): number {
|
|
234
|
+
return dateDiff(this._date, other, unit)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
isBefore(other: DateInput): boolean {
|
|
238
|
+
return this._date.getTime() < new Date(other).getTime()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
isAfter(other: DateInput): boolean {
|
|
242
|
+
return this._date.getTime() > new Date(other).getTime()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
isSameDay(other: DateInput): boolean {
|
|
246
|
+
const o = new Date(other)
|
|
247
|
+
return (
|
|
248
|
+
this._date.getFullYear() === o.getFullYear()
|
|
249
|
+
&& this._date.getMonth() === o.getMonth()
|
|
250
|
+
&& this._date.getDate() === o.getDate()
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
toDate(): Date {
|
|
255
|
+
return new Date(this._date)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
valueOf(): number {
|
|
259
|
+
return this._date.getTime()
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
toString(): string {
|
|
263
|
+
return this._date.toISOString()
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ─── d — blended callable + static namespace ──────────────────────────────────
|
|
268
|
+
|
|
269
|
+
function _d(date?: DateInput): DateChain {
|
|
270
|
+
return new DateChain(date !== undefined ? date : new Date())
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export const d = Object.assign(_d, {
|
|
274
|
+
// ── Relative to now ──────────────────────────────────────────
|
|
275
|
+
get now(): DateChain { return new DateChain(new Date()) },
|
|
276
|
+
get today(): DateChain {
|
|
277
|
+
const t = new Date()
|
|
278
|
+
t.setHours(0, 0, 0, 0)
|
|
279
|
+
return new DateChain(t)
|
|
280
|
+
},
|
|
281
|
+
get tomorrow(): DateChain { return new DateChain(timeDelta(new Date(), { days: 1 })) },
|
|
282
|
+
get yesterday(): DateChain { return new DateChain(timeDelta(new Date(), { days: -1 })) },
|
|
283
|
+
get nextWeek(): DateChain { return new DateChain(timeDelta(new Date(), { weeks: 1 })) },
|
|
284
|
+
get lastWeek(): DateChain { return new DateChain(timeDelta(new Date(), { weeks: -1 })) },
|
|
285
|
+
|
|
286
|
+
// ── Current month ────────────────────────────────────────────
|
|
287
|
+
get firstOfMonth(): DateChain {
|
|
288
|
+
const t = new Date()
|
|
289
|
+
return new DateChain(new Date(t.getFullYear(), t.getMonth(), 1))
|
|
290
|
+
},
|
|
291
|
+
get lastOfMonth(): DateChain {
|
|
292
|
+
const t = new Date()
|
|
293
|
+
return new DateChain(new Date(t.getFullYear(), t.getMonth() + 1, 0))
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
// ── Current week (Sunday start) ──────────────────────────────
|
|
297
|
+
get firstOfWeek(): DateChain {
|
|
298
|
+
const t = new Date()
|
|
299
|
+
return new DateChain(new Date(t.setDate(t.getDate() - t.getDay())))
|
|
300
|
+
},
|
|
301
|
+
get lastOfWeek(): DateChain {
|
|
302
|
+
const t = new Date()
|
|
303
|
+
return new DateChain(new Date(t.setDate(t.getDate() + (6 - t.getDay()))))
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
// ── Next month ───────────────────────────────────────────────
|
|
307
|
+
get firstOfNextMonth(): DateChain {
|
|
308
|
+
const t = new Date()
|
|
309
|
+
return new DateChain(new Date(t.getFullYear(), t.getMonth() + 1, 1))
|
|
310
|
+
},
|
|
311
|
+
get lastOfNextMonth(): DateChain {
|
|
312
|
+
const t = new Date()
|
|
313
|
+
return new DateChain(new Date(t.getFullYear(), t.getMonth() + 2, 0))
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// ── Next year ────────────────────────────────────────────────
|
|
317
|
+
get firstOfNextYear(): DateChain {
|
|
318
|
+
return new DateChain(new Date(new Date().getFullYear() + 1, 0, 1))
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
// ── Methods ──────────────────────────────────────────────────
|
|
322
|
+
in: (n: number, unit: TimeDeltaUnit = 'days'): DateChain => new DateChain(timeDelta(new Date(), { [unit]: n })),
|
|
323
|
+
ago: (n: number, unit: TimeDeltaUnit = 'days'): DateChain => new DateChain(timeDelta(new Date(), { [unit]: -n })),
|
|
324
|
+
diff: (a: DateInput, b: DateInput, unit: DateDiffUnit = 'days'): number => dateDiff(a, b, unit),
|
|
325
|
+
isBefore: (a: DateInput, b: DateInput): boolean => new Date(a).getTime() < new Date(b).getTime(),
|
|
326
|
+
isAfter: (a: DateInput, b: DateInput): boolean => new Date(a).getTime() > new Date(b).getTime(),
|
|
327
|
+
isSameDay: (a: DateInput, b: DateInput): boolean => {
|
|
328
|
+
const da = new Date(a)
|
|
329
|
+
const db = new Date(b)
|
|
330
|
+
return (
|
|
331
|
+
da.getFullYear() === db.getFullYear()
|
|
332
|
+
&& da.getMonth() === db.getMonth()
|
|
333
|
+
&& da.getDate() === db.getDate()
|
|
334
|
+
)
|
|
335
|
+
},
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// ─── Timezone + formatting ─────────────────────────────────────────────────────
|
|
339
|
+
|
|
340
|
+
export function handleTimezone(date: Date, intFmtOpt: Intl.DateTimeFormatOptions): Date {
|
|
341
|
+
if (intFmtOpt.timeZone === 'UTC') {
|
|
342
|
+
const utcDate = new Date(date.getTime())
|
|
343
|
+
utcDate.setMinutes(utcDate.getMinutes() + date.getTimezoneOffset())
|
|
344
|
+
return utcDate
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
const formatter = new Intl.DateTimeFormat('en-US', { ...intFmtOpt, month: 'numeric' })
|
|
348
|
+
const formattedParts = formatter.formatToParts(date)
|
|
349
|
+
const parts: Record<string, number> = {}
|
|
350
|
+
for (const part of formattedParts) {
|
|
351
|
+
if (part.type !== 'literal' && part.type !== 'timeZoneName')
|
|
352
|
+
parts[part.type] = Number.parseInt(part.value, 10)
|
|
353
|
+
}
|
|
354
|
+
return new Date(
|
|
355
|
+
parts.year,
|
|
356
|
+
(parts.month || 1) - 1,
|
|
357
|
+
parts.day,
|
|
358
|
+
parts.hour || 0,
|
|
359
|
+
parts.minute || 0,
|
|
360
|
+
parts.second || 0,
|
|
361
|
+
)
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.warn(`Error handling timezone ${intFmtOpt.timeZone}:`, error)
|
|
364
|
+
return date
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function getDatePartsMap(date: Date, locale: Intl.LocalesArgument, intFmtOpt?: Intl.DateTimeFormatOptions) {
|
|
369
|
+
const d = intFmtOpt?.timeZone ? handleTimezone(date, intFmtOpt) : date
|
|
370
|
+
const year = d.getFullYear().toString()
|
|
371
|
+
return {
|
|
372
|
+
AmPm: d.toLocaleString(locale, { hour: 'numeric', hour12: true, minute: 'numeric' }).split(' ')[1],
|
|
373
|
+
DD: String(d.getDate()).padStart(2, '0'),
|
|
374
|
+
DDD: d.toLocaleString(locale, { weekday: 'short' }),
|
|
375
|
+
DDDD: d.toLocaleString(locale, { weekday: 'long' }),
|
|
376
|
+
HH: String(d.getHours()).padStart(2, '0'),
|
|
377
|
+
mm: String(d.getMinutes()).padStart(2, '0'),
|
|
378
|
+
MM: String(d.getMonth() + 1).padStart(2, '0'),
|
|
379
|
+
MMM: d.toLocaleString(locale, { month: 'short' }),
|
|
380
|
+
MMMM: d.toLocaleString(locale, { month: 'long' }),
|
|
381
|
+
ss: String(d.getSeconds()).padStart(2, '0'),
|
|
382
|
+
sss: String(d.getMilliseconds()).padStart(3, '0'),
|
|
383
|
+
YY: year.slice(-2),
|
|
384
|
+
YYYY: year,
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Precomputed token regex (module-level, avoid re-creating on every call)
|
|
389
|
+
const _sampleMap = getDatePartsMap(new Date(), 'en-US')
|
|
390
|
+
const _orderedTokens = (Object.keys(_sampleMap).sort((a, b) => b.length - a.length)) as (keyof typeof _sampleMap)[]
|
|
391
|
+
const _tokenRegex = new RegExp(_orderedTokens.join('|'), 'g')
|
|
392
|
+
|
|
393
|
+
export function formatDate(date?: DateInput, format?: DateTimeAcceptedFormats): string
|
|
394
|
+
export function formatDate(date?: DateInput, opts?: FormatDateOptions): string
|
|
395
|
+
export function formatDate(
|
|
396
|
+
date?: DateInput,
|
|
397
|
+
formatOrOpts?: DateTimeAcceptedFormats | FormatDateOptions,
|
|
398
|
+
): string {
|
|
399
|
+
if (!date) return ''
|
|
400
|
+
|
|
401
|
+
let format: DateTimeAcceptedFormats | undefined
|
|
402
|
+
let locale: Intl.LocalesArgument | undefined
|
|
403
|
+
let timeZone: string | undefined
|
|
404
|
+
let rest: Partial<Pick<Intl.DateTimeFormatOptions, 'hour12'>> = {}
|
|
405
|
+
|
|
406
|
+
if (typeof formatOrOpts === 'string') {
|
|
407
|
+
format = formatOrOpts
|
|
408
|
+
} else if (formatOrOpts && typeof formatOrOpts === 'object') {
|
|
409
|
+
format = formatOrOpts.format
|
|
410
|
+
locale = formatOrOpts.locale
|
|
411
|
+
timeZone = formatOrOpts.tz
|
|
412
|
+
rest = formatOrOpts
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (format === 'ISO' || format === 'ISO8601') {
|
|
416
|
+
const parsed = new Date(date)
|
|
417
|
+
return Number.isNaN(parsed.getTime()) ? '' : parsed.toISOString()
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
format = format || 'DD.MM.YY'
|
|
421
|
+
locale = locale || getLocale()
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
let parsed = new Date(date)
|
|
425
|
+
|
|
426
|
+
// Handle DD.MM.YY(YY) and DD/MM/YY(YY) strings that new Date() can't parse
|
|
427
|
+
if (typeof date === 'string' && Number.isNaN(parsed.getTime())) {
|
|
428
|
+
const parts = date.split(/[/.-]/)
|
|
429
|
+
if (parts.length === 3) {
|
|
430
|
+
const [d, m, y] = parts.map(p => Number.parseInt(p, 10))
|
|
431
|
+
if (!Number.isNaN(d) && !Number.isNaN(m) && !Number.isNaN(y)) {
|
|
432
|
+
const fullYear = y < 100 ? 2000 + y : y
|
|
433
|
+
parsed = new Date(fullYear, m - 1, d)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
439
|
+
console.warn('Invalid date provided to formatDate:', date)
|
|
440
|
+
return ''
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const intFmtOpt: Intl.DateTimeFormatOptions = {
|
|
444
|
+
day: 'numeric',
|
|
445
|
+
hour: '2-digit',
|
|
446
|
+
hour12: rest.hour12 === undefined ? true : rest.hour12,
|
|
447
|
+
minute: '2-digit',
|
|
448
|
+
month: 'long',
|
|
449
|
+
second: '2-digit',
|
|
450
|
+
timeZone,
|
|
451
|
+
weekday: 'long',
|
|
452
|
+
year: 'numeric',
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const datePartsMap = getDatePartsMap(parsed, locale, intFmtOpt)
|
|
456
|
+
const formattedParts = new Intl.DateTimeFormat(locale, intFmtOpt).formatToParts(parsed)
|
|
457
|
+
const partsMap: Partial<Record<Intl.DateTimeFormatPartTypes, string>> = {}
|
|
458
|
+
|
|
459
|
+
for (const part of formattedParts) {
|
|
460
|
+
if (part.type !== 'literal') partsMap[part.type] = part.value
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (partsMap.month) {
|
|
464
|
+
datePartsMap.MMM = partsMap.month.substring(0, 3)
|
|
465
|
+
datePartsMap.MMMM = partsMap.month
|
|
466
|
+
}
|
|
467
|
+
if (partsMap.weekday) {
|
|
468
|
+
datePartsMap.DDD = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone }).format(parsed)
|
|
469
|
+
datePartsMap.DDDD = partsMap.weekday
|
|
470
|
+
}
|
|
471
|
+
if (partsMap.dayPeriod) {
|
|
472
|
+
datePartsMap.AmPm = partsMap.dayPeriod
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return format.replace(_tokenRegex, match => datePartsMap[match as keyof typeof datePartsMap])
|
|
476
|
+
} catch (error) {
|
|
477
|
+
console.warn(`Error formatting date: ${date} with format: ${format}`, error)
|
|
478
|
+
return ''
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export const fmtDate = formatDate
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { fuzzyMatch } from './string'
|
|
2
|
+
|
|
3
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
type RawResponse<T> = T[] | { data: T[] } | { items: T[] }
|
|
6
|
+
|
|
7
|
+
type ClientFetcher<T> = () => Promise<RawResponse<T>>
|
|
8
|
+
type ServerFetcher<T> = (query: string) => Promise<RawResponse<T>>
|
|
9
|
+
type Fetcher<T> = ClientFetcher<T> | ServerFetcher<T>
|
|
10
|
+
|
|
11
|
+
type Mapper<T, R> = (items: T[]) => R[]
|
|
12
|
+
|
|
13
|
+
export interface OptionItem {
|
|
14
|
+
label: string
|
|
15
|
+
value: string | number
|
|
16
|
+
[key: string]: any
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ToOptionsConfig<T> {
|
|
20
|
+
label: keyof T | ((item: T) => string)
|
|
21
|
+
value: keyof T
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface NormalizedFetchResult<R> {
|
|
25
|
+
(filter?: string): Promise<R[]>
|
|
26
|
+
clearCache: () => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── toOptions ────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function defaultLabel<T>(item: T): string {
|
|
32
|
+
const t = item as any
|
|
33
|
+
return t.label ?? t.name ?? t.title ?? String(t.id ?? '')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function defaultValue<T>(item: T): string | number {
|
|
37
|
+
const t = item as any
|
|
38
|
+
return t.value ?? t.key ?? t.id ?? ''
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function toOptions<T>(items: T[]): OptionItem[]
|
|
42
|
+
export function toOptions<T>(config: ToOptionsConfig<T>): Mapper<T, OptionItem>
|
|
43
|
+
export function toOptions<T>(
|
|
44
|
+
itemsOrConfig: T[] | ToOptionsConfig<T>,
|
|
45
|
+
): OptionItem[] | Mapper<T, OptionItem> {
|
|
46
|
+
if (Array.isArray(itemsOrConfig)) {
|
|
47
|
+
return itemsOrConfig.map(item => ({
|
|
48
|
+
...item as any,
|
|
49
|
+
label: defaultLabel(item),
|
|
50
|
+
value: defaultValue(item),
|
|
51
|
+
}))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const config = itemsOrConfig
|
|
55
|
+
return (items: T[]) => items.map(item => ({
|
|
56
|
+
...item as any,
|
|
57
|
+
label: typeof config.label === 'function'
|
|
58
|
+
? config.label(item)
|
|
59
|
+
: String(item[config.label] ?? ''),
|
|
60
|
+
value: item[config.value] as string | number,
|
|
61
|
+
}))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─── normalizedFetch ──────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
function unwrap<T>(response: RawResponse<T>): T[] {
|
|
67
|
+
if (Array.isArray(response)) return response
|
|
68
|
+
if ('data' in response) return response.data
|
|
69
|
+
if ('items' in response) return response.items
|
|
70
|
+
return []
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function normalizedFetch<T, R>(
|
|
74
|
+
fetcher: Fetcher<T>,
|
|
75
|
+
mapper: Mapper<T, R>,
|
|
76
|
+
options?: { timeout?: number },
|
|
77
|
+
): NormalizedFetchResult<R> {
|
|
78
|
+
const timeout = options?.timeout ?? 10_000
|
|
79
|
+
const isServerSearch = fetcher.length > 0
|
|
80
|
+
let cache: R[] | null = null
|
|
81
|
+
|
|
82
|
+
async function fetchWithTimeout(query?: string): Promise<R[]> {
|
|
83
|
+
const call = isServerSearch
|
|
84
|
+
? (fetcher as ServerFetcher<T>)(query ?? '')
|
|
85
|
+
: (fetcher as ClientFetcher<T>)()
|
|
86
|
+
|
|
87
|
+
const result = await Promise.race([
|
|
88
|
+
call,
|
|
89
|
+
new Promise<never>((_, reject) => setTimeout(() => { reject(new Error('Request timeout')) }, timeout),
|
|
90
|
+
),
|
|
91
|
+
])
|
|
92
|
+
|
|
93
|
+
return mapper(unwrap(result))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const getter = async (filter?: string): Promise<R[]> => {
|
|
97
|
+
try {
|
|
98
|
+
if (isServerSearch) {
|
|
99
|
+
return await fetchWithTimeout(filter)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!cache) {
|
|
103
|
+
cache = await fetchWithTimeout()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (filter) {
|
|
107
|
+
return cache.filter((item) => {
|
|
108
|
+
const { label } = item as any
|
|
109
|
+
if (typeof label !== 'string') return true
|
|
110
|
+
return fuzzyMatch(label, filter).matches
|
|
111
|
+
}).sort((a, b) => {
|
|
112
|
+
const labelA = (a as any).label ?? ''
|
|
113
|
+
const labelB = (b as any).label ?? ''
|
|
114
|
+
return fuzzyMatch(labelB, filter!).score - fuzzyMatch(labelA, filter!).score
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return cache
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('normalizedFetch error:', error)
|
|
121
|
+
return []
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getter.clearCache = () => { cache = null }
|
|
126
|
+
|
|
127
|
+
return getter
|
|
128
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -172,6 +172,25 @@ export async function appendScript(src: string, options?: { id?: string }): Prom
|
|
|
172
172
|
return loadingPromise
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Wait for a global (e.g. a CDN script's `window.X`) to become available.
|
|
177
|
+
* Resolves with the global value, or rejects after `timeout` ms (default 10s).
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* await appendScript('https://unpkg.com/leaflet/dist/leaflet.js')
|
|
181
|
+
* const L = await awaitGlobal('L')
|
|
182
|
+
*/
|
|
183
|
+
export async function awaitGlobal<T = any>(name: string, { timeout = 10_000, interval = 50 }: { timeout?: number, interval?: number } = {}): Promise<T> {
|
|
184
|
+
const start = Date.now()
|
|
185
|
+
while ((window as any)[name] === undefined) {
|
|
186
|
+
if (Date.now() - start > timeout) {
|
|
187
|
+
throw new Error(`awaitGlobal: window.${name} not available after ${timeout}ms`)
|
|
188
|
+
}
|
|
189
|
+
await sleep(interval)
|
|
190
|
+
}
|
|
191
|
+
return (window as any)[name] as T
|
|
192
|
+
}
|
|
193
|
+
|
|
175
194
|
export function appendStyle(src: string): Promise<void> {
|
|
176
195
|
return new Promise((resolve, reject) => {
|
|
177
196
|
if (document.querySelector(`link[href="${src}"]`)) {
|
|
@@ -198,9 +217,6 @@ export function normalizeDimension(value: string | number | undefined, defaultMe
|
|
|
198
217
|
return value
|
|
199
218
|
}
|
|
200
219
|
|
|
201
|
-
export * as bagelFormUtils from './BagelFormUtils'
|
|
202
|
-
export { useForm } from './BagelFormUtils'
|
|
203
|
-
|
|
204
220
|
export type { NormalizedOption } from './options'
|
|
205
221
|
export { getOptionIcon, getOptionLabel, getOptionValue, normalizeOption } from './options'
|
|
206
222
|
|
|
@@ -291,5 +307,40 @@ export function downloadFile(source: string | Blob, fileName?: string) {
|
|
|
291
307
|
|
|
292
308
|
export { defaultOptions, showdown } from './showdown'
|
|
293
309
|
|
|
310
|
+
// Note: the `DateInput` type from './date' is intentionally not re-exported
|
|
311
|
+
// here to avoid clashing with the DateInput component. Import it from
|
|
312
|
+
// '@bagelink/vue/src/utils/date' directly if needed.
|
|
313
|
+
export type {
|
|
314
|
+
CommonDateFormats,
|
|
315
|
+
CommonDateTimeFormats,
|
|
316
|
+
CommonTimeFormats,
|
|
317
|
+
DateDiffUnit,
|
|
318
|
+
DateTimeAcceptedFormats,
|
|
319
|
+
FormatDateOptions,
|
|
320
|
+
NamedFormats,
|
|
321
|
+
TimeDeltaOptions,
|
|
322
|
+
TimeDeltaUnit,
|
|
323
|
+
} from './date'
|
|
324
|
+
export {
|
|
325
|
+
d,
|
|
326
|
+
DateChain,
|
|
327
|
+
dateDiff,
|
|
328
|
+
DAY,
|
|
329
|
+
fmtDate,
|
|
330
|
+
formatDate,
|
|
331
|
+
getDatePartsMap,
|
|
332
|
+
handleTimezone,
|
|
333
|
+
HOUR,
|
|
334
|
+
local,
|
|
335
|
+
MINUTE,
|
|
336
|
+
MS,
|
|
337
|
+
SECOND,
|
|
338
|
+
timeDelta,
|
|
339
|
+
utc,
|
|
340
|
+
WEEK,
|
|
341
|
+
} from './date'
|
|
342
|
+
export * from './fetch'
|
|
343
|
+
export * from './string'
|
|
344
|
+
|
|
294
345
|
export { formatString } from './strings'
|
|
295
346
|
export { useDebounceFn } from '@vueuse/core'
|
package/src/utils/sizeParsing.ts
CHANGED
|
@@ -7,11 +7,11 @@ export function standardSize(value: number | string | undefined, unit: UnitSize
|
|
|
7
7
|
// Handle SizeType values
|
|
8
8
|
if (typeof value === 'string') {
|
|
9
9
|
const sizeTypeMap: Record<SizeType, number> = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
xs: 16,
|
|
11
|
+
sm: 24,
|
|
12
|
+
md: 32,
|
|
13
|
+
lg: 48,
|
|
14
|
+
xl: 64
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (value in sizeTypeMap) {
|