@sondor-technology/sondor-components 1.0.1-develop.13 → 1.0.1-develop.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/alert/alert.constants.d.ts +3 -0
- package/dist/components/alert/alert.constants.d.ts.map +1 -0
- package/dist/components/alert/alert.constants.js +2 -0
- package/dist/components/alert/alert.constants.js.map +1 -0
- package/dist/components/alert/alert.d.ts +4 -3
- package/dist/components/alert/alert.d.ts.map +1 -1
- package/dist/components/alert/alert.js +21 -11
- package/dist/components/alert/alert.js.map +1 -0
- package/dist/components/badge/badge.constants.d.ts +3 -0
- package/dist/components/badge/badge.constants.d.ts.map +1 -0
- package/dist/components/badge/badge.constants.js +2 -0
- package/dist/components/badge/badge.constants.js.map +1 -0
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/badge/badge.d.ts.map +1 -1
- package/dist/components/badge/badge.js +3 -2
- package/dist/components/badge/badge.js.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +2 -1
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/components/button/button.constants.d.ts +7 -0
- package/dist/components/button/button.constants.d.ts.map +1 -0
- package/dist/components/button/button.constants.js +4 -0
- package/dist/components/button/button.constants.js.map +1 -0
- package/dist/components/button/button.d.ts +8 -4
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +181 -137
- package/dist/components/button/button.js.map +1 -0
- package/dist/components/button-group/button-group.d.ts +1 -0
- package/dist/components/button-group/button-group.d.ts.map +1 -1
- package/dist/components/button-group/button-group.js +17 -21
- package/dist/components/button-group/button-group.js.map +1 -0
- package/dist/components/calendar/calendar.d.ts +36 -0
- package/dist/components/calendar/calendar.d.ts.map +1 -0
- package/dist/components/calendar/calendar.js +439 -0
- package/dist/components/calendar/calendar.js.map +1 -0
- package/dist/components/checkbox/checkbox.constants.d.ts +3 -0
- package/dist/components/checkbox/checkbox.constants.d.ts.map +1 -0
- package/dist/components/checkbox/checkbox.constants.js +2 -0
- package/dist/components/checkbox/checkbox.constants.js.map +1 -0
- package/dist/components/checkbox/checkbox.d.ts +5 -1
- package/dist/components/checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox/checkbox.js +21 -5
- package/dist/components/checkbox/checkbox.js.map +1 -0
- package/dist/components/code-block/code-block.d.ts +7 -7
- package/dist/components/code-block/code-block.d.ts.map +1 -1
- package/dist/components/code-block/code-block.js +130 -66
- package/dist/components/code-block/code-block.js.map +1 -0
- package/dist/components/content-card/content-card.d.ts.map +1 -1
- package/dist/components/content-card/content-card.js +10 -15
- package/dist/components/content-card/content-card.js.map +1 -0
- package/dist/components/cron-builder/cron-builder.d.ts +34 -0
- package/dist/components/cron-builder/cron-builder.d.ts.map +1 -0
- package/dist/components/cron-builder/cron-builder.js +456 -0
- package/dist/components/cron-builder/cron-builder.js.map +1 -0
- package/dist/components/divider/divider.constants.d.ts +5 -0
- package/dist/components/divider/divider.constants.d.ts.map +1 -0
- package/dist/components/divider/divider.constants.js +3 -0
- package/dist/components/divider/divider.constants.js.map +1 -0
- package/dist/components/divider/divider.d.ts +4 -2
- package/dist/components/divider/divider.d.ts.map +1 -1
- package/dist/components/divider/divider.js +18 -9
- package/dist/components/divider/divider.js.map +1 -0
- package/dist/components/drawer/drawer.d.ts +7 -4
- package/dist/components/drawer/drawer.d.ts.map +1 -1
- package/dist/components/drawer/drawer.js +73 -30
- package/dist/components/drawer/drawer.js.map +1 -0
- package/dist/components/field/field.d.ts +2 -2
- package/dist/components/field/field.d.ts.map +1 -1
- package/dist/components/field/field.js +8 -9
- package/dist/components/field/field.js.map +1 -0
- package/dist/components/footer/footer.d.ts +8 -1
- package/dist/components/footer/footer.d.ts.map +1 -1
- package/dist/components/footer/footer.js +42 -12
- package/dist/components/footer/footer.js.map +1 -0
- package/dist/components/form/form-step.js +1 -0
- package/dist/components/form/form-step.js.map +1 -0
- package/dist/components/form/form.d.ts +3 -0
- package/dist/components/form/form.d.ts.map +1 -1
- package/dist/components/form/form.js +109 -69
- package/dist/components/form/form.js.map +1 -0
- package/dist/components/grid/grid-row.d.ts +3 -3
- package/dist/components/grid/grid-row.d.ts.map +1 -1
- package/dist/components/grid/grid-row.js +36 -22
- package/dist/components/grid/grid-row.js.map +1 -0
- package/dist/components/grid/grid.d.ts +2 -2
- package/dist/components/grid/grid.d.ts.map +1 -1
- package/dist/components/grid/grid.js +5 -2
- package/dist/components/grid/grid.js.map +1 -0
- package/dist/components/icon/icon-paths.d.ts +161 -86
- package/dist/components/icon/icon-paths.d.ts.map +1 -1
- package/dist/components/icon/icon-paths.js +176 -106
- package/dist/components/icon/icon-paths.js.map +1 -0
- package/dist/components/icon/icon.d.ts +1 -1
- package/dist/components/icon/icon.d.ts.map +1 -1
- package/dist/components/icon/icon.js +6 -5
- package/dist/components/icon/icon.js.map +1 -0
- package/dist/components/input/input.d.ts +10 -0
- package/dist/components/input/input.d.ts.map +1 -1
- package/dist/components/input/input.js +59 -43
- package/dist/components/input/input.js.map +1 -0
- package/dist/components/link/link.d.ts +6 -6
- package/dist/components/link/link.d.ts.map +1 -1
- package/dist/components/link/link.js +42 -19
- package/dist/components/link/link.js.map +1 -0
- package/dist/components/metadata/metadata.d.ts +3 -3
- package/dist/components/metadata/metadata.d.ts.map +1 -1
- package/dist/components/metadata/metadata.js +23 -20
- package/dist/components/metadata/metadata.js.map +1 -0
- package/dist/components/modal/modal.d.ts +4 -16
- package/dist/components/modal/modal.d.ts.map +1 -1
- package/dist/components/modal/modal.js +99 -62
- package/dist/components/modal/modal.js.map +1 -0
- package/dist/components/page-layout/page-layout.d.ts +4 -3
- package/dist/components/page-layout/page-layout.d.ts.map +1 -1
- package/dist/components/page-layout/page-layout.js +95 -68
- package/dist/components/page-layout/page-layout.js.map +1 -0
- package/dist/components/pin-input/pin-input.d.ts +33 -0
- package/dist/components/pin-input/pin-input.d.ts.map +1 -0
- package/dist/components/pin-input/pin-input.js +253 -0
- package/dist/components/pin-input/pin-input.js.map +1 -0
- package/dist/components/popover/popover.d.ts +26 -0
- package/dist/components/popover/popover.d.ts.map +1 -0
- package/dist/components/popover/popover.js +196 -0
- package/dist/components/popover/popover.js.map +1 -0
- package/dist/components/radio/radio.d.ts +8 -0
- package/dist/components/radio/radio.d.ts.map +1 -1
- package/dist/components/radio/radio.js +8 -12
- package/dist/components/radio/radio.js.map +1 -0
- package/dist/components/radio-group/radio-group.d.ts +6 -1
- package/dist/components/radio-group/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group/radio-group.js +44 -18
- package/dist/components/radio-group/radio-group.js.map +1 -0
- package/dist/components/router/router.d.ts +5 -7
- package/dist/components/router/router.d.ts.map +1 -1
- package/dist/components/router/router.js +54 -38
- package/dist/components/router/router.js.map +1 -0
- package/dist/components/select/select.d.ts +5 -4
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +40 -9
- package/dist/components/select/select.js.map +1 -0
- package/dist/components/skeleton/skeleton.d.ts +3 -2
- package/dist/components/skeleton/skeleton.d.ts.map +1 -1
- package/dist/components/skeleton/skeleton.js +10 -13
- package/dist/components/skeleton/skeleton.js.map +1 -0
- package/dist/components/switch/switch.d.ts +12 -0
- package/dist/components/switch/switch.d.ts.map +1 -1
- package/dist/components/switch/switch.js +28 -22
- package/dist/components/switch/switch.js.map +1 -0
- package/dist/components/table/data-table.d.ts +3 -25
- package/dist/components/table/data-table.d.ts.map +1 -1
- package/dist/components/table/data-table.js +211 -146
- package/dist/components/table/data-table.js.map +1 -0
- package/dist/components/table/table.d.ts +3 -3
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +39 -20
- package/dist/components/table/table.js.map +1 -0
- package/dist/components/tabs/tab-panel.d.ts +3 -1
- package/dist/components/tabs/tab-panel.d.ts.map +1 -1
- package/dist/components/tabs/tab-panel.js +21 -11
- package/dist/components/tabs/tab-panel.js.map +1 -0
- package/dist/components/tabs/tab.d.ts +5 -1
- package/dist/components/tabs/tab.d.ts.map +1 -1
- package/dist/components/tabs/tab.js +46 -28
- package/dist/components/tabs/tab.js.map +1 -0
- package/dist/components/tabs/tabs.d.ts +4 -0
- package/dist/components/tabs/tabs.d.ts.map +1 -1
- package/dist/components/tabs/tabs.js +76 -13
- package/dist/components/tabs/tabs.js.map +1 -0
- package/dist/components/textarea/textarea.d.ts +13 -0
- package/dist/components/textarea/textarea.d.ts.map +1 -1
- package/dist/components/textarea/textarea.js +59 -13
- package/dist/components/textarea/textarea.js.map +1 -0
- package/dist/components/theme-toggle/theme-toggle.constants.d.ts +3 -0
- package/dist/components/theme-toggle/theme-toggle.constants.d.ts.map +1 -0
- package/dist/components/theme-toggle/theme-toggle.constants.js +2 -0
- package/dist/components/theme-toggle/theme-toggle.constants.js.map +1 -0
- package/dist/components/theme-toggle/theme-toggle.d.ts +3 -2
- package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -1
- package/dist/components/theme-toggle/theme-toggle.js +46 -19
- package/dist/components/theme-toggle/theme-toggle.js.map +1 -0
- package/dist/components/time-picker/time-picker.d.ts +38 -0
- package/dist/components/time-picker/time-picker.d.ts.map +1 -0
- package/dist/components/time-picker/time-picker.js +280 -0
- package/dist/components/time-picker/time-picker.js.map +1 -0
- package/dist/components/toggle-group/toggle-button.d.ts +23 -0
- package/dist/components/toggle-group/toggle-button.d.ts.map +1 -0
- package/dist/components/toggle-group/toggle-button.js +115 -0
- package/dist/components/toggle-group/toggle-button.js.map +1 -0
- package/dist/components/toggle-group/toggle-group.d.ts +32 -0
- package/dist/components/toggle-group/toggle-group.d.ts.map +1 -0
- package/dist/components/toggle-group/toggle-group.js +150 -0
- package/dist/components/toggle-group/toggle-group.js.map +1 -0
- package/dist/components/tooltip/tooltip.d.ts +33 -0
- package/dist/components/tooltip/tooltip.d.ts.map +1 -0
- package/dist/components/tooltip/tooltip.js +293 -0
- package/dist/components/tooltip/tooltip.js.map +1 -0
- package/dist/components/vertical-nav/vertical-nav-group.d.ts +5 -2
- package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -1
- package/dist/components/vertical-nav/vertical-nav-group.js +50 -14
- package/dist/components/vertical-nav/vertical-nav-group.js.map +1 -0
- package/dist/components/vertical-nav/vertical-nav-item.d.ts +3 -2
- package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -1
- package/dist/components/vertical-nav/vertical-nav-item.js +30 -8
- package/dist/components/vertical-nav/vertical-nav-item.js.map +1 -0
- package/dist/styles/index.css +49 -17
- package/dist/styles/reset.js +1 -0
- package/dist/styles/reset.js.map +1 -0
- package/package.json +30 -1
- package/src/components/alert/alert.constants.ts +2 -0
- package/src/components/alert/alert.ts +24 -15
- package/src/components/badge/badge.constants.ts +2 -0
- package/src/components/badge/badge.ts +3 -4
- package/src/components/breadcrumb/breadcrumb.ts +2 -2
- package/src/components/button/button.constants.ts +8 -0
- package/src/components/button/button.ts +184 -144
- package/src/components/button-group/button-group.ts +18 -22
- package/src/components/calendar/calendar.ts +444 -0
- package/src/components/checkbox/checkbox.constants.ts +2 -0
- package/src/components/checkbox/checkbox.ts +33 -18
- package/src/components/code-block/code-block.ts +151 -84
- package/src/components/content-card/content-card.ts +10 -16
- package/src/components/cron-builder/cron-builder.ts +452 -0
- package/src/components/divider/divider.constants.ts +5 -0
- package/src/components/divider/divider.ts +22 -11
- package/src/components/drawer/drawer.ts +79 -34
- package/src/components/field/field.ts +9 -11
- package/src/components/footer/footer.ts +43 -12
- package/src/components/form/form.ts +121 -84
- package/src/components/grid/grid-row.ts +45 -32
- package/src/components/grid/grid.ts +7 -4
- package/src/components/icon/icon-paths.ts +175 -106
- package/src/components/icon/icon.ts +6 -6
- package/src/components/input/input.ts +59 -45
- package/src/components/link/link.ts +45 -21
- package/src/components/metadata/metadata.ts +26 -24
- package/src/components/modal/modal.ts +108 -74
- package/src/components/page-layout/page-layout.ts +97 -72
- package/src/components/pin-input/pin-input.ts +249 -0
- package/src/components/popover/popover.ts +192 -0
- package/src/components/radio/radio.ts +8 -13
- package/src/components/radio-group/radio-group.ts +45 -21
- package/src/components/router/router.ts +73 -50
- package/src/components/select/select.ts +41 -80
- package/src/components/skeleton/skeleton.ts +14 -14
- package/src/components/switch/switch.ts +31 -25
- package/src/components/table/data-table.ts +248 -177
- package/src/components/table/table.ts +52 -29
- package/src/components/tabs/tab-panel.ts +25 -13
- package/src/components/tabs/tab.ts +58 -33
- package/src/components/tabs/tabs.ts +77 -15
- package/src/components/textarea/textarea.ts +63 -15
- package/src/components/theme-toggle/theme-toggle.constants.ts +2 -0
- package/src/components/theme-toggle/theme-toggle.ts +47 -22
- package/src/components/time-picker/time-picker.ts +272 -0
- package/src/components/toggle-group/toggle-button.ts +104 -0
- package/src/components/toggle-group/toggle-group.ts +148 -0
- package/src/components/tooltip/tooltip.ts +286 -0
- package/src/components/vertical-nav/vertical-nav-group.ts +57 -18
- package/src/components/vertical-nav/vertical-nav-item.ts +35 -12
- package/src/styles/globals.css +27 -4
- package/src/styles/theme.css +40 -32
- package/tsconfig.json +2 -1
- package/dist/components/card/card.d.ts +0 -11
- package/dist/components/card/card.d.ts.map +0 -1
- package/dist/components/card/card.js +0 -54
- package/src/components/card/card.ts +0 -53
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import {LitElement, html, css, PropertyValues} from "lit";
|
|
2
|
+
import {customElement, property, query, state, queryAssignedElements} from "lit/decorators.js";
|
|
3
|
+
import {resetStyles} from "../../styles/reset.js";
|
|
4
|
+
|
|
5
|
+
export type PopoverPosition = "top" | "bottom" | "left" | "right";
|
|
6
|
+
|
|
7
|
+
@customElement("st-popover")
|
|
8
|
+
export class StPopover extends LitElement {
|
|
9
|
+
@property({type: String}) position: PopoverPosition = "bottom";
|
|
10
|
+
@property({type: Number}) offset = 8;
|
|
11
|
+
@property({type: Boolean, reflect: true}) disabled = false;
|
|
12
|
+
|
|
13
|
+
@state() private _isOpen = false;
|
|
14
|
+
|
|
15
|
+
@query(".trigger-wrapper") private _triggerWrapper!: HTMLElement;
|
|
16
|
+
@query("#popover-content") private _popoverEl!: HTMLElement;
|
|
17
|
+
|
|
18
|
+
@queryAssignedElements({slot: "trigger"}) private _triggerElements!: HTMLElement[];
|
|
19
|
+
|
|
20
|
+
static styles = [
|
|
21
|
+
resetStyles,
|
|
22
|
+
css`
|
|
23
|
+
:host {
|
|
24
|
+
display: inline-block;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.trigger-wrapper {
|
|
28
|
+
display: inline-block;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Native popover styles */
|
|
33
|
+
[popover] {
|
|
34
|
+
position: fixed;
|
|
35
|
+
margin: 0;
|
|
36
|
+
padding: 0;
|
|
37
|
+
border: none;
|
|
38
|
+
background: transparent;
|
|
39
|
+
overflow: visible;
|
|
40
|
+
/* 🚀 FIX: Erase the browser's native centering inset so our JS coordinates work */
|
|
41
|
+
inset: auto;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.popover-surface {
|
|
45
|
+
background: var(--st-color-surface);
|
|
46
|
+
border: 1px solid var(--st-color-border);
|
|
47
|
+
border-radius: var(--st-radius-md);
|
|
48
|
+
box-shadow:
|
|
49
|
+
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
50
|
+
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
51
|
+
padding: var(--st-spacing-md);
|
|
52
|
+
color: var(--st-color-text);
|
|
53
|
+
|
|
54
|
+
opacity: 0;
|
|
55
|
+
transform: scale(0.95);
|
|
56
|
+
transition:
|
|
57
|
+
opacity 0.15s ease,
|
|
58
|
+
transform 0.15s ease;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
[popover]:popover-open .popover-surface {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
transform: scale(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
[popover]::backdrop {
|
|
67
|
+
background: transparent;
|
|
68
|
+
}
|
|
69
|
+
`,
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
override updated(changedProperties: PropertyValues) {
|
|
73
|
+
super.updated(changedProperties);
|
|
74
|
+
if (changedProperties.has("_isOpen")) {
|
|
75
|
+
this._triggerElements?.forEach((el) => {
|
|
76
|
+
el.setAttribute("aria-expanded", String(this._isOpen));
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private _handleTriggerSlotChange() {
|
|
82
|
+
this._triggerElements?.forEach((el) => {
|
|
83
|
+
el.setAttribute("aria-haspopup", "dialog");
|
|
84
|
+
el.setAttribute("aria-expanded", String(this._isOpen));
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private _togglePopover() {
|
|
89
|
+
if (this.disabled) return;
|
|
90
|
+
this._isOpen ? this.hide() : this.show();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public show() {
|
|
94
|
+
if (this.disabled || this._isOpen) return;
|
|
95
|
+
|
|
96
|
+
// 🚀 FIX: Update state synchronously so the _reposition check passes instantly
|
|
97
|
+
this._isOpen = true;
|
|
98
|
+
|
|
99
|
+
this._popoverEl.showPopover();
|
|
100
|
+
this._reposition();
|
|
101
|
+
|
|
102
|
+
window.addEventListener("resize", this._reposition);
|
|
103
|
+
window.addEventListener("scroll", this._reposition, true);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public hide() {
|
|
107
|
+
if (!this._isOpen) return;
|
|
108
|
+
|
|
109
|
+
// 🚀 FIX: Update state synchronously
|
|
110
|
+
this._isOpen = false;
|
|
111
|
+
|
|
112
|
+
this._popoverEl.hidePopover();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private _handleNativeToggle(e: Event) {
|
|
116
|
+
const isOpening = (e as any).newState === "open";
|
|
117
|
+
this._isOpen = isOpening;
|
|
118
|
+
|
|
119
|
+
// This handles the cleanup when the user clicks outside or hits ESC
|
|
120
|
+
if (!isOpening) {
|
|
121
|
+
window.removeEventListener("resize", this._reposition);
|
|
122
|
+
window.removeEventListener("scroll", this._reposition, true);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private _reposition = () => {
|
|
127
|
+
if (!this._isOpen || !this._popoverEl || !this._triggerWrapper) return;
|
|
128
|
+
|
|
129
|
+
const triggerRect = this._triggerWrapper.getBoundingClientRect();
|
|
130
|
+
const popoverRect = this._popoverEl.getBoundingClientRect();
|
|
131
|
+
|
|
132
|
+
let top = 0;
|
|
133
|
+
let left = 0;
|
|
134
|
+
|
|
135
|
+
switch (this.position) {
|
|
136
|
+
case "bottom":
|
|
137
|
+
top = triggerRect.bottom + this.offset;
|
|
138
|
+
left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
|
|
139
|
+
break;
|
|
140
|
+
case "top":
|
|
141
|
+
top = triggerRect.top - popoverRect.height - this.offset;
|
|
142
|
+
left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
|
|
143
|
+
break;
|
|
144
|
+
case "right":
|
|
145
|
+
top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
|
|
146
|
+
left = triggerRect.right + this.offset;
|
|
147
|
+
break;
|
|
148
|
+
case "left":
|
|
149
|
+
top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
|
|
150
|
+
left = triggerRect.left - popoverRect.width - this.offset;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const padding = 8;
|
|
155
|
+
if (left < padding) left = padding;
|
|
156
|
+
if (left + popoverRect.width > window.innerWidth - padding) {
|
|
157
|
+
left = window.innerWidth - popoverRect.width - padding;
|
|
158
|
+
}
|
|
159
|
+
if (top < padding) top = padding;
|
|
160
|
+
if (top + popoverRect.height > window.innerHeight - padding) {
|
|
161
|
+
top = window.innerHeight - popoverRect.height - padding;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this._popoverEl.style.top = `${top}px`;
|
|
165
|
+
this._popoverEl.style.left = `${left}px`;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
render() {
|
|
169
|
+
return html`
|
|
170
|
+
<div
|
|
171
|
+
class="trigger-wrapper"
|
|
172
|
+
@click=${this._togglePopover}
|
|
173
|
+
@keydown=${(e: KeyboardEvent) =>
|
|
174
|
+
e.key === "Enter" || e.key === " " ? this._togglePopover() : null}
|
|
175
|
+
>
|
|
176
|
+
<slot name="trigger" @slotchange=${this._handleTriggerSlotChange}></slot>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<div id="popover-content" popover="auto" @toggle=${this._handleNativeToggle}>
|
|
180
|
+
<div class="popover-surface">
|
|
181
|
+
<slot name="content"></slot>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
`;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
declare global {
|
|
189
|
+
interface HTMLElementTagNameMap {
|
|
190
|
+
"st-popover": StPopover;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {LitElement, html, css} from "lit";
|
|
2
2
|
import {customElement, property} from "lit/decorators.js";
|
|
3
|
+
import {ifDefined} from "lit/directives/if-defined.js";
|
|
3
4
|
import {resetStyles} from "../../styles/reset.js";
|
|
4
5
|
|
|
5
6
|
@customElement("st-radio")
|
|
6
7
|
export class StRadio extends LitElement {
|
|
8
|
+
static shadowRootOptions = {...LitElement.shadowRootOptions, delegatesFocus: true};
|
|
9
|
+
|
|
7
10
|
@property({type: String}) value = "";
|
|
8
11
|
@property({type: Boolean, reflect: true}) checked = false;
|
|
9
12
|
@property({type: Boolean, reflect: true}) disabled = false;
|
|
10
|
-
|
|
11
13
|
@property({attribute: "aria-label"}) ariaLabel = "";
|
|
12
14
|
|
|
13
15
|
static styles = [
|
|
@@ -28,7 +30,6 @@ export class StRadio extends LitElement {
|
|
|
28
30
|
font-size: var(--st-font-size-base);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
/* Hide the native input visually, but keep it accessible for screen readers */
|
|
32
33
|
input {
|
|
33
34
|
position: absolute;
|
|
34
35
|
width: 1px;
|
|
@@ -41,7 +42,6 @@ export class StRadio extends LitElement {
|
|
|
41
42
|
border-width: 0;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
/* Custom Radio Circle */
|
|
45
45
|
.control {
|
|
46
46
|
display: flex;
|
|
47
47
|
align-items: center;
|
|
@@ -55,45 +55,42 @@ export class StRadio extends LitElement {
|
|
|
55
55
|
flex-shrink: 0;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/* Inner Dot (Hidden by default) */
|
|
59
58
|
.control::after {
|
|
60
59
|
content: "";
|
|
61
60
|
width: 0.625rem;
|
|
62
61
|
height: 0.625rem;
|
|
63
62
|
border-radius: 50%;
|
|
64
|
-
background-color: var(--st-color-bg);
|
|
63
|
+
background-color: var(--st-color-bg);
|
|
65
64
|
transition:
|
|
66
65
|
transform 0.2s ease,
|
|
67
66
|
background-color 0.2s ease;
|
|
68
67
|
transform: scale(0);
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
/* Hover State */
|
|
72
70
|
label:hover .control {
|
|
73
71
|
border-color: var(--st-color-primary-hover);
|
|
74
72
|
}
|
|
75
73
|
|
|
76
|
-
/* Focus State (Driven by the hidden input) */
|
|
77
74
|
input:focus-visible + .control {
|
|
78
75
|
box-shadow:
|
|
79
76
|
0 0 0 2px var(--st-color-bg),
|
|
80
77
|
0 0 0 4px var(--st-color-primary);
|
|
81
78
|
}
|
|
82
79
|
|
|
83
|
-
/* Checked State */
|
|
84
80
|
:host([checked]) .control {
|
|
85
81
|
border-color: var(--st-color-primary);
|
|
86
82
|
background-color: var(--st-color-primary);
|
|
87
83
|
}
|
|
84
|
+
|
|
88
85
|
:host([checked]) .control::after {
|
|
89
|
-
background-color:
|
|
86
|
+
background-color: #ffffff; /* Explicitly white for contrast */
|
|
90
87
|
transform: scale(1);
|
|
91
88
|
}
|
|
92
89
|
|
|
93
|
-
/* Disabled State */
|
|
94
90
|
:host([disabled]) {
|
|
95
91
|
pointer-events: none;
|
|
96
92
|
}
|
|
93
|
+
|
|
97
94
|
:host([disabled]) label {
|
|
98
95
|
opacity: 0.5;
|
|
99
96
|
cursor: not-allowed;
|
|
@@ -102,11 +99,9 @@ export class StRadio extends LitElement {
|
|
|
102
99
|
];
|
|
103
100
|
|
|
104
101
|
private _handleChange(e: Event) {
|
|
105
|
-
// Prevent the native change event from leaking out
|
|
106
102
|
e.stopPropagation();
|
|
107
103
|
|
|
108
104
|
if (!this.disabled) {
|
|
109
|
-
// Dispatch a custom event to tell the parent group this radio was selected
|
|
110
105
|
this.dispatchEvent(
|
|
111
106
|
new CustomEvent("st-radio-select", {
|
|
112
107
|
detail: {value: this.value},
|
|
@@ -122,7 +117,7 @@ export class StRadio extends LitElement {
|
|
|
122
117
|
<label>
|
|
123
118
|
<input
|
|
124
119
|
type="radio"
|
|
125
|
-
aria-label=${this.ariaLabel ||
|
|
120
|
+
aria-label=${ifDefined(this.ariaLabel || undefined)}
|
|
126
121
|
.value=${this.value}
|
|
127
122
|
.checked=${this.checked}
|
|
128
123
|
?disabled=${this.disabled}
|
|
@@ -12,23 +12,23 @@ export class StRadioGroup extends LitElement {
|
|
|
12
12
|
@property({type: String}) value = "";
|
|
13
13
|
@property({type: String}) label = "";
|
|
14
14
|
@property({attribute: "help-text"}) helpText = "";
|
|
15
|
+
@property({type: String, reflect: true}) layout: "vertical" | "horizontal" = "vertical";
|
|
16
|
+
@property({type: Boolean, reflect: true}) required = false;
|
|
15
17
|
@property({type: Boolean, reflect: true}) disabled = false;
|
|
16
18
|
@property({type: Boolean, reflect: true}) invalid = false;
|
|
17
19
|
|
|
18
|
-
// Grabs all slotted st-radio components automatically
|
|
19
20
|
@queryAssignedElements({selector: "st-radio"})
|
|
20
21
|
private _radios!: StRadio[];
|
|
21
22
|
|
|
22
23
|
constructor() {
|
|
23
24
|
super();
|
|
24
25
|
this._internals = this.attachInternals();
|
|
26
|
+
this._internals.role = "radiogroup";
|
|
25
27
|
|
|
26
|
-
// 1. Trigger error state if the user clicks away and the field is invalid
|
|
27
28
|
this.addEventListener("change", () => {
|
|
28
29
|
this.invalid = !this._internals.validity.valid;
|
|
29
30
|
});
|
|
30
31
|
|
|
31
|
-
// 2. Trigger error state if the st-form tries to submit and fails
|
|
32
32
|
this.addEventListener("invalid", () => {
|
|
33
33
|
this.invalid = true;
|
|
34
34
|
});
|
|
@@ -64,7 +64,6 @@ export class StRadioGroup extends LitElement {
|
|
|
64
64
|
gap: var(--st-spacing-sm);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/* Horizontal layout modifier */
|
|
68
67
|
:host([layout="horizontal"]) .options {
|
|
69
68
|
flex-direction: row;
|
|
70
69
|
gap: var(--st-spacing-md);
|
|
@@ -76,36 +75,40 @@ export class StRadioGroup extends LitElement {
|
|
|
76
75
|
margin: 0;
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
:host([invalid]) .help-text
|
|
80
|
-
color: var(--st-color-error);
|
|
81
|
-
}
|
|
78
|
+
:host([invalid]) .help-text,
|
|
82
79
|
:host([invalid]) legend {
|
|
83
80
|
color: var(--st-color-error);
|
|
84
81
|
}
|
|
85
82
|
`,
|
|
86
83
|
];
|
|
87
84
|
|
|
85
|
+
private _handleRadioSelect = (e: Event) => {
|
|
86
|
+
if (this.disabled) return;
|
|
87
|
+
|
|
88
|
+
const customEvent = e as CustomEvent<{value: string}>;
|
|
89
|
+
this.value = customEvent.detail.value;
|
|
90
|
+
|
|
91
|
+
// 🚀 THE FIX: Clear the invalid visual state as soon as the user makes a selection
|
|
92
|
+
if (this.invalid) {
|
|
93
|
+
this.invalid = false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
|
|
97
|
+
};
|
|
98
|
+
|
|
88
99
|
connectedCallback() {
|
|
89
100
|
super.connectedCallback();
|
|
90
|
-
|
|
91
|
-
this.addEventListener("st-radio-select", this._handleRadioSelect as EventListener);
|
|
101
|
+
this.addEventListener("st-radio-select", this._handleRadioSelect);
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
disconnectedCallback() {
|
|
95
105
|
super.disconnectedCallback();
|
|
96
|
-
this.removeEventListener("st-radio-select", this._handleRadioSelect
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private _handleRadioSelect(e: CustomEvent<{value: string}>) {
|
|
100
|
-
if (this.disabled) return;
|
|
101
|
-
this.value = e.detail.value;
|
|
102
|
-
this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
|
|
106
|
+
this.removeEventListener("st-radio-select", this._handleRadioSelect);
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
override updated(changedProperties: PropertyValues<this>) {
|
|
106
110
|
super.updated(changedProperties);
|
|
107
111
|
|
|
108
|
-
// When the group's value changes, sync the form and update all child radios
|
|
109
112
|
if (changedProperties.has("value")) {
|
|
110
113
|
this._internals.setFormValue(this.value);
|
|
111
114
|
this._syncRadios();
|
|
@@ -114,9 +117,20 @@ export class StRadioGroup extends LitElement {
|
|
|
114
117
|
if (changedProperties.has("disabled")) {
|
|
115
118
|
this._syncRadios();
|
|
116
119
|
}
|
|
120
|
+
|
|
121
|
+
if (changedProperties.has("value") || changedProperties.has("required")) {
|
|
122
|
+
this._syncValidity();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private _syncValidity() {
|
|
127
|
+
if (this.required && !this.value) {
|
|
128
|
+
this._internals.setValidity({valueMissing: true}, "Please select an option.");
|
|
129
|
+
} else {
|
|
130
|
+
this._internals.setValidity({});
|
|
131
|
+
}
|
|
117
132
|
}
|
|
118
133
|
|
|
119
|
-
// Loops through children and unchecks/checks them based on the group's current value
|
|
120
134
|
private _syncRadios() {
|
|
121
135
|
if (!this._radios) return;
|
|
122
136
|
|
|
@@ -128,11 +142,21 @@ export class StRadioGroup extends LitElement {
|
|
|
128
142
|
});
|
|
129
143
|
}
|
|
130
144
|
|
|
131
|
-
// Fired natively by the browser if the parent <form> is reset
|
|
132
145
|
formResetCallback() {
|
|
133
|
-
this.value
|
|
134
|
-
this.
|
|
146
|
+
const initialValue = this.getAttribute("value") || "";
|
|
147
|
+
this.value = initialValue;
|
|
148
|
+
this._internals.setFormValue(initialValue);
|
|
135
149
|
this._syncRadios();
|
|
150
|
+
this.updateComplete.then(() => this._syncValidity());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Expose standard native methods
|
|
154
|
+
checkValidity() {
|
|
155
|
+
return this._internals.checkValidity();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
reportValidity() {
|
|
159
|
+
return this._internals.reportValidity();
|
|
136
160
|
}
|
|
137
161
|
|
|
138
162
|
render() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface RouteDefinition {
|
|
2
2
|
component: string;
|
|
3
|
-
[key: string]: unknown;
|
|
3
|
+
[key: string]: unknown;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export type RouteConfig = Record<string, RouteDefinition>;
|
|
@@ -13,85 +13,105 @@ export interface RouteMatch {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function getRouteWeight(pattern: string): number {
|
|
16
|
-
const segments = pattern.split(
|
|
16
|
+
const segments = pattern.split("/").filter(Boolean);
|
|
17
17
|
return segments.reduce((weight, seg) => {
|
|
18
|
-
return weight + (seg.startsWith(
|
|
18
|
+
return weight + (seg.startsWith(":") ? 1 : 10);
|
|
19
19
|
}, 0);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export class AppRouter {
|
|
23
23
|
private _routes: RouteConfig = {};
|
|
24
24
|
private _initialized = false;
|
|
25
|
+
private _boundNotify: () => void;
|
|
26
|
+
private _boundClick: (e: MouseEvent) => void;
|
|
25
27
|
|
|
26
28
|
constructor(routes?: RouteConfig) {
|
|
29
|
+
this._boundNotify = () => this._notify();
|
|
30
|
+
this._boundClick = (e: MouseEvent) => this._handleClick(e);
|
|
31
|
+
|
|
27
32
|
if (routes) {
|
|
28
33
|
this.setRoutes(routes);
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const path = e.composedPath();
|
|
39
|
-
const link = path.find((el: any) =>
|
|
40
|
-
el && typeof el.hasAttribute === 'function' && el.hasAttribute('href')
|
|
41
|
-
) as HTMLElement | undefined;
|
|
36
|
+
// 🚀 FIX: SSR Safety check. Prevents crashes during Node.js CI test imports.
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
38
|
+
window.addEventListener("popstate", this._boundNotify);
|
|
39
|
+
document.addEventListener("click", this._boundClick as EventListener);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
/**
|
|
44
|
+
* 🚀 FIX: Removes global listeners to prevent memory leaks during testing/teardown.
|
|
45
|
+
*/
|
|
46
|
+
public destroy() {
|
|
47
|
+
if (typeof window !== "undefined") {
|
|
48
|
+
window.removeEventListener("popstate", this._boundNotify);
|
|
49
|
+
document.removeEventListener("click", this._boundClick as EventListener);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
private _handleClick(e: MouseEvent) {
|
|
54
|
+
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
47
57
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
const path = e.composedPath();
|
|
59
|
+
const link = path.find(
|
|
60
|
+
(el: any) => el && typeof el.hasAttribute === "function" && el.hasAttribute("href"),
|
|
61
|
+
) as HTMLElement | undefined;
|
|
62
|
+
|
|
63
|
+
if (!link) return;
|
|
64
|
+
|
|
65
|
+
const href = link.getAttribute("href");
|
|
66
|
+
const target = link.getAttribute("target");
|
|
67
|
+
|
|
68
|
+
// 🚀 FIX: Ignore links with the 'download' attribute so file downloads aren't hijacked
|
|
69
|
+
if (
|
|
70
|
+
!href ||
|
|
71
|
+
href.startsWith("http") ||
|
|
72
|
+
href.startsWith("mailto:") ||
|
|
73
|
+
target === "_blank" ||
|
|
74
|
+
link.hasAttribute("download")
|
|
75
|
+
) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
51
78
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
79
|
+
if (href.startsWith("/")) {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
this.navigate(href);
|
|
82
|
+
}
|
|
57
83
|
}
|
|
58
84
|
|
|
59
|
-
/**
|
|
60
|
-
* Register or update the route configuration for this application.
|
|
61
|
-
*/
|
|
62
85
|
public setRoutes(routes: RouteConfig) {
|
|
63
86
|
this._routes = routes;
|
|
64
87
|
|
|
65
|
-
// Trigger initial route resolution once routes are registered
|
|
66
88
|
if (!this._initialized) {
|
|
67
89
|
this._initialized = true;
|
|
68
|
-
|
|
90
|
+
if (typeof window !== "undefined") {
|
|
91
|
+
setTimeout(() => this._notify(), 0);
|
|
92
|
+
}
|
|
69
93
|
} else {
|
|
70
94
|
this._notify();
|
|
71
95
|
}
|
|
72
96
|
}
|
|
73
97
|
|
|
74
|
-
/**
|
|
75
|
-
* Programmatically navigate to an internal path.
|
|
76
|
-
*/
|
|
77
98
|
public navigate(path: string) {
|
|
78
|
-
|
|
99
|
+
if (typeof window !== "undefined") {
|
|
100
|
+
window.history.pushState(null, "", path);
|
|
101
|
+
}
|
|
79
102
|
this._notify();
|
|
80
103
|
}
|
|
81
104
|
|
|
82
|
-
/**
|
|
83
|
-
* Matches a URL path against the registered routes.
|
|
84
|
-
*/
|
|
85
105
|
public matchRoute(currentPath: string): RouteMatch | null {
|
|
86
106
|
const sortedRoutes = Object.entries(this._routes).sort(
|
|
87
|
-
([patternA], [patternB]) => getRouteWeight(patternB) - getRouteWeight(patternA)
|
|
107
|
+
([patternA], [patternB]) => getRouteWeight(patternB) - getRouteWeight(patternA),
|
|
88
108
|
);
|
|
89
109
|
|
|
90
110
|
for (const [pattern, routeDef] of sortedRoutes) {
|
|
91
111
|
const paramNames: string[] = [];
|
|
92
112
|
const regexPath = pattern.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
|
|
93
113
|
paramNames.push(paramName);
|
|
94
|
-
return
|
|
114
|
+
return "([^/]+)";
|
|
95
115
|
});
|
|
96
116
|
|
|
97
117
|
const regex = new RegExp(`^${regexPath}$`);
|
|
@@ -107,7 +127,7 @@ export class AppRouter {
|
|
|
107
127
|
path: currentPath,
|
|
108
128
|
component: routeDef.component,
|
|
109
129
|
params,
|
|
110
|
-
definition: routeDef
|
|
130
|
+
definition: routeDef,
|
|
111
131
|
};
|
|
112
132
|
}
|
|
113
133
|
}
|
|
@@ -116,19 +136,22 @@ export class AppRouter {
|
|
|
116
136
|
}
|
|
117
137
|
|
|
118
138
|
private _notify() {
|
|
139
|
+
if (typeof window === "undefined") return;
|
|
140
|
+
|
|
119
141
|
const currentPath = window.location.pathname;
|
|
120
142
|
const match = this.matchRoute(currentPath);
|
|
121
143
|
|
|
122
|
-
window.dispatchEvent(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
144
|
+
window.dispatchEvent(
|
|
145
|
+
new CustomEvent("route-changed", {
|
|
146
|
+
detail: {
|
|
147
|
+
path: currentPath,
|
|
148
|
+
component: match ? match.component : "not-found-page",
|
|
149
|
+
params: match ? match.params : {},
|
|
150
|
+
definition: match ? match.definition : null,
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
);
|
|
130
154
|
}
|
|
131
155
|
}
|
|
132
156
|
|
|
133
|
-
|
|
134
|
-
export const router = new AppRouter();
|
|
157
|
+
export const router = new AppRouter();
|