svelte-p5-components 0.4.1 → 0.6.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 +20 -0
- package/dist/ActivityBar.svelte +227 -0
- package/dist/ActivityBar.svelte.d.ts +66 -0
- package/dist/ContextMenu.svelte +439 -0
- package/dist/ContextMenu.svelte.d.ts +80 -0
- package/dist/EntityToggleList.svelte +175 -21
- package/dist/EntityToggleList.svelte.d.ts +7 -0
- package/dist/SidePanel.svelte +272 -0
- package/dist/SidePanel.svelte.d.ts +59 -0
- package/dist/Sketch.svelte +20 -18
- package/dist/Sketch.svelte.d.ts +23 -12
- package/dist/Sketch.test.svelte.d.ts +1 -0
- package/dist/Sketch.test.svelte.js +102 -0
- package/dist/TimelineScrubber.svelte +223 -70
- package/dist/TimelineScrubber.svelte.d.ts +32 -8
- package/dist/TimelineScrubber.test.svelte.js +4 -3
- package/dist/TimelineTrack.svelte +483 -150
- package/dist/TimelineTrack.svelte.d.ts +33 -18
- package/dist/TimelineTrack.test.svelte.js +70 -9
- package/dist/createMediaPlayback.svelte.d.ts +60 -0
- package/dist/createMediaPlayback.svelte.js +169 -0
- package/dist/createMediaPlayback.test.d.ts +1 -0
- package/dist/createMediaPlayback.test.js +176 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +4 -0
- package/dist/sketch-types.d.ts +28 -0
- package/dist/sketch-types.js +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ Higher-level Svelte 5 components built on [`svelte-p5`](../core). The pieces tha
|
|
|
8
8
|
pnpm add svelte-p5 svelte-p5-components p5
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
To test a change that's merged on `main` but not yet released, install the preview build from [pkg.pr.new](https://pkg.pr.new/):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add \
|
|
15
|
+
https://pkg.pr.new/edw1nzhao/svelte-p5/svelte-p5@main \
|
|
16
|
+
https://pkg.pr.new/edw1nzhao/svelte-p5/svelte-p5-components@main
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Pin to a SHA (`@<commit-sha>`) instead of `@main` for reproducible installs.
|
|
20
|
+
|
|
11
21
|
## `<Sketch>`
|
|
12
22
|
|
|
13
23
|
`<P5Canvas>` plus a `ResizeObserver` on the parent element and automatic `pixelDensity(devicePixelRatio)`. The default you probably want.
|
|
@@ -31,6 +41,16 @@ pnpm add svelte-p5 svelte-p5-components p5
|
|
|
31
41
|
</div>
|
|
32
42
|
```
|
|
33
43
|
|
|
44
|
+
Props:
|
|
45
|
+
|
|
46
|
+
| Prop | Type | Default | Notes |
|
|
47
|
+
| ---------- | ------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| `sketch` | `SketchFn<Ext>` | — | Required. Generic over custom instance members (see the core README). |
|
|
49
|
+
| `hidpi` | `boolean \| number` | `true` | `true` → `devicePixelRatio`, `false` → `pixelDensity(1)`, number → that exact density (cap for large WEBGL canvases: `Math.min(devicePixelRatio, 2)`). |
|
|
50
|
+
| `instance` | `ExtendedP5<Ext> \| null` (bindable) | `null` | The p5 instance once mounted. |
|
|
51
|
+
| `onReady` | `(instance) => void` | — | Fires once after creation, density, and initial sizing. |
|
|
52
|
+
| `onResize` | `(instance, width, height) => void` | — | Fires after each `ResizeObserver`-driven `resizeCanvas` (not the initial sizing). Rebuild size-dependent state here; call `instance.loop()` if you're noLoop-gated. |
|
|
53
|
+
|
|
34
54
|
## `<FPSMonitor>`
|
|
35
55
|
|
|
36
56
|
Absolute-positioned FPS readout. Takes the p5 instance and samples `p.frameRate()` every 30 frames by default.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* One item rendered as an icon button in the rail.
|
|
6
|
+
*/
|
|
7
|
+
export interface ActivityBarItem {
|
|
8
|
+
/** Stable id. Passed back via `onSelect` and used for `activeId`. */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Snippet that renders the icon. Called with no arguments. */
|
|
11
|
+
icon: Snippet;
|
|
12
|
+
/** Accessible label + tooltip title. */
|
|
13
|
+
label: string;
|
|
14
|
+
/** Optional numeric badge rendered over the icon. */
|
|
15
|
+
badge?: number;
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* Vertical activity-bar rail (VS Code pattern). Renders a stack of
|
|
22
|
+
* square icon buttons wired into a `role="tablist"` group. One item
|
|
23
|
+
* can be "active"; callers own that state via the `activeId` prop.
|
|
24
|
+
*
|
|
25
|
+
* Visual contract: 48×48 square buttons. Active state renders an
|
|
26
|
+
* accent-colored left border + subtle background tint. Hover is a
|
|
27
|
+
* faint background. All class names are author-written (not scoped
|
|
28
|
+
* hashes) so consumers can safely target them via `:global(...)`:
|
|
29
|
+
*
|
|
30
|
+
* .activity-bar
|
|
31
|
+
* .activity-bar__item
|
|
32
|
+
* .activity-bar__item--active
|
|
33
|
+
* .activity-bar__badge
|
|
34
|
+
*
|
|
35
|
+
* Keyboard: Up/Down arrow moves focus between buttons (wraps); Enter
|
|
36
|
+
* or Space activates the focused item.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```svelte
|
|
40
|
+
* <ActivityBar
|
|
41
|
+
* activeId={active}
|
|
42
|
+
* items={[
|
|
43
|
+
* { id: 'viz', label: 'Visualizations', icon: vizIcon },
|
|
44
|
+
* { id: 'filters', label: 'Filters', icon: filtersIcon }
|
|
45
|
+
* ]}
|
|
46
|
+
* onSelect={(id) => (active = active === id ? undefined : id)}
|
|
47
|
+
* />
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
interface Props {
|
|
51
|
+
/** The items to render. */
|
|
52
|
+
items: ActivityBarItem[];
|
|
53
|
+
/**
|
|
54
|
+
* The id of the currently-active item. When undefined, no item
|
|
55
|
+
* shows the active treatment. Bindable.
|
|
56
|
+
*/
|
|
57
|
+
activeId?: string;
|
|
58
|
+
/** Click / keyboard-activation callback. */
|
|
59
|
+
onSelect?: (id: string) => void;
|
|
60
|
+
/** Rail width in px. Default: 48. */
|
|
61
|
+
width?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Rail orientation. Only `'vertical'` is supported today; the prop
|
|
64
|
+
* is kept so future work can add horizontal without breaking API.
|
|
65
|
+
*/
|
|
66
|
+
orientation?: 'vertical';
|
|
67
|
+
class?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let {
|
|
71
|
+
items,
|
|
72
|
+
activeId = $bindable<string | undefined>(undefined),
|
|
73
|
+
onSelect,
|
|
74
|
+
width = 48,
|
|
75
|
+
orientation = 'vertical',
|
|
76
|
+
class: className = ''
|
|
77
|
+
}: Props = $props();
|
|
78
|
+
|
|
79
|
+
let rootEl: HTMLDivElement | null = $state(null);
|
|
80
|
+
|
|
81
|
+
function handleItemClick(id: string) {
|
|
82
|
+
onSelect?.(id);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
86
|
+
if (!rootEl) return;
|
|
87
|
+
const buttons = Array.from(rootEl.querySelectorAll<HTMLButtonElement>('.activity-bar__item'));
|
|
88
|
+
if (buttons.length === 0) return;
|
|
89
|
+
|
|
90
|
+
const current = document.activeElement as HTMLElement | null;
|
|
91
|
+
const idx = current ? buttons.indexOf(current as HTMLButtonElement) : -1;
|
|
92
|
+
|
|
93
|
+
if (event.key === 'ArrowDown' || (orientation === 'vertical' && event.key === 'ArrowRight')) {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
const next = idx < 0 ? 0 : (idx + 1) % buttons.length;
|
|
96
|
+
buttons[next]?.focus();
|
|
97
|
+
} else if (
|
|
98
|
+
event.key === 'ArrowUp' ||
|
|
99
|
+
(orientation === 'vertical' && event.key === 'ArrowLeft')
|
|
100
|
+
) {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
const next = idx < 0 ? buttons.length - 1 : (idx - 1 + buttons.length) % buttons.length;
|
|
103
|
+
buttons[next]?.focus();
|
|
104
|
+
} else if (event.key === 'Enter' || event.key === ' ') {
|
|
105
|
+
if (idx >= 0) {
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
const id = items[idx]?.id;
|
|
108
|
+
if (id) onSelect?.(id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<div
|
|
115
|
+
bind:this={rootEl}
|
|
116
|
+
class="activity-bar {className}"
|
|
117
|
+
role="tablist"
|
|
118
|
+
aria-orientation={orientation}
|
|
119
|
+
style:width="{width}px"
|
|
120
|
+
onkeydown={handleKeydown}
|
|
121
|
+
tabindex="-1"
|
|
122
|
+
>
|
|
123
|
+
{#each items as item (item.id)}
|
|
124
|
+
{@const isActive = item.id === activeId}
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
class="activity-bar__item {isActive ? 'activity-bar__item--active' : ''}"
|
|
128
|
+
role="tab"
|
|
129
|
+
aria-selected={isActive}
|
|
130
|
+
aria-label={item.label}
|
|
131
|
+
title={item.label}
|
|
132
|
+
tabindex={isActive ? 0 : -1}
|
|
133
|
+
onclick={() => handleItemClick(item.id)}
|
|
134
|
+
>
|
|
135
|
+
<span class="activity-bar__icon">
|
|
136
|
+
{@render item.icon()}
|
|
137
|
+
</span>
|
|
138
|
+
{#if item.badge !== undefined && item.badge > 0}
|
|
139
|
+
<span class="activity-bar__badge" aria-hidden="true">{item.badge}</span>
|
|
140
|
+
{/if}
|
|
141
|
+
</button>
|
|
142
|
+
{/each}
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<style>
|
|
146
|
+
.activity-bar {
|
|
147
|
+
display: flex;
|
|
148
|
+
flex-direction: column;
|
|
149
|
+
align-items: stretch;
|
|
150
|
+
flex: 0 0 auto;
|
|
151
|
+
height: 100%;
|
|
152
|
+
background: var(--activity-bar-bg, #f3f3f3);
|
|
153
|
+
border-right: 1px solid var(--activity-bar-border, rgba(0, 0, 0, 0.08));
|
|
154
|
+
overflow: hidden;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.activity-bar__item {
|
|
158
|
+
position: relative;
|
|
159
|
+
display: inline-flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
width: 48px;
|
|
163
|
+
height: 48px;
|
|
164
|
+
padding: 0;
|
|
165
|
+
margin: 0;
|
|
166
|
+
border: none;
|
|
167
|
+
background: transparent;
|
|
168
|
+
color: var(--activity-bar-fg, #555);
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
transition:
|
|
171
|
+
background-color 120ms ease,
|
|
172
|
+
color 120ms ease;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.activity-bar__item:hover {
|
|
176
|
+
background: var(--activity-bar-bg-hover, rgba(0, 0, 0, 0.05));
|
|
177
|
+
color: var(--activity-bar-fg-hover, #111);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.activity-bar__item:focus-visible {
|
|
181
|
+
outline: 2px solid var(--activity-bar-focus, rgba(59, 130, 246, 0.55));
|
|
182
|
+
outline-offset: -2px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.activity-bar__item--active {
|
|
186
|
+
color: var(--activity-bar-fg-active, #111);
|
|
187
|
+
background: var(--activity-bar-bg-active, rgba(59, 130, 246, 0.08));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.activity-bar__item--active::before {
|
|
191
|
+
content: '';
|
|
192
|
+
position: absolute;
|
|
193
|
+
left: 0;
|
|
194
|
+
top: 0;
|
|
195
|
+
bottom: 0;
|
|
196
|
+
width: 2px;
|
|
197
|
+
background: var(--activity-bar-accent, #3b82f6);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.activity-bar__icon {
|
|
201
|
+
display: inline-flex;
|
|
202
|
+
align-items: center;
|
|
203
|
+
justify-content: center;
|
|
204
|
+
width: 22px;
|
|
205
|
+
height: 22px;
|
|
206
|
+
pointer-events: none;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.activity-bar__badge {
|
|
210
|
+
position: absolute;
|
|
211
|
+
top: 6px;
|
|
212
|
+
right: 6px;
|
|
213
|
+
min-width: 16px;
|
|
214
|
+
height: 16px;
|
|
215
|
+
padding: 0 4px;
|
|
216
|
+
border-radius: 8px;
|
|
217
|
+
background: var(--activity-bar-badge-bg, #ef4444);
|
|
218
|
+
color: var(--activity-bar-badge-fg, #fff);
|
|
219
|
+
font:
|
|
220
|
+
10px/16px system-ui,
|
|
221
|
+
-apple-system,
|
|
222
|
+
Segoe UI,
|
|
223
|
+
sans-serif;
|
|
224
|
+
font-weight: 600;
|
|
225
|
+
text-align: center;
|
|
226
|
+
}
|
|
227
|
+
</style>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* One item rendered as an icon button in the rail.
|
|
4
|
+
*/
|
|
5
|
+
export interface ActivityBarItem {
|
|
6
|
+
/** Stable id. Passed back via `onSelect` and used for `activeId`. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Snippet that renders the icon. Called with no arguments. */
|
|
9
|
+
icon: Snippet;
|
|
10
|
+
/** Accessible label + tooltip title. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Optional numeric badge rendered over the icon. */
|
|
13
|
+
badge?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Vertical activity-bar rail (VS Code pattern). Renders a stack of
|
|
17
|
+
* square icon buttons wired into a `role="tablist"` group. One item
|
|
18
|
+
* can be "active"; callers own that state via the `activeId` prop.
|
|
19
|
+
*
|
|
20
|
+
* Visual contract: 48×48 square buttons. Active state renders an
|
|
21
|
+
* accent-colored left border + subtle background tint. Hover is a
|
|
22
|
+
* faint background. All class names are author-written (not scoped
|
|
23
|
+
* hashes) so consumers can safely target them via `:global(...)`:
|
|
24
|
+
*
|
|
25
|
+
* .activity-bar
|
|
26
|
+
* .activity-bar__item
|
|
27
|
+
* .activity-bar__item--active
|
|
28
|
+
* .activity-bar__badge
|
|
29
|
+
*
|
|
30
|
+
* Keyboard: Up/Down arrow moves focus between buttons (wraps); Enter
|
|
31
|
+
* or Space activates the focused item.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```svelte
|
|
35
|
+
* <ActivityBar
|
|
36
|
+
* activeId={active}
|
|
37
|
+
* items={[
|
|
38
|
+
* { id: 'viz', label: 'Visualizations', icon: vizIcon },
|
|
39
|
+
* { id: 'filters', label: 'Filters', icon: filtersIcon }
|
|
40
|
+
* ]}
|
|
41
|
+
* onSelect={(id) => (active = active === id ? undefined : id)}
|
|
42
|
+
* />
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
interface Props {
|
|
46
|
+
/** The items to render. */
|
|
47
|
+
items: ActivityBarItem[];
|
|
48
|
+
/**
|
|
49
|
+
* The id of the currently-active item. When undefined, no item
|
|
50
|
+
* shows the active treatment. Bindable.
|
|
51
|
+
*/
|
|
52
|
+
activeId?: string;
|
|
53
|
+
/** Click / keyboard-activation callback. */
|
|
54
|
+
onSelect?: (id: string) => void;
|
|
55
|
+
/** Rail width in px. Default: 48. */
|
|
56
|
+
width?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Rail orientation. Only `'vertical'` is supported today; the prop
|
|
59
|
+
* is kept so future work can add horizontal without breaking API.
|
|
60
|
+
*/
|
|
61
|
+
orientation?: 'vertical';
|
|
62
|
+
class?: string;
|
|
63
|
+
}
|
|
64
|
+
declare const ActivityBar: import("svelte").Component<Props, {}, "activeId">;
|
|
65
|
+
type ActivityBar = ReturnType<typeof ActivityBar>;
|
|
66
|
+
export default ActivityBar;
|