@svgrid/grid 2.1.21 → 2.1.23
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 +9 -2
- package/dist/DockNodeView.svelte +306 -0
- package/dist/DockNodeView.svelte.d.ts +7 -0
- package/dist/DockPopoutHost.svelte +17 -0
- package/dist/DockPopoutHost.svelte.d.ts +14 -0
- package/dist/GridMenus.svelte +83 -3
- package/dist/SvAutoComplete.svelte +5 -3
- package/dist/SvCarousel.svelte +20 -42
- package/dist/SvColorInput.svelte +4 -3
- package/dist/SvColorInput.svelte.d.ts +1 -1
- package/dist/SvComboBox.svelte +7 -3
- package/dist/SvCommand.svelte +28 -85
- package/dist/SvCountryInput.svelte +1 -1
- package/dist/SvDateRangeInput.svelte +1 -1
- package/dist/SvDateTimePicker.svelte +9 -3
- package/dist/SvDateTimePicker.svelte.d.ts +3 -0
- package/dist/SvDockLayout.svelte +244 -0
- package/dist/SvDockLayout.svelte.d.ts +77 -0
- package/dist/SvDockManager.svelte +922 -0
- package/dist/SvDockManager.svelte.d.ts +106 -0
- package/dist/SvDrawer.svelte +69 -47
- package/dist/SvDrawer.svelte.d.ts +4 -0
- package/dist/SvDropDownList.svelte +85 -13
- package/dist/SvDropDownList.svelte.d.ts +3 -0
- package/dist/SvField.svelte +21 -4
- package/dist/SvField.svelte.d.ts +1 -0
- package/dist/SvForm.svelte +201 -91
- package/dist/SvForm.svelte.d.ts +15 -22
- package/dist/SvGrid.controller.svelte.d.ts +32 -1
- package/dist/SvGrid.controller.svelte.js +156 -27
- package/dist/SvGrid.css +193 -5
- package/dist/SvGrid.svelte +357 -12
- package/dist/SvGrid.types.d.ts +452 -1
- package/dist/SvGridDropdown.svelte +8 -1
- package/dist/SvGridSelect.svelte +1 -0
- package/dist/SvListBox.svelte +36 -14
- package/dist/SvMaskedInput.svelte +4 -3
- package/dist/SvMaskedInput.svelte.d.ts +1 -1
- package/dist/SvMenu.svelte +1 -1
- package/dist/SvMenuList.svelte +23 -71
- package/dist/SvMenuList.svelte.d.ts +2 -13
- package/dist/SvModal.svelte +12 -29
- package/dist/SvMultiSelect.svelte +1 -0
- package/dist/SvNumberInput.svelte +4 -3
- package/dist/SvNumberInput.svelte.d.ts +1 -1
- package/dist/SvPagination.svelte +18 -18
- package/dist/SvPasswordInput.svelte +3 -2
- package/dist/SvPasswordInput.svelte.d.ts +1 -1
- package/dist/SvPhoneInput.svelte +3 -2
- package/dist/SvPhoneInput.svelte.d.ts +1 -1
- package/dist/SvPopover.svelte +1 -1
- package/dist/SvStepper.svelte +6 -9
- package/dist/SvTagsInput.svelte +3 -2
- package/dist/SvTagsInput.svelte.d.ts +1 -1
- package/dist/SvTooltip.svelte +12 -23
- package/dist/SvTour.svelte +32 -4
- package/dist/SvTreeSelect.svelte +1 -0
- package/dist/build-api.js +76 -54
- package/dist/cdn/svgrid.js +10907 -6444
- package/dist/cdn/svgrid.svelte-external.js +9622 -5475
- package/dist/columns.js +6 -0
- package/dist/conditional-formatting.d.ts +67 -3
- package/dist/conditional-formatting.js +124 -17
- package/dist/createCarousel.svelte.d.ts +72 -0
- package/dist/createCarousel.svelte.js +80 -0
- package/dist/createCommand.svelte.d.ts +62 -0
- package/dist/createCommand.svelte.js +109 -0
- package/dist/createForm.svelte.d.ts +62 -0
- package/dist/createForm.svelte.js +374 -0
- package/dist/createMenu.svelte.d.ts +88 -0
- package/dist/createMenu.svelte.js +153 -0
- package/dist/createOverlay.svelte.d.ts +38 -0
- package/dist/createOverlay.svelte.js +68 -0
- package/dist/createPagination.d.ts +74 -0
- package/dist/createPagination.js +71 -0
- package/dist/createPopoverSelect.svelte.js +6 -12
- package/dist/createStepper.d.ts +44 -0
- package/dist/createStepper.js +26 -0
- package/dist/createTooltip.svelte.d.ts +32 -0
- package/dist/createTooltip.svelte.js +61 -0
- package/dist/datetime/timezone.d.ts +36 -0
- package/dist/datetime/timezone.js +110 -0
- package/dist/dock-context.d.ts +52 -0
- package/dist/dock-context.js +1 -0
- package/dist/dock-manager-model.d.ts +136 -0
- package/dist/dock-manager-model.js +514 -0
- package/dist/dock-model.d.ts +105 -0
- package/dist/dock-model.js +288 -0
- package/dist/dock-popout.d.ts +30 -0
- package/dist/dock-popout.js +81 -0
- package/dist/editor-contract.d.ts +2 -0
- package/dist/filter-operators.d.ts +6 -0
- package/dist/filter-operators.js +35 -2
- package/dist/filtering/excel-filters.d.ts +18 -1
- package/dist/filtering/excel-filters.js +56 -0
- package/dist/form-field.d.ts +73 -0
- package/dist/form-field.js +15 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.js +40 -4
- package/dist/list-nav.d.ts +18 -0
- package/dist/list-nav.js +30 -0
- package/dist/list-option.d.ts +2 -0
- package/dist/menu-item.d.ts +20 -0
- package/dist/menu-item.js +1 -0
- package/dist/menus.d.ts +5 -0
- package/dist/menus.js +68 -16
- package/dist/panel-resize.d.ts +25 -0
- package/dist/panel-resize.js +40 -0
- package/dist/popover.d.ts +29 -3
- package/dist/popover.js +22 -6
- package/dist/recurrence.d.ts +33 -7
- package/dist/recurrence.js +151 -13
- package/dist/row-drag.d.ts +8 -0
- package/dist/row-drag.js +11 -0
- package/dist/scheduler-ical.d.ts +30 -0
- package/dist/scheduler-ical.js +180 -0
- package/dist/scheduler-model.d.ts +269 -0
- package/dist/scheduler-model.js +602 -0
- package/dist/scheduler-view.svelte.d.ts +22 -0
- package/dist/scheduler-view.svelte.js +13 -0
- package/dist/spreadsheet.js +20 -12
- package/dist/ui-app.types.d.ts +3 -0
- package/package.json +5 -1
- package/src/DockNodeView.svelte +306 -0
- package/src/DockPopoutHost.svelte +17 -0
- package/src/GridMenus.svelte +83 -3
- package/src/SvAutoComplete.svelte +5 -3
- package/src/SvCarousel.svelte +20 -42
- package/src/SvCarousel.test.ts +68 -0
- package/src/SvColorInput.svelte +4 -3
- package/src/SvComboBox.svelte +7 -3
- package/src/SvCommand.svelte +28 -85
- package/src/SvCommand.test.ts +78 -0
- package/src/SvCountryInput.svelte +1 -1
- package/src/SvDateRangeInput.svelte +1 -1
- package/src/SvDateTimePicker.svelte +9 -3
- package/src/SvDockLayout.svelte +244 -0
- package/src/SvDockManager.svelte +922 -0
- package/src/SvDrawer.svelte +69 -47
- package/src/SvDropDownList.svelte +85 -13
- package/src/SvField.svelte +21 -4
- package/src/SvForm.svelte +201 -91
- package/src/SvForm.test.ts +299 -0
- package/src/SvGrid.controller.svelte.ts +152 -28
- package/src/SvGrid.css +193 -5
- package/src/SvGrid.svelte +357 -12
- package/src/SvGrid.types.ts +463 -1
- package/src/SvGridDropdown.svelte +8 -1
- package/src/SvGridSelect.svelte +1 -0
- package/src/SvListBox.svelte +36 -14
- package/src/SvMaskedInput.svelte +4 -3
- package/src/SvMenu.svelte +1 -1
- package/src/SvMenu.test.ts +24 -0
- package/src/SvMenuList.svelte +23 -71
- package/src/SvModal.svelte +12 -29
- package/src/SvModal.test.ts +82 -0
- package/src/SvMultiSelect.svelte +1 -0
- package/src/SvNumberInput.svelte +4 -3
- package/src/SvPagination.svelte +18 -18
- package/src/SvPasswordInput.svelte +3 -2
- package/src/SvPhoneInput.svelte +3 -2
- package/src/SvPopover.svelte +1 -1
- package/src/SvStepper.svelte +6 -9
- package/src/SvTagsInput.svelte +3 -2
- package/src/SvTooltip.svelte +12 -23
- package/src/SvTooltip.test.ts +73 -0
- package/src/SvTour.svelte +32 -4
- package/src/SvTreeSelect.svelte +1 -0
- package/src/build-api.ts +74 -51
- package/src/columns.ts +5 -0
- package/src/conditional-formatting.test.ts +104 -0
- package/src/conditional-formatting.ts +176 -19
- package/src/createCarousel.svelte.ts +104 -0
- package/src/createCommand.svelte.ts +125 -0
- package/src/createForm.svelte.ts +333 -0
- package/src/createMenu.svelte.ts +202 -0
- package/src/createOverlay.svelte.ts +101 -0
- package/src/createPagination.test.ts +65 -0
- package/src/createPagination.ts +84 -0
- package/src/createPopoverSelect.svelte.ts +5 -9
- package/src/createStepper.test.ts +62 -0
- package/src/createStepper.ts +61 -0
- package/src/createTooltip.svelte.ts +86 -0
- package/src/datetime/timezone.test.ts +69 -0
- package/src/datetime/timezone.ts +134 -0
- package/src/dock-context.ts +47 -0
- package/src/dock-manager-model.test.ts +275 -0
- package/src/dock-manager-model.ts +593 -0
- package/src/dock-model.test.ts +163 -0
- package/src/dock-model.ts +368 -0
- package/src/dock-popout.ts +94 -0
- package/src/editor-contract.ts +2 -0
- package/src/filter-operators.test.ts +59 -0
- package/src/filter-operators.ts +35 -2
- package/src/filtering/excel-filters.test.ts +86 -1
- package/src/filtering/excel-filters.ts +65 -0
- package/src/form-field.ts +87 -0
- package/src/index.ts +158 -4
- package/src/list-nav.test.ts +49 -0
- package/src/list-nav.ts +29 -0
- package/src/list-option.ts +2 -0
- package/src/menu-item.ts +21 -0
- package/src/menus.test.ts +48 -0
- package/src/menus.ts +73 -16
- package/src/panel-resize.ts +52 -0
- package/src/popover.bounds.test.ts +93 -0
- package/src/popover.ts +51 -7
- package/src/recurrence.test.ts +88 -1
- package/src/recurrence.ts +166 -15
- package/src/row-drag.ts +10 -0
- package/src/scheduler-ical.test.ts +68 -0
- package/src/scheduler-ical.ts +180 -0
- package/src/scheduler-model.test.ts +546 -0
- package/src/scheduler-model.ts +853 -0
- package/src/scheduler-view.svelte.ts +33 -0
- package/src/spreadsheet.ts +20 -12
- package/src/svgrid.effect-driven-filter.svelte.test.ts +116 -0
- package/src/ui-app.types.ts +3 -0
- package/src/virtual.test.ts +6 -4
|
@@ -14,6 +14,6 @@ type Props = SvEditorProps & {
|
|
|
14
14
|
/** Override the built-in popover strings. */
|
|
15
15
|
messages?: Partial<ColorMessages>;
|
|
16
16
|
};
|
|
17
|
-
declare const SvColorInput: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
declare const SvColorInput: import("svelte").Component<Props, {}, "value">;
|
|
18
18
|
type SvColorInput = ReturnType<typeof SvColorInput>;
|
|
19
19
|
export default SvColorInput;
|
package/dist/SvComboBox.svelte
CHANGED
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
let inputEl = $state<HTMLInputElement | null>(null)
|
|
66
66
|
let fieldEl = $state<HTMLDivElement | null>(null)
|
|
67
67
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
68
|
-
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
|
|
68
|
+
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
|
|
69
69
|
|
|
70
70
|
// Remote data source: fetch options as the query changes (debounced).
|
|
71
71
|
let remoteOptions = $state<ListOption[]>([])
|
|
@@ -111,7 +111,11 @@
|
|
|
111
111
|
|
|
112
112
|
function updatePos() {
|
|
113
113
|
if (!fieldEl) return
|
|
114
|
-
|
|
114
|
+
// estimatedHeight only picks the flip direction (up vs down); the panel's
|
|
115
|
+
// real max-height comes from availHeight below, so it takes its natural
|
|
116
|
+
// content height (options + group labels) and scrolls only when it truly
|
|
117
|
+
// overflows the room - never from an estimate that under-counts a row.
|
|
118
|
+
rect = anchoredRect(fieldEl.getBoundingClientRect(), { estimatedHeight: Math.min(combo.filtered.length, 8) * 34 + 12 })
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
// Position + reposition + outside-click close are render concerns (need the DOM).
|
|
@@ -150,7 +154,7 @@
|
|
|
150
154
|
</SvField>
|
|
151
155
|
|
|
152
156
|
{#if combo.open}
|
|
153
|
-
<div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody use:popIn={{ up: rect.openUpward }} style:position="fixed" style:top={`${rect.top}px`} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} {...combo.listboxProps()}>
|
|
157
|
+
<div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody use:popIn={{ up: rect.openUpward }} style:position="fixed" style:top={rect.openUpward ? undefined : `${rect.top}px`} style:bottom={rect.openUpward ? `${rect.bottom}px` : undefined} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} style:max-height={`${Math.min(rect.availHeight, 288)}px`} {...combo.listboxProps()}>
|
|
154
158
|
{#if loading}
|
|
155
159
|
<div class="sv-ddl__empty sv-ddl__loading"><svg class="sv-ddl__spin" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M21 12a9 9 0 1 1-6.2-8.6" /></svg>{M.loading}</div>
|
|
156
160
|
{:else if loadOptions && !searched && combo.query.trim().length < minLength}
|
package/dist/SvCommand.svelte
CHANGED
|
@@ -16,10 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { CommandItem } from './ui-app.types'
|
|
18
18
|
import { portalToBody, popIn } from './popover'
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import { createDismissableLayer } from './a11y/dismissable'
|
|
22
|
-
import { fuzzyScore } from './fuzzy'
|
|
19
|
+
import { createOverlay } from './createOverlay.svelte'
|
|
20
|
+
import { createCommand } from './createCommand.svelte'
|
|
23
21
|
|
|
24
22
|
type Props = {
|
|
25
23
|
open?: boolean
|
|
@@ -42,72 +40,29 @@
|
|
|
42
40
|
hotkey = 'mod+k',
|
|
43
41
|
}: Props = $props()
|
|
44
42
|
|
|
45
|
-
let query = $state('')
|
|
46
|
-
let active = $state(0)
|
|
47
43
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
48
44
|
let inputEl = $state<HTMLInputElement | null>(null)
|
|
49
45
|
|
|
50
|
-
const results = $derived.by(() => {
|
|
51
|
-
const q = query.trim()
|
|
52
|
-
const list = commands.filter((c) => !c.disabled)
|
|
53
|
-
if (!q) return list
|
|
54
|
-
return list
|
|
55
|
-
.map((c) => ({ c, s: fuzzyScore(`${c.label} ${c.keywords ?? ''} ${c.group ?? ''}`, q) }))
|
|
56
|
-
.filter((x) => x.s !== null)
|
|
57
|
-
.sort((a, b) => (b.s as number) - (a.s as number))
|
|
58
|
-
.map((x) => x.c)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
46
|
function close() { open = false; onClose?.() }
|
|
62
|
-
function run(cmd: CommandItem | undefined) {
|
|
63
|
-
if (!cmd || cmd.disabled) return
|
|
64
|
-
close()
|
|
65
|
-
onRun?.(cmd)
|
|
66
|
-
cmd.onRun?.()
|
|
67
|
-
}
|
|
68
|
-
function move(delta: number) {
|
|
69
|
-
const n = results.length
|
|
70
|
-
if (!n) return
|
|
71
|
-
active = (active + delta + n) % n
|
|
72
|
-
queueMicrotask(() =>
|
|
73
|
-
panelEl?.querySelector(`[data-idx="${active}"]`)?.scrollIntoView({ block: 'nearest' }),
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
function onKeydown(e: KeyboardEvent) {
|
|
77
|
-
if (e.key === 'ArrowDown') { e.preventDefault(); move(1) }
|
|
78
|
-
else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1) }
|
|
79
|
-
else if (e.key === 'Enter') { e.preventDefault(); run(results[active]) }
|
|
80
|
-
// Escape + outside-click are owned by the dismissable layer.
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Reset active whenever the query changes.
|
|
84
|
-
$effect(() => { void query; active = 0 })
|
|
85
47
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
48
|
+
// Fuzzy filter + roving + run + hotkey live in the command core; the overlay
|
|
49
|
+
// lifecycle (focus-trap into the input, scroll-lock, Escape/outside dismissal)
|
|
50
|
+
// is the shared dialog core.
|
|
51
|
+
const cmd = createCommand({
|
|
52
|
+
commands: () => commands,
|
|
53
|
+
open: () => open,
|
|
54
|
+
onRun,
|
|
55
|
+
onClose: close,
|
|
56
|
+
onToggleOpen: () => (open = !open),
|
|
57
|
+
hotkey: () => hotkey,
|
|
58
|
+
scrollActiveIntoView: (i) =>
|
|
59
|
+
queueMicrotask(() => panelEl?.querySelector(`[data-idx="${i}"]`)?.scrollIntoView({ block: 'nearest' })),
|
|
97
60
|
})
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const onKey = (e: KeyboardEvent) => {
|
|
104
|
-
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === key) {
|
|
105
|
-
e.preventDefault()
|
|
106
|
-
open = !open
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
window.addEventListener('keydown', onKey)
|
|
110
|
-
return () => window.removeEventListener('keydown', onKey)
|
|
61
|
+
const overlay = createOverlay({
|
|
62
|
+
open: () => open,
|
|
63
|
+
getDialog: () => panelEl,
|
|
64
|
+
onClose: close,
|
|
65
|
+
initialFocus: () => inputEl,
|
|
111
66
|
})
|
|
112
67
|
</script>
|
|
113
68
|
|
|
@@ -117,11 +72,8 @@
|
|
|
117
72
|
bind:this={panelEl}
|
|
118
73
|
class="sv-cmd"
|
|
119
74
|
use:popIn={{}}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
aria-label="Command palette"
|
|
123
|
-
tabindex="-1"
|
|
124
|
-
onkeydown={onKeydown}
|
|
75
|
+
{...overlay.dialogProps({ label: 'Command palette' })}
|
|
76
|
+
onkeydown={cmd.onKeydown}
|
|
125
77
|
>
|
|
126
78
|
<div class="sv-cmd__search">
|
|
127
79
|
<span class="sv-cmd__searchicon" aria-hidden="true">⌕</span>
|
|
@@ -129,29 +81,20 @@
|
|
|
129
81
|
bind:this={inputEl}
|
|
130
82
|
class="sv-cmd__input"
|
|
131
83
|
type="text"
|
|
132
|
-
|
|
133
|
-
aria-expanded="true"
|
|
134
|
-
aria-controls="sv-cmd-list"
|
|
135
|
-
aria-activedescendant={results[active] ? `sv-cmd-opt-${active}` : undefined}
|
|
84
|
+
aria-label="Command palette search"
|
|
136
85
|
{placeholder}
|
|
137
|
-
|
|
138
|
-
oninput={(e) => (query = e.currentTarget.value)}
|
|
86
|
+
{...cmd.inputProps()}
|
|
139
87
|
/>
|
|
140
88
|
</div>
|
|
141
|
-
<ul class="sv-cmd__list"
|
|
142
|
-
{#each results as c, i (c.id)}
|
|
143
|
-
{#if !query.trim() && c.group && c.group !== results[i - 1]?.group}
|
|
89
|
+
<ul class="sv-cmd__list" aria-label="Commands" {...cmd.listProps()}>
|
|
90
|
+
{#each cmd.results as c, i (c.id)}
|
|
91
|
+
{#if !cmd.query.trim() && c.group && c.group !== cmd.results[i - 1]?.group}
|
|
144
92
|
<li class="sv-cmd__group" role="presentation">{c.group}</li>
|
|
145
93
|
{/if}
|
|
146
94
|
<li
|
|
147
|
-
id={`sv-cmd-opt-${i}`}
|
|
148
|
-
data-idx={i}
|
|
149
95
|
class="sv-cmd__opt"
|
|
150
|
-
class:is-active={i
|
|
151
|
-
|
|
152
|
-
aria-selected={i === active}
|
|
153
|
-
onpointermove={() => (active = i)}
|
|
154
|
-
onclick={() => run(c)}
|
|
96
|
+
class:is-active={cmd.isActive(i)}
|
|
97
|
+
{...cmd.optionProps(i)}
|
|
155
98
|
>
|
|
156
99
|
{#if c.icon}<span class="sv-cmd__icon" aria-hidden="true">{c.icon}</span>{/if}
|
|
157
100
|
<span class="sv-cmd__label">{c.label}</span>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
let triggerEl = $state<HTMLButtonElement | null>(null)
|
|
55
55
|
let searchEl = $state<HTMLInputElement | null>(null)
|
|
56
56
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
57
|
-
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
|
|
57
|
+
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
|
|
58
58
|
|
|
59
59
|
const ci = createCountryInput({
|
|
60
60
|
value: () => value,
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
// --- Portalled popover positioning (DOM-bound; stays in the component) -------
|
|
119
119
|
let triggerEl = $state<HTMLDivElement | null>(null)
|
|
120
120
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
121
|
-
let panelRect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
|
|
121
|
+
let panelRect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
|
|
122
122
|
|
|
123
123
|
function updatePos() {
|
|
124
124
|
if (!triggerEl) return
|
|
@@ -54,6 +54,9 @@
|
|
|
54
54
|
stepMinutes?: number
|
|
55
55
|
/** Open the dropdown as soon as the field is focused (grid in-cell editing). */
|
|
56
56
|
autoOpen?: boolean
|
|
57
|
+
/** Stretch the field to fill its container (100% width) instead of the fixed
|
|
58
|
+
* default width - used when mounted as a grid cell editor. */
|
|
59
|
+
block?: boolean
|
|
57
60
|
/** Animate the calendar's month/drill navigation (honors reduced-motion). */
|
|
58
61
|
animate?: boolean | 'slide' | 'fade'
|
|
59
62
|
/** Override the built-in strings (tabs, dialog, clear/open aria-labels). */
|
|
@@ -82,6 +85,7 @@
|
|
|
82
85
|
spinButtons = false,
|
|
83
86
|
stepMinutes = 1,
|
|
84
87
|
autoOpen = false,
|
|
88
|
+
block = false,
|
|
85
89
|
animate = false,
|
|
86
90
|
ariaLabel,
|
|
87
91
|
invalid = false,
|
|
@@ -130,7 +134,7 @@
|
|
|
130
134
|
// --- Portalled dropdown positioning (DOM-bound; stays in the component) -----
|
|
131
135
|
let triggerEl = $state<HTMLDivElement | null>(null)
|
|
132
136
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
133
|
-
let panelRect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
|
|
137
|
+
let panelRect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
|
|
134
138
|
|
|
135
139
|
function updatePos() {
|
|
136
140
|
if (!triggerEl) return
|
|
@@ -171,8 +175,8 @@
|
|
|
171
175
|
}
|
|
172
176
|
</script>
|
|
173
177
|
|
|
174
|
-
<SvField id={uid} {label} {hint} {error} {required} {dir}>
|
|
175
|
-
<div class="sv-dtp" class:sv-dtp--disabled={disabled} dir={resolvedDir} onfocusout={onRootFocusOut}>
|
|
178
|
+
<SvField id={uid} {label} {hint} {error} {required} {dir} {block}>
|
|
179
|
+
<div class="sv-dtp" class:sv-dtp--disabled={disabled} class:sv-dtp--block={block} dir={resolvedDir} onfocusout={onRootFocusOut}>
|
|
176
180
|
<div class="sv-dtp__field" class:is-invalid={invalid} bind:this={triggerEl}>
|
|
177
181
|
{#if spinButtons}
|
|
178
182
|
<div class="sv-dtp__spin">
|
|
@@ -256,6 +260,8 @@
|
|
|
256
260
|
--_radius: var(--sg-radius, 8px);
|
|
257
261
|
display: inline-block; position: relative; width: 240px; color: var(--_fg);
|
|
258
262
|
}
|
|
263
|
+
/* Fill the container (grid cell editor) instead of the fixed default width. */
|
|
264
|
+
.sv-dtp--block { display: block; width: 100%; }
|
|
259
265
|
.sv-dtp--disabled { opacity: 0.6; }
|
|
260
266
|
.sv-dtp__field {
|
|
261
267
|
display: flex; align-items: center; gap: 2px;
|
|
@@ -36,6 +36,9 @@ type Props = SvEditorProps & {
|
|
|
36
36
|
stepMinutes?: number;
|
|
37
37
|
/** Open the dropdown as soon as the field is focused (grid in-cell editing). */
|
|
38
38
|
autoOpen?: boolean;
|
|
39
|
+
/** Stretch the field to fill its container (100% width) instead of the fixed
|
|
40
|
+
* default width - used when mounted as a grid cell editor. */
|
|
41
|
+
block?: boolean;
|
|
39
42
|
/** Animate the calendar's month/drill navigation (honors reduced-motion). */
|
|
40
43
|
animate?: boolean | 'slide' | 'fade';
|
|
41
44
|
/** Override the built-in strings (tabs, dialog, clear/open aria-labels). */
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvDockLayout - a basic docking layout: nested resizable split groups and
|
|
4
|
+
* tabbed leaves, with drag-to-dock. Drag a pane's tab onto another pane and
|
|
5
|
+
* drop it on an edge (split) or the centre (add as a tab); drag the splitters
|
|
6
|
+
* to resize; close panes. The whole layout is a plain `DockNode` tree you pass
|
|
7
|
+
* in and bind - every gesture is a pure transform from `dock-model`, so state
|
|
8
|
+
* is serializable and testable. Inspired by Smart's `smart-layout`.
|
|
9
|
+
*
|
|
10
|
+
* Usage: build a tree with `dockGroup` / `dockTabs` / `dockPane`, bind it, and
|
|
11
|
+
* provide a `pane` snippet that renders each pane's content by id:
|
|
12
|
+
*
|
|
13
|
+
* let layout = $state(dockGroup('row', [
|
|
14
|
+
* dockTabs([dockPane('files', 'Files')]),
|
|
15
|
+
* dockTabs([dockPane('editor', 'Editor')]),
|
|
16
|
+
* ]))
|
|
17
|
+
*
|
|
18
|
+
* <SvDockLayout bind:layout>
|
|
19
|
+
* {#snippet pane(p)} ...render by p.id... {/snippet}
|
|
20
|
+
* </SvDockLayout>
|
|
21
|
+
*/
|
|
22
|
+
import { setContext, type Snippet } from 'svelte'
|
|
23
|
+
import { DOCK_CONTEXT, type DockContext } from './dock-context'
|
|
24
|
+
import DockNodeView from './DockNodeView.svelte'
|
|
25
|
+
import {
|
|
26
|
+
setActive,
|
|
27
|
+
setSizes,
|
|
28
|
+
removePane,
|
|
29
|
+
movePane,
|
|
30
|
+
type DockNode,
|
|
31
|
+
type DockPane,
|
|
32
|
+
type DockZone,
|
|
33
|
+
} from './dock-model'
|
|
34
|
+
|
|
35
|
+
type Props = {
|
|
36
|
+
/** The layout tree. Bindable - every dock/resize/close writes a new tree. */
|
|
37
|
+
layout: DockNode
|
|
38
|
+
/** Renders each pane's content, keyed off the `DockPane`. */
|
|
39
|
+
pane: Snippet<[DockPane]>
|
|
40
|
+
/** Notified after any structural change (dock / resize / close). */
|
|
41
|
+
onLayoutChange?: (layout: DockNode) => void
|
|
42
|
+
/** Minimum pane size in px along the split axis. Default 80. */
|
|
43
|
+
minSize?: number
|
|
44
|
+
/** Surface id stamped on the root (`data-dock-surface`) - used by
|
|
45
|
+
* SvDockManager to hit-test which layout a drag is over. Default 'main'. */
|
|
46
|
+
surface?: string
|
|
47
|
+
/**
|
|
48
|
+
* Manager mode: when provided, SvDockLayout hands OFF tab-drag to the parent
|
|
49
|
+
* (SvDockManager) instead of running its own drag-to-dock, so a drag can flow
|
|
50
|
+
* across the main area, floating windows and edge strips. Return value is
|
|
51
|
+
* ignored; the manager owns the gesture from here.
|
|
52
|
+
*/
|
|
53
|
+
onBeginDrag?: (event: PointerEvent, paneId: string, tabsId: string) => void
|
|
54
|
+
/** Manager mode: the externally-computed dock guide/highlight to paint. */
|
|
55
|
+
externalDrop?: { tabsId: string; zone: DockZone | null; centerOnly?: boolean } | null
|
|
56
|
+
/** Manager mode: the externally-computed tab-reorder insertion indicator. */
|
|
57
|
+
externalReorder?: { tabsId: string; index: number } | null
|
|
58
|
+
/** Stack-header controls for a leaf (maximize / float / pop-out), rendered
|
|
59
|
+
* by the manager once per leaf at the right of its tab strip. */
|
|
60
|
+
leafActions?: Snippet<[import('./dock-model').DockTabs]>
|
|
61
|
+
/** Manager mode: route tab activation to the parent (controlled). */
|
|
62
|
+
onActivate?: (tabsId: string, index: number) => void
|
|
63
|
+
/** Manager mode: route pane close to the parent (controlled). */
|
|
64
|
+
onClose?: (paneId: string) => void
|
|
65
|
+
/** Manager mode: route splitter resize to the parent (controlled). */
|
|
66
|
+
onResize?: (groupId: string, sizes: number[]) => void
|
|
67
|
+
/** Which side of each leaf the tab strip sits on. Default `'top'`. */
|
|
68
|
+
headerPosition?: 'top' | 'bottom' | 'left' | 'right'
|
|
69
|
+
/** Manager mode: id of the focused leaf, for the active-panel highlight. */
|
|
70
|
+
focusedLeaf?: string | null
|
|
71
|
+
/** Keep inactive tabs mounted (hidden) so their content state persists. */
|
|
72
|
+
keepAlive?: boolean
|
|
73
|
+
/** In a single-pane leaf, hide the redundant tab button (auto-hide fly-out). */
|
|
74
|
+
hideSingleTab?: boolean
|
|
75
|
+
/** Locked / split mode: tabs cannot be dragged (no reorder / dock / float) and
|
|
76
|
+
* panel action buttons are hidden - only splitter resize + tab switching remain.
|
|
77
|
+
* Turns the layout into a fixed set of resizable panes. */
|
|
78
|
+
locked?: boolean
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let {
|
|
82
|
+
layout = $bindable(),
|
|
83
|
+
pane,
|
|
84
|
+
onLayoutChange,
|
|
85
|
+
minSize = 80,
|
|
86
|
+
surface = 'main',
|
|
87
|
+
onBeginDrag,
|
|
88
|
+
externalDrop = null,
|
|
89
|
+
externalReorder = null,
|
|
90
|
+
leafActions,
|
|
91
|
+
onActivate,
|
|
92
|
+
onClose,
|
|
93
|
+
onResize,
|
|
94
|
+
headerPosition = 'top',
|
|
95
|
+
focusedLeaf = null,
|
|
96
|
+
keepAlive = false,
|
|
97
|
+
hideSingleTab = false,
|
|
98
|
+
locked = false,
|
|
99
|
+
}: Props = $props()
|
|
100
|
+
|
|
101
|
+
const managed = $derived(!!onBeginDrag)
|
|
102
|
+
|
|
103
|
+
// Ids for nodes created by docking. The per-instance base (from a module
|
|
104
|
+
// counter) keeps them unique across instances and clear of caller ids.
|
|
105
|
+
let nextId = instanceBase()
|
|
106
|
+
const genId = () => `dk-${nextId++}`
|
|
107
|
+
|
|
108
|
+
function commit(next: DockNode) {
|
|
109
|
+
layout = next
|
|
110
|
+
onLayoutChange?.(next)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const activate = (tabsId: string, index: number) =>
|
|
114
|
+
onActivate ? onActivate(tabsId, index) : commit(setActive(layout, tabsId, index))
|
|
115
|
+
const resize = (groupId: string, sizes: number[]) =>
|
|
116
|
+
onResize ? onResize(groupId, sizes) : commit(setSizes(layout, groupId, sizes))
|
|
117
|
+
const close = (paneId: string) => {
|
|
118
|
+
if (onClose) { onClose(paneId); return }
|
|
119
|
+
const { root } = removePane(layout, paneId)
|
|
120
|
+
if (root) commit(root)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ---- drag-to-dock ------------------------------------------------------
|
|
124
|
+
type Drag = { paneId: string; sourceTabsId: string; title: string; x: number; y: number }
|
|
125
|
+
let drag = $state<Drag | null>(null)
|
|
126
|
+
let dropTarget = $state<{ tabsId: string; zone: DockZone } | null>(null)
|
|
127
|
+
let candidate: { paneId: string; sourceTabsId: string; title: string; sx: number; sy: number } | null = null
|
|
128
|
+
|
|
129
|
+
function paneTitle(paneId: string): string {
|
|
130
|
+
const walk = (n: DockNode): string | null => {
|
|
131
|
+
if (n.type === 'tabs') return n.panes.find((p) => p.id === paneId)?.title ?? null
|
|
132
|
+
for (const c of n.children) { const t = walk(c); if (t) return t }
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
return walk(layout) ?? ''
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function beginDrag(e: PointerEvent, paneId: string, tabsId: string) {
|
|
139
|
+
if (e.button !== 0) return
|
|
140
|
+
// Locked / split mode: tabs are fixed - a pointerdown still activates the tab
|
|
141
|
+
// (handled separately), but starts no drag.
|
|
142
|
+
if (locked) return
|
|
143
|
+
// Manager mode: delegate the whole gesture to the parent.
|
|
144
|
+
if (onBeginDrag) { onBeginDrag(e, paneId, tabsId); return }
|
|
145
|
+
candidate = { paneId, sourceTabsId: tabsId, title: paneTitle(paneId), sx: e.clientX, sy: e.clientY }
|
|
146
|
+
window.addEventListener('pointermove', onPointerMove)
|
|
147
|
+
window.addEventListener('pointerup', onPointerUp)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Which leaf is under the pointer, and which of its 5 zones. */
|
|
151
|
+
function hitTest(x: number, y: number): { tabsId: string; zone: DockZone } | null {
|
|
152
|
+
const el = document.elementFromPoint(x, y)?.closest('[data-dock-tabs]') as HTMLElement | null
|
|
153
|
+
if (!el) return null
|
|
154
|
+
const tabsId = el.dataset.dockTabs!
|
|
155
|
+
const r = el.getBoundingClientRect()
|
|
156
|
+
if (r.width <= 0 || r.height <= 0) return null
|
|
157
|
+
const rx = (x - r.left) / r.width
|
|
158
|
+
const ry = (y - r.top) / r.height
|
|
159
|
+
const edge = 0.25
|
|
160
|
+
const m = Math.min(rx, 1 - rx, ry, 1 - ry)
|
|
161
|
+
let zone: DockZone = 'center'
|
|
162
|
+
if (m <= edge) {
|
|
163
|
+
const nearest = Math.min(rx, 1 - rx, ry, 1 - ry)
|
|
164
|
+
zone = nearest === rx ? 'left' : nearest === 1 - rx ? 'right' : nearest === ry ? 'top' : 'bottom'
|
|
165
|
+
}
|
|
166
|
+
return { tabsId, zone }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function onPointerMove(e: PointerEvent) {
|
|
170
|
+
if (!candidate) return
|
|
171
|
+
if (!drag) {
|
|
172
|
+
// Small threshold so a click-to-activate does not start a drag.
|
|
173
|
+
if (Math.hypot(e.clientX - candidate.sx, e.clientY - candidate.sy) < 5) return
|
|
174
|
+
drag = { paneId: candidate.paneId, sourceTabsId: candidate.sourceTabsId, title: candidate.title, x: e.clientX, y: e.clientY }
|
|
175
|
+
document.body.style.userSelect = 'none'
|
|
176
|
+
document.body.style.cursor = 'grabbing'
|
|
177
|
+
}
|
|
178
|
+
drag = { ...drag, x: e.clientX, y: e.clientY }
|
|
179
|
+
dropTarget = hitTest(e.clientX, e.clientY)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function onPointerUp() {
|
|
183
|
+
window.removeEventListener('pointermove', onPointerMove)
|
|
184
|
+
window.removeEventListener('pointerup', onPointerUp)
|
|
185
|
+
if (drag && dropTarget) {
|
|
186
|
+
commit(movePane(layout, drag.paneId, dropTarget.tabsId, dropTarget.zone, genId))
|
|
187
|
+
}
|
|
188
|
+
drag = null
|
|
189
|
+
dropTarget = null
|
|
190
|
+
candidate = null
|
|
191
|
+
document.body.style.userSelect = ''
|
|
192
|
+
document.body.style.cursor = ''
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
setContext<DockContext>(DOCK_CONTEXT, {
|
|
196
|
+
get pane() { return pane },
|
|
197
|
+
get leafActions() { return locked ? undefined : leafActions },
|
|
198
|
+
activate,
|
|
199
|
+
close,
|
|
200
|
+
beginDrag,
|
|
201
|
+
resize,
|
|
202
|
+
minSize: () => minSize,
|
|
203
|
+
// In manager mode the highlight is driven from outside.
|
|
204
|
+
dropTarget: () => (managed ? externalDrop : dropTarget),
|
|
205
|
+
reorderTarget: () => (managed ? externalReorder : null),
|
|
206
|
+
headerPosition: () => headerPosition,
|
|
207
|
+
focusedLeaf: () => focusedLeaf,
|
|
208
|
+
keepAlive: () => keepAlive,
|
|
209
|
+
hideSingleTab: () => hideSingleTab,
|
|
210
|
+
})
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<div class="sv-dock" class:is-dragging={!!drag} data-dock-surface={surface}>
|
|
214
|
+
<DockNodeView node={layout} />
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
{#if drag}
|
|
218
|
+
<div class="sv-dock__ghost" style:left={`${drag.x + 12}px`} style:top={`${drag.y + 12}px`}>
|
|
219
|
+
{drag.title}
|
|
220
|
+
</div>
|
|
221
|
+
{/if}
|
|
222
|
+
|
|
223
|
+
<script lang="ts" module>
|
|
224
|
+
// Per-instance id base so concurrent layouts never mint the same node id.
|
|
225
|
+
let SEQ = 0
|
|
226
|
+
function instanceBase(): number {
|
|
227
|
+
SEQ += 1
|
|
228
|
+
return SEQ * 1_000_000
|
|
229
|
+
}
|
|
230
|
+
</script>
|
|
231
|
+
|
|
232
|
+
<style>
|
|
233
|
+
.sv-dock { width: 100%; height: 100%; min-width: 0; min-height: 0; display: flex; }
|
|
234
|
+
.sv-dock > :global(*) { flex: 1; min-width: 0; min-height: 0; }
|
|
235
|
+
.sv-dock.is-dragging { cursor: grabbing; }
|
|
236
|
+
.sv-dock.is-dragging :global(.sv-dock__content) { pointer-events: none; }
|
|
237
|
+
|
|
238
|
+
.sv-dock__ghost {
|
|
239
|
+
position: fixed; z-index: 2147483646; pointer-events: none;
|
|
240
|
+
padding: 5px 10px; font-size: 12.5px; font-weight: 600;
|
|
241
|
+
color: var(--sg-on-accent, #fff); background: var(--sg-accent, #2563eb);
|
|
242
|
+
border-radius: 6px; box-shadow: 0 8px 24px -6px rgba(15,23,42,0.5); opacity: 0.95;
|
|
243
|
+
}
|
|
244
|
+
</style>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvDockLayout - a basic docking layout: nested resizable split groups and
|
|
3
|
+
* tabbed leaves, with drag-to-dock. Drag a pane's tab onto another pane and
|
|
4
|
+
* drop it on an edge (split) or the centre (add as a tab); drag the splitters
|
|
5
|
+
* to resize; close panes. The whole layout is a plain `DockNode` tree you pass
|
|
6
|
+
* in and bind - every gesture is a pure transform from `dock-model`, so state
|
|
7
|
+
* is serializable and testable. Inspired by Smart's `smart-layout`.
|
|
8
|
+
*
|
|
9
|
+
* Usage: build a tree with `dockGroup` / `dockTabs` / `dockPane`, bind it, and
|
|
10
|
+
* provide a `pane` snippet that renders each pane's content by id:
|
|
11
|
+
*
|
|
12
|
+
* let layout = $state(dockGroup('row', [
|
|
13
|
+
* dockTabs([dockPane('files', 'Files')]),
|
|
14
|
+
* dockTabs([dockPane('editor', 'Editor')]),
|
|
15
|
+
* ]))
|
|
16
|
+
*
|
|
17
|
+
* <SvDockLayout bind:layout>
|
|
18
|
+
* {#snippet pane(p)} ...render by p.id... {/snippet}
|
|
19
|
+
* </SvDockLayout>
|
|
20
|
+
*/
|
|
21
|
+
import { type Snippet } from 'svelte';
|
|
22
|
+
import { type DockNode, type DockPane, type DockZone } from './dock-model';
|
|
23
|
+
type Props = {
|
|
24
|
+
/** The layout tree. Bindable - every dock/resize/close writes a new tree. */
|
|
25
|
+
layout: DockNode;
|
|
26
|
+
/** Renders each pane's content, keyed off the `DockPane`. */
|
|
27
|
+
pane: Snippet<[DockPane]>;
|
|
28
|
+
/** Notified after any structural change (dock / resize / close). */
|
|
29
|
+
onLayoutChange?: (layout: DockNode) => void;
|
|
30
|
+
/** Minimum pane size in px along the split axis. Default 80. */
|
|
31
|
+
minSize?: number;
|
|
32
|
+
/** Surface id stamped on the root (`data-dock-surface`) - used by
|
|
33
|
+
* SvDockManager to hit-test which layout a drag is over. Default 'main'. */
|
|
34
|
+
surface?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Manager mode: when provided, SvDockLayout hands OFF tab-drag to the parent
|
|
37
|
+
* (SvDockManager) instead of running its own drag-to-dock, so a drag can flow
|
|
38
|
+
* across the main area, floating windows and edge strips. Return value is
|
|
39
|
+
* ignored; the manager owns the gesture from here.
|
|
40
|
+
*/
|
|
41
|
+
onBeginDrag?: (event: PointerEvent, paneId: string, tabsId: string) => void;
|
|
42
|
+
/** Manager mode: the externally-computed dock guide/highlight to paint. */
|
|
43
|
+
externalDrop?: {
|
|
44
|
+
tabsId: string;
|
|
45
|
+
zone: DockZone | null;
|
|
46
|
+
centerOnly?: boolean;
|
|
47
|
+
} | null;
|
|
48
|
+
/** Manager mode: the externally-computed tab-reorder insertion indicator. */
|
|
49
|
+
externalReorder?: {
|
|
50
|
+
tabsId: string;
|
|
51
|
+
index: number;
|
|
52
|
+
} | null;
|
|
53
|
+
/** Stack-header controls for a leaf (maximize / float / pop-out), rendered
|
|
54
|
+
* by the manager once per leaf at the right of its tab strip. */
|
|
55
|
+
leafActions?: Snippet<[import('./dock-model').DockTabs]>;
|
|
56
|
+
/** Manager mode: route tab activation to the parent (controlled). */
|
|
57
|
+
onActivate?: (tabsId: string, index: number) => void;
|
|
58
|
+
/** Manager mode: route pane close to the parent (controlled). */
|
|
59
|
+
onClose?: (paneId: string) => void;
|
|
60
|
+
/** Manager mode: route splitter resize to the parent (controlled). */
|
|
61
|
+
onResize?: (groupId: string, sizes: number[]) => void;
|
|
62
|
+
/** Which side of each leaf the tab strip sits on. Default `'top'`. */
|
|
63
|
+
headerPosition?: 'top' | 'bottom' | 'left' | 'right';
|
|
64
|
+
/** Manager mode: id of the focused leaf, for the active-panel highlight. */
|
|
65
|
+
focusedLeaf?: string | null;
|
|
66
|
+
/** Keep inactive tabs mounted (hidden) so their content state persists. */
|
|
67
|
+
keepAlive?: boolean;
|
|
68
|
+
/** In a single-pane leaf, hide the redundant tab button (auto-hide fly-out). */
|
|
69
|
+
hideSingleTab?: boolean;
|
|
70
|
+
/** Locked / split mode: tabs cannot be dragged (no reorder / dock / float) and
|
|
71
|
+
* panel action buttons are hidden - only splitter resize + tab switching remain.
|
|
72
|
+
* Turns the layout into a fixed set of resizable panes. */
|
|
73
|
+
locked?: boolean;
|
|
74
|
+
};
|
|
75
|
+
declare const SvDockLayout: import("svelte").Component<Props, {}, "layout">;
|
|
76
|
+
type SvDockLayout = ReturnType<typeof SvDockLayout>;
|
|
77
|
+
export default SvDockLayout;
|