@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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit-html';
|
|
2
|
+
/**
|
|
3
|
+
* Event detail for tab change events
|
|
4
|
+
*/
|
|
5
|
+
export interface TabChangeEventDetail {
|
|
6
|
+
/** The index of the active tab */
|
|
7
|
+
index: number;
|
|
8
|
+
/** The tab id */
|
|
9
|
+
id: string;
|
|
10
|
+
/** The original event */
|
|
11
|
+
event: Event;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Panel component - visual container with optional tabs and header actions.
|
|
15
|
+
*
|
|
16
|
+
* Use this component when you want the panel UI without state management,
|
|
17
|
+
* or wrap it around `<ease-state>` for full functionality.
|
|
18
|
+
*
|
|
19
|
+
* @tag ease-panel
|
|
20
|
+
*
|
|
21
|
+
* @slot headline - Panel title text (hidden when tabs are present)
|
|
22
|
+
* @slot actions - Header action buttons, links, or dropdowns
|
|
23
|
+
* @slot - Default slot for main content
|
|
24
|
+
* @slot tab-{id} - Tab panel content (use `data-tab-label` for display name)
|
|
25
|
+
* @slot footer - Footer content below all panels
|
|
26
|
+
*
|
|
27
|
+
* @csspart section - Outer container
|
|
28
|
+
* @csspart header - Header row containing headline/tabs and actions
|
|
29
|
+
* @csspart headline - Title element
|
|
30
|
+
* @csspart tabs - Tab button container
|
|
31
|
+
* @csspart tab - Individual tab button
|
|
32
|
+
* @csspart actions - Actions container
|
|
33
|
+
* @csspart content - Content wrapper (handles height animations)
|
|
34
|
+
* @csspart body - Inner body container
|
|
35
|
+
* @csspart tab-panel - Individual tab panel
|
|
36
|
+
* @csspart footer - Footer container
|
|
37
|
+
*
|
|
38
|
+
* @fires tab-change - Fired when the active tab changes
|
|
39
|
+
*/
|
|
40
|
+
export declare class Panel extends HTMLElement {
|
|
41
|
+
#private;
|
|
42
|
+
requestRender: () => void;
|
|
43
|
+
accessor activeTab: number;
|
|
44
|
+
/** @internal */
|
|
45
|
+
handleActiveTabChange(previous: number, next: number): void;
|
|
46
|
+
accessor contentElement: HTMLElement | null;
|
|
47
|
+
accessor bodyElement: HTMLElement | null;
|
|
48
|
+
/**
|
|
49
|
+
* Get the tab configuration
|
|
50
|
+
*/
|
|
51
|
+
get tabs(): ReadonlyArray<{
|
|
52
|
+
id: string;
|
|
53
|
+
label: string;
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Switch to a specific tab by index
|
|
57
|
+
* @param index - The tab index (0-based)
|
|
58
|
+
*/
|
|
59
|
+
setTab(index: number): void;
|
|
60
|
+
connectedCallback(): void;
|
|
61
|
+
afterRender(): void;
|
|
62
|
+
render(): TemplateResult;
|
|
63
|
+
performTabAnimation(fromIndex: number, toIndex: number): Promise<void>;
|
|
64
|
+
onFooterSlotChange(): void;
|
|
65
|
+
onDefaultSlotChange(): void;
|
|
66
|
+
private updateFooterAttribute;
|
|
67
|
+
}
|
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { html, nothing } from 'lit-html';
|
|
2
|
+
import { setBooleanAttribute } from "../shared.js";
|
|
3
|
+
import { Component } from '../../decorators/Component';
|
|
4
|
+
import { Listen } from '../../decorators/Listen';
|
|
5
|
+
import { Prop } from '../../decorators/Prop';
|
|
6
|
+
import { Query } from '../../decorators/Query';
|
|
7
|
+
/**
|
|
8
|
+
* Panel component - visual container with optional tabs and header actions.
|
|
9
|
+
*
|
|
10
|
+
* Use this component when you want the panel UI without state management,
|
|
11
|
+
* or wrap it around `<ease-state>` for full functionality.
|
|
12
|
+
*
|
|
13
|
+
* @tag ease-panel
|
|
14
|
+
*
|
|
15
|
+
* @slot headline - Panel title text (hidden when tabs are present)
|
|
16
|
+
* @slot actions - Header action buttons, links, or dropdowns
|
|
17
|
+
* @slot - Default slot for main content
|
|
18
|
+
* @slot tab-{id} - Tab panel content (use `data-tab-label` for display name)
|
|
19
|
+
* @slot footer - Footer content below all panels
|
|
20
|
+
*
|
|
21
|
+
* @csspart section - Outer container
|
|
22
|
+
* @csspart header - Header row containing headline/tabs and actions
|
|
23
|
+
* @csspart headline - Title element
|
|
24
|
+
* @csspart tabs - Tab button container
|
|
25
|
+
* @csspart tab - Individual tab button
|
|
26
|
+
* @csspart actions - Actions container
|
|
27
|
+
* @csspart content - Content wrapper (handles height animations)
|
|
28
|
+
* @csspart body - Inner body container
|
|
29
|
+
* @csspart tab-panel - Individual tab panel
|
|
30
|
+
* @csspart footer - Footer container
|
|
31
|
+
*
|
|
32
|
+
* @fires tab-change - Fired when the active tab changes
|
|
33
|
+
*/
|
|
34
|
+
@Component({
|
|
35
|
+
tag: 'ease-panel',
|
|
36
|
+
shadowMode: 'open',
|
|
37
|
+
styles: `
|
|
38
|
+
:host {
|
|
39
|
+
--ease-panel-transition-duration: 120ms;
|
|
40
|
+
--ease-panel-transition-easing: cubic-bezier(.25, 0, .5, 1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[part="section"] {
|
|
44
|
+
display: block;
|
|
45
|
+
width: 100%;
|
|
46
|
+
max-width: var(--ease-panel-max-width, 332px);
|
|
47
|
+
border-radius: var(--ease-panel-radius, 12px);
|
|
48
|
+
border: 1px solid var(--ease-panel-border-color, var(--color-white-6));
|
|
49
|
+
background-clip: padding-box;
|
|
50
|
+
background: var(--ease-panel-background, var(--color-gray-1000));
|
|
51
|
+
box-shadow: var(--ease-panel-shadow, 0 0 40px 0 var(--color-white-2) inset);
|
|
52
|
+
box-sizing: border-box;
|
|
53
|
+
padding: var(--ease-panel-padding, 12px);
|
|
54
|
+
margin: auto;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
[part="header"] {
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: 8px;
|
|
61
|
+
width: 100%;
|
|
62
|
+
margin-bottom: 12px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
[part="header"]:not(:has([part="headline"] slot[name="headline"]::slotted(*))):not(:has([part="tabs"]:not(:empty))):not(:has([part="actions"] slot[name="actions"]::slotted(*))) {
|
|
66
|
+
display: none;
|
|
67
|
+
margin-bottom: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[part="headline"] {
|
|
71
|
+
font-size: var(--ease-panel-title-font-size, 14px);
|
|
72
|
+
font-weight: var(--ease-panel-title-font-weight, 500);
|
|
73
|
+
line-height: var(--ease-panel-title-line-height, 24px);
|
|
74
|
+
font-family: var(--ease-font-family, "Instrument Sans", sans-serif);
|
|
75
|
+
color: var(--ease-panel-title-color, var(--color-blue-100));
|
|
76
|
+
margin: 0 0 0 4px;
|
|
77
|
+
flex-grow: 1;
|
|
78
|
+
text-ellipsis: ellipsis;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
[part="headline"]:has(+ [part="tabs"]:not(:empty)) {
|
|
84
|
+
display: none;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
[part="tabs"] {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: 2px;
|
|
91
|
+
flex-grow: 1;
|
|
92
|
+
margin: 0 0 0 4px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[part="tabs"]:empty {
|
|
96
|
+
display: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
[part="tab"] {
|
|
100
|
+
appearance: none;
|
|
101
|
+
font-size: var(--ease-panel-tab-font-size, 13px);
|
|
102
|
+
font-weight: var(--ease-panel-tab-font-weight, 500);
|
|
103
|
+
line-height: var(--ease-panel-tab-line-height, 24px);
|
|
104
|
+
font-family: var(--ease-font-family, "Instrument Sans", sans-serif);
|
|
105
|
+
color: var(--ease-panel-tab-color, var(--color-gray-600));
|
|
106
|
+
background: transparent;
|
|
107
|
+
border: none;
|
|
108
|
+
padding: 4px 8px;
|
|
109
|
+
margin: 0;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
border-radius: var(--ease-panel-tab-radius, 6px);
|
|
112
|
+
transition: color 0.2s, background-color 0.2s;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
[part="tab"]:hover {
|
|
116
|
+
color: var(--ease-panel-tab-color-hover, var(--color-blue-100));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[part="tab"][aria-selected="true"] {
|
|
120
|
+
color: var(--ease-panel-tab-color-active, var(--color-blue-100));
|
|
121
|
+
background: var(--ease-panel-tab-background-active, var(--color-white-4));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[part="actions"] {
|
|
125
|
+
display: flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
gap: 4px;
|
|
128
|
+
margin-left: auto;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
slot[name="actions"]::slotted(button),
|
|
132
|
+
slot[name="actions"]::slotted(a) {
|
|
133
|
+
--ease-icon-size: var(--ease-panel-action-icon-size, 16px);
|
|
134
|
+
|
|
135
|
+
appearance: none;
|
|
136
|
+
flex: 0 0 24px;
|
|
137
|
+
border: none;
|
|
138
|
+
outline: none;
|
|
139
|
+
background-color: transparent;
|
|
140
|
+
padding: 4px;
|
|
141
|
+
margin: 0;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
color: var(--color-gray-600);
|
|
144
|
+
transition: color 0.2s;
|
|
145
|
+
text-decoration: none;
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
justify-content: center;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
slot[name="actions"]::slotted(button:hover),
|
|
152
|
+
slot[name="actions"]::slotted(button:focus-visible),
|
|
153
|
+
slot[name="actions"]::slotted(a:hover),
|
|
154
|
+
slot[name="actions"]::slotted(a:focus-visible) {
|
|
155
|
+
color: var(--color-blue-100);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
slot[name="actions"]::slotted(ease-dropdown) {
|
|
159
|
+
flex: 0 0 auto;
|
|
160
|
+
width: auto;
|
|
161
|
+
|
|
162
|
+
--ease-icon-size: var(--ease-panel-action-icon-size, 16px);
|
|
163
|
+
--ease-dropdown-trigger-padding: 4px;
|
|
164
|
+
--ease-dropdown-radius: 6px;
|
|
165
|
+
--ease-dropdown-background: transparent;
|
|
166
|
+
--ease-dropdown-background-hover: transparent;
|
|
167
|
+
--ease-dropdown-shadow: none;
|
|
168
|
+
--ease-dropdown-color: var(--color-gray-600);
|
|
169
|
+
--ease-popover-placement: bottom-end;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
slot[name="actions"]::slotted(ease-dropdown:hover),
|
|
173
|
+
slot[name="actions"]::slotted(ease-dropdown:focus-within) {
|
|
174
|
+
--ease-dropdown-color: var(--color-blue-100);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
[part="content"] {
|
|
178
|
+
display: block;
|
|
179
|
+
width: 100%;
|
|
180
|
+
box-sizing: border-box;
|
|
181
|
+
margin: auto;
|
|
182
|
+
overflow: hidden;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[part="content"][data-animating="true"] {
|
|
186
|
+
transition: height var(--ease-panel-transition-duration) var(--ease-panel-transition-easing);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
[part="body"] {
|
|
190
|
+
width: 100%;
|
|
191
|
+
position: relative;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
[part="tab-panel"] {
|
|
195
|
+
width: 100%;
|
|
196
|
+
pointer-events: none;
|
|
197
|
+
display: none;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
[part="tab-panel"][data-state="active"] {
|
|
201
|
+
display: block;
|
|
202
|
+
pointer-events: auto;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
[part="tab-panel"][data-state="hidden"] {
|
|
206
|
+
display: none;
|
|
207
|
+
pointer-events: none;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
[part="footer"] {
|
|
211
|
+
display: flex;
|
|
212
|
+
align-items: center;
|
|
213
|
+
justify-content: space-between;
|
|
214
|
+
width: 100%;
|
|
215
|
+
padding: var(--ease-panel-footer-padding, 12px);
|
|
216
|
+
box-sizing: border-box;
|
|
217
|
+
border-top: 1px solid var(--color-white-4);
|
|
218
|
+
|
|
219
|
+
&:not(:has([data-has-content="true"])) {
|
|
220
|
+
display: none;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
::slotted(:not([slot])),
|
|
225
|
+
::slotted([slot^="tab-"]) {
|
|
226
|
+
display: grid;
|
|
227
|
+
gap: 12px;
|
|
228
|
+
box-sizing: border-box;
|
|
229
|
+
width: 100%;
|
|
230
|
+
}
|
|
231
|
+
`
|
|
232
|
+
})
|
|
233
|
+
export class Panel extends HTMLElement {
|
|
234
|
+
#tabs = [];
|
|
235
|
+
#isAnimating = false;
|
|
236
|
+
@Prop({
|
|
237
|
+
type: Number,
|
|
238
|
+
reflect: true,
|
|
239
|
+
attribute: 'active-tab',
|
|
240
|
+
defaultValue: 0,
|
|
241
|
+
onChange(next, previous) {
|
|
242
|
+
const self = this;
|
|
243
|
+
if (next !== previous && previous !== undefined) {
|
|
244
|
+
self.handleActiveTabChange(previous, next);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
accessor activeTab = 0;
|
|
249
|
+
/** @internal */
|
|
250
|
+
handleActiveTabChange(previous, next) {
|
|
251
|
+
this.performTabAnimation(previous, next);
|
|
252
|
+
}
|
|
253
|
+
@Query('[part="content"]')
|
|
254
|
+
accessor contentElement;
|
|
255
|
+
@Query('[part="body"]')
|
|
256
|
+
accessor bodyElement;
|
|
257
|
+
/**
|
|
258
|
+
* Get the tab configuration
|
|
259
|
+
*/
|
|
260
|
+
get tabs() {
|
|
261
|
+
return this.#tabs;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Switch to a specific tab by index
|
|
265
|
+
* @param index - The tab index (0-based)
|
|
266
|
+
*/
|
|
267
|
+
setTab(index) {
|
|
268
|
+
if (index >= 0 && index < this.#tabs.length && index !== this.activeTab) {
|
|
269
|
+
this.activeTab = index;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
connectedCallback() {
|
|
273
|
+
this.#syncTabs();
|
|
274
|
+
}
|
|
275
|
+
afterRender() {
|
|
276
|
+
this.#syncTabs();
|
|
277
|
+
}
|
|
278
|
+
render() {
|
|
279
|
+
const hasTabs = this.#tabs.length > 0;
|
|
280
|
+
return html `
|
|
281
|
+
<section part="section">
|
|
282
|
+
<div part="header">
|
|
283
|
+
<h3 part="headline"><slot name="headline"></slot></h3>
|
|
284
|
+
${this.#renderTabs()}
|
|
285
|
+
<div part="actions">
|
|
286
|
+
<slot name="actions"></slot>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
<div part="content">
|
|
290
|
+
<div part="body">
|
|
291
|
+
${hasTabs ? this.#renderTabPanels() : html `<slot></slot>`}
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
<div part="footer">
|
|
295
|
+
<slot name="footer"></slot>
|
|
296
|
+
</div>
|
|
297
|
+
</section>
|
|
298
|
+
`;
|
|
299
|
+
}
|
|
300
|
+
#renderTabs() {
|
|
301
|
+
if (this.#tabs.length === 0) {
|
|
302
|
+
return nothing;
|
|
303
|
+
}
|
|
304
|
+
return html `
|
|
305
|
+
<div part="tabs" role="tablist">
|
|
306
|
+
${this.#tabs.map((tab, index) => html `
|
|
307
|
+
<button
|
|
308
|
+
part="tab"
|
|
309
|
+
role="tab"
|
|
310
|
+
aria-selected=${index === this.activeTab ? 'true' : 'false'}
|
|
311
|
+
aria-controls=${`panel-${tab.id}`}
|
|
312
|
+
tabindex=${index === this.activeTab ? 0 : -1}
|
|
313
|
+
@click=${(e) => this.#handleTabClick(index, tab.id, e)}
|
|
314
|
+
@keydown=${(e) => this.#handleTabKeydown(e, index)}
|
|
315
|
+
>
|
|
316
|
+
${tab.label}
|
|
317
|
+
</button>
|
|
318
|
+
`)}
|
|
319
|
+
</div>
|
|
320
|
+
`;
|
|
321
|
+
}
|
|
322
|
+
#renderTabPanels() {
|
|
323
|
+
return html `
|
|
324
|
+
${this.#tabs.map((tab, index) => html `
|
|
325
|
+
<div
|
|
326
|
+
part="tab-panel"
|
|
327
|
+
role="tabpanel"
|
|
328
|
+
id=${`panel-${tab.id}`}
|
|
329
|
+
aria-labelledby=${`tab-${tab.id}`}
|
|
330
|
+
data-state=${index === this.activeTab ? 'active' : 'hidden'}
|
|
331
|
+
data-index=${index}
|
|
332
|
+
>
|
|
333
|
+
<slot name=${`tab-${tab.id}`}></slot>
|
|
334
|
+
</div>
|
|
335
|
+
`)}
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
#handleTabClick(index, id, event) {
|
|
339
|
+
if (index === this.activeTab) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
this.activeTab = index;
|
|
343
|
+
this.dispatchEvent(new CustomEvent('tab-change', {
|
|
344
|
+
detail: { index, id, event },
|
|
345
|
+
bubbles: true,
|
|
346
|
+
composed: true
|
|
347
|
+
}));
|
|
348
|
+
}
|
|
349
|
+
#handleTabKeydown(event, currentIndex) {
|
|
350
|
+
let newIndex = currentIndex;
|
|
351
|
+
switch (event.key) {
|
|
352
|
+
case 'ArrowLeft':
|
|
353
|
+
event.preventDefault();
|
|
354
|
+
newIndex = currentIndex > 0 ? currentIndex - 1 : this.#tabs.length - 1;
|
|
355
|
+
break;
|
|
356
|
+
case 'ArrowRight':
|
|
357
|
+
event.preventDefault();
|
|
358
|
+
newIndex = currentIndex < this.#tabs.length - 1 ? currentIndex + 1 : 0;
|
|
359
|
+
break;
|
|
360
|
+
case 'Home':
|
|
361
|
+
event.preventDefault();
|
|
362
|
+
newIndex = 0;
|
|
363
|
+
break;
|
|
364
|
+
case 'End':
|
|
365
|
+
event.preventDefault();
|
|
366
|
+
newIndex = this.#tabs.length - 1;
|
|
367
|
+
break;
|
|
368
|
+
default:
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (newIndex !== currentIndex) {
|
|
372
|
+
this.activeTab = newIndex;
|
|
373
|
+
// Focus the new tab button
|
|
374
|
+
queueMicrotask(() => {
|
|
375
|
+
const tabButtons = this.shadowRoot?.querySelectorAll('[part="tab"]');
|
|
376
|
+
const newTabButton = tabButtons?.[newIndex];
|
|
377
|
+
newTabButton?.focus();
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
async performTabAnimation(fromIndex, toIndex) {
|
|
382
|
+
if (this.#isAnimating) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
this.#isAnimating = true;
|
|
386
|
+
const duration = 120;
|
|
387
|
+
const easing = 'cubic-bezier(.25, 0, .5, 1)';
|
|
388
|
+
const content = this.contentElement;
|
|
389
|
+
if (!content) {
|
|
390
|
+
this.#isAnimating = false;
|
|
391
|
+
this.requestRender();
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
// Get the panels by data-index attribute for reliability
|
|
395
|
+
const fromPanel = this.shadowRoot?.querySelector(`[part="tab-panel"][data-index="${fromIndex}"]`);
|
|
396
|
+
const toPanel = this.shadowRoot?.querySelector(`[part="tab-panel"][data-index="${toIndex}"]`);
|
|
397
|
+
if (!fromPanel || !toPanel) {
|
|
398
|
+
this.#isAnimating = false;
|
|
399
|
+
this.requestRender();
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
// Lock the current height
|
|
403
|
+
const startHeight = content.getBoundingClientRect().height;
|
|
404
|
+
content.style.height = `${startHeight}px`;
|
|
405
|
+
// FIX: Ensure the new panel is hidden immediately.
|
|
406
|
+
toPanel.style.display = 'none';
|
|
407
|
+
toPanel.style.opacity = '0';
|
|
408
|
+
// Fade out old content via WAAPI
|
|
409
|
+
try {
|
|
410
|
+
const fadeOut = fromPanel.animate([{ opacity: 1 }, { opacity: 0 }], { duration, easing, fill: 'forwards' });
|
|
411
|
+
await fadeOut.finished;
|
|
412
|
+
fadeOut.cancel();
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
// ignore
|
|
416
|
+
}
|
|
417
|
+
fromPanel.setAttribute('data-state', 'hidden');
|
|
418
|
+
// Prepare and measure new panel while completely invisible
|
|
419
|
+
const previousToState = toPanel.getAttribute('data-state');
|
|
420
|
+
toPanel.style.display = 'block';
|
|
421
|
+
toPanel.style.visibility = 'hidden';
|
|
422
|
+
toPanel.style.opacity = '0';
|
|
423
|
+
// Force layout, then measure
|
|
424
|
+
void toPanel.offsetHeight;
|
|
425
|
+
const endHeight = toPanel.getBoundingClientRect().height;
|
|
426
|
+
// Animate height
|
|
427
|
+
if (startHeight !== endHeight) {
|
|
428
|
+
content.setAttribute('data-animating', 'true');
|
|
429
|
+
void content.offsetHeight;
|
|
430
|
+
content.style.height = `${endHeight}px`;
|
|
431
|
+
await this.#wait(duration);
|
|
432
|
+
}
|
|
433
|
+
// Show panel but keep opacity at 0, then fade in
|
|
434
|
+
toPanel.style.visibility = 'visible';
|
|
435
|
+
toPanel.style.opacity = '0';
|
|
436
|
+
void toPanel.offsetHeight;
|
|
437
|
+
try {
|
|
438
|
+
const fadeIn = toPanel.animate([{ opacity: 0 }, { opacity: 1 }], { duration, easing, fill: 'forwards' });
|
|
439
|
+
await fadeIn.finished;
|
|
440
|
+
fadeIn.cancel();
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
// ignore
|
|
444
|
+
}
|
|
445
|
+
// Finalize new tab state and cleanup
|
|
446
|
+
toPanel.style.display = '';
|
|
447
|
+
toPanel.style.visibility = '';
|
|
448
|
+
toPanel.style.opacity = '';
|
|
449
|
+
if (previousToState !== 'active') {
|
|
450
|
+
toPanel.setAttribute('data-state', 'active');
|
|
451
|
+
}
|
|
452
|
+
content.style.height = '';
|
|
453
|
+
content.removeAttribute('data-animating');
|
|
454
|
+
this.#isAnimating = false;
|
|
455
|
+
}
|
|
456
|
+
#wait(ms) {
|
|
457
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
458
|
+
}
|
|
459
|
+
#syncTabs() {
|
|
460
|
+
const tabs = [];
|
|
461
|
+
for (const child of Array.from(this.children)) {
|
|
462
|
+
const slot = child.getAttribute('slot');
|
|
463
|
+
if (slot?.startsWith('tab-')) {
|
|
464
|
+
const id = slot.replace('tab-', '');
|
|
465
|
+
const label = child.getAttribute('data-tab-label') || id;
|
|
466
|
+
tabs.push({ id, label });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
this.#tabs = tabs.slice(0, 3);
|
|
470
|
+
if (this.activeTab >= this.#tabs.length && this.#tabs.length > 0) {
|
|
471
|
+
this.activeTab = 0;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
@Listen('slotchange', { selector: 'slot[name="footer"]' })
|
|
475
|
+
onFooterSlotChange() {
|
|
476
|
+
this.updateFooterAttribute();
|
|
477
|
+
}
|
|
478
|
+
@Listen('slotchange', { selector: 'slot:not([name])' })
|
|
479
|
+
onDefaultSlotChange() {
|
|
480
|
+
this.#syncTabs();
|
|
481
|
+
this.requestRender();
|
|
482
|
+
}
|
|
483
|
+
updateFooterAttribute() {
|
|
484
|
+
const footer = this.shadowRoot?.querySelector('[part="footer"]');
|
|
485
|
+
if (!footer) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const footerSlot = this.shadowRoot?.querySelector('slot[name="footer"]');
|
|
489
|
+
const hasFooter = Boolean(footerSlot?.assignedNodes({ flatten: true }).length > 0);
|
|
490
|
+
setBooleanAttribute(footer, 'data-has-content', hasFooter);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Popover = void 0;
|
|
4
4
|
const lit_html_1 = require("lit-html");
|
|
5
|
-
const Component_1 = require("
|
|
6
|
-
const Prop_1 = require("
|
|
5
|
+
const Component_1 = require("../../decorators/Component");
|
|
6
|
+
const Prop_1 = require("../../decorators/Prop");
|
|
7
7
|
const nextAnchorName = () => `--ease-popover-anchor-${crypto.randomUUID()}`;
|
|
8
8
|
@(0, Component_1.Component)({
|
|
9
9
|
tag: 'ease-popover',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
|
-
import { Component } from '
|
|
3
|
-
import { Prop } from '
|
|
2
|
+
import { Component } from '../../decorators/Component';
|
|
3
|
+
import { Prop } from '../../decorators/Prop';
|
|
4
4
|
const nextAnchorName = () => `--ease-popover-anchor-${crypto.randomUUID()}`;
|
|
5
5
|
@Component({
|
|
6
6
|
tag: 'ease-popover',
|
|
@@ -16,9 +16,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.RadioGroup = void 0;
|
|
18
18
|
const lit_html_1 = require("lit-html");
|
|
19
|
-
const Component_1 = require("
|
|
20
|
-
const Prop_1 = require("
|
|
21
|
-
const Query_1 = require("
|
|
19
|
+
const Component_1 = require("../../decorators/Component");
|
|
20
|
+
const Prop_1 = require("../../decorators/Prop");
|
|
21
|
+
const Query_1 = require("../../decorators/Query");
|
|
22
22
|
require("../button/index.cjs");
|
|
23
23
|
const shared_1 = require("../shared.cjs");
|
|
24
24
|
__exportStar(require("./option.cjs"), exports);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
|
-
import { Component } from '
|
|
3
|
-
import { Prop } from '
|
|
4
|
-
import { Query } from '
|
|
2
|
+
import { Component } from '../../decorators/Component';
|
|
3
|
+
import { Prop } from '../../decorators/Prop';
|
|
4
|
+
import { Query } from '../../decorators/Query';
|
|
5
5
|
import "../button/index.js";
|
|
6
6
|
import { dispatchControlEvent } from "../shared.js";
|
|
7
7
|
export * from "./option.js";
|
|
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RadioInput = 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
|
@(0, Component_1.Component)({
|
|
11
11
|
tag: 'ease-radio-input',
|
|
12
12
|
shadowMode: 'open',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
2
|
import { dispatchControlEvent, setBooleanAttribute } from "../shared.js";
|
|
3
|
-
import { Component } from '
|
|
4
|
-
import { Listen } from '
|
|
5
|
-
import { Prop } from '
|
|
6
|
-
import { Query } from '
|
|
3
|
+
import { Component } from '../../decorators/Component';
|
|
4
|
+
import { Listen } from '../../decorators/Listen';
|
|
5
|
+
import { Prop } from '../../decorators/Prop';
|
|
6
|
+
import { Query } from '../../decorators/Query';
|
|
7
7
|
@Component({
|
|
8
8
|
tag: 'ease-radio-input',
|
|
9
9
|
shadowMode: 'open',
|
|
@@ -4,10 +4,10 @@ exports.Slider = void 0;
|
|
|
4
4
|
require("../input/index.cjs");
|
|
5
5
|
const lit_html_1 = require("lit-html");
|
|
6
6
|
const shared_1 = require("../shared.cjs");
|
|
7
|
-
const Component_1 = require("
|
|
8
|
-
const Listen_1 = require("
|
|
9
|
-
const Prop_1 = require("
|
|
10
|
-
const Query_1 = require("
|
|
7
|
+
const Component_1 = require("../../decorators/Component");
|
|
8
|
+
const Listen_1 = require("../../decorators/Listen");
|
|
9
|
+
const Prop_1 = require("../../decorators/Prop");
|
|
10
|
+
const Query_1 = require("../../decorators/Query");
|
|
11
11
|
@(0, Component_1.Component)({
|
|
12
12
|
tag: 'ease-slider',
|
|
13
13
|
styles: `
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "../input/index.js";
|
|
2
2
|
import { html } from 'lit-html';
|
|
3
3
|
import { coerceNumber, dispatchControlEvent, setBooleanAttribute } from "../shared.js";
|
|
4
|
-
import { Component } from '
|
|
5
|
-
import { Listen } from '
|
|
6
|
-
import { Prop } from '
|
|
7
|
-
import { Query } from '
|
|
4
|
+
import { Component } from '../../decorators/Component';
|
|
5
|
+
import { Listen } from '../../decorators/Listen';
|
|
6
|
+
import { Prop } from '../../decorators/Prop';
|
|
7
|
+
import { Query } from '../../decorators/Query';
|
|
8
8
|
@Component({
|
|
9
9
|
tag: 'ease-slider',
|
|
10
10
|
styles: `
|