@adea-ai/ui 0.109.0 → 0.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/components/composites/update-dialog/update-dialog.js +1 -1
- package/dist/components/layout/contextual-sidebar/contextual-sidebar.d.ts.map +1 -1
- package/dist/components/layout/contextual-sidebar/contextual-sidebar.js +45 -35
- package/dist/components/layout/contextual-sidebar/contextual-sidebar.js.map +1 -1
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.d.ts +12 -0
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.d.ts.map +1 -0
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.js +36 -0
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.js.map +1 -0
- package/dist/components/layout/split-layout/index.js +2 -2
- package/dist/components/layout/split-layout/model.d.ts +13 -0
- package/dist/components/layout/split-layout/model.d.ts.map +1 -1
- package/dist/components/layout/split-layout/model.js +55 -18
- package/dist/components/layout/split-layout/model.js.map +1 -1
- package/dist/components/ui/alert-dialog/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog/alert-dialog.js +2 -6
- package/dist/components/ui/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/components/ui/board/board.js +1 -1
- package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog/dialog.js +2 -6
- package/dist/components/ui/dialog/dialog.js.map +1 -1
- package/dist/components/ui/item/item.d.ts +1 -1
- package/dist/components/ui/sheet/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet/sheet.js +2 -5
- package/dist/components/ui/sheet/sheet.js.map +1 -1
- package/dist/components/ui/typography/typography.d.ts.map +1 -1
- package/dist/components/ui/typography/typography.js +2 -2
- package/dist/components/ui/typography/typography.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/lib/overlay.d.ts +5 -0
- package/dist/lib/overlay.d.ts.map +1 -1
- package/dist/lib/overlay.js +6 -1
- package/dist/lib/overlay.js.map +1 -1
- package/dist/r/alert-dialog.json +0 -1
- package/dist/r/contextual-sidebar.json +5 -0
- package/dist/r/dialog.json +0 -1
- package/dist/r/registry.json +5 -3
- package/dist/r/sheet.json +0 -1
- package/dist/r/src/components/layout/contextual-sidebar/contextual-sidebar.tsx +41 -16
- package/dist/r/src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx +28 -0
- package/dist/r/src/components/layout/split-layout/model.ts +62 -10
- package/dist/r/src/components/ui/alert-dialog/alert-dialog.tsx +8 -8
- package/dist/r/src/components/ui/dialog/dialog.tsx +8 -8
- package/dist/r/src/components/ui/sheet/sheet.tsx +2 -7
- package/dist/r/src/components/ui/typography/typography.tsx +3 -2
- package/dist/r/src/lib/overlay.ts +6 -0
- package/package.json +1 -1
- package/registry.json +5 -3
- package/src/components/layout/contextual-sidebar/contextual-sidebar.tsx +41 -16
- package/src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx +28 -0
- package/src/components/layout/split-layout/model.ts +62 -10
- package/src/components/ui/alert-dialog/alert-dialog.tsx +8 -8
- package/src/components/ui/dialog/dialog.tsx +8 -8
- package/src/components/ui/sheet/sheet.tsx +2 -7
- package/src/components/ui/typography/typography.tsx +3 -2
- package/src/lib/overlay.ts +6 -0
package/README.md
CHANGED
|
@@ -659,6 +659,20 @@ editing it; the shared constructor validates visual structure and identities,
|
|
|
659
659
|
while applications still decode/scoped-persist preferences and retain unknown
|
|
660
660
|
future versions for recovery.
|
|
661
661
|
|
|
662
|
+
`splitPaneBalanced(state, targetLeafId, { placement, leaf, splitId })` is an
|
|
663
|
+
opt-in automatic reflow for hosts that want a new pane beside the selected pane
|
|
664
|
+
in reading order. It keeps one row for one or two panes, starts a second row at
|
|
665
|
+
three, and balances the row lengths through the eight-pane limit (two rows of
|
|
666
|
+
four at the cap). Leaves in each row receive equal widths and both rows receive
|
|
667
|
+
equal height; adding a pane can therefore redistribute existing pane widths.
|
|
668
|
+
Opaque leaf objects are retained. Existing split ID strings are recycled across
|
|
669
|
+
the rebuilt branches; an ID can acquire a different axis or descendants, so hosts
|
|
670
|
+
must resolve resize actions against the current tree rather than cache a branch.
|
|
671
|
+
The new split ID names the nearest common branch of the selected and new panes.
|
|
672
|
+
The new pane is focused, and the close-undo stack is cleared like other structural edits. The
|
|
673
|
+
explicit-axis `splitPane` operation is unchanged for hosts that do not want an
|
|
674
|
+
automatic reflow.
|
|
675
|
+
|
|
662
676
|
The model and maintained accessible renderer are implemented in this draft.
|
|
663
677
|
Stable content ownership and movement are exercised in component fixtures.
|
|
664
678
|
The packed renderer gate includes the UI #20 size-token correction and the host
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
-
import { Heading } from "../../ui/typography/typography.js";
|
|
3
2
|
import { Badge } from "../../ui/badge/badge.js";
|
|
3
|
+
import { Heading } from "../../ui/typography/typography.js";
|
|
4
4
|
import { Button } from "../../ui/button/button.js";
|
|
5
5
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, createDialogFocusRestoration } from "../../ui/dialog/dialog.js";
|
|
6
6
|
import { ActionButton } from "../action-button/action-button.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextual-sidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/contextual-sidebar/contextual-sidebar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"contextual-sidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/contextual-sidebar/contextual-sidebar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAe7C,MAAM,MAAM,8BAA8B,GAAG;IAC3C,wEAAwE;IACxE,MAAM,EAAE,OAAO,CAAA;IACf,qFAAqF;IACrF,WAAW,IAAI,WAAW,GAAG,SAAS,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,qDAAqD;IACrD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IACjC,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sEAAsE;IACtE,eAAe,CAAC,EAAE,QAAQ,CAAC,WAAW,GAAG,SAAS,CAAC,CAAA;IACnD,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9E,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACnD,iEAAiE;IACjE,MAAM,CAAC,CAAC,OAAO,EAAE,8BAA8B,GAAG,GAAG,CAAC,OAAO,CAAA;IAC7D,0EAA0E;IAC1E,OAAO,CAAC,OAAO,EAAE,8BAA8B,GAAG,GAAG,CAAC,OAAO,CAAA;IAC7D,oEAAoE;IACpE,MAAM,CAAC,CAAC,OAAO,EAAE,8BAA8B,GAAG,GAAG,CAAC,OAAO,CAAA;IAC7D,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,aAAa,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,eAuI9D"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
-
import { Sheet, SheetContent, SheetTitle } from "../../ui/sheet/sheet.js";
|
|
3
2
|
import { SidebarNav, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavTitle } from "../sidebar-nav/sidebar-nav.js";
|
|
4
3
|
import { PixelResizeHandle } from "../pixel-resize-handle/pixel-resize-handle.js";
|
|
5
4
|
import { createComponent, memo } from "solid-js/web";
|
|
@@ -14,6 +13,17 @@ import { Show, createSignal, createUniqueId, onCleanup, onMount } from "solid-js
|
|
|
14
13
|
*/
|
|
15
14
|
function ContextualSidebar(props) {
|
|
16
15
|
const [isNarrowViewport, setIsNarrowViewport] = createSignal(false);
|
|
16
|
+
const [mobileLoad, setMobileLoad] = createSignal();
|
|
17
|
+
let mobileRequested = false;
|
|
18
|
+
const loadMobile = () => {
|
|
19
|
+
if (mobileRequested) return;
|
|
20
|
+
mobileRequested = true;
|
|
21
|
+
import("./mobile-contextual-sidebar.js").then((module) => {
|
|
22
|
+
if (mobileRequested) setMobileLoad(module);
|
|
23
|
+
}, (error) => {
|
|
24
|
+
if (mobileRequested) setMobileLoad({ error });
|
|
25
|
+
});
|
|
26
|
+
};
|
|
17
27
|
const [mobileMount, setMobileMount] = createSignal();
|
|
18
28
|
const id = props.id ?? `contextual-sidebar-${createUniqueId()}`;
|
|
19
29
|
const resizeLabel = () => props.resizeLabel ?? `Resize ${props.label}`;
|
|
@@ -25,13 +35,18 @@ function ContextualSidebar(props) {
|
|
|
25
35
|
const media = window.matchMedia("(max-width: 48rem)");
|
|
26
36
|
const updateViewport = () => {
|
|
27
37
|
const wasNarrow = isNarrowViewport();
|
|
38
|
+
if (media.matches) loadMobile();
|
|
28
39
|
if (media.matches && !wasNarrow) props.onOpenChange(false);
|
|
29
40
|
setIsNarrowViewport(media.matches);
|
|
30
41
|
};
|
|
42
|
+
if (media.matches) loadMobile();
|
|
31
43
|
if (media.matches && props.wideViewportAtLoad && props.open) props.onOpenChange(false);
|
|
32
44
|
setIsNarrowViewport(media.matches);
|
|
33
45
|
media.addEventListener("change", updateViewport);
|
|
34
|
-
onCleanup(() =>
|
|
46
|
+
onCleanup(() => {
|
|
47
|
+
mobileRequested = false;
|
|
48
|
+
media.removeEventListener("change", updateViewport);
|
|
49
|
+
});
|
|
35
50
|
});
|
|
36
51
|
const renderSidebar = (mobile) => {
|
|
37
52
|
let element;
|
|
@@ -155,43 +170,38 @@ function ContextualSidebar(props) {
|
|
|
155
170
|
}
|
|
156
171
|
});
|
|
157
172
|
};
|
|
158
|
-
return createComponent(
|
|
159
|
-
get
|
|
160
|
-
return
|
|
173
|
+
return [createComponent(Show, {
|
|
174
|
+
get when() {
|
|
175
|
+
return !isNarrowViewport();
|
|
161
176
|
},
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
177
|
+
children: (_wide) => renderSidebar(false)
|
|
178
|
+
}), createComponent(Show, {
|
|
179
|
+
get when() {
|
|
180
|
+
return mobileLoad();
|
|
181
|
+
},
|
|
182
|
+
keyed: true,
|
|
183
|
+
children: (loaded) => {
|
|
184
|
+
if ("error" in loaded) throw loaded.error;
|
|
185
|
+
return createComponent(loaded.MobileSidebar, {
|
|
186
|
+
get open() {
|
|
187
|
+
return memo(() => !!isNarrowViewport())() && props.open;
|
|
167
188
|
},
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
get when() {
|
|
171
|
-
return isNarrowViewport();
|
|
189
|
+
get onOpenChange() {
|
|
190
|
+
return props.onOpenChange;
|
|
172
191
|
},
|
|
173
|
-
get
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
"class": "sr-only",
|
|
185
|
-
get children() {
|
|
186
|
-
return [memo(() => props.title), " navigation"];
|
|
187
|
-
}
|
|
188
|
-
}), memo(() => renderSidebar(true))];
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
})];
|
|
192
|
+
get label() {
|
|
193
|
+
return props.label;
|
|
194
|
+
},
|
|
195
|
+
get sheetClass() {
|
|
196
|
+
return props.sheetClass;
|
|
197
|
+
},
|
|
198
|
+
get restoreFocusRef() {
|
|
199
|
+
return props.restoreFocusRef;
|
|
200
|
+
},
|
|
201
|
+
renderSidebar: () => renderSidebar(true)
|
|
202
|
+
});
|
|
193
203
|
}
|
|
194
|
-
});
|
|
204
|
+
})];
|
|
195
205
|
}
|
|
196
206
|
//#endregion
|
|
197
207
|
export { ContextualSidebar };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextual-sidebar.js","names":["Accessor","JSX","Show","createSignal","createUniqueId","onCleanup","onMount","cn","Sheet","SheetContent","SheetTitle","SidebarNav","SidebarNavContent","SidebarNavFooter","SidebarNavHeader","SidebarNavTitle","PixelResizeHandle","ContextualSidebarRenderContext","mobile","portalMount","HTMLElement","ContextualSidebarProps","id","label","title","open","onOpenChange","width","minimum","maximum","step","wideViewportAtLoad","resizeLabel","restoreFocusRef","onSidebarElement","element","sidebarClass","sheetClass","headerClass","contentClass","footerClass","headingAs","header","context","Element","content","footer","onWidthChange","onWidthCommit","ContextualSidebar","props","isNarrowViewport","setIsNarrowViewport","mobileMount","setMobileMount","connected","mount","isConnected","undefined","media","window","matchMedia","updateViewport","wasNarrow","matches","addEventListener","removeEventListener","renderSidebar","navigationId","_$createComponent","as","aria-label","aria-hidden","inert","data-open","String","ref","node","queueMicrotask","class","children","when","side","value","controls","onChange","onCommit","_$memo","_wide","_$createComponent","_$memo"],"sources":["../../../../src/components/layout/contextual-sidebar/contextual-sidebar.tsx"],"sourcesContent":["import type { Accessor, JSX } from 'solid-js'\nimport { Show, createSignal, createUniqueId, onCleanup, onMount } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Sheet, SheetContent, SheetTitle } from '../../ui/sheet'\nimport {\n SidebarNav,\n SidebarNavContent,\n SidebarNavFooter,\n SidebarNavHeader,\n SidebarNavTitle,\n} from '../sidebar-nav/sidebar-nav'\nimport { PixelResizeHandle } from '../pixel-resize-handle/pixel-resize-handle'\n\nexport type ContextualSidebarRenderContext = {\n /** Whether the slot is being rendered inside the modal mobile sheet. */\n mobile: boolean\n /** Mount nested popovers and menus inside the mobile dialog's accessibility tree. */\n portalMount(): HTMLElement | undefined\n}\n\nexport type ContextualSidebarProps = {\n /** Stable host id prefix; generated when omitted. */\n id?: string\n /** Landmark name shared by the desktop and mobile navigation. */\n label: string\n /** Visible navigation title and mobile dialog title. */\n title: string\n /** Host-controlled expanded state for the desktop panel and mobile dialog. */\n open: boolean\n onOpenChange(open: boolean): void\n /** Current host-owned pixel width, reflected by the resize handle. */\n width: number\n minimum: number\n maximum: number\n step?: number\n /** Pass the host's initial viewport seed to distinguish stale desktop state. */\n wideViewportAtLoad?: boolean\n resizeLabel?: string\n /** Stable external opener used when the modal mobile sheet closes. */\n restoreFocusRef?: Accessor<HTMLElement | undefined>\n /** Called with the nav node so a host can project width onto its layout root. */\n onSidebarElement?: (element: HTMLElement | undefined, mobile: boolean) => void\n /** Host-owned layout classes applied to both nav instances. */\n sidebarClass?: string\n /** Host-owned responsive constraints for the modal sheet. */\n sheetClass?: string\n headerClass?: string\n contentClass?: string\n footerClass?: string\n headingAs?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'\n /** Optional action/content slot alongside the shared heading. */\n header?(context: ContextualSidebarRenderContext): JSX.Element\n /** View-specific navigation rows, projects, rooms, files, or sessions. */\n content(context: ContextualSidebarRenderContext): JSX.Element\n /** Optional view-specific footer, such as an archive affordance. */\n footer?(context: ContextualSidebarRenderContext): JSX.Element\n onWidthChange(width: number): void\n onWidthCommit?(width: number): void\n}\n\n/**\n * ContextualSidebar.\n *\n * Shared navigation geometry and interaction policy for an application's\n * contextual sidebar. The host keeps ownership of the actual grid width,\n * persisted preference, open state, and view-specific navigation content.\n */\nexport function ContextualSidebar(props: ContextualSidebarProps) {\n const [isNarrowViewport, setIsNarrowViewport] = createSignal(false)\n const [mobileMount, setMobileMount] = createSignal<\n { element: HTMLElement; connected: boolean } | undefined\n >()\n const id = props.id ?? `contextual-sidebar-${createUniqueId()}`\n const resizeLabel = () => props.resizeLabel ?? `Resize ${props.label}`\n const portalMount = () => {\n const mount = mobileMount()\n return mount?.connected && mount.element.isConnected ? mount.element : undefined\n }\n\n onMount(() => {\n const media = window.matchMedia('(max-width: 48rem)')\n const updateViewport = () => {\n const wasNarrow = isNarrowViewport()\n // Close before mounting a modal. A desktop-expanded host state is not\n // mobile intent, and opening the Sheet for one frame can steal focus.\n if (media.matches && !wasNarrow) props.onOpenChange(false)\n setIsNarrowViewport(media.matches)\n }\n\n // A route can mount after a wide-loaded host crosses into mobile. Clear\n // that stale seed, but preserve a sidebar intentionally opened at a\n // narrow boot viewport.\n if (media.matches && props.wideViewportAtLoad && props.open) props.onOpenChange(false)\n setIsNarrowViewport(media.matches)\n media.addEventListener('change', updateViewport)\n onCleanup(() => media.removeEventListener('change', updateViewport))\n })\n\n const renderSidebar = (mobile: boolean) => {\n let element: HTMLElement | undefined\n const context: ContextualSidebarRenderContext = {\n mobile,\n portalMount: () => (mobile ? portalMount() : undefined),\n }\n const navigationId = `${id}-${mobile ? 'mobile' : 'desktop'}`\n\n onCleanup(() => {\n if (mobile && mobileMount()?.element === element) setMobileMount(undefined)\n element = undefined\n props.onSidebarElement?.(undefined, mobile)\n })\n\n return (\n <SidebarNav\n as=\"aside\"\n id={navigationId}\n aria-label={props.label}\n aria-hidden={!mobile && !props.open ? 'true' : undefined}\n inert={!mobile && !props.open}\n data-contextual-sidebar={mobile ? 'mobile' : 'desktop'}\n data-open={mobile ? undefined : String(props.open)}\n ref={(node) => {\n element = node\n if (mobile) {\n setMobileMount({ element: node, connected: node.isConnected })\n queueMicrotask(() => {\n if (node.isConnected && mobileMount()?.element === node)\n setMobileMount({ element: node, connected: true })\n })\n }\n props.onSidebarElement?.(node, mobile)\n }}\n class={cn('relative h-full w-full max-w-full', props.sidebarClass)}\n >\n <Show when={!mobile && props.open}>\n <PixelResizeHandle\n side=\"left\"\n value={props.width}\n minimum={props.minimum}\n maximum={props.maximum}\n step={props.step}\n label={resizeLabel()}\n controls={navigationId}\n onChange={props.onWidthChange}\n onCommit={props.onWidthCommit}\n />\n </Show>\n <Show when={!mobile || mobileMount()?.connected}>\n <SidebarNavHeader class={cn(mobile && 'pe-12', props.headerClass)}>\n <SidebarNavTitle as={props.headingAs ?? 'h2'}>{props.title}</SidebarNavTitle>\n {props.header?.(context)}\n </SidebarNavHeader>\n <SidebarNavContent class={props.contentClass}>{props.content(context)}</SidebarNavContent>\n {props.footer ? (\n <SidebarNavFooter class={props.footerClass}>{props.footer(context)}</SidebarNavFooter>\n ) : null}\n </Show>\n </SidebarNav>\n )\n }\n\n return (\n <Sheet\n open={isNarrowViewport() && props.open}\n onOpenChange={(open) => props.onOpenChange(open)}\n >\n <Show when={!isNarrowViewport()}>{(_wide) => renderSidebar(false)}</Show>\n <Show when={isNarrowViewport()}>\n <SheetContent\n side=\"start\"\n class={cn('gap-0 p-0', props.sheetClass)}\n restoreFocusRef={props.restoreFocusRef}\n >\n <SheetTitle class=\"sr-only\">{props.title} navigation</SheetTitle>\n {renderSidebar(true)}\n </SheetContent>\n </Show>\n </Sheet>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"contextual-sidebar.js","names":["Accessor","JSX","Show","createSignal","createUniqueId","onCleanup","onMount","cn","SidebarNav","SidebarNavContent","SidebarNavFooter","SidebarNavHeader","SidebarNavTitle","PixelResizeHandle","MobileModule","MobileLoad","error","ContextualSidebarRenderContext","mobile","portalMount","HTMLElement","ContextualSidebarProps","id","label","title","open","onOpenChange","width","minimum","maximum","step","wideViewportAtLoad","resizeLabel","restoreFocusRef","onSidebarElement","element","sidebarClass","sheetClass","headerClass","contentClass","footerClass","headingAs","header","context","Element","content","footer","onWidthChange","onWidthCommit","ContextualSidebar","props","isNarrowViewport","setIsNarrowViewport","mobileLoad","setMobileLoad","mobileRequested","loadMobile","then","module","mobileMount","setMobileMount","connected","mount","isConnected","undefined","media","window","matchMedia","updateViewport","wasNarrow","matches","addEventListener","removeEventListener","renderSidebar","navigationId","_$createComponent","as","aria-label","aria-hidden","inert","data-open","String","ref","node","queueMicrotask","class","children","when","side","value","controls","onChange","onCommit","_$memo","_wide","keyed","loaded","MobileSidebar","_$createComponent","_$memo"],"sources":["../../../../src/components/layout/contextual-sidebar/contextual-sidebar.tsx"],"sourcesContent":["import type { Accessor, JSX } from 'solid-js'\nimport { Show, createSignal, createUniqueId, onCleanup, onMount } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport {\n SidebarNav,\n SidebarNavContent,\n SidebarNavFooter,\n SidebarNavHeader,\n SidebarNavTitle,\n} from '../sidebar-nav/sidebar-nav'\nimport { PixelResizeHandle } from '../pixel-resize-handle/pixel-resize-handle'\n\ntype MobileModule = typeof import('./mobile-contextual-sidebar')\ntype MobileLoad = MobileModule | { error: unknown }\n\nexport type ContextualSidebarRenderContext = {\n /** Whether the slot is being rendered inside the modal mobile sheet. */\n mobile: boolean\n /** Mount nested popovers and menus inside the mobile dialog's accessibility tree. */\n portalMount(): HTMLElement | undefined\n}\n\nexport type ContextualSidebarProps = {\n /** Stable host id prefix; generated when omitted. */\n id?: string\n /** Landmark name shared by the desktop and mobile navigation. */\n label: string\n /** Visible navigation title and mobile dialog title. */\n title: string\n /** Host-controlled expanded state for the desktop panel and mobile dialog. */\n open: boolean\n onOpenChange(open: boolean): void\n /** Current host-owned pixel width, reflected by the resize handle. */\n width: number\n minimum: number\n maximum: number\n step?: number\n /** Pass the host's initial viewport seed to distinguish stale desktop state. */\n wideViewportAtLoad?: boolean\n resizeLabel?: string\n /** Stable external opener used when the modal mobile sheet closes. */\n restoreFocusRef?: Accessor<HTMLElement | undefined>\n /** Called with the nav node so a host can project width onto its layout root. */\n onSidebarElement?: (element: HTMLElement | undefined, mobile: boolean) => void\n /** Host-owned layout classes applied to both nav instances. */\n sidebarClass?: string\n /** Host-owned responsive constraints for the modal sheet. */\n sheetClass?: string\n headerClass?: string\n contentClass?: string\n footerClass?: string\n headingAs?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'\n /** Optional action/content slot alongside the shared heading. */\n header?(context: ContextualSidebarRenderContext): JSX.Element\n /** View-specific navigation rows, projects, rooms, files, or sessions. */\n content(context: ContextualSidebarRenderContext): JSX.Element\n /** Optional view-specific footer, such as an archive affordance. */\n footer?(context: ContextualSidebarRenderContext): JSX.Element\n onWidthChange(width: number): void\n onWidthCommit?(width: number): void\n}\n\n/**\n * ContextualSidebar.\n *\n * Shared navigation geometry and interaction policy for an application's\n * contextual sidebar. The host keeps ownership of the actual grid width,\n * persisted preference, open state, and view-specific navigation content.\n */\nexport function ContextualSidebar(props: ContextualSidebarProps) {\n const [isNarrowViewport, setIsNarrowViewport] = createSignal(false)\n const [mobileLoad, setMobileLoad] = createSignal<MobileLoad>()\n let mobileRequested = false\n // Keep the resolved component mounted across viewport changes for Sheet cleanup.\n const loadMobile = () => {\n if (mobileRequested) return\n mobileRequested = true\n // Native module caching shares successful loads without keeping a rejected promise.\n void import('./mobile-contextual-sidebar').then(\n (module) => {\n if (mobileRequested) setMobileLoad(module)\n },\n (error: unknown) => {\n if (mobileRequested) setMobileLoad({ error })\n }\n )\n }\n const [mobileMount, setMobileMount] = createSignal<\n { element: HTMLElement; connected: boolean } | undefined\n >()\n const id = props.id ?? `contextual-sidebar-${createUniqueId()}`\n const resizeLabel = () => props.resizeLabel ?? `Resize ${props.label}`\n const portalMount = () => {\n const mount = mobileMount()\n return mount?.connected && mount.element.isConnected ? mount.element : undefined\n }\n\n onMount(() => {\n const media = window.matchMedia('(max-width: 48rem)')\n const updateViewport = () => {\n const wasNarrow = isNarrowViewport()\n if (media.matches) loadMobile()\n // Close before mounting a modal. A desktop-expanded host state is not\n // mobile intent, and opening the Sheet for one frame can steal focus.\n if (media.matches && !wasNarrow) props.onOpenChange(false)\n setIsNarrowViewport(media.matches)\n }\n\n // A route can mount after a wide-loaded host crosses into mobile. Clear\n // that stale seed, but preserve a sidebar intentionally opened at a\n // narrow boot viewport.\n if (media.matches) loadMobile()\n if (media.matches && props.wideViewportAtLoad && props.open) props.onOpenChange(false)\n setIsNarrowViewport(media.matches)\n media.addEventListener('change', updateViewport)\n onCleanup(() => {\n mobileRequested = false\n media.removeEventListener('change', updateViewport)\n })\n })\n\n const renderSidebar = (mobile: boolean) => {\n let element: HTMLElement | undefined\n const context: ContextualSidebarRenderContext = {\n mobile,\n portalMount: () => (mobile ? portalMount() : undefined),\n }\n const navigationId = `${id}-${mobile ? 'mobile' : 'desktop'}`\n\n onCleanup(() => {\n if (mobile && mobileMount()?.element === element) setMobileMount(undefined)\n element = undefined\n props.onSidebarElement?.(undefined, mobile)\n })\n\n return (\n <SidebarNav\n as=\"aside\"\n id={navigationId}\n aria-label={props.label}\n aria-hidden={!mobile && !props.open ? 'true' : undefined}\n inert={!mobile && !props.open}\n data-contextual-sidebar={mobile ? 'mobile' : 'desktop'}\n data-open={mobile ? undefined : String(props.open)}\n ref={(node) => {\n element = node\n if (mobile) {\n setMobileMount({ element: node, connected: node.isConnected })\n queueMicrotask(() => {\n if (node.isConnected && mobileMount()?.element === node)\n setMobileMount({ element: node, connected: true })\n })\n }\n props.onSidebarElement?.(node, mobile)\n }}\n class={cn('relative h-full w-full max-w-full', props.sidebarClass)}\n >\n <Show when={!mobile && props.open}>\n <PixelResizeHandle\n side=\"left\"\n value={props.width}\n minimum={props.minimum}\n maximum={props.maximum}\n step={props.step}\n label={resizeLabel()}\n controls={navigationId}\n onChange={props.onWidthChange}\n onCommit={props.onWidthCommit}\n />\n </Show>\n <Show when={!mobile || mobileMount()?.connected}>\n <SidebarNavHeader class={cn(mobile && 'pe-12', props.headerClass)}>\n <SidebarNavTitle as={props.headingAs ?? 'h2'}>{props.title}</SidebarNavTitle>\n {props.header?.(context)}\n </SidebarNavHeader>\n <SidebarNavContent class={props.contentClass}>{props.content(context)}</SidebarNavContent>\n {props.footer ? (\n <SidebarNavFooter class={props.footerClass}>{props.footer(context)}</SidebarNavFooter>\n ) : null}\n </Show>\n </SidebarNav>\n )\n }\n\n return (\n <>\n <Show when={!isNarrowViewport()}>{(_wide) => renderSidebar(false)}</Show>\n <Show when={mobileLoad()} keyed>\n {(loaded) => {\n if ('error' in loaded) throw loaded.error\n return (\n <loaded.MobileSidebar\n open={isNarrowViewport() && props.open}\n onOpenChange={props.onOpenChange}\n label={props.label}\n sheetClass={props.sheetClass}\n restoreFocusRef={props.restoreFocusRef}\n renderSidebar={() => renderSidebar(true)}\n />\n )\n }}\n </Show>\n </>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Accessor, JSX } from 'solid-js';
|
|
2
|
+
export type MobileContextualSidebarProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange(open: boolean): void;
|
|
5
|
+
label: string;
|
|
6
|
+
sheetClass?: string;
|
|
7
|
+
restoreFocusRef?: Accessor<HTMLElement | undefined>;
|
|
8
|
+
renderSidebar(): JSX.Element;
|
|
9
|
+
};
|
|
10
|
+
/** Sheet wrappers are loaded only when a contextual sidebar enters mobile mode. */
|
|
11
|
+
export declare function MobileSidebar(props: MobileContextualSidebarProps): JSX.Element;
|
|
12
|
+
//# sourceMappingURL=mobile-contextual-sidebar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile-contextual-sidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAI7C,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,eAAe,CAAC,EAAE,QAAQ,CAAC,WAAW,GAAG,SAAS,CAAC,CAAA;IACnD,aAAa,IAAI,GAAG,CAAC,OAAO,CAAA;CAC7B,CAAA;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,EAAE,4BAA4B,eAahE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { Sheet, SheetContent } from "../../ui/sheet/sheet.js";
|
|
3
|
+
import { createComponent } from "solid-js/web";
|
|
4
|
+
//#region src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx
|
|
5
|
+
/** Sheet wrappers are loaded only when a contextual sidebar enters mobile mode. */
|
|
6
|
+
function MobileSidebar(props) {
|
|
7
|
+
return createComponent(Sheet, {
|
|
8
|
+
get open() {
|
|
9
|
+
return props.open;
|
|
10
|
+
},
|
|
11
|
+
get onOpenChange() {
|
|
12
|
+
return props.onOpenChange;
|
|
13
|
+
},
|
|
14
|
+
get children() {
|
|
15
|
+
return createComponent(SheetContent, {
|
|
16
|
+
side: "start",
|
|
17
|
+
get ["aria-label"]() {
|
|
18
|
+
return props.label;
|
|
19
|
+
},
|
|
20
|
+
get ["class"]() {
|
|
21
|
+
return cn("gap-0 p-0", props.sheetClass);
|
|
22
|
+
},
|
|
23
|
+
get restoreFocusRef() {
|
|
24
|
+
return props.restoreFocusRef;
|
|
25
|
+
},
|
|
26
|
+
get children() {
|
|
27
|
+
return props.renderSidebar();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { MobileSidebar };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=mobile-contextual-sidebar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile-contextual-sidebar.js","names":["Accessor","JSX","cn","Sheet","SheetContent","MobileContextualSidebarProps","open","onOpenChange","label","sheetClass","restoreFocusRef","HTMLElement","renderSidebar","Element","MobileSidebar","props","_$createComponent","children","side","aria-label","class","_$createComponent"],"sources":["../../../../src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx"],"sourcesContent":["import type { Accessor, JSX } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Sheet, SheetContent } from '../../ui/sheet'\n\nexport type MobileContextualSidebarProps = {\n open: boolean\n onOpenChange(open: boolean): void\n label: string\n sheetClass?: string\n restoreFocusRef?: Accessor<HTMLElement | undefined>\n renderSidebar(): JSX.Element\n}\n\n/** Sheet wrappers are loaded only when a contextual sidebar enters mobile mode. */\nexport function MobileSidebar(props: MobileContextualSidebarProps) {\n return (\n <Sheet open={props.open} onOpenChange={props.onOpenChange}>\n <SheetContent\n side=\"start\"\n aria-label={props.label}\n class={cn('gap-0 p-0', props.sheetClass)}\n restoreFocusRef={props.restoreFocusRef}\n >\n {props.renderSidebar()}\n </SheetContent>\n </Sheet>\n )\n}\n"],"mappings":";;;;;AAcA,SAAgBc,cAAcC,OAAqC;CACjE,OAAAC,gBACGb,OAAK;EAAA,IAACG,OAAI;GAAA,OAAES,MAAMT;EAAI;EAAA,IAAEC,eAAY;GAAA,OAAEQ,MAAMR;EAAY;EAAA,IAAAU,WAAA;GAAA,OAAAD,gBACtDZ,cAAY;IACXc,MAAI;IAAA,KAAA,gBAAA;KAAA,OACQH,MAAMP;IAAK;IAAA,KAAA,WAAA;KAAA,OAChBN,GAAG,aAAaa,MAAMN,UAAU;IAAC;IAAA,IACxCC,kBAAe;KAAA,OAAEK,MAAML;IAAe;IAAA,IAAAO,WAAA;KAAA,OAErCF,MAAMH,cAAc;IAAC;GAAA,CAAA;EAAA;CAAA,CAAA;AAI9B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, countLeaves, layoutDepth, listLeaves } from "./tree.js";
|
|
2
|
-
import { closePane, createLayoutState, focusPane, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, swapPanes, undoClosePane } from "./model.js";
|
|
2
|
+
import { closePane, createLayoutState, focusPane, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, splitPaneBalanced, swapPanes, undoClosePane } from "./model.js";
|
|
3
3
|
import { computeLayoutFrames } from "./geometry.js";
|
|
4
4
|
import { SplitLayout } from "./split-layout.js";
|
|
5
|
-
export { MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, SplitLayout, closePane, computeLayoutFrames, countLeaves, createLayoutState, focusPane, layoutDepth, listLeaves, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, swapPanes, undoClosePane };
|
|
5
|
+
export { MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, SplitLayout, closePane, computeLayoutFrames, countLeaves, createLayoutState, focusPane, layoutDepth, listLeaves, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, splitPaneBalanced, swapPanes, undoClosePane };
|
|
@@ -14,8 +14,21 @@ export type SplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readon
|
|
|
14
14
|
leaf: L;
|
|
15
15
|
splitId: string;
|
|
16
16
|
}>;
|
|
17
|
+
export type BalancedSplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<Pick<SplitPaneInput<L>, 'placement' | 'leaf' | 'splitId'>>;
|
|
17
18
|
export declare function createLayoutState<L extends SplitLayoutLeaf>(center: SplitLayoutNode<L>): SplitLayoutState<L>;
|
|
18
19
|
export declare function splitPane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, targetLeafId: string, input: SplitPaneInput<L>): SplitLayoutState<L>;
|
|
20
|
+
/**
|
|
21
|
+
* Add a pane beside the selected leaf and reflow the visual tree into a
|
|
22
|
+
* balanced row-major grid. One or two leaves share one row; from three leaves
|
|
23
|
+
* onward, leaves fill a first row and then a second row, with no more than four
|
|
24
|
+
* columns in either. Each row gives its leaves equal width and the two rows
|
|
25
|
+
* equal height. Existing opaque leaf objects are preserved. Split IDs are
|
|
26
|
+
* recycled across rebuilt branches, whose axis and descendants can change;
|
|
27
|
+
* resize actions must resolve against the current tree. Ratios are recomputed.
|
|
28
|
+
* This is opt-in: callers
|
|
29
|
+
* that need an explicit axis and ratio should keep using `splitPane`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function splitPaneBalanced<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, targetLeafId: string, input: BalancedSplitPaneInput<L>): SplitLayoutState<L>;
|
|
19
32
|
export declare function closePane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, leafId: string, createPlaceholderLeaf: () => L): SplitLayoutState<L>;
|
|
20
33
|
export declare function undoClosePane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>): SplitLayoutState<L>;
|
|
21
34
|
export declare function focusPane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, leafId: string): SplitLayoutState<L>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/model.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAEjF,cAAc,QAAQ,CAAA;AAEtB,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI,QAAQ,CAAC;IACnF,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,SAAS,QAAQ,CAAC;QAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAA;CAC5E,CAAC,CAAA;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI,QAAQ,CAAC;IACjF,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IAC5C,SAAS,EAAE,QAAQ,GAAG,OAAO,CAAA;IAC7B,IAAI,EAAE,CAAC,CAAA;IACP,OAAO,EAAE,MAAM,CAAA;CAChB,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,13 +27,18 @@ function assertUniqueInput(state, leaf, splitId) {
|
|
|
27
27
|
assertLayoutTree(leaf);
|
|
28
28
|
if (typeof splitId !== "string" || splitId.length === 0) throw new Error("invalid_state: split identity must be nonempty");
|
|
29
29
|
const ids = /* @__PURE__ */ new Set();
|
|
30
|
+
const splitIds = [];
|
|
30
31
|
const visit = (node) => {
|
|
31
32
|
if (ids.has(node.id)) throw new Error("invalid_state: duplicate pane id");
|
|
32
33
|
ids.add(node.id);
|
|
33
|
-
if (node.kind === "split")
|
|
34
|
+
if (node.kind === "split") {
|
|
35
|
+
splitIds.push(node.id);
|
|
36
|
+
node.children.forEach(visit);
|
|
37
|
+
}
|
|
34
38
|
};
|
|
35
39
|
visit(state.center);
|
|
36
40
|
if (ids.has(leaf.id) || ids.has(splitId) || leaf.id === splitId) throw new Error("invalid_state: duplicate pane id");
|
|
41
|
+
return splitIds;
|
|
37
42
|
}
|
|
38
43
|
/** Visual-tree validation only; applications still decode and scope persisted preferences. */
|
|
39
44
|
function assertLayoutTree(center) {
|
|
@@ -91,6 +96,45 @@ function splitPane(state, targetLeafId, input) {
|
|
|
91
96
|
closed: []
|
|
92
97
|
};
|
|
93
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Add a pane beside the selected leaf and reflow the visual tree into a
|
|
101
|
+
* balanced row-major grid. One or two leaves share one row; from three leaves
|
|
102
|
+
* onward, leaves fill a first row and then a second row, with no more than four
|
|
103
|
+
* columns in either. Each row gives its leaves equal width and the two rows
|
|
104
|
+
* equal height. Existing opaque leaf objects are preserved. Split IDs are
|
|
105
|
+
* recycled across rebuilt branches, whose axis and descendants can change;
|
|
106
|
+
* resize actions must resolve against the current tree. Ratios are recomputed.
|
|
107
|
+
* This is opt-in: callers
|
|
108
|
+
* that need an explicit axis and ratio should keep using `splitPane`.
|
|
109
|
+
*/
|
|
110
|
+
function splitPaneBalanced(state, targetLeafId, input) {
|
|
111
|
+
const leaves = [...listLeaves(state.center)];
|
|
112
|
+
const targetIndex = leaves.findIndex((leaf) => leaf.id === targetLeafId);
|
|
113
|
+
if (targetIndex < 0) return state;
|
|
114
|
+
if (leaves.length >= 8) throw new Error("limit_exceeded: center layout has eight leaves");
|
|
115
|
+
const splitIds = assertUniqueInput(state, input.leaf, input.splitId);
|
|
116
|
+
const insertionIndex = targetIndex + (input.placement === "before" ? 0 : 1);
|
|
117
|
+
leaves.splice(insertionIndex, 0, input.leaf);
|
|
118
|
+
const build = (start, end, vertical = false) => {
|
|
119
|
+
if (end - start === 1) return leaves[start];
|
|
120
|
+
const middle = start + Math.ceil((end - start) / 2);
|
|
121
|
+
return {
|
|
122
|
+
kind: "split",
|
|
123
|
+
id: middle === targetIndex + 1 ? input.splitId : splitIds.shift(),
|
|
124
|
+
direction: vertical ? "column" : "row",
|
|
125
|
+
ratio: vertical ? .5 : (middle - start) / (end - start),
|
|
126
|
+
children: [build(start, middle), build(middle, end)]
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
const center = build(0, leaves.length, leaves.length > 2);
|
|
130
|
+
assertLayoutTree(center);
|
|
131
|
+
return {
|
|
132
|
+
...state,
|
|
133
|
+
center,
|
|
134
|
+
focusedLeafId: input.leaf.id,
|
|
135
|
+
closed: []
|
|
136
|
+
};
|
|
137
|
+
}
|
|
94
138
|
function closePane(state, leafId, createPlaceholderLeaf) {
|
|
95
139
|
if (!containsLeaf(state.center, leafId)) return state;
|
|
96
140
|
const before = state.center;
|
|
@@ -207,28 +251,21 @@ function resizeSplit(state, splitId, ratio) {
|
|
|
207
251
|
*/
|
|
208
252
|
function normalizeLayout(state) {
|
|
209
253
|
const visit = (node) => {
|
|
210
|
-
if (node.kind === "leaf") return
|
|
211
|
-
node,
|
|
212
|
-
changed: false
|
|
213
|
-
};
|
|
254
|
+
if (node.kind === "leaf") return node;
|
|
214
255
|
const first = visit(node.children[0]);
|
|
215
256
|
const second = visit(node.children[1]);
|
|
216
257
|
const ratio = Number.isFinite(node.ratio) ? Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, node.ratio)) : (MIN_SPLIT_RATIO + MAX_SPLIT_RATIO) / 2;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
ratio,
|
|
222
|
-
children: [first.node, second.node]
|
|
223
|
-
} : node,
|
|
224
|
-
changed
|
|
258
|
+
return first === node.children[0] && second === node.children[1] && ratio === node.ratio ? node : {
|
|
259
|
+
...node,
|
|
260
|
+
ratio,
|
|
261
|
+
children: [first, second]
|
|
225
262
|
};
|
|
226
263
|
};
|
|
227
|
-
const
|
|
228
|
-
return
|
|
264
|
+
const center = visit(state.center);
|
|
265
|
+
return center === state.center ? state : {
|
|
229
266
|
...state,
|
|
230
|
-
center
|
|
231
|
-
}
|
|
267
|
+
center
|
|
268
|
+
};
|
|
232
269
|
}
|
|
233
270
|
/** The leaf immediately after (`1`) or before (`-1`) the given leaf in reading order. */
|
|
234
271
|
function neighborLeaf(state, leafId, step) {
|
|
@@ -238,6 +275,6 @@ function neighborLeaf(state, leafId, step) {
|
|
|
238
275
|
return leaves[index + step];
|
|
239
276
|
}
|
|
240
277
|
//#endregion
|
|
241
|
-
export { MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, closePane, countLeaves, createLayoutState, focusPane, layoutDepth, listLeaves, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, swapPanes, undoClosePane };
|
|
278
|
+
export { MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, closePane, countLeaves, createLayoutState, focusPane, layoutDepth, listLeaves, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, splitPaneBalanced, swapPanes, undoClosePane };
|
|
242
279
|
|
|
243
280
|
//# sourceMappingURL=model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","names":[],"sources":["../../../../src/components/layout/split-layout/model.ts"],"sourcesContent":["/*\n * Copyright (c) 2026 Michael Yong\n * Copyright (c) 2026 Muxy\n * SPDX-License-Identifier: MIT\n *\n * Portions of the binary split behavior are substantially translated from:\n * - get-bb/bb apps/app/src/lib/split-layout/ops.ts (MIT), revision\n * 52a9256373d4d36f9b60e9e2a7f333464091a2ac.\n * - muxy-app/muxy Muxy/Models/Workspace/SplitNode.swift (MIT), revision\n * 5c5be8697c57a2fe70cda97fdbaf7c912e2e31b6.\n * Adapted first in Adea packages/dev-view/src/layout/operations.ts at\n * 0322ab09ff5e9775bfddc0bf2d81e1df436dbef7. Extracted here with opaque\n * host-owned leaves and an injected final-pane placeholder; immutable strict\n * binary operations, accepted limits and undoable close are preserved.\n * See NOTICE and docs/research/dev-view-donor-audit.md.\n */\n\nimport {\n MAX_LAYOUT_LEAVES,\n MAX_LAYOUT_DEPTH,\n MIN_SPLIT_RATIO,\n MAX_SPLIT_RATIO,\n listLeaves,\n countLeaves,\n layoutDepth,\n} from './tree'\nimport type { SplitLayoutLeaf, SplitLayoutBranch, SplitLayoutNode } from './tree'\n\nexport * from './tree'\n\nexport type SplitLayoutState<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{\n center: SplitLayoutNode<L>\n focusedLeafId: string\n closed: readonly Readonly<{ center: SplitLayoutNode<L>; leafId: string }>[]\n}>\n\nexport type SplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{\n direction: SplitLayoutBranch<L>['direction']\n placement: 'before' | 'after'\n leaf: L\n splitId: string\n}>\n\nfunction replaceLeaf<L extends SplitLayoutLeaf>(\n node: SplitLayoutNode<L>,\n leafId: string,\n replacement: SplitLayoutNode<L>\n): SplitLayoutNode<L> {\n if (node.kind === 'leaf') return node.id === leafId ? replacement : node\n const first = replaceLeaf(node.children[0], leafId, replacement)\n const second = replaceLeaf(node.children[1], leafId, replacement)\n return first === node.children[0] && second === node.children[1]\n ? node\n : { ...node, children: [first, second] }\n}\n\nfunction removeLeaf<L extends SplitLayoutLeaf>(\n node: SplitLayoutNode<L>,\n leafId: string\n): SplitLayoutNode<L> | null {\n if (node.kind === 'leaf') return node.id === leafId ? null : node\n const first = removeLeaf(node.children[0], leafId)\n const second = removeLeaf(node.children[1], leafId)\n if (!first) return second\n if (!second) return first\n return first === node.children[0] && second === node.children[1]\n ? node\n : { ...node, children: [first, second] }\n}\n\nfunction containsLeaf<L extends SplitLayoutLeaf>(\n node: SplitLayoutNode<L>,\n leafId: string\n): boolean {\n return node.kind === 'leaf'\n ? node.id === leafId\n : containsLeaf(node.children[0], leafId) || containsLeaf(node.children[1], leafId)\n}\n\nfunction assertUniqueInput<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leaf: L,\n splitId: string\n) {\n assertLayoutTree(leaf)\n if (typeof splitId !== 'string' || splitId.length === 0)\n throw new Error('invalid_state: split identity must be nonempty')\n const ids = new Set<string>()\n const visit = (node: SplitLayoutNode<L>) => {\n if (ids.has(node.id)) throw new Error('invalid_state: duplicate pane id')\n ids.add(node.id)\n if (node.kind === 'split') node.children.forEach(visit)\n }\n visit(state.center)\n if (ids.has(leaf.id) || ids.has(splitId) || leaf.id === splitId)\n throw new Error('invalid_state: duplicate pane id')\n}\n\n/** Visual-tree validation only; applications still decode and scope persisted preferences. */\nfunction assertLayoutTree<L extends SplitLayoutLeaf>(center: SplitLayoutNode<L>): void {\n const ids = new Set<string>()\n const seen = new WeakSet<object>()\n let leaves = 0\n const visit = (node: SplitLayoutNode<L>, depth: number) => {\n if (depth > MAX_LAYOUT_DEPTH) throw new Error('limit_exceeded: layout depth exceeds eight')\n if (!node || typeof node !== 'object') throw new Error('invalid_state: missing layout node')\n if (seen.has(node)) throw new Error('invalid_state: cyclic or reused layout node')\n seen.add(node)\n if (typeof node.id !== 'string' || node.id.length === 0)\n throw new Error('invalid_state: layout identity must be nonempty')\n if (ids.has(node.id)) throw new Error('invalid_state: duplicate pane id')\n ids.add(node.id)\n if (node.kind === 'leaf') {\n leaves += 1\n if (leaves > MAX_LAYOUT_LEAVES)\n throw new Error('limit_exceeded: layout has more than eight leaves')\n return\n }\n if (\n node.kind !== 'split' ||\n !Array.isArray(node.children) ||\n node.children.length !== 2 ||\n (node.direction !== 'row' && node.direction !== 'column')\n )\n throw new Error('invalid_state: layout must be a strict binary row or column tree')\n visit(node.children[0], depth + 1)\n visit(node.children[1], depth + 1)\n }\n visit(center, 1)\n}\n\nexport function createLayoutState<L extends SplitLayoutLeaf>(\n center: SplitLayoutNode<L>\n): SplitLayoutState<L> {\n assertLayoutTree(center)\n const first = listLeaves(center)[0]\n if (!first) throw new Error('invalid_state: layout requires a leaf')\n return { center, focusedLeafId: first.id, closed: [] }\n}\n\nexport function splitPane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n targetLeafId: string,\n input: SplitPaneInput<L>\n): SplitLayoutState<L> {\n if (!containsLeaf(state.center, targetLeafId)) return state\n if (countLeaves(state.center) >= MAX_LAYOUT_LEAVES)\n throw new Error('limit_exceeded: center layout has eight leaves')\n assertUniqueInput(state, input.leaf, input.splitId)\n const target = listLeaves(state.center).find((leaf) => leaf.id === targetLeafId)\n if (!target) return state\n const children =\n input.placement === 'before' ? ([input.leaf, target] as const) : ([target, input.leaf] as const)\n const center = replaceLeaf(state.center, targetLeafId, {\n kind: 'split',\n id: input.splitId,\n direction: input.direction,\n ratio: 0.5,\n children,\n })\n if (layoutDepth(center) > MAX_LAYOUT_DEPTH)\n throw new Error('limit_exceeded: center layout depth exceeds eight')\n return { ...state, center, focusedLeafId: input.leaf.id, closed: [] }\n}\n\nexport function closePane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string,\n createPlaceholderLeaf: () => L\n): SplitLayoutState<L> {\n if (!containsLeaf(state.center, leafId)) return state\n const before = state.center\n const readingOrder = listLeaves(before)\n const closedIndex = readingOrder.findIndex((leaf) => leaf.id === leafId)\n const removed = removeLeaf(before, leafId)\n const center: SplitLayoutNode<L> = removed ?? createPlaceholderLeaf()\n if (!removed) {\n if (center.kind !== 'leaf') throw new Error('invalid_state: placeholder must be a leaf')\n assertLayoutTree(center)\n }\n const leaves = listLeaves(center)\n const fallback = leaves[Math.min(closedIndex, leaves.length - 1)] ?? leaves[0]\n if (!fallback) throw new Error('invalid_state: layout requires a leaf')\n return {\n center,\n focusedLeafId: state.focusedLeafId === leafId ? fallback.id : state.focusedLeafId,\n closed: [...state.closed, { center: before, leafId }],\n }\n}\n\nexport function undoClosePane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>\n): SplitLayoutState<L> {\n const previous = state.closed.at(-1)\n if (!previous) return state\n return {\n center: previous.center,\n focusedLeafId: previous.leafId,\n closed: state.closed.slice(0, -1),\n }\n}\n\nexport function focusPane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string\n): SplitLayoutState<L> {\n return containsLeaf(state.center, leafId) && state.focusedLeafId !== leafId\n ? { ...state, focusedLeafId: leafId }\n : state\n}\n\nexport function swapPanes<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n firstLeafId: string,\n secondLeafId: string\n): SplitLayoutState<L> {\n if (firstLeafId === secondLeafId) return state\n const leaves = listLeaves(state.center)\n const first = leaves.find((leaf) => leaf.id === firstLeafId)\n const second = leaves.find((leaf) => leaf.id === secondLeafId)\n if (!first || !second) return state\n const swap = (node: SplitLayoutNode<L>): SplitLayoutNode<L> => {\n if (node.kind === 'leaf') {\n if (node.id === firstLeafId) return second\n if (node.id === secondLeafId) return first\n return node\n }\n const left = swap(node.children[0])\n const right = swap(node.children[1])\n return left === node.children[0] && right === node.children[1]\n ? node\n : { ...node, children: [left, right] }\n }\n return { ...state, center: swap(state.center), closed: [] }\n}\n\nexport function movePane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string,\n targetLeafId: string,\n placement: 'before' | 'after',\n direction: SplitLayoutBranch<L>['direction'],\n splitId: string\n): SplitLayoutState<L> {\n if (leafId === targetLeafId) return state\n const moving = listLeaves(state.center).find((leaf) => leaf.id === leafId)\n if (!moving || !containsLeaf(state.center, targetLeafId)) return state\n const detached = removeLeaf(state.center, leafId)\n if (!detached) return state\n const moved = splitPane(\n { center: detached, focusedLeafId: state.focusedLeafId, closed: state.closed },\n targetLeafId,\n { direction, placement, leaf: moving, splitId }\n )\n return { ...moved, focusedLeafId: leafId }\n}\n\nexport function resizeSplit<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n splitId: string,\n ratio: number\n): SplitLayoutState<L> {\n if (!Number.isFinite(ratio)) return state\n const nextRatio = Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, ratio))\n let found = false\n const visit = (node: SplitLayoutNode<L>): SplitLayoutNode<L> => {\n if (node.kind === 'leaf') return node\n if (node.id === splitId) {\n found = true\n return node.ratio === nextRatio ? node : { ...node, ratio: nextRatio }\n }\n const first = visit(node.children[0])\n const second = visit(node.children[1])\n return first === node.children[0] && second === node.children[1]\n ? node\n : { ...node, children: [first, second] }\n }\n const center = visit(state.center)\n return found && center !== state.center ? { ...state, center, closed: [] } : state\n}\n\n/**\n * Pure repair walk ported from bb's size normalization: every split ratio is\n * clamped into the normative range and a non-finite ratio falls back to an\n * even split. Structure, IDs, and focus are untouched.\n */\nexport function normalizeLayout<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>\n): SplitLayoutState<L> {\n const visit = (node: SplitLayoutNode<L>): { node: SplitLayoutNode<L>; changed: boolean } => {\n if (node.kind === 'leaf') return { node, changed: false }\n const first = visit(node.children[0])\n const second = visit(node.children[1])\n const ratio = Number.isFinite(node.ratio)\n ? Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, node.ratio))\n : (MIN_SPLIT_RATIO + MAX_SPLIT_RATIO) / 2\n const changed = first.changed || second.changed || ratio !== node.ratio\n return {\n node: changed ? { ...node, ratio, children: [first.node, second.node] } : node,\n changed,\n }\n }\n const result = visit(state.center)\n return result.changed ? { ...state, center: result.node } : state\n}\n\n/** The leaf immediately after (`1`) or before (`-1`) the given leaf in reading order. */\nexport function neighborLeaf<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string,\n step: 1 | -1\n): L | undefined {\n const leaves = listLeaves(state.center)\n const index = leaves.findIndex((leaf) => leaf.id === leafId)\n if (index < 0) return undefined\n return leaves[index + step]\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model.js","names":[],"sources":["../../../../src/components/layout/split-layout/model.ts"],"sourcesContent":["/*\n * Copyright (c) 2026 Michael Yong\n * Copyright (c) 2026 Muxy\n * SPDX-License-Identifier: MIT\n *\n * Portions of the binary split behavior are substantially translated from:\n * - get-bb/bb apps/app/src/lib/split-layout/ops.ts (MIT), revision\n * 52a9256373d4d36f9b60e9e2a7f333464091a2ac.\n * - muxy-app/muxy Muxy/Models/Workspace/SplitNode.swift (MIT), revision\n * 5c5be8697c57a2fe70cda97fdbaf7c912e2e31b6.\n * Adapted first in Adea packages/dev-view/src/layout/operations.ts at\n * 0322ab09ff5e9775bfddc0bf2d81e1df436dbef7. Extracted here with opaque\n * host-owned leaves and an injected final-pane placeholder; immutable strict\n * binary operations, accepted limits and undoable close are preserved.\n * See NOTICE and docs/research/dev-view-donor-audit.md.\n */\n\nimport {\n MAX_LAYOUT_LEAVES,\n MAX_LAYOUT_DEPTH,\n MIN_SPLIT_RATIO,\n MAX_SPLIT_RATIO,\n listLeaves,\n countLeaves,\n layoutDepth,\n} from './tree'\nimport type { SplitLayoutLeaf, SplitLayoutBranch, SplitLayoutNode } from './tree'\n\nexport * from './tree'\n\nexport type SplitLayoutState<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{\n center: SplitLayoutNode<L>\n focusedLeafId: string\n closed: readonly Readonly<{ center: SplitLayoutNode<L>; leafId: string }>[]\n}>\n\nexport type SplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{\n direction: SplitLayoutBranch<L>['direction']\n placement: 'before' | 'after'\n leaf: L\n splitId: string\n}>\n\nexport type BalancedSplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<\n Pick<SplitPaneInput<L>, 'placement' | 'leaf' | 'splitId'>\n>\n\nfunction replaceLeaf<L extends SplitLayoutLeaf>(\n node: SplitLayoutNode<L>,\n leafId: string,\n replacement: SplitLayoutNode<L>\n): SplitLayoutNode<L> {\n if (node.kind === 'leaf') return node.id === leafId ? replacement : node\n const first = replaceLeaf(node.children[0], leafId, replacement)\n const second = replaceLeaf(node.children[1], leafId, replacement)\n return first === node.children[0] && second === node.children[1]\n ? node\n : { ...node, children: [first, second] }\n}\n\nfunction removeLeaf<L extends SplitLayoutLeaf>(\n node: SplitLayoutNode<L>,\n leafId: string\n): SplitLayoutNode<L> | null {\n if (node.kind === 'leaf') return node.id === leafId ? null : node\n const first = removeLeaf(node.children[0], leafId)\n const second = removeLeaf(node.children[1], leafId)\n if (!first) return second\n if (!second) return first\n return first === node.children[0] && second === node.children[1]\n ? node\n : { ...node, children: [first, second] }\n}\n\nfunction containsLeaf<L extends SplitLayoutLeaf>(\n node: SplitLayoutNode<L>,\n leafId: string\n): boolean {\n return node.kind === 'leaf'\n ? node.id === leafId\n : containsLeaf(node.children[0], leafId) || containsLeaf(node.children[1], leafId)\n}\n\nfunction assertUniqueInput<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leaf: L,\n splitId: string\n) {\n assertLayoutTree(leaf)\n if (typeof splitId !== 'string' || splitId.length === 0)\n throw new Error('invalid_state: split identity must be nonempty')\n const ids = new Set<string>()\n const splitIds: string[] = []\n const visit = (node: SplitLayoutNode<L>) => {\n if (ids.has(node.id)) throw new Error('invalid_state: duplicate pane id')\n ids.add(node.id)\n if (node.kind === 'split') {\n splitIds.push(node.id)\n node.children.forEach(visit)\n }\n }\n visit(state.center)\n if (ids.has(leaf.id) || ids.has(splitId) || leaf.id === splitId)\n throw new Error('invalid_state: duplicate pane id')\n return splitIds\n}\n\n/** Visual-tree validation only; applications still decode and scope persisted preferences. */\nfunction assertLayoutTree<L extends SplitLayoutLeaf>(center: SplitLayoutNode<L>): void {\n const ids = new Set<string>()\n const seen = new WeakSet<object>()\n let leaves = 0\n const visit = (node: SplitLayoutNode<L>, depth: number) => {\n if (depth > MAX_LAYOUT_DEPTH) throw new Error('limit_exceeded: layout depth exceeds eight')\n if (!node || typeof node !== 'object') throw new Error('invalid_state: missing layout node')\n if (seen.has(node)) throw new Error('invalid_state: cyclic or reused layout node')\n seen.add(node)\n if (typeof node.id !== 'string' || node.id.length === 0)\n throw new Error('invalid_state: layout identity must be nonempty')\n if (ids.has(node.id)) throw new Error('invalid_state: duplicate pane id')\n ids.add(node.id)\n if (node.kind === 'leaf') {\n leaves += 1\n if (leaves > MAX_LAYOUT_LEAVES)\n throw new Error('limit_exceeded: layout has more than eight leaves')\n return\n }\n if (\n node.kind !== 'split' ||\n !Array.isArray(node.children) ||\n node.children.length !== 2 ||\n (node.direction !== 'row' && node.direction !== 'column')\n )\n throw new Error('invalid_state: layout must be a strict binary row or column tree')\n visit(node.children[0], depth + 1)\n visit(node.children[1], depth + 1)\n }\n visit(center, 1)\n}\n\nexport function createLayoutState<L extends SplitLayoutLeaf>(\n center: SplitLayoutNode<L>\n): SplitLayoutState<L> {\n assertLayoutTree(center)\n const first = listLeaves(center)[0]\n if (!first) throw new Error('invalid_state: layout requires a leaf')\n return { center, focusedLeafId: first.id, closed: [] }\n}\n\nexport function splitPane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n targetLeafId: string,\n input: SplitPaneInput<L>\n): SplitLayoutState<L> {\n if (!containsLeaf(state.center, targetLeafId)) return state\n if (countLeaves(state.center) >= MAX_LAYOUT_LEAVES)\n throw new Error('limit_exceeded: center layout has eight leaves')\n assertUniqueInput(state, input.leaf, input.splitId)\n const target = listLeaves(state.center).find((leaf) => leaf.id === targetLeafId)\n if (!target) return state\n const children =\n input.placement === 'before' ? ([input.leaf, target] as const) : ([target, input.leaf] as const)\n const center = replaceLeaf(state.center, targetLeafId, {\n kind: 'split',\n id: input.splitId,\n direction: input.direction,\n ratio: 0.5,\n children,\n })\n if (layoutDepth(center) > MAX_LAYOUT_DEPTH)\n throw new Error('limit_exceeded: center layout depth exceeds eight')\n return { ...state, center, focusedLeafId: input.leaf.id, closed: [] }\n}\n\n/**\n * Add a pane beside the selected leaf and reflow the visual tree into a\n * balanced row-major grid. One or two leaves share one row; from three leaves\n * onward, leaves fill a first row and then a second row, with no more than four\n * columns in either. Each row gives its leaves equal width and the two rows\n * equal height. Existing opaque leaf objects are preserved. Split IDs are\n * recycled across rebuilt branches, whose axis and descendants can change;\n * resize actions must resolve against the current tree. Ratios are recomputed.\n * This is opt-in: callers\n * that need an explicit axis and ratio should keep using `splitPane`.\n */\nexport function splitPaneBalanced<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n targetLeafId: string,\n input: BalancedSplitPaneInput<L>\n): SplitLayoutState<L> {\n const leaves = [...listLeaves(state.center)]\n const targetIndex = leaves.findIndex((leaf) => leaf.id === targetLeafId)\n if (targetIndex < 0) return state\n if (leaves.length >= MAX_LAYOUT_LEAVES)\n throw new Error('limit_exceeded: center layout has eight leaves')\n const splitIds = assertUniqueInput(state, input.leaf, input.splitId)\n\n const insertionIndex = targetIndex + (input.placement === 'before' ? 0 : 1)\n leaves.splice(insertionIndex, 0, input.leaf)\n\n const build = (start: number, end: number, vertical = false): SplitLayoutNode<L> => {\n if (end - start === 1) return leaves[start]!\n const middle = start + Math.ceil((end - start) / 2)\n // The new identity belongs to the lowest branch separating the selected\n // pane from its new neighbour. Other branches reuse the existing ID pool.\n const id = middle === targetIndex + 1 ? input.splitId : splitIds.shift()!\n return {\n kind: 'split',\n id,\n direction: vertical ? 'column' : 'row',\n ratio: vertical ? 0.5 : (middle - start) / (end - start),\n children: [build(start, middle), build(middle, end)],\n }\n }\n const center = build(0, leaves.length, leaves.length > 2)\n assertLayoutTree(center)\n return { ...state, center, focusedLeafId: input.leaf.id, closed: [] }\n}\n\nexport function closePane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string,\n createPlaceholderLeaf: () => L\n): SplitLayoutState<L> {\n if (!containsLeaf(state.center, leafId)) return state\n const before = state.center\n const readingOrder = listLeaves(before)\n const closedIndex = readingOrder.findIndex((leaf) => leaf.id === leafId)\n const removed = removeLeaf(before, leafId)\n const center: SplitLayoutNode<L> = removed ?? createPlaceholderLeaf()\n if (!removed) {\n if (center.kind !== 'leaf') throw new Error('invalid_state: placeholder must be a leaf')\n assertLayoutTree(center)\n }\n const leaves = listLeaves(center)\n const fallback = leaves[Math.min(closedIndex, leaves.length - 1)] ?? leaves[0]\n if (!fallback) throw new Error('invalid_state: layout requires a leaf')\n return {\n center,\n focusedLeafId: state.focusedLeafId === leafId ? fallback.id : state.focusedLeafId,\n closed: [...state.closed, { center: before, leafId }],\n }\n}\n\nexport function undoClosePane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>\n): SplitLayoutState<L> {\n const previous = state.closed.at(-1)\n if (!previous) return state\n return {\n center: previous.center,\n focusedLeafId: previous.leafId,\n closed: state.closed.slice(0, -1),\n }\n}\n\nexport function focusPane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string\n): SplitLayoutState<L> {\n return containsLeaf(state.center, leafId) && state.focusedLeafId !== leafId\n ? { ...state, focusedLeafId: leafId }\n : state\n}\n\nexport function swapPanes<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n firstLeafId: string,\n secondLeafId: string\n): SplitLayoutState<L> {\n if (firstLeafId === secondLeafId) return state\n const leaves = listLeaves(state.center)\n const first = leaves.find((leaf) => leaf.id === firstLeafId)\n const second = leaves.find((leaf) => leaf.id === secondLeafId)\n if (!first || !second) return state\n const swap = (node: SplitLayoutNode<L>): SplitLayoutNode<L> => {\n if (node.kind === 'leaf') {\n if (node.id === firstLeafId) return second\n if (node.id === secondLeafId) return first\n return node\n }\n const left = swap(node.children[0])\n const right = swap(node.children[1])\n return left === node.children[0] && right === node.children[1]\n ? node\n : { ...node, children: [left, right] }\n }\n return { ...state, center: swap(state.center), closed: [] }\n}\n\nexport function movePane<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string,\n targetLeafId: string,\n placement: 'before' | 'after',\n direction: SplitLayoutBranch<L>['direction'],\n splitId: string\n): SplitLayoutState<L> {\n if (leafId === targetLeafId) return state\n const moving = listLeaves(state.center).find((leaf) => leaf.id === leafId)\n if (!moving || !containsLeaf(state.center, targetLeafId)) return state\n const detached = removeLeaf(state.center, leafId)\n if (!detached) return state\n const moved = splitPane(\n { center: detached, focusedLeafId: state.focusedLeafId, closed: state.closed },\n targetLeafId,\n { direction, placement, leaf: moving, splitId }\n )\n return { ...moved, focusedLeafId: leafId }\n}\n\nexport function resizeSplit<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n splitId: string,\n ratio: number\n): SplitLayoutState<L> {\n if (!Number.isFinite(ratio)) return state\n const nextRatio = Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, ratio))\n let found = false\n const visit = (node: SplitLayoutNode<L>): SplitLayoutNode<L> => {\n if (node.kind === 'leaf') return node\n if (node.id === splitId) {\n found = true\n return node.ratio === nextRatio ? node : { ...node, ratio: nextRatio }\n }\n const first = visit(node.children[0])\n const second = visit(node.children[1])\n return first === node.children[0] && second === node.children[1]\n ? node\n : { ...node, children: [first, second] }\n }\n const center = visit(state.center)\n return found && center !== state.center ? { ...state, center, closed: [] } : state\n}\n\n/**\n * Pure repair walk ported from bb's size normalization: every split ratio is\n * clamped into the normative range and a non-finite ratio falls back to an\n * even split. Structure, IDs, and focus are untouched.\n */\nexport function normalizeLayout<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>\n): SplitLayoutState<L> {\n const visit = (node: SplitLayoutNode<L>): SplitLayoutNode<L> => {\n if (node.kind === 'leaf') return node\n const first = visit(node.children[0])\n const second = visit(node.children[1])\n const ratio = Number.isFinite(node.ratio)\n ? Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, node.ratio))\n : (MIN_SPLIT_RATIO + MAX_SPLIT_RATIO) / 2\n return first === node.children[0] && second === node.children[1] && ratio === node.ratio\n ? node\n : { ...node, ratio, children: [first, second] }\n }\n const center = visit(state.center)\n return center === state.center ? state : { ...state, center }\n}\n\n/** The leaf immediately after (`1`) or before (`-1`) the given leaf in reading order. */\nexport function neighborLeaf<L extends SplitLayoutLeaf>(\n state: SplitLayoutState<L>,\n leafId: string,\n step: 1 | -1\n): L | undefined {\n const leaves = listLeaves(state.center)\n const index = leaves.findIndex((leaf) => leaf.id === leafId)\n if (index < 0) return undefined\n return leaves[index + step]\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/alert-dialog/alert-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,IAAI,kBAAkB,EACjC,OAAO,IAAI,yBAAyB,EACrC,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/alert-dialog/alert-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,IAAI,kBAAkB,EACjC,OAAO,IAAI,yBAAyB,EACrC,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAW9C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,kCAE3E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,OAAO,CAAC,kCAE1F;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,MAAM,CAAC,kCAExF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,OAAO,CAAC,kCAY1F;AA2BD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,yBAAyB,CAAC,kCAezF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,kCAS7D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,kCAS7D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,KAAK,CAAC,kCAGtF;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,WAAW,CAAC,kCAS7D;AAsCD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,WAAW,CAAC,kCAE7F;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAAC,WAAW,CAAC,kCAE7F"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
-
import { overlayMotion, overlayPositioner, overlayScrim, overlaySurface } from "../../../lib/overlay.js";
|
|
3
|
-
import { headingVariants } from "../typography/typography.js";
|
|
2
|
+
import { overlayMotion, overlayPositioner, overlayScrim, overlaySurface, overlayTitle } from "../../../lib/overlay.js";
|
|
4
3
|
import { className, createComponent, insert, mergeProps, spread, template, use } from "solid-js/web";
|
|
5
4
|
import { createEffect, createSignal, splitProps } from "solid-js";
|
|
6
5
|
import { AlertDialog, Content } from "@kobalte/core/alert-dialog";
|
|
@@ -99,10 +98,7 @@ function AlertDialogFooter(props) {
|
|
|
99
98
|
function AlertDialogTitle(props) {
|
|
100
99
|
const [local, rest] = splitProps(props, ["class"]);
|
|
101
100
|
return createComponent(AlertDialog.Title, mergeProps({ get ["class"]() {
|
|
102
|
-
return cn(
|
|
103
|
-
size: "card",
|
|
104
|
-
leading: "none"
|
|
105
|
-
}), local.class);
|
|
101
|
+
return cn(overlayTitle, local.class);
|
|
106
102
|
} }, rest));
|
|
107
103
|
}
|
|
108
104
|
function AlertDialogDescription(props) {
|