@easemate/web-kit 0.1.5 → 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/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/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/state/index.cjs +57 -464
- package/build/elements/state/index.d.cts +34 -25
- package/build/elements/state/index.d.ts +34 -25
- package/build/elements/state/index.js +59 -466
- 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 +15 -1
package/README.md
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
<h1>@easemate/web-kit</h1>
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<img src="https://easemate.app/easemate-web-kit-header.png" alt="@easemate/web-kit" />
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<br />
|
|
2
8
|
|
|
3
9
|
A modern, framework-agnostic UI kit of web components for building animation control panels.
|
|
4
10
|
|
|
11
|
+
<div>
|
|
12
|
+
|
|
13
|
+
[](https://www.npmjs.com/package/@easemate/web-kit)
|
|
14
|
+

|
|
15
|
+
[](https://www.npmjs.com/package/@easemate/web-kit)
|
|
16
|
+
[](https://github.com/easemate/web-kit/blob/main/LICENSE)
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
5
20
|
## Table of Contents
|
|
6
21
|
|
|
7
22
|
- [Features](#features)
|
|
@@ -10,6 +25,11 @@ A modern, framework-agnostic UI kit of web components for building animation con
|
|
|
10
25
|
- [Basic Usage](#basic-usage)
|
|
11
26
|
- [Selective Loading](#selective-loading)
|
|
12
27
|
- [Theme Switching](#theme-switching)
|
|
28
|
+
- [React & Next.js](#react--nextjs)
|
|
29
|
+
- [Basic Setup](#basic-setup)
|
|
30
|
+
- [Next.js App Router](#nextjs-app-router)
|
|
31
|
+
- [useEaseState Hook](#useeasestate-hook)
|
|
32
|
+
- [WebKit Provider](#webkit-provider)
|
|
13
33
|
- [Components](#components)
|
|
14
34
|
- [Controls](#controls)
|
|
15
35
|
- [Layout & Display](#layout--display)
|
|
@@ -17,13 +37,10 @@ A modern, framework-agnostic UI kit of web components for building animation con
|
|
|
17
37
|
- [Icons](#icons)
|
|
18
38
|
- [Usage Examples](#usage-examples)
|
|
19
39
|
- [Basic Controls](#basic-controls)
|
|
20
|
-
- [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- [Tabs with Actions](#tabs-with-actions)
|
|
24
|
-
- [Footer](#footer)
|
|
40
|
+
- [Panel Component](#panel-component)
|
|
41
|
+
- [State Component](#state-component)
|
|
42
|
+
- [Combined Panel + State](#combined-panel--state)
|
|
25
43
|
- [JavaScript Integration](#javascript-integration)
|
|
26
|
-
- [Tab Control](#tab-control)
|
|
27
44
|
- [Event Handling](#event-handling)
|
|
28
45
|
- [Configuration](#configuration)
|
|
29
46
|
- [initWebKit Options](#initwebkit-options)
|
|
@@ -38,7 +55,8 @@ A modern, framework-agnostic UI kit of web components for building animation con
|
|
|
38
55
|
- [API Reference](#api-reference)
|
|
39
56
|
- [Controller API](#controller-api)
|
|
40
57
|
- [Package Exports](#package-exports)
|
|
41
|
-
- [
|
|
58
|
+
- [Panel API](#panel-api)
|
|
59
|
+
- [State API](#state-api)
|
|
42
60
|
- [Accessibility](#accessibility)
|
|
43
61
|
- [SSR Support](#ssr-support)
|
|
44
62
|
- [License](#license)
|
|
@@ -47,15 +65,17 @@ A modern, framework-agnostic UI kit of web components for building animation con
|
|
|
47
65
|
|
|
48
66
|
## Features
|
|
49
67
|
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
68
|
+
- **Rich Component Library** - Sliders, toggles, color pickers, dropdowns, curve editors, and more
|
|
69
|
+
- **Dark Theme by Default** - Beautiful dark UI with OKLAB color palette
|
|
70
|
+
- **Framework Agnostic** - Works with vanilla JS, React, Vue, Svelte, or any framework
|
|
71
|
+
- **React/Next.js Ready** - First-class React integration with hooks and SSR support
|
|
72
|
+
- **Tree-Shakeable** - Import only what you need
|
|
73
|
+
- **TypeScript First** - Full type definitions included
|
|
74
|
+
- **Accessible** - ARIA attributes and keyboard navigation
|
|
75
|
+
- **Customizable** - CSS custom properties and `::part` selectors for styling
|
|
76
|
+
- **State Aggregation** - Control panel state management with `<ease-state>`
|
|
77
|
+
- **Flexible Layout** - Separate `<ease-panel>` and `<ease-state>` for maximum flexibility
|
|
78
|
+
- **No CSS Import Required** - `initWebKit()` handles everything programmatically
|
|
59
79
|
|
|
60
80
|
---
|
|
61
81
|
|
|
@@ -153,6 +173,156 @@ kit.theme?.set('light');
|
|
|
153
173
|
|
|
154
174
|
---
|
|
155
175
|
|
|
176
|
+
## React & Next.js
|
|
177
|
+
|
|
178
|
+
The library provides first-class React integration via `@easemate/web-kit/react`.
|
|
179
|
+
|
|
180
|
+
### Basic Setup
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
// app/providers.tsx
|
|
184
|
+
'use client';
|
|
185
|
+
|
|
186
|
+
import { useEffect, useState, useRef } from 'react';
|
|
187
|
+
import { initWebKit, type WebKitController } from '@easemate/web-kit';
|
|
188
|
+
|
|
189
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
190
|
+
const [ready, setReady] = useState(false);
|
|
191
|
+
const controllerRef = useRef<WebKitController | null>(null);
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
const controller = initWebKit({
|
|
195
|
+
theme: 'default',
|
|
196
|
+
styles: 'main',
|
|
197
|
+
fonts: 'default'
|
|
198
|
+
});
|
|
199
|
+
controllerRef.current = controller;
|
|
200
|
+
controller.ready.then(() => setReady(true));
|
|
201
|
+
|
|
202
|
+
return () => controller.dispose();
|
|
203
|
+
}, []);
|
|
204
|
+
|
|
205
|
+
return <>{children}</>;
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Next.js App Router
|
|
210
|
+
|
|
211
|
+
For Next.js 13+ with App Router, create a client component wrapper:
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
// app/layout.tsx
|
|
215
|
+
import { Providers } from './providers';
|
|
216
|
+
|
|
217
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
218
|
+
return (
|
|
219
|
+
<html lang="en">
|
|
220
|
+
<body>
|
|
221
|
+
<Providers>{children}</Providers>
|
|
222
|
+
</body>
|
|
223
|
+
</html>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### useEaseState Hook
|
|
229
|
+
|
|
230
|
+
The `useEaseState` hook provides reactive state management for controls:
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
'use client';
|
|
234
|
+
|
|
235
|
+
import { useState, useCallback, useRef } from 'react';
|
|
236
|
+
import { useEaseState } from '@easemate/web-kit/react';
|
|
237
|
+
|
|
238
|
+
interface AnimationState {
|
|
239
|
+
duration: number;
|
|
240
|
+
easing: string;
|
|
241
|
+
loop: boolean;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function AnimationControls() {
|
|
245
|
+
const { stateRef, panelRef, state, set, reset } = useEaseState<AnimationState>(
|
|
246
|
+
{
|
|
247
|
+
onChange: ({ name, value }) => {
|
|
248
|
+
console.log(`${name} changed to ${value}`);
|
|
249
|
+
},
|
|
250
|
+
onTabChange: ({ index }) => {
|
|
251
|
+
console.log(`Switched to tab ${index}`);
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
{ useState, useCallback, useRef }
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<ease-panel ref={panelRef}>
|
|
259
|
+
<span slot="headline">Animation</span>
|
|
260
|
+
<ease-state ref={stateRef}>
|
|
261
|
+
<ease-field label="Duration">
|
|
262
|
+
<ease-slider name="duration" value="1" min="0" max="5" step="0.1" />
|
|
263
|
+
</ease-field>
|
|
264
|
+
<ease-field label="Easing">
|
|
265
|
+
<ease-dropdown name="easing" value="ease-out">
|
|
266
|
+
<button slot="content" value="linear">Linear</button>
|
|
267
|
+
<button slot="content" value="ease-out">Ease Out</button>
|
|
268
|
+
</ease-dropdown>
|
|
269
|
+
</ease-field>
|
|
270
|
+
<ease-field label="Loop">
|
|
271
|
+
<ease-toggle name="loop" />
|
|
272
|
+
</ease-field>
|
|
273
|
+
</ease-state>
|
|
274
|
+
<div slot="footer">
|
|
275
|
+
<ease-button onClick={() => reset()}>Reset</ease-button>
|
|
276
|
+
</div>
|
|
277
|
+
</ease-panel>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### WebKit Provider
|
|
283
|
+
|
|
284
|
+
For more complex apps, use `createWebKitProvider` to create a context:
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
// providers.tsx
|
|
288
|
+
'use client';
|
|
289
|
+
|
|
290
|
+
import * as React from 'react';
|
|
291
|
+
import { createWebKitProvider } from '@easemate/web-kit/react';
|
|
292
|
+
|
|
293
|
+
const { WebKitProvider, useWebKitContext } = createWebKitProvider(React);
|
|
294
|
+
|
|
295
|
+
export { WebKitProvider, useWebKitContext };
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
// layout.tsx
|
|
300
|
+
import { WebKitProvider } from './providers';
|
|
301
|
+
|
|
302
|
+
export default function Layout({ children }: { children: React.ReactNode }) {
|
|
303
|
+
return (
|
|
304
|
+
<WebKitProvider options={{ theme: 'default', styles: 'main', fonts: 'default' }}>
|
|
305
|
+
{children}
|
|
306
|
+
</WebKitProvider>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
// component.tsx
|
|
313
|
+
import { useWebKitContext } from './providers';
|
|
314
|
+
|
|
315
|
+
function MyComponent() {
|
|
316
|
+
const { ready, theme } = useWebKitContext();
|
|
317
|
+
|
|
318
|
+
if (!ready) return <div>Loading...</div>;
|
|
319
|
+
|
|
320
|
+
return <ease-slider name="value" value="0.5" />;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
156
326
|
## Components
|
|
157
327
|
|
|
158
328
|
### Controls
|
|
@@ -175,7 +345,8 @@ kit.theme?.set('light');
|
|
|
175
345
|
|
|
176
346
|
| Component | Tag | Description |
|
|
177
347
|
|-----------|-----|-------------|
|
|
178
|
-
|
|
|
348
|
+
| Panel | `<ease-panel>` | Visual container with tabs, header, and footer |
|
|
349
|
+
| State | `<ease-state>` | State aggregator for controls (no visual styling) |
|
|
179
350
|
| Field | `<ease-field>` | Label + control wrapper |
|
|
180
351
|
| Button | `<ease-button>` | Action button |
|
|
181
352
|
| Tooltip | `<ease-tooltip>` | Tooltip wrapper |
|
|
@@ -215,163 +386,143 @@ All icon components follow the pattern `<ease-icon-*>`:
|
|
|
215
386
|
<ease-number-input name="count" value="42" min="0" max="100"></ease-number-input>
|
|
216
387
|
```
|
|
217
388
|
|
|
218
|
-
###
|
|
389
|
+
### Panel Component
|
|
219
390
|
|
|
220
|
-
|
|
391
|
+
The `<ease-panel>` component provides the visual container with optional tabs, header actions, and footer. It does NOT manage state - use `<ease-state>` for that.
|
|
221
392
|
|
|
222
393
|
```html
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
</ease-field>
|
|
229
|
-
<ease-field label="Easing">
|
|
230
|
-
<ease-dropdown name="easing" value="ease-out">
|
|
231
|
-
<button slot="content" value="linear">Linear</button>
|
|
232
|
-
<button slot="content" value="ease-in">Ease In</button>
|
|
233
|
-
<button slot="content" value="ease-out">Ease Out</button>
|
|
234
|
-
<button slot="content" value="ease-in-out">Ease In-Out</button>
|
|
235
|
-
</ease-dropdown>
|
|
236
|
-
</ease-field>
|
|
237
|
-
<ease-field label="Loop">
|
|
238
|
-
<ease-toggle name="loop"></ease-toggle>
|
|
239
|
-
</ease-field>
|
|
394
|
+
<!-- Simple panel with headline -->
|
|
395
|
+
<ease-panel>
|
|
396
|
+
<span slot="headline">Settings</span>
|
|
397
|
+
<div>
|
|
398
|
+
<!-- Your content here -->
|
|
240
399
|
</div>
|
|
241
|
-
</ease-
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
#### Header Actions
|
|
400
|
+
</ease-panel>
|
|
245
401
|
|
|
246
|
-
|
|
402
|
+
<!-- Panel with tabs -->
|
|
403
|
+
<ease-panel active-tab="0">
|
|
404
|
+
<div slot="tab-general" data-tab-label="General">
|
|
405
|
+
<!-- General settings -->
|
|
406
|
+
</div>
|
|
407
|
+
<div slot="tab-advanced" data-tab-label="Advanced">
|
|
408
|
+
<!-- Advanced settings -->
|
|
409
|
+
</div>
|
|
410
|
+
</ease-panel>
|
|
247
411
|
|
|
248
|
-
|
|
249
|
-
<ease-
|
|
250
|
-
<span slot="headline">
|
|
251
|
-
|
|
252
|
-
<!-- Action buttons -->
|
|
412
|
+
<!-- Panel with header actions -->
|
|
413
|
+
<ease-panel>
|
|
414
|
+
<span slot="headline">Controls</span>
|
|
253
415
|
<button slot="actions" title="Settings">
|
|
254
416
|
<ease-icon-settings></ease-icon-settings>
|
|
255
417
|
</button>
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
</a>
|
|
259
|
-
|
|
260
|
-
<!-- Dropdown menu in actions -->
|
|
261
|
-
<ease-dropdown slot="actions">
|
|
262
|
-
<ease-icon-dots slot="trigger"></ease-icon-dots>
|
|
263
|
-
<button slot="content" value="export">Export</button>
|
|
264
|
-
<button slot="content" value="import">Import</button>
|
|
265
|
-
<button slot="content" value="reset">Reset</button>
|
|
266
|
-
</ease-dropdown>
|
|
267
|
-
|
|
268
|
-
<div slot="entry">
|
|
269
|
-
<!-- controls -->
|
|
418
|
+
<div>
|
|
419
|
+
<!-- Content -->
|
|
270
420
|
</div>
|
|
271
|
-
|
|
421
|
+
<div slot="footer">
|
|
422
|
+
<ease-button>Save</ease-button>
|
|
423
|
+
</div>
|
|
424
|
+
</ease-panel>
|
|
272
425
|
```
|
|
273
426
|
|
|
274
|
-
|
|
275
|
-
- `<button>` — Action button with icon
|
|
276
|
-
- `<a>` — Link with icon
|
|
277
|
-
- `<ease-dropdown>` — Dropdown menu (auto-positioned to bottom-end)
|
|
427
|
+
### State Component
|
|
278
428
|
|
|
279
|
-
|
|
429
|
+
The `<ease-state>` component aggregates state from child controls. It can be used standalone (no panel styling) or inside a panel.
|
|
280
430
|
|
|
281
|
-
|
|
431
|
+
```html
|
|
432
|
+
<!-- Standalone state (no panel) - useful for embedding in your own UI -->
|
|
433
|
+
<ease-state>
|
|
434
|
+
<ease-field label="Duration">
|
|
435
|
+
<ease-slider name="duration" value="1" min="0" max="5" step="0.1"></ease-slider>
|
|
436
|
+
</ease-field>
|
|
437
|
+
<ease-field label="Easing">
|
|
438
|
+
<ease-dropdown name="easing" value="ease-out">
|
|
439
|
+
<button slot="content" value="linear">Linear</button>
|
|
440
|
+
<button slot="content" value="ease-out">Ease Out</button>
|
|
441
|
+
</ease-dropdown>
|
|
442
|
+
</ease-field>
|
|
443
|
+
<ease-field label="Loop">
|
|
444
|
+
<ease-toggle name="loop"></ease-toggle>
|
|
445
|
+
</ease-field>
|
|
446
|
+
</ease-state>
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Combined Panel + State
|
|
450
|
+
|
|
451
|
+
For a complete control panel experience, combine `<ease-panel>` with `<ease-state>`:
|
|
282
452
|
|
|
283
453
|
```html
|
|
284
|
-
<ease-
|
|
285
|
-
|
|
286
|
-
<!-- Tab label comes from data-tab-label attribute -->
|
|
287
|
-
|
|
288
|
-
<div slot="tab-transform" data-tab-label="Transform">
|
|
289
|
-
<ease-field label="X">
|
|
290
|
-
<ease-number-input name="x" value="0"></ease-number-input>
|
|
291
|
-
</ease-field>
|
|
292
|
-
<ease-field label="Y">
|
|
293
|
-
<ease-number-input name="y" value="0"></ease-number-input>
|
|
294
|
-
</ease-field>
|
|
295
|
-
<ease-field label="Rotation">
|
|
296
|
-
<ease-slider name="rotation" value="0" min="0" max="360"></ease-slider>
|
|
297
|
-
</ease-field>
|
|
298
|
-
</div>
|
|
454
|
+
<ease-panel>
|
|
455
|
+
<span slot="headline">Animation Controls</span>
|
|
299
456
|
|
|
300
|
-
<
|
|
301
|
-
<ease-
|
|
302
|
-
|
|
303
|
-
</ease-field>
|
|
304
|
-
<ease-field label="Color">
|
|
305
|
-
<ease-color-input name="color" value="#3b82f6"></ease-color-input>
|
|
306
|
-
</ease-field>
|
|
307
|
-
</div>
|
|
457
|
+
<button slot="actions" title="Reset">
|
|
458
|
+
<ease-icon-minus></ease-icon-minus>
|
|
459
|
+
</button>
|
|
308
460
|
|
|
309
|
-
<
|
|
461
|
+
<ease-state>
|
|
310
462
|
<ease-field label="Duration">
|
|
311
463
|
<ease-slider name="duration" value="1" min="0" max="5" step="0.1"></ease-slider>
|
|
312
464
|
</ease-field>
|
|
313
|
-
<ease-field label="
|
|
314
|
-
<ease-
|
|
465
|
+
<ease-field label="Easing">
|
|
466
|
+
<ease-dropdown name="easing" value="ease-out">
|
|
467
|
+
<button slot="content" value="linear">Linear</button>
|
|
468
|
+
<button slot="content" value="ease-in">Ease In</button>
|
|
469
|
+
<button slot="content" value="ease-out">Ease Out</button>
|
|
470
|
+
<button slot="content" value="ease-in-out">Ease In-Out</button>
|
|
471
|
+
</ease-dropdown>
|
|
315
472
|
</ease-field>
|
|
473
|
+
<ease-field label="Loop">
|
|
474
|
+
<ease-toggle name="loop"></ease-toggle>
|
|
475
|
+
</ease-field>
|
|
476
|
+
</ease-state>
|
|
477
|
+
|
|
478
|
+
<div slot="footer">
|
|
479
|
+
<ease-button>Apply</ease-button>
|
|
480
|
+
<ease-button variant="secondary">Cancel</ease-button>
|
|
316
481
|
</div>
|
|
317
|
-
</ease-
|
|
482
|
+
</ease-panel>
|
|
318
483
|
```
|
|
319
484
|
|
|
320
|
-
|
|
485
|
+
#### Panel with Tabs + State
|
|
321
486
|
|
|
322
|
-
|
|
323
|
-
|-----------|-------------|
|
|
324
|
-
| `slot="tab-{id}"` | Assigns content to a tab. The `id` is used internally and for events. |
|
|
325
|
-
| `data-tab-label` | Display label for the tab button. Falls back to `id` if not provided. |
|
|
326
|
-
| `active-tab` | (on `<ease-state>`) Zero-based index of the initially active tab. |
|
|
327
|
-
|
|
328
|
-
**Tab Behavior:**
|
|
329
|
-
- Tabs are detected automatically from slotted elements with `slot="tab-*"` pattern
|
|
330
|
-
- Maximum of 3 tabs supported
|
|
331
|
-
- Switching tabs triggers a smooth crossfade animation with height transition
|
|
332
|
-
- Keyboard navigation: Arrow keys, Home, End
|
|
333
|
-
- State is tracked per-tab (only active tab's controls are in the state object)
|
|
334
|
-
|
|
335
|
-
#### Tabs with Actions
|
|
336
|
-
|
|
337
|
-
Combine tabs and header actions:
|
|
487
|
+
When using tabs with state, place the `<ease-state>` inside each tab:
|
|
338
488
|
|
|
339
489
|
```html
|
|
340
|
-
<ease-
|
|
341
|
-
<!-- Actions appear to the right of tabs -->
|
|
490
|
+
<ease-panel active-tab="0">
|
|
342
491
|
<button slot="actions" title="Reset">
|
|
343
492
|
<ease-icon-minus></ease-icon-minus>
|
|
344
493
|
</button>
|
|
345
494
|
|
|
346
|
-
<div slot="tab-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
```html
|
|
360
|
-
<ease-state>
|
|
361
|
-
<span slot="headline">Controls</span>
|
|
362
|
-
<div slot="entry">
|
|
363
|
-
<!-- controls -->
|
|
495
|
+
<div slot="tab-transform" data-tab-label="Transform">
|
|
496
|
+
<ease-state>
|
|
497
|
+
<ease-field label="X">
|
|
498
|
+
<ease-number-input name="x" value="0"></ease-number-input>
|
|
499
|
+
</ease-field>
|
|
500
|
+
<ease-field label="Y">
|
|
501
|
+
<ease-number-input name="y" value="0"></ease-number-input>
|
|
502
|
+
</ease-field>
|
|
503
|
+
<ease-field label="Rotation">
|
|
504
|
+
<ease-slider name="rotation" value="0" min="0" max="360"></ease-slider>
|
|
505
|
+
</ease-field>
|
|
506
|
+
</ease-state>
|
|
364
507
|
</div>
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
<ease-
|
|
508
|
+
|
|
509
|
+
<div slot="tab-style" data-tab-label="Style">
|
|
510
|
+
<ease-state>
|
|
511
|
+
<ease-field label="Opacity">
|
|
512
|
+
<ease-slider name="opacity" value="1" min="0" max="1" step="0.01"></ease-slider>
|
|
513
|
+
</ease-field>
|
|
514
|
+
<ease-field label="Color">
|
|
515
|
+
<ease-color-input name="color" value="#3b82f6"></ease-color-input>
|
|
516
|
+
</ease-field>
|
|
517
|
+
</ease-state>
|
|
368
518
|
</div>
|
|
369
|
-
</ease-
|
|
519
|
+
</ease-panel>
|
|
370
520
|
```
|
|
371
521
|
|
|
372
522
|
### JavaScript Integration
|
|
373
523
|
|
|
374
524
|
```typescript
|
|
525
|
+
// Working with ease-state
|
|
375
526
|
const state = document.querySelector('ease-state');
|
|
376
527
|
|
|
377
528
|
// Get current state
|
|
@@ -400,19 +551,18 @@ state.reset();
|
|
|
400
551
|
sub.unsubscribe();
|
|
401
552
|
```
|
|
402
553
|
|
|
403
|
-
#### Tab Control
|
|
404
|
-
|
|
405
554
|
```typescript
|
|
406
|
-
|
|
555
|
+
// Working with ease-panel
|
|
556
|
+
const panel = document.querySelector('ease-panel');
|
|
407
557
|
|
|
408
558
|
// Get current active tab index
|
|
409
|
-
console.log(
|
|
559
|
+
console.log(panel.activeTab); // 0
|
|
410
560
|
|
|
411
561
|
// Switch to a specific tab programmatically
|
|
412
|
-
|
|
562
|
+
panel.setTab(1); // Switch to second tab (0-indexed)
|
|
413
563
|
|
|
414
564
|
// Or set directly via property
|
|
415
|
-
|
|
565
|
+
panel.activeTab = 2;
|
|
416
566
|
```
|
|
417
567
|
|
|
418
568
|
### Logo Loader
|
|
@@ -497,8 +647,8 @@ state.addEventListener('state-change', (e: CustomEvent) => {
|
|
|
497
647
|
console.log('Full state:', state);
|
|
498
648
|
});
|
|
499
649
|
|
|
500
|
-
//
|
|
501
|
-
|
|
650
|
+
// Panel tab change event
|
|
651
|
+
panel.addEventListener('tab-change', (e: CustomEvent) => {
|
|
502
652
|
const { index, id, event } = e.detail;
|
|
503
653
|
console.log(`Switched to tab ${id} (index: ${index})`);
|
|
504
654
|
});
|
|
@@ -506,11 +656,11 @@ state.addEventListener('tab-change', (e: CustomEvent) => {
|
|
|
506
656
|
|
|
507
657
|
#### Event Types
|
|
508
658
|
|
|
509
|
-
| Event | Detail | Description |
|
|
510
|
-
|
|
511
|
-
| `control-change` | `{ name, value, event }` | Fired
|
|
512
|
-
| `state-change` | `{ name, value, state, event }` | Fired
|
|
513
|
-
| `tab-change` | `{ index, id, event }` | Fired
|
|
659
|
+
| Event | Component | Detail | Description |
|
|
660
|
+
|-------|-----------|--------|-------------|
|
|
661
|
+
| `control-change` | Controls | `{ name, value, event }` | Fired when value changes |
|
|
662
|
+
| `state-change` | `<ease-state>` | `{ name, value, state, event }` | Fired when any control changes |
|
|
663
|
+
| `tab-change` | `<ease-panel>` | `{ index, id, event }` | Fired when active tab changes |
|
|
514
664
|
|
|
515
665
|
---
|
|
516
666
|
|
|
@@ -687,7 +837,7 @@ setThemeName('light', { colorScheme: 'light' });
|
|
|
687
837
|
| Panel Tabs | `--ease-panel-tab-font-size`, `--ease-panel-tab-font-weight`, `--ease-panel-tab-line-height`, `--ease-panel-tab-color`, `--ease-panel-tab-color-hover`, `--ease-panel-tab-color-active`, `--ease-panel-tab-background-active`, `--ease-panel-tab-radius` |
|
|
688
838
|
| Panel Actions | `--ease-panel-action-icon-size` |
|
|
689
839
|
| Panel Footer | `--ease-panel-footer-padding` |
|
|
690
|
-
|
|
|
840
|
+
| Panel Transitions | `--ease-panel-transition-duration`, `--ease-panel-transition-easing` |
|
|
691
841
|
| Field | `--ease-field-label-width`, `--ease-field-column-gap`, `--ease-field-row-gap` |
|
|
692
842
|
| Controls | Each control exposes `--ease-<component>-*` tokens |
|
|
693
843
|
|
|
@@ -715,6 +865,7 @@ interface WebKitController {
|
|
|
715
865
|
| Export | Description |
|
|
716
866
|
|--------|-------------|
|
|
717
867
|
| `@easemate/web-kit` | Main entry (initWebKit + theme + types) |
|
|
868
|
+
| `@easemate/web-kit/react` | React hooks and utilities |
|
|
718
869
|
| `@easemate/web-kit/register` | Side-effect registration (all components) |
|
|
719
870
|
| `@easemate/web-kit/elements` | UI components only |
|
|
720
871
|
| `@easemate/web-kit/decorators` | Component decorators |
|
|
@@ -722,27 +873,20 @@ interface WebKitController {
|
|
|
722
873
|
| `@easemate/web-kit/utils` | Utility functions |
|
|
723
874
|
| `@easemate/web-kit/styles/*` | CSS assets (optional) |
|
|
724
875
|
|
|
725
|
-
###
|
|
876
|
+
### Panel API
|
|
726
877
|
|
|
727
|
-
The `<ease-
|
|
878
|
+
The `<ease-panel>` component provides the visual container.
|
|
728
879
|
|
|
729
880
|
#### Properties
|
|
730
881
|
|
|
731
882
|
| Property | Type | Default | Description |
|
|
732
883
|
|----------|------|---------|-------------|
|
|
733
|
-
| `value` | `string \| null` | `null` | Legacy: reflects the last changed control's value |
|
|
734
884
|
| `activeTab` | `number` | `0` | Zero-based index of the active tab |
|
|
735
|
-
| `state` | `Record<string, unknown>` | `{}` | Read-only object containing all control values |
|
|
736
885
|
|
|
737
886
|
#### Methods
|
|
738
887
|
|
|
739
888
|
| Method | Signature | Description |
|
|
740
889
|
|--------|-----------|-------------|
|
|
741
|
-
| `get` | `(name: string) => unknown` | Get a specific control value by name |
|
|
742
|
-
| `set` | `(name: string, value: unknown) => void` | Set a control value programmatically |
|
|
743
|
-
| `subscribe` | `(callback: (value, name) => void) => { unsubscribe }` | Subscribe to all state changes |
|
|
744
|
-
| `subscribe` | `(name: string, callback: (value, name) => void) => { unsubscribe }` | Subscribe to a specific control |
|
|
745
|
-
| `reset` | `() => void` | Reset all controls to their initial values |
|
|
746
890
|
| `setTab` | `(index: number) => void` | Switch to a specific tab by index |
|
|
747
891
|
|
|
748
892
|
#### Slots
|
|
@@ -751,7 +895,7 @@ The `<ease-state>` component provides a complete API for state management.
|
|
|
751
895
|
|------|-------------|
|
|
752
896
|
| `headline` | Panel title text (hidden when tabs are present) |
|
|
753
897
|
| `actions` | Header action buttons, links, or dropdowns |
|
|
754
|
-
|
|
|
898
|
+
| (default) | Main content area (used when no tabs) |
|
|
755
899
|
| `tab-{id}` | Tab panel content (use `data-tab-label` for display name) |
|
|
756
900
|
| `footer` | Footer content below all panels |
|
|
757
901
|
|
|
@@ -766,7 +910,7 @@ The `<ease-state>` component provides a complete API for state management.
|
|
|
766
910
|
| `tab` | Individual tab button |
|
|
767
911
|
| `actions` | Actions container |
|
|
768
912
|
| `content` | Content wrapper (handles height animations) |
|
|
769
|
-
| `
|
|
913
|
+
| `body` | Inner body container |
|
|
770
914
|
| `tab-panel` | Individual tab panel |
|
|
771
915
|
| `footer` | Footer container |
|
|
772
916
|
|
|
@@ -774,9 +918,55 @@ The `<ease-state>` component provides a complete API for state management.
|
|
|
774
918
|
|
|
775
919
|
| Event | Detail Type | Description |
|
|
776
920
|
|-------|-------------|-------------|
|
|
777
|
-
| `state-change` | `StateChangeEventDetail` | Fired when any control value changes |
|
|
778
921
|
| `tab-change` | `TabChangeEventDetail` | Fired when the active tab changes |
|
|
779
922
|
|
|
923
|
+
```typescript
|
|
924
|
+
interface TabChangeEventDetail {
|
|
925
|
+
index: number; // Tab index (0-based)
|
|
926
|
+
id: string; // Tab id from slot name
|
|
927
|
+
event: Event; // Original event
|
|
928
|
+
}
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### State API
|
|
932
|
+
|
|
933
|
+
The `<ease-state>` component provides state management for controls.
|
|
934
|
+
|
|
935
|
+
#### Properties
|
|
936
|
+
|
|
937
|
+
| Property | Type | Default | Description |
|
|
938
|
+
|----------|------|---------|-------------|
|
|
939
|
+
| `value` | `string \| null` | `null` | Reflects the last changed control's value |
|
|
940
|
+
| `state` | `Record<string, unknown>` | `{}` | Read-only object containing all control values |
|
|
941
|
+
|
|
942
|
+
#### Methods
|
|
943
|
+
|
|
944
|
+
| Method | Signature | Description |
|
|
945
|
+
|--------|-----------|-------------|
|
|
946
|
+
| `get` | `(name: string) => unknown` | Get a specific control value by name |
|
|
947
|
+
| `set` | `(name: string, value: unknown) => void` | Set a control value programmatically |
|
|
948
|
+
| `subscribe` | `(callback: (value, name) => void) => { unsubscribe }` | Subscribe to all state changes |
|
|
949
|
+
| `subscribe` | `(name: string, callback: (value, name) => void) => { unsubscribe }` | Subscribe to a specific control |
|
|
950
|
+
| `reset` | `() => void` | Reset all controls to their initial values |
|
|
951
|
+
|
|
952
|
+
#### Slots
|
|
953
|
+
|
|
954
|
+
| Slot | Description |
|
|
955
|
+
|------|-------------|
|
|
956
|
+
| (default) | Controls to aggregate state from |
|
|
957
|
+
|
|
958
|
+
#### CSS Parts
|
|
959
|
+
|
|
960
|
+
| Part | Description |
|
|
961
|
+
|------|-------------|
|
|
962
|
+
| `container` | Inner container wrapping controls |
|
|
963
|
+
|
|
964
|
+
#### Events
|
|
965
|
+
|
|
966
|
+
| Event | Detail Type | Description |
|
|
967
|
+
|-------|-------------|-------------|
|
|
968
|
+
| `state-change` | `StateChangeEventDetail` | Fired when any control value changes |
|
|
969
|
+
|
|
780
970
|
```typescript
|
|
781
971
|
interface StateChangeEventDetail {
|
|
782
972
|
name: string; // Control name
|
|
@@ -784,12 +974,6 @@ interface StateChangeEventDetail {
|
|
|
784
974
|
state: Record<string, unknown>; // Complete state object
|
|
785
975
|
event: Event; // Original event
|
|
786
976
|
}
|
|
787
|
-
|
|
788
|
-
interface TabChangeEventDetail {
|
|
789
|
-
index: number; // Tab index (0-based)
|
|
790
|
-
id: string; // Tab id from slot name
|
|
791
|
-
event: Event; // Original event
|
|
792
|
-
}
|
|
793
977
|
```
|
|
794
978
|
|
|
795
979
|
---
|
|
@@ -817,6 +1001,14 @@ const kit = initWebKit({ theme: 'default' });
|
|
|
817
1001
|
await kit.ready; // Resolves immediately on server
|
|
818
1002
|
```
|
|
819
1003
|
|
|
1004
|
+
For React/Next.js, all hooks check for browser environment:
|
|
1005
|
+
|
|
1006
|
+
```tsx
|
|
1007
|
+
// This is safe to call on the server
|
|
1008
|
+
const { ready, theme } = useWebKitContext();
|
|
1009
|
+
// ready will be false on server, true after hydration
|
|
1010
|
+
```
|
|
1011
|
+
|
|
820
1012
|
---
|
|
821
1013
|
|
|
822
1014
|
## License
|