@classic-homes/theme-svelte 1.17.0 → 1.19.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.
Files changed (44) hide show
  1. package/dist/__tests__/dropdown-trigger.test.js +9 -0
  2. package/dist/__tests__/sidebar-mobile.test.js +13 -0
  3. package/dist/__tests__/studio.test.js +223 -0
  4. package/dist/lib/components/DropdownMenu.svelte +17 -0
  5. package/dist/lib/components/DropdownMenu.svelte.d.ts +5 -0
  6. package/dist/lib/components/Sheet.svelte +155 -0
  7. package/dist/lib/components/Sheet.svelte.d.ts +28 -0
  8. package/dist/lib/components/SortableList.svelte +251 -0
  9. package/dist/lib/components/SortableList.svelte.d.ts +64 -0
  10. package/dist/lib/components/layout/DashboardLayout.svelte +34 -12
  11. package/dist/lib/components/layout/sidebar/SidebarNavItem.svelte +6 -2
  12. package/dist/lib/components/layout/sidebar/SidebarSection.svelte +5 -3
  13. package/dist/lib/components/studio/InsertPalette.svelte +192 -0
  14. package/dist/lib/components/studio/InsertPalette.svelte.d.ts +39 -0
  15. package/dist/lib/components/studio/IssuesButton.svelte +141 -0
  16. package/dist/lib/components/studio/IssuesButton.svelte.d.ts +33 -0
  17. package/dist/lib/components/studio/SaveStatus.svelte +110 -0
  18. package/dist/lib/components/studio/SaveStatus.svelte.d.ts +22 -0
  19. package/dist/lib/components/studio/Studio.svelte +189 -0
  20. package/dist/lib/components/studio/Studio.svelte.d.ts +49 -0
  21. package/dist/lib/components/studio/StudioBar.svelte +168 -0
  22. package/dist/lib/components/studio/StudioBar.svelte.d.ts +46 -0
  23. package/dist/lib/components/studio/StudioCanvas.svelte +87 -0
  24. package/dist/lib/components/studio/StudioCanvas.svelte.d.ts +30 -0
  25. package/dist/lib/components/studio/StudioCanvasItem.svelte +121 -0
  26. package/dist/lib/components/studio/StudioCanvasItem.svelte.d.ts +29 -0
  27. package/dist/lib/components/studio/StudioInspector.svelte +133 -0
  28. package/dist/lib/components/studio/StudioInspector.svelte.d.ts +37 -0
  29. package/dist/lib/components/studio/StudioInspectorSection.svelte +79 -0
  30. package/dist/lib/components/studio/StudioInspectorSection.svelte.d.ts +24 -0
  31. package/dist/lib/components/studio/StudioOutline.svelte +303 -0
  32. package/dist/lib/components/studio/StudioOutline.svelte.d.ts +55 -0
  33. package/dist/lib/components/studio/StudioPage.svelte +36 -0
  34. package/dist/lib/components/studio/StudioPage.svelte.d.ts +18 -0
  35. package/dist/lib/components/studio/context.d.ts +25 -0
  36. package/dist/lib/components/studio/context.js +9 -0
  37. package/dist/lib/components/studio/index.d.ts +12 -0
  38. package/dist/lib/components/studio/index.js +12 -0
  39. package/dist/lib/index.d.ts +3 -0
  40. package/dist/lib/index.js +6 -0
  41. package/dist/lib/schemas/common.d.ts +2 -2
  42. package/dist/lib/stores/sidebar.svelte.d.ts +6 -0
  43. package/dist/lib/stores/sidebar.svelte.js +13 -0
  44. package/package.json +1 -1
@@ -0,0 +1,141 @@
1
+ <script lang="ts" module>
2
+ export interface StudioIssue {
3
+ id: string;
4
+ /** What is wrong, phrased as what to do: "Add at least two options" */
5
+ message: string;
6
+ /** `error` blocks publishing; `warning` is advice (default: error) */
7
+ severity?: 'error' | 'warning';
8
+ /** Where it is, e.g. "Question 3" or "Settings" */
9
+ location?: string;
10
+ }
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import { Popover } from 'bits-ui';
15
+ import { cn } from '../../utils.js';
16
+ import Icon from '../Icon.svelte';
17
+
18
+ /**
19
+ * IssuesButton - the single place an editor lists what needs fixing.
20
+ *
21
+ * A count in the bar that opens a checklist; picking an entry calls
22
+ * `onSelect` so the editor can select the item and focus the field. Bind
23
+ * `open` to show the same list when someone tries to publish with errors,
24
+ * instead of a separate publish-problems dialog. Hidden when there is
25
+ * nothing to fix, unless `showWhenEmpty`.
26
+ */
27
+ interface Props {
28
+ issues: StudioIssue[];
29
+ /** Jump to an issue: select its item and focus the field */
30
+ onSelect?: (issue: StudioIssue) => void;
31
+ /** Popover open (bindable) */
32
+ open?: boolean;
33
+ /** Heading of the list (default: "Before you publish") */
34
+ title?: string;
35
+ /** Render a quiet "No issues" chip when the list is empty */
36
+ showWhenEmpty?: boolean;
37
+ class?: string;
38
+ }
39
+
40
+ let {
41
+ issues,
42
+ onSelect,
43
+ open = $bindable(false),
44
+ title = 'Before you publish',
45
+ showWhenEmpty = false,
46
+ class: className,
47
+ }: Props = $props();
48
+
49
+ const errors = $derived(issues.filter((i) => (i.severity ?? 'error') === 'error'));
50
+ const warnings = $derived(issues.filter((i) => i.severity === 'warning'));
51
+ const countLabel = $derived(
52
+ [
53
+ errors.length && `${errors.length} ${errors.length === 1 ? 'issue' : 'issues'} to fix`,
54
+ warnings.length &&
55
+ `${warnings.length} ${warnings.length === 1 ? 'suggestion' : 'suggestions'}`,
56
+ ]
57
+ .filter(Boolean)
58
+ .join(', ')
59
+ );
60
+
61
+ function pick(issue: StudioIssue) {
62
+ open = false;
63
+ onSelect?.(issue);
64
+ }
65
+ </script>
66
+
67
+ {#snippet group(list: StudioIssue[], heading: string, error: boolean)}
68
+ {#if list.length}
69
+ <p class="px-3 pb-1 pt-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">
70
+ {heading}
71
+ </p>
72
+ <ul class="pb-1">
73
+ {#each list as issue (issue.id)}
74
+ <li>
75
+ <button
76
+ type="button"
77
+ class="flex w-full items-start gap-2 px-3 py-2 text-left text-sm hover:bg-muted-hover focus-visible:bg-muted-hover focus-visible:outline-none"
78
+ onclick={() => pick(issue)}
79
+ >
80
+ <Icon
81
+ name={error ? 'alert-circle' : 'info'}
82
+ size={16}
83
+ class={cn('mt-0.5 shrink-0', error ? 'text-destructive' : 'text-warning')}
84
+ aria-hidden="true"
85
+ />
86
+ <span class="min-w-0 flex-1">
87
+ {#if issue.location}
88
+ <span class="block text-xs text-muted-foreground">{issue.location}</span>
89
+ {/if}
90
+ {issue.message}
91
+ </span>
92
+ </button>
93
+ </li>
94
+ {/each}
95
+ </ul>
96
+ {/if}
97
+ {/snippet}
98
+
99
+ {#if issues.length > 0}
100
+ <Popover.Root bind:open>
101
+ <Popover.Trigger
102
+ class={cn(
103
+ 'inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-sm font-medium hover:bg-muted-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
104
+ className
105
+ )}
106
+ aria-label={countLabel}
107
+ >
108
+ <Icon
109
+ name="alert-triangle"
110
+ size={16}
111
+ class={errors.length ? 'text-destructive' : 'text-warning'}
112
+ aria-hidden="true"
113
+ />
114
+ <span aria-hidden="true">{issues.length}</span>
115
+ </Popover.Trigger>
116
+ <Popover.Portal>
117
+ <Popover.Content
118
+ align="end"
119
+ sideOffset={6}
120
+ collisionPadding={8}
121
+ class="z-50 flex max-h-[min(28rem,70dvh)] w-80 flex-col overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md"
122
+ >
123
+ <div class="border-b border-border px-3 py-2">
124
+ <p class="text-sm font-semibold">{title}</p>
125
+ <p class="text-xs text-muted-foreground">{countLabel}</p>
126
+ </div>
127
+ <div class="overflow-y-auto">
128
+ {@render group(errors, 'Must fix', true)}
129
+ {@render group(warnings, 'Suggestions', false)}
130
+ </div>
131
+ </Popover.Content>
132
+ </Popover.Portal>
133
+ </Popover.Root>
134
+ {:else if showWhenEmpty}
135
+ <span
136
+ class={cn('inline-flex h-8 items-center gap-1.5 px-2 text-sm text-muted-foreground', className)}
137
+ >
138
+ <Icon name="check-circle" size={16} class="text-success" aria-hidden="true" />
139
+ <span class="sr-only sm:not-sr-only">No issues</span>
140
+ </span>
141
+ {/if}
@@ -0,0 +1,33 @@
1
+ export interface StudioIssue {
2
+ id: string;
3
+ /** What is wrong, phrased as what to do: "Add at least two options" */
4
+ message: string;
5
+ /** `error` blocks publishing; `warning` is advice (default: error) */
6
+ severity?: 'error' | 'warning';
7
+ /** Where it is, e.g. "Question 3" or "Settings" */
8
+ location?: string;
9
+ }
10
+ /**
11
+ * IssuesButton - the single place an editor lists what needs fixing.
12
+ *
13
+ * A count in the bar that opens a checklist; picking an entry calls
14
+ * `onSelect` so the editor can select the item and focus the field. Bind
15
+ * `open` to show the same list when someone tries to publish with errors,
16
+ * instead of a separate publish-problems dialog. Hidden when there is
17
+ * nothing to fix, unless `showWhenEmpty`.
18
+ */
19
+ interface Props {
20
+ issues: StudioIssue[];
21
+ /** Jump to an issue: select its item and focus the field */
22
+ onSelect?: (issue: StudioIssue) => void;
23
+ /** Popover open (bindable) */
24
+ open?: boolean;
25
+ /** Heading of the list (default: "Before you publish") */
26
+ title?: string;
27
+ /** Render a quiet "No issues" chip when the list is empty */
28
+ showWhenEmpty?: boolean;
29
+ class?: string;
30
+ }
31
+ declare const IssuesButton: import("svelte").Component<Props, {}, "open">;
32
+ type IssuesButton = ReturnType<typeof IssuesButton>;
33
+ export default IssuesButton;
@@ -0,0 +1,110 @@
1
+ <script lang="ts" module>
2
+ export type SaveState =
3
+ | 'saved'
4
+ | 'saving'
5
+ | 'unsaved'
6
+ | 'error'
7
+ | 'offline'
8
+ | 'conflict'
9
+ | 'invalid'
10
+ | 'readonly';
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import { Popover } from 'bits-ui';
15
+ import { cn } from '../../utils.js';
16
+ import Icon, { type IconName } from '../Icon.svelte';
17
+ import Button from '../Button.svelte';
18
+
19
+ /**
20
+ * SaveStatus - one compact signal for an autosaving editor.
21
+ *
22
+ * Shows an icon and one word (Saved, Saving…, Not saved…). When there is
23
+ * more to say - why a save failed, what needs fixing - the chip opens a
24
+ * popover with that detail and, for failures, a Retry button. Replaces
25
+ * stacks of status badges and save-error alerts.
26
+ */
27
+ interface Props {
28
+ state: SaveState;
29
+ /** Longer explanation shown in the popover, e.g. "Fix 2 questions to save them." */
30
+ detail?: string;
31
+ /** Offered in the popover for `error` and `offline` */
32
+ onRetry?: () => void;
33
+ /** Override the one-word label */
34
+ label?: string;
35
+ class?: string;
36
+ }
37
+
38
+ let { state, detail, onRetry, label, class: className }: Props = $props();
39
+
40
+ const meta: Record<SaveState, { label: string; icon: IconName; tone: string }> = {
41
+ saved: { label: 'Saved', icon: 'check-circle', tone: 'text-muted-foreground' },
42
+ saving: { label: 'Saving…', icon: 'loader', tone: 'text-muted-foreground' },
43
+ unsaved: { label: 'Unsaved', icon: 'edit', tone: 'text-muted-foreground' },
44
+ error: { label: 'Not saved', icon: 'alert-circle', tone: 'text-destructive' },
45
+ offline: { label: 'Offline', icon: 'wifi-off', tone: 'text-warning' },
46
+ conflict: { label: 'Conflict', icon: 'alert-triangle', tone: 'text-destructive' },
47
+ invalid: { label: 'Needs fixes', icon: 'alert-triangle', tone: 'text-warning' },
48
+ readonly: { label: 'View only', icon: 'eye', tone: 'text-muted-foreground' },
49
+ };
50
+
51
+ const current = $derived(meta[state]);
52
+ const text = $derived(label ?? current.label);
53
+ const canRetry = $derived(!!onRetry && (state === 'error' || state === 'offline'));
54
+ const hasDetail = $derived(!!detail || canRetry);
55
+ const announced = $derived(
56
+ state === 'error' || state === 'offline' || state === 'conflict' || state === 'invalid'
57
+ );
58
+
59
+ const chip =
60
+ 'inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-sm font-medium text-foreground';
61
+ </script>
62
+
63
+ {#snippet content()}
64
+ <Icon
65
+ name={current.icon}
66
+ size={16}
67
+ class={cn('shrink-0', current.tone, state === 'saving' && 'animate-spin')}
68
+ aria-hidden="true"
69
+ />
70
+ <!-- Icon-only on phones; the word stays available to screen readers -->
71
+ <span class="sr-only sm:not-sr-only">{text}</span>
72
+ {/snippet}
73
+
74
+ <div class={cn('inline-flex', className)}>
75
+ {#if hasDetail}
76
+ <Popover.Root>
77
+ <Popover.Trigger
78
+ class={cn(
79
+ chip,
80
+ 'hover:bg-muted-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'
81
+ )}
82
+ aria-label="Save status: {text}. Show details"
83
+ >
84
+ {@render content()}
85
+ <Icon name="chevron-down" size={14} class="text-muted-foreground" aria-hidden="true" />
86
+ </Popover.Trigger>
87
+ <Popover.Portal>
88
+ <Popover.Content
89
+ align="end"
90
+ sideOffset={6}
91
+ collisionPadding={8}
92
+ class="z-50 w-72 rounded-md border border-border bg-popover p-3 text-sm text-popover-foreground shadow-md"
93
+ >
94
+ <p class="font-medium">{text}</p>
95
+ {#if detail}<p class="mt-1 text-muted-foreground">{detail}</p>{/if}
96
+ {#if canRetry}
97
+ <Button size="sm" variant="secondary" class="mt-3" onclick={() => onRetry?.()}>
98
+ <Icon name="refresh" size={14} aria-hidden="true" />
99
+ Retry
100
+ </Button>
101
+ {/if}
102
+ </Popover.Content>
103
+ </Popover.Portal>
104
+ </Popover.Root>
105
+ {:else}
106
+ <span class={chip}>{@render content()}</span>
107
+ {/if}
108
+ <!-- Only problems are announced; "Saving… Saved" after every keystroke would be noise -->
109
+ <span class="sr-only" role="status" aria-live="polite">{announced ? text : ''}</span>
110
+ </div>
@@ -0,0 +1,22 @@
1
+ export type SaveState = 'saved' | 'saving' | 'unsaved' | 'error' | 'offline' | 'conflict' | 'invalid' | 'readonly';
2
+ /**
3
+ * SaveStatus - one compact signal for an autosaving editor.
4
+ *
5
+ * Shows an icon and one word (Saved, Saving…, Not saved…). When there is
6
+ * more to say - why a save failed, what needs fixing - the chip opens a
7
+ * popover with that detail and, for failures, a Retry button. Replaces
8
+ * stacks of status badges and save-error alerts.
9
+ */
10
+ interface Props {
11
+ state: SaveState;
12
+ /** Longer explanation shown in the popover, e.g. "Fix 2 questions to save them." */
13
+ detail?: string;
14
+ /** Offered in the popover for `error` and `offline` */
15
+ onRetry?: () => void;
16
+ /** Override the one-word label */
17
+ label?: string;
18
+ class?: string;
19
+ }
20
+ declare const SaveStatus: import("svelte").Component<Props, {}, "">;
21
+ type SaveStatus = ReturnType<typeof SaveStatus>;
22
+ export default SaveStatus;
@@ -0,0 +1,189 @@
1
+ <script lang="ts">
2
+ import { onMount, type Snippet } from 'svelte';
3
+ import { cn } from '../../utils.js';
4
+ import Icon from '../Icon.svelte';
5
+ import Sheet from '../Sheet.svelte';
6
+ import { setStudioContext, type StudioLayout } from './context.js';
7
+
8
+ /**
9
+ * Studio - full-screen frame for editors (survey builders, slide decks,
10
+ * page and form builders).
11
+ *
12
+ * One fixed layout that never page-scrolls:
13
+ *
14
+ * ```
15
+ * ┌ bar ─────────────────────────────────────────┐
16
+ * │ banner (optional) │
17
+ * ├ rail ──┬ canvas ─────────────┬ inspector ────┤
18
+ * │ outline│ the real thing, │ settings for │
19
+ * │ │ click to select │ the selection │
20
+ * └────────┴─────────────────────┴───────────────┘
21
+ * ```
22
+ *
23
+ * Rail, canvas and inspector scroll on their own. Below 1280px the rail
24
+ * moves into a left sheet; below 768px the inspector moves into a bottom
25
+ * sheet as well, leaving the canvas full screen. Parts read the layout
26
+ * through `getStudioContext()`, so `StudioOutline` closes its sheet after a
27
+ * pick and `StudioCanvasItem` opens the inspector sheet on phones.
28
+ *
29
+ * Leave out `rail` and `inspector` for full-width modes such as a settings
30
+ * page (render a `StudioPage` as the canvas).
31
+ */
32
+ interface Props {
33
+ /** Top bar, normally a `StudioBar` */
34
+ bar: Snippet;
35
+ /** One message slot under the bar, for conflicts and decisions that block editing */
36
+ banner?: Snippet;
37
+ /** Left column, normally a `StudioOutline` */
38
+ rail?: Snippet;
39
+ /** Main area, normally a `StudioCanvas` or `StudioPage` */
40
+ canvas: Snippet;
41
+ /** Right column, normally a `StudioInspector` */
42
+ inspector?: Snippet;
43
+ /** Accessible name of the rail and the button that opens it (default: "Outline") */
44
+ railLabel?: string;
45
+ /** Accessible name of the inspector and the button that opens it (default: "Properties") */
46
+ inspectorLabel?: string;
47
+ /** Rail sheet open below 1280px (bindable) */
48
+ railOpen?: boolean;
49
+ /** Inspector sheet open below 768px (bindable) */
50
+ inspectorOpen?: boolean;
51
+ class?: string;
52
+ }
53
+
54
+ let {
55
+ bar,
56
+ banner,
57
+ rail,
58
+ canvas,
59
+ inspector,
60
+ railLabel = 'Outline',
61
+ inspectorLabel = 'Properties',
62
+ railOpen = $bindable(false),
63
+ inspectorOpen = $bindable(false),
64
+ class: className,
65
+ }: Props = $props();
66
+
67
+ // Starts wide so SSR and desktop's first paint match. The columns also carry
68
+ // the breakpoints as CSS, so server-rendered HTML is already right on phones.
69
+ let layout = $state<StudioLayout>('wide');
70
+
71
+ onMount(() => {
72
+ const wide = window.matchMedia('(min-width: 1280px)');
73
+ const medium = window.matchMedia('(min-width: 768px)');
74
+ const update = () => {
75
+ layout = wide.matches ? 'wide' : medium.matches ? 'medium' : 'compact';
76
+ if (layout === 'wide') railOpen = false;
77
+ if (layout !== 'compact') inspectorOpen = false;
78
+ };
79
+ update();
80
+ wide.addEventListener('change', update);
81
+ medium.addEventListener('change', update);
82
+ return () => {
83
+ wide.removeEventListener('change', update);
84
+ medium.removeEventListener('change', update);
85
+ };
86
+ });
87
+
88
+ const railInSheet = $derived(!!rail && layout !== 'wide');
89
+ const inspectorInSheet = $derived(!!inspector && layout === 'compact');
90
+
91
+ setStudioContext({
92
+ get layout() {
93
+ return layout;
94
+ },
95
+ get railInSheet() {
96
+ return railInSheet;
97
+ },
98
+ get inspectorInSheet() {
99
+ return inspectorInSheet;
100
+ },
101
+ openRail: () => {
102
+ if (railInSheet) railOpen = true;
103
+ },
104
+ closeRail: () => (railOpen = false),
105
+ revealInspector: () => {
106
+ railOpen = false;
107
+ if (inspectorInSheet) inspectorOpen = true;
108
+ },
109
+ closeInspector: () => (inspectorOpen = false),
110
+ });
111
+
112
+ const floatingButton =
113
+ 'pointer-events-auto inline-flex h-9 items-center gap-2 rounded-full border border-border bg-background px-3 text-sm font-medium shadow-md hover:bg-muted-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring';
114
+ </script>
115
+
116
+ <div class={cn('flex h-dvh flex-col overflow-hidden bg-background text-foreground', className)}>
117
+ {@render bar()}
118
+
119
+ {#if banner}
120
+ <div class="shrink-0">{@render banner()}</div>
121
+ {/if}
122
+
123
+ <div class="relative flex min-h-0 flex-1">
124
+ {#if rail && !railInSheet}
125
+ <aside
126
+ aria-label={railLabel}
127
+ class="hidden w-64 shrink-0 flex-col overflow-hidden border-r border-border bg-background xl:flex"
128
+ >
129
+ {@render rail()}
130
+ </aside>
131
+ {/if}
132
+
133
+ <div class="relative min-w-0 flex-1 overflow-hidden bg-muted/40">
134
+ {@render canvas()}
135
+
136
+ {#if railInSheet || inspectorInSheet}
137
+ <div
138
+ class="pointer-events-none absolute inset-x-3 bottom-3 z-10 flex items-end justify-between gap-2"
139
+ >
140
+ {#if railInSheet}
141
+ <button
142
+ type="button"
143
+ class={floatingButton}
144
+ aria-expanded={railOpen}
145
+ onclick={() => (railOpen = true)}
146
+ >
147
+ <Icon name="list" size={16} aria-hidden="true" />
148
+ {railLabel}
149
+ </button>
150
+ {:else}
151
+ <span></span>
152
+ {/if}
153
+ {#if inspectorInSheet}
154
+ <button
155
+ type="button"
156
+ class={floatingButton}
157
+ aria-expanded={inspectorOpen}
158
+ onclick={() => (inspectorOpen = true)}
159
+ >
160
+ <Icon name="settings" size={16} aria-hidden="true" />
161
+ {inspectorLabel}
162
+ </button>
163
+ {/if}
164
+ </div>
165
+ {/if}
166
+ </div>
167
+
168
+ {#if inspector && !inspectorInSheet}
169
+ <aside
170
+ aria-label={inspectorLabel}
171
+ class="hidden w-80 shrink-0 flex-col overflow-hidden border-l border-border bg-background md:flex 2xl:w-96"
172
+ >
173
+ {@render inspector()}
174
+ </aside>
175
+ {/if}
176
+ </div>
177
+ </div>
178
+
179
+ {#if rail && railInSheet}
180
+ <Sheet bind:open={railOpen} side="left" size="sm" title={railLabel}>
181
+ <div class="flex h-full flex-col">{@render rail()}</div>
182
+ </Sheet>
183
+ {/if}
184
+
185
+ {#if inspector && inspectorInSheet}
186
+ <Sheet bind:open={inspectorOpen} side="bottom" title={inspectorLabel} hideTitle>
187
+ {@render inspector()}
188
+ </Sheet>
189
+ {/if}
@@ -0,0 +1,49 @@
1
+ import { type Snippet } from 'svelte';
2
+ /**
3
+ * Studio - full-screen frame for editors (survey builders, slide decks,
4
+ * page and form builders).
5
+ *
6
+ * One fixed layout that never page-scrolls:
7
+ *
8
+ * ```
9
+ * ┌ bar ─────────────────────────────────────────┐
10
+ * │ banner (optional) │
11
+ * ├ rail ──┬ canvas ─────────────┬ inspector ────┤
12
+ * │ outline│ the real thing, │ settings for │
13
+ * │ │ click to select │ the selection │
14
+ * └────────┴─────────────────────┴───────────────┘
15
+ * ```
16
+ *
17
+ * Rail, canvas and inspector scroll on their own. Below 1280px the rail
18
+ * moves into a left sheet; below 768px the inspector moves into a bottom
19
+ * sheet as well, leaving the canvas full screen. Parts read the layout
20
+ * through `getStudioContext()`, so `StudioOutline` closes its sheet after a
21
+ * pick and `StudioCanvasItem` opens the inspector sheet on phones.
22
+ *
23
+ * Leave out `rail` and `inspector` for full-width modes such as a settings
24
+ * page (render a `StudioPage` as the canvas).
25
+ */
26
+ interface Props {
27
+ /** Top bar, normally a `StudioBar` */
28
+ bar: Snippet;
29
+ /** One message slot under the bar, for conflicts and decisions that block editing */
30
+ banner?: Snippet;
31
+ /** Left column, normally a `StudioOutline` */
32
+ rail?: Snippet;
33
+ /** Main area, normally a `StudioCanvas` or `StudioPage` */
34
+ canvas: Snippet;
35
+ /** Right column, normally a `StudioInspector` */
36
+ inspector?: Snippet;
37
+ /** Accessible name of the rail and the button that opens it (default: "Outline") */
38
+ railLabel?: string;
39
+ /** Accessible name of the inspector and the button that opens it (default: "Properties") */
40
+ inspectorLabel?: string;
41
+ /** Rail sheet open below 1280px (bindable) */
42
+ railOpen?: boolean;
43
+ /** Inspector sheet open below 768px (bindable) */
44
+ inspectorOpen?: boolean;
45
+ class?: string;
46
+ }
47
+ declare const Studio: import("svelte").Component<Props, {}, "railOpen" | "inspectorOpen">;
48
+ type Studio = ReturnType<typeof Studio>;
49
+ export default Studio;