@tellegen/svelte 0.2.0 → 0.3.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 (66) hide show
  1. package/README.md +36 -5
  2. package/dist/TellegenMap.svelte +325 -163
  3. package/dist/TellegenProvider.svelte +4 -0
  4. package/dist/TellegenShell.svelte +19 -13
  5. package/dist/api.d.ts +8 -1
  6. package/dist/api.js +6 -2
  7. package/dist/colors.d.ts +7 -7
  8. package/dist/colors.js +13 -13
  9. package/dist/components/AppHeader.svelte +23 -11
  10. package/dist/components/BindingLines.svelte +3 -6
  11. package/dist/components/BusPicker.svelte +8 -2
  12. package/dist/components/ControlPanel.svelte +106 -343
  13. package/dist/components/DemandSlider.svelte +21 -23
  14. package/dist/components/DiagramCanvas.svelte +486 -0
  15. package/dist/components/DiagramCanvas.svelte.d.ts +13 -0
  16. package/dist/components/DisplayControls.svelte +4 -2
  17. package/dist/components/FormulationSelector.svelte +8 -3
  18. package/dist/components/LocalCaseDetails.svelte +55 -45
  19. package/dist/components/McResults.svelte +365 -0
  20. package/dist/components/McResults.svelte.d.ts +11 -0
  21. package/dist/components/ModelDetails.svelte +230 -0
  22. package/dist/components/ModelDetails.svelte.d.ts +7 -0
  23. package/dist/components/MulticonductorDetails.svelte +227 -78
  24. package/dist/components/NetworkStats.svelte +20 -18
  25. package/dist/components/NotificationCenter.svelte +243 -0
  26. package/dist/components/NotificationCenter.svelte.d.ts +3 -0
  27. package/dist/components/PanelCard.svelte +319 -0
  28. package/dist/components/PanelCard.svelte.d.ts +9 -0
  29. package/dist/components/PanelFrame.svelte +46 -0
  30. package/dist/components/PanelFrame.svelte.d.ts +16 -0
  31. package/dist/components/PanelHost.svelte +212 -0
  32. package/dist/components/PanelHost.svelte.d.ts +3 -0
  33. package/dist/components/PowerFlowDetails.svelte +99 -0
  34. package/dist/components/PowerFlowDetails.svelte.d.ts +3 -0
  35. package/dist/components/RatingSlider.svelte +5 -9
  36. package/dist/components/SavedNetworkDetails.svelte +214 -0
  37. package/dist/components/SavedNetworkDetails.svelte.d.ts +3 -0
  38. package/dist/components/SensitivityReadout.svelte +24 -16
  39. package/dist/components/SolveCard.svelte +46 -49
  40. package/dist/components/TopMovers.svelte +8 -3
  41. package/dist/components/TopMovers.svelte.d.ts +2 -17
  42. package/dist/components.d.ts +2 -0
  43. package/dist/components.js +2 -0
  44. package/dist/controller.svelte.d.ts +54 -33
  45. package/dist/controller.svelte.js +927 -233
  46. package/dist/diagram-labels.d.ts +9 -0
  47. package/dist/diagram-labels.js +40 -0
  48. package/dist/display.d.ts +3 -7
  49. package/dist/display.js +31 -27
  50. package/dist/format.js +5 -4
  51. package/dist/index.d.ts +10 -4
  52. package/dist/index.js +7 -2
  53. package/dist/map.d.ts +1 -1
  54. package/dist/map.js +1 -1
  55. package/dist/multiconductor.d.ts +11 -2
  56. package/dist/multiconductor.js +107 -14
  57. package/dist/notices.svelte.d.ts +29 -0
  58. package/dist/notices.svelte.js +82 -0
  59. package/dist/panels.svelte.d.ts +65 -0
  60. package/dist/panels.svelte.js +215 -0
  61. package/dist/state.svelte.d.ts +90 -23
  62. package/dist/state.svelte.js +94 -20
  63. package/dist/synthetic-layout.js +1 -0
  64. package/package.json +22 -9
  65. package/dist/study-package.d.ts +0 -8
  66. package/dist/study-package.js +0 -17
@@ -0,0 +1,243 @@
1
+ <script lang="ts">
2
+ import { onDestroy, untrack } from 'svelte';
3
+ import { formatPowerIoDiagnostic } from '@tellegen/engine';
4
+ import { getAppState, getController } from '../context.svelte.js';
5
+ import { getNoticeCenter, errorNoticeTitle } from '../notices.svelte.js';
6
+ const app = getAppState();
7
+ const ctrl = getController();
8
+ const notices = getNoticeCenter();
9
+ let root = $state.raw<HTMLElement>();
10
+ let focused = $state(false);
11
+ let hovered = $state(false);
12
+ const observed = new WeakSet<object>();
13
+ $effect(() => {
14
+ const message = app.error;
15
+ const revision = app.errorRevision;
16
+ const retry = app.errorRetry;
17
+ if (message)
18
+ untrack(() =>
19
+ notices.push({
20
+ kind: 'error',
21
+ title: errorNoticeTitle(message),
22
+ details: message,
23
+ retry: () => ctrl.retryError(),
24
+ canRetry: () =>
25
+ app.errorRevision === revision && app.error === message && app.errorRetry === retry
26
+ })
27
+ );
28
+ });
29
+ $effect(() => {
30
+ const local = app.activeLocal;
31
+ const multi = app.activeMulti;
32
+ const summary = local?.summary ?? multi?.summary;
33
+ const geo = local?.geoWarnings ?? multi?.geoWarnings;
34
+ for (const [identity, title, details] of [
35
+ [
36
+ summary,
37
+ 'Case import notes',
38
+ summary
39
+ ? [
40
+ ...summary.diagnostics.map(formatPowerIoDiagnostic),
41
+ ...('warnings' in summary ? summary.warnings : [])
42
+ ].join('\n\n')
43
+ : ''
44
+ ],
45
+ [geo, 'Coordinate import notes', geo?.join('\n\n') ?? '']
46
+ ] as Array<[object | undefined, string, string]>) {
47
+ if (identity && !observed.has(identity)) {
48
+ observed.add(identity);
49
+ if (details) untrack(() => notices.push({ kind: 'warning', title, details }));
50
+ }
51
+ }
52
+ });
53
+ $effect(() => {
54
+ notices.pause(focused || hovered || notices.expanded);
55
+ });
56
+ onDestroy(() => notices.dispose());
57
+ </script>
58
+
59
+ <div class="announcement" role="status" aria-live="polite" aria-atomic="true">
60
+ {notices.announcement}
61
+ </div>
62
+ {#if notices.entries.length}
63
+ <section
64
+ class="notifications"
65
+ aria-label="Notifications"
66
+ bind:this={root}
67
+ onfocusin={() => (focused = true)}
68
+ onfocusout={(event) => {
69
+ focused = root?.contains(event.relatedTarget as Node | null) ?? false;
70
+ }}
71
+ onpointerenter={() => (hovered = true)}
72
+ onpointerleave={() => (hovered = false)}
73
+ >
74
+ {#if notices.expanded}
75
+ <div class="recent" id="recent-notifications">
76
+ <header>
77
+ <strong>Recent messages</strong><button
78
+ onclick={() => {
79
+ focused = false;
80
+ hovered = false;
81
+ notices.clear();
82
+ }}>Clear</button
83
+ >
84
+ </header>
85
+ <ol>
86
+ {#each notices.entries as entry (entry.id)}
87
+ <li>
88
+ <details>
89
+ <summary
90
+ >{entry.title}{#if entry.count > 1}<span class="count">{entry.count}</span
91
+ >{/if}</summary
92
+ >
93
+ <pre data-testid="notification-details">{entry.details}</pre>
94
+ </details>
95
+ {#if entry.retry && entry.canRetry?.()}<button onclick={entry.retry}>Retry</button
96
+ >{/if}
97
+ </li>
98
+ {/each}
99
+ </ol>
100
+ </div>
101
+ {/if}
102
+ <div
103
+ class="notice"
104
+ data-testid="notification-toast"
105
+ data-kind={notices.active?.kind}
106
+ class:quiet={!notices.active}
107
+ >
108
+ {#if notices.active}
109
+ <span class="message">{notices.active.title}</span>
110
+ {#if notices.active.count > 1}<span
111
+ class="count"
112
+ aria-label={`${notices.active.count} occurrences`}>{notices.active.count}</span
113
+ >{/if}
114
+ {#if notices.active.retry && notices.active.canRetry?.()}<button
115
+ onclick={notices.active.retry}>Retry</button
116
+ >{/if}
117
+ {/if}
118
+ <button
119
+ class="history"
120
+ aria-expanded={notices.expanded}
121
+ aria-controls="recent-notifications"
122
+ onclick={() => (notices.expanded = !notices.expanded)}
123
+ >{notices.active ? 'Details' : 'Messages'}{#if !notices.active}<span class="count"
124
+ >{notices.entries.length}</span
125
+ >{/if}</button
126
+ >
127
+ {#if notices.active}<button
128
+ aria-label="Dismiss notification"
129
+ onclick={() => notices.dismiss()}>Close</button
130
+ >{/if}
131
+ </div>
132
+ </section>
133
+ {/if}
134
+
135
+ <style>
136
+ .announcement {
137
+ position: absolute;
138
+ width: 1px;
139
+ height: 1px;
140
+ overflow: hidden;
141
+ clip-path: inset(50%);
142
+ }
143
+ .notifications {
144
+ position: fixed;
145
+ z-index: 65;
146
+ bottom: 40px;
147
+ left: 50%;
148
+ transform: translateX(-50%);
149
+ width: min(540px, calc(100vw - 200px));
150
+ font: 12px/1.4 var(--font-display);
151
+ pointer-events: none;
152
+ }
153
+ .notice,
154
+ .recent {
155
+ pointer-events: auto;
156
+ background: var(--panel);
157
+ border: 1px solid var(--line);
158
+ border-radius: 5px;
159
+ box-shadow: var(--elev-2);
160
+ }
161
+ .notice {
162
+ display: flex;
163
+ align-items: center;
164
+ gap: 8px;
165
+ min-height: 38px;
166
+ padding: 3px 8px 3px 12px;
167
+ box-sizing: border-box;
168
+ }
169
+ .message {
170
+ min-width: 0;
171
+ flex: 1;
172
+ overflow: hidden;
173
+ text-overflow: ellipsis;
174
+ white-space: nowrap;
175
+ }
176
+ .quiet {
177
+ width: fit-content;
178
+ margin: auto;
179
+ padding-left: 5px;
180
+ background: var(--paper);
181
+ }
182
+ button {
183
+ font: inherit;
184
+ color: var(--ink);
185
+ border: 0;
186
+ background: transparent;
187
+ padding: 6px;
188
+ border-radius: 3px;
189
+ cursor: pointer;
190
+ flex-shrink: 0;
191
+ }
192
+ button:hover {
193
+ background: var(--accent-soft);
194
+ }
195
+ button:focus-visible,
196
+ summary:focus-visible {
197
+ outline: 2px solid var(--focus-ring);
198
+ outline-offset: 2px;
199
+ }
200
+ .count {
201
+ margin-left: 5px;
202
+ font: 10px var(--font-mono);
203
+ color: var(--text-secondary);
204
+ }
205
+ .recent {
206
+ margin-bottom: 6px;
207
+ max-height: min(320px, 40dvh);
208
+ overflow-y: auto;
209
+ }
210
+ header {
211
+ display: flex;
212
+ align-items: center;
213
+ justify-content: space-between;
214
+ padding: 8px 12px;
215
+ }
216
+ ol {
217
+ list-style: none;
218
+ padding: 0;
219
+ margin: 0;
220
+ }
221
+ li {
222
+ padding: 10px 12px;
223
+ border-top: 1px solid var(--line);
224
+ }
225
+ summary {
226
+ cursor: pointer;
227
+ }
228
+ pre {
229
+ font: 11px/1.5 var(--font-mono);
230
+ white-space: pre-wrap;
231
+ overflow-wrap: anywhere;
232
+ margin: 10px 0 0;
233
+ }
234
+ @media (max-width: 960px) {
235
+ .notifications {
236
+ width: calc(100vw - 24px);
237
+ bottom: 60px;
238
+ }
239
+ button {
240
+ min-height: 36px;
241
+ }
242
+ }
243
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const NotificationCenter: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type NotificationCenter = ReturnType<typeof NotificationCenter>;
3
+ export default NotificationCenter;
@@ -0,0 +1,319 @@
1
+ <script lang="ts">
2
+ import { tick } from 'svelte';
3
+ import { getPanelLayout, type PanelRegistration, type PanelSide } from '../panels.svelte.js';
4
+ let {
5
+ panel,
6
+ floating = false,
7
+ mobile = false
8
+ }: {
9
+ panel: PanelRegistration;
10
+ floating?: boolean;
11
+ mobile?: boolean;
12
+ } = $props();
13
+ const layout = getPanelLayout();
14
+ let menu = $state(false);
15
+ let card: HTMLElement;
16
+ const bounds = $derived(layout.bounds(panel));
17
+ const preferred = $derived(!floating && !mobile && layout.preferred(panel));
18
+ const dockReserve = $derived.by(() => {
19
+ const side = layout.position(panel);
20
+ return side === 'floating'
21
+ ? 0
22
+ : Math.max(0, layout.ordered(side).filter((candidate) => candidate.open).length - 1) * 142;
23
+ });
24
+ function start(event: PointerEvent, kind: 'move' | 'resize') {
25
+ if (mobile || !event.isPrimary || event.button !== 0) return;
26
+ const rect = card.getBoundingClientRect();
27
+ layout.beginGesture(panel, kind, event.clientX, event.clientY, {
28
+ x: rect.x,
29
+ y: rect.y,
30
+ width: rect.width,
31
+ height: rect.height
32
+ });
33
+ (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId);
34
+ event.preventDefault();
35
+ }
36
+ async function key(event: KeyboardEvent) {
37
+ if (mobile) return;
38
+ const panelId = panel.id;
39
+ const step = event.shiftKey ? 40 : 10;
40
+ const directions: Record<string, [number, number]> = {
41
+ ArrowLeft: [-step, 0],
42
+ ArrowRight: [step, 0],
43
+ ArrowUp: [0, -step],
44
+ ArrowDown: [0, step]
45
+ };
46
+ if (directions[event.key]) {
47
+ event.preventDefault();
48
+ const [x, y] = directions[event.key];
49
+ const rect = card.getBoundingClientRect();
50
+ layout.float(panel, {
51
+ x: rect.x + x,
52
+ y: rect.y + y,
53
+ width: rect.width,
54
+ height: rect.height
55
+ });
56
+ layout.save();
57
+ await tick();
58
+ document.querySelector<HTMLElement>(`[data-panel="${CSS.escape(panelId)}"] .title`)?.focus();
59
+ } else if (event.key === 'Escape') {
60
+ menu = false;
61
+ }
62
+ }
63
+ function resizeKey(event: KeyboardEvent) {
64
+ const step = event.shiftKey ? 40 : 10;
65
+ if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)) return;
66
+ event.preventDefault();
67
+ layout.float(panel, {
68
+ ...bounds,
69
+ width:
70
+ bounds.width + (event.key === 'ArrowRight' ? step : event.key === 'ArrowLeft' ? -step : 0),
71
+ height:
72
+ bounds.height + (event.key === 'ArrowDown' ? step : event.key === 'ArrowUp' ? -step : 0)
73
+ });
74
+ layout.save();
75
+ }
76
+ async function position(side?: PanelSide) {
77
+ const panelId = panel.id;
78
+ menu = false;
79
+ if (side) layout.dock(panel, side);
80
+ else {
81
+ layout.float(panel);
82
+ layout.save();
83
+ }
84
+ await tick();
85
+ document.querySelector<HTMLElement>(`[data-panel="${CSS.escape(panelId)}"] .title`)?.focus();
86
+ }
87
+ async function close() {
88
+ const title = panel.title;
89
+ layout.close(panel);
90
+ await tick();
91
+ document
92
+ .querySelector<HTMLElement>(`.panel-toolbar button[aria-label="${CSS.escape(title)}"]`)
93
+ ?.focus();
94
+ }
95
+ </script>
96
+
97
+ <svelte:window
98
+ onkeydown={(event) => {
99
+ if (event.key === 'Escape') menu = false;
100
+ }}
101
+ />
102
+ <aside
103
+ bind:this={card}
104
+ class="panel-frame"
105
+ class:floating
106
+ class:mobile
107
+ class:preferred
108
+ data-panel={panel.id}
109
+ aria-label={panel.title}
110
+ style:left={floating ? `${bounds.x}px` : undefined}
111
+ style:top={floating ? `${bounds.y}px` : undefined}
112
+ style:width={floating ? `${bounds.width}px` : undefined}
113
+ style:height={floating ? `${bounds.height}px` : undefined}
114
+ style:max-height={preferred ? `calc(100% - ${dockReserve}px)` : undefined}
115
+ >
116
+ <header>
117
+ {#if mobile}
118
+ <h2 class="title">{panel.title}</h2>
119
+ {:else}
120
+ <button
121
+ class="title"
122
+ aria-label={`Move ${panel.title} panel`}
123
+ title="Click to expand. Drag or use arrow keys to move. Open panel options to dock."
124
+ onpointerdown={(event) => start(event, 'move')}
125
+ onkeydown={key}
126
+ onclick={() => layout.activate(panel)}>{panel.title}</button
127
+ >
128
+ {/if}
129
+ <div class="header-actions">{@render panel.headerActions?.()}</div>
130
+ {#if !mobile}<div class="menu-anchor">
131
+ <button
132
+ class="icon"
133
+ aria-label={`${panel.title} panel options`}
134
+ aria-expanded={menu}
135
+ onclick={() => (menu = !menu)}
136
+ title="Panel options"
137
+ >
138
+ <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"
139
+ ><circle cx="3" cy="8" r="1" /><circle cx="8" cy="8" r="1" /><circle
140
+ cx="13"
141
+ cy="8"
142
+ r="1"
143
+ /></svg
144
+ >
145
+ </button>
146
+ {#if menu}<div class="panel-menu">
147
+ <button onclick={() => position('left')}>Dock left</button>
148
+ <button onclick={() => position('right')}>Dock right</button>
149
+ <button onclick={() => position()}>Detach</button>
150
+ </div>{/if}
151
+ </div>{/if}
152
+ <button
153
+ class="icon"
154
+ aria-label={`Close ${panel.title.toLowerCase()} panel`}
155
+ title="Close panel"
156
+ onclick={close}
157
+ >
158
+ <svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"
159
+ ><path d="m4 4 8 8m0-8-8 8" stroke="currentColor" stroke-width="1.5" fill="none" /></svg
160
+ >
161
+ </button>
162
+ </header>
163
+ <div class="panel-content">{@render panel.children()}</div>
164
+ {#if floating}<button
165
+ class="resize"
166
+ aria-label={`Resize ${panel.title} panel`}
167
+ title="Drag to resize, or use arrow keys"
168
+ onpointerdown={(event) => start(event, 'resize')}
169
+ onkeydown={resizeKey}
170
+ >
171
+ <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"
172
+ ><path d="m3 10 7-7m-3 7 3-3" stroke="currentColor" /></svg
173
+ >
174
+ </button>{/if}
175
+ </aside>
176
+
177
+ <style>
178
+ .panel-frame {
179
+ display: flex;
180
+ flex-direction: column;
181
+ min-height: 130px;
182
+ max-height: 100%;
183
+ flex: 0 1 auto;
184
+ pointer-events: auto;
185
+ background: var(--paper);
186
+ color: var(--ink);
187
+ border: 1px solid var(--line);
188
+ border-radius: 5px;
189
+ box-shadow: var(--elev-1);
190
+ font: 13px/1.5 var(--font-display);
191
+ position: relative;
192
+ }
193
+ header {
194
+ display: flex;
195
+ align-items: center;
196
+ gap: 5px;
197
+ padding: 8px 10px 8px 14px;
198
+ flex: none;
199
+ border-bottom: 1px solid var(--line);
200
+ }
201
+ button {
202
+ color: inherit;
203
+ font: inherit;
204
+ cursor: pointer;
205
+ border: 0;
206
+ background: transparent;
207
+ border-radius: 3px;
208
+ }
209
+ button:hover {
210
+ background: var(--accent-soft);
211
+ }
212
+ button:focus-visible {
213
+ outline: 2px solid var(--focus-ring);
214
+ outline-offset: 2px;
215
+ }
216
+ .title {
217
+ flex: 1;
218
+ min-width: 0;
219
+ text-align: left;
220
+ font-size: inherit;
221
+ font-weight: 600;
222
+ margin: 0;
223
+ padding: 3px 0;
224
+ cursor: grab;
225
+ touch-action: none;
226
+ }
227
+ .title:active {
228
+ cursor: grabbing;
229
+ }
230
+ .icon {
231
+ display: grid;
232
+ place-items: center;
233
+ width: 26px;
234
+ height: 28px;
235
+ padding: 0;
236
+ flex: none;
237
+ }
238
+ .icon svg {
239
+ fill: currentColor;
240
+ }
241
+ .header-actions {
242
+ display: flex;
243
+ align-items: center;
244
+ gap: 8px;
245
+ }
246
+ .header-actions :global(button) {
247
+ font: 11px/1.3 var(--font-display);
248
+ padding: 4px;
249
+ border: 0;
250
+ background: none;
251
+ color: var(--text-secondary);
252
+ }
253
+ .panel-content {
254
+ min-height: 0;
255
+ overflow: auto;
256
+ overscroll-behavior: contain;
257
+ display: flex;
258
+ flex-direction: column;
259
+ border-radius: 0 0 5px 5px;
260
+ }
261
+ .menu-anchor {
262
+ position: relative;
263
+ }
264
+ .panel-menu {
265
+ position: absolute;
266
+ z-index: 3;
267
+ top: 100%;
268
+ right: 0;
269
+ min-width: 130px;
270
+ padding: 5px;
271
+ background: var(--paper);
272
+ border: 1px solid var(--line);
273
+ border-radius: 4px;
274
+ box-shadow: var(--elev-2);
275
+ }
276
+ .panel-menu button {
277
+ display: block;
278
+ padding: 7px 10px;
279
+ width: 100%;
280
+ text-align: left;
281
+ white-space: nowrap;
282
+ font-size: 12px;
283
+ }
284
+ .preferred {
285
+ flex-shrink: 0;
286
+ }
287
+ .floating {
288
+ position: fixed;
289
+ min-height: 0;
290
+ z-index: 40;
291
+ max-height: none;
292
+ box-shadow: var(--elev-2);
293
+ }
294
+ .floating .panel-content {
295
+ flex: 1;
296
+ padding-bottom: 12px;
297
+ }
298
+ .resize {
299
+ position: absolute;
300
+ bottom: 0;
301
+ right: 0;
302
+ width: 20px;
303
+ height: 20px;
304
+ cursor: nwse-resize;
305
+ touch-action: none;
306
+ color: var(--text-secondary);
307
+ }
308
+ .mobile {
309
+ min-height: 0;
310
+ height: 100%;
311
+ border-radius: 10px 10px 0 0;
312
+ }
313
+ .mobile .panel-content {
314
+ flex: 1;
315
+ }
316
+ .mobile .title {
317
+ cursor: default;
318
+ }
319
+ </style>
@@ -0,0 +1,9 @@
1
+ import { type PanelRegistration } from '../panels.svelte.js';
2
+ type $$ComponentProps = {
3
+ panel: PanelRegistration;
4
+ floating?: boolean;
5
+ mobile?: boolean;
6
+ };
7
+ declare const PanelCard: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type PanelCard = ReturnType<typeof PanelCard>;
9
+ export default PanelCard;
@@ -0,0 +1,46 @@
1
+ <script lang="ts">
2
+ import { onMount, untrack, type Snippet } from 'svelte';
3
+ import { getPanelLayout, type PanelSide } from '../panels.svelte.js';
4
+ let {
5
+ id,
6
+ title,
7
+ side,
8
+ order = 0,
9
+ width = 340,
10
+ open = $bindable(false),
11
+ children,
12
+ headerActions,
13
+ onopen
14
+ }: {
15
+ id: string;
16
+ title: string;
17
+ side: PanelSide;
18
+ order?: number;
19
+ width?: number;
20
+ open?: boolean;
21
+ children: Snippet;
22
+ headerActions?: Snippet;
23
+ onopen?: () => void;
24
+ } = $props();
25
+ const layout = getPanelLayout();
26
+ onMount(() =>
27
+ layout.register({
28
+ id,
29
+ title,
30
+ side,
31
+ order,
32
+ width,
33
+ open,
34
+ children,
35
+ headerActions,
36
+ onopen,
37
+ setOpen: (value) => {
38
+ open = value;
39
+ }
40
+ })
41
+ );
42
+ $effect(() => {
43
+ const values = { id, open, title, width };
44
+ untrack(() => layout.update(values.id, values.open, values.title, values.width));
45
+ });
46
+ </script>
@@ -0,0 +1,16 @@
1
+ import { type Snippet } from 'svelte';
2
+ import { type PanelSide } from '../panels.svelte.js';
3
+ type $$ComponentProps = {
4
+ id: string;
5
+ title: string;
6
+ side: PanelSide;
7
+ order?: number;
8
+ width?: number;
9
+ open?: boolean;
10
+ children: Snippet;
11
+ headerActions?: Snippet;
12
+ onopen?: () => void;
13
+ };
14
+ declare const PanelFrame: import("svelte").Component<$$ComponentProps, {}, "open">;
15
+ type PanelFrame = ReturnType<typeof PanelFrame>;
16
+ export default PanelFrame;