@classic-homes/theme-svelte 1.18.0 → 1.19.1
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/__tests__/studio.test.js +240 -0
- package/dist/lib/components/Sheet.svelte +155 -0
- package/dist/lib/components/Sheet.svelte.d.ts +28 -0
- package/dist/lib/components/SortableList.svelte +251 -0
- package/dist/lib/components/SortableList.svelte.d.ts +64 -0
- package/dist/lib/components/studio/InsertPalette.svelte +192 -0
- package/dist/lib/components/studio/InsertPalette.svelte.d.ts +39 -0
- package/dist/lib/components/studio/IssuesButton.svelte +141 -0
- package/dist/lib/components/studio/IssuesButton.svelte.d.ts +33 -0
- package/dist/lib/components/studio/SaveStatus.svelte +110 -0
- package/dist/lib/components/studio/SaveStatus.svelte.d.ts +22 -0
- package/dist/lib/components/studio/Studio.svelte +189 -0
- package/dist/lib/components/studio/Studio.svelte.d.ts +49 -0
- package/dist/lib/components/studio/StudioBar.svelte +168 -0
- package/dist/lib/components/studio/StudioBar.svelte.d.ts +46 -0
- package/dist/lib/components/studio/StudioCanvas.svelte +87 -0
- package/dist/lib/components/studio/StudioCanvas.svelte.d.ts +30 -0
- package/dist/lib/components/studio/StudioCanvasItem.svelte +129 -0
- package/dist/lib/components/studio/StudioCanvasItem.svelte.d.ts +29 -0
- package/dist/lib/components/studio/StudioInspector.svelte +133 -0
- package/dist/lib/components/studio/StudioInspector.svelte.d.ts +37 -0
- package/dist/lib/components/studio/StudioInspectorSection.svelte +79 -0
- package/dist/lib/components/studio/StudioInspectorSection.svelte.d.ts +24 -0
- package/dist/lib/components/studio/StudioOutline.svelte +303 -0
- package/dist/lib/components/studio/StudioOutline.svelte.d.ts +55 -0
- package/dist/lib/components/studio/StudioPage.svelte +36 -0
- package/dist/lib/components/studio/StudioPage.svelte.d.ts +18 -0
- package/dist/lib/components/studio/context.d.ts +25 -0
- package/dist/lib/components/studio/context.js +9 -0
- package/dist/lib/components/studio/index.d.ts +12 -0
- package/dist/lib/components/studio/index.js +12 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.js +6 -0
- package/dist/lib/schemas/common.d.ts +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { IconName } from '../Icon.svelte';
|
|
3
|
+
|
|
4
|
+
export interface StudioOutlineEntry {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
/** Second line, e.g. the question type or slide layout */
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
icon?: IconName;
|
|
10
|
+
/** Number of problems on this item; shown as a count, not just a colour */
|
|
11
|
+
issues?: number;
|
|
12
|
+
/** Colour of the issue count (default: error) */
|
|
13
|
+
issueSeverity?: 'error' | 'warning';
|
|
14
|
+
/** Dim the row, e.g. a hidden slide */
|
|
15
|
+
muted?: boolean;
|
|
16
|
+
/** Short trailing tag, e.g. "Hidden" or "2 comments" */
|
|
17
|
+
badge?: string;
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import { tick, type Snippet } from 'svelte';
|
|
23
|
+
import { cn } from '../../utils.js';
|
|
24
|
+
import Icon from '../Icon.svelte';
|
|
25
|
+
import DropdownMenu, { type DropdownMenuItem } from '../DropdownMenu.svelte';
|
|
26
|
+
import SortableList from '../SortableList.svelte';
|
|
27
|
+
import { getStudioContext } from './context.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* StudioOutline - the Studio's rail: every item in order, the one place to
|
|
31
|
+
* select, reorder and add.
|
|
32
|
+
*
|
|
33
|
+
* Rows reorder by their drag handle (or keyboard, see `SortableList`); the
|
|
34
|
+
* row menu holds duplicate/delete and Move up/down. Arrow keys move between
|
|
35
|
+
* rows. Picking a row inside the phone sheet closes the sheet and opens the
|
|
36
|
+
* inspector. Pass `thumbnail` to show a preview per row (slides).
|
|
37
|
+
*/
|
|
38
|
+
interface Props {
|
|
39
|
+
items: StudioOutlineEntry[];
|
|
40
|
+
/** Selected item id (bindable) */
|
|
41
|
+
selected?: string | null;
|
|
42
|
+
onSelect?: (id: string) => void;
|
|
43
|
+
/** Move item `from` to index `to`; omit to turn reordering off */
|
|
44
|
+
onReorder?: (from: number, to: number) => void;
|
|
45
|
+
/** Row menu items; Move up/down are added for you when `onReorder` is set */
|
|
46
|
+
menuItems?: (entry: StudioOutlineEntry, index: number) => DropdownMenuItem[];
|
|
47
|
+
/** Shows the add button at the bottom of the rail */
|
|
48
|
+
onAdd?: () => void;
|
|
49
|
+
/** Add button text (default: "Add") */
|
|
50
|
+
addLabel?: string;
|
|
51
|
+
/** Accessible name of the list, e.g. "Questions" (default: "Items") */
|
|
52
|
+
label?: string;
|
|
53
|
+
/** Number rows 1, 2, 3... (default: true) */
|
|
54
|
+
numbered?: boolean;
|
|
55
|
+
/** Preview per row, e.g. a slide thumbnail */
|
|
56
|
+
thumbnail?: Snippet<[StudioOutlineEntry, number]>;
|
|
57
|
+
/** Above the list, e.g. a heading or filter */
|
|
58
|
+
header?: Snippet;
|
|
59
|
+
/** Shown when there are no items */
|
|
60
|
+
empty?: Snippet;
|
|
61
|
+
class?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let {
|
|
65
|
+
items,
|
|
66
|
+
selected = $bindable(null),
|
|
67
|
+
onSelect,
|
|
68
|
+
onReorder,
|
|
69
|
+
menuItems,
|
|
70
|
+
onAdd,
|
|
71
|
+
addLabel = 'Add',
|
|
72
|
+
label = 'Items',
|
|
73
|
+
numbered = true,
|
|
74
|
+
thumbnail,
|
|
75
|
+
header,
|
|
76
|
+
empty,
|
|
77
|
+
class: className,
|
|
78
|
+
}: Props = $props();
|
|
79
|
+
|
|
80
|
+
const studio = getStudioContext();
|
|
81
|
+
let listHost: HTMLDivElement | undefined = $state();
|
|
82
|
+
|
|
83
|
+
const focusIndex = $derived(
|
|
84
|
+
Math.max(
|
|
85
|
+
0,
|
|
86
|
+
items.findIndex((i) => i.id === selected)
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
function select(id: string) {
|
|
91
|
+
selected = id;
|
|
92
|
+
onSelect?.(id);
|
|
93
|
+
studio?.revealInspector();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function rowButtons() {
|
|
97
|
+
return Array.from(listHost?.querySelectorAll<HTMLElement>('[data-outline-row]') ?? []);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function rowKeydown(event: KeyboardEvent, index: number) {
|
|
101
|
+
const buttons = rowButtons();
|
|
102
|
+
let next = -1;
|
|
103
|
+
if (event.key === 'ArrowDown') next = Math.min(index + 1, buttons.length - 1);
|
|
104
|
+
else if (event.key === 'ArrowUp') next = Math.max(index - 1, 0);
|
|
105
|
+
else if (event.key === 'Home') next = 0;
|
|
106
|
+
else if (event.key === 'End') next = buttons.length - 1;
|
|
107
|
+
if (next < 0) return;
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
buttons[next]?.focus();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function rowMenu(entry: StudioOutlineEntry, index: number): DropdownMenuItem[] {
|
|
113
|
+
const own = menuItems?.(entry, index) ?? [];
|
|
114
|
+
if (!onReorder) return own;
|
|
115
|
+
const reorder = onReorder;
|
|
116
|
+
const moves: DropdownMenuItem[] = [
|
|
117
|
+
{
|
|
118
|
+
label: 'Move up',
|
|
119
|
+
icon: 'arrow-up',
|
|
120
|
+
disabled: index === 0,
|
|
121
|
+
onSelect: () => reorder(index, index - 1),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
label: 'Move down',
|
|
125
|
+
icon: 'arrow-down',
|
|
126
|
+
disabled: index === items.length - 1,
|
|
127
|
+
onSelect: () => reorder(index, index + 1),
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
return own.length ? [...moves, { type: 'separator' }, ...own] : moves;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Keep the selected row visible when selection comes from the canvas
|
|
134
|
+
$effect(() => {
|
|
135
|
+
const id = selected;
|
|
136
|
+
if (!id || !listHost) return;
|
|
137
|
+
void tick().then(() => {
|
|
138
|
+
listHost
|
|
139
|
+
?.querySelector(`[data-outline-id="${CSS.escape(id)}"]`)
|
|
140
|
+
?.scrollIntoView?.({ block: 'nearest' });
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
{#snippet row(entry: StudioOutlineEntry, index: number, handle?: Record<string, unknown>)}
|
|
146
|
+
{@const active = entry.id === selected}
|
|
147
|
+
{@const menu = rowMenu(entry, index)}
|
|
148
|
+
<div
|
|
149
|
+
data-outline-id={entry.id}
|
|
150
|
+
class={cn(
|
|
151
|
+
'group relative flex items-stretch gap-0.5 rounded-md',
|
|
152
|
+
active ? 'bg-primary/10 ring-1 ring-inset ring-primary/40' : 'hover:bg-muted-hover'
|
|
153
|
+
)}
|
|
154
|
+
>
|
|
155
|
+
{#if handle}
|
|
156
|
+
<button
|
|
157
|
+
{...handle}
|
|
158
|
+
class="flex w-6 shrink-0 cursor-grab touch-none items-center justify-center rounded-l-md text-muted-foreground opacity-0 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring active:cursor-grabbing group-hover:opacity-100 aria-pressed:opacity-100 [@media(hover:none)]:opacity-100"
|
|
159
|
+
class:opacity-100={active}
|
|
160
|
+
>
|
|
161
|
+
<Icon name="grip" size={14} aria-hidden="true" />
|
|
162
|
+
</button>
|
|
163
|
+
{/if}
|
|
164
|
+
|
|
165
|
+
<button
|
|
166
|
+
type="button"
|
|
167
|
+
data-outline-row
|
|
168
|
+
tabindex={index === focusIndex ? 0 : -1}
|
|
169
|
+
aria-current={active ? 'true' : undefined}
|
|
170
|
+
class={cn(
|
|
171
|
+
'flex min-w-0 flex-1 flex-col gap-1.5 rounded-md py-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
172
|
+
!handle && 'pl-2',
|
|
173
|
+
entry.muted && 'opacity-60'
|
|
174
|
+
)}
|
|
175
|
+
onclick={() => select(entry.id)}
|
|
176
|
+
onkeydown={(e) => rowKeydown(e, index)}
|
|
177
|
+
>
|
|
178
|
+
{#if thumbnail}
|
|
179
|
+
<div class="w-full overflow-hidden rounded border border-border bg-background">
|
|
180
|
+
{@render thumbnail(entry, index)}
|
|
181
|
+
</div>
|
|
182
|
+
{/if}
|
|
183
|
+
<span class="flex min-w-0 items-start gap-2">
|
|
184
|
+
{#if numbered}
|
|
185
|
+
<span class="w-5 shrink-0 pt-px text-right text-xs tabular-nums text-muted-foreground">
|
|
186
|
+
{index + 1}
|
|
187
|
+
</span>
|
|
188
|
+
{/if}
|
|
189
|
+
{#if entry.icon && !thumbnail}
|
|
190
|
+
<Icon
|
|
191
|
+
name={entry.icon}
|
|
192
|
+
size={16}
|
|
193
|
+
class="mt-px shrink-0 text-muted-foreground"
|
|
194
|
+
aria-hidden="true"
|
|
195
|
+
/>
|
|
196
|
+
{/if}
|
|
197
|
+
<span class="min-w-0 flex-1">
|
|
198
|
+
<span
|
|
199
|
+
class={cn(
|
|
200
|
+
'block truncate text-sm',
|
|
201
|
+
active ? 'font-medium text-foreground' : 'text-foreground'
|
|
202
|
+
)}
|
|
203
|
+
>
|
|
204
|
+
{entry.title || 'Untitled'}
|
|
205
|
+
</span>
|
|
206
|
+
{#if entry.subtitle}
|
|
207
|
+
<span class="block truncate text-xs text-muted-foreground">{entry.subtitle}</span>
|
|
208
|
+
{/if}
|
|
209
|
+
</span>
|
|
210
|
+
{#if entry.badge}
|
|
211
|
+
<span
|
|
212
|
+
class="shrink-0 rounded bg-muted px-1.5 py-px text-[0.6875rem] text-muted-foreground"
|
|
213
|
+
>
|
|
214
|
+
{entry.badge}
|
|
215
|
+
</span>
|
|
216
|
+
{/if}
|
|
217
|
+
{#if entry.issues}
|
|
218
|
+
<span
|
|
219
|
+
class="inline-flex shrink-0 items-center gap-0.5 text-xs font-medium tabular-nums"
|
|
220
|
+
title="{entry.issues} {entry.issues === 1 ? 'issue' : 'issues'}"
|
|
221
|
+
>
|
|
222
|
+
<Icon
|
|
223
|
+
name="alert-triangle"
|
|
224
|
+
size={12}
|
|
225
|
+
class={entry.issueSeverity === 'warning' ? 'text-warning' : 'text-destructive'}
|
|
226
|
+
aria-hidden="true"
|
|
227
|
+
/>
|
|
228
|
+
{entry.issues}
|
|
229
|
+
<span class="sr-only">{entry.issues === 1 ? 'issue' : 'issues'}</span>
|
|
230
|
+
</span>
|
|
231
|
+
{/if}
|
|
232
|
+
</span>
|
|
233
|
+
</button>
|
|
234
|
+
|
|
235
|
+
{#if menu.length}
|
|
236
|
+
<div
|
|
237
|
+
class={cn(
|
|
238
|
+
'flex shrink-0 items-start pt-1 opacity-0 focus-within:opacity-100 group-hover:opacity-100 [@media(hover:none)]:opacity-100',
|
|
239
|
+
active && 'opacity-100'
|
|
240
|
+
)}
|
|
241
|
+
>
|
|
242
|
+
<DropdownMenu
|
|
243
|
+
items={menu}
|
|
244
|
+
align="end"
|
|
245
|
+
triggerClass="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
246
|
+
triggerLabel="Actions for {entry.title || `item ${index + 1}`}"
|
|
247
|
+
>
|
|
248
|
+
{#snippet trigger()}
|
|
249
|
+
<Icon name="more-horizontal" size={16} aria-hidden="true" />
|
|
250
|
+
{/snippet}
|
|
251
|
+
</DropdownMenu>
|
|
252
|
+
</div>
|
|
253
|
+
{/if}
|
|
254
|
+
</div>
|
|
255
|
+
{/snippet}
|
|
256
|
+
|
|
257
|
+
<div class={cn('flex h-full min-h-0 flex-col', className)}>
|
|
258
|
+
{#if header}
|
|
259
|
+
<div class="shrink-0 border-b border-border px-3 py-2">{@render header()}</div>
|
|
260
|
+
{/if}
|
|
261
|
+
|
|
262
|
+
<div bind:this={listHost} class="min-h-0 flex-1 overflow-y-auto p-2">
|
|
263
|
+
{#if items.length === 0}
|
|
264
|
+
{#if empty}
|
|
265
|
+
{@render empty()}
|
|
266
|
+
{:else}
|
|
267
|
+
<p class="px-2 py-6 text-center text-sm text-muted-foreground">Nothing here yet.</p>
|
|
268
|
+
{/if}
|
|
269
|
+
{:else if onReorder}
|
|
270
|
+
<SortableList
|
|
271
|
+
{items}
|
|
272
|
+
getKey={(entry) => entry.id}
|
|
273
|
+
{onReorder}
|
|
274
|
+
{label}
|
|
275
|
+
getItemLabel={(entry, index) => entry.title || `item ${index + 1}`}
|
|
276
|
+
class="space-y-0.5"
|
|
277
|
+
>
|
|
278
|
+
{#snippet item({ item: entry, index, handle })}
|
|
279
|
+
{@render row(entry, index, handle as unknown as Record<string, unknown>)}
|
|
280
|
+
{/snippet}
|
|
281
|
+
</SortableList>
|
|
282
|
+
{:else}
|
|
283
|
+
<ul aria-label={label} class="space-y-0.5">
|
|
284
|
+
{#each items as entry, index (entry.id)}
|
|
285
|
+
<li>{@render row(entry, index)}</li>
|
|
286
|
+
{/each}
|
|
287
|
+
</ul>
|
|
288
|
+
{/if}
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
{#if onAdd}
|
|
292
|
+
<div class="shrink-0 border-t border-border p-2">
|
|
293
|
+
<button
|
|
294
|
+
type="button"
|
|
295
|
+
class="flex h-9 w-full items-center justify-center gap-2 rounded-md border border-dashed border-border text-sm font-medium text-muted-foreground hover:border-primary hover:bg-primary/5 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
296
|
+
onclick={onAdd}
|
|
297
|
+
>
|
|
298
|
+
<Icon name="plus" size={16} aria-hidden="true" />
|
|
299
|
+
{addLabel}
|
|
300
|
+
</button>
|
|
301
|
+
</div>
|
|
302
|
+
{/if}
|
|
303
|
+
</div>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { IconName } from '../Icon.svelte';
|
|
2
|
+
export interface StudioOutlineEntry {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
/** Second line, e.g. the question type or slide layout */
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
icon?: IconName;
|
|
8
|
+
/** Number of problems on this item; shown as a count, not just a colour */
|
|
9
|
+
issues?: number;
|
|
10
|
+
/** Colour of the issue count (default: error) */
|
|
11
|
+
issueSeverity?: 'error' | 'warning';
|
|
12
|
+
/** Dim the row, e.g. a hidden slide */
|
|
13
|
+
muted?: boolean;
|
|
14
|
+
/** Short trailing tag, e.g. "Hidden" or "2 comments" */
|
|
15
|
+
badge?: string;
|
|
16
|
+
}
|
|
17
|
+
import { type Snippet } from 'svelte';
|
|
18
|
+
import { type DropdownMenuItem } from '../DropdownMenu.svelte';
|
|
19
|
+
/**
|
|
20
|
+
* StudioOutline - the Studio's rail: every item in order, the one place to
|
|
21
|
+
* select, reorder and add.
|
|
22
|
+
*
|
|
23
|
+
* Rows reorder by their drag handle (or keyboard, see `SortableList`); the
|
|
24
|
+
* row menu holds duplicate/delete and Move up/down. Arrow keys move between
|
|
25
|
+
* rows. Picking a row inside the phone sheet closes the sheet and opens the
|
|
26
|
+
* inspector. Pass `thumbnail` to show a preview per row (slides).
|
|
27
|
+
*/
|
|
28
|
+
interface Props {
|
|
29
|
+
items: StudioOutlineEntry[];
|
|
30
|
+
/** Selected item id (bindable) */
|
|
31
|
+
selected?: string | null;
|
|
32
|
+
onSelect?: (id: string) => void;
|
|
33
|
+
/** Move item `from` to index `to`; omit to turn reordering off */
|
|
34
|
+
onReorder?: (from: number, to: number) => void;
|
|
35
|
+
/** Row menu items; Move up/down are added for you when `onReorder` is set */
|
|
36
|
+
menuItems?: (entry: StudioOutlineEntry, index: number) => DropdownMenuItem[];
|
|
37
|
+
/** Shows the add button at the bottom of the rail */
|
|
38
|
+
onAdd?: () => void;
|
|
39
|
+
/** Add button text (default: "Add") */
|
|
40
|
+
addLabel?: string;
|
|
41
|
+
/** Accessible name of the list, e.g. "Questions" (default: "Items") */
|
|
42
|
+
label?: string;
|
|
43
|
+
/** Number rows 1, 2, 3... (default: true) */
|
|
44
|
+
numbered?: boolean;
|
|
45
|
+
/** Preview per row, e.g. a slide thumbnail */
|
|
46
|
+
thumbnail?: Snippet<[StudioOutlineEntry, number]>;
|
|
47
|
+
/** Above the list, e.g. a heading or filter */
|
|
48
|
+
header?: Snippet;
|
|
49
|
+
/** Shown when there are no items */
|
|
50
|
+
empty?: Snippet;
|
|
51
|
+
class?: string;
|
|
52
|
+
}
|
|
53
|
+
declare const StudioOutline: import("svelte").Component<Props, {}, "selected">;
|
|
54
|
+
type StudioOutline = ReturnType<typeof StudioOutline>;
|
|
55
|
+
export default StudioOutline;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { cn } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* StudioPage - a full-width, scrolling page inside a Studio, for modes that
|
|
7
|
+
* configure rather than build (Settings, Share, Deck). Pass it as the
|
|
8
|
+
* Studio's `canvas` with no rail or inspector. Group its fields in Cards or
|
|
9
|
+
* `StudioInspectorSection`s with plain headings: Basics, Schedule, Privacy...
|
|
10
|
+
*/
|
|
11
|
+
interface Props {
|
|
12
|
+
title?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
children: Snippet;
|
|
15
|
+
/** Content width (default: 42rem) */
|
|
16
|
+
maxWidth?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let { title, description, children, maxWidth = '42rem', class: className }: Props = $props();
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<div class="h-full overflow-y-auto bg-muted/40">
|
|
24
|
+
<div
|
|
25
|
+
class={cn('mx-auto w-full space-y-6 px-4 py-6 md:py-10', className)}
|
|
26
|
+
style:max-width={maxWidth}
|
|
27
|
+
>
|
|
28
|
+
{#if title || description}
|
|
29
|
+
<div>
|
|
30
|
+
{#if title}<h2 class="text-xl font-semibold">{title}</h2>{/if}
|
|
31
|
+
{#if description}<p class="mt-1 text-sm text-muted-foreground">{description}</p>{/if}
|
|
32
|
+
</div>
|
|
33
|
+
{/if}
|
|
34
|
+
{@render children()}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* StudioPage - a full-width, scrolling page inside a Studio, for modes that
|
|
4
|
+
* configure rather than build (Settings, Share, Deck). Pass it as the
|
|
5
|
+
* Studio's `canvas` with no rail or inspector. Group its fields in Cards or
|
|
6
|
+
* `StudioInspectorSection`s with plain headings: Basics, Schedule, Privacy...
|
|
7
|
+
*/
|
|
8
|
+
interface Props {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
children: Snippet;
|
|
12
|
+
/** Content width (default: 42rem) */
|
|
13
|
+
maxWidth?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
declare const StudioPage: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type StudioPage = ReturnType<typeof StudioPage>;
|
|
18
|
+
export default StudioPage;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much room the Studio has:
|
|
3
|
+
* - `wide` (≥1280px): outline, canvas and inspector side by side
|
|
4
|
+
* - `medium` (768–1279px): the outline moves into a sheet; canvas and inspector stay
|
|
5
|
+
* - `compact` (<768px): canvas only; outline and inspector open as sheets
|
|
6
|
+
*/
|
|
7
|
+
export type StudioLayout = 'wide' | 'medium' | 'compact';
|
|
8
|
+
export interface StudioContext {
|
|
9
|
+
readonly layout: StudioLayout;
|
|
10
|
+
/** Whether the outline is shown in a sheet rather than as a column */
|
|
11
|
+
readonly railInSheet: boolean;
|
|
12
|
+
/** Whether the inspector is shown in a sheet rather than as a column */
|
|
13
|
+
readonly inspectorInSheet: boolean;
|
|
14
|
+
openRail(): void;
|
|
15
|
+
closeRail(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Bring the inspector into view after the user picks something to edit:
|
|
18
|
+
* closes the outline sheet and, on phones, opens the inspector sheet.
|
|
19
|
+
*/
|
|
20
|
+
revealInspector(): void;
|
|
21
|
+
closeInspector(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function setStudioContext(context: StudioContext): void;
|
|
24
|
+
/** The surrounding Studio, or undefined when a Studio part is used on its own */
|
|
25
|
+
export declare function getStudioContext(): StudioContext | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
const KEY = Symbol('classic-studio');
|
|
3
|
+
export function setStudioContext(context) {
|
|
4
|
+
setContext(KEY, context);
|
|
5
|
+
}
|
|
6
|
+
/** The surrounding Studio, or undefined when a Studio part is used on its own */
|
|
7
|
+
export function getStudioContext() {
|
|
8
|
+
return getContext(KEY);
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { default as Studio } from './Studio.svelte';
|
|
2
|
+
export { default as StudioBar } from './StudioBar.svelte';
|
|
3
|
+
export { default as StudioOutline, type StudioOutlineEntry } from './StudioOutline.svelte';
|
|
4
|
+
export { default as StudioCanvas } from './StudioCanvas.svelte';
|
|
5
|
+
export { default as StudioCanvasItem } from './StudioCanvasItem.svelte';
|
|
6
|
+
export { default as StudioInspector, type StudioInspectorTab } from './StudioInspector.svelte';
|
|
7
|
+
export { default as StudioInspectorSection } from './StudioInspectorSection.svelte';
|
|
8
|
+
export { default as StudioPage } from './StudioPage.svelte';
|
|
9
|
+
export { default as SaveStatus, type SaveState } from './SaveStatus.svelte';
|
|
10
|
+
export { default as IssuesButton, type StudioIssue } from './IssuesButton.svelte';
|
|
11
|
+
export { default as InsertPalette, type InsertPaletteItem, type InsertPaletteGroup, } from './InsertPalette.svelte';
|
|
12
|
+
export { getStudioContext, type StudioContext, type StudioLayout } from './context.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { default as Studio } from './Studio.svelte';
|
|
2
|
+
export { default as StudioBar } from './StudioBar.svelte';
|
|
3
|
+
export { default as StudioOutline } from './StudioOutline.svelte';
|
|
4
|
+
export { default as StudioCanvas } from './StudioCanvas.svelte';
|
|
5
|
+
export { default as StudioCanvasItem } from './StudioCanvasItem.svelte';
|
|
6
|
+
export { default as StudioInspector } from './StudioInspector.svelte';
|
|
7
|
+
export { default as StudioInspectorSection } from './StudioInspectorSection.svelte';
|
|
8
|
+
export { default as StudioPage } from './StudioPage.svelte';
|
|
9
|
+
export { default as SaveStatus } from './SaveStatus.svelte';
|
|
10
|
+
export { default as IssuesButton } from './IssuesButton.svelte';
|
|
11
|
+
export { default as InsertPalette, } from './InsertPalette.svelte';
|
|
12
|
+
export { getStudioContext } from './context.js';
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -65,6 +65,9 @@ export { default as FilterPanel } from './components/FilterPanel.svelte';
|
|
|
65
65
|
export { default as ActiveFilterChips, type ActiveFilterChip, } from './components/ActiveFilterChips.svelte';
|
|
66
66
|
export { default as SegmentedControl, type SegmentedControlOption, } from './components/SegmentedControl.svelte';
|
|
67
67
|
export { default as ViewToggle, type ViewToggleOption, defaultViewToggleOptions, } from './components/ViewToggle.svelte';
|
|
68
|
+
export { default as Sheet } from './components/Sheet.svelte';
|
|
69
|
+
export { default as SortableList, type SortableHandleProps, type SortableItemContext, } from './components/SortableList.svelte';
|
|
70
|
+
export { Studio, StudioBar, StudioOutline, StudioCanvas, StudioCanvasItem, StudioInspector, StudioInspectorSection, StudioPage, SaveStatus, IssuesButton, InsertPalette, getStudioContext, type StudioOutlineEntry, type StudioInspectorTab, type SaveState, type StudioIssue, type InsertPaletteItem, type InsertPaletteGroup, type StudioContext, type StudioLayout, } from './components/studio/index.js';
|
|
68
71
|
export { AspectRatio, Image, Video, Audio, MediaEmbed, MediaGrid, MediaCarousel, Lightbox, } from './components/media/index.js';
|
|
69
72
|
export type { MediaGridColumns } from './components/media/MediaGrid.svelte';
|
|
70
73
|
export type { DropdownMenuItem, DropdownMenuGroup, SelectOption, SelectGroup, Tab, FileMetadata, DataTableColumn, DataTableFilterFn, DataTableFilterOption, RadioOption, ComboboxOption, MultiSelectOption, SignatureData, SignatureStroke, SignaturePoint, SignatureMetadata, SignatureFont, SignatureValidationResult, MediaKind, MediaEmbedProvider, MediaSource, MediaTextTrack, MediaItemBase, ImageMediaItem, VideoMediaItem, AudioMediaItem, EmbedMediaItem, MediaItem, MediaAspectRatio, MediaFit, CloudflareStreamAccount, MatterportPlayerOptions, } from './types/components.js';
|
package/dist/lib/index.js
CHANGED
|
@@ -78,6 +78,12 @@ export { default as FilterPanel } from './components/FilterPanel.svelte';
|
|
|
78
78
|
export { default as ActiveFilterChips, } from './components/ActiveFilterChips.svelte';
|
|
79
79
|
export { default as SegmentedControl, } from './components/SegmentedControl.svelte';
|
|
80
80
|
export { default as ViewToggle, defaultViewToggleOptions, } from './components/ViewToggle.svelte';
|
|
81
|
+
// Overlay: edge sheet
|
|
82
|
+
export { default as Sheet } from './components/Sheet.svelte';
|
|
83
|
+
// Reorderable list
|
|
84
|
+
export { default as SortableList, } from './components/SortableList.svelte';
|
|
85
|
+
// Studio - full-screen editor frame (outline, canvas, inspector)
|
|
86
|
+
export { Studio, StudioBar, StudioOutline, StudioCanvas, StudioCanvasItem, StudioInspector, StudioInspectorSection, StudioPage, SaveStatus, IssuesButton, InsertPalette, getStudioContext, } from './components/studio/index.js';
|
|
81
87
|
// Media Components
|
|
82
88
|
export { AspectRatio, Image, Video, Audio, MediaEmbed, MediaGrid, MediaCarousel, Lightbox, } from './components/media/index.js';
|
|
83
89
|
// DataTable sub-components and utilities
|
|
@@ -155,15 +155,15 @@ export declare const addressSchema: z.ZodObject<{
|
|
|
155
155
|
street: string;
|
|
156
156
|
city: string;
|
|
157
157
|
postalCode: string;
|
|
158
|
-
street2?: string | undefined;
|
|
159
158
|
state?: string | undefined;
|
|
159
|
+
street2?: string | undefined;
|
|
160
160
|
}, {
|
|
161
161
|
country: string;
|
|
162
162
|
street: string;
|
|
163
163
|
city: string;
|
|
164
164
|
postalCode: string;
|
|
165
|
-
street2?: string | undefined;
|
|
166
165
|
state?: string | undefined;
|
|
166
|
+
street2?: string | undefined;
|
|
167
167
|
}>;
|
|
168
168
|
export type Address = z.infer<typeof addressSchema>;
|
|
169
169
|
/**
|