@yewdiel21/motion-kit 0.14.0 → 0.16.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 +178 -155
- package/bin/motion-kit.mjs +3 -3
- package/dist/auto/index.d.ts +6 -3
- package/dist/auto/index.js +8 -2
- package/dist/auto/index.js.map +1 -1
- package/dist/{chunk-EY4BJKC4.js → chunk-NFZIX3IA.js} +2 -2
- package/dist/chunk-RBEKR5UZ.js +3660 -0
- package/dist/chunk-RBEKR5UZ.js.map +1 -0
- package/dist/{chunk-CU6SXW3E.js → chunk-SDQ776LG.js} +1596 -485
- package/dist/chunk-SDQ776LG.js.map +1 -0
- package/dist/css/components/add-to-cart.css +72 -0
- package/dist/css/components/bidirectional-slider.css +168 -0
- package/dist/css/components/chart.css +7 -0
- package/dist/css/components/data-table.css +121 -26
- package/dist/css/components/filter-builder.css +10 -15
- package/dist/css/components/heatmap-chart.css +398 -0
- package/dist/css/components/hover-card.css +8 -2
- package/dist/css/components/image-preview.css +90 -2
- package/dist/css/components/json-viewer.css +33 -1
- package/dist/css/components/nav.css +102 -102
- package/dist/css/components/orbit-menu.css +130 -0
- package/dist/css/components/pagination.css +10 -5
- package/dist/css/components/pie-chart.css +397 -0
- package/dist/css/components/pill-button.css +135 -0
- package/dist/css/components/radar-chart.css +377 -0
- package/dist/css/components/radial-chart.css +412 -0
- package/dist/css/components/sankey-chart.css +365 -0
- package/dist/css/components/scroll-island.css +114 -0
- package/dist/css/components/select.css +136 -0
- package/dist/css/components/toolbar.css +122 -0
- package/dist/css/components/tree-view.css +79 -0
- package/dist/css/components/voice-dots.css +70 -0
- package/dist/elements/index.d.ts +180 -7
- package/dist/elements/index.js +293 -7
- package/dist/elements/index.js.map +1 -1
- package/dist/{engine-Cn6IhE3z.d.ts → engine-5zRIssTO.d.ts} +11 -1
- package/dist/index.css +1849 -168
- package/dist/index.d.ts +4 -4
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +117 -11
- package/dist/react/index.js +320 -13
- package/dist/react/index.js.map +1 -1
- package/dist/{reduced-BBUusld6.d.ts → reduced-DQwVXQYb.d.ts} +1 -1
- package/dist/{reveal-DEvp6U6T.d.ts → reveal-DRpNO4KP.d.ts} +109 -4
- package/dist/store-Da3zSh8Z.d.ts +457 -0
- package/package.json +9 -16
- package/registry.json +1096 -948
- package/src/auto/auto-init.ts +6 -0
- package/src/components/add-to-cart/README.md +49 -0
- package/src/components/add-to-cart/add-to-cart.css +71 -0
- package/src/components/add-to-cart/element.ts +20 -0
- package/src/components/add-to-cart/engine.ts +206 -0
- package/src/components/add-to-cart/react.tsx +33 -0
- package/src/components/bidirectional-slider/README.md +46 -0
- package/src/components/bidirectional-slider/bidirectional-slider.css +167 -0
- package/src/components/bidirectional-slider/element.ts +42 -0
- package/src/components/bidirectional-slider/engine.ts +289 -0
- package/src/components/bidirectional-slider/react.tsx +45 -0
- package/src/components/chart/README.md +49 -24
- package/src/components/chart/chart.css +7 -0
- package/src/components/chart/element.ts +4 -1
- package/src/components/chart/engine.ts +153 -34
- package/src/components/chart/parts.ts +237 -0
- package/src/components/chart/react.tsx +2 -2
- package/src/components/chart/use-chart.ts +55 -0
- package/src/components/data-table/README.md +3 -3
- package/src/components/data-table/data-table.css +121 -26
- package/src/components/data-table/engine.ts +81 -29
- package/src/components/editable-table/engine.ts +67 -11
- package/src/components/filter-builder/engine.ts +33 -2
- package/src/components/filter-builder/filter-builder.css +10 -15
- package/src/components/heatmap-chart/README.md +48 -0
- package/src/components/heatmap-chart/element.ts +21 -0
- package/src/components/heatmap-chart/engine.ts +281 -0
- package/src/components/heatmap-chart/heatmap-chart.css +93 -0
- package/src/components/heatmap-chart/react.tsx +14 -0
- package/src/components/hover-card/engine.ts +30 -1
- package/src/components/hover-card/hover-card.css +8 -2
- package/src/components/image-preview/element.ts +1 -1
- package/src/components/image-preview/engine.ts +95 -15
- package/src/components/image-preview/image-preview.css +90 -2
- package/src/components/image-preview/react.tsx +3 -3
- package/src/components/json-viewer/engine.ts +11 -2
- package/src/components/json-viewer/json-viewer.css +33 -1
- package/src/components/nav/README.md +12 -10
- package/src/components/nav/element.ts +5 -5
- package/src/components/nav/engine.ts +21 -14
- package/src/components/nav/nav.css +102 -102
- package/src/components/nav/react.tsx +4 -4
- package/src/components/orbit-menu/README.md +46 -0
- package/src/components/orbit-menu/element.ts +27 -0
- package/src/components/orbit-menu/engine.ts +179 -0
- package/src/components/orbit-menu/orbit-menu.css +129 -0
- package/src/components/orbit-menu/react.tsx +44 -0
- package/src/components/pagination/README.md +1 -1
- package/src/components/pagination/engine.ts +50 -4
- package/src/components/pagination/pagination.css +10 -5
- package/src/components/pie-chart/README.md +48 -0
- package/src/components/pie-chart/element.ts +30 -0
- package/src/components/pie-chart/engine.ts +283 -0
- package/src/components/pie-chart/pie-chart.css +92 -0
- package/src/components/pie-chart/react.tsx +14 -0
- package/src/components/pill-button/README.md +46 -0
- package/src/components/pill-button/element.ts +16 -0
- package/src/components/pill-button/engine.ts +77 -0
- package/src/components/pill-button/pill-button.css +134 -0
- package/src/components/pill-button/react.tsx +32 -0
- package/src/components/radar-chart/README.md +48 -0
- package/src/components/radar-chart/element.ts +30 -0
- package/src/components/radar-chart/engine.ts +327 -0
- package/src/components/radar-chart/radar-chart.css +72 -0
- package/src/components/radar-chart/react.tsx +14 -0
- package/src/components/radial-chart/README.md +58 -0
- package/src/components/radial-chart/element.ts +30 -0
- package/src/components/radial-chart/engine.ts +317 -0
- package/src/components/radial-chart/radial-chart.css +107 -0
- package/src/components/radial-chart/react.tsx +14 -0
- package/src/components/sankey-chart/README.md +48 -0
- package/src/components/sankey-chart/element.ts +31 -0
- package/src/components/sankey-chart/engine.ts +401 -0
- package/src/components/sankey-chart/react.tsx +14 -0
- package/src/components/sankey-chart/sankey-chart.css +60 -0
- package/src/components/scroll-island/README.md +44 -0
- package/src/components/scroll-island/element.ts +16 -0
- package/src/components/scroll-island/engine.ts +274 -0
- package/src/components/scroll-island/react.tsx +26 -0
- package/src/components/scroll-island/scroll-island.css +113 -0
- package/src/components/select/README.md +34 -0
- package/src/components/select/element.ts +26 -0
- package/src/components/select/engine.ts +267 -0
- package/src/components/select/react.tsx +33 -0
- package/src/components/select/select.css +135 -0
- package/src/components/toolbar/README.md +46 -0
- package/src/components/toolbar/element.ts +30 -0
- package/src/components/toolbar/engine.ts +230 -0
- package/src/components/toolbar/react.tsx +34 -0
- package/src/components/toolbar/toolbar.css +121 -0
- package/src/components/tree-view/element.ts +1 -1
- package/src/components/tree-view/engine.ts +174 -0
- package/src/components/tree-view/react.tsx +7 -3
- package/src/components/tree-view/tree-view.css +79 -0
- package/src/components/voice-dots/README.md +48 -0
- package/src/components/voice-dots/element.ts +45 -0
- package/src/components/voice-dots/engine.ts +243 -0
- package/src/components/voice-dots/react.tsx +52 -0
- package/src/components/voice-dots/voice-dots.css +69 -0
- package/src/elements/index.ts +39 -0
- package/src/index.ts +16 -0
- package/src/motion/liquid.ts +14 -4
- package/src/react/index.ts +14 -1
- package/dist/chunk-6P4P7NU6.js +0 -1401
- package/dist/chunk-6P4P7NU6.js.map +0 -1
- package/dist/chunk-CU6SXW3E.js.map +0 -1
- package/dist/store-DjkTBX7f.d.ts +0 -195
- /package/dist/{chunk-EY4BJKC4.js.map → chunk-NFZIX3IA.js.map} +0 -0
|
@@ -207,3 +207,82 @@ mk-tree-view {
|
|
|
207
207
|
animation: none;
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
+
|
|
211
|
+
/* Folder and page icons (icons option): the folder's front opens like a lid with its branch, or under a dragged card. */
|
|
212
|
+
:is([data-mk-tree-folder], [data-mk-tree-page]) {
|
|
213
|
+
display: inline-grid;
|
|
214
|
+
flex: none;
|
|
215
|
+
place-items: center;
|
|
216
|
+
width: 1.15rem;
|
|
217
|
+
height: 1.15rem;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
:is([data-mk-tree-folder], [data-mk-tree-page]) svg {
|
|
221
|
+
width: 100%;
|
|
222
|
+
height: 100%;
|
|
223
|
+
overflow: visible;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
[data-mk-tree-folder-back] {
|
|
227
|
+
fill: color-mix(in srgb, var(--mk-accent) 70%, var(--mk-surface-1));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
[data-mk-tree-folder-front] {
|
|
231
|
+
fill: var(--mk-accent);
|
|
232
|
+
transform-box: fill-box;
|
|
233
|
+
transform-origin: 50% 100%;
|
|
234
|
+
transition: transform 0.35s cubic-bezier(0.38, 0.49, 0, 1.5);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
[role='treeitem'][aria-expanded='true'] > [data-mk-tree-row] [data-mk-tree-folder-front],
|
|
238
|
+
[role='treeitem'][data-mk-tree-over] > [data-mk-tree-row] [data-mk-tree-folder-front] {
|
|
239
|
+
transform: skewX(-14deg) scaleY(0.78);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
[data-mk-tree-sheet] {
|
|
243
|
+
fill: var(--mk-surface-3);
|
|
244
|
+
stroke: var(--mk-outline);
|
|
245
|
+
stroke-width: 2;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
[data-mk-tree-fold] {
|
|
249
|
+
fill: var(--mk-outline);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* Dragging: the source row fades, the branch under the card lights up, the card floats with a shadow. */
|
|
253
|
+
[role='treeitem'][data-mk-tree-dragging] > [data-mk-tree-row] {
|
|
254
|
+
opacity: 0.4;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
[role='treeitem'][data-mk-tree-over] > [data-mk-tree-row] {
|
|
258
|
+
background: color-mix(in srgb, var(--mk-accent) 14%, transparent);
|
|
259
|
+
box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--mk-accent) 55%, transparent);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
[data-mk-tree-drag] {
|
|
263
|
+
position: fixed;
|
|
264
|
+
z-index: 2147483646;
|
|
265
|
+
display: inline-flex;
|
|
266
|
+
align-items: center;
|
|
267
|
+
gap: 0.5rem;
|
|
268
|
+
max-width: 16rem;
|
|
269
|
+
padding: 0.45rem 0.75rem;
|
|
270
|
+
overflow: hidden;
|
|
271
|
+
border-radius: 10px;
|
|
272
|
+
background: var(--mk-surface-1);
|
|
273
|
+
box-shadow:
|
|
274
|
+
0 0 0 1px var(--mk-outline-soft),
|
|
275
|
+
0 14px 30px -10px color-mix(in srgb, var(--mk-shadow-color, #000) 45%, transparent);
|
|
276
|
+
color: var(--mk-text);
|
|
277
|
+
font-size: 0.875rem;
|
|
278
|
+
font-weight: 500;
|
|
279
|
+
white-space: nowrap;
|
|
280
|
+
text-overflow: ellipsis;
|
|
281
|
+
pointer-events: none;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@media (prefers-reduced-motion: reduce) {
|
|
285
|
+
[data-mk-tree-folder-front] {
|
|
286
|
+
transition: none;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# voice-dots
|
|
2
|
+
|
|
3
|
+
Puntos que respiran, escuchan, piensan y hablan; pueden seguir el nivel real de una fuente de audio.
|
|
4
|
+
|
|
5
|
+
## Instalación
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import '@yewdiel21/motion-kit/css';
|
|
9
|
+
import { VoiceDots } from '@yewdiel21/motion-kit/react';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npx @yewdiel21/motion-kit add voice-dots --react --elements
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## React
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<VoiceDots state="listening" source={stream} level={level} />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Los callbacks del ejemplo (`ask`, `run`, `setTool`, `setVolume`) pertenecen a tu aplicación. React acepta `className`, `style` y atributos HTML. Los componentes con estado exponen `instanceRef` (excepto PillButton).
|
|
23
|
+
|
|
24
|
+
## HTML / Astro
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<mk-voice-dots state="listening" size="12"></mk-voice-dots>
|
|
28
|
+
<script type="module">
|
|
29
|
+
import { defineElements } from '@yewdiel21/motion-kit/elements';
|
|
30
|
+
defineElements(['mk-voice-dots']);
|
|
31
|
+
</script>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
En Astro usa un `<script>` procesado por Astro. En HTML directo el import requiere bundler o import map. También puedes usar `import { createVoiceDots } from '@yewdiel21/motion-kit'` y llamar a la factory con el contenedor y las mismas opciones. Al desmontar, llama a `instance.destroy()`.
|
|
35
|
+
|
|
36
|
+
## Opciones
|
|
37
|
+
|
|
38
|
+
state: idle, listening, thinking o speaking. count: 4 (3 a 6). size: 12 px. colorful: false. labels: textos de estado. En React source acepta MediaStream o HTMLMediaElement; level acepta 0…1 o un array. Instancia: setState(), setLevel(), listen(source), stop(), state.
|
|
39
|
+
|
|
40
|
+
El componente no solicita acceso al micrófono por sí solo. Obtén el stream tras una acción del usuario con `navigator.mediaDevices.getUserMedia({audio:true})`. `await instance.listen(stream)` devuelve una función para desconectar el análisis; la aplicación debe detener sus tracks al terminar. La fuente se analiza localmente. `stop()` desconecta la fuente. Sin audio, listening acepta `setLevel()` y speaking usa una onda de demostración.
|
|
41
|
+
|
|
42
|
+
Los nombres camelCase se escriben como atributos kebab-case. Los arrays y objetos se pasan como JSON. Los booleanos aceptan atributo vacío o `"false"`.
|
|
43
|
+
|
|
44
|
+
## Estilos y accesibilidad
|
|
45
|
+
|
|
46
|
+
Variables: --mk-voice-size; colores semánticos del tema..
|
|
47
|
+
|
|
48
|
+
Respeta los colores del tema, foco visible y `prefers-reduced-motion`. Los gráficos incluyen una tabla accesible y selección con flechas, Home, End y Escape. Heatmap usa arriba/abajo para un día e izquierda/derecha para una semana. Toolbar usa flechas y Home/End; OrbitMenu cierra con Escape y devuelve el foco. Los controles conservan nombres accesibles; VoiceDots anuncia los estados sin leer cada fotograma.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MkElement, defineElement, type OptionType } from '../../elements/base';
|
|
2
|
+
import { createVoiceDots, type VoiceDotsInstance, type VoiceState } from './engine';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* <mk-voice-dots class="mk-voice-dots" state="listening"></mk-voice-dots>
|
|
6
|
+
* Dots that move with a voice. element.listen(stream) follows a microphone; emits mk:state ({state}).
|
|
7
|
+
*/
|
|
8
|
+
export class MkVoiceDotsElement extends MkElement<VoiceDotsInstance> {
|
|
9
|
+
static options: Record<string, OptionType> = { state: 'string', count: 'number', colorful: 'boolean', size: 'number' };
|
|
10
|
+
|
|
11
|
+
protected create(options: Record<string, unknown>) {
|
|
12
|
+
return createVoiceDots(this, options);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** A new state flows from where the dots are (the engine, and the audio it follows, stay). */
|
|
16
|
+
attributeChangedCallback(name: string, previous: string | null, next: string | null) {
|
|
17
|
+
if (name === 'state' && this.instance) {
|
|
18
|
+
this.instance.setState((next ?? 'idle') as VoiceState);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
super.attributeChangedCallback(name, previous, next);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get state() {
|
|
25
|
+
return this.instance?.state ?? 'idle';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
set state(next: VoiceState) {
|
|
29
|
+
this.setAttribute('state', next);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setLevel(level: number | number[]) {
|
|
33
|
+
this.instance?.setLevel(level);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
listen(source: MediaStream | HTMLMediaElement) {
|
|
37
|
+
return this.instance?.listen(source) ?? Promise.resolve(() => {});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
stop() {
|
|
41
|
+
this.instance?.stop();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const defineVoiceDots = () => defineElement('mk-voice-dots', MkVoiceDotsElement);
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { registerMotion } from '../../core/gsap';
|
|
2
|
+
import { whileVisible } from '../../core/loop';
|
|
3
|
+
import { reduced } from '../../core/reduced';
|
|
4
|
+
import { createScope } from '../../core/scope';
|
|
5
|
+
import { inert, type Instance } from '../../core/types';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Voice dots: four dots that show a voice conversation, like an assistant's.
|
|
9
|
+
* Idle, they breathe one after another. Listening, each dot stretches into a
|
|
10
|
+
* pill with its band of the voice (from a microphone or a playing element
|
|
11
|
+
* through an AnalyserNode, or levels the app gives), springing up fast and
|
|
12
|
+
* settling slower so syllables read as a bounce. Thinking, they gather into a
|
|
13
|
+
* ring and orbit. Speaking, they ride a travelling wave (with the playing
|
|
14
|
+
* audio's level when there is one). Every change of state flows from where the
|
|
15
|
+
* dots are, so nothing jumps; the frame loop only runs while the dots are on
|
|
16
|
+
* screen. Monochrome (the text colour) by default, or the chart colours. A
|
|
17
|
+
* status line says the state to assistive tech; with reduced motion the dots
|
|
18
|
+
* stay still and only their colour and the status change.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export type VoiceState = 'idle' | 'listening' | 'thinking' | 'speaking';
|
|
22
|
+
|
|
23
|
+
export interface VoiceDotsOptions {
|
|
24
|
+
/** Default 'idle'. */
|
|
25
|
+
state?: VoiceState;
|
|
26
|
+
/** Dots (default 4, 3…6). */
|
|
27
|
+
count?: number;
|
|
28
|
+
/** The chart colours instead of the text colour. */
|
|
29
|
+
colorful?: boolean;
|
|
30
|
+
/** Dot size in px (default 12). */
|
|
31
|
+
size?: number;
|
|
32
|
+
/** The status text for each state. */
|
|
33
|
+
labels?: Partial<Record<VoiceState, string>>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface VoiceDotsInstance extends Instance {
|
|
37
|
+
setState(state: VoiceState): void;
|
|
38
|
+
readonly state: VoiceState;
|
|
39
|
+
/** The voice's level, 0…1: one for all dots or one per dot (from your own analysis). */
|
|
40
|
+
setLevel(level: number | number[]): void;
|
|
41
|
+
/** Follow a microphone stream or a playing <audio>/<video>; resolves to a function that stops following. */
|
|
42
|
+
listen(source: MediaStream | HTMLMediaElement): Promise<() => void>;
|
|
43
|
+
/** Stop following the audio (the state stays). */
|
|
44
|
+
stop(): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const LABELS: Record<VoiceState, string> = { idle: 'Asistente en espera', listening: 'Escuchando…', thinking: 'Pensando…', speaking: 'Hablando…' };
|
|
48
|
+
const STATES: VoiceState[] = ['idle', 'listening', 'thinking', 'speaking'];
|
|
49
|
+
/** One AudioContext source per media element: the platform allows only one. */
|
|
50
|
+
const mediaSources = new WeakMap<HTMLMediaElement, MediaElementAudioSourceNode>();
|
|
51
|
+
let audio: AudioContext | null = null;
|
|
52
|
+
|
|
53
|
+
export function createVoiceDots(root: HTMLElement | null, options: VoiceDotsOptions = {}): VoiceDotsInstance {
|
|
54
|
+
if (!root) return inert({ setState() {}, state: 'idle' as VoiceState, setLevel() {}, listen: async () => () => {}, stop() {} });
|
|
55
|
+
registerMotion();
|
|
56
|
+
const scope = createScope();
|
|
57
|
+
const count = Math.max(3, Math.min(6, Math.round(options.count ?? 4)));
|
|
58
|
+
const size = Math.max(4, options.size ?? 12);
|
|
59
|
+
const labels = { ...LABELS, ...options.labels };
|
|
60
|
+
let state: VoiceState = STATES.includes(options.state as VoiceState) ? options.state! : 'idle';
|
|
61
|
+
|
|
62
|
+
const row = document.createElement('span');
|
|
63
|
+
row.dataset.mkVoiceRow = '';
|
|
64
|
+
row.setAttribute('aria-hidden', 'true');
|
|
65
|
+
const dots = Array.from({ length: count }, (_, i) => {
|
|
66
|
+
const dot = document.createElement('span');
|
|
67
|
+
dot.dataset.mkVoiceDot = String(i + 1);
|
|
68
|
+
row.append(dot);
|
|
69
|
+
return dot;
|
|
70
|
+
});
|
|
71
|
+
const status = document.createElement('span');
|
|
72
|
+
status.className = 'mk-sr-only';
|
|
73
|
+
status.setAttribute('role', 'status');
|
|
74
|
+
root.append(row, status);
|
|
75
|
+
scope.add(() => {
|
|
76
|
+
row.remove();
|
|
77
|
+
status.remove();
|
|
78
|
+
});
|
|
79
|
+
scope.attr(root, 'data-mk-ready', '');
|
|
80
|
+
scope.attr(root, 'data-mk-state', state);
|
|
81
|
+
if (options.colorful) scope.attr(root, 'data-mk-colorful', '');
|
|
82
|
+
root.style.setProperty('--mk-voice-size', `${size}px`);
|
|
83
|
+
scope.add(() => {
|
|
84
|
+
root.style.removeProperty('--mk-voice-size');
|
|
85
|
+
if (!root.getAttribute('style')) root.removeAttribute('style');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Per dot: what is drawn (height, x, y) and the level it follows.
|
|
89
|
+
const drawn = dots.map(() => ({ h: size, x: 0, y: 0, vh: 0 }));
|
|
90
|
+
const levels = dots.map(() => 0);
|
|
91
|
+
let time = 0;
|
|
92
|
+
const gap = size * 0.9;
|
|
93
|
+
const home = (i: number) => (i - (count - 1) / 2) * (size + gap);
|
|
94
|
+
|
|
95
|
+
const draw = () => {
|
|
96
|
+
dots.forEach((dot, i) => {
|
|
97
|
+
const d = drawn[i]!;
|
|
98
|
+
dot.style.height = `${d.h.toFixed(2)}px`;
|
|
99
|
+
dot.style.transform = `translate(${d.x.toFixed(2)}px, ${d.y.toFixed(2)}px)`;
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
const still = () => {
|
|
103
|
+
dots.forEach((_, i) => Object.assign(drawn[i]!, { h: size, x: 0, y: 0, vh: 0 }));
|
|
104
|
+
draw();
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
let analyser: AnalyserNode | null = null;
|
|
108
|
+
let bins: Uint8Array<ArrayBuffer> | null = null;
|
|
109
|
+
let release: (() => void) | null = null;
|
|
110
|
+
let listening = 0;
|
|
111
|
+
/** The voice's bands (about 120 Hz – 3 kHz) into one level per dot. */
|
|
112
|
+
const sample = () => {
|
|
113
|
+
if (!analyser || !bins) return;
|
|
114
|
+
analyser.getByteFrequencyData(bins);
|
|
115
|
+
const hz = (audio?.sampleRate ?? 48000) / 2 / bins.length;
|
|
116
|
+
const low = Math.max(1, Math.floor(120 / hz));
|
|
117
|
+
const high = Math.min(bins.length - 1, Math.ceil(3000 / hz));
|
|
118
|
+
const span = (high - low) / count;
|
|
119
|
+
for (let i = 0; i < count; i++) {
|
|
120
|
+
let sum = 0;
|
|
121
|
+
const from = Math.floor(low + span * i);
|
|
122
|
+
const to = Math.max(from + 1, Math.floor(low + span * (i + 1)));
|
|
123
|
+
for (let k = from; k < to; k++) sum += bins[k]!;
|
|
124
|
+
// Higher bands are quieter in a voice: they get a lift.
|
|
125
|
+
levels[i] = Math.min(1, (sum / (to - from) / 255) * (1.4 + i * 0.35));
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const tick = (dt: number) => {
|
|
130
|
+
if (reduced()) return;
|
|
131
|
+
time += dt / 60;
|
|
132
|
+
sample();
|
|
133
|
+
const k = 1 - Math.exp(-dt * 0.35);
|
|
134
|
+
dots.forEach((_, i) => {
|
|
135
|
+
const d = drawn[i]!;
|
|
136
|
+
let h = size;
|
|
137
|
+
let x = 0;
|
|
138
|
+
let y = 0;
|
|
139
|
+
if (state === 'idle') {
|
|
140
|
+
h = size * (1 + 0.18 * Math.max(0, Math.sin(time * 3.2 - i * 0.7)));
|
|
141
|
+
y = -2 * Math.max(0, Math.sin(time * 3.2 - i * 0.7));
|
|
142
|
+
} else if (state === 'listening') {
|
|
143
|
+
h = size + levels[i]! * size * 3.2;
|
|
144
|
+
} else if (state === 'thinking') {
|
|
145
|
+
// A ring around the middle, turning.
|
|
146
|
+
const angle = time * 4 + (i / count) * Math.PI * 2;
|
|
147
|
+
const ring = size * 1.2;
|
|
148
|
+
x = Math.cos(angle) * ring - home(i);
|
|
149
|
+
y = Math.sin(angle) * ring;
|
|
150
|
+
} else {
|
|
151
|
+
const wave = 0.5 + 0.5 * Math.sin(time * 9 - i * 0.9);
|
|
152
|
+
const voice = analyser ? levels[i]! : 0.55;
|
|
153
|
+
h = size + wave * voice * size * 2.4;
|
|
154
|
+
}
|
|
155
|
+
// Height on a spring that rises fast and falls slower (a bounce per syllable); position eases.
|
|
156
|
+
const stiffness = h > d.h ? 0.5 : 0.18;
|
|
157
|
+
d.vh = (d.vh + (h - d.h) * stiffness) * 0.6;
|
|
158
|
+
d.h = Math.max(size * 0.8, d.h + d.vh * dt);
|
|
159
|
+
d.x += (x - d.x) * k;
|
|
160
|
+
d.y += (y - d.y) * k;
|
|
161
|
+
});
|
|
162
|
+
draw();
|
|
163
|
+
};
|
|
164
|
+
const stopLoop = whileVisible(root, tick);
|
|
165
|
+
scope.add(stopLoop);
|
|
166
|
+
|
|
167
|
+
const stop = () => {
|
|
168
|
+
listening++;
|
|
169
|
+
release?.();
|
|
170
|
+
release = null;
|
|
171
|
+
analyser = null;
|
|
172
|
+
bins = null;
|
|
173
|
+
levels.fill(0);
|
|
174
|
+
};
|
|
175
|
+
scope.add(stop);
|
|
176
|
+
|
|
177
|
+
const setState = (next: VoiceState) => {
|
|
178
|
+
if (!STATES.includes(next)) return;
|
|
179
|
+
state = next;
|
|
180
|
+
root.setAttribute('data-mk-state', next);
|
|
181
|
+
status.textContent = next === 'idle' ? '' : labels[next];
|
|
182
|
+
if (reduced()) still();
|
|
183
|
+
root.dispatchEvent(new CustomEvent('mk:state', { bubbles: true, detail: { state: next } }));
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
draw();
|
|
187
|
+
if (state !== 'idle') status.textContent = labels[state];
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
setState,
|
|
191
|
+
get state() {
|
|
192
|
+
return state;
|
|
193
|
+
},
|
|
194
|
+
setLevel(level) {
|
|
195
|
+
const list = Array.isArray(level) ? level : dots.map(() => level);
|
|
196
|
+
dots.forEach((_, i) => {
|
|
197
|
+
const value = Number(list[i % Math.max(1, list.length)]);
|
|
198
|
+
levels[i] = Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0;
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
async listen(source) {
|
|
202
|
+
stop();
|
|
203
|
+
const request = listening;
|
|
204
|
+
const Context = globalThis.AudioContext ?? (globalThis as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
|
|
205
|
+
if (!Context || scope.disposed) return () => {};
|
|
206
|
+
audio ??= new Context();
|
|
207
|
+
if (audio.state === 'suspended') await audio.resume().catch(() => {});
|
|
208
|
+
if (scope.disposed || request !== listening) return () => {};
|
|
209
|
+
const node = audio.createAnalyser();
|
|
210
|
+
node.fftSize = 256;
|
|
211
|
+
node.smoothingTimeConstant = 0.55;
|
|
212
|
+
let input: AudioNode;
|
|
213
|
+
if (source instanceof HTMLMediaElement) {
|
|
214
|
+
const existing = mediaSources.get(source) ?? audio.createMediaElementSource(source);
|
|
215
|
+
mediaSources.set(source, existing);
|
|
216
|
+
// Followed before: its direct line to the speakers goes, the analyser's takes over.
|
|
217
|
+
existing.disconnect();
|
|
218
|
+
input = existing;
|
|
219
|
+
// A playing element must still be heard.
|
|
220
|
+
node.connect(audio.destination);
|
|
221
|
+
} else input = audio.createMediaStreamSource(source);
|
|
222
|
+
input.connect(node);
|
|
223
|
+
analyser = node;
|
|
224
|
+
bins = new Uint8Array(new ArrayBuffer(node.frequencyBinCount));
|
|
225
|
+
const mine = () => {
|
|
226
|
+
try {
|
|
227
|
+
input.disconnect(node);
|
|
228
|
+
node.disconnect();
|
|
229
|
+
} catch {
|
|
230
|
+
// Already disconnected.
|
|
231
|
+
}
|
|
232
|
+
// The element keeps playing through the speakers.
|
|
233
|
+
if (source instanceof HTMLMediaElement && audio) input.connect(audio.destination);
|
|
234
|
+
};
|
|
235
|
+
release = mine;
|
|
236
|
+
return () => {
|
|
237
|
+
if (release === mine) stop();
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
stop,
|
|
241
|
+
destroy: () => scope.dispose(),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useRef, type HTMLAttributes, type MutableRefObject } from 'react';
|
|
3
|
+
import { useIsomorphicLayoutEffect } from '../../react/use-recipe';
|
|
4
|
+
import { createVoiceDots, type VoiceDotsInstance, type VoiceDotsOptions } from './engine';
|
|
5
|
+
|
|
6
|
+
export interface VoiceDotsProps extends HTMLAttributes<HTMLDivElement>, VoiceDotsOptions {
|
|
7
|
+
/** A microphone stream or a playing <audio>/<video> to follow. */
|
|
8
|
+
source?: MediaStream | HTMLMediaElement | null;
|
|
9
|
+
/** The voice's level, 0…1 (one or one per dot), when the app measures it. */
|
|
10
|
+
level?: number | number[];
|
|
11
|
+
instanceRef?: MutableRefObject<VoiceDotsInstance | null>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** <VoiceDots state={listening ? 'listening' : 'idle'} source={micStream} /> */
|
|
15
|
+
export function VoiceDots({ state = 'idle', count, colorful, size, labels, source, level, instanceRef, className, ...props }: VoiceDotsProps) {
|
|
16
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
17
|
+
const instance = useRef<VoiceDotsInstance | null>(null);
|
|
18
|
+
useIsomorphicLayoutEffect(() => {
|
|
19
|
+
const created = createVoiceDots(ref.current, { state, count, colorful, size, labels });
|
|
20
|
+
instance.current = created;
|
|
21
|
+
if (instanceRef) instanceRef.current = created;
|
|
22
|
+
return () => {
|
|
23
|
+
created.destroy();
|
|
24
|
+
instance.current = null;
|
|
25
|
+
if (instanceRef) instanceRef.current = null;
|
|
26
|
+
};
|
|
27
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
28
|
+
}, [count, colorful, size]);
|
|
29
|
+
useIsomorphicLayoutEffect(() => {
|
|
30
|
+
if (instance.current && instance.current.state !== state) instance.current.setState(state);
|
|
31
|
+
}, [state]);
|
|
32
|
+
useIsomorphicLayoutEffect(() => {
|
|
33
|
+
const dots = instance.current;
|
|
34
|
+
if (!dots || !source) return;
|
|
35
|
+
let stop: (() => void) | null = null;
|
|
36
|
+
let gone = false;
|
|
37
|
+
void dots.listen(source).then((done) => {
|
|
38
|
+
if (gone) done();
|
|
39
|
+
else stop = done;
|
|
40
|
+
}).catch((error: unknown) => {
|
|
41
|
+
if (!gone) ref.current?.dispatchEvent(new CustomEvent('mk:error', { bubbles: true, detail: { error } }));
|
|
42
|
+
});
|
|
43
|
+
return () => {
|
|
44
|
+
gone = true;
|
|
45
|
+
stop?.();
|
|
46
|
+
};
|
|
47
|
+
}, [source, count, colorful, size]);
|
|
48
|
+
useIsomorphicLayoutEffect(() => {
|
|
49
|
+
if (level !== undefined) instance.current?.setLevel(level);
|
|
50
|
+
}, [level]);
|
|
51
|
+
return <div {...props} ref={ref} className={['mk-voice-dots', className].filter(Boolean).join(' ')} />;
|
|
52
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* voice-dots — round dots in the text colour (or the chart colours) that stretch into pills with the voice.
|
|
2
|
+
The row is tall enough for the tallest pill, so the layout around it never moves. */
|
|
3
|
+
.mk-voice-dots,
|
|
4
|
+
mk-voice-dots {
|
|
5
|
+
--mk-voice-size: 12px;
|
|
6
|
+
--mk-voice-dot: var(--mk-text);
|
|
7
|
+
display: inline-grid;
|
|
8
|
+
place-items: center;
|
|
9
|
+
min-width: calc(var(--mk-voice-size) * 8);
|
|
10
|
+
height: calc(var(--mk-voice-size) * 4.6);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-mk-voice-row] {
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: calc(var(--mk-voice-size) * 0.9);
|
|
17
|
+
height: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[data-mk-voice-dot] {
|
|
21
|
+
display: block;
|
|
22
|
+
width: var(--mk-voice-size);
|
|
23
|
+
height: var(--mk-voice-size);
|
|
24
|
+
border-radius: var(--mk-radius-full);
|
|
25
|
+
background: var(--mk-voice-dot);
|
|
26
|
+
transition:
|
|
27
|
+
background-color 0.3s ease,
|
|
28
|
+
opacity 0.3s ease;
|
|
29
|
+
will-change: transform, height;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-state='idle'] [data-mk-voice-dot] {
|
|
33
|
+
opacity: 0.55;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='1'] {
|
|
37
|
+
--mk-voice-dot: var(--mk-chart-1, #2a78d6);
|
|
38
|
+
}
|
|
39
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='2'] {
|
|
40
|
+
--mk-voice-dot: var(--mk-chart-8, #e34948);
|
|
41
|
+
}
|
|
42
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='3'] {
|
|
43
|
+
--mk-voice-dot: var(--mk-chart-4, #eda100);
|
|
44
|
+
}
|
|
45
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='4'] {
|
|
46
|
+
--mk-voice-dot: var(--mk-chart-3, #1baf7a);
|
|
47
|
+
}
|
|
48
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='5'] {
|
|
49
|
+
--mk-voice-dot: var(--mk-chart-7, #6250d6);
|
|
50
|
+
}
|
|
51
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='6'] {
|
|
52
|
+
--mk-voice-dot: var(--mk-chart-2, #eb6834);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Without motion the state shows in the dots' colour. */
|
|
56
|
+
@media (prefers-reduced-motion: reduce) {
|
|
57
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-state='listening'] [data-mk-voice-dot] {
|
|
58
|
+
--mk-voice-dot: var(--mk-accent);
|
|
59
|
+
}
|
|
60
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-state='thinking'] [data-mk-voice-dot] {
|
|
61
|
+
opacity: 0.75;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@media (forced-colors: active) {
|
|
66
|
+
[data-mk-voice-dot] {
|
|
67
|
+
background: CanvasText;
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/elements/index.ts
CHANGED
|
@@ -66,6 +66,19 @@ import { MkSplitViewElement } from '../components/split-view/element';
|
|
|
66
66
|
import { MkDataTableElement } from '../components/data-table/element';
|
|
67
67
|
import { MkEditableTableElement } from '../components/editable-table/element';
|
|
68
68
|
import { MkRichTextEditorElement } from '../components/rich-text-editor/element';
|
|
69
|
+
import { MkSelectElement } from '../components/select/element';
|
|
70
|
+
import { MkAddToCartElement } from '../components/add-to-cart/element';
|
|
71
|
+
import { MkScrollIslandElement } from '../components/scroll-island/element';
|
|
72
|
+
import { MkPillButtonElement } from '../components/pill-button/element';
|
|
73
|
+
import { MkToolbarElement } from '../components/toolbar/element';
|
|
74
|
+
import { MkBidirectionalSliderElement } from '../components/bidirectional-slider/element';
|
|
75
|
+
import { MkVoiceDotsElement } from '../components/voice-dots/element';
|
|
76
|
+
import { MkPieChartElement } from '../components/pie-chart/element';
|
|
77
|
+
import { MkRadarChartElement } from '../components/radar-chart/element';
|
|
78
|
+
import { MkRadialChartElement } from '../components/radial-chart/element';
|
|
79
|
+
import { MkSankeyChartElement } from '../components/sankey-chart/element';
|
|
80
|
+
import { MkHeatmapChartElement } from '../components/heatmap-chart/element';
|
|
81
|
+
import { MkOrbitMenuElement } from '../components/orbit-menu/element';
|
|
69
82
|
// new-component:imports (scripts/new-component.mjs inserts above this line)
|
|
70
83
|
|
|
71
84
|
export { MkElement, SafeHTMLElement, defineElement, type OptionType } from './base';
|
|
@@ -137,6 +150,19 @@ export { MkSplitViewElement } from '../components/split-view/element';
|
|
|
137
150
|
export { MkDataTableElement } from '../components/data-table/element';
|
|
138
151
|
export { MkEditableTableElement } from '../components/editable-table/element';
|
|
139
152
|
export { MkRichTextEditorElement } from '../components/rich-text-editor/element';
|
|
153
|
+
export { MkSelectElement } from '../components/select/element';
|
|
154
|
+
export { MkAddToCartElement } from '../components/add-to-cart/element';
|
|
155
|
+
export { MkScrollIslandElement } from '../components/scroll-island/element';
|
|
156
|
+
export { MkPillButtonElement } from '../components/pill-button/element';
|
|
157
|
+
export { MkToolbarElement } from '../components/toolbar/element';
|
|
158
|
+
export { MkBidirectionalSliderElement } from '../components/bidirectional-slider/element';
|
|
159
|
+
export { MkVoiceDotsElement } from '../components/voice-dots/element';
|
|
160
|
+
export { MkPieChartElement } from '../components/pie-chart/element';
|
|
161
|
+
export { MkRadarChartElement } from '../components/radar-chart/element';
|
|
162
|
+
export { MkRadialChartElement } from '../components/radial-chart/element';
|
|
163
|
+
export { MkSankeyChartElement } from '../components/sankey-chart/element';
|
|
164
|
+
export { MkHeatmapChartElement } from '../components/heatmap-chart/element';
|
|
165
|
+
export { MkOrbitMenuElement } from '../components/orbit-menu/element';
|
|
140
166
|
// new-component:exports
|
|
141
167
|
|
|
142
168
|
/** Tag name → element class, for every component. */
|
|
@@ -208,6 +234,19 @@ export const ELEMENTS = {
|
|
|
208
234
|
'mk-data-table': MkDataTableElement,
|
|
209
235
|
'mk-editable-table': MkEditableTableElement,
|
|
210
236
|
'mk-rich-text-editor': MkRichTextEditorElement,
|
|
237
|
+
'mk-select': MkSelectElement,
|
|
238
|
+
'mk-add-to-cart': MkAddToCartElement,
|
|
239
|
+
'mk-scroll-island': MkScrollIslandElement,
|
|
240
|
+
'mk-pill-button': MkPillButtonElement,
|
|
241
|
+
'mk-toolbar': MkToolbarElement,
|
|
242
|
+
'mk-bidirectional-slider': MkBidirectionalSliderElement,
|
|
243
|
+
'mk-voice-dots': MkVoiceDotsElement,
|
|
244
|
+
'mk-pie-chart': MkPieChartElement,
|
|
245
|
+
'mk-radar-chart': MkRadarChartElement,
|
|
246
|
+
'mk-radial-chart': MkRadialChartElement,
|
|
247
|
+
'mk-sankey-chart': MkSankeyChartElement,
|
|
248
|
+
'mk-heatmap-chart': MkHeatmapChartElement,
|
|
249
|
+
'mk-orbit-menu': MkOrbitMenuElement,
|
|
211
250
|
// new-component:entries
|
|
212
251
|
} as const;
|
|
213
252
|
|
package/src/index.ts
CHANGED
|
@@ -153,9 +153,12 @@ export {
|
|
|
153
153
|
createNav,
|
|
154
154
|
revealShell,
|
|
155
155
|
NAV_STYLES,
|
|
156
|
+
NAV_STYLE_ALIASES,
|
|
157
|
+
toNavStyle,
|
|
156
158
|
type NavOptions,
|
|
157
159
|
type NavInstance,
|
|
158
160
|
type NavStyle,
|
|
161
|
+
type NavStyleAlias,
|
|
159
162
|
type NavigateDetail,
|
|
160
163
|
} from './components/nav/engine';
|
|
161
164
|
export { createSteps, type StepsOptions, type StepsInstance, type StepDetail } from './components/steps/engine';
|
|
@@ -272,3 +275,16 @@ export {
|
|
|
272
275
|
} from './components/data-table/engine';
|
|
273
276
|
export { createEditableTable, type EditableTableOptions, type EditableTableInstance, type EditableColumn, type RowChange, type EditorKind } from './components/editable-table/engine';
|
|
274
277
|
export { createRichTextEditor, sanitizeHtml, type RichTextEditorOptions, type RichTextEditorInstance, type RichCommand } from './components/rich-text-editor/engine';
|
|
278
|
+
export { createSelect, type SelectOptions, type SelectInstance, type SelectOption } from './components/select/engine';
|
|
279
|
+
export { createAddToCart, type AddToCartOptions, type AddToCartInstance } from './components/add-to-cart/engine';
|
|
280
|
+
export { createScrollIsland, type ScrollIslandOptions, type ScrollIslandInstance } from './components/scroll-island/engine';
|
|
281
|
+
export { createPillButton, type PillButtonOptions, type PillButtonInstance } from './components/pill-button/engine';
|
|
282
|
+
export { createToolbar, type ToolbarOptions, type ToolbarInstance } from './components/toolbar/engine';
|
|
283
|
+
export { createBidirectionalSlider, type BidirectionalSliderOptions, type BidirectionalSliderInstance } from './components/bidirectional-slider/engine';
|
|
284
|
+
export { createVoiceDots, type VoiceDotsOptions, type VoiceDotsInstance, type VoiceState } from './components/voice-dots/engine';
|
|
285
|
+
export { createPieChart, type PieChartOptions, type PieChartInstance, type PieSlice } from './components/pie-chart/engine';
|
|
286
|
+
export { createRadarChart, type RadarChartOptions, type RadarChartInstance, type RadarSeries } from './components/radar-chart/engine';
|
|
287
|
+
export { createRadialChart, type RadialChartOptions, type RadialChartInstance, type RadialDatum } from './components/radial-chart/engine';
|
|
288
|
+
export { createSankeyChart, type SankeyChartOptions, type SankeyChartInstance, type SankeyNode, type SankeyLink } from './components/sankey-chart/engine';
|
|
289
|
+
export { createHeatmapChart, type HeatmapChartOptions, type HeatmapChartInstance, type HeatmapDatum } from './components/heatmap-chart/engine';
|
|
290
|
+
export { createOrbitMenu, type OrbitMenuOptions, type OrbitMenuInstance } from './components/orbit-menu/engine';
|