tyrell-components 1.0.0-TC45 → 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 +5 -162
- package/lib/components/date-picker.js +28 -583
- 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 +11 -150
- package/lib/components/select.js +7 -581
- 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 +2 -14
- 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 +0 -2
- package/lib/utils/positioning.js +0 -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,48 +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
|
-
* Clear the date value programmatically WITH a change event (source:
|
|
905
|
-
* 'clear'). Works regardless of the `clearable` attribute — lets any
|
|
906
|
-
* external trigger (e.g. a button elsewhere in the page) clear the value
|
|
907
|
-
* without needing the built-in clear button. Mirrors ty-select's clear().
|
|
908
|
-
*/
|
|
909
545
|
clear() {
|
|
910
546
|
this.clearValue();
|
|
911
547
|
}
|
|
912
|
-
// ==========================================================================
|
|
913
|
-
// Rendering & DOM Management
|
|
914
|
-
// ==========================================================================
|
|
915
|
-
/**
|
|
916
|
-
* Build CSS classes for the stub element
|
|
917
|
-
*
|
|
918
|
-
* PORTED FROM: build-stub-classes in date_picker.cljs
|
|
919
|
-
*/
|
|
920
548
|
buildStubClasses() {
|
|
921
549
|
const classes = ['date-picker-stub'];
|
|
922
550
|
const size = this.getProperty('size') || 'md';
|
|
@@ -929,11 +557,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
929
557
|
classes.push('open');
|
|
930
558
|
return classes.join(' ');
|
|
931
559
|
}
|
|
932
|
-
/**
|
|
933
|
-
* Render the date picker stub (input display)
|
|
934
|
-
*
|
|
935
|
-
* PORTED FROM: render-date-picker-stub in date_picker.cljs
|
|
936
|
-
*/
|
|
937
560
|
renderStub() {
|
|
938
561
|
const stub = document.createElement('div');
|
|
939
562
|
stub.className = this.buildStubClasses();
|
|
@@ -941,11 +564,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
941
564
|
if (isDisabled) {
|
|
942
565
|
stub.setAttribute('disabled', 'true');
|
|
943
566
|
}
|
|
944
|
-
// Not text-editable (click/Enter/Space just opens the calendar dialog),
|
|
945
|
-
// so role="button" — not role="combobox", which implies typing. The
|
|
946
|
-
// popup is a real <dialog> opened via showModal() (see openDropdown),
|
|
947
|
-
// so it already gets role="dialog" + focus-trap from the browser for
|
|
948
|
-
// free; only the trigger itself needs wiring.
|
|
949
567
|
stub.setAttribute('role', 'button');
|
|
950
568
|
stub.setAttribute('aria-haspopup', 'dialog');
|
|
951
569
|
stub.setAttribute('aria-expanded', String(this._state.open));
|
|
@@ -955,9 +573,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
955
573
|
if (this.getProperty('label')) {
|
|
956
574
|
stub.setAttribute('aria-labelledby', `date-picker-label-${getElementHash(this)}`);
|
|
957
575
|
}
|
|
958
|
-
// Same gap as ty-select had: a plain div's Enter/Space don't turn into
|
|
959
|
-
// click events on their own, so without this a keyboard-only user could
|
|
960
|
-
// Tab to the field but never open it.
|
|
961
576
|
stub.addEventListener('keydown', (e) => {
|
|
962
577
|
const ke = e;
|
|
963
578
|
if (this._state.open)
|
|
@@ -967,10 +582,8 @@ export class TyDatePicker extends TyComponent {
|
|
|
967
582
|
this.handleStubClick(e);
|
|
968
583
|
}
|
|
969
584
|
});
|
|
970
|
-
// Start slot — leading icon (search, calendar variant, etc.)
|
|
971
585
|
const startSlot = document.createElement('slot');
|
|
972
586
|
startSlot.name = 'start';
|
|
973
|
-
// Display text
|
|
974
587
|
const displayText = document.createElement('span');
|
|
975
588
|
displayText.className = 'stub-text';
|
|
976
589
|
const formattedValue = this.getFormattedValue();
|
|
@@ -979,10 +592,8 @@ export class TyDatePicker extends TyComponent {
|
|
|
979
592
|
if (!formattedValue) {
|
|
980
593
|
displayText.classList.add('placeholder');
|
|
981
594
|
}
|
|
982
|
-
// Icons container
|
|
983
595
|
const iconContainer = document.createElement('div');
|
|
984
596
|
iconContainer.className = 'stub-icons';
|
|
985
|
-
// Clear button
|
|
986
597
|
const clearable = this.getProperty('clearable');
|
|
987
598
|
if (clearable && formattedValue && !isDisabled) {
|
|
988
599
|
const clearButton = document.createElement('button');
|
|
@@ -992,23 +603,16 @@ export class TyDatePicker extends TyComponent {
|
|
|
992
603
|
clearButton.addEventListener('click', (e) => this.handleClearClick(e));
|
|
993
604
|
iconContainer.appendChild(clearButton);
|
|
994
605
|
}
|
|
995
|
-
// Calendar icon
|
|
996
606
|
const calendarIcon = document.createElement('span');
|
|
997
607
|
calendarIcon.className = 'stub-arrow';
|
|
998
608
|
calendarIcon.innerHTML = CALENDAR_ICON_SVG;
|
|
999
609
|
iconContainer.appendChild(calendarIcon);
|
|
1000
|
-
// Stub click handler
|
|
1001
610
|
stub.addEventListener('click', (e) => this.handleStubClick(e));
|
|
1002
611
|
stub.appendChild(startSlot);
|
|
1003
612
|
stub.appendChild(displayText);
|
|
1004
613
|
stub.appendChild(iconContainer);
|
|
1005
614
|
return stub;
|
|
1006
615
|
}
|
|
1007
|
-
/**
|
|
1008
|
-
* Create time input element
|
|
1009
|
-
*
|
|
1010
|
-
* PORTED FROM: create-time-input in date_picker.cljs
|
|
1011
|
-
*/
|
|
1012
616
|
createTimeInputElement() {
|
|
1013
617
|
const input = document.createElement('input');
|
|
1014
618
|
input.type = 'text';
|
|
@@ -1018,9 +622,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1018
622
|
input.maxLength = 5;
|
|
1019
623
|
return input;
|
|
1020
624
|
}
|
|
1021
|
-
/**
|
|
1022
|
-
* Render time input section
|
|
1023
|
-
*/
|
|
1024
625
|
renderTimeSection() {
|
|
1025
626
|
const timeSection = document.createElement('div');
|
|
1026
627
|
timeSection.className = 'time-section';
|
|
@@ -1028,7 +629,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1028
629
|
timeLabel.className = 'time-label';
|
|
1029
630
|
timeLabel.textContent = 'Time:';
|
|
1030
631
|
const timeInputElement = this.createTimeInputElement();
|
|
1031
|
-
// Create TimeInput instance
|
|
1032
632
|
const hour = this._state.hour || 0;
|
|
1033
633
|
const minute = this._state.minute || 0;
|
|
1034
634
|
this._timeInput = new TimeInput(timeInputElement, this, hour, minute);
|
|
@@ -1040,19 +640,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1040
640
|
timeSection.appendChild(timeIcon);
|
|
1041
641
|
return timeSection;
|
|
1042
642
|
}
|
|
1043
|
-
/**
|
|
1044
|
-
* Render calendar dropdown with dialog
|
|
1045
|
-
*
|
|
1046
|
-
* PORTED FROM: render-calendar-dropdown in date_picker.cljs
|
|
1047
|
-
*/
|
|
1048
643
|
renderCalendarDropdown() {
|
|
1049
644
|
const dialog = document.createElement('dialog');
|
|
1050
645
|
dialog.className = 'calendar-dialog';
|
|
1051
646
|
const contentWrapper = document.createElement('div');
|
|
1052
647
|
contentWrapper.className = 'calendar-content';
|
|
1053
|
-
// Create ty-calendar
|
|
1054
648
|
const calendar = document.createElement('ty-calendar');
|
|
1055
|
-
// Set current date if available
|
|
1056
649
|
if (this._state.year && this._state.month && this._state.day) {
|
|
1057
650
|
calendar.setAttribute('year', this._state.year.toString());
|
|
1058
651
|
calendar.setAttribute('month', this._state.month.toString());
|
|
@@ -1062,38 +655,26 @@ export class TyDatePicker extends TyComponent {
|
|
|
1062
655
|
if (locale) {
|
|
1063
656
|
calendar.setAttribute('locale', locale);
|
|
1064
657
|
}
|
|
1065
|
-
// Pass through date bounds
|
|
1066
658
|
const min = this.getProperty('min');
|
|
1067
659
|
const max = this.getProperty('max');
|
|
1068
660
|
if (min)
|
|
1069
661
|
calendar.setAttribute('min', min);
|
|
1070
662
|
if (max)
|
|
1071
663
|
calendar.setAttribute('max', max);
|
|
1072
|
-
// Match the popup calendar to the stub's flavor. 'default' is the
|
|
1073
|
-
// stub's own unstyled state, not a real flavor — leave the calendar on
|
|
1074
|
-
// its own default rather than forwarding the literal string.
|
|
1075
664
|
const flavor = this.getProperty('flavor');
|
|
1076
665
|
if (flavor && flavor !== 'default')
|
|
1077
666
|
calendar.setAttribute('flavor', flavor);
|
|
1078
|
-
// Add calendar change handler
|
|
1079
667
|
calendar.addEventListener('change', (e) => this.handleCalendarChange(e));
|
|
1080
668
|
contentWrapper.appendChild(calendar);
|
|
1081
|
-
// Add time section if with-time enabled
|
|
1082
669
|
if (this._state.withTime) {
|
|
1083
670
|
contentWrapper.appendChild(this.renderTimeSection());
|
|
1084
671
|
}
|
|
1085
672
|
dialog.appendChild(contentWrapper);
|
|
1086
|
-
// Dialog close handler
|
|
1087
673
|
dialog.addEventListener('close', () => {
|
|
1088
674
|
this.updateState({ open: false });
|
|
1089
675
|
});
|
|
1090
676
|
return dialog;
|
|
1091
677
|
}
|
|
1092
|
-
/**
|
|
1093
|
-
* Render native date input for mobile touch devices.
|
|
1094
|
-
* Uses <input type="date"> or <input type="datetime-local"> when with-time.
|
|
1095
|
-
* Reuses .date-picker-stub styling and existing event/state infrastructure.
|
|
1096
|
-
*/
|
|
1097
678
|
renderNativeInput() {
|
|
1098
679
|
const wrapper = document.createElement('div');
|
|
1099
680
|
wrapper.className = 'dropdown-wrapper';
|
|
@@ -1103,7 +684,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1103
684
|
if (isDisabled) {
|
|
1104
685
|
stub.setAttribute('disabled', 'true');
|
|
1105
686
|
}
|
|
1106
|
-
// Native input (hidden, activated via picker indicator)
|
|
1107
687
|
const input = document.createElement('input');
|
|
1108
688
|
input.className = 'native-date-input';
|
|
1109
689
|
input.type = this._state.withTime ? 'datetime-local' : 'date';
|
|
@@ -1111,25 +691,21 @@ export class TyDatePicker extends TyComponent {
|
|
|
1111
691
|
input.disabled = true;
|
|
1112
692
|
if (this.getProperty('required'))
|
|
1113
693
|
input.required = true;
|
|
1114
|
-
// Native min/max (datetime-local wants a time suffix)
|
|
1115
694
|
const minBound = this.getProperty('min');
|
|
1116
695
|
const maxBound = this.getProperty('max');
|
|
1117
696
|
if (minBound)
|
|
1118
697
|
input.min = this._state.withTime ? `${minBound}T00:00` : minBound;
|
|
1119
698
|
if (maxBound)
|
|
1120
699
|
input.max = this._state.withTime ? `${maxBound}T23:59` : maxBound;
|
|
1121
|
-
// Pre-fill value
|
|
1122
700
|
const localValue = componentsToLocalValue(this.getComponents(), this._state.withTime);
|
|
1123
701
|
if (localValue) {
|
|
1124
702
|
input.value = localValue;
|
|
1125
703
|
}
|
|
1126
|
-
// Placeholder span (native date inputs ignore placeholder attr)
|
|
1127
704
|
const placeholder = this.getProperty('placeholder') || 'Select date...';
|
|
1128
705
|
const formattedValue = this.getFormattedValue();
|
|
1129
706
|
const placeholderEl = document.createElement('span');
|
|
1130
707
|
placeholderEl.className = 'stub-text' + (formattedValue ? '' : ' placeholder');
|
|
1131
708
|
placeholderEl.textContent = formattedValue || placeholder;
|
|
1132
|
-
// Change listener
|
|
1133
709
|
input.addEventListener('change', () => {
|
|
1134
710
|
if (!input.value) {
|
|
1135
711
|
this.clearValue();
|
|
@@ -1141,13 +717,11 @@ export class TyDatePicker extends TyComponent {
|
|
|
1141
717
|
this.emitChangeEvent(parsed, 'selection');
|
|
1142
718
|
}
|
|
1143
719
|
});
|
|
1144
|
-
// Start slot — leading icon
|
|
1145
720
|
const startSlot = document.createElement('slot');
|
|
1146
721
|
startSlot.name = 'start';
|
|
1147
722
|
stub.appendChild(startSlot);
|
|
1148
723
|
stub.appendChild(input);
|
|
1149
724
|
stub.appendChild(placeholderEl);
|
|
1150
|
-
// Icons container (clear + calendar icon)
|
|
1151
725
|
const iconContainer = document.createElement('div');
|
|
1152
726
|
iconContainer.className = 'stub-icons';
|
|
1153
727
|
const clearable = this.getProperty('clearable');
|
|
@@ -1167,15 +741,9 @@ export class TyDatePicker extends TyComponent {
|
|
|
1167
741
|
wrapper.appendChild(stub);
|
|
1168
742
|
return wrapper;
|
|
1169
743
|
}
|
|
1170
|
-
/**
|
|
1171
|
-
* Calculate calendar position
|
|
1172
|
-
*
|
|
1173
|
-
* PORTED FROM: calculate-calendar-position! in date_picker.cljs
|
|
1174
|
-
*/
|
|
1175
744
|
calculateCalendarPosition() {
|
|
1176
745
|
if (!this.shadowRoot)
|
|
1177
746
|
return;
|
|
1178
|
-
// Mobile uses native input, no positioning needed
|
|
1179
747
|
if (isMobileTouch())
|
|
1180
748
|
return;
|
|
1181
749
|
const stub = this.shadowRoot.querySelector('.date-picker-stub');
|
|
@@ -1183,7 +751,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1183
751
|
if (!stub || !dialog)
|
|
1184
752
|
return;
|
|
1185
753
|
const stubRect = stub.getBoundingClientRect();
|
|
1186
|
-
// Measure when shown; the estimate only covers the pre-layout call
|
|
1187
754
|
const dialogRect = dialog.getBoundingClientRect();
|
|
1188
755
|
const estimatedHeight = dialogRect.height || 400;
|
|
1189
756
|
const popupWidth = dialogRect.width || 320;
|
|
@@ -1197,12 +764,10 @@ export class TyDatePicker extends TyComponent {
|
|
|
1197
764
|
const positionBelow = pos.below;
|
|
1198
765
|
const x = pos.x - wrapPadding;
|
|
1199
766
|
const y = positionBelow ? pos.topY : pos.bottomY;
|
|
1200
|
-
// Set CSS variables for positioning
|
|
1201
767
|
this.style.setProperty('--calendar-x', `${x}px`);
|
|
1202
768
|
this.style.setProperty('--calendar-y', `${y}px`);
|
|
1203
769
|
this.style.setProperty('--calendar-offset-x', '0px');
|
|
1204
770
|
this.style.setProperty('--calendar-offset-y', '0px');
|
|
1205
|
-
// Set direction classes
|
|
1206
771
|
if (positionBelow) {
|
|
1207
772
|
dialog.classList.add('position-below');
|
|
1208
773
|
dialog.classList.remove('position-above');
|
|
@@ -1212,11 +777,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1212
777
|
dialog.classList.remove('position-below');
|
|
1213
778
|
}
|
|
1214
779
|
}
|
|
1215
|
-
/**
|
|
1216
|
-
* Render container structure
|
|
1217
|
-
*
|
|
1218
|
-
* PORTED FROM: render-container-structure in date_picker.cljs
|
|
1219
|
-
*/
|
|
1220
780
|
renderContainer() {
|
|
1221
781
|
const container = document.createElement('div');
|
|
1222
782
|
container.className = 'dropdown-container';
|
|
@@ -1230,40 +790,29 @@ export class TyDatePicker extends TyComponent {
|
|
|
1230
790
|
}
|
|
1231
791
|
return container;
|
|
1232
792
|
}
|
|
1233
|
-
/**
|
|
1234
|
-
* Main render method
|
|
1235
|
-
*
|
|
1236
|
-
* PORTED FROM: render! in date_picker.cljs
|
|
1237
|
-
*/
|
|
1238
793
|
render() {
|
|
1239
794
|
if (!this.shadowRoot)
|
|
1240
795
|
return;
|
|
1241
796
|
ensureStyles(this.shadowRoot, { css: datePickerStyles, id: 'ty-date-picker' });
|
|
1242
|
-
// Mobile: native input, no dialog/event listeners/scroll lock
|
|
1243
797
|
if (isMobileTouch()) {
|
|
1244
798
|
const existingNativeInput = this.shadowRoot.querySelector('.native-date-input');
|
|
1245
799
|
if (existingNativeInput) {
|
|
1246
|
-
// PARTIAL UPDATE: don't destroy DOM while native picker may be open
|
|
1247
800
|
this.updateDisplay();
|
|
1248
801
|
return;
|
|
1249
802
|
}
|
|
1250
|
-
// FIRST RENDER: build from scratch
|
|
1251
803
|
this.shadowRoot.innerHTML = '';
|
|
1252
804
|
const container = this.renderContainer();
|
|
1253
805
|
container.appendChild(this.renderNativeInput());
|
|
1254
806
|
this.shadowRoot.appendChild(container);
|
|
1255
807
|
return;
|
|
1256
808
|
}
|
|
1257
|
-
// Check if dialog is currently open
|
|
1258
809
|
const existingDialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1259
810
|
const isDialogOpen = existingDialog && existingDialog.open;
|
|
1260
811
|
if (isDialogOpen && this._state.open) {
|
|
1261
|
-
// PARTIAL UPDATE: Dialog is open - just update display
|
|
1262
812
|
this.updateDisplay();
|
|
1263
813
|
this.calculateCalendarPosition();
|
|
1264
814
|
return;
|
|
1265
815
|
}
|
|
1266
|
-
// FULL REBUILD: Dialog is closed or doesn't exist
|
|
1267
816
|
this.shadowRoot.innerHTML = '';
|
|
1268
817
|
const container = this.renderContainer();
|
|
1269
818
|
const wrapper = document.createElement('div');
|
|
@@ -1272,14 +821,8 @@ export class TyDatePicker extends TyComponent {
|
|
|
1272
821
|
wrapper.appendChild(this.renderCalendarDropdown());
|
|
1273
822
|
container.appendChild(wrapper);
|
|
1274
823
|
this.shadowRoot.appendChild(container);
|
|
1275
|
-
// Setup document-level event listeners
|
|
1276
824
|
this.setupEventListeners();
|
|
1277
825
|
}
|
|
1278
|
-
/**
|
|
1279
|
-
* Update display without destroying DOM (for open dialog)
|
|
1280
|
-
*
|
|
1281
|
-
* PORTED FROM: update-display! in date_picker.cljs
|
|
1282
|
-
*/
|
|
1283
826
|
updateDisplay() {
|
|
1284
827
|
if (!this.shadowRoot)
|
|
1285
828
|
return;
|
|
@@ -1288,18 +831,15 @@ export class TyDatePicker extends TyComponent {
|
|
|
1288
831
|
const hasValue = this.hasDate();
|
|
1289
832
|
const clearable = this.getProperty('clearable');
|
|
1290
833
|
const isDisabled = this.getProperty('disabled');
|
|
1291
|
-
// Update stub text (shared between mobile and desktop)
|
|
1292
834
|
const stubText = this.shadowRoot.querySelector('.stub-text');
|
|
1293
835
|
if (stubText) {
|
|
1294
836
|
stubText.textContent = formattedValue || placeholder;
|
|
1295
837
|
stubText.classList.toggle('placeholder', !formattedValue);
|
|
1296
838
|
}
|
|
1297
|
-
// Update clear button visibility
|
|
1298
839
|
const clearButton = this.shadowRoot.querySelector('.stub-clear');
|
|
1299
840
|
if (clearButton) {
|
|
1300
841
|
clearButton.style.display = (clearable && hasValue && !isDisabled) ? '' : 'none';
|
|
1301
842
|
}
|
|
1302
|
-
// Mobile: also sync native input value
|
|
1303
843
|
if (isMobileTouch()) {
|
|
1304
844
|
const nativeInput = this.shadowRoot.querySelector('.native-date-input');
|
|
1305
845
|
if (nativeInput) {
|
|
@@ -1308,17 +848,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1308
848
|
}
|
|
1309
849
|
return;
|
|
1310
850
|
}
|
|
1311
|
-
// Desktop: update calendar and time input
|
|
1312
851
|
const calendar = this.shadowRoot.querySelector('ty-calendar');
|
|
1313
852
|
if (calendar && hasValue) {
|
|
1314
853
|
calendar.setAttribute('year', this._state.year.toString());
|
|
1315
854
|
calendar.setAttribute('month', this._state.month.toString());
|
|
1316
855
|
calendar.setAttribute('day', this._state.day.toString());
|
|
1317
856
|
}
|
|
1318
|
-
// Keep an already-open popup's flavor in sync. render() only rebuilds
|
|
1319
|
-
// (and re-forwards flavor) while the dialog is CLOSED — while open it
|
|
1320
|
-
// takes this partial-update path instead, so without this the calendar
|
|
1321
|
-
// would stay stuck on whatever flavor was active when it was opened.
|
|
1322
857
|
if (calendar) {
|
|
1323
858
|
const flavor = this.getProperty('flavor');
|
|
1324
859
|
if (flavor && flavor !== 'default')
|
|
@@ -1330,55 +865,35 @@ export class TyDatePicker extends TyComponent {
|
|
|
1330
865
|
this._timeInput.setTime(this._state.hour, this._state.minute);
|
|
1331
866
|
}
|
|
1332
867
|
}
|
|
1333
|
-
/**
|
|
1334
|
-
* Setup document-level event listeners
|
|
1335
|
-
*/
|
|
1336
868
|
setupEventListeners() {
|
|
1337
|
-
// Remove old listeners if they exist
|
|
1338
869
|
if (this._clickListener) {
|
|
1339
870
|
document.removeEventListener('click', this._clickListener);
|
|
1340
871
|
}
|
|
1341
872
|
if (this._keydownListener) {
|
|
1342
873
|
document.removeEventListener('keydown', this._keydownListener);
|
|
1343
874
|
}
|
|
1344
|
-
// Create new listeners
|
|
1345
875
|
this._clickListener = (e) => this.handleOutsideClick(e);
|
|
1346
876
|
this._keydownListener = (e) => this.handleEscapeKey(e);
|
|
1347
|
-
// Add document listeners
|
|
1348
877
|
document.addEventListener('click', this._clickListener);
|
|
1349
878
|
document.addEventListener('keydown', this._keydownListener);
|
|
1350
|
-
// Add dialog click listener
|
|
1351
879
|
const dialog = this.shadowRoot?.querySelector('.calendar-dialog');
|
|
1352
880
|
if (dialog) {
|
|
1353
881
|
this._dialogClickListener = (e) => this.handleDialogClick(e);
|
|
1354
882
|
dialog.addEventListener('click', this._dialogClickListener);
|
|
1355
883
|
}
|
|
1356
884
|
}
|
|
1357
|
-
// ==========================================================================
|
|
1358
|
-
// Event Handlers
|
|
1359
|
-
// ==========================================================================
|
|
1360
885
|
handleStubClick(event) {
|
|
1361
886
|
event.preventDefault();
|
|
1362
|
-
// Heal state/DOM desync: a host re-render can replace the dialog element
|
|
1363
|
-
// while `open` is still true, stranding the picker (openDropdown
|
|
1364
|
-
// early-returns on _state.open). Reality wins: closed dialog → closed state.
|
|
1365
887
|
const dialog = this.shadowRoot?.querySelector('.calendar-dialog');
|
|
1366
888
|
if (this._state.open && (!dialog || !dialog.open)) {
|
|
1367
889
|
this.updateState({ open: false }, true);
|
|
1368
890
|
unlockScroll(`date-picker-${this.id || this.toString()}`);
|
|
1369
891
|
}
|
|
1370
892
|
const disabled = this.getProperty('disabled');
|
|
1371
|
-
// FIXME: Timestamp guard is a workaround. Calendar day cells use pointerdown,
|
|
1372
|
-
// which closes the dialog before the click event fires — the click then hits the
|
|
1373
|
-
// stub and reopens. Proper fix: switch calendar-month day cells from pointerdown
|
|
1374
|
-
// to click so the event is consumed before the dialog closes.
|
|
1375
893
|
if (!disabled && Date.now() - this._closeTimestamp > 300) {
|
|
1376
894
|
this.openDropdown();
|
|
1377
895
|
}
|
|
1378
896
|
}
|
|
1379
|
-
/**
|
|
1380
|
-
* Clear the date value, sync state, emit event, and re-render.
|
|
1381
|
-
*/
|
|
1382
897
|
clearValue() {
|
|
1383
898
|
this.updateState({
|
|
1384
899
|
year: undefined,
|
|
@@ -1396,11 +911,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1396
911
|
event.stopPropagation();
|
|
1397
912
|
this.clearValue();
|
|
1398
913
|
}
|
|
1399
|
-
/**
|
|
1400
|
-
* Handle calendar date selection
|
|
1401
|
-
*
|
|
1402
|
-
* PORTED FROM: handle-calendar-change! in date_picker.cljs
|
|
1403
|
-
*/
|
|
1404
914
|
handleCalendarChange(event) {
|
|
1405
915
|
event.preventDefault();
|
|
1406
916
|
event.stopPropagation();
|
|
@@ -1409,7 +919,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1409
919
|
const dayContext = detail.dayContext;
|
|
1410
920
|
if (!dayContext)
|
|
1411
921
|
return;
|
|
1412
|
-
// Update state with new date
|
|
1413
922
|
const newComponents = {
|
|
1414
923
|
year: dayContext.year,
|
|
1415
924
|
month: dayContext.month,
|
|
@@ -1417,15 +926,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1417
926
|
hour: this._state.hour,
|
|
1418
927
|
minute: this._state.minute,
|
|
1419
928
|
};
|
|
1420
|
-
// Force sync when calendar date changes
|
|
1421
929
|
this.updateState(newComponents, true);
|
|
1422
930
|
this.emitChangeEvent(newComponents, 'selection');
|
|
1423
|
-
// Close calendar if time is not required
|
|
1424
931
|
if (!this._state.withTime) {
|
|
1425
932
|
this.closeDropdown();
|
|
1426
933
|
}
|
|
1427
934
|
else {
|
|
1428
|
-
// Auto-focus time input after date selection
|
|
1429
935
|
requestAnimationFrame(() => {
|
|
1430
936
|
if (!this.shadowRoot)
|
|
1431
937
|
return;
|
|
@@ -1436,43 +942,26 @@ export class TyDatePicker extends TyComponent {
|
|
|
1436
942
|
});
|
|
1437
943
|
}
|
|
1438
944
|
}
|
|
1439
|
-
/**
|
|
1440
|
-
* Handle dialog backdrop clicks
|
|
1441
|
-
*
|
|
1442
|
-
* PORTED FROM: handle-dialog-click! in date_picker.cljs
|
|
1443
|
-
*/
|
|
1444
945
|
handleDialogClick(event) {
|
|
1445
946
|
if (!this.shadowRoot)
|
|
1446
947
|
return;
|
|
1447
948
|
const dialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1448
949
|
const content = this.shadowRoot.querySelector('.calendar-content');
|
|
1449
|
-
// Close if clicking on dialog backdrop (not calendar content)
|
|
1450
950
|
if (event.target === dialog && this._state.open && content && !content.contains(event.target)) {
|
|
1451
951
|
event.preventDefault();
|
|
1452
952
|
event.stopPropagation();
|
|
1453
953
|
this.closeDropdown();
|
|
1454
954
|
}
|
|
1455
955
|
}
|
|
1456
|
-
/**
|
|
1457
|
-
* Handle clicks outside the date picker
|
|
1458
|
-
*
|
|
1459
|
-
* PORTED FROM: handle-outside-click! in date_picker.cljs
|
|
1460
|
-
*/
|
|
1461
956
|
handleOutsideClick(event) {
|
|
1462
957
|
if (!this.shadowRoot)
|
|
1463
958
|
return;
|
|
1464
959
|
const target = event.target;
|
|
1465
960
|
const dialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1466
|
-
// Check if click is truly outside everything
|
|
1467
961
|
if (this._state.open && dialog && !this.contains(target) && !dialog.contains(target)) {
|
|
1468
962
|
this.closeDropdown();
|
|
1469
963
|
}
|
|
1470
964
|
}
|
|
1471
|
-
/**
|
|
1472
|
-
* Handle Escape key press
|
|
1473
|
-
*
|
|
1474
|
-
* PORTED FROM: handle-escape-key! in date_picker.cljs
|
|
1475
|
-
*/
|
|
1476
965
|
handleEscapeKey(event) {
|
|
1477
966
|
const keyboardEvent = event;
|
|
1478
967
|
if (keyboardEvent.key === 'Escape' && this._state.open) {
|
|
@@ -1480,18 +969,12 @@ export class TyDatePicker extends TyComponent {
|
|
|
1480
969
|
this.closeDropdown();
|
|
1481
970
|
}
|
|
1482
971
|
}
|
|
1483
|
-
/**
|
|
1484
|
-
* Open calendar dropdown
|
|
1485
|
-
*
|
|
1486
|
-
* PORTED FROM: open-dropdown! in date_picker.cljs
|
|
1487
|
-
*/
|
|
1488
972
|
openDropdown() {
|
|
1489
973
|
if (this._state.open)
|
|
1490
974
|
return;
|
|
1491
975
|
if (isMobileTouch())
|
|
1492
976
|
return;
|
|
1493
977
|
this.updateState({ open: true });
|
|
1494
|
-
// Dispatch open event
|
|
1495
978
|
this.dispatchEvent(new CustomEvent('open', {
|
|
1496
979
|
bubbles: true,
|
|
1497
980
|
composed: true
|
|
@@ -1510,7 +993,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1510
993
|
const stubEl = this.shadowRoot.querySelector('.date-picker-stub');
|
|
1511
994
|
if (stubEl)
|
|
1512
995
|
stubEl.setAttribute('aria-expanded', 'true');
|
|
1513
|
-
// Remove focus from any focused elements to prevent the blue outline
|
|
1514
996
|
const focusedElement = this.shadowRoot.activeElement;
|
|
1515
997
|
if (focusedElement) {
|
|
1516
998
|
focusedElement.blur();
|
|
@@ -1518,17 +1000,11 @@ export class TyDatePicker extends TyComponent {
|
|
|
1518
1000
|
}
|
|
1519
1001
|
});
|
|
1520
1002
|
}
|
|
1521
|
-
/**
|
|
1522
|
-
* Close calendar dropdown
|
|
1523
|
-
*
|
|
1524
|
-
* PORTED FROM: close-dropdown! in date_picker.cljs
|
|
1525
|
-
*/
|
|
1526
1003
|
closeDropdown() {
|
|
1527
1004
|
if (!this._state.open)
|
|
1528
1005
|
return;
|
|
1529
1006
|
this._closeTimestamp = Date.now();
|
|
1530
1007
|
const pickerId = `date-picker-${this.id || this.toString()}`;
|
|
1531
|
-
// Force sync any staged updates when closing
|
|
1532
1008
|
this.updateState({ open: false }, true);
|
|
1533
1009
|
unlockScroll(pickerId);
|
|
1534
1010
|
if (this.shadowRoot) {
|
|
@@ -1537,8 +1013,6 @@ export class TyDatePicker extends TyComponent {
|
|
|
1537
1013
|
dialog.classList.remove('position-above', 'position-below', 'open');
|
|
1538
1014
|
dialog.close();
|
|
1539
1015
|
}
|
|
1540
|
-
// closeDropdown() doesn't call render(), so the stub's aria-expanded
|
|
1541
|
-
// (still "true" from open) needs an explicit flip here too.
|
|
1542
1016
|
const stubEl = this.shadowRoot.querySelector('.date-picker-stub');
|
|
1543
1017
|
if (stubEl)
|
|
1544
1018
|
stubEl.setAttribute('aria-expanded', 'false');
|
|
@@ -1549,51 +1023,34 @@ export class TyDatePicker extends TyComponent {
|
|
|
1549
1023
|
}));
|
|
1550
1024
|
this.render();
|
|
1551
1025
|
}
|
|
1552
|
-
// ==========================================================================
|
|
1553
|
-
// Cleanup
|
|
1554
|
-
// ==========================================================================
|
|
1555
|
-
/**
|
|
1556
|
-
* Clean up event listeners and state
|
|
1557
|
-
*
|
|
1558
|
-
* PORTED FROM: cleanup! in date_picker.cljs
|
|
1559
|
-
*/
|
|
1560
1026
|
cleanup() {
|
|
1561
|
-
// Remove document listeners
|
|
1562
1027
|
if (this._clickListener) {
|
|
1563
1028
|
document.removeEventListener('click', this._clickListener);
|
|
1564
1029
|
}
|
|
1565
1030
|
if (this._keydownListener) {
|
|
1566
1031
|
document.removeEventListener('keydown', this._keydownListener);
|
|
1567
1032
|
}
|
|
1568
|
-
// Remove dialog listener
|
|
1569
1033
|
if (this._dialogClickListener && this.shadowRoot) {
|
|
1570
1034
|
const dialog = this.shadowRoot.querySelector('.calendar-dialog');
|
|
1571
1035
|
if (dialog) {
|
|
1572
1036
|
dialog.removeEventListener('click', this._dialogClickListener);
|
|
1573
1037
|
}
|
|
1574
1038
|
}
|
|
1575
|
-
// Unlock scroll if open
|
|
1576
1039
|
if (this._state.open) {
|
|
1577
1040
|
const pickerId = `date-picker-${this.id || this.toString()}`;
|
|
1578
1041
|
unlockScroll(pickerId);
|
|
1579
1042
|
}
|
|
1580
|
-
// Clear references
|
|
1581
1043
|
this._clickListener = undefined;
|
|
1582
1044
|
this._keydownListener = undefined;
|
|
1583
1045
|
this._dialogClickListener = undefined;
|
|
1584
1046
|
this._timeInput = undefined;
|
|
1585
1047
|
}
|
|
1586
1048
|
}
|
|
1587
|
-
// ============================================================================
|
|
1588
|
-
// PROPERTY CONFIGURATION - Single source of truth
|
|
1589
|
-
// ============================================================================
|
|
1590
1049
|
TyDatePicker.properties = {
|
|
1591
|
-
// String properties
|
|
1592
1050
|
size: {
|
|
1593
1051
|
type: 'string',
|
|
1594
1052
|
visual: true,
|
|
1595
1053
|
default: 'md',
|
|
1596
|
-
// Fields come in exactly three sizes; legacy xs/xl map to sm/lg.
|
|
1597
1054
|
validate: (v) => ['sm', 'md', 'lg'].includes(v),
|
|
1598
1055
|
coerce: (v) => {
|
|
1599
1056
|
if (v === 'xs')
|
|
@@ -1624,10 +1081,9 @@ TyDatePicker.properties = {
|
|
|
1624
1081
|
},
|
|
1625
1082
|
name: {
|
|
1626
1083
|
type: 'string',
|
|
1627
|
-
visual: false,
|
|
1084
|
+
visual: false,
|
|
1628
1085
|
default: ''
|
|
1629
1086
|
},
|
|
1630
|
-
// Date bounds (ISO) — passed through to the embedded ty-calendar
|
|
1631
1087
|
min: {
|
|
1632
1088
|
type: 'string',
|
|
1633
1089
|
visual: true,
|
|
@@ -1638,7 +1094,6 @@ TyDatePicker.properties = {
|
|
|
1638
1094
|
visual: true,
|
|
1639
1095
|
default: ''
|
|
1640
1096
|
},
|
|
1641
|
-
// Boolean properties
|
|
1642
1097
|
required: {
|
|
1643
1098
|
type: 'boolean',
|
|
1644
1099
|
visual: true,
|
|
@@ -1657,7 +1112,6 @@ TyDatePicker.properties = {
|
|
|
1657
1112
|
'not-clearable': false
|
|
1658
1113
|
}
|
|
1659
1114
|
},
|
|
1660
|
-
// Format property (medium complexity)
|
|
1661
1115
|
format: {
|
|
1662
1116
|
type: 'string',
|
|
1663
1117
|
visual: true,
|
|
@@ -1672,34 +1126,25 @@ TyDatePicker.properties = {
|
|
|
1672
1126
|
return v;
|
|
1673
1127
|
}
|
|
1674
1128
|
},
|
|
1675
|
-
// Locale property (medium complexity - has observer)
|
|
1676
1129
|
locale: {
|
|
1677
1130
|
type: 'string',
|
|
1678
1131
|
visual: true,
|
|
1679
1132
|
default: ''
|
|
1680
1133
|
},
|
|
1681
|
-
// Value property (high complexity - UTC parsing, date components)
|
|
1682
|
-
// Note: Custom getter/setter will handle the complex logic
|
|
1683
1134
|
value: {
|
|
1684
1135
|
type: 'string',
|
|
1685
1136
|
visual: true,
|
|
1686
|
-
formValue: true,
|
|
1687
|
-
emitChange: false,
|
|
1688
|
-
default: null
|
|
1137
|
+
formValue: true,
|
|
1138
|
+
emitChange: false,
|
|
1139
|
+
default: null
|
|
1689
1140
|
},
|
|
1690
|
-
// With-time property (high complexity - affects parsing and rendering)
|
|
1691
1141
|
'with-time': {
|
|
1692
1142
|
type: 'boolean',
|
|
1693
1143
|
visual: true,
|
|
1694
1144
|
default: false
|
|
1695
1145
|
}
|
|
1696
1146
|
};
|
|
1697
|
-
/**
|
|
1698
|
-
* Form-associated custom element
|
|
1699
|
-
*/
|
|
1700
1147
|
TyDatePicker.formAssociated = true;
|
|
1701
|
-
// Register custom element
|
|
1702
1148
|
if (!customElements.get('ty-date-picker')) {
|
|
1703
1149
|
customElements.define('ty-date-picker', TyDatePicker);
|
|
1704
1150
|
}
|
|
1705
|
-
//# sourceMappingURL=date-picker.js.map
|