@easemate/web-kit 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +360 -168
- package/build/components/code/index.cjs +3 -3
- package/build/components/code/index.js +3 -3
- package/build/components/curve/canvas-controls.cjs +3 -3
- package/build/components/curve/canvas-controls.js +3 -3
- package/build/components/curve/canvas.cjs +4 -4
- package/build/components/curve/canvas.js +4 -4
- package/build/components/curve/controls.cjs +6 -6
- package/build/components/curve/controls.d.cts +4 -4
- package/build/components/curve/controls.d.ts +4 -4
- package/build/components/curve/controls.js +6 -6
- package/build/components/curve/index.cjs +3 -3
- package/build/components/curve/index.d.cts +1 -1
- package/build/components/curve/index.d.ts +1 -1
- package/build/components/curve/index.js +3 -3
- package/build/components/curve/output.cjs +3 -3
- package/build/components/curve/output.d.cts +1 -1
- package/build/components/curve/output.d.ts +1 -1
- package/build/components/curve/output.js +3 -3
- package/build/components/curve/toolbar.cjs +7 -7
- package/build/components/curve/toolbar.d.cts +4 -4
- package/build/components/curve/toolbar.d.ts +4 -4
- package/build/components/curve/toolbar.js +7 -7
- package/build/decorators/OutsideClick.cjs +2 -2
- package/build/decorators/OutsideClick.d.cts +2 -2
- package/build/decorators/OutsideClick.d.ts +2 -2
- package/build/decorators/OutsideClick.js +2 -2
- package/build/elements/button/index.cjs +2 -2
- package/build/elements/button/index.js +2 -2
- package/build/elements/checkbox/index.cjs +4 -4
- package/build/elements/checkbox/index.js +4 -4
- package/build/elements/color/index.cjs +4 -4
- package/build/elements/color/index.d.cts +1 -1
- package/build/elements/color/index.d.ts +1 -1
- package/build/elements/color/index.js +4 -4
- package/build/elements/color/picker.cjs +4 -4
- package/build/elements/color/picker.js +4 -4
- package/build/elements/dropdown/index.cjs +4 -4
- package/build/elements/dropdown/index.d.cts +1 -1
- package/build/elements/dropdown/index.d.ts +1 -1
- package/build/elements/dropdown/index.js +4 -4
- package/build/elements/field/index.cjs +2 -2
- package/build/elements/field/index.js +2 -2
- package/build/elements/icons/animation/chevron.cjs +2 -2
- package/build/elements/icons/animation/chevron.js +2 -2
- package/build/elements/icons/animation/clear.cjs +1 -1
- package/build/elements/icons/animation/clear.js +1 -1
- package/build/elements/icons/animation/grid.cjs +2 -2
- package/build/elements/icons/animation/grid.js +2 -2
- package/build/elements/icons/animation/loading.cjs +1 -1
- package/build/elements/icons/animation/loading.js +1 -1
- package/build/elements/icons/animation/snap.cjs +2 -2
- package/build/elements/icons/animation/snap.js +2 -2
- package/build/elements/icons/interface/anchor-add.cjs +1 -1
- package/build/elements/icons/interface/anchor-add.js +1 -1
- package/build/elements/icons/interface/anchor-remove.cjs +1 -1
- package/build/elements/icons/interface/anchor-remove.js +1 -1
- package/build/elements/icons/interface/arrow-up.cjs +1 -1
- package/build/elements/icons/interface/arrow-up.js +1 -1
- package/build/elements/icons/interface/arrows-vertical.cjs +1 -1
- package/build/elements/icons/interface/arrows-vertical.js +1 -1
- package/build/elements/icons/interface/bezier-angle.cjs +1 -1
- package/build/elements/icons/interface/bezier-angle.js +1 -1
- package/build/elements/icons/interface/bezier-distribute.cjs +1 -1
- package/build/elements/icons/interface/bezier-distribute.js +1 -1
- package/build/elements/icons/interface/bezier-length.cjs +1 -1
- package/build/elements/icons/interface/bezier-length.js +1 -1
- package/build/elements/icons/interface/bezier-mirror.cjs +1 -1
- package/build/elements/icons/interface/bezier-mirror.js +1 -1
- package/build/elements/icons/interface/bezier.cjs +1 -1
- package/build/elements/icons/interface/bezier.js +1 -1
- package/build/elements/icons/interface/check.cjs +1 -1
- package/build/elements/icons/interface/check.js +1 -1
- package/build/elements/icons/interface/circle-arrow-left.cjs +1 -1
- package/build/elements/icons/interface/circle-arrow-left.js +1 -1
- package/build/elements/icons/interface/circle-arrow-right.cjs +1 -1
- package/build/elements/icons/interface/circle-arrow-right.js +1 -1
- package/build/elements/icons/interface/code.cjs +1 -1
- package/build/elements/icons/interface/code.js +1 -1
- package/build/elements/icons/interface/dots.cjs +1 -1
- package/build/elements/icons/interface/dots.js +1 -1
- package/build/elements/icons/interface/mention.cjs +1 -1
- package/build/elements/icons/interface/mention.js +1 -1
- package/build/elements/icons/interface/minus.cjs +1 -1
- package/build/elements/icons/interface/minus.js +1 -1
- package/build/elements/icons/interface/picker.cjs +1 -1
- package/build/elements/icons/interface/picker.js +1 -1
- package/build/elements/icons/interface/plus.cjs +1 -1
- package/build/elements/icons/interface/plus.js +1 -1
- package/build/elements/icons/interface/settings.cjs +1 -1
- package/build/elements/icons/interface/settings.js +1 -1
- package/build/elements/index.cjs +5 -2
- package/build/elements/index.d.cts +2 -1
- package/build/elements/index.d.ts +2 -1
- package/build/elements/index.js +2 -1
- package/build/elements/input/index.cjs +4 -4
- package/build/elements/input/index.js +4 -4
- package/build/elements/logo/index.cjs +2 -2
- package/build/elements/logo/index.js +2 -2
- package/build/elements/monitor/fps.cjs +3 -3
- package/build/elements/monitor/fps.js +3 -3
- package/build/elements/monitor/index.cjs +4 -4
- package/build/elements/monitor/index.js +4 -4
- package/build/elements/number/index.cjs +4 -4
- package/build/elements/number/index.js +4 -4
- package/build/elements/origin/index.cjs +4 -4
- package/build/elements/origin/index.js +4 -4
- package/build/elements/panel/index.cjs +496 -0
- package/build/elements/panel/index.d.cts +67 -0
- package/build/elements/panel/index.d.ts +67 -0
- package/build/elements/panel/index.js +492 -0
- package/build/elements/popover/index.cjs +2 -2
- package/build/elements/popover/index.js +2 -2
- package/build/elements/radio/index.cjs +3 -3
- package/build/elements/radio/index.js +3 -3
- package/build/elements/radio/input.cjs +4 -4
- package/build/elements/radio/input.js +4 -4
- package/build/elements/slider/index.cjs +4 -4
- package/build/elements/slider/index.js +4 -4
- package/build/elements/state/index.cjs +61 -468
- package/build/elements/state/index.d.cts +34 -25
- package/build/elements/state/index.d.ts +34 -25
- package/build/elements/state/index.js +63 -470
- package/build/elements/toggle/index.cjs +4 -4
- package/build/elements/toggle/index.js +4 -4
- package/build/elements/tooltip/index.cjs +4 -4
- package/build/elements/tooltip/index.d.cts +1 -1
- package/build/elements/tooltip/index.d.ts +1 -1
- package/build/elements/tooltip/index.js +4 -4
- package/build/internal/component-loaders.cjs +2 -0
- package/build/internal/component-loaders.d.cts +2 -2
- package/build/internal/component-loaders.d.ts +2 -2
- package/build/internal/component-loaders.js +2 -0
- package/build/react/events.cjs +25 -0
- package/build/react/events.d.cts +39 -0
- package/build/react/events.d.ts +39 -0
- package/build/react/events.js +22 -0
- package/build/react/index.cjs +19 -0
- package/build/react/index.d.cts +13 -0
- package/build/react/index.d.ts +13 -0
- package/build/react/index.js +12 -0
- package/build/react/provider.cjs +134 -0
- package/build/react/provider.d.cts +81 -0
- package/build/react/provider.d.ts +81 -0
- package/build/react/provider.js +98 -0
- package/build/react/types.cjs +8 -0
- package/build/react/types.d.cts +55 -0
- package/build/react/types.d.ts +55 -0
- package/build/react/types.js +7 -0
- package/build/react/use-ease-state.cjs +129 -0
- package/build/react/use-ease-state.d.cts +95 -0
- package/build/react/use-ease-state.d.ts +95 -0
- package/build/react/use-ease-state.js +126 -0
- package/build/react/use-web-kit.cjs +150 -0
- package/build/react/use-web-kit.d.cts +80 -0
- package/build/react/use-web-kit.d.ts +80 -0
- package/build/react/use-web-kit.js +114 -0
- package/build/register.cjs +1 -0
- package/build/register.d.cts +1 -0
- package/build/register.d.ts +1 -0
- package/build/register.js +1 -0
- package/package.json +16 -2
|
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.State = void 0;
|
|
4
4
|
const lit_html_1 = require("lit-html");
|
|
5
5
|
const shared_1 = require("../shared.cjs");
|
|
6
|
-
const Component_1 = require("
|
|
7
|
-
const Listen_1 = require("
|
|
8
|
-
const Prop_1 = require("
|
|
9
|
-
const Query_1 = require("
|
|
6
|
+
const Component_1 = require("../../decorators/Component");
|
|
7
|
+
const Listen_1 = require("../../decorators/Listen");
|
|
8
|
+
const Prop_1 = require("../../decorators/Prop");
|
|
9
|
+
const Query_1 = require("../../decorators/Query");
|
|
10
10
|
const readControlValue = (element) => {
|
|
11
11
|
if (typeof element.value === 'string' || typeof element.value === 'number') {
|
|
12
12
|
return element.value;
|
|
@@ -28,195 +28,52 @@ const getControlName = (element) => {
|
|
|
28
28
|
}
|
|
29
29
|
return element.getAttribute?.('name') ?? null;
|
|
30
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* State aggregator component - collects and manages state from child controls.
|
|
33
|
+
*
|
|
34
|
+
* This component provides state management without any visual styling.
|
|
35
|
+
* Use it standalone or wrap it with `<ease-panel>` for a styled container.
|
|
36
|
+
*
|
|
37
|
+
* @tag ease-state
|
|
38
|
+
*
|
|
39
|
+
* @slot - Default slot for controls
|
|
40
|
+
*
|
|
41
|
+
* @fires state-change - Fired when any control value changes
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```html
|
|
45
|
+
* <!-- Standalone usage (no panel) -->
|
|
46
|
+
* <ease-state>
|
|
47
|
+
* <ease-field label="Duration">
|
|
48
|
+
* <ease-slider name="duration" value="1" min="0" max="5"></ease-slider>
|
|
49
|
+
* </ease-field>
|
|
50
|
+
* <ease-field label="Loop">
|
|
51
|
+
* <ease-toggle name="loop"></ease-toggle>
|
|
52
|
+
* </ease-field>
|
|
53
|
+
* </ease-state>
|
|
54
|
+
*
|
|
55
|
+
* <!-- With panel wrapper -->
|
|
56
|
+
* <ease-panel>
|
|
57
|
+
* <span slot="headline">Animation Controls</span>
|
|
58
|
+
* <ease-state>
|
|
59
|
+
* <ease-field label="Duration">
|
|
60
|
+
* <ease-slider name="duration" value="1" min="0" max="5"></ease-slider>
|
|
61
|
+
* </ease-field>
|
|
62
|
+
* </ease-state>
|
|
63
|
+
* </ease-panel>
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
31
66
|
@(0, Component_1.Component)({
|
|
32
67
|
tag: 'ease-state',
|
|
33
68
|
shadowMode: 'open',
|
|
34
69
|
styles: `
|
|
35
70
|
:host {
|
|
36
|
-
|
|
37
|
-
--ease-state-transition-easing: cubic-bezier(.25, 0, .5, 1);
|
|
71
|
+
display: contents;
|
|
38
72
|
}
|
|
39
73
|
|
|
40
|
-
[part="
|
|
41
|
-
display: block;
|
|
42
|
-
width: 100%;
|
|
43
|
-
max-width: var(--ease-panel-max-width, 332px);
|
|
44
|
-
border-radius: var(--ease-panel-radius, 12px);
|
|
45
|
-
border: 1px solid var(--ease-panel-border-color, var(--color-white-6));
|
|
46
|
-
background-clip: padding-box;
|
|
47
|
-
background: var(--ease-panel-background, var(--color-gray-1000));
|
|
48
|
-
box-shadow: var(--ease-panel-shadow, 0 0 40px 0 var(--color-white-2) inset);
|
|
49
|
-
box-sizing: border-box;
|
|
50
|
-
padding: var(--ease-panel-padding, 12px);
|
|
51
|
-
margin: auto;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
[part="header"] {
|
|
55
|
-
display: flex;
|
|
56
|
-
align-items: center;
|
|
57
|
-
gap: 8px;
|
|
58
|
-
width: 100%;
|
|
59
|
-
margin-bottom: 12px;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
[part="headline"] {
|
|
63
|
-
font-size: var(--ease-panel-title-font-size, 14px);
|
|
64
|
-
font-weight: var(--ease-panel-title-font-weight, 500);
|
|
65
|
-
line-height: var(--ease-panel-title-line-height, 24px);
|
|
66
|
-
font-family: var(--ease-font-family, "Instrument Sans", sans-serif);
|
|
67
|
-
color: var(--ease-panel-title-color, var(--color-blue-100));
|
|
68
|
-
margin: 0 0 0 4px;
|
|
69
|
-
flex-grow: 1;
|
|
70
|
-
text-ellipsis: ellipsis;
|
|
71
|
-
overflow: hidden;
|
|
72
|
-
white-space: nowrap;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
[part="headline"]:has(+ [part="tabs"]:not(:empty)) {
|
|
76
|
-
display: none;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
[part="tabs"] {
|
|
80
|
-
display: flex;
|
|
81
|
-
align-items: center;
|
|
82
|
-
gap: 2px;
|
|
83
|
-
flex-grow: 1;
|
|
84
|
-
margin: 0 0 0 4px;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
[part="tabs"]:empty {
|
|
88
|
-
display: none;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
[part="tab"] {
|
|
92
|
-
appearance: none;
|
|
93
|
-
font-size: var(--ease-panel-tab-font-size, 13px);
|
|
94
|
-
font-weight: var(--ease-panel-tab-font-weight, 500);
|
|
95
|
-
line-height: var(--ease-panel-tab-line-height, 24px);
|
|
96
|
-
font-family: var(--ease-font-family, "Instrument Sans", sans-serif);
|
|
97
|
-
color: var(--ease-panel-tab-color, var(--color-gray-600));
|
|
98
|
-
background: transparent;
|
|
99
|
-
border: none;
|
|
100
|
-
padding: 4px 8px;
|
|
101
|
-
margin: 0;
|
|
102
|
-
cursor: pointer;
|
|
103
|
-
border-radius: var(--ease-panel-tab-radius, 6px);
|
|
104
|
-
transition: color 0.2s, background-color 0.2s;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
[part="tab"]:hover {
|
|
108
|
-
color: var(--ease-panel-tab-color-hover, var(--color-blue-100));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
[part="tab"][aria-selected="true"] {
|
|
112
|
-
color: var(--ease-panel-tab-color-active, var(--color-blue-100));
|
|
113
|
-
background: var(--ease-panel-tab-background-active, var(--color-white-4));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
[part="actions"] {
|
|
117
|
-
display: flex;
|
|
118
|
-
align-items: center;
|
|
119
|
-
gap: 4px;
|
|
120
|
-
margin-left: auto;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
slot[name="actions"]::slotted(button),
|
|
124
|
-
slot[name="actions"]::slotted(a) {
|
|
125
|
-
--ease-icon-size: var(--ease-panel-action-icon-size, 16px);
|
|
126
|
-
|
|
127
|
-
appearance: none;
|
|
128
|
-
flex: 0 0 24px;
|
|
129
|
-
border: none;
|
|
130
|
-
outline: none;
|
|
131
|
-
background-color: transparent;
|
|
132
|
-
padding: 4px;
|
|
133
|
-
margin: 0;
|
|
134
|
-
cursor: pointer;
|
|
135
|
-
color: var(--color-gray-600);
|
|
136
|
-
transition: color 0.2s;
|
|
137
|
-
text-decoration: none;
|
|
138
|
-
display: flex;
|
|
139
|
-
align-items: center;
|
|
140
|
-
justify-content: center;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
slot[name="actions"]::slotted(button:hover),
|
|
144
|
-
slot[name="actions"]::slotted(button:focus-visible),
|
|
145
|
-
slot[name="actions"]::slotted(a:hover),
|
|
146
|
-
slot[name="actions"]::slotted(a:focus-visible) {
|
|
147
|
-
color: var(--color-blue-100);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
slot[name="actions"]::slotted(ease-dropdown) {
|
|
151
|
-
flex: 0 0 auto;
|
|
152
|
-
width: auto;
|
|
153
|
-
|
|
154
|
-
--ease-icon-size: var(--ease-panel-action-icon-size, 16px);
|
|
155
|
-
--ease-dropdown-trigger-padding: 4px;
|
|
156
|
-
--ease-dropdown-radius: 6px;
|
|
157
|
-
--ease-dropdown-background: transparent;
|
|
158
|
-
--ease-dropdown-background-hover: transparent;
|
|
159
|
-
--ease-dropdown-shadow: none;
|
|
160
|
-
--ease-dropdown-color: var(--color-gray-600);
|
|
161
|
-
--ease-popover-placement: bottom-end;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
slot[name="actions"]::slotted(ease-dropdown:hover),
|
|
165
|
-
slot[name="actions"]::slotted(ease-dropdown:focus-within) {
|
|
166
|
-
--ease-dropdown-color: var(--color-blue-100);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
[part="content"] {
|
|
170
|
-
display: block;
|
|
171
|
-
width: 100%;
|
|
172
|
-
box-sizing: border-box;
|
|
173
|
-
margin: auto;
|
|
174
|
-
overflow: hidden;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
[part="content"][data-animating="true"] {
|
|
178
|
-
transition: height var(--ease-state-transition-duration) var(--ease-state-transition-easing);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
[part="form"] {
|
|
182
|
-
width: 100%;
|
|
183
|
-
position: relative;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
[part="tab-panel"] {
|
|
187
|
-
width: 100%;
|
|
188
|
-
pointer-events: none;
|
|
189
|
-
display: none;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
[part="tab-panel"][data-state="active"] {
|
|
193
|
-
display: block;
|
|
194
|
-
pointer-events: auto;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
[part="tab-panel"][data-state="hidden"] {
|
|
198
|
-
display: none;
|
|
199
|
-
pointer-events: none;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
[part="footer"] {
|
|
203
|
-
display: flex;
|
|
204
|
-
align-items: center;
|
|
205
|
-
justify-content: space-between;
|
|
206
|
-
width: 100%;
|
|
207
|
-
padding: var(--ease-panel-footer-padding, 12px);
|
|
208
|
-
box-sizing: border-box;
|
|
209
|
-
border-top: 1px solid var(--color-white-4);
|
|
210
|
-
|
|
211
|
-
&:not(:has([data-has-content="true"])) {
|
|
212
|
-
display: none;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
::slotted([slot="entry"]),
|
|
217
|
-
::slotted([slot^="tab-"]) {
|
|
74
|
+
[part="container"] {
|
|
218
75
|
display: grid;
|
|
219
|
-
gap: 12px;
|
|
76
|
+
gap: var(--ease-state-gap, 12px);
|
|
220
77
|
box-sizing: border-box;
|
|
221
78
|
width: 100%;
|
|
222
79
|
}
|
|
@@ -227,35 +84,10 @@ class State extends HTMLElement {
|
|
|
227
84
|
#state = {};
|
|
228
85
|
#initialState = {};
|
|
229
86
|
#subscribers = new Map();
|
|
230
|
-
#tabs = [];
|
|
231
|
-
#isAnimating = false;
|
|
232
87
|
@(0, Prop_1.Prop)({ reflect: true })
|
|
233
88
|
accessor value;
|
|
234
|
-
@(0,
|
|
235
|
-
|
|
236
|
-
reflect: true,
|
|
237
|
-
attribute: 'active-tab',
|
|
238
|
-
defaultValue: 0,
|
|
239
|
-
onChange(next, previous) {
|
|
240
|
-
const self = this;
|
|
241
|
-
if (next !== previous && previous !== undefined) {
|
|
242
|
-
self.handleActiveTabChange(previous, next);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
})
|
|
246
|
-
accessor activeTab = 0;
|
|
247
|
-
/** @internal */
|
|
248
|
-
handleActiveTabChange(previous, next) {
|
|
249
|
-
this.performTabAnimation(previous, next);
|
|
250
|
-
}
|
|
251
|
-
@(0, Query_1.Query)('slot[name="entry"]')
|
|
252
|
-
accessor entrySlot;
|
|
253
|
-
@(0, Query_1.Query)('output')
|
|
254
|
-
accessor outputElement;
|
|
255
|
-
@(0, Query_1.Query)('[part="content"]')
|
|
256
|
-
accessor contentElement;
|
|
257
|
-
@(0, Query_1.Query)('[part="form"]')
|
|
258
|
-
accessor formElement;
|
|
89
|
+
@(0, Query_1.Query)('slot')
|
|
90
|
+
accessor defaultSlot;
|
|
259
91
|
/**
|
|
260
92
|
* Get the current state object with all control values
|
|
261
93
|
*/
|
|
@@ -320,233 +152,21 @@ class State extends HTMLElement {
|
|
|
320
152
|
this.set(name, value);
|
|
321
153
|
}
|
|
322
154
|
}
|
|
323
|
-
/**
|
|
324
|
-
* Switch to a specific tab by index
|
|
325
|
-
* @param index - The tab index (0-based)
|
|
326
|
-
*/
|
|
327
|
-
setTab(index) {
|
|
328
|
-
if (index >= 0 && index < this.#tabs.length && index !== this.activeTab) {
|
|
329
|
-
this.activeTab = index;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
155
|
connectedCallback() {
|
|
333
|
-
this.#syncTabs();
|
|
334
156
|
this.#attach();
|
|
335
|
-
this.
|
|
157
|
+
this.defaultSlot?.addEventListener('slotchange', this.#handleSlotChange);
|
|
336
158
|
}
|
|
337
159
|
disconnectedCallback() {
|
|
338
160
|
this.#detach();
|
|
339
|
-
this.
|
|
340
|
-
}
|
|
341
|
-
afterRender() {
|
|
342
|
-
if (this.outputElement) {
|
|
343
|
-
this.outputElement.value = this.value ?? '';
|
|
344
|
-
}
|
|
345
|
-
this.#syncTabs();
|
|
161
|
+
this.defaultSlot?.removeEventListener('slotchange', this.#handleSlotChange);
|
|
346
162
|
}
|
|
347
163
|
render() {
|
|
348
|
-
const hasTabs = this.#tabs.length > 0;
|
|
349
164
|
return (0, lit_html_1.html) `
|
|
350
|
-
<
|
|
351
|
-
<
|
|
352
|
-
<h3 part="headline"><slot name="headline"></slot></h3>
|
|
353
|
-
${this.#renderTabs()}
|
|
354
|
-
<div part="actions">
|
|
355
|
-
<slot name="actions"></slot>
|
|
356
|
-
</div>
|
|
357
|
-
</div>
|
|
358
|
-
<div part="content">
|
|
359
|
-
<div part="form">
|
|
360
|
-
${hasTabs ? this.#renderTabPanels() : (0, lit_html_1.html) `<slot name="entry"></slot>`}
|
|
361
|
-
</div>
|
|
362
|
-
</div>
|
|
363
|
-
<div part="footer">
|
|
364
|
-
<slot name="footer"></slot>
|
|
365
|
-
</div>
|
|
366
|
-
</section>
|
|
367
|
-
`;
|
|
368
|
-
}
|
|
369
|
-
#renderTabs() {
|
|
370
|
-
if (this.#tabs.length === 0) {
|
|
371
|
-
return lit_html_1.nothing;
|
|
372
|
-
}
|
|
373
|
-
return (0, lit_html_1.html) `
|
|
374
|
-
<div part="tabs" role="tablist">
|
|
375
|
-
${this.#tabs.map((tab, index) => (0, lit_html_1.html) `
|
|
376
|
-
<button
|
|
377
|
-
part="tab"
|
|
378
|
-
role="tab"
|
|
379
|
-
aria-selected=${index === this.activeTab ? 'true' : 'false'}
|
|
380
|
-
aria-controls=${`panel-${tab.id}`}
|
|
381
|
-
tabindex=${index === this.activeTab ? 0 : -1}
|
|
382
|
-
@click=${(e) => this.#handleTabClick(index, tab.id, e)}
|
|
383
|
-
@keydown=${(e) => this.#handleTabKeydown(e, index)}
|
|
384
|
-
>
|
|
385
|
-
${tab.label}
|
|
386
|
-
</button>
|
|
387
|
-
`)}
|
|
165
|
+
<div part="container">
|
|
166
|
+
<slot></slot>
|
|
388
167
|
</div>
|
|
389
168
|
`;
|
|
390
169
|
}
|
|
391
|
-
#renderTabPanels() {
|
|
392
|
-
return (0, lit_html_1.html) `
|
|
393
|
-
${this.#tabs.map((tab, index) => (0, lit_html_1.html) `
|
|
394
|
-
<div
|
|
395
|
-
part="tab-panel"
|
|
396
|
-
role="tabpanel"
|
|
397
|
-
id=${`panel-${tab.id}`}
|
|
398
|
-
aria-labelledby=${`tab-${tab.id}`}
|
|
399
|
-
data-state=${index === this.activeTab ? 'active' : 'hidden'}
|
|
400
|
-
data-index=${index}
|
|
401
|
-
>
|
|
402
|
-
<slot name=${`tab-${tab.id}`}></slot>
|
|
403
|
-
</div>
|
|
404
|
-
`)}
|
|
405
|
-
`;
|
|
406
|
-
}
|
|
407
|
-
#handleTabClick(index, id, event) {
|
|
408
|
-
if (index === this.activeTab) {
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
this.activeTab = index;
|
|
412
|
-
(0, shared_1.dispatchControlEvent)(this, 'tab-change', {
|
|
413
|
-
index,
|
|
414
|
-
id,
|
|
415
|
-
event
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
#handleTabKeydown(event, currentIndex) {
|
|
419
|
-
let newIndex = currentIndex;
|
|
420
|
-
switch (event.key) {
|
|
421
|
-
case 'ArrowLeft':
|
|
422
|
-
event.preventDefault();
|
|
423
|
-
newIndex = currentIndex > 0 ? currentIndex - 1 : this.#tabs.length - 1;
|
|
424
|
-
break;
|
|
425
|
-
case 'ArrowRight':
|
|
426
|
-
event.preventDefault();
|
|
427
|
-
newIndex = currentIndex < this.#tabs.length - 1 ? currentIndex + 1 : 0;
|
|
428
|
-
break;
|
|
429
|
-
case 'Home':
|
|
430
|
-
event.preventDefault();
|
|
431
|
-
newIndex = 0;
|
|
432
|
-
break;
|
|
433
|
-
case 'End':
|
|
434
|
-
event.preventDefault();
|
|
435
|
-
newIndex = this.#tabs.length - 1;
|
|
436
|
-
break;
|
|
437
|
-
default:
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
if (newIndex !== currentIndex) {
|
|
441
|
-
this.activeTab = newIndex;
|
|
442
|
-
// Focus the new tab button
|
|
443
|
-
queueMicrotask(() => {
|
|
444
|
-
const tabButtons = this.shadowRoot?.querySelectorAll('[part="tab"]');
|
|
445
|
-
const newTabButton = tabButtons?.[newIndex];
|
|
446
|
-
newTabButton?.focus();
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
async performTabAnimation(fromIndex, toIndex) {
|
|
451
|
-
if (this.#isAnimating) {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
this.#isAnimating = true;
|
|
455
|
-
const duration = 120;
|
|
456
|
-
const easing = 'cubic-bezier(.25, 0, .5, 1)';
|
|
457
|
-
const content = this.contentElement;
|
|
458
|
-
if (!content) {
|
|
459
|
-
this.#isAnimating = false;
|
|
460
|
-
this.requestRender();
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
// Get the panels by data-index attribute for reliability
|
|
464
|
-
const fromPanel = this.shadowRoot?.querySelector(`[part="tab-panel"][data-index="${fromIndex}"]`);
|
|
465
|
-
const toPanel = this.shadowRoot?.querySelector(`[part="tab-panel"][data-index="${toIndex}"]`);
|
|
466
|
-
if (!fromPanel || !toPanel) {
|
|
467
|
-
this.#isAnimating = false;
|
|
468
|
-
this.requestRender();
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
// Lock the current height
|
|
472
|
-
const startHeight = content.getBoundingClientRect().height;
|
|
473
|
-
content.style.height = `${startHeight}px`;
|
|
474
|
-
// FIX: Ensure the new panel is hidden immediately.
|
|
475
|
-
// Changing activeTab triggers a render which sets data-state="active" (display: block).
|
|
476
|
-
// We must override this with inline styles to prevent the content from showing during the fade-out.
|
|
477
|
-
toPanel.style.display = 'none';
|
|
478
|
-
toPanel.style.opacity = '0';
|
|
479
|
-
// Fade out old content via WAAPI (avoids any "one-frame" flashes)
|
|
480
|
-
try {
|
|
481
|
-
const fadeOut = fromPanel.animate([{ opacity: 1 }, { opacity: 0 }], { duration, easing, fill: 'forwards' });
|
|
482
|
-
await fadeOut.finished;
|
|
483
|
-
fadeOut.cancel();
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
// ignore
|
|
487
|
-
}
|
|
488
|
-
fromPanel.setAttribute('data-state', 'hidden');
|
|
489
|
-
// Prepare and measure new panel while completely invisible
|
|
490
|
-
const previousToState = toPanel.getAttribute('data-state');
|
|
491
|
-
// Reset display to block (overriding our 'none' above) but keep invisible for measuring
|
|
492
|
-
toPanel.style.display = 'block';
|
|
493
|
-
toPanel.style.visibility = 'hidden';
|
|
494
|
-
toPanel.style.opacity = '0';
|
|
495
|
-
// Force layout, then measure
|
|
496
|
-
void toPanel.offsetHeight;
|
|
497
|
-
const endHeight = toPanel.getBoundingClientRect().height;
|
|
498
|
-
// Animate height
|
|
499
|
-
if (startHeight !== endHeight) {
|
|
500
|
-
content.setAttribute('data-animating', 'true');
|
|
501
|
-
void content.offsetHeight;
|
|
502
|
-
content.style.height = `${endHeight}px`;
|
|
503
|
-
await this.#wait(duration);
|
|
504
|
-
}
|
|
505
|
-
// Show panel but keep opacity at 0, then fade in
|
|
506
|
-
toPanel.style.visibility = 'visible';
|
|
507
|
-
toPanel.style.opacity = '0';
|
|
508
|
-
// Ensure the 0-opacity state is committed
|
|
509
|
-
void toPanel.offsetHeight;
|
|
510
|
-
try {
|
|
511
|
-
const fadeIn = toPanel.animate([{ opacity: 0 }, { opacity: 1 }], { duration, easing, fill: 'forwards' });
|
|
512
|
-
await fadeIn.finished;
|
|
513
|
-
fadeIn.cancel();
|
|
514
|
-
}
|
|
515
|
-
catch {
|
|
516
|
-
// ignore
|
|
517
|
-
}
|
|
518
|
-
// Finalize new tab state and cleanup
|
|
519
|
-
toPanel.style.display = '';
|
|
520
|
-
toPanel.style.visibility = '';
|
|
521
|
-
toPanel.style.opacity = '';
|
|
522
|
-
// Restore state attribute (we only want one active)
|
|
523
|
-
if (previousToState !== 'active') {
|
|
524
|
-
toPanel.setAttribute('data-state', 'active');
|
|
525
|
-
}
|
|
526
|
-
content.style.height = '';
|
|
527
|
-
content.removeAttribute('data-animating');
|
|
528
|
-
this.#isAnimating = false;
|
|
529
|
-
this.#detach();
|
|
530
|
-
this.#attach();
|
|
531
|
-
}
|
|
532
|
-
#wait(ms) {
|
|
533
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
534
|
-
}
|
|
535
|
-
#syncTabs() {
|
|
536
|
-
const tabs = [];
|
|
537
|
-
for (const child of Array.from(this.children)) {
|
|
538
|
-
const slot = child.getAttribute('slot');
|
|
539
|
-
if (slot?.startsWith('tab-')) {
|
|
540
|
-
const id = slot.replace('tab-', '');
|
|
541
|
-
const label = child.getAttribute('data-tab-label') || id;
|
|
542
|
-
tabs.push({ id, label });
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
this.#tabs = tabs.slice(0, 3);
|
|
546
|
-
if (this.activeTab >= this.#tabs.length && this.#tabs.length > 0) {
|
|
547
|
-
this.activeTab = 0;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
170
|
@(0, Listen_1.Listen)('input', { target: (host) => host })
|
|
551
171
|
handleInternalInput(event) {
|
|
552
172
|
this.#handleControlEvent(event);
|
|
@@ -559,10 +179,6 @@ class State extends HTMLElement {
|
|
|
559
179
|
handleControlChange(event) {
|
|
560
180
|
this.#handleControlEvent(event);
|
|
561
181
|
}
|
|
562
|
-
@(0, Listen_1.Listen)('slotchange', { selector: 'slot[name="footer"]' })
|
|
563
|
-
onFooterSlotChange() {
|
|
564
|
-
this.updateFooterAttribute();
|
|
565
|
-
}
|
|
566
182
|
#handleControlEvent(event) {
|
|
567
183
|
if ('detail' in event && event.detail?.name) {
|
|
568
184
|
this.#updateState(event.detail.name, event.detail.value, event);
|
|
@@ -580,25 +196,13 @@ class State extends HTMLElement {
|
|
|
580
196
|
this.#updateState(name, value, event);
|
|
581
197
|
}
|
|
582
198
|
#handleSlotChange = () => {
|
|
583
|
-
this.#syncTabs();
|
|
584
199
|
this.#detach();
|
|
585
200
|
this.#attach();
|
|
586
201
|
};
|
|
587
202
|
#attach() {
|
|
588
|
-
const
|
|
589
|
-
if (
|
|
590
|
-
|
|
591
|
-
slots.push(this.entrySlot);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
const activeTab = this.#tabs[this.activeTab];
|
|
596
|
-
if (activeTab) {
|
|
597
|
-
const tabSlot = this.shadowRoot?.querySelector(`slot[name="tab-${activeTab.id}"]`);
|
|
598
|
-
if (tabSlot) {
|
|
599
|
-
slots.push(tabSlot);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
203
|
+
const slot = this.defaultSlot;
|
|
204
|
+
if (!slot) {
|
|
205
|
+
return;
|
|
602
206
|
}
|
|
603
207
|
const findControls = (el) => {
|
|
604
208
|
const controls = [];
|
|
@@ -624,18 +228,16 @@ class State extends HTMLElement {
|
|
|
624
228
|
};
|
|
625
229
|
this.#controls.clear();
|
|
626
230
|
this.#state = {};
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
this.#initialState[name] = value;
|
|
638
|
-
}
|
|
231
|
+
const elements = slot.assignedElements({ flatten: true });
|
|
232
|
+
for (const element of elements) {
|
|
233
|
+
const controls = findControls(element);
|
|
234
|
+
for (const control of controls) {
|
|
235
|
+
const name = getControlName(control);
|
|
236
|
+
if (name) {
|
|
237
|
+
this.#controls.set(name, control);
|
|
238
|
+
const value = readControlValue(control);
|
|
239
|
+
this.#state[name] = value;
|
|
240
|
+
this.#initialState[name] = value;
|
|
639
241
|
}
|
|
640
242
|
}
|
|
641
243
|
}
|
|
@@ -668,14 +270,5 @@ class State extends HTMLElement {
|
|
|
668
270
|
event
|
|
669
271
|
});
|
|
670
272
|
}
|
|
671
|
-
updateFooterAttribute() {
|
|
672
|
-
const footer = this.shadowRoot?.querySelector('[part="footer"]');
|
|
673
|
-
if (!footer) {
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
const footerSlot = this.shadowRoot?.querySelector('slot[name="footer"]');
|
|
677
|
-
const hasFooter = Boolean(footerSlot?.assignedNodes({ flatten: true }).length > 0);
|
|
678
|
-
(0, shared_1.setBooleanAttribute)(footer, 'data-has-content', hasFooter);
|
|
679
|
-
}
|
|
680
273
|
}
|
|
681
274
|
exports.State = State;
|
|
@@ -18,27 +18,45 @@ export interface StateChangeEventDetail {
|
|
|
18
18
|
event: Event;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* State aggregator component - collects and manages state from child controls.
|
|
22
|
+
*
|
|
23
|
+
* This component provides state management without any visual styling.
|
|
24
|
+
* Use it standalone or wrap it with `<ease-panel>` for a styled container.
|
|
25
|
+
*
|
|
26
|
+
* @tag ease-state
|
|
27
|
+
*
|
|
28
|
+
* @slot - Default slot for controls
|
|
29
|
+
*
|
|
30
|
+
* @fires state-change - Fired when any control value changes
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```html
|
|
34
|
+
* <!-- Standalone usage (no panel) -->
|
|
35
|
+
* <ease-state>
|
|
36
|
+
* <ease-field label="Duration">
|
|
37
|
+
* <ease-slider name="duration" value="1" min="0" max="5"></ease-slider>
|
|
38
|
+
* </ease-field>
|
|
39
|
+
* <ease-field label="Loop">
|
|
40
|
+
* <ease-toggle name="loop"></ease-toggle>
|
|
41
|
+
* </ease-field>
|
|
42
|
+
* </ease-state>
|
|
43
|
+
*
|
|
44
|
+
* <!-- With panel wrapper -->
|
|
45
|
+
* <ease-panel>
|
|
46
|
+
* <span slot="headline">Animation Controls</span>
|
|
47
|
+
* <ease-state>
|
|
48
|
+
* <ease-field label="Duration">
|
|
49
|
+
* <ease-slider name="duration" value="1" min="0" max="5"></ease-slider>
|
|
50
|
+
* </ease-field>
|
|
51
|
+
* </ease-state>
|
|
52
|
+
* </ease-panel>
|
|
53
|
+
* ```
|
|
22
54
|
*/
|
|
23
|
-
export interface TabChangeEventDetail {
|
|
24
|
-
/** The index of the active tab */
|
|
25
|
-
index: number;
|
|
26
|
-
/** The tab id */
|
|
27
|
-
id: string;
|
|
28
|
-
/** The original event */
|
|
29
|
-
event: Event;
|
|
30
|
-
}
|
|
31
55
|
export declare class State extends HTMLElement {
|
|
32
56
|
#private;
|
|
33
57
|
requestRender: () => void;
|
|
34
58
|
accessor value: string | null;
|
|
35
|
-
accessor
|
|
36
|
-
/** @internal */
|
|
37
|
-
handleActiveTabChange(previous: number, next: number): void;
|
|
38
|
-
accessor entrySlot: HTMLSlotElement | null;
|
|
39
|
-
accessor outputElement: HTMLOutputElement | null;
|
|
40
|
-
accessor contentElement: HTMLElement | null;
|
|
41
|
-
accessor formElement: HTMLElement | null;
|
|
59
|
+
accessor defaultSlot: HTMLSlotElement | null;
|
|
42
60
|
/**
|
|
43
61
|
* Get the current state object with all control values
|
|
44
62
|
*/
|
|
@@ -67,20 +85,11 @@ export declare class State extends HTMLElement {
|
|
|
67
85
|
* Reset all controls to their initial values
|
|
68
86
|
*/
|
|
69
87
|
reset(): void;
|
|
70
|
-
/**
|
|
71
|
-
* Switch to a specific tab by index
|
|
72
|
-
* @param index - The tab index (0-based)
|
|
73
|
-
*/
|
|
74
|
-
setTab(index: number): void;
|
|
75
88
|
connectedCallback(): void;
|
|
76
89
|
disconnectedCallback(): void;
|
|
77
|
-
afterRender(): void;
|
|
78
90
|
render(): TemplateResult;
|
|
79
|
-
performTabAnimation(fromIndex: number, toIndex: number): Promise<void>;
|
|
80
91
|
handleInternalInput(event: CustomEvent<ControlEventDetail>): void;
|
|
81
92
|
handleInternalChange(event: CustomEvent<ControlEventDetail>): void;
|
|
82
93
|
handleControlChange(event: CustomEvent<ControlEventDetail>): void;
|
|
83
|
-
onFooterSlotChange(): void;
|
|
84
|
-
private updateFooterAttribute;
|
|
85
94
|
}
|
|
86
95
|
export {};
|