@nsaa/core 0.1.0-alpha.1 → 0.1.0-alpha.2
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/accordion/accordion.d.ts +7 -0
- package/dist/accordion/accordion.js +54 -0
- package/dist/accordion/index.d.ts +3 -0
- package/dist/alert/alert.d.ts +18 -0
- package/dist/alert/alert.js +53 -0
- package/dist/alert/index.d.ts +3 -0
- package/dist/alert-dialog/alert-dialog.d.ts +19 -0
- package/dist/alert-dialog/alert-dialog.js +100 -0
- package/dist/alert-dialog/index.d.ts +3 -0
- package/dist/avatar/avatar.d.ts +12 -0
- package/dist/avatar/avatar.js +50 -0
- package/dist/avatar/index.d.ts +3 -0
- package/dist/badge/badge.d.ts +8 -0
- package/dist/badge/badge.js +29 -0
- package/dist/badge/index.d.ts +3 -0
- package/dist/button/button.d.ts +25 -0
- package/dist/button/button.js +136 -0
- package/dist/button/index.d.ts +3 -0
- package/dist/calendar/calendar.d.ts +11 -0
- package/dist/calendar/calendar.js +131 -0
- package/dist/calendar/index.d.ts +3 -0
- package/dist/card/card.d.ts +12 -0
- package/dist/card/card.js +55 -0
- package/dist/card/index.d.ts +3 -0
- package/dist/checkbox/checkbox.d.ts +4 -0
- package/dist/checkbox/checkbox.js +35 -0
- package/dist/checkbox/index.d.ts +3 -0
- package/dist/collapsible/collapsible.d.ts +6 -0
- package/dist/collapsible/collapsible.js +24 -0
- package/dist/collapsible/index.d.ts +3 -0
- package/dist/combobox/combobox.d.ts +27 -0
- package/dist/combobox/combobox.js +226 -0
- package/dist/combobox/index.d.ts +3 -0
- package/dist/command/command.d.ts +20 -0
- package/dist/command/command.js +116 -0
- package/dist/command/index.d.ts +3 -0
- package/dist/context-menu/context-menu.d.ts +30 -0
- package/dist/context-menu/context-menu.js +173 -0
- package/dist/context-menu/index.d.ts +3 -0
- package/dist/dialog/dialog.d.ts +18 -0
- package/dist/dialog/dialog.js +106 -0
- package/dist/dialog/index.d.ts +3 -0
- package/dist/direction.d.ts +3 -0
- package/dist/direction.js +2 -0
- package/dist/drawer/drawer.d.ts +17 -0
- package/dist/drawer/drawer.js +136 -0
- package/dist/drawer/index.d.ts +3 -0
- package/dist/dropdown-menu/dropdown-menu.d.ts +30 -0
- package/dist/dropdown-menu/dropdown-menu.js +177 -0
- package/dist/dropdown-menu/index.d.ts +3 -0
- package/dist/empty/empty.d.ts +35 -0
- package/dist/empty/empty.js +93 -0
- package/dist/empty/index.d.ts +3 -0
- package/dist/field/field.d.ts +26 -0
- package/dist/field/field.js +109 -0
- package/dist/field/index.d.ts +3 -0
- package/dist/index.d.ts +89 -674
- package/dist/index.js +48 -3876
- package/dist/input/index.d.ts +3 -0
- package/dist/input/input.d.ts +4 -0
- package/dist/input/input.js +14 -0
- package/dist/input-group/index.d.ts +3 -0
- package/dist/input-group/input-group.d.ts +19 -0
- package/dist/input-group/input-group.js +78 -0
- package/dist/input-otp/index.d.ts +3 -0
- package/dist/input-otp/input-otp.d.ts +12 -0
- package/dist/input-otp/input-otp.js +73 -0
- package/dist/kbd/index.d.ts +3 -0
- package/dist/kbd/kbd.d.ts +5 -0
- package/dist/kbd/kbd.js +19 -0
- package/dist/label/index.d.ts +3 -0
- package/dist/label/label.d.ts +4 -0
- package/dist/label/label.js +13 -0
- package/dist/legacy/icon-button/icon-swap.js +58 -0
- package/dist/lib/cn.d.ts +3 -0
- package/dist/{cn-DxFBFdwT.js → lib/cn.js} +1 -1
- package/dist/lib/control-defaults.js +8 -0
- package/dist/lib/icon-swap.d.ts +15 -0
- package/dist/lib/icon-swap.js +26 -0
- package/dist/lib/input-modality.js +39 -0
- package/dist/lib/merge-refs.js +21 -0
- package/dist/lib/shortcut.js +50 -0
- package/dist/lib/track-input-modality.d.ts +2 -0
- package/dist/lib/track-input-modality.js +4 -0
- package/dist/lib/tv.js +7 -0
- package/dist/lib/use-press.js +48 -0
- package/dist/lib/user-action.js +13 -0
- package/dist/loaders/arc.d.ts +6 -0
- package/dist/loaders/arc.js +31 -0
- package/dist/loaders/atom.d.ts +5 -0
- package/dist/loaders/atom.js +70 -0
- package/dist/loaders/blocks.d.ts +8 -0
- package/dist/loaders/blocks.js +74 -0
- package/dist/loaders/bouncing-dots.d.ts +3 -0
- package/dist/loaders/bouncing-dots.js +45 -0
- package/dist/loaders/cap.d.ts +6 -0
- package/dist/loaders/cap.js +7 -0
- package/dist/loaders/cascade.d.ts +5 -0
- package/dist/loaders/cascade.js +50 -0
- package/dist/loaders/circular-dots.d.ts +3 -0
- package/dist/loaders/circular-dots.js +40 -0
- package/dist/loaders/classic-v2.d.ts +3 -0
- package/dist/loaders/classic-v2.js +41 -0
- package/dist/loaders/classic.d.ts +3 -0
- package/dist/loaders/classic.js +48 -0
- package/dist/loaders/clock.d.ts +5 -0
- package/dist/loaders/clock.js +31 -0
- package/dist/loaders/comet.d.ts +5 -0
- package/dist/loaders/comet.js +52 -0
- package/dist/loaders/compass.d.ts +3 -0
- package/dist/loaders/compass.js +39 -0
- package/dist/loaders/dual.d.ts +6 -0
- package/dist/loaders/dual.js +47 -0
- package/dist/loaders/easing.d.ts +6 -0
- package/dist/loaders/easing.js +34 -0
- package/dist/loaders/eclipse.d.ts +3 -0
- package/dist/loaders/eclipse.js +69 -0
- package/dist/loaders/fade.js +28 -0
- package/dist/loaders/flip.d.ts +3 -0
- package/dist/loaders/flip.js +41 -0
- package/dist/{frame-CK9IBOZB.js → loaders/frame.js} +2 -50
- package/dist/loaders/gather.d.ts +3 -0
- package/dist/loaders/gather.js +85 -0
- package/dist/loaders/leap.d.ts +3 -0
- package/dist/loaders/leap.js +67 -0
- package/dist/loaders/linear-dots.d.ts +3 -0
- package/dist/loaders/linear-dots.js +54 -0
- package/dist/loaders/loading.d.ts +3 -0
- package/dist/loaders/loading.js +64 -0
- package/dist/loaders/morph.d.ts +3 -0
- package/dist/loaders/morph.js +40 -0
- package/dist/loaders/motion.d.ts +35 -0
- package/dist/loaders/motion.js +51 -0
- package/dist/loaders/orbit.d.ts +5 -0
- package/dist/loaders/orbit.js +44 -0
- package/dist/loaders/pulse.d.ts +3 -0
- package/dist/loaders/pulse.js +52 -0
- package/dist/loaders/radar.d.ts +5 -0
- package/dist/loaders/radar.js +64 -0
- package/dist/loaders/ring.d.ts +6 -0
- package/dist/loaders/ring.js +38 -0
- package/dist/loaders/ripple.d.ts +8 -0
- package/dist/loaders/ripple.js +57 -0
- package/dist/loaders/slide.d.ts +3 -0
- package/dist/loaders/slide.js +74 -0
- package/dist/loaders/snake.d.ts +6 -0
- package/dist/loaders/snake.js +59 -0
- package/dist/loaders/spinners.d.ts +61 -0
- package/dist/loaders/spinners.js +63 -0
- package/dist/loaders/swirl.d.ts +3 -0
- package/dist/loaders/swirl.js +50 -0
- package/dist/loaders/text/shared.js +25 -0
- package/dist/loaders/text/text-loaders.d.ts +48 -0
- package/dist/loaders/text/text-loaders.js +723 -0
- package/dist/loaders/trace.d.ts +6 -0
- package/dist/loaders/trace.js +46 -0
- package/dist/loaders/types.d.ts +10 -0
- package/dist/loaders/wave.d.ts +8 -0
- package/dist/loaders/wave.js +59 -0
- package/dist/loaders-text.d.ts +2 -47
- package/dist/loaders-text.js +1 -743
- package/dist/loaders.d.ts +36 -132
- package/dist/loaders.js +35 -1096
- package/dist/native-select/index.d.ts +3 -0
- package/dist/native-select/native-select.d.ts +9 -0
- package/dist/native-select/native-select.js +44 -0
- package/dist/pagination/index.d.ts +3 -0
- package/dist/pagination/pagination.d.ts +18 -0
- package/dist/pagination/pagination.js +134 -0
- package/dist/popover/index.d.ts +3 -0
- package/dist/popover/popover.d.ts +10 -0
- package/dist/popover/popover.js +54 -0
- package/dist/radio-group/index.d.ts +3 -0
- package/dist/radio-group/radio-group.d.ts +6 -0
- package/dist/radio-group/radio-group.js +33 -0
- package/dist/select/index.d.ts +3 -0
- package/dist/select/select.d.ts +16 -0
- package/dist/select/select.js +148 -0
- package/dist/separator/index.d.ts +3 -0
- package/dist/separator/separator.d.ts +4 -0
- package/dist/separator/separator.js +15 -0
- package/dist/skeleton/index.d.ts +3 -0
- package/dist/skeleton/skeleton.d.ts +10 -0
- package/dist/skeleton/skeleton.js +24 -0
- package/dist/slider/index.d.ts +3 -0
- package/dist/slider/slider.d.ts +4 -0
- package/dist/slider/slider.js +39 -0
- package/dist/sound/sound-map.d.ts +70 -0
- package/dist/sound/sound-map.js +72 -0
- package/dist/sound/sound.d.ts +38 -0
- package/dist/sound/sound.js +136 -0
- package/dist/sound/uisfx.d.ts +37 -0
- package/dist/sound/uisfx.js +59 -0
- package/dist/sound-uisfx.d.ts +2 -37
- package/dist/sound-uisfx.js +1 -58
- package/dist/spinner/index.d.ts +5 -0
- package/dist/spinner/loader-provider.d.ts +16 -0
- package/dist/spinner/loader-provider.js +28 -0
- package/dist/spinner/registry.d.ts +9 -0
- package/dist/spinner/registry.js +15 -0
- package/dist/spinner/spinner.d.ts +26 -0
- package/dist/spinner/spinner.js +143 -0
- package/dist/switch/index.d.ts +3 -0
- package/dist/switch/switch.d.ts +6 -0
- package/dist/switch/switch.js +25 -0
- package/dist/table/index.d.ts +3 -0
- package/dist/table/table.d.ts +17 -0
- package/dist/table/table.js +66 -0
- package/dist/tabs/index.d.ts +3 -0
- package/dist/tabs/tabs.d.ts +11 -0
- package/dist/tabs/tabs.js +46 -0
- package/dist/textarea/index.d.ts +3 -0
- package/dist/textarea/textarea.d.ts +4 -0
- package/dist/textarea/textarea.js +12 -0
- package/dist/toast/index.d.ts +3 -0
- package/dist/toast/toast.d.ts +17 -0
- package/dist/toast/toast.js +211 -0
- package/dist/toggle-group/index.d.ts +3 -0
- package/dist/toggle-group/toggle-group.d.ts +64 -0
- package/dist/toggle-group/toggle-group.js +95 -0
- package/dist/tokens/dist/motion.d.ts +24 -0
- package/dist/{motion-NQq2BA78.js → tokens/dist/motion.js} +1 -1
- package/dist/toolbar/index.d.ts +3 -0
- package/dist/toolbar/overflow.d.ts +4 -0
- package/dist/toolbar/overflow.js +242 -0
- package/dist/toolbar/toolbar-item.d.ts +29 -0
- package/dist/toolbar/toolbar-item.js +71 -0
- package/dist/toolbar/toolbar.d.ts +73 -0
- package/dist/toolbar/toolbar.js +250 -0
- package/dist/tooltip/index.d.ts +3 -0
- package/dist/tooltip/tooltip.d.ts +7 -0
- package/dist/tooltip/tooltip.js +41 -0
- package/package.json +4 -3
- package/dist/registry-2PyPW9Xw.js +0 -390
- package/dist/registry-DjfWNw3R.d.ts +0 -18
- package/dist/sound-DiYNAnR_.d.ts +0 -61
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
|
|
2
|
+
import "./sound.js";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
/**
|
|
5
|
+
* The one table of interface sounds: component × event → cue (feel layer, §4). Components play
|
|
6
|
+
* from here, so the map is reviewable in one place. Surfaces that aren't listed (Popover, Tooltip,
|
|
7
|
+
* Command, menu open and close, Select open, Tabs, Accordion, Collapsible, inputs) are silent: opening a light surface isn't a commit,
|
|
8
|
+
* and the palette is a 100+/day tool. Nothing plays on hover, load or typing.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SOUND_MAP: {
|
|
11
|
+
readonly button: {
|
|
12
|
+
readonly press: "input.press";
|
|
13
|
+
};
|
|
14
|
+
readonly checkbox: {
|
|
15
|
+
readonly on: "input.toggle-on";
|
|
16
|
+
readonly off: "input.toggle-off";
|
|
17
|
+
};
|
|
18
|
+
readonly "radio-group": {
|
|
19
|
+
readonly change: "selection.select";
|
|
20
|
+
};
|
|
21
|
+
readonly switch: {
|
|
22
|
+
readonly on: "input.toggle-on";
|
|
23
|
+
readonly off: "input.toggle-off";
|
|
24
|
+
};
|
|
25
|
+
readonly slider: {
|
|
26
|
+
readonly commit: "movement.snap";
|
|
27
|
+
};
|
|
28
|
+
readonly "input-otp": {
|
|
29
|
+
readonly invalid: "feedback.error";
|
|
30
|
+
};
|
|
31
|
+
readonly alert: {
|
|
32
|
+
readonly destructive: "feedback.error";
|
|
33
|
+
};
|
|
34
|
+
readonly dialog: {
|
|
35
|
+
readonly open: "navigation.open";
|
|
36
|
+
readonly close: "navigation.close";
|
|
37
|
+
};
|
|
38
|
+
readonly "alert-dialog": {
|
|
39
|
+
readonly open: "navigation.open";
|
|
40
|
+
readonly close: "navigation.close";
|
|
41
|
+
};
|
|
42
|
+
readonly drawer: {
|
|
43
|
+
readonly open: "navigation.open";
|
|
44
|
+
readonly close: "navigation.close";
|
|
45
|
+
readonly snap: "movement.snap";
|
|
46
|
+
};
|
|
47
|
+
readonly select: {
|
|
48
|
+
readonly choose: "selection.select";
|
|
49
|
+
};
|
|
50
|
+
readonly calendar: {
|
|
51
|
+
readonly select: "selection.select";
|
|
52
|
+
};
|
|
53
|
+
readonly combobox: {
|
|
54
|
+
readonly choose: "selection.select";
|
|
55
|
+
readonly clear: "input.press";
|
|
56
|
+
readonly "chip-remove": "input.press";
|
|
57
|
+
};
|
|
58
|
+
readonly "dropdown-menu": {
|
|
59
|
+
readonly check: "selection.select";
|
|
60
|
+
};
|
|
61
|
+
readonly "context-menu": {
|
|
62
|
+
readonly check: "selection.select";
|
|
63
|
+
};
|
|
64
|
+
readonly toast: {
|
|
65
|
+
readonly success: "feedback.success";
|
|
66
|
+
readonly warning: "feedback.warning";
|
|
67
|
+
readonly error: "feedback.error";
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
export type SoundComponent = keyof typeof SOUND_MAP;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSound } from "./sound.js";
|
|
3
|
+
import { createContext, createElement, useCallback, useContext } from "react";
|
|
4
|
+
// src/sound/sound-map.ts
|
|
5
|
+
/**
|
|
6
|
+
* The one table of interface sounds: component × event → cue (feel layer, §4). Components play
|
|
7
|
+
* from here, so the map is reviewable in one place. Surfaces that aren't listed (Popover, Tooltip,
|
|
8
|
+
* Command, menu open and close, Select open, Tabs, Accordion, Collapsible, inputs) are silent: opening a light surface isn't a commit,
|
|
9
|
+
* and the palette is a 100+/day tool. Nothing plays on hover, load or typing.
|
|
10
|
+
*/
|
|
11
|
+
const SOUND_MAP = {
|
|
12
|
+
button: { press: "input.press" },
|
|
13
|
+
checkbox: {
|
|
14
|
+
on: "input.toggle-on",
|
|
15
|
+
off: "input.toggle-off"
|
|
16
|
+
},
|
|
17
|
+
"radio-group": { change: "selection.select" },
|
|
18
|
+
switch: {
|
|
19
|
+
on: "input.toggle-on",
|
|
20
|
+
off: "input.toggle-off"
|
|
21
|
+
},
|
|
22
|
+
slider: { commit: "movement.snap" },
|
|
23
|
+
"input-otp": { invalid: "feedback.error" },
|
|
24
|
+
alert: { destructive: "feedback.error" },
|
|
25
|
+
dialog: {
|
|
26
|
+
open: "navigation.open",
|
|
27
|
+
close: "navigation.close"
|
|
28
|
+
},
|
|
29
|
+
"alert-dialog": {
|
|
30
|
+
open: "navigation.open",
|
|
31
|
+
close: "navigation.close"
|
|
32
|
+
},
|
|
33
|
+
drawer: {
|
|
34
|
+
open: "navigation.open",
|
|
35
|
+
close: "navigation.close",
|
|
36
|
+
snap: "movement.snap"
|
|
37
|
+
},
|
|
38
|
+
select: { choose: "selection.select" },
|
|
39
|
+
calendar: { select: "selection.select" },
|
|
40
|
+
combobox: {
|
|
41
|
+
choose: "selection.select",
|
|
42
|
+
clear: "input.press",
|
|
43
|
+
"chip-remove": "input.press"
|
|
44
|
+
},
|
|
45
|
+
"dropdown-menu": { check: "selection.select" },
|
|
46
|
+
"context-menu": { check: "selection.select" },
|
|
47
|
+
toast: {
|
|
48
|
+
success: "feedback.success",
|
|
49
|
+
warning: "feedback.warning",
|
|
50
|
+
error: "feedback.error"
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const SilentContext = createContext(false);
|
|
54
|
+
/** Silences the sound map below it (CommandDialog reuses Dialog but stays silent, §4). */
|
|
55
|
+
function SoundSilence({ children }) {
|
|
56
|
+
return createElement(SilentContext.Provider, { value: true }, children);
|
|
57
|
+
}
|
|
58
|
+
/** Plays a component's mapped cue for an event, through `useSound()` and its safeguards. */
|
|
59
|
+
function useCue(component) {
|
|
60
|
+
const { play } = useSound();
|
|
61
|
+
const silent = useContext(SilentContext);
|
|
62
|
+
return useCallback((event, options) => {
|
|
63
|
+
if (silent) return;
|
|
64
|
+
const cue = SOUND_MAP[component][event];
|
|
65
|
+
if (cue) play(cue, options);
|
|
66
|
+
}, [
|
|
67
|
+
component,
|
|
68
|
+
play,
|
|
69
|
+
silent
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
export { SOUND_MAP, SoundSilence, useCue };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
import { sound } from "../tokens/dist/motion.js";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
/** Cue names come from the tokens (`sound.cue` in packages/tokens/src/effects.json). */
|
|
5
|
+
export type SoundCue = keyof typeof sound.cue;
|
|
6
|
+
/**
|
|
7
|
+
* Plays a cue. App-supplied, so the library makes no network calls and the sound engine
|
|
8
|
+
* (UISFX or anything else) stays swappable. `asset` is the engine's cue name from the tokens.
|
|
9
|
+
*/
|
|
10
|
+
export type SoundPlayer = (cue: SoundCue, options: {
|
|
11
|
+
asset: string;
|
|
12
|
+
volume: number;
|
|
13
|
+
}) => void | (() => void);
|
|
14
|
+
export interface PlayOptions {
|
|
15
|
+
/** Give way to any other cue played during the same event (a Button press under a Dialog close). */
|
|
16
|
+
yield?: boolean | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface SoundProviderProps {
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
/** Plays cues. Without one every cue is silent (the current stub behaviour). */
|
|
21
|
+
player?: SoundPlayer | undefined;
|
|
22
|
+
/** Apps can turn the default off (decision #30). */
|
|
23
|
+
enabled?: boolean | undefined;
|
|
24
|
+
volume?: number | undefined;
|
|
25
|
+
}
|
|
26
|
+
/** Global sound settings: one remembered mute for the whole app. */
|
|
27
|
+
export declare function SoundProvider({ children, player, enabled, volume }: SoundProviderProps): import("react").JSX.Element;
|
|
28
|
+
/**
|
|
29
|
+
* Plays interface sounds with manners: only from a user action (call `play` in an event handler),
|
|
30
|
+
* never when muted, turned off, or when the user asks for reduced motion; repeats are throttled.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useSound(): {
|
|
33
|
+
play: (cue: SoundCue, options?: PlayOptions) => void;
|
|
34
|
+
loop: (cue: SoundCue) => (() => void);
|
|
35
|
+
muted: boolean;
|
|
36
|
+
setMuted: (muted: boolean) => void;
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { sound } from "../tokens/dist/motion.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { useReducedMotionConfig } from "motion/react";
|
|
6
|
+
// src/sound/sound.tsx
|
|
7
|
+
/** One moment per player (per SoundProvider), outside React state: one cue per moment (#71). */
|
|
8
|
+
const moments = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
function momentOf(player) {
|
|
10
|
+
let moment = moments.get(player);
|
|
11
|
+
if (!moment) {
|
|
12
|
+
moment = {
|
|
13
|
+
lastAt: 0,
|
|
14
|
+
pending: null
|
|
15
|
+
};
|
|
16
|
+
moments.set(player, moment);
|
|
17
|
+
}
|
|
18
|
+
return moment;
|
|
19
|
+
}
|
|
20
|
+
const MUTE_KEY = "nsaa-sound-muted";
|
|
21
|
+
function readMuted() {
|
|
22
|
+
try {
|
|
23
|
+
return localStorage.getItem(MUTE_KEY) === "true";
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function writeMuted(muted) {
|
|
29
|
+
try {
|
|
30
|
+
localStorage.setItem(MUTE_KEY, String(muted));
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
33
|
+
const SoundContext = createContext(null);
|
|
34
|
+
/** The app's own switch on <html data-motion> (feel.css reads it too): "reduce" silences sound,
|
|
35
|
+
* "full" overrides the OS setting. */
|
|
36
|
+
function htmlMotion() {
|
|
37
|
+
return typeof document === "undefined" ? void 0 : document.documentElement.dataset.motion;
|
|
38
|
+
}
|
|
39
|
+
/** Global sound settings: one remembered mute for the whole app. */
|
|
40
|
+
function SoundProvider({ children, player, enabled = sound.enabled, volume = sound.volume }) {
|
|
41
|
+
const [muted, setMutedState] = useState(readMuted);
|
|
42
|
+
const setMuted = useCallback((next) => {
|
|
43
|
+
setMutedState(next);
|
|
44
|
+
writeMuted(next);
|
|
45
|
+
}, []);
|
|
46
|
+
const value = useMemo(() => ({
|
|
47
|
+
enabled,
|
|
48
|
+
volume,
|
|
49
|
+
muted,
|
|
50
|
+
setMuted,
|
|
51
|
+
player
|
|
52
|
+
}), [
|
|
53
|
+
enabled,
|
|
54
|
+
volume,
|
|
55
|
+
muted,
|
|
56
|
+
setMuted,
|
|
57
|
+
player
|
|
58
|
+
]);
|
|
59
|
+
return /* @__PURE__ */ jsx(SoundContext.Provider, {
|
|
60
|
+
value,
|
|
61
|
+
children
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Plays interface sounds with manners: only from a user action (call `play` in an event handler),
|
|
66
|
+
* never when muted, turned off, or when the user asks for reduced motion; repeats are throttled.
|
|
67
|
+
*/
|
|
68
|
+
function useSound() {
|
|
69
|
+
const context = useContext(SoundContext);
|
|
70
|
+
const reducedMotion = useReducedMotionConfig() ?? false;
|
|
71
|
+
const enabled = context?.enabled ?? sound.enabled;
|
|
72
|
+
const muted = context?.muted ?? false;
|
|
73
|
+
const player = context?.player;
|
|
74
|
+
const volume = context?.volume ?? sound.volume;
|
|
75
|
+
const last = useRef(null);
|
|
76
|
+
return {
|
|
77
|
+
play: useCallback((cue, options) => {
|
|
78
|
+
if (!enabled || muted || !player) return;
|
|
79
|
+
const html = htmlMotion();
|
|
80
|
+
if (html === "reduce" || reducedMotion && html !== "full") return;
|
|
81
|
+
const moment = momentOf(player);
|
|
82
|
+
const fire = () => {
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
if (last.current?.cue === cue && now - last.current.at < 80) return;
|
|
85
|
+
last.current = {
|
|
86
|
+
cue,
|
|
87
|
+
at: now
|
|
88
|
+
};
|
|
89
|
+
moment.lastAt = now;
|
|
90
|
+
player(cue, {
|
|
91
|
+
asset: sound.cue[cue],
|
|
92
|
+
volume
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
if (!options?.yield) {
|
|
96
|
+
if (moment.pending) clearTimeout(moment.pending);
|
|
97
|
+
moment.pending = null;
|
|
98
|
+
fire();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const scheduledAt = Date.now();
|
|
102
|
+
if (moment.pending) clearTimeout(moment.pending);
|
|
103
|
+
moment.pending = setTimeout(() => {
|
|
104
|
+
moment.pending = null;
|
|
105
|
+
if (scheduledAt - moment.lastAt < 100) return;
|
|
106
|
+
fire();
|
|
107
|
+
}, 0);
|
|
108
|
+
}, [
|
|
109
|
+
enabled,
|
|
110
|
+
muted,
|
|
111
|
+
reducedMotion,
|
|
112
|
+
player,
|
|
113
|
+
volume
|
|
114
|
+
]),
|
|
115
|
+
loop: useCallback((cue) => {
|
|
116
|
+
if (!enabled || muted || !player) return () => {};
|
|
117
|
+
const html = htmlMotion();
|
|
118
|
+
if (html === "reduce" || reducedMotion && html !== "full") return () => {};
|
|
119
|
+
const stop = player(cue, {
|
|
120
|
+
asset: sound.cue[cue],
|
|
121
|
+
volume
|
|
122
|
+
});
|
|
123
|
+
return typeof stop === "function" ? stop : () => {};
|
|
124
|
+
}, [
|
|
125
|
+
enabled,
|
|
126
|
+
muted,
|
|
127
|
+
reducedMotion,
|
|
128
|
+
player,
|
|
129
|
+
volume
|
|
130
|
+
]),
|
|
131
|
+
muted,
|
|
132
|
+
setMuted: context?.setMuted ?? writeMuted,
|
|
133
|
+
enabled
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export { SoundProvider, useSound };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
import { SoundPlayer } from "./sound.js";
|
|
3
|
+
import { PackName } from "uisfx";
|
|
4
|
+
export type SoundPack = PackName;
|
|
5
|
+
/** Every UISFX pack, for pickers. */
|
|
6
|
+
export declare const soundPacks: readonly SoundPack[];
|
|
7
|
+
export interface UisfxPlayerOptions {
|
|
8
|
+
/** UISFX pack (sound family). Defaults to the `sound.feel` token. */
|
|
9
|
+
pack?: SoundPack | undefined;
|
|
10
|
+
/** Where to listen for the first user gesture. Defaults to `window`. */
|
|
11
|
+
target?: EventTarget | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** A `SoundPlayer` for `SoundProvider`, plus controls for the engine. */
|
|
14
|
+
export interface UisfxSoundPlayer extends SoundPlayer {
|
|
15
|
+
setPack: (pack: SoundPack) => void;
|
|
16
|
+
getPack: () => SoundPack;
|
|
17
|
+
/** True once a trusted pointer or key action has happened (or `unlock` was called from one). */
|
|
18
|
+
isUnlocked: () => boolean;
|
|
19
|
+
/** Unlock audio from your own user-gesture handler. Never call it on load. */
|
|
20
|
+
unlock: () => void;
|
|
21
|
+
/** Stops sounds, removes listeners and closes the audio context. */
|
|
22
|
+
destroy: () => Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
|
|
26
|
+
* Manners: nothing plays, and no audio context is created, until the first trusted pointer or
|
|
27
|
+
* key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
|
|
28
|
+
*
|
|
29
|
+
* Create one per app, at module scope (not inside a component, where StrictMode or re-renders
|
|
30
|
+
* would make extra engines):
|
|
31
|
+
*
|
|
32
|
+
* ```tsx
|
|
33
|
+
* const player = createUisfxPlayer({ pack: "soft" });
|
|
34
|
+
* <SoundProvider player={player}>…</SoundProvider>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function createUisfxPlayer({ pack, target }?: UisfxPlayerOptions): UisfxSoundPlayer;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { sound } from "../tokens/dist/motion.js";
|
|
3
|
+
import { createUISFX, cueNames, getPlaybackMode, packNames } from "uisfx";
|
|
4
|
+
// src/sound/uisfx.ts
|
|
5
|
+
/** Every UISFX pack, for pickers. */
|
|
6
|
+
const soundPacks = packNames;
|
|
7
|
+
const GESTURES = ["pointerdown", "keydown"];
|
|
8
|
+
const CUES = new Set(cueNames);
|
|
9
|
+
function isPack(value) {
|
|
10
|
+
return packNames.includes(value);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
|
|
14
|
+
* Manners: nothing plays, and no audio context is created, until the first trusted pointer or
|
|
15
|
+
* key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
|
|
16
|
+
*
|
|
17
|
+
* Create one per app, at module scope (not inside a component, where StrictMode or re-renders
|
|
18
|
+
* would make extra engines):
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const player = createUisfxPlayer({ pack: "soft" });
|
|
22
|
+
* <SoundProvider player={player}>…</SoundProvider>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function createUisfxPlayer({ pack, target } = {}) {
|
|
26
|
+
const initialPack = pack ?? (isPack(sound.feel) ? sound.feel : "minimal");
|
|
27
|
+
const engine = createUISFX({
|
|
28
|
+
pack: initialPack,
|
|
29
|
+
volume: sound.volume
|
|
30
|
+
});
|
|
31
|
+
const listenOn = target ?? (typeof window === "undefined" ? void 0 : window);
|
|
32
|
+
let unlocked = false;
|
|
33
|
+
const unlock = () => {
|
|
34
|
+
if (unlocked) return;
|
|
35
|
+
unlocked = true;
|
|
36
|
+
for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
|
|
37
|
+
engine.unlock();
|
|
38
|
+
};
|
|
39
|
+
function onGesture(event) {
|
|
40
|
+
if (event.isTrusted) unlock();
|
|
41
|
+
}
|
|
42
|
+
for (const type of GESTURES) listenOn?.addEventListener(type, onGesture, true);
|
|
43
|
+
const play = ((_cue, { asset, volume }) => {
|
|
44
|
+
if (!unlocked || !CUES.has(asset)) return;
|
|
45
|
+
if (engine.getVolume() !== volume) engine.setVolume(volume);
|
|
46
|
+
const playing = engine.play(asset);
|
|
47
|
+
if (getPlaybackMode(asset) === "loop") return () => playing?.stop();
|
|
48
|
+
});
|
|
49
|
+
play.setPack = (next) => engine.setPack(next);
|
|
50
|
+
play.getPack = () => engine.getPack();
|
|
51
|
+
play.isUnlocked = () => unlocked;
|
|
52
|
+
play.unlock = unlock;
|
|
53
|
+
play.destroy = async () => {
|
|
54
|
+
for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
|
|
55
|
+
await engine.destroy();
|
|
56
|
+
};
|
|
57
|
+
return play;
|
|
58
|
+
}
|
|
59
|
+
export { createUisfxPlayer, soundPacks };
|
package/dist/sound-uisfx.d.ts
CHANGED
|
@@ -1,38 +1,3 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
type SoundPack = PackName;
|
|
5
|
-
/** Every UISFX pack, for pickers. */
|
|
6
|
-
export declare const soundPacks: readonly SoundPack[];
|
|
7
|
-
interface UisfxPlayerOptions {
|
|
8
|
-
/** UISFX pack (sound family). Defaults to the `sound.feel` token. */
|
|
9
|
-
pack?: SoundPack | undefined;
|
|
10
|
-
/** Where to listen for the first user gesture. Defaults to `window`. */
|
|
11
|
-
target?: EventTarget | undefined;
|
|
12
|
-
}
|
|
13
|
-
/** A `SoundPlayer` for `SoundProvider`, plus controls for the engine. */
|
|
14
|
-
interface UisfxSoundPlayer extends SoundPlayer {
|
|
15
|
-
setPack: (pack: SoundPack) => void;
|
|
16
|
-
getPack: () => SoundPack;
|
|
17
|
-
/** True once a trusted pointer or key action has happened (or `unlock` was called from one). */
|
|
18
|
-
isUnlocked: () => boolean;
|
|
19
|
-
/** Unlock audio from your own user-gesture handler. Never call it on load. */
|
|
20
|
-
unlock: () => void;
|
|
21
|
-
/** Stops sounds, removes listeners and closes the audio context. */
|
|
22
|
-
destroy: () => Promise<void>;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
|
|
26
|
-
* Manners: nothing plays, and no audio context is created, until the first trusted pointer or
|
|
27
|
-
* key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
|
|
28
|
-
*
|
|
29
|
-
* Create one per app, at module scope (not inside a component, where StrictMode or re-renders
|
|
30
|
-
* would make extra engines):
|
|
31
|
-
*
|
|
32
|
-
* ```tsx
|
|
33
|
-
* const player = createUisfxPlayer({ pack: "soft" });
|
|
34
|
-
* <SoundProvider player={player}>…</SoundProvider>
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export declare function createUisfxPlayer({ pack, target }?: UisfxPlayerOptions): UisfxSoundPlayer;
|
|
38
|
-
export type { SoundPack, UisfxPlayerOptions, UisfxSoundPlayer };
|
|
2
|
+
import { SoundPack, UisfxPlayerOptions, UisfxSoundPlayer, createUisfxPlayer, soundPacks } from "./sound/uisfx.js";
|
|
3
|
+
export { type SoundPack, type UisfxPlayerOptions, type UisfxSoundPlayer, createUisfxPlayer, soundPacks };
|
package/dist/sound-uisfx.js
CHANGED
|
@@ -1,59 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
3
|
-
import { createUISFX, cueNames, getPlaybackMode, packNames } from "uisfx";
|
|
4
|
-
// src/sound/uisfx.ts
|
|
5
|
-
/** Every UISFX pack, for pickers. */
|
|
6
|
-
const soundPacks = packNames;
|
|
7
|
-
const GESTURES = ["pointerdown", "keydown"];
|
|
8
|
-
const CUES = new Set(cueNames);
|
|
9
|
-
function isPack(value) {
|
|
10
|
-
return packNames.includes(value);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
|
|
14
|
-
* Manners: nothing plays, and no audio context is created, until the first trusted pointer or
|
|
15
|
-
* key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
|
|
16
|
-
*
|
|
17
|
-
* Create one per app, at module scope (not inside a component, where StrictMode or re-renders
|
|
18
|
-
* would make extra engines):
|
|
19
|
-
*
|
|
20
|
-
* ```tsx
|
|
21
|
-
* const player = createUisfxPlayer({ pack: "soft" });
|
|
22
|
-
* <SoundProvider player={player}>…</SoundProvider>
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
function createUisfxPlayer({ pack, target } = {}) {
|
|
26
|
-
const initialPack = pack ?? (isPack(sound.feel) ? sound.feel : "minimal");
|
|
27
|
-
const engine = createUISFX({
|
|
28
|
-
pack: initialPack,
|
|
29
|
-
volume: sound.volume
|
|
30
|
-
});
|
|
31
|
-
const listenOn = target ?? (typeof window === "undefined" ? void 0 : window);
|
|
32
|
-
let unlocked = false;
|
|
33
|
-
const unlock = () => {
|
|
34
|
-
if (unlocked) return;
|
|
35
|
-
unlocked = true;
|
|
36
|
-
for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
|
|
37
|
-
engine.unlock();
|
|
38
|
-
};
|
|
39
|
-
function onGesture(event) {
|
|
40
|
-
if (event.isTrusted) unlock();
|
|
41
|
-
}
|
|
42
|
-
for (const type of GESTURES) listenOn?.addEventListener(type, onGesture, true);
|
|
43
|
-
const play = ((_cue, { asset, volume }) => {
|
|
44
|
-
if (!unlocked || !CUES.has(asset)) return;
|
|
45
|
-
if (engine.getVolume() !== volume) engine.setVolume(volume);
|
|
46
|
-
const playing = engine.play(asset);
|
|
47
|
-
if (getPlaybackMode(asset) === "loop") return () => playing?.stop();
|
|
48
|
-
});
|
|
49
|
-
play.setPack = (next) => engine.setPack(next);
|
|
50
|
-
play.getPack = () => engine.getPack();
|
|
51
|
-
play.isUnlocked = () => unlocked;
|
|
52
|
-
play.unlock = unlock;
|
|
53
|
-
play.destroy = async () => {
|
|
54
|
-
for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
|
|
55
|
-
await engine.destroy();
|
|
56
|
-
};
|
|
57
|
-
return play;
|
|
58
|
-
}
|
|
59
|
-
export { createUisfxPlayer, soundPacks };
|
|
2
|
+
export { createUisfxPlayer, soundPacks } from "./sound/uisfx.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
import { LoaderProvider, LoaderProviderProps } from "./loader-provider.js";
|
|
3
|
+
import { LoaderComponent, registerLoader, registerLoaders } from "./registry.js";
|
|
4
|
+
import { Spinner, SpinnerProps, SpinnerVariant } from "./spinner.js";
|
|
5
|
+
export { type LoaderComponent, LoaderProvider, type LoaderProviderProps, Spinner, type SpinnerProps, type SpinnerVariant, registerLoader, registerLoaders };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
export interface LoaderProviderProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
/** Pauses every loader inside (sets `data-loader-paused`, which the play state follows). */
|
|
6
|
+
paused?: boolean | undefined;
|
|
7
|
+
/** One speed for every loader inside, in ms. */
|
|
8
|
+
duration?: number | undefined;
|
|
9
|
+
/** Default Spinner label (e.g. a translation of "Loading"). */
|
|
10
|
+
label?: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Cascades the loader play state and speed to a region (`display: contents`, so it adds no box) and
|
|
14
|
+
* gives its Spinners a default label.
|
|
15
|
+
*/
|
|
16
|
+
export declare function LoaderProvider({ children, paused, duration, label }: LoaderProviderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
// src/spinner/loader-provider.tsx
|
|
5
|
+
const LoaderContext = createContext({});
|
|
6
|
+
function useLoaderContext() {
|
|
7
|
+
return useContext(LoaderContext);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Cascades the loader play state and speed to a region (`display: contents`, so it adds no box) and
|
|
11
|
+
* gives its Spinners a default label.
|
|
12
|
+
*/
|
|
13
|
+
function LoaderProvider({ children, paused, duration, label }) {
|
|
14
|
+
const style = {
|
|
15
|
+
display: "contents",
|
|
16
|
+
...duration === void 0 ? null : { "--nsaa-loader-duration": `${duration}ms` }
|
|
17
|
+
};
|
|
18
|
+
return /* @__PURE__ */ jsx(LoaderContext.Provider, {
|
|
19
|
+
value: { label },
|
|
20
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
21
|
+
"data-slot": "loader-provider",
|
|
22
|
+
"data-loader-paused": paused ? "" : void 0,
|
|
23
|
+
style,
|
|
24
|
+
children
|
|
25
|
+
})
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export { LoaderProvider, useLoaderContext };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
import { SpinnerProps } from "../loaders/types.js";
|
|
3
|
+
import { ComponentType } from "react";
|
|
4
|
+
/** A loader: a decorative, `aria-hidden` glyph painted with `currentColor` (the loader contract). */
|
|
5
|
+
export type LoaderComponent = ComponentType<SpinnerProps>;
|
|
6
|
+
/** Makes a loader available as `<Spinner variant={name} />`. A later registration replaces an earlier one. */
|
|
7
|
+
export declare function registerLoader(name: string, component: LoaderComponent): void;
|
|
8
|
+
/** Registers a map of loaders, e.g. `registerLoaders(LOADERS)` for all 29 from `@nsaa/core/loaders`. */
|
|
9
|
+
export declare function registerLoaders(loaders: Record<string, LoaderComponent>): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/spinner/registry.ts
|
|
3
|
+
const registry = /* @__PURE__ */ new Map();
|
|
4
|
+
/** Makes a loader available as `<Spinner variant={name} />`. A later registration replaces an earlier one. */
|
|
5
|
+
function registerLoader(name, component) {
|
|
6
|
+
registry.set(name, component);
|
|
7
|
+
}
|
|
8
|
+
/** Registers a map of loaders, e.g. `registerLoaders(LOADERS)` for all 29 from `@nsaa/core/loaders`. */
|
|
9
|
+
function registerLoaders(loaders) {
|
|
10
|
+
for (const [name, component] of Object.entries(loaders)) registry.set(name, component);
|
|
11
|
+
}
|
|
12
|
+
function getLoader(name) {
|
|
13
|
+
return registry.get(name);
|
|
14
|
+
}
|
|
15
|
+
export { getLoader, registerLoader, registerLoaders };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
import { LoaderSize } from "../loaders/types.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
export type SpinnerVariant = "arc" | "ring" | "classic" | "circular-dots" | "bouncing-dots" | "pulse" | "orbit" | "wave" | "lucide" | (string & {});
|
|
5
|
+
export interface SpinnerProps extends Omit<React.ComponentProps<"span">, "children"> {
|
|
6
|
+
/** A registered loader name (the featured 8 built in, more via registerLoader) or "lucide". */
|
|
7
|
+
variant?: SpinnerVariant | undefined;
|
|
8
|
+
/** A token size (xs 12, sm 14, md 16, lg 20, xl 24) or pixels. */
|
|
9
|
+
size?: LoaderSize | number | undefined;
|
|
10
|
+
/** Announced, and shown as text under reduced motion. Default: the LoaderProvider's, else "Loading". */
|
|
11
|
+
label?: string | undefined;
|
|
12
|
+
/** Wait before showing, ms (loader.delay, 150; 0 inside a control). */
|
|
13
|
+
delay?: number | undefined;
|
|
14
|
+
/** Once shown, stay at least this long after `loading` turns false, ms (loader.min-display, 500). */
|
|
15
|
+
minDisplay?: number | undefined;
|
|
16
|
+
/** Turn false to hide it, honouring `minDisplay` (unmounting it hides it at once). Default true. */
|
|
17
|
+
loading?: boolean | undefined;
|
|
18
|
+
/** One turn, ms. Default: the loader's token. */
|
|
19
|
+
duration?: number | undefined;
|
|
20
|
+
/** Hold the animation. Offscreen and hidden-tab spinners pause on their own. */
|
|
21
|
+
paused?: boolean | undefined;
|
|
22
|
+
/** Play `progress.loop` while shown; it stops when the Spinner hides or unmounts. Default false. */
|
|
23
|
+
sound?: boolean | undefined;
|
|
24
|
+
}
|
|
25
|
+
declare function Spinner({ variant, size, label, delay: delayProp, minDisplay, loading, duration, paused, sound, className, style, ...props }: SpinnerProps): React.JSX.Element | null;
|
|
26
|
+
export { Spinner };
|