@human-synthesis/norns-ui 0.0.2 → 0.0.5
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/LICENSE +21 -0
- package/README.md +77 -129
- package/package.json +23 -15
- package/src/auto-import.js +96 -22
- package/src/components/Accordion.n +38 -0
- package/src/components/Audio.n +24 -0
- package/src/components/Autocomplete.n +59 -0
- package/src/components/Avatar.n +27 -0
- package/src/components/AvatarGroup.n +25 -0
- package/src/components/Badge.n +20 -0
- package/src/components/Banner.n +25 -0
- package/src/components/Breadcrumbs.n +22 -0
- package/src/components/Btn.n +29 -8
- package/src/components/ButtonGroup.n +14 -0
- package/src/components/Calendar.n +153 -0
- package/src/components/Card.n +34 -0
- package/src/components/Carousel.n +66 -0
- package/src/components/Chip.n +27 -0
- package/src/components/Collapsible.n +32 -0
- package/src/components/ColorPicker.n +62 -0
- package/src/components/ContextMenu.n +48 -0
- package/src/components/CopyButton.n +50 -0
- package/src/components/DataTable.n +61 -0
- package/src/components/DatePicker.n +68 -0
- package/src/components/DateRangePicker.n +59 -0
- package/src/components/Dialog.n +54 -0
- package/src/components/Drawer.n +49 -0
- package/src/components/Dropdown.n +41 -0
- package/src/components/Field.n +29 -20
- package/src/components/Form.n +22 -0
- package/src/components/GradientText.n +21 -0
- package/src/components/Header.n +23 -0
- package/src/components/HeroBanner.n +37 -0
- package/src/components/HierarchicalMenu.n +38 -0
- package/src/components/Icon.n +16 -0
- package/src/components/Image.n +31 -0
- package/src/components/MegaMenu.n +36 -0
- package/src/components/MultiSelect.n +56 -0
- package/src/components/NumberInput.n +49 -0
- package/src/components/OtpField.n +67 -0
- package/src/components/Pagination.n +73 -0
- package/src/components/Popover.n +32 -0
- package/src/components/PreviewCard.n +30 -0
- package/src/components/Progress.n +26 -0
- package/src/components/ProgressCircular.n +52 -0
- package/src/components/RichTooltip.n +33 -0
- package/src/components/RippleButton.n +56 -0
- package/src/components/ScrollArea.n +31 -0
- package/src/components/Separator.n +12 -0
- package/src/components/Sheet.n +55 -0
- package/src/components/ShinyButton.n +36 -0
- package/src/components/Skeleton.n +27 -0
- package/src/components/Stepper.n +35 -0
- package/src/components/Surface.n +10 -0
- package/src/components/Tabs.n +28 -0
- package/src/components/TagsInput.n +66 -0
- package/src/components/ThemeToggler.n +43 -0
- package/src/components/TimePicker.n +166 -0
- package/src/components/Timeline.n +27 -0
- package/src/components/ToastProvider.n +16 -0
- package/src/components/ToggleButton.n +29 -0
- package/src/components/ToggleButtonGroup.n +34 -0
- package/src/components/Toolbar.n +15 -0
- package/src/components/Tooltip.n +34 -0
- package/src/components/Tree.n +57 -0
- package/src/components/Uploader.n +88 -0
- package/src/components/Video.n +26 -0
- package/src/index.js +76 -0
- package/src/lib/toast.svelte.js +42 -0
- package/src/motion/AnimatedNumber.n +55 -0
- package/src/motion/GradientBackground.n +25 -0
- package/src/motion/LiquidButton.n +48 -0
- package/src/motion/Reveal.n +37 -0
- package/src/motion/Sparkles.n +53 -0
- package/src/motion/index.js +18 -0
- package/src/styles/atoms.css +1794 -71
- package/src/styles/tokens.css +171 -40
- package/src/types/Accordion.d.ts +17 -0
- package/src/types/AnimatedNumber.d.ts +15 -0
- package/src/types/Audio.d.ts +18 -0
- package/src/types/Autocomplete.d.ts +17 -0
- package/src/types/Avatar.d.ts +14 -0
- package/src/types/AvatarGroup.d.ts +15 -0
- package/src/types/Badge.d.ts +14 -0
- package/src/types/Banner.d.ts +15 -0
- package/src/types/Breadcrumbs.d.ts +15 -0
- package/src/types/Btn.d.ts +10 -0
- package/src/types/ButtonGroup.d.ts +10 -0
- package/src/types/Calendar.d.ts +20 -0
- package/src/types/Card.d.ts +19 -0
- package/src/types/Carousel.d.ts +23 -0
- package/src/types/Chip.d.ts +12 -0
- package/src/types/Collapsible.d.ts +12 -0
- package/src/types/ColorPicker.d.ts +15 -0
- package/src/types/ContextMenu.d.ts +19 -0
- package/src/types/CopyButton.d.ts +16 -0
- package/src/types/DataTable.d.ts +27 -0
- package/src/types/DatePicker.d.ts +18 -0
- package/src/types/DateRangePicker.d.ts +17 -0
- package/src/types/Dialog.d.ts +17 -0
- package/src/types/Drawer.d.ts +18 -0
- package/src/types/Dropdown.d.ts +23 -0
- package/src/types/Field.d.ts +14 -3
- package/src/types/Form.d.ts +17 -0
- package/src/types/GradientBackground.d.ts +14 -0
- package/src/types/GradientText.d.ts +19 -0
- package/src/types/Header.d.ts +12 -0
- package/src/types/HeroBanner.d.ts +17 -0
- package/src/types/HierarchicalMenu.d.ts +20 -0
- package/src/types/Icon.d.ts +21 -0
- package/src/types/Image.d.ts +15 -0
- package/src/types/LiquidButton.d.ts +15 -0
- package/src/types/MegaMenu.d.ts +29 -0
- package/src/types/MultiSelect.d.ts +13 -0
- package/src/types/NumberInput.d.ts +17 -0
- package/src/types/OtpField.d.ts +13 -0
- package/src/types/Pagination.d.ts +13 -0
- package/src/types/Popover.d.ts +18 -0
- package/src/types/PreviewCard.d.ts +16 -0
- package/src/types/Progress.d.ts +14 -0
- package/src/types/ProgressCircular.d.ts +14 -0
- package/src/types/Reveal.d.ts +20 -0
- package/src/types/RichTooltip.d.ts +16 -0
- package/src/types/RippleButton.d.ts +15 -0
- package/src/types/ScrollArea.d.ts +11 -0
- package/src/types/Separator.d.ts +9 -0
- package/src/types/Sheet.d.ts +20 -0
- package/src/types/ShinyButton.d.ts +15 -0
- package/src/types/Skeleton.d.ts +13 -0
- package/src/types/Sparkles.d.ts +13 -0
- package/src/types/Stepper.d.ts +19 -0
- package/src/types/Surface.d.ts +10 -0
- package/src/types/Tabs.d.ts +17 -0
- package/src/types/TagsInput.d.ts +17 -0
- package/src/types/ThemeToggler.d.ts +17 -0
- package/src/types/TimePicker.d.ts +20 -0
- package/src/types/Timeline.d.ts +17 -0
- package/src/types/ToastProvider.d.ts +15 -0
- package/src/types/ToggleButton.d.ts +15 -0
- package/src/types/ToggleButtonGroup.d.ts +20 -0
- package/src/types/Toolbar.d.ts +12 -0
- package/src/types/Tooltip.d.ts +15 -0
- package/src/types/Tree.d.ts +20 -0
- package/src/types/Uploader.d.ts +15 -0
- package/src/types/Video.d.ts +20 -0
package/src/index.js
CHANGED
|
@@ -21,7 +21,83 @@ export { default as Select } from './components/Select.n';
|
|
|
21
21
|
export { default as Switch } from './components/Switch.n';
|
|
22
22
|
export { default as Textarea } from './components/Textarea.n';
|
|
23
23
|
|
|
24
|
+
// Phase 3 — behavior tier (Bits UI)
|
|
25
|
+
export { default as Dialog } from './components/Dialog.n';
|
|
26
|
+
export { default as Sheet } from './components/Sheet.n';
|
|
27
|
+
export { default as Popover } from './components/Popover.n';
|
|
28
|
+
export { default as Dropdown } from './components/Dropdown.n';
|
|
29
|
+
export { default as Tooltip } from './components/Tooltip.n';
|
|
30
|
+
export { default as Tabs } from './components/Tabs.n';
|
|
31
|
+
export { default as ToastProvider } from './components/ToastProvider.n';
|
|
32
|
+
|
|
33
|
+
// 0.0.4 — display layer
|
|
34
|
+
export { default as Icon } from './components/Icon.n';
|
|
35
|
+
export { default as Card } from './components/Card.n';
|
|
36
|
+
export { default as Surface } from './components/Surface.n';
|
|
37
|
+
export { default as Banner } from './components/Banner.n';
|
|
38
|
+
export { default as Badge } from './components/Badge.n';
|
|
39
|
+
export { default as Chip } from './components/Chip.n';
|
|
40
|
+
export { default as Avatar } from './components/Avatar.n';
|
|
41
|
+
export { default as Skeleton } from './components/Skeleton.n';
|
|
42
|
+
export { default as Progress } from './components/Progress.n';
|
|
43
|
+
export { default as ProgressCircular } from './components/ProgressCircular.n';
|
|
44
|
+
|
|
45
|
+
// 0.0.4 — composite layer
|
|
46
|
+
export { default as Header } from './components/Header.n';
|
|
47
|
+
export { default as Drawer } from './components/Drawer.n';
|
|
48
|
+
export { default as HeroBanner } from './components/HeroBanner.n';
|
|
49
|
+
export { default as Stepper } from './components/Stepper.n';
|
|
50
|
+
export { default as Breadcrumbs } from './components/Breadcrumbs.n';
|
|
51
|
+
export { default as Pagination } from './components/Pagination.n';
|
|
52
|
+
export { default as Accordion } from './components/Accordion.n';
|
|
53
|
+
export { default as Carousel } from './components/Carousel.n';
|
|
54
|
+
export { default as Tree } from './components/Tree.n';
|
|
55
|
+
|
|
56
|
+
// 0.0.4 — wave A
|
|
57
|
+
export { default as Separator } from './components/Separator.n';
|
|
58
|
+
export { default as Toolbar } from './components/Toolbar.n';
|
|
59
|
+
export { default as ButtonGroup } from './components/ButtonGroup.n';
|
|
60
|
+
export { default as ToggleButton } from './components/ToggleButton.n';
|
|
61
|
+
export { default as ToggleButtonGroup } from './components/ToggleButtonGroup.n';
|
|
62
|
+
export { default as Collapsible } from './components/Collapsible.n';
|
|
63
|
+
export { default as NumberInput } from './components/NumberInput.n';
|
|
64
|
+
export { default as OtpField } from './components/OtpField.n';
|
|
65
|
+
export { default as TagsInput } from './components/TagsInput.n';
|
|
66
|
+
export { default as Image } from './components/Image.n';
|
|
67
|
+
export { default as Audio } from './components/Audio.n';
|
|
68
|
+
export { default as Video } from './components/Video.n';
|
|
69
|
+
export { default as Timeline } from './components/Timeline.n';
|
|
70
|
+
|
|
71
|
+
// 0.0.4 — wave B
|
|
72
|
+
export { default as Autocomplete } from './components/Autocomplete.n';
|
|
73
|
+
export { default as MultiSelect } from './components/MultiSelect.n';
|
|
74
|
+
export { default as ContextMenu } from './components/ContextMenu.n';
|
|
75
|
+
export { default as RichTooltip } from './components/RichTooltip.n';
|
|
76
|
+
export { default as PreviewCard } from './components/PreviewCard.n';
|
|
77
|
+
export { default as ScrollArea } from './components/ScrollArea.n';
|
|
78
|
+
export { default as ColorPicker } from './components/ColorPicker.n';
|
|
79
|
+
export { default as Uploader } from './components/Uploader.n';
|
|
80
|
+
export { default as HierarchicalMenu } from './components/HierarchicalMenu.n';
|
|
81
|
+
export { default as MegaMenu } from './components/MegaMenu.n';
|
|
82
|
+
|
|
83
|
+
// 0.0.4 — wave C
|
|
84
|
+
export { default as DatePicker } from './components/DatePicker.n';
|
|
85
|
+
export { default as DateRangePicker } from './components/DateRangePicker.n';
|
|
86
|
+
export { default as TimePicker } from './components/TimePicker.n';
|
|
87
|
+
export { default as Calendar } from './components/Calendar.n';
|
|
88
|
+
export { default as DataTable } from './components/DataTable.n';
|
|
89
|
+
|
|
90
|
+
// 0.0.5 — CSS-only motion components
|
|
91
|
+
export { default as AvatarGroup } from './components/AvatarGroup.n';
|
|
92
|
+
export { default as GradientText } from './components/GradientText.n';
|
|
93
|
+
export { default as CopyButton } from './components/CopyButton.n';
|
|
94
|
+
export { default as ThemeToggler } from './components/ThemeToggler.n';
|
|
95
|
+
export { default as ShinyButton } from './components/ShinyButton.n';
|
|
96
|
+
export { default as RippleButton } from './components/RippleButton.n';
|
|
97
|
+
// Motion-driven: `import … from '@human-synthesis/norns-ui/motion'`.
|
|
98
|
+
|
|
24
99
|
// Helpers
|
|
25
100
|
export { cn } from './lib/cn.js';
|
|
26
101
|
export { variantClasses } from './lib/variants.js';
|
|
27
102
|
export { presetUI } from './auto-import.js';
|
|
103
|
+
export { toast, notify, dismiss, clear } from './lib/toast.svelte.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast store + helpers. Single in-process queue, mounted once via
|
|
3
|
+
* <ToastProvider>. `toast(msg)` pushes from anywhere — server actions,
|
|
4
|
+
* client effects, button handlers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
let nextId = 0;
|
|
8
|
+
|
|
9
|
+
export const toastStore = $state({ items: [] });
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Push a toast. Returns the toast id so the caller can dismiss early.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} message
|
|
15
|
+
* @param {{ variant?: 'info' | 'success' | 'warning' | 'error', duration?: number }} [opts]
|
|
16
|
+
* `duration` in ms, default 4000. Set to 0 for sticky.
|
|
17
|
+
*/
|
|
18
|
+
export function toast(message, opts = {}) {
|
|
19
|
+
const id = ++nextId;
|
|
20
|
+
const item = {
|
|
21
|
+
id,
|
|
22
|
+
message,
|
|
23
|
+
variant: opts.variant ?? 'info',
|
|
24
|
+
duration: opts.duration ?? 4000
|
|
25
|
+
};
|
|
26
|
+
toastStore.items.push(item);
|
|
27
|
+
if (item.duration > 0) {
|
|
28
|
+
setTimeout(() => dismiss(id), item.duration);
|
|
29
|
+
}
|
|
30
|
+
return id;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const notify = toast;
|
|
34
|
+
|
|
35
|
+
export function dismiss(id) {
|
|
36
|
+
const i = toastStore.items.findIndex((t) => t.id === id);
|
|
37
|
+
if (i >= 0) toastStore.items.splice(i, 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function clear() {
|
|
41
|
+
toastStore.items.length = 0;
|
|
42
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
span(class!="{classes}" bind:this!="{el}")
|
|
2
|
+
| {display}
|
|
3
|
+
|
|
4
|
+
<script>
|
|
5
|
+
import { onMount } from 'svelte'
|
|
6
|
+
import { cn } from '@human-synthesis/norns-ui/cn'
|
|
7
|
+
|
|
8
|
+
{
|
|
9
|
+
value = 0
|
|
10
|
+
from = 0
|
|
11
|
+
duration = 0.9
|
|
12
|
+
decimals = 0
|
|
13
|
+
format
|
|
14
|
+
class: extra = ''
|
|
15
|
+
} := $props()
|
|
16
|
+
|
|
17
|
+
display .= $state String((from ?? value).toFixed(decimals))
|
|
18
|
+
el .= $state null
|
|
19
|
+
hasMounted .= $state false
|
|
20
|
+
|
|
21
|
+
classes := cn 'tabular-nums', extra
|
|
22
|
+
|
|
23
|
+
doFormat := (n) =>
|
|
24
|
+
if format
|
|
25
|
+
format(n)
|
|
26
|
+
else if decimals === 0
|
|
27
|
+
Math.round(n).toLocaleString()
|
|
28
|
+
else
|
|
29
|
+
n.toFixed(decimals)
|
|
30
|
+
|
|
31
|
+
$effect =>
|
|
32
|
+
return unless hasMounted
|
|
33
|
+
// Re-runs whenever `value` changes (after mount).
|
|
34
|
+
v := value
|
|
35
|
+
(async =>
|
|
36
|
+
{ animate } := await import('motion')
|
|
37
|
+
anim := animate from, v, {
|
|
38
|
+
duration
|
|
39
|
+
onUpdate: (latest) => display = doFormat(latest)
|
|
40
|
+
onComplete: => display = doFormat(v)
|
|
41
|
+
}
|
|
42
|
+
anim
|
|
43
|
+
)()
|
|
44
|
+
|
|
45
|
+
onMount =>
|
|
46
|
+
hasMounted = true
|
|
47
|
+
(async =>
|
|
48
|
+
{ animate } := await import('motion')
|
|
49
|
+
animate (from ?? 0), value, {
|
|
50
|
+
duration
|
|
51
|
+
onUpdate: (latest) => display = doFormat(latest)
|
|
52
|
+
onComplete: => display = doFormat(value)
|
|
53
|
+
}
|
|
54
|
+
)()
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.gradient-bg(class!="{classes}" style!="{styleStr}")
|
|
2
|
+
+if('children')
|
|
3
|
+
.relative
|
|
4
|
+
| {@render children()}
|
|
5
|
+
|
|
6
|
+
<script>
|
|
7
|
+
import { cn } from '@human-synthesis/norns-ui/cn'
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
colors = [
|
|
11
|
+
'oklch(60% 0.25 290)'
|
|
12
|
+
'oklch(60% 0.18 240)'
|
|
13
|
+
'oklch(70% 0.18 200)'
|
|
14
|
+
'oklch(60% 0.25 320)'
|
|
15
|
+
]
|
|
16
|
+
duration = 18
|
|
17
|
+
angle = 130
|
|
18
|
+
children
|
|
19
|
+
class: extra = ''
|
|
20
|
+
} := $props()
|
|
21
|
+
|
|
22
|
+
classes := cn 'gradient-bg', extra
|
|
23
|
+
stops := $derived colors.join ', '
|
|
24
|
+
styleStr := $derived `--gradient-bg-stops: ${stops}; --gradient-bg-angle: ${angle}deg; --gradient-bg-duration: ${duration}s`
|
|
25
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
button(
|
|
2
|
+
type!="{type}"
|
|
3
|
+
class!="{classes}"
|
|
4
|
+
disabled!="{disabled}"
|
|
5
|
+
onclick!="{onclick}"
|
|
6
|
+
bind:this!="{el}"
|
|
7
|
+
)
|
|
8
|
+
span.liquid-btn-blob(aria-hidden="true")
|
|
9
|
+
span.liquid-btn-content
|
|
10
|
+
+if('icon')
|
|
11
|
+
Icon(name!="{icon}" size!="{iconSize}")
|
|
12
|
+
+if('children')
|
|
13
|
+
| {@render children()}
|
|
14
|
+
|
|
15
|
+
<script>
|
|
16
|
+
import { onMount } from 'svelte'
|
|
17
|
+
import { cn } from '@human-synthesis/norns-ui/cn'
|
|
18
|
+
import Icon from './../components/Icon.n'
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
variant = 'primary'
|
|
22
|
+
size = 'md'
|
|
23
|
+
type = 'button'
|
|
24
|
+
disabled = false
|
|
25
|
+
onclick
|
|
26
|
+
icon
|
|
27
|
+
children
|
|
28
|
+
class: extra = ''
|
|
29
|
+
} := $props()
|
|
30
|
+
|
|
31
|
+
iconSize := size === 'sm' ? 'size-3.5' : size === 'lg' ? 'size-5' : 'size-4'
|
|
32
|
+
|
|
33
|
+
classes := cn 'btn', `btn-${variant}`, size !== 'md' && `btn-${size}`, 'liquid-btn', extra
|
|
34
|
+
|
|
35
|
+
el .= $state null
|
|
36
|
+
|
|
37
|
+
onMount =>
|
|
38
|
+
return unless el
|
|
39
|
+
(async =>
|
|
40
|
+
{ animate } := await import('motion')
|
|
41
|
+
blob := el.querySelector '.liquid-btn-blob'
|
|
42
|
+
return unless blob
|
|
43
|
+
el.addEventListener 'mouseenter', =>
|
|
44
|
+
animate blob, { transform: ['scale(0.6)', 'scale(1.4)'] }, { duration: 0.6, easing: [0.34, 1.56, 0.64, 1] }
|
|
45
|
+
el.addEventListener 'mouseleave', =>
|
|
46
|
+
animate blob, { transform: 'scale(0.6)' }, { duration: 0.4 }
|
|
47
|
+
)()
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
div(class!="{classes}" style!="{initialStyle}" bind:this!="{el}")
|
|
2
|
+
| {@render children?.()}
|
|
3
|
+
|
|
4
|
+
<script>
|
|
5
|
+
import { onMount } from 'svelte'
|
|
6
|
+
import { cn } from '@human-synthesis/norns-ui/cn'
|
|
7
|
+
|
|
8
|
+
{
|
|
9
|
+
direction = 'up'
|
|
10
|
+
distance = 12
|
|
11
|
+
duration = 0.5
|
|
12
|
+
delay = 0
|
|
13
|
+
once = true
|
|
14
|
+
threshold = 0.15
|
|
15
|
+
children
|
|
16
|
+
class: extra = ''
|
|
17
|
+
} := $props()
|
|
18
|
+
|
|
19
|
+
classes := cn 'will-change-transform', extra
|
|
20
|
+
|
|
21
|
+
axis := direction === 'left' || direction === 'right' ? 'X' : 'Y'
|
|
22
|
+
sign := direction === 'down' || direction === 'right' ? -distance : distance
|
|
23
|
+
|
|
24
|
+
initialStyle := `opacity: 0; transform: translate${axis}(${sign}px)`
|
|
25
|
+
|
|
26
|
+
el .= $state null
|
|
27
|
+
|
|
28
|
+
onMount =>
|
|
29
|
+
return unless el
|
|
30
|
+
(async =>
|
|
31
|
+
{ inView, animate } := await import('motion')
|
|
32
|
+
inView el, =>
|
|
33
|
+
animate el, { opacity: [0, 1], [`transform`]: [`translate${axis}(${sign}px)`, `translate${axis}(0)`] }, { duration, delay }
|
|
34
|
+
once ? false : (=> animate(el, { opacity: 0 }, { duration: 0 }))
|
|
35
|
+
, { amount: threshold }
|
|
36
|
+
)()
|
|
37
|
+
</script>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.sparkles-wrap(class!="{classes}" bind:this!="{wrap}")
|
|
2
|
+
+if('children')
|
|
3
|
+
| {@render children()}
|
|
4
|
+
.sparkles-overlay(aria-hidden="true")
|
|
5
|
+
+each('seeds as s')
|
|
6
|
+
span.sparkle(style!="{`left: ${s.x}%; top: ${s.y}%; width: ${s.size}px; height: ${s.size}px; --sparkle-color: ${s.color}; animation-delay: ${s.delay}s; animation-duration: ${s.duration}s`}")
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import { onMount, onDestroy } from 'svelte'
|
|
10
|
+
import { cn } from '@human-synthesis/norns-ui/cn'
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
density = 14
|
|
14
|
+
colors = ['var(--color-primary-400)', 'var(--color-info-400)', 'var(--color-warning-400)']
|
|
15
|
+
minSize = 4
|
|
16
|
+
maxSize = 10
|
|
17
|
+
children
|
|
18
|
+
class: extra = ''
|
|
19
|
+
} := $props()
|
|
20
|
+
|
|
21
|
+
classes := cn 'sparkles-wrap', extra
|
|
22
|
+
|
|
23
|
+
rand := (min, max) => min + Math.random() * (max - min)
|
|
24
|
+
|
|
25
|
+
seeds := $derived.by =>
|
|
26
|
+
out := []
|
|
27
|
+
i .= 0
|
|
28
|
+
while i < density
|
|
29
|
+
out.push {
|
|
30
|
+
x: rand(2, 96)
|
|
31
|
+
y: rand(2, 96)
|
|
32
|
+
size: Math.round rand(minSize, maxSize)
|
|
33
|
+
color: colors[i % colors.length]
|
|
34
|
+
delay: rand(0, 3)
|
|
35
|
+
duration: rand(1.6, 3.2)
|
|
36
|
+
}
|
|
37
|
+
i++
|
|
38
|
+
out
|
|
39
|
+
|
|
40
|
+
wrap .= $state null
|
|
41
|
+
stopObserver .= $state null
|
|
42
|
+
|
|
43
|
+
onMount =>
|
|
44
|
+
return unless wrap
|
|
45
|
+
// Pause CSS animations off-screen via animation-play-state.
|
|
46
|
+
io := new IntersectionObserver (entries) =>
|
|
47
|
+
for entry of entries
|
|
48
|
+
wrap.classList.toggle 'sparkles-paused', !entry.isIntersecting
|
|
49
|
+
io.observe wrap
|
|
50
|
+
stopObserver = => io.disconnect()
|
|
51
|
+
|
|
52
|
+
onDestroy => stopObserver?.()
|
|
53
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@human-synthesis/norns-ui/motion` — opt-in motion subpath.
|
|
3
|
+
*
|
|
4
|
+
* Importing from this path pulls in [Motion One](https://motion.dev/) (~3 KB
|
|
5
|
+
* gzipped). The default barrel at the package root stays motion-free; if you
|
|
6
|
+
* never import from `/motion` your bundle stays unaffected.
|
|
7
|
+
*
|
|
8
|
+
* Auto-import (via `presetUI()`) deliberately does NOT register these names —
|
|
9
|
+
* we don't want every page to pay the Motion One cost. Import explicitly:
|
|
10
|
+
*
|
|
11
|
+
* import AnimatedNumber from '@human-synthesis/norns-ui/motion/AnimatedNumber';
|
|
12
|
+
* import Reveal from '@human-synthesis/norns-ui/motion/Reveal';
|
|
13
|
+
*/
|
|
14
|
+
export { default as AnimatedNumber } from './AnimatedNumber.n';
|
|
15
|
+
export { default as Reveal } from './Reveal.n';
|
|
16
|
+
export { default as LiquidButton } from './LiquidButton.n';
|
|
17
|
+
export { default as Sparkles } from './Sparkles.n';
|
|
18
|
+
export { default as GradientBackground } from './GradientBackground.n';
|