tyrell-components 1.0.0-TC44 → 1.0.0-TC46
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/tyrell.js +1 -1
- package/lib/base/ty-component.d.ts +12 -7
- package/lib/base/ty-component.js +8 -139
- package/lib/components/button.d.ts +2 -16
- package/lib/components/button.js +0 -36
- package/lib/components/calendar-month.d.ts +2 -48
- package/lib/components/calendar-month.js +4 -166
- package/lib/components/calendar-navigation.d.ts +3 -48
- package/lib/components/calendar-navigation.js +3 -126
- package/lib/components/calendar.d.ts +3 -80
- package/lib/components/calendar.js +8 -230
- package/lib/components/checkbox.d.ts +4 -71
- package/lib/components/checkbox.js +7 -151
- package/lib/components/copy.d.ts +3 -56
- package/lib/components/copy.js +1 -120
- package/lib/components/date-picker.d.ts +10 -160
- package/lib/components/date-picker.js +31 -577
- package/lib/components/file-upload.d.ts +3 -15
- package/lib/components/file-upload.js +0 -37
- package/lib/components/icon.d.ts +3 -24
- package/lib/components/icon.js +1 -90
- package/lib/components/input.d.ts +7 -107
- package/lib/components/input.js +4 -193
- package/lib/components/modal.d.ts +3 -26
- package/lib/components/modal.js +1 -188
- package/lib/components/option.d.ts +2 -9
- package/lib/components/option.js +0 -48
- package/lib/components/popup.d.ts +2 -17
- package/lib/components/popup.js +3 -130
- package/lib/components/radio.d.ts +3 -23
- package/lib/components/radio.js +0 -63
- package/lib/components/resize-observer.d.ts +2 -30
- package/lib/components/resize-observer.js +0 -45
- package/lib/components/scroll-container.d.ts +2 -12
- package/lib/components/scroll-container.js +3 -46
- package/lib/components/select.d.ts +43 -150
- package/lib/components/select.js +68 -558
- package/lib/components/selected-tags.d.ts +4 -47
- package/lib/components/selected-tags.js +0 -94
- package/lib/components/step.d.ts +1 -6
- package/lib/components/step.js +0 -6
- package/lib/components/switch.d.ts +0 -11
- package/lib/components/switch.js +2 -36
- package/lib/components/tab.d.ts +1 -6
- package/lib/components/tab.js +0 -7
- package/lib/components/tabs.d.ts +3 -63
- package/lib/components/tabs.js +2 -282
- package/lib/components/tag.d.ts +2 -36
- package/lib/components/tag.js +5 -105
- package/lib/components/textarea.d.ts +6 -86
- package/lib/components/textarea.js +2 -173
- package/lib/components/tooltip.d.ts +3 -11
- package/lib/components/tooltip.js +0 -154
- package/lib/components/wizard.d.ts +4 -54
- package/lib/components/wizard.js +0 -228
- package/lib/icons/fontawesome/brands.d.ts +0 -1
- package/lib/icons/fontawesome/brands.js +0 -8
- package/lib/icons/fontawesome/regular.d.ts +0 -1
- package/lib/icons/fontawesome/regular.js +0 -8
- package/lib/icons/fontawesome/solid.d.ts +0 -1
- package/lib/icons/fontawesome/solid.js +0 -8
- package/lib/icons/heroicons/micro.d.ts +0 -1
- package/lib/icons/heroicons/micro.js +0 -8
- package/lib/icons/heroicons/mini.d.ts +0 -1
- package/lib/icons/heroicons/mini.js +0 -8
- package/lib/icons/heroicons/outline.d.ts +0 -1
- package/lib/icons/heroicons/outline.js +0 -8
- package/lib/icons/heroicons/solid.d.ts +0 -1
- package/lib/icons/heroicons/solid.js +0 -8
- package/lib/icons/lucide.d.ts +0 -1
- package/lib/icons/lucide.js +0 -1872
- package/lib/icons/material/filled.d.ts +0 -1
- package/lib/icons/material/filled.js +0 -10
- package/lib/icons/material/outlined.d.ts +0 -1
- package/lib/icons/material/outlined.js +0 -10
- package/lib/icons/material/round.d.ts +0 -1
- package/lib/icons/material/round.js +0 -10
- package/lib/icons/material/sharp.d.ts +0 -1
- package/lib/icons/material/sharp.js +0 -10
- package/lib/icons/material/two-tone.d.ts +0 -1
- package/lib/icons/material/two-tone.js +0 -10
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -20
- package/lib/styles/button.d.ts +2 -20
- package/lib/styles/button.js +13 -120
- package/lib/styles/calendar-month.d.ts +1 -5
- package/lib/styles/calendar-month.js +3 -60
- package/lib/styles/calendar-navigation.d.ts +2 -6
- package/lib/styles/calendar-navigation.js +1 -26
- package/lib/styles/calendar.d.ts +1 -2
- package/lib/styles/calendar.js +0 -13
- package/lib/styles/checkbox.d.ts +0 -1
- package/lib/styles/checkbox.js +1 -23
- package/lib/styles/copy.d.ts +0 -1
- package/lib/styles/copy.js +2 -17
- package/lib/styles/custom-scrollbar.d.ts +1 -2
- package/lib/styles/custom-scrollbar.js +3 -18
- package/lib/styles/date-picker.d.ts +1 -5
- package/lib/styles/date-picker.js +5 -35
- package/lib/styles/file-upload.d.ts +1 -2
- package/lib/styles/file-upload.js +0 -15
- package/lib/styles/icon.d.ts +2 -6
- package/lib/styles/icon.js +1 -26
- package/lib/styles/input.d.ts +1 -6
- package/lib/styles/input.js +3 -53
- package/lib/styles/modal.d.ts +1 -2
- package/lib/styles/modal.js +3 -23
- package/lib/styles/option.d.ts +2 -6
- package/lib/styles/option.js +1 -11
- package/lib/styles/popup.d.ts +1 -2
- package/lib/styles/popup.js +4 -25
- package/lib/styles/radio.d.ts +0 -1
- package/lib/styles/radio.js +1 -15
- package/lib/styles/resize-observer.d.ts +1 -2
- package/lib/styles/resize-observer.js +0 -7
- package/lib/styles/scroll-container.d.ts +1 -2
- package/lib/styles/scroll-container.js +0 -19
- package/lib/styles/select-base.d.ts +0 -1
- package/lib/styles/select-base.js +9 -70
- package/lib/styles/select.d.ts +1 -2
- package/lib/styles/select.js +50 -18
- package/lib/styles/step.d.ts +1 -2
- package/lib/styles/step.js +0 -4
- package/lib/styles/switch.d.ts +0 -1
- package/lib/styles/switch.js +2 -20
- package/lib/styles/tab.d.ts +1 -2
- package/lib/styles/tab.js +0 -5
- package/lib/styles/tabs.d.ts +3 -11
- package/lib/styles/tabs.js +4 -47
- package/lib/styles/tag.d.ts +1 -5
- package/lib/styles/tag.js +2 -31
- package/lib/styles/textarea.d.ts +1 -2
- package/lib/styles/textarea.js +0 -44
- package/lib/styles/tooltip.d.ts +0 -1
- package/lib/styles/tooltip.js +0 -10
- package/lib/styles/wizard.d.ts +5 -21
- package/lib/styles/wizard.js +5 -85
- package/lib/types/common.d.ts +0 -2
- package/lib/types/common.js +0 -12
- package/lib/utils/calendar-utils.d.ts +4 -21
- package/lib/utils/calendar-utils.js +4 -177
- package/lib/utils/custom-scrollbar.d.ts +6 -20
- package/lib/utils/custom-scrollbar.js +1 -45
- package/lib/utils/flavor-sheet.d.ts +0 -1
- package/lib/utils/flavor-sheet.js +0 -23
- package/lib/utils/icon-registry.d.ts +0 -2
- package/lib/utils/icon-registry.js +1 -107
- package/lib/utils/icons.d.ts +0 -1
- package/lib/utils/icons.js +0 -9
- package/lib/utils/loader-registry.d.ts +0 -1
- package/lib/utils/loader-registry.js +0 -32
- package/lib/utils/locale.d.ts +2 -23
- package/lib/utils/locale.js +0 -86
- package/lib/utils/mobile.d.ts +0 -1
- package/lib/utils/mobile.js +0 -17
- package/lib/utils/number-format.d.ts +0 -2
- package/lib/utils/number-format.js +2 -65
- package/lib/utils/positioning.d.ts +4 -2
- package/lib/utils/positioning.js +2 -54
- package/lib/utils/property-capture.d.ts +5 -27
- package/lib/utils/property-capture.js +0 -108
- package/lib/utils/property-manager.d.ts +0 -1
- package/lib/utils/property-manager.js +0 -70
- package/lib/utils/resize-observer.d.ts +0 -1
- package/lib/utils/resize-observer.js +0 -36
- package/lib/utils/scroll-lock.d.ts +4 -11
- package/lib/utils/scroll-lock.js +0 -107
- package/lib/utils/styles.d.ts +0 -1
- package/lib/utils/styles.js +0 -24
- package/lib/version.d.ts +1 -2
- package/lib/version.js +1 -11
- package/package.json +2 -2
- package/lib/base/ty-component.d.ts.map +0 -1
- package/lib/base/ty-component.js.map +0 -1
- package/lib/components/button.d.ts.map +0 -1
- package/lib/components/button.js.map +0 -1
- package/lib/components/calendar-month.d.ts.map +0 -1
- package/lib/components/calendar-month.js.map +0 -1
- package/lib/components/calendar-navigation.d.ts.map +0 -1
- package/lib/components/calendar-navigation.js.map +0 -1
- package/lib/components/calendar.d.ts.map +0 -1
- package/lib/components/calendar.js.map +0 -1
- package/lib/components/checkbox.d.ts.map +0 -1
- package/lib/components/checkbox.js.map +0 -1
- package/lib/components/copy.d.ts.map +0 -1
- package/lib/components/copy.js.map +0 -1
- package/lib/components/date-picker.d.ts.map +0 -1
- package/lib/components/date-picker.js.map +0 -1
- package/lib/components/file-upload.d.ts.map +0 -1
- package/lib/components/file-upload.js.map +0 -1
- package/lib/components/icon.d.ts.map +0 -1
- package/lib/components/icon.js.map +0 -1
- package/lib/components/input.d.ts.map +0 -1
- package/lib/components/input.js.map +0 -1
- package/lib/components/modal.d.ts.map +0 -1
- package/lib/components/modal.js.map +0 -1
- package/lib/components/option.d.ts.map +0 -1
- package/lib/components/option.js.map +0 -1
- package/lib/components/popup.d.ts.map +0 -1
- package/lib/components/popup.js.map +0 -1
- package/lib/components/radio.d.ts.map +0 -1
- package/lib/components/radio.js.map +0 -1
- package/lib/components/resize-observer.d.ts.map +0 -1
- package/lib/components/resize-observer.js.map +0 -1
- package/lib/components/scroll-container.d.ts.map +0 -1
- package/lib/components/scroll-container.js.map +0 -1
- package/lib/components/select.d.ts.map +0 -1
- package/lib/components/select.js.map +0 -1
- package/lib/components/selected-tags.d.ts.map +0 -1
- package/lib/components/selected-tags.js.map +0 -1
- package/lib/components/step.d.ts.map +0 -1
- package/lib/components/step.js.map +0 -1
- package/lib/components/switch.d.ts.map +0 -1
- package/lib/components/switch.js.map +0 -1
- package/lib/components/tab.d.ts.map +0 -1
- package/lib/components/tab.js.map +0 -1
- package/lib/components/tabs.d.ts.map +0 -1
- package/lib/components/tabs.js.map +0 -1
- package/lib/components/tag.d.ts.map +0 -1
- package/lib/components/tag.js.map +0 -1
- package/lib/components/textarea.d.ts.map +0 -1
- package/lib/components/textarea.js.map +0 -1
- package/lib/components/tooltip.d.ts.map +0 -1
- package/lib/components/tooltip.js.map +0 -1
- package/lib/components/wizard.d.ts.map +0 -1
- package/lib/components/wizard.js.map +0 -1
- package/lib/icons/fontawesome/brands.d.ts.map +0 -1
- package/lib/icons/fontawesome/brands.js.map +0 -1
- package/lib/icons/fontawesome/regular.d.ts.map +0 -1
- package/lib/icons/fontawesome/regular.js.map +0 -1
- package/lib/icons/fontawesome/solid.d.ts.map +0 -1
- package/lib/icons/fontawesome/solid.js.map +0 -1
- package/lib/icons/heroicons/micro.d.ts.map +0 -1
- package/lib/icons/heroicons/micro.js.map +0 -1
- package/lib/icons/heroicons/mini.d.ts.map +0 -1
- package/lib/icons/heroicons/mini.js.map +0 -1
- package/lib/icons/heroicons/outline.d.ts.map +0 -1
- package/lib/icons/heroicons/outline.js.map +0 -1
- package/lib/icons/heroicons/solid.d.ts.map +0 -1
- package/lib/icons/heroicons/solid.js.map +0 -1
- package/lib/icons/lucide.d.ts.map +0 -1
- package/lib/icons/lucide.js.map +0 -1
- package/lib/icons/material/filled.d.ts.map +0 -1
- package/lib/icons/material/filled.js.map +0 -1
- package/lib/icons/material/outlined.d.ts.map +0 -1
- package/lib/icons/material/outlined.js.map +0 -1
- package/lib/icons/material/round.d.ts.map +0 -1
- package/lib/icons/material/round.js.map +0 -1
- package/lib/icons/material/sharp.d.ts.map +0 -1
- package/lib/icons/material/sharp.js.map +0 -1
- package/lib/icons/material/two-tone.d.ts.map +0 -1
- package/lib/icons/material/two-tone.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/styles/button.d.ts.map +0 -1
- package/lib/styles/button.js.map +0 -1
- package/lib/styles/calendar-month.d.ts.map +0 -1
- package/lib/styles/calendar-month.js.map +0 -1
- package/lib/styles/calendar-navigation.d.ts.map +0 -1
- package/lib/styles/calendar-navigation.js.map +0 -1
- package/lib/styles/calendar.d.ts.map +0 -1
- package/lib/styles/calendar.js.map +0 -1
- package/lib/styles/checkbox.d.ts.map +0 -1
- package/lib/styles/checkbox.js.map +0 -1
- package/lib/styles/copy.d.ts.map +0 -1
- package/lib/styles/copy.js.map +0 -1
- package/lib/styles/custom-scrollbar.d.ts.map +0 -1
- package/lib/styles/custom-scrollbar.js.map +0 -1
- package/lib/styles/date-picker.d.ts.map +0 -1
- package/lib/styles/date-picker.js.map +0 -1
- package/lib/styles/file-upload.d.ts.map +0 -1
- package/lib/styles/file-upload.js.map +0 -1
- package/lib/styles/icon.d.ts.map +0 -1
- package/lib/styles/icon.js.map +0 -1
- package/lib/styles/input.d.ts.map +0 -1
- package/lib/styles/input.js.map +0 -1
- package/lib/styles/modal.d.ts.map +0 -1
- package/lib/styles/modal.js.map +0 -1
- package/lib/styles/multiselect.d.ts +0 -8
- package/lib/styles/multiselect.d.ts.map +0 -1
- package/lib/styles/multiselect.js +0 -849
- package/lib/styles/multiselect.js.map +0 -1
- package/lib/styles/option.d.ts.map +0 -1
- package/lib/styles/option.js.map +0 -1
- package/lib/styles/popup.d.ts.map +0 -1
- package/lib/styles/popup.js.map +0 -1
- package/lib/styles/radio.d.ts.map +0 -1
- package/lib/styles/radio.js.map +0 -1
- package/lib/styles/resize-observer.d.ts.map +0 -1
- package/lib/styles/resize-observer.js.map +0 -1
- package/lib/styles/scroll-container.d.ts.map +0 -1
- package/lib/styles/scroll-container.js.map +0 -1
- package/lib/styles/select-base.d.ts.map +0 -1
- package/lib/styles/select-base.js.map +0 -1
- package/lib/styles/select.d.ts.map +0 -1
- package/lib/styles/select.js.map +0 -1
- package/lib/styles/step.d.ts.map +0 -1
- package/lib/styles/step.js.map +0 -1
- package/lib/styles/switch.d.ts.map +0 -1
- package/lib/styles/switch.js.map +0 -1
- package/lib/styles/tab.d.ts.map +0 -1
- package/lib/styles/tab.js.map +0 -1
- package/lib/styles/tabs.d.ts.map +0 -1
- package/lib/styles/tabs.js.map +0 -1
- package/lib/styles/tag.d.ts.map +0 -1
- package/lib/styles/tag.js.map +0 -1
- package/lib/styles/textarea.d.ts.map +0 -1
- package/lib/styles/textarea.js.map +0 -1
- package/lib/styles/tooltip.d.ts.map +0 -1
- package/lib/styles/tooltip.js.map +0 -1
- package/lib/styles/wizard.d.ts.map +0 -1
- package/lib/styles/wizard.js.map +0 -1
- package/lib/types/common.d.ts.map +0 -1
- package/lib/types/common.js.map +0 -1
- package/lib/utils/calendar-utils.d.ts.map +0 -1
- package/lib/utils/calendar-utils.js.map +0 -1
- package/lib/utils/custom-scrollbar.d.ts.map +0 -1
- package/lib/utils/custom-scrollbar.js.map +0 -1
- package/lib/utils/flavor-sheet.d.ts.map +0 -1
- package/lib/utils/flavor-sheet.js.map +0 -1
- package/lib/utils/icon-registry.d.ts.map +0 -1
- package/lib/utils/icon-registry.js.map +0 -1
- package/lib/utils/icons.d.ts.map +0 -1
- package/lib/utils/icons.js.map +0 -1
- package/lib/utils/loader-registry.d.ts.map +0 -1
- package/lib/utils/loader-registry.js.map +0 -1
- package/lib/utils/locale.d.ts.map +0 -1
- package/lib/utils/locale.js.map +0 -1
- package/lib/utils/mobile.d.ts.map +0 -1
- package/lib/utils/mobile.js.map +0 -1
- package/lib/utils/number-format.d.ts.map +0 -1
- package/lib/utils/number-format.js.map +0 -1
- package/lib/utils/parse-boolean.d.ts +0 -39
- package/lib/utils/parse-boolean.d.ts.map +0 -1
- package/lib/utils/parse-boolean.js +0 -58
- package/lib/utils/parse-boolean.js.map +0 -1
- package/lib/utils/positioning.d.ts.map +0 -1
- package/lib/utils/positioning.js.map +0 -1
- package/lib/utils/property-capture.d.ts.map +0 -1
- package/lib/utils/property-capture.js.map +0 -1
- package/lib/utils/property-manager.d.ts.map +0 -1
- package/lib/utils/property-manager.js.map +0 -1
- package/lib/utils/resize-observer.d.ts.map +0 -1
- package/lib/utils/resize-observer.js.map +0 -1
- package/lib/utils/scroll-lock.d.ts.map +0 -1
- package/lib/utils/scroll-lock.js.map +0 -1
- package/lib/utils/styles.d.ts.map +0 -1
- package/lib/utils/styles.js.map +0 -1
- package/lib/version.d.ts.map +0 -1
- package/lib/version.js.map +0 -1
|
@@ -1,51 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TyDatePicker Web Component
|
|
3
|
-
* PORTED FROM: cljs/ty/components/date_picker.cljs
|
|
4
|
-
*
|
|
5
|
-
* A date picker component with read-only input and calendar dropdown.
|
|
6
|
-
* Supports date-only and date+time modes with smart time input.
|
|
7
|
-
*
|
|
8
|
-
* Architecture:
|
|
9
|
-
* - Read-only input stub (displays formatted date)
|
|
10
|
-
* - Calendar dropdown (modal dialog with ty-calendar)
|
|
11
|
-
* - Optional time input (with smart digit navigation)
|
|
12
|
-
* - Form participation via ElementInternals
|
|
13
|
-
* - UTC output, local display
|
|
14
|
-
*
|
|
15
|
-
* Features:
|
|
16
|
-
* - Date selection with calendar dropdown
|
|
17
|
-
* - Optional time input with smart navigation
|
|
18
|
-
* - Form integration (works with FormData)
|
|
19
|
-
* - UTC value output for server communication
|
|
20
|
-
* - Localized display formatting (Intl API)
|
|
21
|
-
* - Clearable with clear button
|
|
22
|
-
* - Keyboard navigation (Escape to close)
|
|
23
|
-
* - Outside click handling
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```html
|
|
27
|
-
* <!-- Basic date picker -->
|
|
28
|
-
* <ty-date-picker
|
|
29
|
-
* label="Select Date"
|
|
30
|
-
* placeholder="Choose a date">
|
|
31
|
-
* </ty-date-picker>
|
|
32
|
-
*
|
|
33
|
-
* <!-- With time -->
|
|
34
|
-
* <ty-date-picker
|
|
35
|
-
* with-time="true"
|
|
36
|
-
* label="Select Date & Time">
|
|
37
|
-
* </ty-date-picker>
|
|
38
|
-
*
|
|
39
|
-
* <!-- Form integration -->
|
|
40
|
-
* <form>
|
|
41
|
-
* <ty-date-picker name="booking-date"></ty-date-picker>
|
|
42
|
-
* <button type="submit">Submit</button>
|
|
43
|
-
* </form>
|
|
44
|
-
*
|
|
45
|
-
* <!-- Pre-filled value (UTC) -->
|
|
46
|
-
* <ty-date-picker value="2024-09-21T08:30:00.000Z"></ty-date-picker>
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
1
|
import { ensureStyles } from '../utils/styles.js';
|
|
50
2
|
import { syncCustomFlavorSheet } from '../utils/flavor-sheet.js';
|
|
51
3
|
import { datePickerStyles, datePickerCustomFlavorCss } from '../styles/date-picker.js';
|
|
@@ -54,16 +6,9 @@ import { computeAnchoredPosition } from '../utils/positioning.js';
|
|
|
54
6
|
import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
|
|
55
7
|
import { isMobileTouch } from '../utils/mobile.js';
|
|
56
8
|
import { TyComponent } from '../base/ty-component.js';
|
|
57
|
-
// ============================================================================
|
|
58
|
-
// Constants
|
|
59
|
-
// ============================================================================
|
|
60
9
|
const CALENDAR_ICON_SVG = `<svg stroke='currentColor' fill='none' stroke-width='2' viewBox='0 0 24 24' width='16' height='16' xmlns='http://www.w3.org/2000/svg'><rect x='3' y='4' width='18' height='18' rx='2' ry='2'></rect><line x1='16' y1='2' x2='16' y2='6'></line><line x1='8' y1='2' x2='8' y2='6'></line><line x1='3' y1='10' x2='21' y2='10'></line></svg>`;
|
|
61
10
|
const CLEAR_ICON_SVG = `<svg stroke='currentColor' fill='none' stroke-width='2' viewBox='0 0 24 24' width='14' height='14' xmlns='http://www.w3.org/2000/svg'><line x1='18' y1='6' x2='6' y2='18'></line><line x1='6' y1='6' x2='18' y2='18'></line></svg>`;
|
|
62
11
|
const SCHEDULE_ICON_SVG = `<svg stroke='currentColor' fill='none' stroke-width='2' viewBox='0 0 24 24' width='16' height='16' xmlns='http://www.w3.org/2000/svg'><circle cx='12' cy='12' r='10'></circle><polyline points='12,6 12,12 16,14'></polyline></svg>`;
|
|
63
|
-
// Stable per-instance id (survives across full-rebuild re-renders, unlike a
|
|
64
|
-
// fresh Math.random() per render) — used to wire the stub's aria-labelledby
|
|
65
|
-
// to the label element it's paired with. Same pattern as select.ts's
|
|
66
|
-
// getElementHash.
|
|
67
12
|
const elementIds = new WeakMap();
|
|
68
13
|
let elementIdCounter = 0;
|
|
69
14
|
function getElementHash(element) {
|
|
@@ -74,86 +19,43 @@ function getElementHash(element) {
|
|
|
74
19
|
}
|
|
75
20
|
return id;
|
|
76
21
|
}
|
|
77
|
-
// ============================================================================
|
|
78
|
-
// Helper Functions - Date Parsing & Conversion
|
|
79
|
-
// ============================================================================
|
|
80
|
-
/**
|
|
81
|
-
* Parse ANY input format into year/month/day/hour/minute components.
|
|
82
|
-
* Accepts:
|
|
83
|
-
* - UTC strings: '2024-09-21T08:30:00Z' or '2024-09-21T08:30:00.000Z'
|
|
84
|
-
* - Datetime-local: '2024-09-21T10:30'
|
|
85
|
-
* - Date only: '2024-09-21'
|
|
86
|
-
* - Timestamps: milliseconds since epoch
|
|
87
|
-
* - With timezone: '2024-09-21T10:30:00+02:00'
|
|
88
|
-
*
|
|
89
|
-
* Always extracts components in LOCAL timezone for display/editing.
|
|
90
|
-
*
|
|
91
|
-
* PORTED FROM: parse-value in date_picker.cljs
|
|
92
|
-
*/
|
|
93
22
|
function parseValue(value, withTime) {
|
|
94
23
|
if (!value)
|
|
95
24
|
return null;
|
|
96
25
|
let dateObj = null;
|
|
97
26
|
if (typeof value === 'string') {
|
|
98
|
-
// Date-only format: YYYY-MM-DD
|
|
99
27
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
100
28
|
const [yearStr, monthStr, dayStr] = value.split('-');
|
|
101
29
|
const year = parseInt(yearStr, 10);
|
|
102
|
-
const month = parseInt(monthStr, 10) - 1;
|
|
30
|
+
const month = parseInt(monthStr, 10) - 1;
|
|
103
31
|
const day = parseInt(dayStr, 10);
|
|
104
|
-
// Create at midnight local time
|
|
105
32
|
dateObj = new Date(year, month, day, 0, 0, 0, 0);
|
|
106
33
|
}
|
|
107
34
|
else {
|
|
108
|
-
// Let JS Date handle datetime-local, UTC, with timezone
|
|
109
35
|
dateObj = new Date(value);
|
|
110
36
|
}
|
|
111
37
|
}
|
|
112
38
|
else if (typeof value === 'number') {
|
|
113
|
-
// Numeric inputs (milliseconds)
|
|
114
39
|
dateObj = new Date(value);
|
|
115
40
|
}
|
|
116
41
|
if (!dateObj || isNaN(dateObj.getTime())) {
|
|
117
42
|
return null;
|
|
118
43
|
}
|
|
119
|
-
// Extract components in LOCAL timezone for display
|
|
120
44
|
const components = {
|
|
121
45
|
year: dateObj.getFullYear(),
|
|
122
|
-
month: dateObj.getMonth() + 1,
|
|
46
|
+
month: dateObj.getMonth() + 1,
|
|
123
47
|
day: dateObj.getDate(),
|
|
124
48
|
hour: withTime ? dateObj.getHours() : 0,
|
|
125
49
|
minute: withTime ? dateObj.getMinutes() : 0,
|
|
126
50
|
};
|
|
127
51
|
return components;
|
|
128
52
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Convert internal components to Date object.
|
|
131
|
-
*
|
|
132
|
-
* PORTED FROM: components->date-object in date_picker.cljs
|
|
133
|
-
*/
|
|
134
53
|
function componentsToDateObject(components) {
|
|
135
54
|
const { year, month, day, hour, minute } = components;
|
|
136
55
|
if (!year || !month || !day)
|
|
137
56
|
return null;
|
|
138
|
-
|
|
139
|
-
return new Date(year, month - 1, // Convert to 0-based
|
|
140
|
-
day, hour || 0, minute || 0, 0, // seconds
|
|
141
|
-
0 // milliseconds
|
|
142
|
-
);
|
|
57
|
+
return new Date(year, month - 1, day, hour || 0, minute || 0, 0, 0);
|
|
143
58
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Convert internal components to UTC ISO 8601 format.
|
|
146
|
-
*
|
|
147
|
-
* For date+time mode: Outputs full UTC timestamp with milliseconds
|
|
148
|
-
* Example: '2024-09-21T08:30:00.000Z'
|
|
149
|
-
*
|
|
150
|
-
* For date-only mode: Outputs UTC timestamp at midnight local time
|
|
151
|
-
* Example: '2024-09-20T22:00:00.000Z' (midnight Sept 21 CEST = 10pm Sept 20 UTC)
|
|
152
|
-
*
|
|
153
|
-
* Always returns UTC to ensure unambiguous server communication.
|
|
154
|
-
*
|
|
155
|
-
* PORTED FROM: components->output-value in date_picker.cljs
|
|
156
|
-
*/
|
|
157
59
|
function componentsToOutputValue(components) {
|
|
158
60
|
if (!components.year || !components.month || !components.day) {
|
|
159
61
|
return null;
|
|
@@ -161,18 +63,6 @@ function componentsToOutputValue(components) {
|
|
|
161
63
|
const dateObj = componentsToDateObject(components);
|
|
162
64
|
return dateObj ? dateObj.toISOString() : null;
|
|
163
65
|
}
|
|
164
|
-
/**
|
|
165
|
-
* Convert internal components to local datetime-local format.
|
|
166
|
-
*
|
|
167
|
-
* For date+time mode: Outputs local datetime without timezone
|
|
168
|
-
* Example: '2024-09-21T10:30'
|
|
169
|
-
*
|
|
170
|
-
* For date-only mode: Outputs date only
|
|
171
|
-
* Example: '2024-09-21'
|
|
172
|
-
*
|
|
173
|
-
* This format matches HTML5 <input type="datetime-local"> and is useful
|
|
174
|
-
* for setting other inputs or displaying local time without timezone conversion.
|
|
175
|
-
*/
|
|
176
66
|
function componentsToLocalValue(components, withTime) {
|
|
177
67
|
if (!components.year || !components.month || !components.day) {
|
|
178
68
|
return null;
|
|
@@ -189,11 +79,6 @@ function componentsToLocalValue(components, withTime) {
|
|
|
189
79
|
return `${year}-${month}-${day}`;
|
|
190
80
|
}
|
|
191
81
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Format components for display in input using Intl API.
|
|
194
|
-
*
|
|
195
|
-
* PORTED FROM: format-display-value in date_picker.cljs
|
|
196
|
-
*/
|
|
197
82
|
function formatDisplayValue(components, formatType, locale, withTime) {
|
|
198
83
|
if (!components.year || !components.month || !components.day) {
|
|
199
84
|
return null;
|
|
@@ -204,21 +89,12 @@ function formatDisplayValue(components, formatType, locale, withTime) {
|
|
|
204
89
|
const options = {
|
|
205
90
|
dateStyle: formatType || 'long',
|
|
206
91
|
};
|
|
207
|
-
// Add time styling if with-time is enabled
|
|
208
92
|
if (withTime) {
|
|
209
93
|
options.timeStyle = 'short';
|
|
210
94
|
}
|
|
211
95
|
const formatter = new Intl.DateTimeFormat(locale, options);
|
|
212
96
|
return formatter.format(dateObj);
|
|
213
97
|
}
|
|
214
|
-
// ============================================================================
|
|
215
|
-
// Helper Functions - Time Input
|
|
216
|
-
// ============================================================================
|
|
217
|
-
/**
|
|
218
|
-
* Parse hour and minute from raw digits (4 chars: "HHmm")
|
|
219
|
-
*
|
|
220
|
-
* PORTED FROM: parse-time-components in date_picker.cljs
|
|
221
|
-
*/
|
|
222
98
|
function parseTimeComponents(rawDigits) {
|
|
223
99
|
if (!rawDigits || rawDigits.length !== 4)
|
|
224
100
|
return null;
|
|
@@ -231,99 +107,60 @@ function parseTimeComponents(rawDigits) {
|
|
|
231
107
|
}
|
|
232
108
|
return { hour, minute };
|
|
233
109
|
}
|
|
234
|
-
/**
|
|
235
|
-
* Validate if digit is valid for given position
|
|
236
|
-
* Position 0: first hour digit (0-2)
|
|
237
|
-
* Position 1: second hour digit (0-9, but 0-3 if first is 2)
|
|
238
|
-
* Position 3: first minute digit (0-5)
|
|
239
|
-
* Position 4: second minute digit (0-9)
|
|
240
|
-
*
|
|
241
|
-
* PORTED FROM: validate-time-digit in date_picker.cljs
|
|
242
|
-
*/
|
|
243
110
|
function validateTimeDigit(digit, position, currentDigits) {
|
|
244
111
|
switch (position) {
|
|
245
112
|
case 0:
|
|
246
|
-
return digit <= 2;
|
|
113
|
+
return digit <= 2;
|
|
247
114
|
case 1: {
|
|
248
115
|
const firstHour = parseInt(currentDigits[0], 10);
|
|
249
|
-
return firstHour === 2 ? digit <= 3 : true;
|
|
116
|
+
return firstHour === 2 ? digit <= 3 : true;
|
|
250
117
|
}
|
|
251
118
|
case 3:
|
|
252
|
-
return digit <= 5;
|
|
119
|
+
return digit <= 5;
|
|
253
120
|
case 4:
|
|
254
|
-
return true;
|
|
121
|
+
return true;
|
|
255
122
|
default:
|
|
256
123
|
return false;
|
|
257
124
|
}
|
|
258
125
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Format hour and minute into "HH:mm" display
|
|
261
|
-
*
|
|
262
|
-
* PORTED FROM: format-time-display in date_picker.cljs
|
|
263
|
-
*/
|
|
264
126
|
function formatTimeDisplay(hour, minute) {
|
|
265
127
|
const hourStr = hour.toString().padStart(2, '0');
|
|
266
128
|
const minuteStr = minute.toString().padStart(2, '0');
|
|
267
129
|
return `${hourStr}:${minuteStr}`;
|
|
268
130
|
}
|
|
269
|
-
/**
|
|
270
|
-
* Find next editable position, skipping delimiter at position 2
|
|
271
|
-
*
|
|
272
|
-
* PORTED FROM: find-next-editable-position in date_picker.cljs
|
|
273
|
-
*/
|
|
274
131
|
function findNextEditablePosition(currentPos) {
|
|
275
132
|
switch (currentPos) {
|
|
276
|
-
case 0: return 1;
|
|
277
|
-
case 1: return 3;
|
|
278
|
-
case 3: return 4;
|
|
279
|
-
case 4: return 5;
|
|
280
|
-
case 5: return 5;
|
|
133
|
+
case 0: return 1;
|
|
134
|
+
case 1: return 3;
|
|
135
|
+
case 3: return 4;
|
|
136
|
+
case 4: return 5;
|
|
137
|
+
case 5: return 5;
|
|
281
138
|
default: return currentPos;
|
|
282
139
|
}
|
|
283
140
|
}
|
|
284
|
-
/**
|
|
285
|
-
* Find previous editable position, skipping delimiter at position 2
|
|
286
|
-
*
|
|
287
|
-
* PORTED FROM: find-prev-editable-position in date_picker.cljs
|
|
288
|
-
*/
|
|
289
141
|
function findPrevEditablePosition(currentPos) {
|
|
290
142
|
switch (currentPos) {
|
|
291
|
-
case 5: return 4;
|
|
292
|
-
case 4: return 3;
|
|
293
|
-
case 3: return 1;
|
|
294
|
-
case 1: return 0;
|
|
295
|
-
case 0: return 0;
|
|
143
|
+
case 5: return 4;
|
|
144
|
+
case 4: return 3;
|
|
145
|
+
case 3: return 1;
|
|
146
|
+
case 1: return 0;
|
|
147
|
+
case 0: return 0;
|
|
296
148
|
default: return currentPos;
|
|
297
149
|
}
|
|
298
150
|
}
|
|
299
|
-
/**
|
|
300
|
-
* Convert internal position (0,1,3,4) to raw digits index (0,1,2,3)
|
|
301
|
-
*
|
|
302
|
-
* PORTED FROM: position->raw-digit-index in date_picker.cljs
|
|
303
|
-
*/
|
|
304
151
|
function positionToRawDigitIndex(internalPos) {
|
|
305
152
|
switch (internalPos) {
|
|
306
|
-
case 0: return 0;
|
|
307
|
-
case 1: return 1;
|
|
308
|
-
case 3: return 2;
|
|
309
|
-
case 4: return 3;
|
|
153
|
+
case 0: return 0;
|
|
154
|
+
case 1: return 1;
|
|
155
|
+
case 3: return 2;
|
|
156
|
+
case 4: return 3;
|
|
310
157
|
default: return 0;
|
|
311
158
|
}
|
|
312
159
|
}
|
|
313
|
-
// ============================================================================
|
|
314
|
-
// TimeInput Helper Class
|
|
315
|
-
// ============================================================================
|
|
316
|
-
/**
|
|
317
|
-
* TimeInput manages the state and behavior of the time input element.
|
|
318
|
-
* Handles smart cursor navigation, digit replacement, and validation.
|
|
319
|
-
*
|
|
320
|
-
* PORTED FROM: Time input state management functions in date_picker.cljs
|
|
321
|
-
*/
|
|
322
160
|
class TimeInput {
|
|
323
161
|
constructor(element, datePickerElement, hour = 0, minute = 0) {
|
|
324
162
|
this.element = element;
|
|
325
163
|
this.datePickerElement = datePickerElement;
|
|
326
|
-
// Initialize state
|
|
327
164
|
const display = formatTimeDisplay(hour, minute);
|
|
328
165
|
const rawDigits = hour.toString().padStart(2, '0') + minute.toString().padStart(2, '0');
|
|
329
166
|
this.state = {
|
|
@@ -333,23 +170,15 @@ class TimeInput {
|
|
|
333
170
|
displayValue: display,
|
|
334
171
|
rawDigits,
|
|
335
172
|
};
|
|
336
|
-
// Set initial value
|
|
337
173
|
this.element.value = display;
|
|
338
|
-
// Bind event handlers
|
|
339
174
|
this.setupEventListeners();
|
|
340
175
|
}
|
|
341
|
-
/**
|
|
342
|
-
* Setup all event listeners for the time input
|
|
343
|
-
*/
|
|
344
176
|
setupEventListeners() {
|
|
345
177
|
this.element.addEventListener('keydown', (e) => this.handleKeyDown(e));
|
|
346
178
|
this.element.addEventListener('input', (e) => this.handleInput(e));
|
|
347
179
|
this.element.addEventListener('click', () => this.handleClick());
|
|
348
180
|
this.element.addEventListener('focus', () => this.handleFocus());
|
|
349
181
|
}
|
|
350
|
-
/**
|
|
351
|
-
* Handle keydown events
|
|
352
|
-
*/
|
|
353
182
|
handleKeyDown(event) {
|
|
354
183
|
const key = event.key;
|
|
355
184
|
switch (key) {
|
|
@@ -374,89 +203,52 @@ class TimeInput {
|
|
|
374
203
|
this.updateState({ caretPosition: 5 });
|
|
375
204
|
break;
|
|
376
205
|
case 'Tab':
|
|
377
|
-
// Allow default tab behavior
|
|
378
206
|
break;
|
|
379
207
|
default:
|
|
380
|
-
// Handle digit input
|
|
381
208
|
if (/^\d$/.test(key)) {
|
|
382
209
|
this.handleDigitInput(event, parseInt(key, 10));
|
|
383
210
|
}
|
|
384
211
|
break;
|
|
385
212
|
}
|
|
386
213
|
}
|
|
387
|
-
/**
|
|
388
|
-
* Handle input events (prevent default browser input)
|
|
389
|
-
*/
|
|
390
214
|
handleInput(event) {
|
|
391
215
|
event.preventDefault();
|
|
392
216
|
}
|
|
393
|
-
/**
|
|
394
|
-
* Handle click events - position cursor at first digit
|
|
395
|
-
*/
|
|
396
217
|
handleClick() {
|
|
397
218
|
this.updateState({ caretPosition: 0 });
|
|
398
219
|
}
|
|
399
|
-
/**
|
|
400
|
-
* Handle focus events - position cursor at first digit
|
|
401
|
-
*/
|
|
402
220
|
handleFocus() {
|
|
403
221
|
this.updateState({ caretPosition: 0 });
|
|
404
222
|
}
|
|
405
|
-
/**
|
|
406
|
-
* Handle arrow right - move to next editable position
|
|
407
|
-
*
|
|
408
|
-
* PORTED FROM: handle-time-arrow-right! in date_picker.cljs
|
|
409
|
-
*/
|
|
410
223
|
handleArrowRight(event) {
|
|
411
224
|
event.preventDefault();
|
|
412
225
|
const nextPos = findNextEditablePosition(this.state.caretPosition);
|
|
413
226
|
this.updateState({ caretPosition: nextPos });
|
|
414
227
|
}
|
|
415
|
-
/**
|
|
416
|
-
* Handle arrow left - move to previous editable position
|
|
417
|
-
*
|
|
418
|
-
* PORTED FROM: handle-time-arrow-left! in date_picker.cljs
|
|
419
|
-
*/
|
|
420
228
|
handleArrowLeft(event) {
|
|
421
229
|
event.preventDefault();
|
|
422
230
|
const prevPos = findPrevEditablePosition(this.state.caretPosition);
|
|
423
231
|
this.updateState({ caretPosition: prevPos });
|
|
424
232
|
}
|
|
425
|
-
/**
|
|
426
|
-
* Handle digit input - replace digit at cursor position
|
|
427
|
-
*
|
|
428
|
-
* PORTED FROM: handle-time-digit-input! in date_picker.cljs
|
|
429
|
-
*/
|
|
430
233
|
handleDigitInput(event, digit) {
|
|
431
234
|
event.preventDefault();
|
|
432
235
|
const currentPos = this.state.caretPosition;
|
|
433
|
-
// Only process if at editable position and digit is valid
|
|
434
236
|
if (![0, 1, 3, 4].includes(currentPos))
|
|
435
237
|
return;
|
|
436
238
|
if (!validateTimeDigit(digit, currentPos, this.state.rawDigits))
|
|
437
239
|
return;
|
|
438
|
-
// Replace digit at current position
|
|
439
240
|
const newState = this.replaceDigitAtPosition(currentPos, digit);
|
|
440
241
|
if (!newState)
|
|
441
242
|
return;
|
|
442
|
-
// Move to next position
|
|
443
243
|
const nextPos = findNextEditablePosition(currentPos);
|
|
444
244
|
this.updateState({ ...newState, caretPosition: nextPos });
|
|
445
|
-
// Notify date picker of time change
|
|
446
245
|
this.notifyTimeChange();
|
|
447
246
|
}
|
|
448
|
-
/**
|
|
449
|
-
* Handle backspace - zero digit and move back
|
|
450
|
-
*
|
|
451
|
-
* PORTED FROM: handle-time-backspace! in date_picker.cljs
|
|
452
|
-
*/
|
|
453
247
|
handleBackspace(event) {
|
|
454
248
|
event.preventDefault();
|
|
455
249
|
const currentPos = this.state.caretPosition;
|
|
456
|
-
// Can't go back from position 0
|
|
457
250
|
if (currentPos === 0)
|
|
458
251
|
return;
|
|
459
|
-
// Find target position to zero
|
|
460
252
|
const targetPos = currentPos === 1 ? 0 :
|
|
461
253
|
currentPos === 3 ? 1 :
|
|
462
254
|
currentPos === 4 ? 3 :
|
|
@@ -467,15 +259,9 @@ class TimeInput {
|
|
|
467
259
|
this.updateState({ ...newState, caretPosition: targetPos });
|
|
468
260
|
this.notifyTimeChange();
|
|
469
261
|
}
|
|
470
|
-
/**
|
|
471
|
-
* Handle delete - zero digit at current position
|
|
472
|
-
*
|
|
473
|
-
* PORTED FROM: handle-time-delete! in date_picker.cljs
|
|
474
|
-
*/
|
|
475
262
|
handleDelete(event) {
|
|
476
263
|
event.preventDefault();
|
|
477
264
|
const currentPos = this.state.caretPosition;
|
|
478
|
-
// Only at editable positions
|
|
479
265
|
if (![0, 1, 3, 4].includes(currentPos))
|
|
480
266
|
return;
|
|
481
267
|
const newState = this.zeroDigitAtPosition(currentPos);
|
|
@@ -484,11 +270,6 @@ class TimeInput {
|
|
|
484
270
|
this.updateState({ ...newState, caretPosition: currentPos });
|
|
485
271
|
this.notifyTimeChange();
|
|
486
272
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Replace digit at specific position
|
|
489
|
-
*
|
|
490
|
-
* PORTED FROM: replace-digit-at-position in date_picker.cljs
|
|
491
|
-
*/
|
|
492
273
|
replaceDigitAtPosition(position, newDigit) {
|
|
493
274
|
const rawIndex = positionToRawDigitIndex(position);
|
|
494
275
|
const digitsArray = this.state.rawDigits.split('');
|
|
@@ -504,24 +285,12 @@ class TimeInput {
|
|
|
504
285
|
displayValue: formatTimeDisplay(parsed.hour, parsed.minute),
|
|
505
286
|
};
|
|
506
287
|
}
|
|
507
|
-
/**
|
|
508
|
-
* Zero digit at specific position
|
|
509
|
-
*
|
|
510
|
-
* PORTED FROM: zero-digit-at-position in date_picker.cljs
|
|
511
|
-
*/
|
|
512
288
|
zeroDigitAtPosition(position) {
|
|
513
289
|
return this.replaceDigitAtPosition(position, 0);
|
|
514
290
|
}
|
|
515
|
-
/**
|
|
516
|
-
* Update internal state and refresh display
|
|
517
|
-
*
|
|
518
|
-
* PORTED FROM: update-time-input-state! in date_picker.cljs
|
|
519
|
-
*/
|
|
520
291
|
updateState(updates) {
|
|
521
292
|
this.state = { ...this.state, ...updates };
|
|
522
|
-
// Update display value
|
|
523
293
|
this.element.value = this.state.displayValue;
|
|
524
|
-
// Set cursor position, mapping internal positions to DOM positions
|
|
525
294
|
const caretPos = this.state.caretPosition;
|
|
526
295
|
const actualPos = caretPos === 0 ? 0 :
|
|
527
296
|
caretPos === 1 ? 1 :
|
|
@@ -531,24 +300,15 @@ class TimeInput {
|
|
|
531
300
|
caretPos === 5 ? 5 : caretPos;
|
|
532
301
|
this.element.setSelectionRange(actualPos, actualPos);
|
|
533
302
|
}
|
|
534
|
-
/**
|
|
535
|
-
* Notify date picker of time change
|
|
536
|
-
*/
|
|
537
303
|
notifyTimeChange() {
|
|
538
304
|
this.datePickerElement.handleTimeInputChange(this.state.hour, this.state.minute);
|
|
539
305
|
}
|
|
540
|
-
/**
|
|
541
|
-
* Get current time values
|
|
542
|
-
*/
|
|
543
306
|
getTime() {
|
|
544
307
|
return {
|
|
545
308
|
hour: this.state.hour,
|
|
546
309
|
minute: this.state.minute,
|
|
547
310
|
};
|
|
548
311
|
}
|
|
549
|
-
/**
|
|
550
|
-
* Update time from external source
|
|
551
|
-
*/
|
|
552
312
|
setTime(hour, minute) {
|
|
553
313
|
const display = formatTimeDisplay(hour, minute);
|
|
554
314
|
const rawDigits = hour.toString().padStart(2, '0') + minute.toString().padStart(2, '0');
|
|
@@ -562,76 +322,43 @@ class TimeInput {
|
|
|
562
322
|
this.element.value = display;
|
|
563
323
|
}
|
|
564
324
|
}
|
|
565
|
-
// ============================================================================
|
|
566
|
-
// TyDatePicker Custom Element
|
|
567
|
-
// ============================================================================
|
|
568
325
|
export class TyDatePicker extends TyComponent {
|
|
569
|
-
// observedAttributes is auto-generated by TyComponent from properties config
|
|
570
326
|
constructor() {
|
|
571
|
-
super();
|
|
572
|
-
// ============================================================================
|
|
573
|
-
// INTERNAL STATE
|
|
574
|
-
// ============================================================================
|
|
575
|
-
// Internal state
|
|
327
|
+
super();
|
|
576
328
|
this._state = {
|
|
577
329
|
withTime: false,
|
|
578
330
|
open: false,
|
|
579
331
|
};
|
|
580
|
-
// Reopen guard — prevents click event from reopening after pointerdown close
|
|
581
332
|
this._closeTimestamp = 0;
|
|
582
333
|
this._customFlavorSheet = null;
|
|
583
|
-
// Initialize styles in shadow root
|
|
584
334
|
const shadow = this.shadowRoot;
|
|
585
335
|
ensureStyles(shadow, { css: datePickerStyles, id: 'ty-date-picker' });
|
|
586
336
|
}
|
|
587
|
-
// ==========================================================================
|
|
588
|
-
// Lifecycle Hooks (TyComponent)
|
|
589
|
-
// ==========================================================================
|
|
590
|
-
/**
|
|
591
|
-
* Called when component connects to DOM
|
|
592
|
-
* TyComponent handles property capture automatically
|
|
593
|
-
*/
|
|
594
337
|
onConnect() {
|
|
595
|
-
// Initialize component state
|
|
596
338
|
this.initializeState();
|
|
597
|
-
// Initial render
|
|
598
339
|
this.render();
|
|
599
|
-
// Setup locale observer to watch for ancestor lang changes
|
|
600
340
|
this._localeObserver = observeLocaleChanges(this, () => {
|
|
601
341
|
this.render();
|
|
602
342
|
});
|
|
603
343
|
this._syncCustomFlavor();
|
|
604
344
|
}
|
|
605
|
-
/** Custom (non-built-in) flavors — see utils/flavor-sheet.ts. */
|
|
606
345
|
_syncCustomFlavor() {
|
|
607
|
-
// 'default' is this component's neutral: map it to a built-in so no
|
|
608
|
-
// custom sheet is generated for it.
|
|
609
346
|
const flavor = this.flavor === 'default' ? 'neutral' : this.flavor;
|
|
610
347
|
this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, flavor, ({ base }) => datePickerCustomFlavorCss(base));
|
|
611
348
|
}
|
|
612
|
-
/**
|
|
613
|
-
* Called when component disconnects from DOM
|
|
614
|
-
*/
|
|
615
349
|
onDisconnect() {
|
|
616
|
-
// Cleanup locale observer
|
|
617
350
|
if (this._localeObserver) {
|
|
618
351
|
this._localeObserver();
|
|
619
352
|
this._localeObserver = undefined;
|
|
620
353
|
}
|
|
621
|
-
// Cleanup event listeners and state
|
|
622
354
|
this.cleanup();
|
|
623
355
|
}
|
|
624
|
-
/**
|
|
625
|
-
* Called when properties change
|
|
626
|
-
* Handle state updates BEFORE render
|
|
627
|
-
*/
|
|
628
356
|
onPropertiesChanged(changes) {
|
|
629
357
|
for (const { name, newValue } of changes) {
|
|
630
358
|
switch (name) {
|
|
631
359
|
case 'flavor':
|
|
632
360
|
this._syncCustomFlavor();
|
|
633
361
|
break;
|
|
634
|
-
// Simple visual properties - just trigger re-render (handled by TyComponent)
|
|
635
362
|
case 'size':
|
|
636
363
|
case 'label':
|
|
637
364
|
case 'placeholder':
|
|
@@ -640,30 +367,21 @@ export class TyDatePicker extends TyComponent {
|
|
|
640
367
|
case 'clearable':
|
|
641
368
|
case 'format':
|
|
642
369
|
case 'locale':
|
|
643
|
-
// These properties just affect rendering, no internal state to update
|
|
644
|
-
// TyComponent will call render() automatically for visual properties
|
|
645
370
|
break;
|
|
646
|
-
// Complex properties
|
|
647
371
|
case 'value': {
|
|
648
|
-
// Parse the new value using current withTime setting
|
|
649
372
|
const newComponents = parseValue(newValue, this._state.withTime);
|
|
650
|
-
// If newComponents is null, CLEAR the state completely
|
|
651
373
|
if (newComponents === null) {
|
|
652
|
-
// Check if we actually have a date to clear
|
|
653
374
|
const hasDate = this._state.year !== undefined ||
|
|
654
375
|
this._state.month !== undefined ||
|
|
655
376
|
this._state.day !== undefined;
|
|
656
377
|
if (hasDate) {
|
|
657
|
-
// Clear all date components, keeping only withTime and open flags
|
|
658
378
|
this._state = {
|
|
659
379
|
withTime: this._state.withTime,
|
|
660
380
|
open: this._state.open,
|
|
661
|
-
// year, month, day, hour, minute are now undefined
|
|
662
381
|
};
|
|
663
382
|
}
|
|
664
383
|
break;
|
|
665
384
|
}
|
|
666
|
-
// Check if components actually changed
|
|
667
385
|
const currentComponents = {
|
|
668
386
|
year: this._state.year,
|
|
669
387
|
month: this._state.month,
|
|
@@ -677,7 +395,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
677
395
|
newComponents?.hour !== currentComponents.hour ||
|
|
678
396
|
newComponents?.minute !== currentComponents.minute;
|
|
679
397
|
if (changed) {
|
|
680
|
-
// Update state with new date components
|
|
681
398
|
this._state = {
|
|
682
399
|
...this._state,
|
|
683
400
|
...newComponents,
|
|
@@ -686,13 +403,10 @@ export class TyDatePicker extends TyComponent {
|
|
|
686
403
|
break;
|
|
687
404
|
}
|
|
688
405
|
case 'with-time': {
|
|
689
|
-
// Update internal withTime flag
|
|
690
406
|
const newWithTime = newValue;
|
|
691
407
|
const oldWithTime = this._state.withTime;
|
|
692
408
|
if (newWithTime !== oldWithTime) {
|
|
693
409
|
this._state.withTime = newWithTime;
|
|
694
|
-
// If we have an existing date, re-sync form value
|
|
695
|
-
// (format changes based on withTime flag)
|
|
696
410
|
const hasDate = this._state.year !== undefined &&
|
|
697
411
|
this._state.month !== undefined &&
|
|
698
412
|
this._state.day !== undefined;
|
|
@@ -705,20 +419,9 @@ export class TyDatePicker extends TyComponent {
|
|
|
705
419
|
}
|
|
706
420
|
}
|
|
707
421
|
}
|
|
708
|
-
// attributeChangedCallback removed - TyComponent handles all attribute changes via onPropertiesChanged
|
|
709
|
-
/**
|
|
710
|
-
* Get form value - returns UTC string from current state
|
|
711
|
-
* TyComponent calls this automatically when value property changes
|
|
712
|
-
*/
|
|
713
422
|
getFormValue() {
|
|
714
423
|
return componentsToOutputValue(this.getComponents());
|
|
715
424
|
}
|
|
716
|
-
// ==========================================================================
|
|
717
|
-
// Internal Helpers
|
|
718
|
-
// ==========================================================================
|
|
719
|
-
/**
|
|
720
|
-
* Extract current date components from internal state
|
|
721
|
-
*/
|
|
722
425
|
getComponents() {
|
|
723
426
|
return {
|
|
724
427
|
year: this._state.year,
|
|
@@ -728,34 +431,19 @@ export class TyDatePicker extends TyComponent {
|
|
|
728
431
|
minute: this._state.minute,
|
|
729
432
|
};
|
|
730
433
|
}
|
|
731
|
-
/**
|
|
732
|
-
* Whether internal state has a complete date
|
|
733
|
-
*/
|
|
734
434
|
hasDate() {
|
|
735
435
|
return !!(this._state.year && this._state.month && this._state.day);
|
|
736
436
|
}
|
|
737
|
-
/**
|
|
738
|
-
* Format current components for display using Intl API
|
|
739
|
-
*/
|
|
740
437
|
getFormattedValue(components) {
|
|
741
438
|
const c = components || this.getComponents();
|
|
742
439
|
if (!c.year || !c.month || !c.day)
|
|
743
440
|
return null;
|
|
744
441
|
return formatDisplayValue(c, this.getProperty('format') || 'long', getEffectiveLocale(this, this.getProperty('locale')), this._state.withTime);
|
|
745
442
|
}
|
|
746
|
-
// ==========================================================================
|
|
747
|
-
// State Management
|
|
748
|
-
// ==========================================================================
|
|
749
|
-
/**
|
|
750
|
-
* Initialize component state from attributes
|
|
751
|
-
*
|
|
752
|
-
* PORTED FROM: init-component-state! in date_picker.cljs
|
|
753
|
-
*/
|
|
754
443
|
initializeState() {
|
|
755
444
|
const valueAttr = this.getProperty('value');
|
|
756
445
|
const withTime = this.getProperty('with-time');
|
|
757
446
|
const components = parseValue(valueAttr, withTime);
|
|
758
|
-
// If components is null, initialize with empty state
|
|
759
447
|
if (components === null) {
|
|
760
448
|
this._state = {
|
|
761
449
|
withTime,
|
|
@@ -769,34 +457,19 @@ export class TyDatePicker extends TyComponent {
|
|
|
769
457
|
open: false,
|
|
770
458
|
};
|
|
771
459
|
}
|
|
772
|
-
// Convert to UTC and sync to form
|
|
773
460
|
this.syncFormValue();
|
|
774
461
|
}
|
|
775
|
-
/**
|
|
776
|
-
* Update internal state
|
|
777
|
-
*
|
|
778
|
-
* PORTED FROM: update-component-state! in date_picker.cljs
|
|
779
|
-
*/
|
|
780
462
|
updateState(updates, forceSync = false) {
|
|
781
463
|
this._state = { ...this._state, ...updates };
|
|
782
|
-
// STAGING: Only sync attributes if dialog is closed OR force-sync is true
|
|
783
|
-
// This prevents re-renders during time input editing
|
|
784
464
|
const shouldSync = forceSync || !this._state.open || !this._state.withTime;
|
|
785
465
|
if (shouldSync) {
|
|
786
466
|
this.syncFormValue();
|
|
787
467
|
}
|
|
788
468
|
}
|
|
789
|
-
/**
|
|
790
|
-
* Sync form value with current state
|
|
791
|
-
* Compares before setting to prevent circular triggers
|
|
792
|
-
*/
|
|
793
469
|
syncFormValue() {
|
|
794
470
|
const utcValue = this.getFormValue();
|
|
795
471
|
const currentValue = this.getProperty('value');
|
|
796
|
-
// Only update property if value actually changed (prevents circular triggers)
|
|
797
472
|
if (utcValue !== currentValue) {
|
|
798
|
-
// Use property setter to maintain TyComponent lifecycle
|
|
799
|
-
// This will automatically handle attribute sync and form value update
|
|
800
473
|
if (utcValue) {
|
|
801
474
|
this.setProperty('value', utcValue);
|
|
802
475
|
}
|
|
@@ -804,17 +477,7 @@ export class TyDatePicker extends TyComponent {
|
|
|
804
477
|
this.setProperty('value', null);
|
|
805
478
|
}
|
|
806
479
|
}
|
|
807
|
-
|
|
808
|
-
}
|
|
809
|
-
// handleValueChange removed - logic moved to onPropertiesChanged hook
|
|
810
|
-
// ==========================================================================
|
|
811
|
-
// Time Input Handling
|
|
812
|
-
// ==========================================================================
|
|
813
|
-
/**
|
|
814
|
-
* Handle time input changes from TimeInput class
|
|
815
|
-
*
|
|
816
|
-
* PORTED FROM: handle-time-change! in date_picker.cljs
|
|
817
|
-
*/
|
|
480
|
+
}
|
|
818
481
|
handleTimeInputChange(hour, minute) {
|
|
819
482
|
if (!this.hasDate())
|
|
820
483
|
return;
|
|
@@ -822,11 +485,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
822
485
|
this.updateState(components, true);
|
|
823
486
|
this.emitChangeEvent(components, 'time-change');
|
|
824
487
|
}
|
|
825
|
-
/**
|
|
826
|
-
* Emit change event
|
|
827
|
-
*
|
|
828
|
-
* PORTED FROM: emit-change-event! in date_picker.cljs
|
|
829
|
-
*/
|
|
830
488
|
emitChangeEvent(components, source) {
|
|
831
489
|
const utcValue = components ? componentsToOutputValue(components) : null;
|
|
832
490
|
const localValue = components ? componentsToLocalValue(components, this._state.withTime) : null;
|
|
@@ -846,10 +504,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
846
504
|
});
|
|
847
505
|
this.dispatchEvent(event);
|
|
848
506
|
}
|
|
849
|
-
// ==========================================================================
|
|
850
|
-
// Public API (Properties) - Simple Accessors
|
|
851
|
-
// ==========================================================================
|
|
852
|
-
// String properties
|
|
853
507
|
get size() { return this.getProperty('size'); }
|
|
854
508
|
set size(v) { this.setProperty('size', v); }
|
|
855
509
|
get flavor() { return this.getProperty('flavor'); }
|
|
@@ -864,7 +518,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
864
518
|
set format(v) { this.setProperty('format', v); }
|
|
865
519
|
get locale() { return this.getProperty('locale'); }
|
|
866
520
|
set locale(v) { this.setProperty('locale', v); }
|
|
867
|
-
// Boolean properties
|
|
868
521
|
get required() { return this.getProperty('required'); }
|
|
869
522
|
set required(v) { this.setProperty('required', v); }
|
|
870
523
|
get disabled() { return this.getProperty('disabled'); }
|
|
@@ -875,39 +528,23 @@ export class TyDatePicker extends TyComponent {
|
|
|
875
528
|
set min(v) { this.setProperty('min', v ?? ''); }
|
|
876
529
|
get max() { return this.getProperty('max'); }
|
|
877
530
|
set max(v) { this.setProperty('max', v ?? ''); }
|
|
878
|
-
// With-time property
|
|
879
531
|
get withTime() { return this.getProperty('with-time'); }
|
|
880
532
|
set withTime(v) { this.setProperty('with-time', v); }
|
|
881
|
-
// Value property - Keep custom getter/setter for now (complex UTC parsing logic)
|
|
882
|
-
/**
|
|
883
|
-
* Get current value (UTC ISO string)
|
|
884
|
-
*/
|
|
885
533
|
get value() {
|
|
886
534
|
return this.getProperty('value') || null;
|
|
887
535
|
}
|
|
888
|
-
/**
|
|
889
|
-
* Set value (UTC ISO string, Date object, or null)
|
|
890
|
-
*
|
|
891
|
-
* When set to null/undefined/empty string, the attribute is removed.
|
|
892
|
-
* When set to a valid date, the attribute is set to ISO UTC string.
|
|
893
|
-
*/
|
|
894
536
|
set value(val) {
|
|
895
537
|
if (val === null || val === undefined || val === '') {
|
|
896
|
-
this.setProperty('value', null);
|
|
538
|
+
this.setProperty('value', null);
|
|
897
539
|
}
|
|
898
540
|
else {
|
|
899
541
|
const strValue = val instanceof Date ? val.toISOString() : val;
|
|
900
542
|
this.setProperty('value', strValue);
|
|
901
543
|
}
|
|
902
544
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
/**
|
|
907
|
-
* Build CSS classes for the stub element
|
|
908
|
-
*
|
|
909
|
-
* PORTED FROM: build-stub-classes in date_picker.cljs
|
|
910
|
-
*/
|
|
545
|
+
clear() {
|
|
546
|
+
this.clearValue();
|
|
547
|
+
}
|
|
911
548
|
buildStubClasses() {
|
|
912
549
|
const classes = ['date-picker-stub'];
|
|
913
550
|
const size = this.getProperty('size') || 'md';
|
|
@@ -920,11 +557,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
920
557
|
classes.push('open');
|
|
921
558
|
return classes.join(' ');
|
|
922
559
|
}
|
|
923
|
-
/**
|
|
924
|
-
* Render the date picker stub (input display)
|
|
925
|
-
*
|
|
926
|
-
* PORTED FROM: render-date-picker-stub in date_picker.cljs
|
|
927
|
-
*/
|
|
928
560
|
renderStub() {
|
|
929
561
|
const stub = document.createElement('div');
|
|
930
562
|
stub.className = this.buildStubClasses();
|
|
@@ -932,11 +564,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
932
564
|
if (isDisabled) {
|
|
933
565
|
stub.setAttribute('disabled', 'true');
|
|
934
566
|
}
|
|
935
|
-
// Not text-editable (click/Enter/Space just opens the calendar dialog),
|
|
936
|
-
// so role="button" — not role="combobox", which implies typing. The
|
|
937
|
-
// popup is a real <dialog> opened via showModal() (see openDropdown),
|
|
938
|
-
// so it already gets role="dialog" + focus-trap from the browser for
|
|
939
|
-
// free; only the trigger itself needs wiring.
|
|
940
567
|
stub.setAttribute('role', 'button');
|
|
941
568
|
stub.setAttribute('aria-haspopup', 'dialog');
|
|
942
569
|
stub.setAttribute('aria-expanded', String(this._state.open));
|
|
@@ -946,9 +573,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
946
573
|
if (this.getProperty('label')) {
|
|
947
574
|
stub.setAttribute('aria-labelledby', `date-picker-label-${getElementHash(this)}`);
|
|
948
575
|
}
|
|
949
|
-
// Same gap as ty-select had: a plain div's Enter/Space don't turn into
|
|
950
|
-
// click events on their own, so without this a keyboard-only user could
|
|
951
|
-
// Tab to the field but never open it.
|
|
952
576
|
stub.addEventListener('keydown', (e) => {
|
|
953
577
|
const ke = e;
|
|
954
578
|
if (this._state.open)
|
|
@@ -958,10 +582,8 @@ export class TyDatePicker extends TyComponent {
|
|
|
958
582
|
this.handleStubClick(e);
|
|
959
583
|
}
|
|
960
584
|
});
|
|
961
|
-
// Start slot — leading icon (search, calendar variant, etc.)
|
|
962
585
|
const startSlot = document.createElement('slot');
|
|
963
586
|
startSlot.name = 'start';
|
|
964
|
-
// Display text
|
|
965
587
|
const displayText = document.createElement('span');
|
|
966
588
|
displayText.className = 'stub-text';
|
|
967
589
|
const formattedValue = this.getFormattedValue();
|
|
@@ -970,10 +592,8 @@ export class TyDatePicker extends TyComponent {
|
|
|
970
592
|
if (!formattedValue) {
|
|
971
593
|
displayText.classList.add('placeholder');
|
|
972
594
|
}
|
|
973
|
-
// Icons container
|
|
974
595
|
const iconContainer = document.createElement('div');
|
|
975
596
|
iconContainer.className = 'stub-icons';
|
|
976
|
-
// Clear button
|
|
977
597
|
const clearable = this.getProperty('clearable');
|
|
978
598
|
if (clearable && formattedValue && !isDisabled) {
|
|
979
599
|
const clearButton = document.createElement('button');
|
|
@@ -983,23 +603,16 @@ export class TyDatePicker extends TyComponent {
|
|
|
983
603
|
clearButton.addEventListener('click', (e) => this.handleClearClick(e));
|
|
984
604
|
iconContainer.appendChild(clearButton);
|
|
985
605
|
}
|
|
986
|
-
// Calendar icon
|
|
987
606
|
const calendarIcon = document.createElement('span');
|
|
988
607
|
calendarIcon.className = 'stub-arrow';
|
|
989
608
|
calendarIcon.innerHTML = CALENDAR_ICON_SVG;
|
|
990
609
|
iconContainer.appendChild(calendarIcon);
|
|
991
|
-
// Stub click handler
|
|
992
610
|
stub.addEventListener('click', (e) => this.handleStubClick(e));
|
|
993
611
|
stub.appendChild(startSlot);
|
|
994
612
|
stub.appendChild(displayText);
|
|
995
613
|
stub.appendChild(iconContainer);
|
|
996
614
|
return stub;
|
|
997
615
|
}
|
|
998
|
-
/**
|
|
999
|
-
* Create time input element
|
|
1000
|
-
*
|
|
1001
|
-
* PORTED FROM: create-time-input in date_picker.cljs
|
|
1002
|
-
*/
|
|
1003
616
|
createTimeInputElement() {
|
|
1004
617
|
const input = document.createElement('input');
|
|
1005
618
|
input.type = 'text';
|
|
@@ -1009,9 +622,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1009
622
|
input.maxLength = 5;
|
|
1010
623
|
return input;
|
|
1011
624
|
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Render time input section
|
|
1014
|
-
*/
|
|
1015
625
|
renderTimeSection() {
|
|
1016
626
|
const timeSection = document.createElement('div');
|
|
1017
627
|
timeSection.className = 'time-section';
|
|
@@ -1019,7 +629,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1019
629
|
timeLabel.className = 'time-label';
|
|
1020
630
|
timeLabel.textContent = 'Time:';
|
|
1021
631
|
const timeInputElement = this.createTimeInputElement();
|
|
1022
|
-
// Create TimeInput instance
|
|
1023
632
|
const hour = this._state.hour || 0;
|
|
1024
633
|
const minute = this._state.minute || 0;
|
|
1025
634
|
this._timeInput = new TimeInput(timeInputElement, this, hour, minute);
|
|
@@ -1031,19 +640,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1031
640
|
timeSection.appendChild(timeIcon);
|
|
1032
641
|
return timeSection;
|
|
1033
642
|
}
|
|
1034
|
-
/**
|
|
1035
|
-
* Render calendar dropdown with dialog
|
|
1036
|
-
*
|
|
1037
|
-
* PORTED FROM: render-calendar-dropdown in date_picker.cljs
|
|
1038
|
-
*/
|
|
1039
643
|
renderCalendarDropdown() {
|
|
1040
644
|
const dialog = document.createElement('dialog');
|
|
1041
645
|
dialog.className = 'calendar-dialog';
|
|
1042
646
|
const contentWrapper = document.createElement('div');
|
|
1043
647
|
contentWrapper.className = 'calendar-content';
|
|
1044
|
-
// Create ty-calendar
|
|
1045
648
|
const calendar = document.createElement('ty-calendar');
|
|
1046
|
-
// Set current date if available
|
|
1047
649
|
if (this._state.year && this._state.month && this._state.day) {
|
|
1048
650
|
calendar.setAttribute('year', this._state.year.toString());
|
|
1049
651
|
calendar.setAttribute('month', this._state.month.toString());
|
|
@@ -1053,38 +655,26 @@ export class TyDatePicker extends TyComponent {
|
|
|
1053
655
|
if (locale) {
|
|
1054
656
|
calendar.setAttribute('locale', locale);
|
|
1055
657
|
}
|
|
1056
|
-
// Pass through date bounds
|
|
1057
658
|
const min = this.getProperty('min');
|
|
1058
659
|
const max = this.getProperty('max');
|
|
1059
660
|
if (min)
|
|
1060
661
|
calendar.setAttribute('min', min);
|
|
1061
662
|
if (max)
|
|
1062
663
|
calendar.setAttribute('max', max);
|
|
1063
|
-
// Match the popup calendar to the stub's flavor. 'default' is the
|
|
1064
|
-
// stub's own unstyled state, not a real flavor — leave the calendar on
|
|
1065
|
-
// its own default rather than forwarding the literal string.
|
|
1066
664
|
const flavor = this.getProperty('flavor');
|
|
1067
665
|
if (flavor && flavor !== 'default')
|
|
1068
666
|
calendar.setAttribute('flavor', flavor);
|
|
1069
|
-
// Add calendar change handler
|
|
1070
667
|
calendar.addEventListener('change', (e) => this.handleCalendarChange(e));
|
|
1071
668
|
contentWrapper.appendChild(calendar);
|
|
1072
|
-
// Add time section if with-time enabled
|
|
1073
669
|
if (this._state.withTime) {
|
|
1074
670
|
contentWrapper.appendChild(this.renderTimeSection());
|
|
1075
671
|
}
|
|
1076
672
|
dialog.appendChild(contentWrapper);
|
|
1077
|
-
// Dialog close handler
|
|
1078
673
|
dialog.addEventListener('close', () => {
|
|
1079
674
|
this.updateState({ open: false });
|
|
1080
675
|
});
|
|
1081
676
|
return dialog;
|
|
1082
677
|
}
|
|
1083
|
-
/**
|
|
1084
|
-
* Render native date input for mobile touch devices.
|
|
1085
|
-
* Uses <input type="date"> or <input type="datetime-local"> when with-time.
|
|
1086
|
-
* Reuses .date-picker-stub styling and existing event/state infrastructure.
|
|
1087
|
-
*/
|
|
1088
678
|
renderNativeInput() {
|
|
1089
679
|
const wrapper = document.createElement('div');
|
|
1090
680
|
wrapper.className = 'dropdown-wrapper';
|
|
@@ -1094,7 +684,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1094
684
|
if (isDisabled) {
|
|
1095
685
|
stub.setAttribute('disabled', 'true');
|
|
1096
686
|
}
|
|
1097
|
-
// Native input (hidden, activated via picker indicator)
|
|
1098
687
|
const input = document.createElement('input');
|
|
1099
688
|
input.className = 'native-date-input';
|
|
1100
689
|
input.type = this._state.withTime ? 'datetime-local' : 'date';
|
|
@@ -1102,25 +691,21 @@ export class TyDatePicker extends TyComponent {
|
|
|
1102
691
|
input.disabled = true;
|
|
1103
692
|
if (this.getProperty('required'))
|
|
1104
693
|
input.required = true;
|
|
1105
|
-
// Native min/max (datetime-local wants a time suffix)
|
|
1106
694
|
const minBound = this.getProperty('min');
|
|
1107
695
|
const maxBound = this.getProperty('max');
|
|
1108
696
|
if (minBound)
|
|
1109
697
|
input.min = this._state.withTime ? `${minBound}T00:00` : minBound;
|
|
1110
698
|
if (maxBound)
|
|
1111
699
|
input.max = this._state.withTime ? `${maxBound}T23:59` : maxBound;
|
|
1112
|
-
// Pre-fill value
|
|
1113
700
|
const localValue = componentsToLocalValue(this.getComponents(), this._state.withTime);
|
|
1114
701
|
if (localValue) {
|
|
1115
702
|
input.value = localValue;
|
|
1116
703
|
}
|
|
1117
|
-
// Placeholder span (native date inputs ignore placeholder attr)
|
|
1118
704
|
const placeholder = this.getProperty('placeholder') || 'Select date...';
|
|
1119
705
|
const formattedValue = this.getFormattedValue();
|
|
1120
706
|
const placeholderEl = document.createElement('span');
|
|
1121
707
|
placeholderEl.className = 'stub-text' + (formattedValue ? '' : ' placeholder');
|
|
1122
708
|
placeholderEl.textContent = formattedValue || placeholder;
|
|
1123
|
-
// Change listener
|
|
1124
709
|
input.addEventListener('change', () => {
|
|
1125
710
|
if (!input.value) {
|
|
1126
711
|
this.clearValue();
|
|
@@ -1132,13 +717,11 @@ export class TyDatePicker extends TyComponent {
|
|
|
1132
717
|
this.emitChangeEvent(parsed, 'selection');
|
|
1133
718
|
}
|
|
1134
719
|
});
|
|
1135
|
-
// Start slot — leading icon
|
|
1136
720
|
const startSlot = document.createElement('slot');
|
|
1137
721
|
startSlot.name = 'start';
|
|
1138
722
|
stub.appendChild(startSlot);
|
|
1139
723
|
stub.appendChild(input);
|
|
1140
724
|
stub.appendChild(placeholderEl);
|
|
1141
|
-
// Icons container (clear + calendar icon)
|
|
1142
725
|
const iconContainer = document.createElement('div');
|
|
1143
726
|
iconContainer.className = 'stub-icons';
|
|
1144
727
|
const clearable = this.getProperty('clearable');
|
|
@@ -1158,15 +741,9 @@ export class TyDatePicker extends TyComponent {
|
|
|
1158
741
|
wrapper.appendChild(stub);
|
|
1159
742
|
return wrapper;
|
|
1160
743
|
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Calculate calendar position
|
|
1163
|
-
*
|
|
1164
|
-
* PORTED FROM: calculate-calendar-position! in date_picker.cljs
|
|
1165
|
-
*/
|
|
1166
744
|
calculateCalendarPosition() {
|
|
1167
745
|
if (!this.shadowRoot)
|
|
1168
746
|
return;
|
|
1169
|
-
// Mobile uses native input, no positioning needed
|
|
1170
747
|
if (isMobileTouch())
|
|
1171
748
|
return;
|
|
1172
749
|
const stub = this.shadowRoot.querySelector('.date-picker-stub');
|
|
@@ -1174,7 +751,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1174
751
|
if (!stub || !dialog)
|
|
1175
752
|
return;
|
|
1176
753
|
const stubRect = stub.getBoundingClientRect();
|
|
1177
|
-
// Measure when shown; the estimate only covers the pre-layout call
|
|
1178
754
|
const dialogRect = dialog.getBoundingClientRect();
|
|
1179
755
|
const estimatedHeight = dialogRect.height || 400;
|
|
1180
756
|
const popupWidth = dialogRect.width || 320;
|
|
@@ -1188,12 +764,10 @@ export class TyDatePicker extends TyComponent {
|
|
|
1188
764
|
const positionBelow = pos.below;
|
|
1189
765
|
const x = pos.x - wrapPadding;
|
|
1190
766
|
const y = positionBelow ? pos.topY : pos.bottomY;
|
|
1191
|
-
// Set CSS variables for positioning
|
|
1192
767
|
this.style.setProperty('--calendar-x', `${x}px`);
|
|
1193
768
|
this.style.setProperty('--calendar-y', `${y}px`);
|
|
1194
769
|
this.style.setProperty('--calendar-offset-x', '0px');
|
|
1195
770
|
this.style.setProperty('--calendar-offset-y', '0px');
|
|
1196
|
-
// Set direction classes
|
|
1197
771
|
if (positionBelow) {
|
|
1198
772
|
dialog.classList.add('position-below');
|
|
1199
773
|
dialog.classList.remove('position-above');
|
|
@@ -1203,11 +777,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1203
777
|
dialog.classList.remove('position-below');
|
|
1204
778
|
}
|
|
1205
779
|
}
|
|
1206
|
-
/**
|
|
1207
|
-
* Render container structure
|
|
1208
|
-
*
|
|
1209
|
-
* PORTED FROM: render-container-structure in date_picker.cljs
|
|
1210
|
-
*/
|
|
1211
780
|
renderContainer() {
|
|
1212
781
|
const container = document.createElement('div');
|
|
1213
782
|
container.className = 'dropdown-container';
|
|
@@ -1221,40 +790,29 @@ export class TyDatePicker extends TyComponent {
|
|
|
1221
790
|
}
|
|
1222
791
|
return container;
|
|
1223
792
|
}
|
|
1224
|
-
/**
|
|
1225
|
-
* Main render method
|
|
1226
|
-
*
|
|
1227
|
-
* PORTED FROM: render! in date_picker.cljs
|
|
1228
|
-
*/
|
|
1229
793
|
render() {
|
|
1230
794
|
if (!this.shadowRoot)
|
|
1231
795
|
return;
|
|
1232
796
|
ensureStyles(this.shadowRoot, { css: datePickerStyles, id: 'ty-date-picker' });
|
|
1233
|
-
// Mobile: native input, no dialog/event listeners/scroll lock
|
|
1234
797
|
if (isMobileTouch()) {
|
|
1235
798
|
const existingNativeInput = this.shadowRoot.querySelector('.native-date-input');
|
|
1236
799
|
if (existingNativeInput) {
|
|
1237
|
-
// PARTIAL UPDATE: don't destroy DOM while native picker may be open
|
|
1238
800
|
this.updateDisplay();
|
|
1239
801
|
return;
|
|
1240
802
|
}
|
|
1241
|
-
// FIRST RENDER: build from scratch
|
|
1242
803
|
this.shadowRoot.innerHTML = '';
|
|
1243
804
|
const container = this.renderContainer();
|
|
1244
805
|
container.appendChild(this.renderNativeInput());
|
|
1245
806
|
this.shadowRoot.appendChild(container);
|
|
1246
807
|
return;
|
|
1247
808
|
}
|
|
1248
|
-
// Check if dialog is currently open
|
|
1249
809
|
const existingDialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1250
810
|
const isDialogOpen = existingDialog && existingDialog.open;
|
|
1251
811
|
if (isDialogOpen && this._state.open) {
|
|
1252
|
-
// PARTIAL UPDATE: Dialog is open - just update display
|
|
1253
812
|
this.updateDisplay();
|
|
1254
813
|
this.calculateCalendarPosition();
|
|
1255
814
|
return;
|
|
1256
815
|
}
|
|
1257
|
-
// FULL REBUILD: Dialog is closed or doesn't exist
|
|
1258
816
|
this.shadowRoot.innerHTML = '';
|
|
1259
817
|
const container = this.renderContainer();
|
|
1260
818
|
const wrapper = document.createElement('div');
|
|
@@ -1263,14 +821,8 @@ export class TyDatePicker extends TyComponent {
|
|
|
1263
821
|
wrapper.appendChild(this.renderCalendarDropdown());
|
|
1264
822
|
container.appendChild(wrapper);
|
|
1265
823
|
this.shadowRoot.appendChild(container);
|
|
1266
|
-
// Setup document-level event listeners
|
|
1267
824
|
this.setupEventListeners();
|
|
1268
825
|
}
|
|
1269
|
-
/**
|
|
1270
|
-
* Update display without destroying DOM (for open dialog)
|
|
1271
|
-
*
|
|
1272
|
-
* PORTED FROM: update-display! in date_picker.cljs
|
|
1273
|
-
*/
|
|
1274
826
|
updateDisplay() {
|
|
1275
827
|
if (!this.shadowRoot)
|
|
1276
828
|
return;
|
|
@@ -1279,18 +831,15 @@ export class TyDatePicker extends TyComponent {
|
|
|
1279
831
|
const hasValue = this.hasDate();
|
|
1280
832
|
const clearable = this.getProperty('clearable');
|
|
1281
833
|
const isDisabled = this.getProperty('disabled');
|
|
1282
|
-
// Update stub text (shared between mobile and desktop)
|
|
1283
834
|
const stubText = this.shadowRoot.querySelector('.stub-text');
|
|
1284
835
|
if (stubText) {
|
|
1285
836
|
stubText.textContent = formattedValue || placeholder;
|
|
1286
837
|
stubText.classList.toggle('placeholder', !formattedValue);
|
|
1287
838
|
}
|
|
1288
|
-
// Update clear button visibility
|
|
1289
839
|
const clearButton = this.shadowRoot.querySelector('.stub-clear');
|
|
1290
840
|
if (clearButton) {
|
|
1291
841
|
clearButton.style.display = (clearable && hasValue && !isDisabled) ? '' : 'none';
|
|
1292
842
|
}
|
|
1293
|
-
// Mobile: also sync native input value
|
|
1294
843
|
if (isMobileTouch()) {
|
|
1295
844
|
const nativeInput = this.shadowRoot.querySelector('.native-date-input');
|
|
1296
845
|
if (nativeInput) {
|
|
@@ -1299,17 +848,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1299
848
|
}
|
|
1300
849
|
return;
|
|
1301
850
|
}
|
|
1302
|
-
// Desktop: update calendar and time input
|
|
1303
851
|
const calendar = this.shadowRoot.querySelector('ty-calendar');
|
|
1304
852
|
if (calendar && hasValue) {
|
|
1305
853
|
calendar.setAttribute('year', this._state.year.toString());
|
|
1306
854
|
calendar.setAttribute('month', this._state.month.toString());
|
|
1307
855
|
calendar.setAttribute('day', this._state.day.toString());
|
|
1308
856
|
}
|
|
1309
|
-
// Keep an already-open popup's flavor in sync. render() only rebuilds
|
|
1310
|
-
// (and re-forwards flavor) while the dialog is CLOSED — while open it
|
|
1311
|
-
// takes this partial-update path instead, so without this the calendar
|
|
1312
|
-
// would stay stuck on whatever flavor was active when it was opened.
|
|
1313
857
|
if (calendar) {
|
|
1314
858
|
const flavor = this.getProperty('flavor');
|
|
1315
859
|
if (flavor && flavor !== 'default')
|
|
@@ -1321,55 +865,35 @@ export class TyDatePicker extends TyComponent {
|
|
|
1321
865
|
this._timeInput.setTime(this._state.hour, this._state.minute);
|
|
1322
866
|
}
|
|
1323
867
|
}
|
|
1324
|
-
/**
|
|
1325
|
-
* Setup document-level event listeners
|
|
1326
|
-
*/
|
|
1327
868
|
setupEventListeners() {
|
|
1328
|
-
// Remove old listeners if they exist
|
|
1329
869
|
if (this._clickListener) {
|
|
1330
870
|
document.removeEventListener('click', this._clickListener);
|
|
1331
871
|
}
|
|
1332
872
|
if (this._keydownListener) {
|
|
1333
873
|
document.removeEventListener('keydown', this._keydownListener);
|
|
1334
874
|
}
|
|
1335
|
-
// Create new listeners
|
|
1336
875
|
this._clickListener = (e) => this.handleOutsideClick(e);
|
|
1337
876
|
this._keydownListener = (e) => this.handleEscapeKey(e);
|
|
1338
|
-
// Add document listeners
|
|
1339
877
|
document.addEventListener('click', this._clickListener);
|
|
1340
878
|
document.addEventListener('keydown', this._keydownListener);
|
|
1341
|
-
// Add dialog click listener
|
|
1342
879
|
const dialog = this.shadowRoot?.querySelector('.calendar-dialog');
|
|
1343
880
|
if (dialog) {
|
|
1344
881
|
this._dialogClickListener = (e) => this.handleDialogClick(e);
|
|
1345
882
|
dialog.addEventListener('click', this._dialogClickListener);
|
|
1346
883
|
}
|
|
1347
884
|
}
|
|
1348
|
-
// ==========================================================================
|
|
1349
|
-
// Event Handlers
|
|
1350
|
-
// ==========================================================================
|
|
1351
885
|
handleStubClick(event) {
|
|
1352
886
|
event.preventDefault();
|
|
1353
|
-
// Heal state/DOM desync: a host re-render can replace the dialog element
|
|
1354
|
-
// while `open` is still true, stranding the picker (openDropdown
|
|
1355
|
-
// early-returns on _state.open). Reality wins: closed dialog → closed state.
|
|
1356
887
|
const dialog = this.shadowRoot?.querySelector('.calendar-dialog');
|
|
1357
888
|
if (this._state.open && (!dialog || !dialog.open)) {
|
|
1358
889
|
this.updateState({ open: false }, true);
|
|
1359
890
|
unlockScroll(`date-picker-${this.id || this.toString()}`);
|
|
1360
891
|
}
|
|
1361
892
|
const disabled = this.getProperty('disabled');
|
|
1362
|
-
// FIXME: Timestamp guard is a workaround. Calendar day cells use pointerdown,
|
|
1363
|
-
// which closes the dialog before the click event fires — the click then hits the
|
|
1364
|
-
// stub and reopens. Proper fix: switch calendar-month day cells from pointerdown
|
|
1365
|
-
// to click so the event is consumed before the dialog closes.
|
|
1366
893
|
if (!disabled && Date.now() - this._closeTimestamp > 300) {
|
|
1367
894
|
this.openDropdown();
|
|
1368
895
|
}
|
|
1369
896
|
}
|
|
1370
|
-
/**
|
|
1371
|
-
* Clear the date value, sync state, emit event, and re-render.
|
|
1372
|
-
*/
|
|
1373
897
|
clearValue() {
|
|
1374
898
|
this.updateState({
|
|
1375
899
|
year: undefined,
|
|
@@ -1387,11 +911,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1387
911
|
event.stopPropagation();
|
|
1388
912
|
this.clearValue();
|
|
1389
913
|
}
|
|
1390
|
-
/**
|
|
1391
|
-
* Handle calendar date selection
|
|
1392
|
-
*
|
|
1393
|
-
* PORTED FROM: handle-calendar-change! in date_picker.cljs
|
|
1394
|
-
*/
|
|
1395
914
|
handleCalendarChange(event) {
|
|
1396
915
|
event.preventDefault();
|
|
1397
916
|
event.stopPropagation();
|
|
@@ -1400,7 +919,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1400
919
|
const dayContext = detail.dayContext;
|
|
1401
920
|
if (!dayContext)
|
|
1402
921
|
return;
|
|
1403
|
-
// Update state with new date
|
|
1404
922
|
const newComponents = {
|
|
1405
923
|
year: dayContext.year,
|
|
1406
924
|
month: dayContext.month,
|
|
@@ -1408,15 +926,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1408
926
|
hour: this._state.hour,
|
|
1409
927
|
minute: this._state.minute,
|
|
1410
928
|
};
|
|
1411
|
-
// Force sync when calendar date changes
|
|
1412
929
|
this.updateState(newComponents, true);
|
|
1413
930
|
this.emitChangeEvent(newComponents, 'selection');
|
|
1414
|
-
// Close calendar if time is not required
|
|
1415
931
|
if (!this._state.withTime) {
|
|
1416
932
|
this.closeDropdown();
|
|
1417
933
|
}
|
|
1418
934
|
else {
|
|
1419
|
-
// Auto-focus time input after date selection
|
|
1420
935
|
requestAnimationFrame(() => {
|
|
1421
936
|
if (!this.shadowRoot)
|
|
1422
937
|
return;
|
|
@@ -1427,43 +942,26 @@ export class TyDatePicker extends TyComponent {
|
|
|
1427
942
|
});
|
|
1428
943
|
}
|
|
1429
944
|
}
|
|
1430
|
-
/**
|
|
1431
|
-
* Handle dialog backdrop clicks
|
|
1432
|
-
*
|
|
1433
|
-
* PORTED FROM: handle-dialog-click! in date_picker.cljs
|
|
1434
|
-
*/
|
|
1435
945
|
handleDialogClick(event) {
|
|
1436
946
|
if (!this.shadowRoot)
|
|
1437
947
|
return;
|
|
1438
948
|
const dialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1439
949
|
const content = this.shadowRoot.querySelector('.calendar-content');
|
|
1440
|
-
// Close if clicking on dialog backdrop (not calendar content)
|
|
1441
950
|
if (event.target === dialog && this._state.open && content && !content.contains(event.target)) {
|
|
1442
951
|
event.preventDefault();
|
|
1443
952
|
event.stopPropagation();
|
|
1444
953
|
this.closeDropdown();
|
|
1445
954
|
}
|
|
1446
955
|
}
|
|
1447
|
-
/**
|
|
1448
|
-
* Handle clicks outside the date picker
|
|
1449
|
-
*
|
|
1450
|
-
* PORTED FROM: handle-outside-click! in date_picker.cljs
|
|
1451
|
-
*/
|
|
1452
956
|
handleOutsideClick(event) {
|
|
1453
957
|
if (!this.shadowRoot)
|
|
1454
958
|
return;
|
|
1455
959
|
const target = event.target;
|
|
1456
960
|
const dialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1457
|
-
// Check if click is truly outside everything
|
|
1458
961
|
if (this._state.open && dialog && !this.contains(target) && !dialog.contains(target)) {
|
|
1459
962
|
this.closeDropdown();
|
|
1460
963
|
}
|
|
1461
964
|
}
|
|
1462
|
-
/**
|
|
1463
|
-
* Handle Escape key press
|
|
1464
|
-
*
|
|
1465
|
-
* PORTED FROM: handle-escape-key! in date_picker.cljs
|
|
1466
|
-
*/
|
|
1467
965
|
handleEscapeKey(event) {
|
|
1468
966
|
const keyboardEvent = event;
|
|
1469
967
|
if (keyboardEvent.key === 'Escape' && this._state.open) {
|
|
@@ -1471,18 +969,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1471
969
|
this.closeDropdown();
|
|
1472
970
|
}
|
|
1473
971
|
}
|
|
1474
|
-
/**
|
|
1475
|
-
* Open calendar dropdown
|
|
1476
|
-
*
|
|
1477
|
-
* PORTED FROM: open-dropdown! in date_picker.cljs
|
|
1478
|
-
*/
|
|
1479
972
|
openDropdown() {
|
|
1480
973
|
if (this._state.open)
|
|
1481
974
|
return;
|
|
1482
975
|
if (isMobileTouch())
|
|
1483
976
|
return;
|
|
1484
977
|
this.updateState({ open: true });
|
|
1485
|
-
// Dispatch open event
|
|
1486
978
|
this.dispatchEvent(new CustomEvent('open', {
|
|
1487
979
|
bubbles: true,
|
|
1488
980
|
composed: true
|
|
@@ -1501,7 +993,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1501
993
|
const stubEl = this.shadowRoot.querySelector('.date-picker-stub');
|
|
1502
994
|
if (stubEl)
|
|
1503
995
|
stubEl.setAttribute('aria-expanded', 'true');
|
|
1504
|
-
// Remove focus from any focused elements to prevent the blue outline
|
|
1505
996
|
const focusedElement = this.shadowRoot.activeElement;
|
|
1506
997
|
if (focusedElement) {
|
|
1507
998
|
focusedElement.blur();
|
|
@@ -1509,17 +1000,11 @@ export class TyDatePicker extends TyComponent {
|
|
|
1509
1000
|
}
|
|
1510
1001
|
});
|
|
1511
1002
|
}
|
|
1512
|
-
/**
|
|
1513
|
-
* Close calendar dropdown
|
|
1514
|
-
*
|
|
1515
|
-
* PORTED FROM: close-dropdown! in date_picker.cljs
|
|
1516
|
-
*/
|
|
1517
1003
|
closeDropdown() {
|
|
1518
1004
|
if (!this._state.open)
|
|
1519
1005
|
return;
|
|
1520
1006
|
this._closeTimestamp = Date.now();
|
|
1521
1007
|
const pickerId = `date-picker-${this.id || this.toString()}`;
|
|
1522
|
-
// Force sync any staged updates when closing
|
|
1523
1008
|
this.updateState({ open: false }, true);
|
|
1524
1009
|
unlockScroll(pickerId);
|
|
1525
1010
|
if (this.shadowRoot) {
|
|
@@ -1528,8 +1013,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1528
1013
|
dialog.classList.remove('position-above', 'position-below', 'open');
|
|
1529
1014
|
dialog.close();
|
|
1530
1015
|
}
|
|
1531
|
-
// closeDropdown() doesn't call render(), so the stub's aria-expanded
|
|
1532
|
-
// (still "true" from open) needs an explicit flip here too.
|
|
1533
1016
|
const stubEl = this.shadowRoot.querySelector('.date-picker-stub');
|
|
1534
1017
|
if (stubEl)
|
|
1535
1018
|
stubEl.setAttribute('aria-expanded', 'false');
|
|
@@ -1540,51 +1023,34 @@ export class TyDatePicker extends TyComponent {
|
|
|
1540
1023
|
}));
|
|
1541
1024
|
this.render();
|
|
1542
1025
|
}
|
|
1543
|
-
// ==========================================================================
|
|
1544
|
-
// Cleanup
|
|
1545
|
-
// ==========================================================================
|
|
1546
|
-
/**
|
|
1547
|
-
* Clean up event listeners and state
|
|
1548
|
-
*
|
|
1549
|
-
* PORTED FROM: cleanup! in date_picker.cljs
|
|
1550
|
-
*/
|
|
1551
1026
|
cleanup() {
|
|
1552
|
-
// Remove document listeners
|
|
1553
1027
|
if (this._clickListener) {
|
|
1554
1028
|
document.removeEventListener('click', this._clickListener);
|
|
1555
1029
|
}
|
|
1556
1030
|
if (this._keydownListener) {
|
|
1557
1031
|
document.removeEventListener('keydown', this._keydownListener);
|
|
1558
1032
|
}
|
|
1559
|
-
// Remove dialog listener
|
|
1560
1033
|
if (this._dialogClickListener && this.shadowRoot) {
|
|
1561
1034
|
const dialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1562
1035
|
if (dialog) {
|
|
1563
1036
|
dialog.removeEventListener('click', this._dialogClickListener);
|
|
1564
1037
|
}
|
|
1565
1038
|
}
|
|
1566
|
-
// Unlock scroll if open
|
|
1567
1039
|
if (this._state.open) {
|
|
1568
1040
|
const pickerId = `date-picker-${this.id || this.toString()}`;
|
|
1569
1041
|
unlockScroll(pickerId);
|
|
1570
1042
|
}
|
|
1571
|
-
// Clear references
|
|
1572
1043
|
this._clickListener = undefined;
|
|
1573
1044
|
this._keydownListener = undefined;
|
|
1574
1045
|
this._dialogClickListener = undefined;
|
|
1575
1046
|
this._timeInput = undefined;
|
|
1576
1047
|
}
|
|
1577
1048
|
}
|
|
1578
|
-
// ============================================================================
|
|
1579
|
-
// PROPERTY CONFIGURATION - Single source of truth
|
|
1580
|
-
// ============================================================================
|
|
1581
1049
|
TyDatePicker.properties = {
|
|
1582
|
-
// String properties
|
|
1583
1050
|
size: {
|
|
1584
1051
|
type: 'string',
|
|
1585
1052
|
visual: true,
|
|
1586
1053
|
default: 'md',
|
|
1587
|
-
// Fields come in exactly three sizes; legacy xs/xl map to sm/lg.
|
|
1588
1054
|
validate: (v) => ['sm', 'md', 'lg'].includes(v),
|
|
1589
1055
|
coerce: (v) => {
|
|
1590
1056
|
if (v === 'xs')
|
|
@@ -1615,10 +1081,9 @@ TyDatePicker.properties = {
|
|
|
1615
1081
|
},
|
|
1616
1082
|
name: {
|
|
1617
1083
|
type: 'string',
|
|
1618
|
-
visual: false,
|
|
1084
|
+
visual: false,
|
|
1619
1085
|
default: ''
|
|
1620
1086
|
},
|
|
1621
|
-
// Date bounds (ISO) — passed through to the embedded ty-calendar
|
|
1622
1087
|
min: {
|
|
1623
1088
|
type: 'string',
|
|
1624
1089
|
visual: true,
|
|
@@ -1629,7 +1094,6 @@ TyDatePicker.properties = {
|
|
|
1629
1094
|
visual: true,
|
|
1630
1095
|
default: ''
|
|
1631
1096
|
},
|
|
1632
|
-
// Boolean properties
|
|
1633
1097
|
required: {
|
|
1634
1098
|
type: 'boolean',
|
|
1635
1099
|
visual: true,
|
|
@@ -1648,7 +1112,6 @@ TyDatePicker.properties = {
|
|
|
1648
1112
|
'not-clearable': false
|
|
1649
1113
|
}
|
|
1650
1114
|
},
|
|
1651
|
-
// Format property (medium complexity)
|
|
1652
1115
|
format: {
|
|
1653
1116
|
type: 'string',
|
|
1654
1117
|
visual: true,
|
|
@@ -1663,34 +1126,25 @@ TyDatePicker.properties = {
|
|
|
1663
1126
|
return v;
|
|
1664
1127
|
}
|
|
1665
1128
|
},
|
|
1666
|
-
// Locale property (medium complexity - has observer)
|
|
1667
1129
|
locale: {
|
|
1668
1130
|
type: 'string',
|
|
1669
1131
|
visual: true,
|
|
1670
1132
|
default: ''
|
|
1671
1133
|
},
|
|
1672
|
-
// Value property (high complexity - UTC parsing, date components)
|
|
1673
|
-
// Note: Custom getter/setter will handle the complex logic
|
|
1674
1134
|
value: {
|
|
1675
1135
|
type: 'string',
|
|
1676
1136
|
visual: true,
|
|
1677
|
-
formValue: true,
|
|
1678
|
-
emitChange: false,
|
|
1679
|
-
default: null
|
|
1137
|
+
formValue: true,
|
|
1138
|
+
emitChange: false,
|
|
1139
|
+
default: null
|
|
1680
1140
|
},
|
|
1681
|
-
// With-time property (high complexity - affects parsing and rendering)
|
|
1682
1141
|
'with-time': {
|
|
1683
1142
|
type: 'boolean',
|
|
1684
1143
|
visual: true,
|
|
1685
1144
|
default: false
|
|
1686
1145
|
}
|
|
1687
1146
|
};
|
|
1688
|
-
/**
|
|
1689
|
-
* Form-associated custom element
|
|
1690
|
-
*/
|
|
1691
1147
|
TyDatePicker.formAssociated = true;
|
|
1692
|
-
// Register custom element
|
|
1693
1148
|
if (!customElements.get('ty-date-picker')) {
|
|
1694
1149
|
customElements.define('ty-date-picker', TyDatePicker);
|
|
1695
1150
|
}
|
|
1696
|
-
//# sourceMappingURL=date-picker.js.map
|