panelui-web 0.5.0 → 0.7.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/dist/components/avatar/index.d.ts +66 -0
- package/dist/components/avatar/index.js +251 -0
- package/dist/components/flow/index.d.ts +26 -4
- package/dist/components/flow/index.js +81 -20
- package/dist/components/label/index.d.ts +89 -0
- package/dist/components/label/index.js +64 -0
- package/dist/components/message/index.d.ts +161 -0
- package/dist/components/message/index.js +295 -0
- package/dist/components/sheet/index.d.ts +43 -4
- package/dist/components/sheet/index.js +78 -10
- package/dist/components/soundwave/index.d.ts +195 -0
- package/dist/components/soundwave/index.js +732 -0
- package/dist/components/switch/index.d.ts +51 -0
- package/dist/components/switch/index.js +88 -0
- package/dist/components/tabs/index.d.ts +94 -0
- package/dist/components/tabs/index.js +196 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +7 -1
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ export interface SheetProps {
|
|
|
17
17
|
*/
|
|
18
18
|
dismissible?: boolean;
|
|
19
19
|
}
|
|
20
|
-
declare function SheetRoot(
|
|
20
|
+
declare function SheetRoot(props: SheetProps): import("react").JSX.Element;
|
|
21
21
|
declare namespace SheetRoot {
|
|
22
22
|
var displayName: string;
|
|
23
23
|
}
|
|
@@ -28,12 +28,50 @@ export interface SheetTriggerProps extends Omit<ComponentProps<'button'>, 'child
|
|
|
28
28
|
* the click handler and the attributes that tie it to the sheet.
|
|
29
29
|
*/
|
|
30
30
|
render?: ReactElement;
|
|
31
|
+
/**
|
|
32
|
+
* The `name` of a `Sheet.Next` to open instead of the sheet this trigger is
|
|
33
|
+
* in. Any button in a sheet can open the next one this way, wherever it sits.
|
|
34
|
+
*/
|
|
35
|
+
to?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Handed to the `Sheet.Next` that `to` names, as the argument of its
|
|
38
|
+
* children function. Use it to open one sheet for every row of a list.
|
|
39
|
+
*/
|
|
40
|
+
payload?: unknown;
|
|
31
41
|
}
|
|
32
|
-
/**
|
|
33
|
-
|
|
42
|
+
/**
|
|
43
|
+
* What opens a sheet: its own, or with `to`, the `Sheet.Next` of that name.
|
|
44
|
+
* A plain button unless `render` supplies one.
|
|
45
|
+
*/
|
|
46
|
+
declare function SheetTrigger({ render, children, to, payload, ...props }: SheetTriggerProps): import("react").JSX.Element;
|
|
34
47
|
declare namespace SheetTrigger {
|
|
35
48
|
var displayName: string;
|
|
36
49
|
}
|
|
50
|
+
export interface SheetNextProps extends Omit<SheetContentProps, 'children' | 'inline'> {
|
|
51
|
+
/** What a `Sheet.Trigger` passes as `to` to open this sheet. Unique within one stack of sheets. */
|
|
52
|
+
name: string;
|
|
53
|
+
/**
|
|
54
|
+
* The sheet's parts. Pass a function to read the `payload` of the trigger
|
|
55
|
+
* that opened it; it is `undefined` when that trigger has none.
|
|
56
|
+
*/
|
|
57
|
+
children?: ReactNode | ((payload: any) => ReactNode);
|
|
58
|
+
/** The edge it rises from. Default: the edge of the sheet it is declared in. */
|
|
59
|
+
side?: SheetSide;
|
|
60
|
+
/** Whether a click outside, a swipe or Escape closes it. Default true. */
|
|
61
|
+
dismissible?: boolean;
|
|
62
|
+
/** Called when it opens or closes. */
|
|
63
|
+
onOpenChange?: (open: boolean) => void;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A sheet opened from the one it is declared in, by any `Sheet.Trigger` whose
|
|
67
|
+
* `to` is its `name`. Put it anywhere inside the first sheet's content; it
|
|
68
|
+
* takes `Sheet.Content`'s props and draws its own content, so its children are
|
|
69
|
+
* the header, body and footer.
|
|
70
|
+
*/
|
|
71
|
+
declare function SheetNext({ name, children, side, dismissible, onOpenChange, ...content }: SheetNextProps): import("react").JSX.Element;
|
|
72
|
+
declare namespace SheetNext {
|
|
73
|
+
var displayName: string;
|
|
74
|
+
}
|
|
37
75
|
export interface SheetContentProps extends Omit<ComponentProps<'div'>, 'children'> {
|
|
38
76
|
children?: ReactNode;
|
|
39
77
|
/**
|
|
@@ -136,6 +174,7 @@ declare namespace SheetStage {
|
|
|
136
174
|
}
|
|
137
175
|
export declare const Sheet: typeof SheetRoot & {
|
|
138
176
|
Trigger: typeof SheetTrigger;
|
|
177
|
+
Next: typeof SheetNext;
|
|
139
178
|
Content: typeof SheetContent;
|
|
140
179
|
Header: typeof SheetHeader;
|
|
141
180
|
Title: typeof SheetTitle;
|
|
@@ -145,4 +184,4 @@ export declare const Sheet: typeof SheetRoot & {
|
|
|
145
184
|
Close: typeof SheetClose;
|
|
146
185
|
Stage: typeof SheetStage;
|
|
147
186
|
};
|
|
148
|
-
export { SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
187
|
+
export { SheetTrigger, SheetNext, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
@@ -34,6 +34,13 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
34
34
|
* still shows, and dimmed. Only the first sheet darkens the page, so two open
|
|
35
35
|
* sheets do not stack two shades of black.
|
|
36
36
|
*
|
|
37
|
+
* The next sheet is declared once, as `Sheet.Next` with a `name`, and opened
|
|
38
|
+
* by any `Sheet.Trigger to="name"` in the sheet: a row in the body, an icon in
|
|
39
|
+
* the header, a button in the footer. The trigger and the sheet it opens are
|
|
40
|
+
* apart in the markup because they are apart on the screen: a list of rows
|
|
41
|
+
* that each open a sheet should not have to wrap every row in one. A trigger
|
|
42
|
+
* can carry a `payload`, so one `Sheet.Next` serves every row of a list.
|
|
43
|
+
*
|
|
37
44
|
* ## The hand decides
|
|
38
45
|
*
|
|
39
46
|
* Every sheet follows a drag and leaves on a flick toward its own edge. The
|
|
@@ -48,7 +55,7 @@ import { tv } from 'tailwind-variants';
|
|
|
48
55
|
* back, the page receding. A fast start that eases into place reads as weight,
|
|
49
56
|
* where a symmetric ease reads as a slide show.
|
|
50
57
|
*/
|
|
51
|
-
const MOVE = '[transition:transform_450ms_cubic-bezier(0.32,0.72,0,1),border-radius_450ms_cubic-bezier(0.32,0.72,0,1),margin_450ms_cubic-bezier(0.32,0.72,0,1)]';
|
|
58
|
+
const MOVE = '[transition:transform_450ms_cubic-bezier(0.32,0.72,0,1),border-radius_450ms_cubic-bezier(0.32,0.72,0,1),margin_450ms_cubic-bezier(0.32,0.72,0,1),height_450ms_cubic-bezier(0.32,0.72,0,1)]';
|
|
52
59
|
const sheetVariants = tv({
|
|
53
60
|
slots: {
|
|
54
61
|
backdrop: [
|
|
@@ -83,6 +90,17 @@ const sheetVariants = tv({
|
|
|
83
90
|
"after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-popover after:opacity-0 after:transition-opacity after:duration-[450ms] after:content-['']",
|
|
84
91
|
'data-nested-drawer-open:after:opacity-50',
|
|
85
92
|
],
|
|
93
|
+
/*
|
|
94
|
+
* A top or bottom sheet behind another takes the front one's height, so
|
|
95
|
+
* the strip that shows past it is the same whichever of the two is taller.
|
|
96
|
+
* A taller sheet behind would otherwise stand above the stack, and a
|
|
97
|
+
* shorter one would vanish behind the front one entirely.
|
|
98
|
+
*/
|
|
99
|
+
stacked: [
|
|
100
|
+
'data-nested-drawer-open:h-(--drawer-frontmost-height) data-nested-drawer-open:overflow-hidden',
|
|
101
|
+
// Lets the height animate to and from `auto` where the browser can.
|
|
102
|
+
'[interpolate-size:allow-keywords]',
|
|
103
|
+
],
|
|
86
104
|
grabber: [
|
|
87
105
|
'pointer-events-none absolute inset-x-0 z-10 mx-auto flex h-6 w-24 justify-center',
|
|
88
106
|
'before:h-[5px] before:w-9 before:rounded-full before:bg-muted-foreground/35',
|
|
@@ -206,6 +224,19 @@ const SWIPE = {
|
|
|
206
224
|
left: 'left',
|
|
207
225
|
right: 'right',
|
|
208
226
|
};
|
|
227
|
+
function createStack() {
|
|
228
|
+
const handles = new Map();
|
|
229
|
+
return {
|
|
230
|
+
handle(name) {
|
|
231
|
+
let handle = handles.get(name);
|
|
232
|
+
if (!handle) {
|
|
233
|
+
handle = Drawer.createHandle();
|
|
234
|
+
handles.set(name, handle);
|
|
235
|
+
}
|
|
236
|
+
return handle;
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
209
240
|
const SheetContext = createContext(null);
|
|
210
241
|
function useSheet(part) {
|
|
211
242
|
const context = useContext(SheetContext);
|
|
@@ -213,10 +244,16 @@ function useSheet(part) {
|
|
|
213
244
|
throw new Error(`${part} must be used inside a <Sheet>.`);
|
|
214
245
|
return context;
|
|
215
246
|
}
|
|
216
|
-
function SheetRoot(
|
|
247
|
+
function SheetRoot(props) {
|
|
248
|
+
return _jsx(SheetFrame, { ...props });
|
|
249
|
+
}
|
|
250
|
+
SheetRoot.displayName = 'Sheet';
|
|
251
|
+
function SheetFrame({ children, open, onOpenChange, defaultOpen = false, side = 'bottom', dismissible = true, handle: drawerHandle, }) {
|
|
217
252
|
// A sheet inside another sheet's content finds that sheet's context.
|
|
218
253
|
const parent = useContext(SheetContext);
|
|
219
254
|
const nested = parent !== null && !parent.inline;
|
|
255
|
+
const [ownStack] = useState(createStack);
|
|
256
|
+
const stack = nested && parent ? parent.stack : ownStack;
|
|
220
257
|
// Held here as well as by the primitive, so a sheet opened from this one
|
|
221
258
|
// can close it.
|
|
222
259
|
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
@@ -243,6 +280,7 @@ function SheetRoot({ children, open, onOpenChange, defaultOpen = false, side = '
|
|
|
243
280
|
useEffect(() => (nested && parent ? parent.register(handle) : undefined), [nested, parent, handle]);
|
|
244
281
|
const context = useMemo(() => ({
|
|
245
282
|
side,
|
|
283
|
+
stack,
|
|
246
284
|
nested,
|
|
247
285
|
inline: false,
|
|
248
286
|
showClose: true,
|
|
@@ -252,8 +290,8 @@ function SheetRoot({ children, open, onOpenChange, defaultOpen = false, side = '
|
|
|
252
290
|
},
|
|
253
291
|
closeTree: handle.closeTree,
|
|
254
292
|
hasOpenChild: () => [...opened.current].some((child) => child.isOpen()),
|
|
255
|
-
}), [side, nested, handle]);
|
|
256
|
-
return (_jsx(SheetContext.Provider, { value: context, children: _jsx(Drawer.Root, { open: isOpen, swipeDirection: SWIPE[side], disablePointerDismissal: !dismissible, onOpenChange: (next, details) => {
|
|
293
|
+
}), [side, stack, nested, handle]);
|
|
294
|
+
return (_jsx(SheetContext.Provider, { value: context, children: _jsx(Drawer.Root, { open: isOpen, handle: drawerHandle, swipeDirection: SWIPE[side], disablePointerDismissal: !dismissible, onOpenChange: (next, details) => {
|
|
257
295
|
// The primitive has a switch for a press outside and none for
|
|
258
296
|
// Escape or a swipe, so those are refused here.
|
|
259
297
|
if (!next &&
|
|
@@ -263,14 +301,43 @@ function SheetRoot({ children, open, onOpenChange, defaultOpen = false, side = '
|
|
|
263
301
|
return;
|
|
264
302
|
}
|
|
265
303
|
setOpen(next);
|
|
266
|
-
}, children: children
|
|
304
|
+
}, children: typeof children === 'function'
|
|
305
|
+
? ({ payload }) => children(payload)
|
|
306
|
+
: children }) }));
|
|
267
307
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
308
|
+
/**
|
|
309
|
+
* What opens a sheet: its own, or with `to`, the `Sheet.Next` of that name.
|
|
310
|
+
* A plain button unless `render` supplies one.
|
|
311
|
+
*/
|
|
312
|
+
function SheetTrigger({ render, children, to, payload, ...props }) {
|
|
313
|
+
const sheet = useContext(SheetContext);
|
|
314
|
+
if (to !== undefined) {
|
|
315
|
+
if (!sheet)
|
|
316
|
+
throw new Error('Sheet.Trigger with `to` must be used inside a <Sheet>.');
|
|
317
|
+
return (_jsx(Drawer.Trigger, { handle: sheet.stack.handle(to), payload: payload, render: render, ...props, children: children }));
|
|
318
|
+
}
|
|
271
319
|
return (_jsx(Drawer.Trigger, { render: render, ...props, children: children }));
|
|
272
320
|
}
|
|
273
321
|
SheetTrigger.displayName = 'Sheet.Trigger';
|
|
322
|
+
/**
|
|
323
|
+
* A sheet opened from the one it is declared in, by any `Sheet.Trigger` whose
|
|
324
|
+
* `to` is its `name`. Put it anywhere inside the first sheet's content; it
|
|
325
|
+
* takes `Sheet.Content`'s props and draws its own content, so its children are
|
|
326
|
+
* the header, body and footer.
|
|
327
|
+
*/
|
|
328
|
+
function SheetNext({ name, children, side, dismissible, onOpenChange, ...content }) {
|
|
329
|
+
const parent = useSheet('Sheet.Next');
|
|
330
|
+
return (_jsx(SheetFrame, { handle: parent.stack.handle(name), side: side ?? parent.side, dismissible: dismissible, onOpenChange: onOpenChange, children: (payload) => (_jsx(SheetContent, { ...content, children: typeof children === 'function' ? _jsx(NextParts, { render: children, payload: payload }) : children })) }));
|
|
331
|
+
}
|
|
332
|
+
SheetNext.displayName = 'Sheet.Next';
|
|
333
|
+
/**
|
|
334
|
+
* Calls a `Sheet.Next` children function. As a component of its own it only
|
|
335
|
+
* runs once the sheet is open and its content mounted, which is when the
|
|
336
|
+
* trigger has handed over its payload.
|
|
337
|
+
*/
|
|
338
|
+
function NextParts({ render, payload }) {
|
|
339
|
+
return _jsx(_Fragment, { children: render(payload) });
|
|
340
|
+
}
|
|
274
341
|
/** The sheet itself, with the backdrop behind it. */
|
|
275
342
|
function SheetContent({ children, detached = true, size = 'auto', showGrabber, showClose = true, blur = false, inline = false, className, ...props }) {
|
|
276
343
|
const sheet = useSheet('Sheet.Content');
|
|
@@ -291,7 +358,7 @@ function SheetContent({ children, detached = true, size = 'auto', showGrabber, s
|
|
|
291
358
|
onClick: () => {
|
|
292
359
|
if (sheet.hasOpenChild())
|
|
293
360
|
sheet.closeTree();
|
|
294
|
-
} })), _jsx(Drawer.Viewport, { className: slots.viewport(), children: _jsx(Drawer.Popup, { className: slots.popup({ className }), ...props, children: _jsx(SheetContext.Provider, { value: context, children: inside }) }) })] }));
|
|
361
|
+
} })), _jsx(Drawer.Viewport, { className: slots.viewport(), children: _jsx(Drawer.Popup, { className: slots.popup({ className: [vertical && slots.stacked(), className] }), ...props, children: _jsx(SheetContext.Provider, { value: context, children: inside }) }) })] }));
|
|
295
362
|
}
|
|
296
363
|
SheetContent.displayName = 'Sheet.Content';
|
|
297
364
|
/**
|
|
@@ -359,6 +426,7 @@ function SheetStage({ className, children, ...props }) {
|
|
|
359
426
|
SheetStage.displayName = 'Sheet.Stage';
|
|
360
427
|
export const Sheet = Object.assign(SheetRoot, {
|
|
361
428
|
Trigger: SheetTrigger,
|
|
429
|
+
Next: SheetNext,
|
|
362
430
|
Content: SheetContent,
|
|
363
431
|
Header: SheetHeader,
|
|
364
432
|
Title: SheetTitle,
|
|
@@ -368,4 +436,4 @@ export const Sheet = Object.assign(SheetRoot, {
|
|
|
368
436
|
Close: SheetClose,
|
|
369
437
|
Stage: SheetStage,
|
|
370
438
|
});
|
|
371
|
-
export { SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
439
|
+
export { SheetTrigger, SheetNext, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soundwave — what a voice looks like while an app listens to it.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <Soundwave variant="dots" state="listening" level={level} />
|
|
6
|
+
*
|
|
7
|
+
* <Soundwave variant="radial" state="speaking">
|
|
8
|
+
* <Soundwave.Center>
|
|
9
|
+
* <Avatar name="Khalid Abdi" size="xl" />
|
|
10
|
+
* </Soundwave.Center>
|
|
11
|
+
* </Soundwave>
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Seven looks. Four are the app's: `pills` over a microphone button, `bars`
|
|
15
|
+
* for a metering strip, `line` for a travelling wave, `ambient` for a glow
|
|
16
|
+
* behind a whole screen. Three are drawn in dots and spokes: `dots`, a field
|
|
17
|
+
* of dots lit into a wave; `radial`, spokes around a person; and `ring`,
|
|
18
|
+
* rings of dots lit outward by angle.
|
|
19
|
+
*
|
|
20
|
+
* ## It draws a level; it does not record one
|
|
21
|
+
*
|
|
22
|
+
* Nothing here touches the microphone. The app owns the recorder and hands
|
|
23
|
+
* over a number from 0 to 1, so the same wave works against a real meter, a
|
|
24
|
+
* synthesised one or a remote peer's. With no level it runs its own motion
|
|
25
|
+
* for the current `state`, so a voice screen can be built before any audio
|
|
26
|
+
* exists.
|
|
27
|
+
*
|
|
28
|
+
* ## Levels arrive faster than React should render
|
|
29
|
+
*
|
|
30
|
+
* A meter reports dozens of times a second. `level` takes a ref as well as a
|
|
31
|
+
* number: write each reading into `ref.current` and the wave picks it up on
|
|
32
|
+
* its next frame with nothing above it rendering. Each frame writes the `d`
|
|
33
|
+
* of one or two SVG paths, never React state, and the dots are zero-length
|
|
34
|
+
* round-capped segments, so a field of four hundred dots is a single path.
|
|
35
|
+
*
|
|
36
|
+
* Either way the level is smoothed with a fast attack and a slow release,
|
|
37
|
+
* which is what makes it read as a meter: it snaps up on a syllable and falls
|
|
38
|
+
* back gently instead of chattering on every sample.
|
|
39
|
+
*/
|
|
40
|
+
import { type ComponentProps, type ReactNode, type RefObject } from 'react';
|
|
41
|
+
export type SoundwaveVariant = 'pills' | 'bars' | 'line' | 'ambient' | 'dots' | 'radial' | 'ring';
|
|
42
|
+
export type SoundwaveState = 'idle' | 'listening' | 'thinking' | 'speaking';
|
|
43
|
+
/** A role from the theme. The wave never carries a colour of its own. */
|
|
44
|
+
export type SoundwaveColor = 'foreground' | 'primary' | 'info' | 'success' | 'warning' | 'destructive' | 'chart-1' | 'chart-2' | 'chart-3' | 'chart-4' | 'chart-5';
|
|
45
|
+
export interface SoundwaveProps extends Omit<ComponentProps<'div'>, 'color' | 'children'> {
|
|
46
|
+
/**
|
|
47
|
+
* Which look to draw: `pills` for a few capsules over a microphone button,
|
|
48
|
+
* `bars` for a metering strip, `line` for a travelling wave, `ambient` for a
|
|
49
|
+
* glow that fills its parent, `dots` for a field of dots lit into a wave,
|
|
50
|
+
* `radial` for spokes around a centre, and `ring` for rings of dots lit
|
|
51
|
+
* outward by angle.
|
|
52
|
+
*/
|
|
53
|
+
variant?: SoundwaveVariant;
|
|
54
|
+
/**
|
|
55
|
+
* What the app is doing. With no `level` it picks the motion the wave runs
|
|
56
|
+
* on its own, and it always sets what a screen reader announces.
|
|
57
|
+
*/
|
|
58
|
+
state?: SoundwaveState;
|
|
59
|
+
/**
|
|
60
|
+
* How loud the input is now, from 0 to 1. Pass a ref and write each
|
|
61
|
+
* reading into `ref.current` to keep updates out of React entirely. Leave
|
|
62
|
+
* it out and the wave runs its own motion for `state`.
|
|
63
|
+
*/
|
|
64
|
+
level?: number | RefObject<number | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Sound to draw, in place of `level`: a `MediaStream` from a microphone or
|
|
67
|
+
* a call, or an `<audio>` or `<video>` element. A media file has to be on
|
|
68
|
+
* the same site or sent with CORS headers, or the browser hides its sound
|
|
69
|
+
* from the page and the wave stays still.
|
|
70
|
+
*/
|
|
71
|
+
source?: SoundwaveSource | null;
|
|
72
|
+
/**
|
|
73
|
+
* Fixed values, 0 to 1 each: the stored shape of a recording, a frequency
|
|
74
|
+
* analysis, or one value per angle for `ring` (24 for the hours of a day).
|
|
75
|
+
* Resampled to the bar count, and drawn still rather than animated.
|
|
76
|
+
*/
|
|
77
|
+
levels?: readonly number[];
|
|
78
|
+
/**
|
|
79
|
+
* How far a recording has played, from 0 to 1. Bars and dots behind it keep
|
|
80
|
+
* full ink and the rest are dimmed. `bars` and `dots`, with `levels`.
|
|
81
|
+
*/
|
|
82
|
+
progress?: number;
|
|
83
|
+
/** How many capsules, bars or spokes, or how many angles for `ring`. `dots` fits as many columns as the width allows. */
|
|
84
|
+
bars?: number;
|
|
85
|
+
/** Capsule or bar width, the stroke of `line` and `radial`, or the size of a dot. */
|
|
86
|
+
barWidth?: number;
|
|
87
|
+
/** Space between capsules, or between dots. `bars` spaces itself across the width. */
|
|
88
|
+
barGap?: number;
|
|
89
|
+
/** Drawing height. For `radial` and `ring` it is the diameter. `ambient` fills its parent instead. */
|
|
90
|
+
height?: number;
|
|
91
|
+
/**
|
|
92
|
+
* `static` gives every bar a share of the current level; `scrolling` keeps a
|
|
93
|
+
* history that slides across, newest at the trailing edge. `bars` and `dots`.
|
|
94
|
+
*/
|
|
95
|
+
mode?: 'static' | 'scrolling';
|
|
96
|
+
/** Grow bars and dots from the middle out rather than up from the bottom. */
|
|
97
|
+
centered?: boolean;
|
|
98
|
+
/** Fade a strip out at both ends, so it does not stop at a hard edge. */
|
|
99
|
+
fadeEdges?: boolean;
|
|
100
|
+
/** Multiplier on the incoming level, applied before it is capped at 1. */
|
|
101
|
+
sensitivity?: number;
|
|
102
|
+
/** Multiplier on the wave's own tempo, including how fast a history scrolls. */
|
|
103
|
+
speed?: number;
|
|
104
|
+
/**
|
|
105
|
+
* The ink, as a role from the theme. Left unset the wave is drawn in the
|
|
106
|
+
* text colour of whatever it sits in, which is right on most surfaces;
|
|
107
|
+
* `ambient` defaults to `info`, since a glow in the text colour reads as a
|
|
108
|
+
* shadow.
|
|
109
|
+
*/
|
|
110
|
+
color?: SoundwaveColor;
|
|
111
|
+
/**
|
|
112
|
+
* Colours across the wave instead of one ink, as theme roles: left to right
|
|
113
|
+
* for the strips, up from the bottom for `ambient`.
|
|
114
|
+
*/
|
|
115
|
+
gradient?: readonly SoundwaveColor[];
|
|
116
|
+
/**
|
|
117
|
+
* The colour of the unlit dots in `dots` and `ring`, and of the part of a
|
|
118
|
+
* recording not yet played in `bars`. Defaults to the ink, faint.
|
|
119
|
+
*/
|
|
120
|
+
trackColor?: SoundwaveColor;
|
|
121
|
+
/** Hold the current frame. */
|
|
122
|
+
paused?: boolean;
|
|
123
|
+
/** What a screen reader calls it. Defaults to the state: "Listening". */
|
|
124
|
+
label?: string;
|
|
125
|
+
/** For `radial` and `ring`: a `Soundwave.Center` holding what sits in the middle. */
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
}
|
|
128
|
+
/** A voice as a wave. Draws a `level` it is handed, or runs its own motion for `state`. */
|
|
129
|
+
declare function SoundwaveRoot({ variant, state, level: levelProp, source, levels, progress, bars, barWidth, barGap, height, mode, centered, fadeEdges, sensitivity, speed, color, gradient, trackColor, paused, label, className, style, children, ...props }: SoundwaveProps): import("react").JSX.Element;
|
|
130
|
+
declare namespace SoundwaveRoot {
|
|
131
|
+
var displayName: string;
|
|
132
|
+
}
|
|
133
|
+
export interface SoundwaveCenterProps extends ComponentProps<'div'> {
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* What sits in the hole of a `radial` or `ring` wave: a person's avatar, a
|
|
137
|
+
* figure, a button. It is sized to the hole and centred in it.
|
|
138
|
+
*/
|
|
139
|
+
declare function SoundwaveCenter({ className, ...props }: SoundwaveCenterProps): import("react").JSX.Element;
|
|
140
|
+
declare namespace SoundwaveCenter {
|
|
141
|
+
var displayName: string;
|
|
142
|
+
}
|
|
143
|
+
export declare const Soundwave: typeof SoundwaveRoot & {
|
|
144
|
+
Center: typeof SoundwaveCenter;
|
|
145
|
+
};
|
|
146
|
+
export { SoundwaveCenter };
|
|
147
|
+
/** Something with sound in it: a microphone or call stream, or an `<audio>` or `<video>` element. */
|
|
148
|
+
export type SoundwaveSource = MediaStream | HTMLMediaElement;
|
|
149
|
+
export interface AudioLevelOptions {
|
|
150
|
+
/**
|
|
151
|
+
* The quietest sound that still moves the wave, in decibels below full
|
|
152
|
+
* scale. Default `-60`. Raise it towards `-45` in a noisy room.
|
|
153
|
+
*/
|
|
154
|
+
floor?: number;
|
|
155
|
+
/** The loudness that fills the wave, in decibels below full scale. Default `-12`. */
|
|
156
|
+
ceiling?: number;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* How loud `source` is, from 0 to 1, kept in a ref that is updated every
|
|
160
|
+
* frame. Pass the ref to `Soundwave` as `level`, or read `ref.current`
|
|
161
|
+
* yourself. With no source the ref holds 0.
|
|
162
|
+
*/
|
|
163
|
+
export declare function useAudioLevel(source: SoundwaveSource | null | undefined, { floor, ceiling }?: AudioLevelOptions): RefObject<number>;
|
|
164
|
+
export type MicrophoneStatus = 'idle' | 'requesting' | 'listening' | 'denied' | 'unsupported';
|
|
165
|
+
export interface UseMicrophoneOptions extends AudioLevelOptions {
|
|
166
|
+
/** Passed to `getUserMedia` as `audio`, to pick a device or change its processing. */
|
|
167
|
+
constraints?: MediaTrackConstraints;
|
|
168
|
+
}
|
|
169
|
+
export interface MicrophoneControls {
|
|
170
|
+
/** How loud the microphone is, from 0 to 1. Pass it to `Soundwave` as `level`. */
|
|
171
|
+
level: RefObject<number>;
|
|
172
|
+
/** The live stream while listening, for a recorder or a call. `null` otherwise. */
|
|
173
|
+
stream: MediaStream | null;
|
|
174
|
+
/** `requesting` while the browser asks for permission, `denied` if it was refused or there is no microphone. */
|
|
175
|
+
status: MicrophoneStatus;
|
|
176
|
+
/** Whether it is listening now. */
|
|
177
|
+
active: boolean;
|
|
178
|
+
/** Ask for the microphone and start listening. Call it from a click: browsers only allow sound to start from one. */
|
|
179
|
+
start: () => Promise<void>;
|
|
180
|
+
/** Stop listening and release the microphone, which turns the browser's recording light off. */
|
|
181
|
+
stop: () => void;
|
|
182
|
+
/** `start` when stopped, `stop` when listening. */
|
|
183
|
+
toggle: () => void;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The microphone, ready to draw. Nothing is asked for until `start`, and the
|
|
187
|
+
* microphone is released on `stop` and when the component unmounts.
|
|
188
|
+
*
|
|
189
|
+
* ```tsx
|
|
190
|
+
* const mic = useMicrophone();
|
|
191
|
+
* <Soundwave level={mic.active ? mic.level : undefined} />
|
|
192
|
+
* <Button onClick={mic.toggle}>{mic.active ? 'Stop' : 'Speak'}</Button>
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export declare function useMicrophone({ constraints, ...options }?: UseMicrophoneOptions): MicrophoneControls;
|