tyrell-components 1.0.0-TC45 → 1.0.0-TC47
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/README.md +1 -1
- package/css/tyrell-theme.css +93 -100
- package/dist/tyrell-theme.css +93 -100
- 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 +13 -162
- package/lib/components/date-picker.js +39 -584
- 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 +4 -135
- 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 +22 -153
- package/lib/components/select.js +30 -585
- 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 +5 -283
- 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 +2 -160
- 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 +18 -121
- package/lib/styles/calendar-month.d.ts +1 -5
- package/lib/styles/calendar-month.js +4 -61
- package/lib/styles/calendar-navigation.d.ts +2 -6
- package/lib/styles/calendar-navigation.js +2 -27
- package/lib/styles/calendar.d.ts +1 -2
- package/lib/styles/calendar.js +1 -14
- 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 +8 -38
- package/lib/styles/file-upload.d.ts +1 -2
- package/lib/styles/file-upload.js +1 -16
- package/lib/styles/icon.d.ts +2 -6
- package/lib/styles/icon.js +7 -21
- package/lib/styles/input.d.ts +1 -6
- package/lib/styles/input.js +5 -54
- 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 +2 -12
- 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 +2 -16
- 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 +15 -74
- 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 +10 -48
- 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 +2 -45
- 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 +8 -88
- 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 +50 -7
- package/lib/utils/positioning.js +49 -59
- 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
package/lib/components/popup.js
CHANGED
|
@@ -1,45 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
* Popup Component
|
|
3
|
-
*
|
|
4
|
-
* Interactive popup component with dropdown-like behavior and tooltip positioning.
|
|
5
|
-
* Uses parent-child relationship - popup opens on click, stays open for interaction,
|
|
6
|
-
* closes on outside click or ESC key.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* <button>
|
|
10
|
-
* Click me
|
|
11
|
-
* <ty-popup placement="bottom" offset="8">
|
|
12
|
-
* <div class="ty-elevated p-4 rounded-lg">
|
|
13
|
-
* Popup content here
|
|
14
|
-
* </div>
|
|
15
|
-
* </ty-popup>
|
|
16
|
-
* </button>
|
|
17
|
-
*/
|
|
18
|
-
import { findBestPosition } from '../utils/positioning.js';
|
|
1
|
+
import { findBestPosition, preferenceChain } from '../utils/positioning.js';
|
|
19
2
|
import { lockScroll, unlockScroll } from '../utils/scroll-lock.js';
|
|
20
3
|
import { ensureStyles } from '../utils/styles.js';
|
|
21
4
|
import { popupStyles } from '../styles/popup.js';
|
|
22
|
-
// ============================================================================
|
|
23
|
-
// WeakMaps for State Management
|
|
24
|
-
// ============================================================================
|
|
25
5
|
const anchorClickHandlers = new WeakMap();
|
|
26
6
|
const anchorKeyClickHandlers = new WeakMap();
|
|
27
7
|
const outsideClickHandlers = new WeakMap();
|
|
28
8
|
const escapeHandlers = new WeakMap();
|
|
29
9
|
const closeRequestHandlers = new WeakMap();
|
|
30
|
-
const popupIds = new WeakMap();
|
|
31
|
-
// ============================================================================
|
|
32
|
-
// Helper Functions
|
|
33
|
-
// ============================================================================
|
|
34
|
-
/**
|
|
35
|
-
* Parse boolean attribute
|
|
36
|
-
*/
|
|
10
|
+
const popupIds = new WeakMap();
|
|
37
11
|
function parseBoolAttr(el, name) {
|
|
38
12
|
return el.hasAttribute(name);
|
|
39
13
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Parse integer attribute
|
|
42
|
-
*/
|
|
43
14
|
function parseIntAttr(el, name, defaultValue) {
|
|
44
15
|
const value = el.getAttribute(name);
|
|
45
16
|
if (value === null)
|
|
@@ -47,9 +18,6 @@ function parseIntAttr(el, name, defaultValue) {
|
|
|
47
18
|
const parsed = parseInt(value, 10);
|
|
48
19
|
return isNaN(parsed) ? defaultValue : parsed;
|
|
49
20
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Read popup attributes with simplified API
|
|
52
|
-
*/
|
|
53
21
|
function getPopupAttributes(el) {
|
|
54
22
|
return {
|
|
55
23
|
manual: parseBoolAttr(el, 'manual'),
|
|
@@ -58,21 +26,12 @@ function getPopupAttributes(el) {
|
|
|
58
26
|
offset: parseIntAttr(el, 'offset', 8),
|
|
59
27
|
};
|
|
60
28
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Get the parent element as anchor (like tooltip)
|
|
63
|
-
*/
|
|
64
29
|
function getAnchorElement(el) {
|
|
65
30
|
return el.parentElement;
|
|
66
31
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Get the popup dialog element
|
|
69
|
-
*/
|
|
70
32
|
function getPopupDialog(shadowRoot) {
|
|
71
33
|
return shadowRoot.querySelector('.popup-dialog');
|
|
72
34
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Get or create a unique popup ID for scroll locking
|
|
75
|
-
*/
|
|
76
35
|
function getPopupId(el) {
|
|
77
36
|
let id = popupIds.get(el);
|
|
78
37
|
if (!id) {
|
|
@@ -81,9 +40,6 @@ function getPopupId(el) {
|
|
|
81
40
|
}
|
|
82
41
|
return id;
|
|
83
42
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Calculate and update popup position based on anchor (tooltip style)
|
|
86
|
-
*/
|
|
87
43
|
function updatePosition(el) {
|
|
88
44
|
const { placement, offset } = getPopupAttributes(el);
|
|
89
45
|
const shadowRoot = el.shadowRoot;
|
|
@@ -91,13 +47,7 @@ function updatePosition(el) {
|
|
|
91
47
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
92
48
|
if (!anchor || !dialog)
|
|
93
49
|
return;
|
|
94
|
-
|
|
95
|
-
const preferences = placement === 'top' ? ['top', 'bottom', 'left', 'right'] :
|
|
96
|
-
placement === 'bottom' ? ['bottom', 'top', 'left', 'right'] :
|
|
97
|
-
placement === 'left' ? ['left', 'right', 'top', 'bottom'] :
|
|
98
|
-
placement === 'right' ? ['right', 'left', 'top', 'bottom'] :
|
|
99
|
-
['bottom', 'top', 'right', 'left']; // Default popup placement
|
|
100
|
-
// Use positioning engine to find best position
|
|
50
|
+
const preferences = preferenceChain(placement);
|
|
101
51
|
const positionData = findBestPosition({
|
|
102
52
|
targetEl: anchor,
|
|
103
53
|
floatingEl: dialog,
|
|
@@ -106,15 +56,10 @@ function updatePosition(el) {
|
|
|
106
56
|
padding: 8,
|
|
107
57
|
containerPadding: 16,
|
|
108
58
|
});
|
|
109
|
-
// Update CSS variables with compensated coordinates
|
|
110
59
|
el.style.setProperty('--popup-x', `${positionData.x}px`);
|
|
111
60
|
el.style.setProperty('--popup-y', `${positionData.y}px`);
|
|
112
|
-
// Add position class like dropdown does (this applies the variables)
|
|
113
61
|
dialog.classList.add('position-calculated');
|
|
114
62
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Close popup with clean animation -> position reset -> hide sequence
|
|
117
|
-
*/
|
|
118
63
|
function closePopup(el, force = false) {
|
|
119
64
|
const { disableClose } = getPopupAttributes(el);
|
|
120
65
|
if (!force && disableClose)
|
|
@@ -123,8 +68,6 @@ function closePopup(el, force = false) {
|
|
|
123
68
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
124
69
|
if (!dialog)
|
|
125
70
|
return;
|
|
126
|
-
// Don't unlock scroll here - let the dialog 'close' event handler do it
|
|
127
|
-
// This prevents double-unlock issues
|
|
128
71
|
dialog.classList.remove('open');
|
|
129
72
|
dialog.classList.remove('preparing-animation');
|
|
130
73
|
const anchor = getAnchorElement(el);
|
|
@@ -132,46 +75,30 @@ function closePopup(el, force = false) {
|
|
|
132
75
|
anchor.setAttribute('aria-expanded', 'false');
|
|
133
76
|
setTimeout(() => {
|
|
134
77
|
dialog.classList.remove('position-calculated');
|
|
135
|
-
dialog.close();
|
|
78
|
+
dialog.close();
|
|
136
79
|
}, 150);
|
|
137
|
-
// Dispatch close event
|
|
138
80
|
el.dispatchEvent(new CustomEvent('close', { bubbles: true }));
|
|
139
81
|
}
|
|
140
|
-
/**
|
|
141
|
-
* Open popup with dialog open but hidden → position → animate sequence
|
|
142
|
-
*/
|
|
143
82
|
function openPopup(el) {
|
|
144
|
-
// Lock scroll using the stored popup ID
|
|
145
83
|
const popupId = getPopupId(el);
|
|
146
84
|
lockScroll(popupId);
|
|
147
85
|
const shadowRoot = el.shadowRoot;
|
|
148
86
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
149
87
|
if (!dialog)
|
|
150
88
|
return;
|
|
151
|
-
// Step 1: showModal() immediately - dialog gets [open] attribute and proper layout
|
|
152
89
|
dialog.showModal();
|
|
153
90
|
const anchor = getAnchorElement(el);
|
|
154
91
|
if (anchor?.hasAttribute('aria-haspopup'))
|
|
155
92
|
anchor.setAttribute('aria-expanded', 'true');
|
|
156
|
-
// Step 2: Wait for layout to settle, then measure and position
|
|
157
93
|
requestAnimationFrame(() => {
|
|
158
|
-
// Now dialog has [open] and accurate dimensions
|
|
159
|
-
// Calculate and apply position while dialog is hidden by CSS
|
|
160
94
|
updatePosition(el);
|
|
161
|
-
// Step 3: Add scale transform for animation prep
|
|
162
95
|
dialog.classList.add('preparing-animation');
|
|
163
|
-
// Step 4: Third RAF - THEN add open class for smooth scale animation
|
|
164
96
|
requestAnimationFrame(() => {
|
|
165
|
-
// Now animate: scale(0.95) → scale(1) + opacity + visibility
|
|
166
97
|
dialog.classList.add('open');
|
|
167
|
-
// Dispatch open event
|
|
168
98
|
el.dispatchEvent(new CustomEvent('open', { bubbles: true }));
|
|
169
99
|
});
|
|
170
100
|
});
|
|
171
101
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Toggle popup visibility
|
|
174
|
-
*/
|
|
175
102
|
function togglePopup(el) {
|
|
176
103
|
const shadowRoot = el.shadowRoot;
|
|
177
104
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
@@ -184,30 +111,20 @@ function togglePopup(el) {
|
|
|
184
111
|
openPopup(el);
|
|
185
112
|
}
|
|
186
113
|
}
|
|
187
|
-
/**
|
|
188
|
-
* Handle click on anchor element
|
|
189
|
-
*/
|
|
190
114
|
function handleAnchorClick(el, event) {
|
|
191
115
|
event.preventDefault();
|
|
192
116
|
event.stopPropagation();
|
|
193
117
|
togglePopup(el);
|
|
194
118
|
}
|
|
195
|
-
/**
|
|
196
|
-
* Handle clicks on the dialog element (backdrop clicks)
|
|
197
|
-
*/
|
|
198
119
|
function handleOutsideClick(el, event) {
|
|
199
120
|
event.stopPropagation();
|
|
200
121
|
const shadowRoot = el.shadowRoot;
|
|
201
122
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
202
|
-
// Close if clicking on dialog backdrop (not popup content)
|
|
203
123
|
if (dialog && event.target === dialog) {
|
|
204
124
|
event.preventDefault();
|
|
205
125
|
closePopup(el);
|
|
206
126
|
}
|
|
207
127
|
}
|
|
208
|
-
/**
|
|
209
|
-
* Handle escape key press
|
|
210
|
-
*/
|
|
211
128
|
function handleEscape(el, event) {
|
|
212
129
|
event.stopPropagation();
|
|
213
130
|
if (event.key === 'Escape') {
|
|
@@ -220,28 +137,18 @@ function handleEscape(el, event) {
|
|
|
220
137
|
}
|
|
221
138
|
}
|
|
222
139
|
}
|
|
223
|
-
/**
|
|
224
|
-
* Handle close requests from inside popup content
|
|
225
|
-
*/
|
|
226
140
|
function handleCloseRequest(el, event) {
|
|
227
141
|
event.stopPropagation();
|
|
228
142
|
closePopup(el, true);
|
|
229
143
|
}
|
|
230
|
-
/**
|
|
231
|
-
* Setup click listener on anchor element (unless manual=true)
|
|
232
|
-
*/
|
|
233
144
|
function setupAnchorEvents(el) {
|
|
234
145
|
const { manual } = getPopupAttributes(el);
|
|
235
146
|
const anchor = getAnchorElement(el);
|
|
236
147
|
if (!anchor || manual)
|
|
237
148
|
return;
|
|
238
|
-
// Screen reader gets no other signal that clicking this opens something —
|
|
239
|
-
// the popup itself is a real <dialog> (showModal()), which already gives
|
|
240
|
-
// it role="dialog" + focus-trap for free, so only the trigger needs this.
|
|
241
149
|
anchor.setAttribute('aria-haspopup', 'dialog');
|
|
242
150
|
if (!anchor.hasAttribute('aria-expanded'))
|
|
243
151
|
anchor.setAttribute('aria-expanded', 'false');
|
|
244
|
-
// Remove any existing listeners first
|
|
245
152
|
const existingHandler = anchorClickHandlers.get(el);
|
|
246
153
|
if (existingHandler) {
|
|
247
154
|
anchor.removeEventListener('pointerdown', existingHandler);
|
|
@@ -250,14 +157,9 @@ function setupAnchorEvents(el) {
|
|
|
250
157
|
if (existingKeyHandler) {
|
|
251
158
|
anchor.removeEventListener('click', existingKeyHandler);
|
|
252
159
|
}
|
|
253
|
-
// Create new handlers and store references
|
|
254
160
|
const handler = (e) => handleAnchorClick(el, e);
|
|
255
161
|
anchorClickHandlers.set(el, handler);
|
|
256
162
|
anchor.addEventListener('pointerdown', handler);
|
|
257
|
-
// pointerdown covers mouse/touch; native keyboard activation (Enter/Space
|
|
258
|
-
// on a focused anchor) fires 'click' with detail === 0, which pointerdown
|
|
259
|
-
// never sees — without this, a keyboard-only user could focus the anchor
|
|
260
|
-
// but had no way to open the popup (same gap fixed in ty-tabs).
|
|
261
163
|
const keyHandler = (e) => {
|
|
262
164
|
if (e.detail === 0)
|
|
263
165
|
handleAnchorClick(el, e);
|
|
@@ -265,9 +167,6 @@ function setupAnchorEvents(el) {
|
|
|
265
167
|
anchorKeyClickHandlers.set(el, keyHandler);
|
|
266
168
|
anchor.addEventListener('click', keyHandler);
|
|
267
169
|
}
|
|
268
|
-
/**
|
|
269
|
-
* Cleanup anchor event listeners
|
|
270
|
-
*/
|
|
271
170
|
function cleanupAnchorEvents(el) {
|
|
272
171
|
const anchor = getAnchorElement(el);
|
|
273
172
|
const handler = anchorClickHandlers.get(el);
|
|
@@ -283,17 +182,12 @@ function cleanupAnchorEvents(el) {
|
|
|
283
182
|
anchor?.removeAttribute('aria-haspopup');
|
|
284
183
|
anchor?.removeAttribute('aria-expanded');
|
|
285
184
|
}
|
|
286
|
-
/**
|
|
287
|
-
* Render the popup structure
|
|
288
|
-
*/
|
|
289
185
|
function render(el) {
|
|
290
186
|
const shadowRoot = el.shadowRoot;
|
|
291
187
|
if (!shadowRoot)
|
|
292
188
|
return;
|
|
293
189
|
const existingDialog = getPopupDialog(shadowRoot);
|
|
294
|
-
// Ensure styles are loaded
|
|
295
190
|
ensureStyles(shadowRoot, { css: popupStyles, id: 'ty-popup' });
|
|
296
|
-
// Create structure if it doesn't exist
|
|
297
191
|
if (!existingDialog) {
|
|
298
192
|
const dialog = document.createElement('dialog');
|
|
299
193
|
const container = document.createElement('div');
|
|
@@ -304,17 +198,14 @@ function render(el) {
|
|
|
304
198
|
shadowRoot.appendChild(dialog);
|
|
305
199
|
dialog.appendChild(container);
|
|
306
200
|
container.appendChild(content);
|
|
307
|
-
// Initialize position
|
|
308
201
|
el.style.setProperty('--popup-x', '0px');
|
|
309
202
|
el.style.setProperty('--popup-y', '0px');
|
|
310
|
-
// Setup event listeners
|
|
311
203
|
const outsideHandler = (e) => handleOutsideClick(el, e);
|
|
312
204
|
outsideClickHandlers.set(el, outsideHandler);
|
|
313
205
|
dialog.addEventListener('pointerdown', outsideHandler);
|
|
314
206
|
const escapeHandler = (e) => handleEscape(el, e);
|
|
315
207
|
escapeHandlers.set(el, escapeHandler);
|
|
316
208
|
el.addEventListener('keydown', escapeHandler);
|
|
317
|
-
// Handle native dialog close event
|
|
318
209
|
dialog.addEventListener('close', () => {
|
|
319
210
|
const popupId = getPopupId(el);
|
|
320
211
|
unlockScroll(popupId);
|
|
@@ -323,30 +214,21 @@ function render(el) {
|
|
|
323
214
|
el.dispatchEvent(new CustomEvent('close', { bubbles: true }));
|
|
324
215
|
});
|
|
325
216
|
}
|
|
326
|
-
// Add close method to element for programmatic access
|
|
327
217
|
el.closePopup = () => closePopup(el, true);
|
|
328
218
|
el.openPopup = () => openPopup(el);
|
|
329
219
|
el.togglePopup = () => togglePopup(el);
|
|
330
|
-
// Add event listener for close requests from content
|
|
331
220
|
const closeRequestHandler = (e) => handleCloseRequest(el, e);
|
|
332
221
|
closeRequestHandlers.set(el, closeRequestHandler);
|
|
333
222
|
el.addEventListener('ty:close-popup', closeRequestHandler);
|
|
334
|
-
// Setup anchor events
|
|
335
223
|
setupAnchorEvents(el);
|
|
336
224
|
}
|
|
337
|
-
/**
|
|
338
|
-
* Cleanup all resources when component disconnects
|
|
339
|
-
*/
|
|
340
225
|
function cleanup(el) {
|
|
341
|
-
// Force unlock scroll if popup is being removed
|
|
342
226
|
const popupId = popupIds.get(el);
|
|
343
227
|
if (popupId) {
|
|
344
228
|
unlockScroll(popupId);
|
|
345
229
|
popupIds.delete(el);
|
|
346
230
|
}
|
|
347
|
-
// Cleanup anchor events
|
|
348
231
|
cleanupAnchorEvents(el);
|
|
349
|
-
// Cleanup dialog events
|
|
350
232
|
const shadowRoot = el.shadowRoot;
|
|
351
233
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
352
234
|
if (dialog) {
|
|
@@ -367,14 +249,7 @@ function cleanup(el) {
|
|
|
367
249
|
closeRequestHandlers.delete(el);
|
|
368
250
|
}
|
|
369
251
|
}
|
|
370
|
-
// ============================================================================
|
|
371
|
-
// Component Definition
|
|
372
|
-
// ============================================================================
|
|
373
|
-
/**
|
|
374
|
-
* TyPopup Web Component
|
|
375
|
-
*/
|
|
376
252
|
export class TyPopup extends HTMLElement {
|
|
377
|
-
/** Observed attributes */
|
|
378
253
|
static get observedAttributes() {
|
|
379
254
|
return ['manual', 'disable-close', 'placement', 'offset'];
|
|
380
255
|
}
|
|
@@ -389,26 +264,20 @@ export class TyPopup extends HTMLElement {
|
|
|
389
264
|
cleanup(this);
|
|
390
265
|
}
|
|
391
266
|
attributeChangedCallback(name, _oldValue, _newValue) {
|
|
392
|
-
// Re-render on attribute changes
|
|
393
267
|
render(this);
|
|
394
|
-
// Handle specific attribute changes
|
|
395
268
|
switch (name) {
|
|
396
269
|
case 'placement':
|
|
397
270
|
case 'offset':
|
|
398
|
-
// Update position immediately if open and placement-related attributes change
|
|
399
271
|
render(this);
|
|
400
272
|
break;
|
|
401
273
|
case 'manual':
|
|
402
274
|
case 'disable-close':
|
|
403
|
-
// Recreate anchor events if manual changes
|
|
404
275
|
cleanupAnchorEvents(this);
|
|
405
276
|
setupAnchorEvents(this);
|
|
406
277
|
break;
|
|
407
278
|
}
|
|
408
279
|
}
|
|
409
280
|
}
|
|
410
|
-
// Register the custom element
|
|
411
281
|
if (!customElements.get('ty-popup')) {
|
|
412
282
|
customElements.define('ty-popup', TyPopup);
|
|
413
283
|
}
|
|
414
|
-
//# sourceMappingURL=popup.js.map
|
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TyRadio + TyRadioGroup Web Components
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* value and form integration; ty-radio is the individual choice element.
|
|
6
|
-
*
|
|
7
|
-
* Composition (container + children, like ty-select + ty-option):
|
|
8
|
-
* <ty-radio-group name="plan" value="pro" label="Plan">
|
|
9
|
-
* <ty-radio value="free">Free</ty-radio>
|
|
10
|
-
* <ty-radio value="pro">Pro</ty-radio>
|
|
11
|
-
* <ty-radio value="team">Team</ty-radio>
|
|
12
|
-
* </ty-radio-group>
|
|
13
|
-
*
|
|
14
|
-
* Keyboard:
|
|
15
|
-
* - Tab focuses the group's currently-selected (or first) radio
|
|
16
|
-
* - Arrow keys move focus AND selection within the group (per W3C ARIA)
|
|
17
|
-
* - Space/Enter selects the focused radio
|
|
2
|
+
* TyRadio + TyRadioGroup Web Components - exclusive selection within a group.
|
|
3
|
+
* ty-radio-group owns the value and form integration; ty-radio is one choice.
|
|
4
|
+
* Keyboard follows W3C ARIA: arrow keys move focus AND selection together.
|
|
18
5
|
*/
|
|
19
6
|
import type { Flavor, Size } from "../types/common.js";
|
|
20
7
|
import { TyComponent } from "../base/ty-component.js";
|
|
@@ -162,13 +149,7 @@ export declare class TyRadioGroup extends TyComponent<RadioGroupState> implement
|
|
|
162
149
|
* because consumers may wrap each radio in a `<label>` for click delegation.
|
|
163
150
|
*/
|
|
164
151
|
private getRadios;
|
|
165
|
-
/**
|
|
166
|
-
* Push group state down to each radio child.
|
|
167
|
-
*/
|
|
168
152
|
private syncChildren;
|
|
169
|
-
/**
|
|
170
|
-
* Handle a child's select request — update value, sync, emit change.
|
|
171
|
-
*/
|
|
172
153
|
private handleRadioSelect;
|
|
173
154
|
/**
|
|
174
155
|
* Arrow-key navigation within group (W3C radio practice: arrow keys move
|
|
@@ -199,4 +180,3 @@ export declare class TyRadioGroup extends TyComponent<RadioGroupState> implement
|
|
|
199
180
|
get form(): HTMLFormElement | null;
|
|
200
181
|
}
|
|
201
182
|
export {};
|
|
202
|
-
//# sourceMappingURL=radio.d.ts.map
|
package/lib/components/radio.js
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TyRadio + TyRadioGroup Web Components
|
|
3
|
-
*
|
|
4
|
-
* Exclusive selection within a group. ty-radio-group manages the selection
|
|
5
|
-
* value and form integration; ty-radio is the individual choice element.
|
|
6
|
-
*
|
|
7
|
-
* Composition (container + children, like ty-select + ty-option):
|
|
8
|
-
* <ty-radio-group name="plan" value="pro" label="Plan">
|
|
9
|
-
* <ty-radio value="free">Free</ty-radio>
|
|
10
|
-
* <ty-radio value="pro">Pro</ty-radio>
|
|
11
|
-
* <ty-radio value="team">Team</ty-radio>
|
|
12
|
-
* </ty-radio-group>
|
|
13
|
-
*
|
|
14
|
-
* Keyboard:
|
|
15
|
-
* - Tab focuses the group's currently-selected (or first) radio
|
|
16
|
-
* - Arrow keys move focus AND selection within the group (per W3C ARIA)
|
|
17
|
-
* - Space/Enter selects the focused radio
|
|
18
|
-
*/
|
|
19
1
|
import { TyComponent } from "../base/ty-component.js";
|
|
20
2
|
import { ensureStyles } from "../utils/styles.js";
|
|
21
3
|
import { syncCustomFlavorSheet } from "../utils/flavor-sheet.js";
|
|
@@ -33,12 +15,7 @@ export class TyRadio extends TyComponent {
|
|
|
33
15
|
ensureStyles(shadow, { css: radioStyles, id: "ty-radio" });
|
|
34
16
|
}
|
|
35
17
|
onConnect() {
|
|
36
|
-
// Re-arm listeners on RE-connect (base skips render() when already
|
|
37
|
-
// rendered; onDisconnect removed them). No-op on first connect.
|
|
38
18
|
this.setupEventListeners();
|
|
39
|
-
// Tell the group we exist. During HTML parsing the group connects BEFORE
|
|
40
|
-
// its children, so its own onConnect sync sees an empty group — each
|
|
41
|
-
// radio announcing itself lets the group re-sync as children arrive.
|
|
42
19
|
this.dispatchEvent(new CustomEvent("ty-radio-connected", { bubbles: true }));
|
|
43
20
|
this._syncCustomFlavor();
|
|
44
21
|
}
|
|
@@ -50,7 +27,6 @@ export class TyRadio extends TyComponent {
|
|
|
50
27
|
this._syncCustomFlavor();
|
|
51
28
|
}
|
|
52
29
|
}
|
|
53
|
-
/** Custom (non-built-in) flavors — see utils/flavor-sheet.ts. */
|
|
54
30
|
_syncCustomFlavor() {
|
|
55
31
|
this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.flavor, ({ base }) => radioCustomFlavorCss(base));
|
|
56
32
|
}
|
|
@@ -64,7 +40,6 @@ export class TyRadio extends TyComponent {
|
|
|
64
40
|
if (this.disabled)
|
|
65
41
|
return;
|
|
66
42
|
e.stopPropagation();
|
|
67
|
-
// Notify the group — group will set checked attribute on this and clear others
|
|
68
43
|
this.dispatchEvent(new CustomEvent("ty-radio-select", {
|
|
69
44
|
detail: { value: this.value },
|
|
70
45
|
bubbles: true,
|
|
@@ -74,8 +49,6 @@ export class TyRadio extends TyComponent {
|
|
|
74
49
|
removeEventListeners() {
|
|
75
50
|
if (!this._listenersSetup)
|
|
76
51
|
return;
|
|
77
|
-
// Click lives on the HOST (label delegation + full-element trigger) —
|
|
78
|
-
// remove it before the shadow-container guard below.
|
|
79
52
|
if (this._clickHandler) {
|
|
80
53
|
this.removeEventListener("click", this._clickHandler);
|
|
81
54
|
this._clickHandler = null;
|
|
@@ -104,8 +77,6 @@ export class TyRadio extends TyComponent {
|
|
|
104
77
|
this._clickHandler = (e) => this.handleClick(e);
|
|
105
78
|
this._focusHandler = () => el.classList.add("focused");
|
|
106
79
|
this._blurHandler = () => el.classList.remove("focused");
|
|
107
|
-
// Click on the HOST: whole element is a trigger and a wrapping <label>
|
|
108
|
-
// delegates (the synthetic click lands on the host, not the shadow).
|
|
109
80
|
this.addEventListener("click", this._clickHandler);
|
|
110
81
|
el.addEventListener("focus", this._focusHandler);
|
|
111
82
|
el.addEventListener("blur", this._blurHandler);
|
|
@@ -133,13 +104,9 @@ export class TyRadio extends TyComponent {
|
|
|
133
104
|
radioEl.setAttribute("aria-checked", String(this.checked));
|
|
134
105
|
radioEl.setAttribute("aria-disabled", String(this.disabled));
|
|
135
106
|
}
|
|
136
|
-
// (Re)attach listeners — onDisconnect removes them, so a reconnected
|
|
137
|
-
// element needs them again even though the container already exists.
|
|
138
107
|
this.setupEventListeners();
|
|
139
|
-
// a11y: name the role="radio" element from its wrapping <label>.
|
|
140
108
|
this.applyLabelName(radioEl);
|
|
141
109
|
}
|
|
142
|
-
// Property accessors
|
|
143
110
|
get value() { return this.getProperty("value"); }
|
|
144
111
|
set value(v) { this.setProperty("value", v); }
|
|
145
112
|
get checked() { return this.getProperty("checked"); }
|
|
@@ -188,11 +155,7 @@ export class TyRadioGroup extends TyComponent {
|
|
|
188
155
|
ensureStyles(shadow, { css: radioStyles, id: "ty-radio-group" });
|
|
189
156
|
}
|
|
190
157
|
onConnect() {
|
|
191
|
-
// Re-arm listeners on RE-connect; no-op on first (setup runs in render too)
|
|
192
158
|
this.setupEventListeners();
|
|
193
|
-
// Sync radio children with current value once light DOM is ready.
|
|
194
|
-
// NOTE: during HTML parsing this runs before children are parsed — the
|
|
195
|
-
// ty-radio-connected listener covers late-arriving radios.
|
|
196
159
|
queueMicrotask(() => this.syncChildren());
|
|
197
160
|
}
|
|
198
161
|
onDisconnect() {
|
|
@@ -206,30 +169,19 @@ export class TyRadioGroup extends TyComponent {
|
|
|
206
169
|
getFormValue() {
|
|
207
170
|
return this.value || null;
|
|
208
171
|
}
|
|
209
|
-
/**
|
|
210
|
-
* Find ty-radio descendants in this group's light DOM. Searches deeply
|
|
211
|
-
* because consumers may wrap each radio in a `<label>` for click delegation.
|
|
212
|
-
*/
|
|
213
172
|
getRadios() {
|
|
214
173
|
return Array.from(this.querySelectorAll("ty-radio"));
|
|
215
174
|
}
|
|
216
|
-
/**
|
|
217
|
-
* Push group state down to each radio child.
|
|
218
|
-
*/
|
|
219
175
|
syncChildren() {
|
|
220
176
|
const radios = this.getRadios();
|
|
221
177
|
for (const radio of radios) {
|
|
222
178
|
radio.checked = radio.value === this.value;
|
|
223
|
-
// Inherit size/flavor/disabled from group unless radio set its own
|
|
224
179
|
if (this.disabled)
|
|
225
180
|
radio.disabled = true;
|
|
226
181
|
radio.size = this.size;
|
|
227
182
|
radio.flavor = this.flavor;
|
|
228
183
|
}
|
|
229
184
|
}
|
|
230
|
-
/**
|
|
231
|
-
* Handle a child's select request — update value, sync, emit change.
|
|
232
|
-
*/
|
|
233
185
|
handleRadioSelect(e) {
|
|
234
186
|
if (this.disabled)
|
|
235
187
|
return;
|
|
@@ -249,10 +201,6 @@ export class TyRadioGroup extends TyComponent {
|
|
|
249
201
|
this.dispatchEvent(new CustomEvent("change", { detail: eventDetail, bubbles: true, composed: true }));
|
|
250
202
|
}, 0);
|
|
251
203
|
}
|
|
252
|
-
/**
|
|
253
|
-
* Arrow-key navigation within group (W3C radio practice: arrow keys move
|
|
254
|
-
* focus and selection together).
|
|
255
|
-
*/
|
|
256
204
|
handleKeydown(e) {
|
|
257
205
|
if (this.disabled)
|
|
258
206
|
return;
|
|
@@ -294,7 +242,6 @@ export class TyRadioGroup extends TyComponent {
|
|
|
294
242
|
const next = radios[nextIndex];
|
|
295
243
|
if (next) {
|
|
296
244
|
next.focus();
|
|
297
|
-
// Per W3C: arrow keys also change selection
|
|
298
245
|
this.value = next.value;
|
|
299
246
|
this.syncChildren();
|
|
300
247
|
const eventDetail = {
|
|
@@ -329,8 +276,6 @@ export class TyRadioGroup extends TyComponent {
|
|
|
329
276
|
return;
|
|
330
277
|
this._selectHandler = (e) => this.handleRadioSelect(e);
|
|
331
278
|
this._keydownHandler = (e) => this.handleKeydown(e);
|
|
332
|
-
// Radios announce themselves on connect — re-sync so radios parsed AFTER
|
|
333
|
-
// the group's own onConnect still receive the group's value/size/flavor.
|
|
334
279
|
this._connectedHandler = () => this.syncChildren();
|
|
335
280
|
this.addEventListener("ty-radio-select", this._selectHandler);
|
|
336
281
|
this.addEventListener("keydown", this._keydownHandler);
|
|
@@ -345,7 +290,6 @@ export class TyRadioGroup extends TyComponent {
|
|
|
345
290
|
container.className = "radio-group-container";
|
|
346
291
|
container.setAttribute("role", "radiogroup");
|
|
347
292
|
container.setAttribute("aria-disabled", String(this.disabled));
|
|
348
|
-
// Label
|
|
349
293
|
if (this.label) {
|
|
350
294
|
const labelEl = document.createElement("div");
|
|
351
295
|
labelEl.className = "radio-group-label";
|
|
@@ -358,7 +302,6 @@ export class TyRadioGroup extends TyComponent {
|
|
|
358
302
|
}
|
|
359
303
|
container.appendChild(labelEl);
|
|
360
304
|
}
|
|
361
|
-
// List wrapper that hosts the slot
|
|
362
305
|
const list = document.createElement("div");
|
|
363
306
|
list.className = "radio-group-list " + this.orientation;
|
|
364
307
|
const slot = document.createElement("slot");
|
|
@@ -374,7 +317,6 @@ export class TyRadioGroup extends TyComponent {
|
|
|
374
317
|
this.setupEventListeners();
|
|
375
318
|
}
|
|
376
319
|
else {
|
|
377
|
-
// Update label
|
|
378
320
|
let labelEl = container.querySelector(".radio-group-label");
|
|
379
321
|
if (this.label) {
|
|
380
322
|
if (!labelEl) {
|
|
@@ -396,11 +338,9 @@ export class TyRadioGroup extends TyComponent {
|
|
|
396
338
|
else if (labelEl) {
|
|
397
339
|
labelEl.remove();
|
|
398
340
|
}
|
|
399
|
-
// Update orientation
|
|
400
341
|
const list = container.querySelector(".radio-group-list");
|
|
401
342
|
if (list)
|
|
402
343
|
list.className = "radio-group-list " + this.orientation;
|
|
403
|
-
// Update error
|
|
404
344
|
let errorEl = container.querySelector(".radio-group-error");
|
|
405
345
|
if (this.error) {
|
|
406
346
|
if (!errorEl) {
|
|
@@ -415,10 +355,8 @@ export class TyRadioGroup extends TyComponent {
|
|
|
415
355
|
}
|
|
416
356
|
container.setAttribute("aria-disabled", String(this.disabled));
|
|
417
357
|
}
|
|
418
|
-
// Always sync child radios after render
|
|
419
358
|
this.syncChildren();
|
|
420
359
|
}
|
|
421
|
-
// Property accessors
|
|
422
360
|
get value() { return this.getProperty("value"); }
|
|
423
361
|
set value(v) { this.setProperty("value", v); }
|
|
424
362
|
get name() { return this.getProperty("name"); }
|
|
@@ -476,4 +414,3 @@ TyRadioGroup.properties = {
|
|
|
476
414
|
if (!customElements.get("ty-radio-group")) {
|
|
477
415
|
customElements.define("ty-radio-group", TyRadioGroup);
|
|
478
416
|
}
|
|
479
|
-
//# sourceMappingURL=radio.js.map
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TyResizeObserver Web Component
|
|
3
|
-
*
|
|
4
|
-
* Self-observing resize component that tracks its own dimensions
|
|
5
|
-
* and stores them in a global registry accessible by element ID.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```html
|
|
9
|
-
* <ty-resize-observer id="parent-container">
|
|
10
|
-
* <div class="child">
|
|
11
|
-
* <!-- Child can query parent size via getSize('parent-container') -->
|
|
12
|
-
* </div>
|
|
13
|
-
* </ty-resize-observer>
|
|
14
|
-
* ```
|
|
15
|
-
*
|
|
16
|
-
* @example With debouncing
|
|
17
|
-
* ```html
|
|
18
|
-
* <ty-resize-observer id="container" debounce="100">
|
|
19
|
-
* <!-- Only updates after 100ms of no resize activity -->
|
|
20
|
-
* </ty-resize-observer>
|
|
21
|
-
* ```
|
|
2
|
+
* TyResizeObserver Web Component - self-observing element that tracks its own
|
|
3
|
+
* dimensions in a global registry keyed by element ID. Optional debounce.
|
|
22
4
|
*/
|
|
23
5
|
export declare class TyResizeObserver extends HTMLElement {
|
|
24
6
|
private _resizeObserver;
|
|
@@ -32,17 +14,7 @@ export declare class TyResizeObserver extends HTMLElement {
|
|
|
32
14
|
constructor();
|
|
33
15
|
connectedCallback(): void;
|
|
34
16
|
disconnectedCallback(): void;
|
|
35
|
-
/**
|
|
36
|
-
* Setup ResizeObserver to watch this element
|
|
37
|
-
*/
|
|
38
17
|
private setupObserver;
|
|
39
|
-
/**
|
|
40
|
-
* Handle resize events - update registry and notify callbacks
|
|
41
|
-
*/
|
|
42
18
|
private handleResize;
|
|
43
|
-
/**
|
|
44
|
-
* Cleanup observer and remove from registry
|
|
45
|
-
*/
|
|
46
19
|
private cleanup;
|
|
47
20
|
}
|
|
48
|
-
//# sourceMappingURL=resize-observer.d.ts.map
|