@adea-ai/ui 0.103.1 → 0.104.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 +10 -0
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts +4 -0
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-detail.js +36 -7
- package/dist/components/composites/catalog-browser/catalog-detail.js.map +1 -1
- package/dist/components/layout/split-layout/split-layout.d.ts.map +1 -1
- package/dist/components/layout/split-layout/split-layout.js +131 -3
- package/dist/components/layout/split-layout/split-layout.js.map +1 -1
- package/dist/r/catalog-browser.json +1 -0
- package/dist/r/registry.json +1 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-detail.tsx +36 -6
- package/dist/r/src/components/layout/split-layout/split-layout.tsx +149 -9
- package/package.json +1 -1
- package/registry.json +1 -0
- package/src/components/composites/catalog-browser/catalog-detail.tsx +36 -6
- package/src/components/layout/split-layout/split-layout.tsx +149 -9
package/README.md
CHANGED
|
@@ -122,6 +122,10 @@ host-specific permissions or metadata a shared section surface. Supply
|
|
|
122
122
|
`resultsRegionLabel` and `detailRegionLabel` to name their keyboard-focusable,
|
|
123
123
|
shared `ScrollArea` regions. The results area scrolls notices along with loading,
|
|
124
124
|
empty, and error states even when those states contain no interactive control.
|
|
125
|
+
Use `CatalogDetail sectionsLayout="columns"` for related detail fields in a single
|
|
126
|
+
padded, outlined card. Its sections share dividers on wide screens and stack on
|
|
127
|
+
narrow screens; hosts should pass `CatalogDetailSection` children directly and
|
|
128
|
+
use no local padding or border overrides. The default keeps separate section cards.
|
|
125
129
|
|
|
126
130
|
```tsx
|
|
127
131
|
import {
|
|
@@ -649,6 +653,12 @@ header content does not define the region's accessible name or the close
|
|
|
649
653
|
button's name: both continue to come from `labelForLeaf`, so hosts should
|
|
650
654
|
provide an accessible domain label there as well.
|
|
651
655
|
|
|
656
|
+
Pointer moves use a bounded, window-framed native drag-image snapshot while
|
|
657
|
+
the source dims and the handle changes from a grab to a grabbing cursor. The
|
|
658
|
+
temporary preview is inert, has no duplicate IDs, and is removed when dragging
|
|
659
|
+
ends. Reduced-motion preference disables the source opacity transition;
|
|
660
|
+
keyboard moves remain host-owned through `renderPaneActions`.
|
|
661
|
+
|
|
652
662
|
The `HostPresentation` story shows a decorative icon and host title, keyboard
|
|
653
663
|
pane stops, and a workspace-specific separator name. A narrow-width component
|
|
654
664
|
fixture also checks that a long host title does not widen its pane header.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type JSX } from 'solid-js';
|
|
2
|
+
type SectionsLayout = 'stacked' | 'columns';
|
|
2
3
|
/** Shared item summary and action frame for catalog-owned detail content. */
|
|
3
4
|
export declare function CatalogDetail(props: {
|
|
4
5
|
title: string;
|
|
@@ -11,6 +12,8 @@ export declare function CatalogDetail(props: {
|
|
|
11
12
|
badges?: JSX.Element;
|
|
12
13
|
action?: JSX.Element;
|
|
13
14
|
status?: JSX.Element;
|
|
15
|
+
/** Groups related fields in one padded card; stacks them on narrow screens. */
|
|
16
|
+
sectionsLayout?: SectionsLayout;
|
|
14
17
|
children?: JSX.Element;
|
|
15
18
|
}): JSX.Element;
|
|
16
19
|
/** A shared compact row for host-specific fields, permissions, and copy. */
|
|
@@ -18,4 +21,5 @@ export declare function CatalogDetailSection(props: {
|
|
|
18
21
|
title: string;
|
|
19
22
|
children?: JSX.Element;
|
|
20
23
|
}): JSX.Element;
|
|
24
|
+
export {};
|
|
21
25
|
//# sourceMappingURL=catalog-detail.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-detail.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/catalog-browser/catalog-detail.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"catalog-detail.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/catalog-browser/catalog-detail.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAIpF,KAAK,cAAc,GAAG,SAAS,GAAG,SAAS,CAAA;AAqB3C,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;IAC/C,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB,eAqCA;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CAAE,eAcpF"}
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import { Badge } from "../../ui/badge/badge.js";
|
|
2
|
-
import { createComponent, insert, setAttribute, template } from "solid-js/web";
|
|
3
|
-
import { Show, createUniqueId } from "solid-js";
|
|
2
|
+
import { className, createComponent, effect, insert, setAttribute, template } from "solid-js/web";
|
|
3
|
+
import { Show, createContext, createUniqueId, useContext } from "solid-js";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
4
5
|
//#region src/components/composites/catalog-browser/catalog-detail.tsx
|
|
5
6
|
var _tmpl$ = /*#__PURE__*/ template(`<span class=shrink-0>`);
|
|
6
7
|
var _tmpl$2 = /*#__PURE__*/ template(`<div class="flex shrink-0 flex-col items-end gap-2">`);
|
|
7
|
-
var _tmpl$3 = /*#__PURE__*/ template(`<
|
|
8
|
-
var _tmpl$4 = /*#__PURE__*/ template(`<
|
|
8
|
+
var _tmpl$3 = /*#__PURE__*/ template(`<div data-catalog-detail-sections>`);
|
|
9
|
+
var _tmpl$4 = /*#__PURE__*/ template(`<article class="grid min-w-0 gap-2"data-catalog-detail><div class="flex min-w-0 items-start gap-3 rounded-xl border border-border p-3"><div class="grid min-w-0 flex-1 gap-1"><div class="flex min-w-0 flex-wrap items-center gap-1.5"></div><h2 class="text-base leading-tight font-semibold tracking-tight"></h2><p class="text-muted-foreground text-sm"></p><p class="text-muted-foreground text-xs">`);
|
|
10
|
+
var _tmpl$5 = /*#__PURE__*/ template(`<section><h3 class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"></h3><div class="grid min-w-0 gap-1.5">`);
|
|
11
|
+
var SectionsLayoutContext = createContext(() => "stacked");
|
|
12
|
+
var sectionsVariants = cva("grid min-w-0", { variants: { layout: {
|
|
13
|
+
stacked: "gap-2",
|
|
14
|
+
columns: "gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0"
|
|
15
|
+
} } });
|
|
16
|
+
var sectionVariants = cva("grid min-w-0 gap-1.5 wrap-anywhere", { variants: { layout: {
|
|
17
|
+
stacked: "rounded-xl border border-border p-3",
|
|
18
|
+
columns: "md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0"
|
|
19
|
+
} } });
|
|
9
20
|
/** Shared item summary and action frame for catalog-owned detail content. */
|
|
10
21
|
function CatalogDetail(props) {
|
|
11
22
|
return (() => {
|
|
12
|
-
var _el$ = _tmpl$
|
|
23
|
+
var _el$ = _tmpl$4(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling;
|
|
13
24
|
insert(_el$2, createComponent(Show, {
|
|
14
25
|
get when() {
|
|
15
26
|
return props.leading;
|
|
@@ -54,19 +65,37 @@ function CatalogDetail(props) {
|
|
|
54
65
|
return _el$9;
|
|
55
66
|
}
|
|
56
67
|
}), null);
|
|
57
|
-
insert(_el
|
|
68
|
+
insert(_el$, createComponent(SectionsLayoutContext.Provider, {
|
|
69
|
+
value: () => props.sectionsLayout ?? "stacked",
|
|
70
|
+
get children() {
|
|
71
|
+
var _el$0 = _tmpl$3();
|
|
72
|
+
insert(_el$0, () => props.children);
|
|
73
|
+
effect((_p$) => {
|
|
74
|
+
var _v$ = props.sectionsLayout ?? "stacked", _v$2 = sectionsVariants({ layout: props.sectionsLayout ?? "stacked" });
|
|
75
|
+
_v$ !== _p$.e && setAttribute(_el$0, "data-layout", _p$.e = _v$);
|
|
76
|
+
_v$2 !== _p$.t && className(_el$0, _p$.t = _v$2);
|
|
77
|
+
return _p$;
|
|
78
|
+
}, {
|
|
79
|
+
e: void 0,
|
|
80
|
+
t: void 0
|
|
81
|
+
});
|
|
82
|
+
return _el$0;
|
|
83
|
+
}
|
|
84
|
+
}), null);
|
|
58
85
|
return _el$;
|
|
59
86
|
})();
|
|
60
87
|
}
|
|
61
88
|
/** A shared compact row for host-specific fields, permissions, and copy. */
|
|
62
89
|
function CatalogDetailSection(props) {
|
|
90
|
+
const layout = useContext(SectionsLayoutContext);
|
|
63
91
|
const headingId = `catalog-detail-section-${createUniqueId()}`;
|
|
64
92
|
return (() => {
|
|
65
|
-
var _el$1 = _tmpl$
|
|
93
|
+
var _el$1 = _tmpl$5(), _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling;
|
|
66
94
|
setAttribute(_el$1, "aria-labelledby", headingId);
|
|
67
95
|
setAttribute(_el$10, "id", headingId);
|
|
68
96
|
insert(_el$10, () => props.title);
|
|
69
97
|
insert(_el$11, () => props.children);
|
|
98
|
+
effect(() => className(_el$1, sectionVariants({ layout: layout() })));
|
|
70
99
|
return _el$1;
|
|
71
100
|
})();
|
|
72
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-detail.js","names":["createUniqueId","Show","JSX","Badge","CatalogDetail","props","title","description","category","publisher","publishedByLabel","leading","Element","eyebrow","badges","action","status","children","_el$","_tmpl$
|
|
1
|
+
{"version":3,"file":"catalog-detail.js","names":["createContext","createUniqueId","Show","useContext","JSX","Badge","cva","SectionsLayout","SectionsLayoutContext","sectionsVariants","variants","layout","stacked","columns","sectionVariants","CatalogDetail","props","title","description","category","publisher","publishedByLabel","leading","Element","eyebrow","badges","action","status","sectionsLayout","children","_el$","_tmpl$4","_el$2","firstChild","_el$4","_el$5","_el$6","nextSibling","_el$7","_el$8","_$insert","_$createComponent","when","_el$3","_tmpl$","variant","_el$9","_tmpl$2","Provider","value","_el$0","_tmpl$3","_$effect","_p$","_v$","_v$2","e","_$setAttribute","t","_$className","undefined","CatalogDetailSection","headingId","_el$1","_tmpl$5","_el$10","_el$11","_$createComponent","_$setAttribute","_$className"],"sources":["../../../../src/components/composites/catalog-browser/catalog-detail.tsx"],"sourcesContent":["import { createContext, createUniqueId, Show, useContext, type JSX } from 'solid-js'\nimport { Badge } from '../../ui/badge'\nimport { cva } from 'class-variance-authority'\n\ntype SectionsLayout = 'stacked' | 'columns'\nconst SectionsLayoutContext = createContext<() => SectionsLayout>((): SectionsLayout => 'stacked')\nconst sectionsVariants = cva('grid min-w-0', {\n variants: {\n layout: {\n stacked: 'gap-2',\n columns:\n 'gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0',\n },\n },\n})\nconst sectionVariants = cva('grid min-w-0 gap-1.5 wrap-anywhere', {\n variants: {\n layout: {\n stacked: 'rounded-xl border border-border p-3',\n columns:\n 'md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0',\n },\n },\n})\n\n/** Shared item summary and action frame for catalog-owned detail content. */\nexport function CatalogDetail(props: {\n title: string\n description: string\n category: string\n publisher: string\n publishedByLabel: (publisher: string) => string\n leading?: JSX.Element\n eyebrow?: string\n badges?: JSX.Element\n action?: JSX.Element\n status?: JSX.Element\n /** Groups related fields in one padded card; stacks them on narrow screens. */\n sectionsLayout?: SectionsLayout\n children?: JSX.Element\n}) {\n return (\n <article class=\"grid min-w-0 gap-2\" data-catalog-detail>\n <div class=\"flex min-w-0 items-start gap-3 rounded-xl border border-border p-3\">\n <Show when={props.leading}>\n <span class=\"shrink-0\">{props.leading}</span>\n </Show>\n <div class=\"grid min-w-0 flex-1 gap-1\">\n <div class=\"flex min-w-0 flex-wrap items-center gap-1.5\">\n <Show when={props.eyebrow}>\n <Badge variant=\"outline\">{props.eyebrow}</Badge>\n </Show>\n <Badge variant=\"secondary\">{props.category}</Badge>\n {props.badges}\n </div>\n <h2 class=\"text-base leading-tight font-semibold tracking-tight\">{props.title}</h2>\n <p class=\"text-muted-foreground text-sm\">{props.description}</p>\n <p class=\"text-muted-foreground text-xs\">{props.publishedByLabel(props.publisher)}</p>\n </div>\n <Show when={props.action || props.status}>\n <div class=\"flex shrink-0 flex-col items-end gap-2\">\n {props.action}\n {props.status}\n </div>\n </Show>\n </div>\n <SectionsLayoutContext.Provider value={() => props.sectionsLayout ?? 'stacked'}>\n <div\n data-catalog-detail-sections\n data-layout={props.sectionsLayout ?? 'stacked'}\n class={sectionsVariants({ layout: props.sectionsLayout ?? 'stacked' })}\n >\n {props.children}\n </div>\n </SectionsLayoutContext.Provider>\n </article>\n )\n}\n\n/** A shared compact row for host-specific fields, permissions, and copy. */\nexport function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {\n const layout = useContext(SectionsLayoutContext)\n const headingId = `catalog-detail-section-${createUniqueId()}`\n return (\n <section class={sectionVariants({ layout: layout() })} aria-labelledby={headingId}>\n <h3\n id={headingId}\n class=\"text-2xs font-semibold tracking-wider text-muted-foreground uppercase\"\n >\n {props.title}\n </h3>\n <div class=\"grid min-w-0 gap-1.5\">{props.children}</div>\n </section>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,QAAQ,EACb,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"split-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,QAAQ,EACb,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAI5C,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAA;IACjC,yFAAyF;IACzF,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IAC9C,yFAAyF;IACzF,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACpD,+GAA+G;IAC/G,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IACrB,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAA;IAC5D,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,sFAAsF;IACtF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAChD,qGAAqG;IACrG,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3E,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAqHD,mGAAmG;AACnG,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,eAqVhF"}
|
|
@@ -9,9 +9,11 @@ import { For, createComputed, createMemo, createSignal, createUniqueId, on, onCl
|
|
|
9
9
|
import Resizable from "@corvu/resizable";
|
|
10
10
|
//#region src/components/layout/split-layout/split-layout.tsx
|
|
11
11
|
var _tmpl$ = /*#__PURE__*/ template(`<div role=group data-slot=split-layout><span class=sr-only role=status>`);
|
|
12
|
-
var _tmpl$2 = /*#__PURE__*/ template(`<section role=region class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"><header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2"><span data-pane-drag-handle><span class="min-w-0 flex-1 truncate"></span></span></header><div class="min-h-0 min-w-0 flex-1 overflow-auto">`);
|
|
12
|
+
var _tmpl$2 = /*#__PURE__*/ template(`<section role=region class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"><header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2"><span data-pane-drag-handle><span class="min-w-0 flex-1 truncate"></span></span></header><div class="min-h-0 min-w-0 flex-1 overflow-auto">`);
|
|
13
13
|
var _tmpl$3 = /*#__PURE__*/ template(`<div aria-hidden=true>`);
|
|
14
14
|
var PANE_DRAG_TYPE = "application/x-adea-pane-move";
|
|
15
|
+
var DRAG_PREVIEW_MAX_WIDTH = 384;
|
|
16
|
+
var DRAG_PREVIEW_MAX_HEIGHT = 256;
|
|
15
17
|
function rectStyle(rect) {
|
|
16
18
|
return {
|
|
17
19
|
left: `${rect.x * 100}%`,
|
|
@@ -20,6 +22,99 @@ function rectStyle(rect) {
|
|
|
20
22
|
height: `${rect.height * 100}%`
|
|
21
23
|
};
|
|
22
24
|
}
|
|
25
|
+
function dragPreviewPlaceholder(source, message) {
|
|
26
|
+
const placeholder = document.createElement("div");
|
|
27
|
+
placeholder.className = cn(source.getAttribute("class"), "flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground");
|
|
28
|
+
placeholder.setAttribute("data-split-drag-placeholder", source.localName);
|
|
29
|
+
placeholder.textContent = message;
|
|
30
|
+
return placeholder;
|
|
31
|
+
}
|
|
32
|
+
function boundedSnapshotSize(width, height) {
|
|
33
|
+
const sourceWidth = Math.max(1, width);
|
|
34
|
+
const sourceHeight = Math.max(1, height);
|
|
35
|
+
const scale = Math.min(1, DRAG_PREVIEW_MAX_WIDTH / sourceWidth, DRAG_PREVIEW_MAX_HEIGHT / sourceHeight);
|
|
36
|
+
return {
|
|
37
|
+
width: Math.max(1, Math.round(sourceWidth * scale)),
|
|
38
|
+
height: Math.max(1, Math.round(sourceHeight * scale))
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function cloneDragPreviewNode(source) {
|
|
42
|
+
if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false);
|
|
43
|
+
if (!(source instanceof HTMLElement || source instanceof SVGElement)) return source.cloneNode(false);
|
|
44
|
+
if ([
|
|
45
|
+
"script",
|
|
46
|
+
"style",
|
|
47
|
+
"link",
|
|
48
|
+
"source",
|
|
49
|
+
"track",
|
|
50
|
+
"meta"
|
|
51
|
+
].includes(source.localName)) return void 0;
|
|
52
|
+
if ([
|
|
53
|
+
"iframe",
|
|
54
|
+
"video",
|
|
55
|
+
"audio",
|
|
56
|
+
"object",
|
|
57
|
+
"embed"
|
|
58
|
+
].includes(source.localName)) return dragPreviewPlaceholder(source, "Embedded content unavailable while moving");
|
|
59
|
+
if (source instanceof HTMLImageElement) {
|
|
60
|
+
const snapshot = document.createElement("canvas");
|
|
61
|
+
const size = boundedSnapshotSize(source.naturalWidth || source.width, source.naturalHeight || source.height);
|
|
62
|
+
snapshot.width = size.width;
|
|
63
|
+
snapshot.height = size.height;
|
|
64
|
+
snapshot.className = source.getAttribute("class") ?? "";
|
|
65
|
+
snapshot.setAttribute("data-split-drag-image", "");
|
|
66
|
+
try {
|
|
67
|
+
if (!source.complete || source.naturalWidth === 0) throw new Error("image has no decoded pixels");
|
|
68
|
+
const context = snapshot.getContext("2d");
|
|
69
|
+
if (!context) throw new Error("canvas snapshot context is unavailable");
|
|
70
|
+
context.drawImage(source, 0, 0, snapshot.width, snapshot.height);
|
|
71
|
+
return snapshot;
|
|
72
|
+
} catch {
|
|
73
|
+
return dragPreviewPlaceholder(source, "Image preview unavailable");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (source instanceof HTMLElement && source.localName.includes("-")) {
|
|
77
|
+
const children = document.createDocumentFragment();
|
|
78
|
+
for (const child of source.childNodes) {
|
|
79
|
+
const clone = cloneDragPreviewNode(child);
|
|
80
|
+
if (clone) children.append(clone);
|
|
81
|
+
}
|
|
82
|
+
return children.childNodes.length > 0 ? children : dragPreviewPlaceholder(source, "Live component preview unavailable while moving");
|
|
83
|
+
}
|
|
84
|
+
const clone = source.cloneNode(false);
|
|
85
|
+
for (const attribute of [
|
|
86
|
+
"id",
|
|
87
|
+
"style",
|
|
88
|
+
"draggable",
|
|
89
|
+
"data-pane-drag-handle",
|
|
90
|
+
"data-pane-id",
|
|
91
|
+
"src",
|
|
92
|
+
"srcset",
|
|
93
|
+
"href",
|
|
94
|
+
"xlink:href",
|
|
95
|
+
"data",
|
|
96
|
+
"poster"
|
|
97
|
+
]) clone.removeAttribute(attribute);
|
|
98
|
+
for (const attribute of Array.from(clone.attributes)) if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name);
|
|
99
|
+
if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {
|
|
100
|
+
const size = boundedSnapshotSize(source.width, source.height);
|
|
101
|
+
clone.width = size.width;
|
|
102
|
+
clone.height = size.height;
|
|
103
|
+
try {
|
|
104
|
+
const context = clone.getContext("2d");
|
|
105
|
+
if (!context) throw new Error("canvas snapshot context is unavailable");
|
|
106
|
+
context.drawImage(source, 0, 0, clone.width, clone.height);
|
|
107
|
+
} catch {
|
|
108
|
+
return dragPreviewPlaceholder(source, "Canvas preview unavailable while moving");
|
|
109
|
+
}
|
|
110
|
+
return clone;
|
|
111
|
+
}
|
|
112
|
+
for (const child of source.childNodes) {
|
|
113
|
+
const childClone = cloneDragPreviewNode(child);
|
|
114
|
+
if (childClone) clone.append(childClone);
|
|
115
|
+
}
|
|
116
|
+
return clone;
|
|
117
|
+
}
|
|
23
118
|
/** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */
|
|
24
119
|
function SplitLayout(props) {
|
|
25
120
|
let root;
|
|
@@ -29,11 +124,34 @@ function SplitLayout(props) {
|
|
|
29
124
|
const prefix = createUniqueId();
|
|
30
125
|
let nextDomId = 0;
|
|
31
126
|
let nextDragId = 0;
|
|
127
|
+
let dragPreview;
|
|
32
128
|
const [drag, setDrag] = createSignal();
|
|
33
129
|
const [drop, setDrop] = createSignal();
|
|
34
130
|
const clearDrag = () => {
|
|
35
131
|
setDrag(void 0);
|
|
36
132
|
setDrop(void 0);
|
|
133
|
+
dragPreview?.remove();
|
|
134
|
+
dragPreview = void 0;
|
|
135
|
+
};
|
|
136
|
+
const createDragPreview = (source) => {
|
|
137
|
+
dragPreview?.remove();
|
|
138
|
+
const preview = cloneDragPreviewNode(source);
|
|
139
|
+
preview.className = "pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl";
|
|
140
|
+
preview.removeAttribute("id");
|
|
141
|
+
preview.removeAttribute("style");
|
|
142
|
+
preview.removeAttribute("role");
|
|
143
|
+
preview.removeAttribute("aria-label");
|
|
144
|
+
preview.removeAttribute("data-pane-id");
|
|
145
|
+
preview.removeAttribute("data-focused");
|
|
146
|
+
preview.removeAttribute("data-drag-source");
|
|
147
|
+
preview.removeAttribute("data-drop-direction");
|
|
148
|
+
preview.removeAttribute("data-drop-placement");
|
|
149
|
+
preview.setAttribute("data-split-drag-preview", "");
|
|
150
|
+
preview.setAttribute("aria-hidden", "true");
|
|
151
|
+
preview.inert = true;
|
|
152
|
+
document.body.append(preview);
|
|
153
|
+
dragPreview = preview;
|
|
154
|
+
return preview;
|
|
37
155
|
};
|
|
38
156
|
const domId = (id) => {
|
|
39
157
|
const existing = domIds.get(id);
|
|
@@ -163,7 +281,14 @@ function SplitLayout(props) {
|
|
|
163
281
|
event.dataTransfer.effectAllowed = "move";
|
|
164
282
|
const source = refs.get(id);
|
|
165
283
|
const frame = source?.getBoundingClientRect();
|
|
166
|
-
if (source && frame && typeof event.dataTransfer.setDragImage === "function")
|
|
284
|
+
if (source && frame && typeof event.dataTransfer.setDragImage === "function") {
|
|
285
|
+
const preview = createDragPreview(source);
|
|
286
|
+
const previewFrame = preview.getBoundingClientRect();
|
|
287
|
+
const scaleX = previewFrame.width / frame.width;
|
|
288
|
+
const scaleY = previewFrame.height / frame.height;
|
|
289
|
+
event.dataTransfer.setDragImage(preview, Math.max(0, Math.round((event.clientX - frame.left) * scaleX)), Math.max(0, Math.round((event.clientY - frame.top) * scaleY)));
|
|
290
|
+
preview.setAttribute("data-native-drag-image", "");
|
|
291
|
+
}
|
|
167
292
|
});
|
|
168
293
|
insert(_el$5, (() => {
|
|
169
294
|
var _c$ = memo(() => !!props.onMove);
|
|
@@ -208,7 +333,10 @@ function SplitLayout(props) {
|
|
|
208
333
|
y: 0,
|
|
209
334
|
width: 0,
|
|
210
335
|
height: 0
|
|
211
|
-
}), _v$10 = Boolean(props.onMove), _v$11 = cn("flex min-w-0 flex-1 items-center gap-2 truncate text-sm", {
|
|
336
|
+
}), _v$10 = Boolean(props.onMove), _v$11 = cn("flex min-w-0 flex-1 items-center gap-2 truncate text-sm", {
|
|
337
|
+
"cursor-grab": Boolean(props.onMove),
|
|
338
|
+
"cursor-grabbing": drag()?.id === id
|
|
339
|
+
}), _v$12 = props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : void 0;
|
|
212
340
|
_v$4 !== _p$.e && setAttribute(_el$3, "id", _p$.e = _v$4);
|
|
213
341
|
_v$5 !== _p$.t && setAttribute(_el$3, "aria-label", _p$.t = _v$5);
|
|
214
342
|
_v$6 !== _p$.a && setAttribute(_el$3, "tabindex", _p$.a = _v$6);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split-layout.js","names":["Resizable","For","createMemo","createComputed","createUniqueId","createSignal","onCleanup","on","Accessor","JSX","X","GripVertical","Button","cn","computeLayoutFrames","LayoutRect","paneDropIntent","PaneDropIntent","PANE_DRAG_TYPE","listLeaves","MIN_SPLIT_RATIO","MAX_SPLIT_RATIO","SplitLayoutBranch","SplitLayoutLeaf","SplitLayoutState","SplitLayoutProps","state","L","label","labelForLeaf","leaf","renderLeaf","Element","renderPaneLabel","paneTabIndex","labelForSeparator","branch","onResize","splitId","ratio","onFocus","leafId","onClose","onMove","targetId","intent","renderPaneActions","class","rectStyle","rect","CSSProperties","left","x","top","y","width","height","SplitLayout","props","root","HTMLDivElement","pendingFrame","refs","Map","HTMLElement","domIds","prefix","nextDomId","nextDragId","drag","setDrag","id","token","drop","setDrop","clearDrag","undefined","domId","existing","get","created","set","leaves","center","leafMap","map","frames","branches","Array","from","values","flatMap","frame","node","kind","current","has","target","allowedTarget","transfer","DataTransfer","types","includes","dropLabel","placement","branchMap","schedule","action","cancelAnimationFrame","requestAnimationFrame","active","ownerDocument","activeElement","contains","field","HTMLTextAreaElement","HTMLInputElement","selection","start","selectionStart","end","selectionEnd","direction","selectionDirection","isConnected","body","focus","preventScroll","setSelectionRange","clear","close","next","_el$","_tmpl$","_el$2","firstChild","_$use","el","_$insert","_$createComponent","each","children","initial","content","paneLabel","actions","delete","_el$3","_tmpl$2","_el$4","_el$5","_el$6","_el$7","nextSibling","$$focusin","addEventListener","event","dataTransfer","clientX","clientY","currentTarget","getBoundingClientRect","accepted","getData","preventDefault","relatedTarget","Node","dropEffect","_$setAttribute","setData","effectAllowed","source","setDragImage","Math","max","round","_c$","_$memo","_c$2","variant","size","aria-label","onClick","_c$3","_el$8","_tmpl$3","_$effect","_$className","_p$","_v$4","_v$5","_v$6","_v$7","focusedLeafId","_v$8","_v$9","_v$0","_v$1","_v$10","Boolean","_v$11","_v$12","e","t","a","o","i","n","s","h","_$style","r","d","l","orientation","sizes","keyboardDelta","onSizesChange","getClientRects","length","some","Number","isFinite","abs","style","Panel","minSize","maxSize","Handle","aria-orientation","aria-controls","join","aria-valuenow","_v$","_v$2","_v$3","_$delegateEvents","_$createComponent","_$memo","_$className","_$setAttribute","_$style","_$delegateEvents"],"sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"sourcesContent":["import Resizable from '@corvu/resizable'\nimport {\n For,\n createMemo,\n createComputed,\n createUniqueId,\n createSignal,\n onCleanup,\n on,\n type Accessor,\n type JSX,\n} from 'solid-js'\nimport { X, GripVertical } from 'lucide-solid'\nimport { Button } from '../../ui/button'\nimport { cn } from '#lib/utils'\nimport { computeLayoutFrames, type LayoutRect } from './geometry'\nimport { paneDropIntent, type PaneDropIntent } from './drop'\nexport type { PaneDropIntent } from './drop'\nconst PANE_DRAG_TYPE = 'application/x-adea-pane-move'\nimport {\n listLeaves,\n MIN_SPLIT_RATIO,\n MAX_SPLIT_RATIO,\n type SplitLayoutBranch,\n type SplitLayoutLeaf,\n type SplitLayoutState,\n} from './model'\n\nexport type SplitLayoutProps<L extends SplitLayoutLeaf> = {\n state: SplitLayoutState<L>\n label: string\n /** Accessible name for a pane region and its close button. */\n labelForLeaf: (leaf: L) => string\n /** Called once per stable leaf owner; the accessor tracks opaque payload replacement. */\n renderLeaf: (leaf: Accessor<L>) => JSX.Element\n /** Inline visible header composition, separate from the region and close-button name. */\n renderPaneLabel?: (leaf: Accessor<L>) => JSX.Element\n /** Whether pane regions participate in sequential keyboard navigation; defaults to programmatic focus only. */\n paneTabIndex?: 0 | -1\n /** Accessible splitter name; defaults to the current orientation-specific label. */\n labelForSeparator?: (branch: SplitLayoutBranch<L>) => string\n onResize: (splitId: string, ratio: number) => void\n onFocus?: (leafId: string) => void\n /** Host performs the model transition and returns the surviving focus destination. */\n onClose?: (leafId: string) => string | undefined\n /** Pointer placement only; the host supplies keyboard commands and performs the model transition. */\n onMove?: (leafId: string, targetId: string, intent: PaneDropIntent) => void\n /** Stable header composition for host-owned keyboard actions/capability feedback. */\n renderPaneActions?: (leaf: Accessor<L>) => JSX.Element\n class?: string\n}\nfunction rectStyle(rect: LayoutRect): JSX.CSSProperties {\n return {\n left: `${rect.x * 100}%`,\n top: `${rect.y * 100}%`,\n width: `${rect.width * 100}%`,\n height: `${rect.height * 100}%`,\n }\n}\n/** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */\nexport function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {\n let root: HTMLDivElement | undefined\n let pendingFrame: number | undefined\n const refs = new Map<string, HTMLElement>()\n const domIds = new Map<string, string>()\n const prefix = createUniqueId()\n let nextDomId = 0\n let nextDragId = 0\n const [drag, setDrag] = createSignal<{ id: string; token: string }>()\n const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()\n const clearDrag = () => {\n setDrag(undefined)\n setDrop(undefined)\n }\n const domId = (id: string) => {\n const existing = domIds.get(id)\n if (existing) return existing\n const created = `${prefix}-pane-${nextDomId++}`\n domIds.set(id, created)\n return created\n }\n const leaves = createMemo(() => listLeaves(props.state.center))\n const leafMap = createMemo(() => new Map(leaves().map((leaf) => [leaf.id, leaf])))\n const frames = createMemo(() => computeLayoutFrames(props.state.center))\n const branches = createMemo(() =>\n Array.from(frames().values()).flatMap((frame) =>\n frame.node.kind === 'split' ? [frame.node] : []\n )\n )\n createComputed(() => {\n const current = drag()\n if (current && (!props.onMove || !leafMap().has(current.id))) clearDrag()\n const target = drop()\n if (target && !leafMap().has(target.id)) setDrop(undefined)\n })\n const allowedTarget = (id: string, transfer: DataTransfer | null) => {\n const current = drag()\n return props.onMove &&\n current &&\n current.id !== id &&\n leafMap().has(current.id) &&\n leafMap().has(id) &&\n transfer?.types.includes(PANE_DRAG_TYPE)\n ? current\n : undefined\n }\n const dropLabel = createMemo(() => {\n const target = drop()\n const leaf = target ? leafMap().get(target.id) : undefined\n return target && leaf\n ? `Drop to place pane ${target.intent.placement} ${props.labelForLeaf(leaf)}`\n : ''\n })\n const branchMap = createMemo(() => new Map(branches().map((branch) => [branch.id, branch])))\n const schedule = (action: () => void) => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n pendingFrame = requestAnimationFrame(() => {\n pendingFrame = undefined\n action()\n })\n }\n // Register before keyed children update: capture a focused editor before an\n // ordered DOM move can blur it. Never override a newer focus outside this root.\n createComputed(\n on(\n () => props.state.center,\n () => {\n const active = root?.ownerDocument.activeElement\n if (!root || !(active instanceof HTMLElement) || !root.contains(active)) return\n const field =\n active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement\n ? active\n : undefined\n const selection = field\n ? {\n start: field.selectionStart,\n end: field.selectionEnd,\n direction: field.selectionDirection,\n }\n : undefined\n schedule(() => {\n if (!active.isConnected || !root?.contains(active)) return\n const current = active.ownerDocument.activeElement\n if (current !== active && current !== active.ownerDocument.body) return\n active.focus({ preventScroll: true })\n if (field && selection?.start !== null && selection?.end !== null && selection)\n field.setSelectionRange(\n selection.start,\n selection.end,\n selection.direction ?? undefined\n )\n })\n }\n )\n )\n onCleanup(() => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n clearDrag()\n refs.clear()\n domIds.clear()\n })\n const close = (id: string) => {\n const next = props.onClose?.(id)\n if (next) schedule(() => refs.get(next)?.focus({ preventScroll: true }))\n }\n return (\n <div\n ref={(el) => {\n root = el\n }}\n role=\"group\"\n aria-label={props.label}\n data-slot=\"split-layout\"\n data-dragging={drag() ? '' : undefined}\n class={cn('relative size-full min-h-0 min-w-0 data-[dragging]:cursor-grabbing', props.class)}\n >\n <For each={leaves().map((leaf) => leaf.id)}>\n {(id) => {\n const initial = leafMap().get(id)!\n const leaf = () => leafMap().get(id) ?? initial\n const content = props.renderLeaf(leaf)\n const paneLabel = props.renderPaneLabel?.(leaf)\n const actions = props.renderPaneActions?.(leaf)\n const intent = () => (drop()?.id === id ? drop()?.intent : undefined)\n onCleanup(() => {\n refs.delete(id)\n domIds.delete(id)\n })\n return (\n <section\n ref={(el) => refs.set(id, el)}\n id={domId(id)}\n role=\"region\"\n aria-label={props.labelForLeaf(leaf())}\n tabIndex={props.paneTabIndex ?? -1}\n data-pane-id={id}\n data-focused={props.state.focusedLeafId === id ? '' : undefined}\n data-drag-source={drag()?.id === id ? '' : undefined}\n data-drop-direction={intent()?.direction}\n data-drop-placement={intent()?.placement}\n onDragOver={(event) => {\n if (!allowedTarget(id, event.dataTransfer)) return\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n if (!next) {\n setDrop(undefined)\n return\n }\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n setDrop({ id, intent: next })\n }}\n onDragLeave={(event) => {\n if (\n event.relatedTarget instanceof Node &&\n event.currentTarget.contains(event.relatedTarget)\n )\n return\n if (drop()?.id === id) setDrop(undefined)\n }}\n onDrop={(event) => {\n const current = allowedTarget(id, event.dataTransfer)\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n const accepted =\n current && next && event.dataTransfer?.getData(PANE_DRAG_TYPE) === current.token\n clearDrag()\n if (!accepted) return\n event.preventDefault()\n props.onMove?.(current.id, id, next)\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring\"\n onFocusIn={() => props.onFocus?.(id)}\n >\n <header class=\"flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2\">\n <span\n data-pane-drag-handle=\"\"\n draggable={Boolean(props.onMove)}\n class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {\n 'cursor-grab': Boolean(props.onMove),\n })}\n title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}\n onDragStart={(event) => {\n if (!props.onMove || !event.dataTransfer) {\n event.preventDefault()\n return\n }\n const token = `${prefix}-${++nextDragId}`\n setDrag({ id, token })\n event.dataTransfer.setData(PANE_DRAG_TYPE, token)\n event.dataTransfer.effectAllowed = 'move'\n // The ghost is the pane itself, not the handle strip: the\n // browser snapshots the region before the drag state dims\n // this section, so the dragged pane stays readable while\n // its source visibly recedes.\n const source = refs.get(id)\n const frame = source?.getBoundingClientRect()\n if (source && frame && typeof event.dataTransfer.setDragImage === 'function')\n event.dataTransfer.setDragImage(\n source,\n Math.max(0, Math.round(event.clientX - frame.left)),\n Math.max(0, Math.round(event.clientY - frame.top))\n )\n }}\n onDragEnd={clearDrag}\n >\n {props.onMove ? (\n <GripVertical class=\"size-3 shrink-0\" aria-hidden=\"true\" />\n ) : null}\n <span class=\"min-w-0 flex-1 truncate\">\n {paneLabel ?? props.labelForLeaf(leaf())}\n </span>\n </span>\n {actions}\n {props.onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={`Close ${props.labelForLeaf(leaf())}`}\n onClick={() => close(id)}\n >\n <X />\n </Button>\n ) : null}\n </header>\n <div class=\"min-h-0 min-w-0 flex-1 overflow-auto\">{content}</div>\n {intent() ? (\n <div\n aria-hidden=\"true\"\n class={cn('pointer-events-none absolute border-2 border-primary bg-primary/10', {\n 'inset-y-0 left-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'before',\n 'inset-y-0 right-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'after',\n 'inset-x-0 top-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'before',\n 'inset-x-0 bottom-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'after',\n })}\n />\n ) : null}\n </section>\n )\n }}\n </For>\n <span class=\"sr-only\" role=\"status\">\n {dropLabel()}\n </span>\n <For each={branches().map((branch) => branch.id)}>\n {(id) => {\n const initial = branchMap().get(id)!\n const branch: Accessor<SplitLayoutBranch<L>> = () => branchMap().get(id) ?? initial\n return (\n <Resizable\n orientation={branch().direction === 'row' ? 'horizontal' : 'vertical'}\n sizes={[branch().ratio, 1 - branch().ratio]}\n keyboardDelta={0.05}\n onSizesChange={(sizes) => {\n // Corvu also reports panel registration/unregistration while\n // mounting or disposing a controller. Those incomplete or\n // hidden-host sizes are not user resize intent.\n if (!root?.isConnected || root.getClientRects().length === 0) return\n if (\n sizes.length !== 2 ||\n sizes.some((size) => !Number.isFinite(size)) ||\n Math.abs(sizes[0]! + sizes[1]! - 1) > 0.000001\n )\n return\n if (sizes[0] !== undefined && Math.abs(sizes[0] - branch().ratio) > 0.000001)\n props.onResize(id, sizes[0])\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"pointer-events-none absolute flex min-h-0 min-w-0 data-[orientation=vertical]:flex-col\"\n >\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n <Resizable.Handle\n aria-label={\n props.labelForSeparator?.(branch()) ??\n (branch().direction === 'row' ? 'Resize pane columns' : 'Resize pane rows')\n }\n aria-orientation={branch().direction === 'row' ? 'vertical' : 'horizontal'}\n aria-controls={listLeaves(branch())\n .map((leaf) => domId(leaf.id))\n .join(' ')}\n aria-valuemin={10}\n aria-valuemax={90}\n aria-valuenow={Math.round(branch().ratio * 100)}\n class=\"pointer-events-auto relative w-px shrink-0 bg-border after:absolute after:inset-y-0 after:-inset-x-1 after:w-3 focus-visible:bg-primary focus-visible:outline-none hover:bg-primary data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:inset-x-0 data-[orientation=vertical]:after:-inset-y-1 data-[orientation=vertical]:after:h-3 data-[orientation=vertical]:after:w-full\"\n />\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n </Resizable>\n )\n }}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"split-layout.js","names":["Resizable","For","createMemo","createComputed","createUniqueId","createSignal","onCleanup","on","Accessor","JSX","X","GripVertical","Button","cn","computeLayoutFrames","LayoutRect","paneDropIntent","PaneDropIntent","PANE_DRAG_TYPE","DRAG_PREVIEW_MAX_WIDTH","DRAG_PREVIEW_MAX_HEIGHT","listLeaves","MIN_SPLIT_RATIO","MAX_SPLIT_RATIO","SplitLayoutBranch","SplitLayoutLeaf","SplitLayoutState","SplitLayoutProps","state","L","label","labelForLeaf","leaf","renderLeaf","Element","renderPaneLabel","paneTabIndex","labelForSeparator","branch","onResize","splitId","ratio","onFocus","leafId","onClose","onMove","targetId","intent","renderPaneActions","class","rectStyle","rect","CSSProperties","left","x","top","y","width","height","dragPreviewPlaceholder","source","HTMLElement","message","placeholder","document","createElement","className","getAttribute","setAttribute","localName","textContent","boundedSnapshotSize","sourceWidth","Math","max","sourceHeight","scale","min","round","cloneDragPreviewNode","Node","nodeType","TEXT_NODE","cloneNode","SVGElement","includes","undefined","HTMLImageElement","snapshot","size","naturalWidth","naturalHeight","complete","Error","context","getContext","drawImage","children","createDocumentFragment","child","childNodes","clone","append","length","attribute","removeAttribute","Array","from","attributes","test","name","HTMLCanvasElement","childClone","SplitLayout","props","root","HTMLDivElement","pendingFrame","refs","Map","domIds","prefix","nextDomId","nextDragId","dragPreview","drag","setDrag","id","token","drop","setDrop","clearDrag","remove","createDragPreview","preview","inert","body","domId","existing","get","created","set","leaves","center","leafMap","map","frames","branches","values","flatMap","frame","node","kind","current","has","target","allowedTarget","transfer","DataTransfer","types","dropLabel","placement","branchMap","schedule","action","cancelAnimationFrame","requestAnimationFrame","active","ownerDocument","activeElement","contains","field","HTMLTextAreaElement","HTMLInputElement","selection","start","selectionStart","end","selectionEnd","direction","selectionDirection","isConnected","focus","preventScroll","setSelectionRange","clear","close","next","_el$","_tmpl$","_el$2","firstChild","_$use","el","_$insert","_$createComponent","each","initial","content","paneLabel","actions","delete","_el$3","_tmpl$2","_el$4","_el$5","_el$6","_el$7","nextSibling","$$focusin","addEventListener","event","dataTransfer","clientX","clientY","currentTarget","getBoundingClientRect","accepted","getData","preventDefault","relatedTarget","dropEffect","_$setAttribute","setData","effectAllowed","setDragImage","previewFrame","scaleX","scaleY","_c$","_$memo","_c$2","variant","aria-label","onClick","_c$3","_el$8","_tmpl$3","_$effect","_$className","_p$","_v$4","_v$5","_v$6","_v$7","focusedLeafId","_v$8","_v$9","_v$0","_v$1","_v$10","Boolean","_v$11","_v$12","e","t","a","o","i","n","s","h","_$style","r","d","l","orientation","sizes","keyboardDelta","onSizesChange","getClientRects","some","Number","isFinite","abs","style","Panel","minSize","maxSize","Handle","aria-orientation","aria-controls","join","aria-valuenow","_v$","_v$2","_v$3","_$delegateEvents","_$createComponent","_$memo","_$className","_$setAttribute","_$style","_$delegateEvents"],"sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"sourcesContent":["import Resizable from '@corvu/resizable'\nimport {\n For,\n createMemo,\n createComputed,\n createUniqueId,\n createSignal,\n onCleanup,\n on,\n type Accessor,\n type JSX,\n} from 'solid-js'\nimport { X, GripVertical } from 'lucide-solid'\nimport { Button } from '../../ui/button'\nimport { cn } from '#lib/utils'\nimport { computeLayoutFrames, type LayoutRect } from './geometry'\nimport { paneDropIntent, type PaneDropIntent } from './drop'\nexport type { PaneDropIntent } from './drop'\nconst PANE_DRAG_TYPE = 'application/x-adea-pane-move'\nconst DRAG_PREVIEW_MAX_WIDTH = 384\nconst DRAG_PREVIEW_MAX_HEIGHT = 256\nimport {\n listLeaves,\n MIN_SPLIT_RATIO,\n MAX_SPLIT_RATIO,\n type SplitLayoutBranch,\n type SplitLayoutLeaf,\n type SplitLayoutState,\n} from './model'\n\nexport type SplitLayoutProps<L extends SplitLayoutLeaf> = {\n state: SplitLayoutState<L>\n label: string\n /** Accessible name for a pane region and its close button. */\n labelForLeaf: (leaf: L) => string\n /** Called once per stable leaf owner; the accessor tracks opaque payload replacement. */\n renderLeaf: (leaf: Accessor<L>) => JSX.Element\n /** Inline visible header composition, separate from the region and close-button name. */\n renderPaneLabel?: (leaf: Accessor<L>) => JSX.Element\n /** Whether pane regions participate in sequential keyboard navigation; defaults to programmatic focus only. */\n paneTabIndex?: 0 | -1\n /** Accessible splitter name; defaults to the current orientation-specific label. */\n labelForSeparator?: (branch: SplitLayoutBranch<L>) => string\n onResize: (splitId: string, ratio: number) => void\n onFocus?: (leafId: string) => void\n /** Host performs the model transition and returns the surviving focus destination. */\n onClose?: (leafId: string) => string | undefined\n /** Pointer placement only; the host supplies keyboard commands and performs the model transition. */\n onMove?: (leafId: string, targetId: string, intent: PaneDropIntent) => void\n /** Stable header composition for host-owned keyboard actions/capability feedback. */\n renderPaneActions?: (leaf: Accessor<L>) => JSX.Element\n class?: string\n}\nfunction rectStyle(rect: LayoutRect): JSX.CSSProperties {\n return {\n left: `${rect.x * 100}%`,\n top: `${rect.y * 100}%`,\n width: `${rect.width * 100}%`,\n height: `${rect.height * 100}%`,\n }\n}\nfunction dragPreviewPlaceholder(source: HTMLElement, message: string) {\n const placeholder = document.createElement('div')\n placeholder.className = cn(\n source.getAttribute('class'),\n 'flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground'\n )\n placeholder.setAttribute('data-split-drag-placeholder', source.localName)\n placeholder.textContent = message\n return placeholder\n}\nfunction boundedSnapshotSize(width: number, height: number) {\n const sourceWidth = Math.max(1, width)\n const sourceHeight = Math.max(1, height)\n const scale = Math.min(\n 1,\n DRAG_PREVIEW_MAX_WIDTH / sourceWidth,\n DRAG_PREVIEW_MAX_HEIGHT / sourceHeight\n )\n return {\n width: Math.max(1, Math.round(sourceWidth * scale)),\n height: Math.max(1, Math.round(sourceHeight * scale)),\n }\n}\nfunction cloneDragPreviewNode(source: Node): Node | undefined {\n if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false)\n if (!(source instanceof HTMLElement || source instanceof SVGElement))\n return source.cloneNode(false)\n\n if (['script', 'style', 'link', 'source', 'track', 'meta'].includes(source.localName))\n return undefined\n if (['iframe', 'video', 'audio', 'object', 'embed'].includes(source.localName))\n return dragPreviewPlaceholder(\n source as HTMLElement,\n 'Embedded content unavailable while moving'\n )\n if (source instanceof HTMLImageElement) {\n const snapshot = document.createElement('canvas')\n const size = boundedSnapshotSize(\n source.naturalWidth || source.width,\n source.naturalHeight || source.height\n )\n snapshot.width = size.width\n snapshot.height = size.height\n snapshot.className = source.getAttribute('class') ?? ''\n snapshot.setAttribute('data-split-drag-image', '')\n try {\n if (!source.complete || source.naturalWidth === 0)\n throw new Error('image has no decoded pixels')\n const context = snapshot.getContext('2d')\n if (!context) throw new Error('canvas snapshot context is unavailable')\n context.drawImage(source, 0, 0, snapshot.width, snapshot.height)\n return snapshot\n } catch {\n return dragPreviewPlaceholder(source, 'Image preview unavailable')\n }\n }\n\n // Copying a defined custom element can run its constructor; inserting it can\n // also reconnect a stream or terminal session. Flatten its display tree.\n if (source instanceof HTMLElement && source.localName.includes('-')) {\n const children = document.createDocumentFragment()\n for (const child of source.childNodes) {\n const clone = cloneDragPreviewNode(child)\n if (clone) children.append(clone)\n }\n return children.childNodes.length > 0\n ? children\n : dragPreviewPlaceholder(source, 'Live component preview unavailable while moving')\n }\n\n const clone = source.cloneNode(false) as HTMLElement | SVGElement\n for (const attribute of [\n 'id',\n 'style',\n 'draggable',\n 'data-pane-drag-handle',\n 'data-pane-id',\n 'src',\n 'srcset',\n 'href',\n 'xlink:href',\n 'data',\n 'poster',\n ])\n clone.removeAttribute(attribute)\n for (const attribute of Array.from(clone.attributes))\n if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name)\n\n if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {\n const size = boundedSnapshotSize(source.width, source.height)\n clone.width = size.width\n clone.height = size.height\n try {\n const context = clone.getContext('2d')\n if (!context) throw new Error('canvas snapshot context is unavailable')\n context.drawImage(source, 0, 0, clone.width, clone.height)\n } catch {\n return dragPreviewPlaceholder(source, 'Canvas preview unavailable while moving')\n }\n return clone\n }\n\n for (const child of source.childNodes) {\n const childClone = cloneDragPreviewNode(child)\n if (childClone) clone.append(childClone)\n }\n return clone\n}\n/** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */\nexport function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {\n let root: HTMLDivElement | undefined\n let pendingFrame: number | undefined\n const refs = new Map<string, HTMLElement>()\n const domIds = new Map<string, string>()\n const prefix = createUniqueId()\n let nextDomId = 0\n let nextDragId = 0\n let dragPreview: HTMLElement | undefined\n const [drag, setDrag] = createSignal<{ id: string; token: string }>()\n const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()\n const clearDrag = () => {\n setDrag(undefined)\n setDrop(undefined)\n dragPreview?.remove()\n dragPreview = undefined\n }\n const createDragPreview = (source: HTMLElement) => {\n dragPreview?.remove()\n const preview = cloneDragPreviewNode(source) as HTMLElement\n preview.className =\n 'pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl'\n preview.removeAttribute('id')\n preview.removeAttribute('style')\n preview.removeAttribute('role')\n preview.removeAttribute('aria-label')\n preview.removeAttribute('data-pane-id')\n preview.removeAttribute('data-focused')\n preview.removeAttribute('data-drag-source')\n preview.removeAttribute('data-drop-direction')\n preview.removeAttribute('data-drop-placement')\n preview.setAttribute('data-split-drag-preview', '')\n preview.setAttribute('aria-hidden', 'true')\n preview.inert = true\n document.body.append(preview)\n dragPreview = preview\n return preview\n }\n const domId = (id: string) => {\n const existing = domIds.get(id)\n if (existing) return existing\n const created = `${prefix}-pane-${nextDomId++}`\n domIds.set(id, created)\n return created\n }\n const leaves = createMemo(() => listLeaves(props.state.center))\n const leafMap = createMemo(() => new Map(leaves().map((leaf) => [leaf.id, leaf])))\n const frames = createMemo(() => computeLayoutFrames(props.state.center))\n const branches = createMemo(() =>\n Array.from(frames().values()).flatMap((frame) =>\n frame.node.kind === 'split' ? [frame.node] : []\n )\n )\n createComputed(() => {\n const current = drag()\n if (current && (!props.onMove || !leafMap().has(current.id))) clearDrag()\n const target = drop()\n if (target && !leafMap().has(target.id)) setDrop(undefined)\n })\n const allowedTarget = (id: string, transfer: DataTransfer | null) => {\n const current = drag()\n return props.onMove &&\n current &&\n current.id !== id &&\n leafMap().has(current.id) &&\n leafMap().has(id) &&\n transfer?.types.includes(PANE_DRAG_TYPE)\n ? current\n : undefined\n }\n const dropLabel = createMemo(() => {\n const target = drop()\n const leaf = target ? leafMap().get(target.id) : undefined\n return target && leaf\n ? `Drop to place pane ${target.intent.placement} ${props.labelForLeaf(leaf)}`\n : ''\n })\n const branchMap = createMemo(() => new Map(branches().map((branch) => [branch.id, branch])))\n const schedule = (action: () => void) => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n pendingFrame = requestAnimationFrame(() => {\n pendingFrame = undefined\n action()\n })\n }\n // Register before keyed children update: capture a focused editor before an\n // ordered DOM move can blur it. Never override a newer focus outside this root.\n createComputed(\n on(\n () => props.state.center,\n () => {\n const active = root?.ownerDocument.activeElement\n if (!root || !(active instanceof HTMLElement) || !root.contains(active)) return\n const field =\n active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement\n ? active\n : undefined\n const selection = field\n ? {\n start: field.selectionStart,\n end: field.selectionEnd,\n direction: field.selectionDirection,\n }\n : undefined\n schedule(() => {\n if (!active.isConnected || !root?.contains(active)) return\n const current = active.ownerDocument.activeElement\n if (current !== active && current !== active.ownerDocument.body) return\n active.focus({ preventScroll: true })\n if (field && selection?.start !== null && selection?.end !== null && selection)\n field.setSelectionRange(\n selection.start,\n selection.end,\n selection.direction ?? undefined\n )\n })\n }\n )\n )\n onCleanup(() => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n clearDrag()\n refs.clear()\n domIds.clear()\n })\n const close = (id: string) => {\n const next = props.onClose?.(id)\n if (next) schedule(() => refs.get(next)?.focus({ preventScroll: true }))\n }\n return (\n <div\n ref={(el) => {\n root = el\n }}\n role=\"group\"\n aria-label={props.label}\n data-slot=\"split-layout\"\n data-dragging={drag() ? '' : undefined}\n class={cn('relative size-full min-h-0 min-w-0 data-[dragging]:cursor-grabbing', props.class)}\n >\n <For each={leaves().map((leaf) => leaf.id)}>\n {(id) => {\n const initial = leafMap().get(id)!\n const leaf = () => leafMap().get(id) ?? initial\n const content = props.renderLeaf(leaf)\n const paneLabel = props.renderPaneLabel?.(leaf)\n const actions = props.renderPaneActions?.(leaf)\n const intent = () => (drop()?.id === id ? drop()?.intent : undefined)\n onCleanup(() => {\n refs.delete(id)\n domIds.delete(id)\n })\n return (\n <section\n ref={(el) => refs.set(id, el)}\n id={domId(id)}\n role=\"region\"\n aria-label={props.labelForLeaf(leaf())}\n tabIndex={props.paneTabIndex ?? -1}\n data-pane-id={id}\n data-focused={props.state.focusedLeafId === id ? '' : undefined}\n data-drag-source={drag()?.id === id ? '' : undefined}\n data-drop-direction={intent()?.direction}\n data-drop-placement={intent()?.placement}\n onDragOver={(event) => {\n if (!allowedTarget(id, event.dataTransfer)) return\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n if (!next) {\n setDrop(undefined)\n return\n }\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n setDrop({ id, intent: next })\n }}\n onDragLeave={(event) => {\n if (\n event.relatedTarget instanceof Node &&\n event.currentTarget.contains(event.relatedTarget)\n )\n return\n if (drop()?.id === id) setDrop(undefined)\n }}\n onDrop={(event) => {\n const current = allowedTarget(id, event.dataTransfer)\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n const accepted =\n current && next && event.dataTransfer?.getData(PANE_DRAG_TYPE) === current.token\n clearDrag()\n if (!accepted) return\n event.preventDefault()\n props.onMove?.(current.id, id, next)\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring\"\n onFocusIn={() => props.onFocus?.(id)}\n >\n <header class=\"flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2\">\n <span\n data-pane-drag-handle=\"\"\n draggable={Boolean(props.onMove)}\n class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {\n 'cursor-grab': Boolean(props.onMove),\n 'cursor-grabbing': drag()?.id === id,\n })}\n title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}\n onDragStart={(event) => {\n if (!props.onMove || !event.dataTransfer) {\n event.preventDefault()\n return\n }\n const token = `${prefix}-${++nextDragId}`\n setDrag({ id, token })\n event.dataTransfer.setData(PANE_DRAG_TYPE, token)\n event.dataTransfer.effectAllowed = 'move'\n // The native drag image carries this pane's actual content\n // in a bounded floating-window frame; the source recedes\n // independently as the browser follows the image pointer.\n const source = refs.get(id)\n const frame = source?.getBoundingClientRect()\n if (source && frame && typeof event.dataTransfer.setDragImage === 'function') {\n const preview = createDragPreview(source)\n const previewFrame = preview.getBoundingClientRect()\n const scaleX = previewFrame.width / frame.width\n const scaleY = previewFrame.height / frame.height\n event.dataTransfer.setDragImage(\n preview,\n Math.max(0, Math.round((event.clientX - frame.left) * scaleX)),\n Math.max(0, Math.round((event.clientY - frame.top) * scaleY))\n )\n preview.setAttribute('data-native-drag-image', '')\n }\n }}\n onDragEnd={clearDrag}\n >\n {props.onMove ? (\n <GripVertical class=\"size-3 shrink-0\" aria-hidden=\"true\" />\n ) : null}\n <span class=\"min-w-0 flex-1 truncate\">\n {paneLabel ?? props.labelForLeaf(leaf())}\n </span>\n </span>\n {actions}\n {props.onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={`Close ${props.labelForLeaf(leaf())}`}\n onClick={() => close(id)}\n >\n <X />\n </Button>\n ) : null}\n </header>\n <div class=\"min-h-0 min-w-0 flex-1 overflow-auto\">{content}</div>\n {intent() ? (\n <div\n aria-hidden=\"true\"\n class={cn('pointer-events-none absolute border-2 border-primary bg-primary/10', {\n 'inset-y-0 left-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'before',\n 'inset-y-0 right-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'after',\n 'inset-x-0 top-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'before',\n 'inset-x-0 bottom-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'after',\n })}\n />\n ) : null}\n </section>\n )\n }}\n </For>\n <span class=\"sr-only\" role=\"status\">\n {dropLabel()}\n </span>\n <For each={branches().map((branch) => branch.id)}>\n {(id) => {\n const initial = branchMap().get(id)!\n const branch: Accessor<SplitLayoutBranch<L>> = () => branchMap().get(id) ?? initial\n return (\n <Resizable\n orientation={branch().direction === 'row' ? 'horizontal' : 'vertical'}\n sizes={[branch().ratio, 1 - branch().ratio]}\n keyboardDelta={0.05}\n onSizesChange={(sizes) => {\n // Corvu also reports panel registration/unregistration while\n // mounting or disposing a controller. Those incomplete or\n // hidden-host sizes are not user resize intent.\n if (!root?.isConnected || root.getClientRects().length === 0) return\n if (\n sizes.length !== 2 ||\n sizes.some((size) => !Number.isFinite(size)) ||\n Math.abs(sizes[0]! + sizes[1]! - 1) > 0.000001\n )\n return\n if (sizes[0] !== undefined && Math.abs(sizes[0] - branch().ratio) > 0.000001)\n props.onResize(id, sizes[0])\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"pointer-events-none absolute flex min-h-0 min-w-0 data-[orientation=vertical]:flex-col\"\n >\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n <Resizable.Handle\n aria-label={\n props.labelForSeparator?.(branch()) ??\n (branch().direction === 'row' ? 'Resize pane columns' : 'Resize pane rows')\n }\n aria-orientation={branch().direction === 'row' ? 'vertical' : 'horizontal'}\n aria-controls={listLeaves(branch())\n .map((leaf) => domId(leaf.id))\n .join(' ')}\n aria-valuemin={10}\n aria-valuemax={90}\n aria-valuenow={Math.round(branch().ratio * 100)}\n class=\"pointer-events-auto relative w-px shrink-0 bg-border after:absolute after:inset-y-0 after:-inset-x-1 after:w-3 focus-visible:bg-primary focus-visible:outline-none hover:bg-primary data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:inset-x-0 data-[orientation=vertical]:after:-inset-y-1 data-[orientation=vertical]:after:h-3 data-[orientation=vertical]:after:w-full\"\n />\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n </Resizable>\n )\n }}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
|
package/dist/r/registry.json
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
import { createUniqueId, Show, type JSX } from 'solid-js'
|
|
1
|
+
import { createContext, createUniqueId, Show, useContext, type JSX } from 'solid-js'
|
|
2
2
|
import { Badge } from '../../ui/badge'
|
|
3
|
+
import { cva } from 'class-variance-authority'
|
|
4
|
+
|
|
5
|
+
type SectionsLayout = 'stacked' | 'columns'
|
|
6
|
+
const SectionsLayoutContext = createContext<() => SectionsLayout>((): SectionsLayout => 'stacked')
|
|
7
|
+
const sectionsVariants = cva('grid min-w-0', {
|
|
8
|
+
variants: {
|
|
9
|
+
layout: {
|
|
10
|
+
stacked: 'gap-2',
|
|
11
|
+
columns:
|
|
12
|
+
'gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
const sectionVariants = cva('grid min-w-0 gap-1.5 wrap-anywhere', {
|
|
17
|
+
variants: {
|
|
18
|
+
layout: {
|
|
19
|
+
stacked: 'rounded-xl border border-border p-3',
|
|
20
|
+
columns:
|
|
21
|
+
'md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
})
|
|
3
25
|
|
|
4
26
|
/** Shared item summary and action frame for catalog-owned detail content. */
|
|
5
27
|
export function CatalogDetail(props: {
|
|
@@ -13,6 +35,8 @@ export function CatalogDetail(props: {
|
|
|
13
35
|
badges?: JSX.Element
|
|
14
36
|
action?: JSX.Element
|
|
15
37
|
status?: JSX.Element
|
|
38
|
+
/** Groups related fields in one padded card; stacks them on narrow screens. */
|
|
39
|
+
sectionsLayout?: SectionsLayout
|
|
16
40
|
children?: JSX.Element
|
|
17
41
|
}) {
|
|
18
42
|
return (
|
|
@@ -40,19 +64,25 @@ export function CatalogDetail(props: {
|
|
|
40
64
|
</div>
|
|
41
65
|
</Show>
|
|
42
66
|
</div>
|
|
43
|
-
<
|
|
67
|
+
<SectionsLayoutContext.Provider value={() => props.sectionsLayout ?? 'stacked'}>
|
|
68
|
+
<div
|
|
69
|
+
data-catalog-detail-sections
|
|
70
|
+
data-layout={props.sectionsLayout ?? 'stacked'}
|
|
71
|
+
class={sectionsVariants({ layout: props.sectionsLayout ?? 'stacked' })}
|
|
72
|
+
>
|
|
73
|
+
{props.children}
|
|
74
|
+
</div>
|
|
75
|
+
</SectionsLayoutContext.Provider>
|
|
44
76
|
</article>
|
|
45
77
|
)
|
|
46
78
|
}
|
|
47
79
|
|
|
48
80
|
/** A shared compact row for host-specific fields, permissions, and copy. */
|
|
49
81
|
export function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {
|
|
82
|
+
const layout = useContext(SectionsLayoutContext)
|
|
50
83
|
const headingId = `catalog-detail-section-${createUniqueId()}`
|
|
51
84
|
return (
|
|
52
|
-
<section
|
|
53
|
-
class="grid min-w-0 gap-1.5 rounded-xl border border-border p-3"
|
|
54
|
-
aria-labelledby={headingId}
|
|
55
|
-
>
|
|
85
|
+
<section class={sectionVariants({ layout: layout() })} aria-labelledby={headingId}>
|
|
56
86
|
<h3
|
|
57
87
|
id={headingId}
|
|
58
88
|
class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"
|
|
@@ -17,6 +17,8 @@ import { computeLayoutFrames, type LayoutRect } from './geometry'
|
|
|
17
17
|
import { paneDropIntent, type PaneDropIntent } from './drop'
|
|
18
18
|
export type { PaneDropIntent } from './drop'
|
|
19
19
|
const PANE_DRAG_TYPE = 'application/x-adea-pane-move'
|
|
20
|
+
const DRAG_PREVIEW_MAX_WIDTH = 384
|
|
21
|
+
const DRAG_PREVIEW_MAX_HEIGHT = 256
|
|
20
22
|
import {
|
|
21
23
|
listLeaves,
|
|
22
24
|
MIN_SPLIT_RATIO,
|
|
@@ -57,6 +59,114 @@ function rectStyle(rect: LayoutRect): JSX.CSSProperties {
|
|
|
57
59
|
height: `${rect.height * 100}%`,
|
|
58
60
|
}
|
|
59
61
|
}
|
|
62
|
+
function dragPreviewPlaceholder(source: HTMLElement, message: string) {
|
|
63
|
+
const placeholder = document.createElement('div')
|
|
64
|
+
placeholder.className = cn(
|
|
65
|
+
source.getAttribute('class'),
|
|
66
|
+
'flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground'
|
|
67
|
+
)
|
|
68
|
+
placeholder.setAttribute('data-split-drag-placeholder', source.localName)
|
|
69
|
+
placeholder.textContent = message
|
|
70
|
+
return placeholder
|
|
71
|
+
}
|
|
72
|
+
function boundedSnapshotSize(width: number, height: number) {
|
|
73
|
+
const sourceWidth = Math.max(1, width)
|
|
74
|
+
const sourceHeight = Math.max(1, height)
|
|
75
|
+
const scale = Math.min(
|
|
76
|
+
1,
|
|
77
|
+
DRAG_PREVIEW_MAX_WIDTH / sourceWidth,
|
|
78
|
+
DRAG_PREVIEW_MAX_HEIGHT / sourceHeight
|
|
79
|
+
)
|
|
80
|
+
return {
|
|
81
|
+
width: Math.max(1, Math.round(sourceWidth * scale)),
|
|
82
|
+
height: Math.max(1, Math.round(sourceHeight * scale)),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function cloneDragPreviewNode(source: Node): Node | undefined {
|
|
86
|
+
if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false)
|
|
87
|
+
if (!(source instanceof HTMLElement || source instanceof SVGElement))
|
|
88
|
+
return source.cloneNode(false)
|
|
89
|
+
|
|
90
|
+
if (['script', 'style', 'link', 'source', 'track', 'meta'].includes(source.localName))
|
|
91
|
+
return undefined
|
|
92
|
+
if (['iframe', 'video', 'audio', 'object', 'embed'].includes(source.localName))
|
|
93
|
+
return dragPreviewPlaceholder(
|
|
94
|
+
source as HTMLElement,
|
|
95
|
+
'Embedded content unavailable while moving'
|
|
96
|
+
)
|
|
97
|
+
if (source instanceof HTMLImageElement) {
|
|
98
|
+
const snapshot = document.createElement('canvas')
|
|
99
|
+
const size = boundedSnapshotSize(
|
|
100
|
+
source.naturalWidth || source.width,
|
|
101
|
+
source.naturalHeight || source.height
|
|
102
|
+
)
|
|
103
|
+
snapshot.width = size.width
|
|
104
|
+
snapshot.height = size.height
|
|
105
|
+
snapshot.className = source.getAttribute('class') ?? ''
|
|
106
|
+
snapshot.setAttribute('data-split-drag-image', '')
|
|
107
|
+
try {
|
|
108
|
+
if (!source.complete || source.naturalWidth === 0)
|
|
109
|
+
throw new Error('image has no decoded pixels')
|
|
110
|
+
const context = snapshot.getContext('2d')
|
|
111
|
+
if (!context) throw new Error('canvas snapshot context is unavailable')
|
|
112
|
+
context.drawImage(source, 0, 0, snapshot.width, snapshot.height)
|
|
113
|
+
return snapshot
|
|
114
|
+
} catch {
|
|
115
|
+
return dragPreviewPlaceholder(source, 'Image preview unavailable')
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Copying a defined custom element can run its constructor; inserting it can
|
|
120
|
+
// also reconnect a stream or terminal session. Flatten its display tree.
|
|
121
|
+
if (source instanceof HTMLElement && source.localName.includes('-')) {
|
|
122
|
+
const children = document.createDocumentFragment()
|
|
123
|
+
for (const child of source.childNodes) {
|
|
124
|
+
const clone = cloneDragPreviewNode(child)
|
|
125
|
+
if (clone) children.append(clone)
|
|
126
|
+
}
|
|
127
|
+
return children.childNodes.length > 0
|
|
128
|
+
? children
|
|
129
|
+
: dragPreviewPlaceholder(source, 'Live component preview unavailable while moving')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const clone = source.cloneNode(false) as HTMLElement | SVGElement
|
|
133
|
+
for (const attribute of [
|
|
134
|
+
'id',
|
|
135
|
+
'style',
|
|
136
|
+
'draggable',
|
|
137
|
+
'data-pane-drag-handle',
|
|
138
|
+
'data-pane-id',
|
|
139
|
+
'src',
|
|
140
|
+
'srcset',
|
|
141
|
+
'href',
|
|
142
|
+
'xlink:href',
|
|
143
|
+
'data',
|
|
144
|
+
'poster',
|
|
145
|
+
])
|
|
146
|
+
clone.removeAttribute(attribute)
|
|
147
|
+
for (const attribute of Array.from(clone.attributes))
|
|
148
|
+
if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name)
|
|
149
|
+
|
|
150
|
+
if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {
|
|
151
|
+
const size = boundedSnapshotSize(source.width, source.height)
|
|
152
|
+
clone.width = size.width
|
|
153
|
+
clone.height = size.height
|
|
154
|
+
try {
|
|
155
|
+
const context = clone.getContext('2d')
|
|
156
|
+
if (!context) throw new Error('canvas snapshot context is unavailable')
|
|
157
|
+
context.drawImage(source, 0, 0, clone.width, clone.height)
|
|
158
|
+
} catch {
|
|
159
|
+
return dragPreviewPlaceholder(source, 'Canvas preview unavailable while moving')
|
|
160
|
+
}
|
|
161
|
+
return clone
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const child of source.childNodes) {
|
|
165
|
+
const childClone = cloneDragPreviewNode(child)
|
|
166
|
+
if (childClone) clone.append(childClone)
|
|
167
|
+
}
|
|
168
|
+
return clone
|
|
169
|
+
}
|
|
60
170
|
/** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */
|
|
61
171
|
export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {
|
|
62
172
|
let root: HTMLDivElement | undefined
|
|
@@ -66,11 +176,35 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
66
176
|
const prefix = createUniqueId()
|
|
67
177
|
let nextDomId = 0
|
|
68
178
|
let nextDragId = 0
|
|
179
|
+
let dragPreview: HTMLElement | undefined
|
|
69
180
|
const [drag, setDrag] = createSignal<{ id: string; token: string }>()
|
|
70
181
|
const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()
|
|
71
182
|
const clearDrag = () => {
|
|
72
183
|
setDrag(undefined)
|
|
73
184
|
setDrop(undefined)
|
|
185
|
+
dragPreview?.remove()
|
|
186
|
+
dragPreview = undefined
|
|
187
|
+
}
|
|
188
|
+
const createDragPreview = (source: HTMLElement) => {
|
|
189
|
+
dragPreview?.remove()
|
|
190
|
+
const preview = cloneDragPreviewNode(source) as HTMLElement
|
|
191
|
+
preview.className =
|
|
192
|
+
'pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl'
|
|
193
|
+
preview.removeAttribute('id')
|
|
194
|
+
preview.removeAttribute('style')
|
|
195
|
+
preview.removeAttribute('role')
|
|
196
|
+
preview.removeAttribute('aria-label')
|
|
197
|
+
preview.removeAttribute('data-pane-id')
|
|
198
|
+
preview.removeAttribute('data-focused')
|
|
199
|
+
preview.removeAttribute('data-drag-source')
|
|
200
|
+
preview.removeAttribute('data-drop-direction')
|
|
201
|
+
preview.removeAttribute('data-drop-placement')
|
|
202
|
+
preview.setAttribute('data-split-drag-preview', '')
|
|
203
|
+
preview.setAttribute('aria-hidden', 'true')
|
|
204
|
+
preview.inert = true
|
|
205
|
+
document.body.append(preview)
|
|
206
|
+
dragPreview = preview
|
|
207
|
+
return preview
|
|
74
208
|
}
|
|
75
209
|
const domId = (id: string) => {
|
|
76
210
|
const existing = domIds.get(id)
|
|
@@ -236,7 +370,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
236
370
|
props.onMove?.(current.id, id, next)
|
|
237
371
|
}}
|
|
238
372
|
style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}
|
|
239
|
-
class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
|
|
373
|
+
class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
|
|
240
374
|
onFocusIn={() => props.onFocus?.(id)}
|
|
241
375
|
>
|
|
242
376
|
<header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2">
|
|
@@ -245,6 +379,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
245
379
|
draggable={Boolean(props.onMove)}
|
|
246
380
|
class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {
|
|
247
381
|
'cursor-grab': Boolean(props.onMove),
|
|
382
|
+
'cursor-grabbing': drag()?.id === id,
|
|
248
383
|
})}
|
|
249
384
|
title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}
|
|
250
385
|
onDragStart={(event) => {
|
|
@@ -256,18 +391,23 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
256
391
|
setDrag({ id, token })
|
|
257
392
|
event.dataTransfer.setData(PANE_DRAG_TYPE, token)
|
|
258
393
|
event.dataTransfer.effectAllowed = 'move'
|
|
259
|
-
// The
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
// its source visibly recedes.
|
|
394
|
+
// The native drag image carries this pane's actual content
|
|
395
|
+
// in a bounded floating-window frame; the source recedes
|
|
396
|
+
// independently as the browser follows the image pointer.
|
|
263
397
|
const source = refs.get(id)
|
|
264
398
|
const frame = source?.getBoundingClientRect()
|
|
265
|
-
if (source && frame && typeof event.dataTransfer.setDragImage === 'function')
|
|
399
|
+
if (source && frame && typeof event.dataTransfer.setDragImage === 'function') {
|
|
400
|
+
const preview = createDragPreview(source)
|
|
401
|
+
const previewFrame = preview.getBoundingClientRect()
|
|
402
|
+
const scaleX = previewFrame.width / frame.width
|
|
403
|
+
const scaleY = previewFrame.height / frame.height
|
|
266
404
|
event.dataTransfer.setDragImage(
|
|
267
|
-
|
|
268
|
-
Math.max(0, Math.round(event.clientX - frame.left)),
|
|
269
|
-
Math.max(0, Math.round(event.clientY - frame.top))
|
|
405
|
+
preview,
|
|
406
|
+
Math.max(0, Math.round((event.clientX - frame.left) * scaleX)),
|
|
407
|
+
Math.max(0, Math.round((event.clientY - frame.top) * scaleY))
|
|
270
408
|
)
|
|
409
|
+
preview.setAttribute('data-native-drag-image', '')
|
|
410
|
+
}
|
|
271
411
|
}}
|
|
272
412
|
onDragEnd={clearDrag}
|
|
273
413
|
>
|
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
import { createUniqueId, Show, type JSX } from 'solid-js'
|
|
1
|
+
import { createContext, createUniqueId, Show, useContext, type JSX } from 'solid-js'
|
|
2
2
|
import { Badge } from '../../ui/badge'
|
|
3
|
+
import { cva } from 'class-variance-authority'
|
|
4
|
+
|
|
5
|
+
type SectionsLayout = 'stacked' | 'columns'
|
|
6
|
+
const SectionsLayoutContext = createContext<() => SectionsLayout>((): SectionsLayout => 'stacked')
|
|
7
|
+
const sectionsVariants = cva('grid min-w-0', {
|
|
8
|
+
variants: {
|
|
9
|
+
layout: {
|
|
10
|
+
stacked: 'gap-2',
|
|
11
|
+
columns:
|
|
12
|
+
'gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
const sectionVariants = cva('grid min-w-0 gap-1.5 wrap-anywhere', {
|
|
17
|
+
variants: {
|
|
18
|
+
layout: {
|
|
19
|
+
stacked: 'rounded-xl border border-border p-3',
|
|
20
|
+
columns:
|
|
21
|
+
'md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
})
|
|
3
25
|
|
|
4
26
|
/** Shared item summary and action frame for catalog-owned detail content. */
|
|
5
27
|
export function CatalogDetail(props: {
|
|
@@ -13,6 +35,8 @@ export function CatalogDetail(props: {
|
|
|
13
35
|
badges?: JSX.Element
|
|
14
36
|
action?: JSX.Element
|
|
15
37
|
status?: JSX.Element
|
|
38
|
+
/** Groups related fields in one padded card; stacks them on narrow screens. */
|
|
39
|
+
sectionsLayout?: SectionsLayout
|
|
16
40
|
children?: JSX.Element
|
|
17
41
|
}) {
|
|
18
42
|
return (
|
|
@@ -40,19 +64,25 @@ export function CatalogDetail(props: {
|
|
|
40
64
|
</div>
|
|
41
65
|
</Show>
|
|
42
66
|
</div>
|
|
43
|
-
<
|
|
67
|
+
<SectionsLayoutContext.Provider value={() => props.sectionsLayout ?? 'stacked'}>
|
|
68
|
+
<div
|
|
69
|
+
data-catalog-detail-sections
|
|
70
|
+
data-layout={props.sectionsLayout ?? 'stacked'}
|
|
71
|
+
class={sectionsVariants({ layout: props.sectionsLayout ?? 'stacked' })}
|
|
72
|
+
>
|
|
73
|
+
{props.children}
|
|
74
|
+
</div>
|
|
75
|
+
</SectionsLayoutContext.Provider>
|
|
44
76
|
</article>
|
|
45
77
|
)
|
|
46
78
|
}
|
|
47
79
|
|
|
48
80
|
/** A shared compact row for host-specific fields, permissions, and copy. */
|
|
49
81
|
export function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {
|
|
82
|
+
const layout = useContext(SectionsLayoutContext)
|
|
50
83
|
const headingId = `catalog-detail-section-${createUniqueId()}`
|
|
51
84
|
return (
|
|
52
|
-
<section
|
|
53
|
-
class="grid min-w-0 gap-1.5 rounded-xl border border-border p-3"
|
|
54
|
-
aria-labelledby={headingId}
|
|
55
|
-
>
|
|
85
|
+
<section class={sectionVariants({ layout: layout() })} aria-labelledby={headingId}>
|
|
56
86
|
<h3
|
|
57
87
|
id={headingId}
|
|
58
88
|
class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"
|
|
@@ -17,6 +17,8 @@ import { computeLayoutFrames, type LayoutRect } from './geometry'
|
|
|
17
17
|
import { paneDropIntent, type PaneDropIntent } from './drop'
|
|
18
18
|
export type { PaneDropIntent } from './drop'
|
|
19
19
|
const PANE_DRAG_TYPE = 'application/x-adea-pane-move'
|
|
20
|
+
const DRAG_PREVIEW_MAX_WIDTH = 384
|
|
21
|
+
const DRAG_PREVIEW_MAX_HEIGHT = 256
|
|
20
22
|
import {
|
|
21
23
|
listLeaves,
|
|
22
24
|
MIN_SPLIT_RATIO,
|
|
@@ -57,6 +59,114 @@ function rectStyle(rect: LayoutRect): JSX.CSSProperties {
|
|
|
57
59
|
height: `${rect.height * 100}%`,
|
|
58
60
|
}
|
|
59
61
|
}
|
|
62
|
+
function dragPreviewPlaceholder(source: HTMLElement, message: string) {
|
|
63
|
+
const placeholder = document.createElement('div')
|
|
64
|
+
placeholder.className = cn(
|
|
65
|
+
source.getAttribute('class'),
|
|
66
|
+
'flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground'
|
|
67
|
+
)
|
|
68
|
+
placeholder.setAttribute('data-split-drag-placeholder', source.localName)
|
|
69
|
+
placeholder.textContent = message
|
|
70
|
+
return placeholder
|
|
71
|
+
}
|
|
72
|
+
function boundedSnapshotSize(width: number, height: number) {
|
|
73
|
+
const sourceWidth = Math.max(1, width)
|
|
74
|
+
const sourceHeight = Math.max(1, height)
|
|
75
|
+
const scale = Math.min(
|
|
76
|
+
1,
|
|
77
|
+
DRAG_PREVIEW_MAX_WIDTH / sourceWidth,
|
|
78
|
+
DRAG_PREVIEW_MAX_HEIGHT / sourceHeight
|
|
79
|
+
)
|
|
80
|
+
return {
|
|
81
|
+
width: Math.max(1, Math.round(sourceWidth * scale)),
|
|
82
|
+
height: Math.max(1, Math.round(sourceHeight * scale)),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function cloneDragPreviewNode(source: Node): Node | undefined {
|
|
86
|
+
if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false)
|
|
87
|
+
if (!(source instanceof HTMLElement || source instanceof SVGElement))
|
|
88
|
+
return source.cloneNode(false)
|
|
89
|
+
|
|
90
|
+
if (['script', 'style', 'link', 'source', 'track', 'meta'].includes(source.localName))
|
|
91
|
+
return undefined
|
|
92
|
+
if (['iframe', 'video', 'audio', 'object', 'embed'].includes(source.localName))
|
|
93
|
+
return dragPreviewPlaceholder(
|
|
94
|
+
source as HTMLElement,
|
|
95
|
+
'Embedded content unavailable while moving'
|
|
96
|
+
)
|
|
97
|
+
if (source instanceof HTMLImageElement) {
|
|
98
|
+
const snapshot = document.createElement('canvas')
|
|
99
|
+
const size = boundedSnapshotSize(
|
|
100
|
+
source.naturalWidth || source.width,
|
|
101
|
+
source.naturalHeight || source.height
|
|
102
|
+
)
|
|
103
|
+
snapshot.width = size.width
|
|
104
|
+
snapshot.height = size.height
|
|
105
|
+
snapshot.className = source.getAttribute('class') ?? ''
|
|
106
|
+
snapshot.setAttribute('data-split-drag-image', '')
|
|
107
|
+
try {
|
|
108
|
+
if (!source.complete || source.naturalWidth === 0)
|
|
109
|
+
throw new Error('image has no decoded pixels')
|
|
110
|
+
const context = snapshot.getContext('2d')
|
|
111
|
+
if (!context) throw new Error('canvas snapshot context is unavailable')
|
|
112
|
+
context.drawImage(source, 0, 0, snapshot.width, snapshot.height)
|
|
113
|
+
return snapshot
|
|
114
|
+
} catch {
|
|
115
|
+
return dragPreviewPlaceholder(source, 'Image preview unavailable')
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Copying a defined custom element can run its constructor; inserting it can
|
|
120
|
+
// also reconnect a stream or terminal session. Flatten its display tree.
|
|
121
|
+
if (source instanceof HTMLElement && source.localName.includes('-')) {
|
|
122
|
+
const children = document.createDocumentFragment()
|
|
123
|
+
for (const child of source.childNodes) {
|
|
124
|
+
const clone = cloneDragPreviewNode(child)
|
|
125
|
+
if (clone) children.append(clone)
|
|
126
|
+
}
|
|
127
|
+
return children.childNodes.length > 0
|
|
128
|
+
? children
|
|
129
|
+
: dragPreviewPlaceholder(source, 'Live component preview unavailable while moving')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const clone = source.cloneNode(false) as HTMLElement | SVGElement
|
|
133
|
+
for (const attribute of [
|
|
134
|
+
'id',
|
|
135
|
+
'style',
|
|
136
|
+
'draggable',
|
|
137
|
+
'data-pane-drag-handle',
|
|
138
|
+
'data-pane-id',
|
|
139
|
+
'src',
|
|
140
|
+
'srcset',
|
|
141
|
+
'href',
|
|
142
|
+
'xlink:href',
|
|
143
|
+
'data',
|
|
144
|
+
'poster',
|
|
145
|
+
])
|
|
146
|
+
clone.removeAttribute(attribute)
|
|
147
|
+
for (const attribute of Array.from(clone.attributes))
|
|
148
|
+
if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name)
|
|
149
|
+
|
|
150
|
+
if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {
|
|
151
|
+
const size = boundedSnapshotSize(source.width, source.height)
|
|
152
|
+
clone.width = size.width
|
|
153
|
+
clone.height = size.height
|
|
154
|
+
try {
|
|
155
|
+
const context = clone.getContext('2d')
|
|
156
|
+
if (!context) throw new Error('canvas snapshot context is unavailable')
|
|
157
|
+
context.drawImage(source, 0, 0, clone.width, clone.height)
|
|
158
|
+
} catch {
|
|
159
|
+
return dragPreviewPlaceholder(source, 'Canvas preview unavailable while moving')
|
|
160
|
+
}
|
|
161
|
+
return clone
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const child of source.childNodes) {
|
|
165
|
+
const childClone = cloneDragPreviewNode(child)
|
|
166
|
+
if (childClone) clone.append(childClone)
|
|
167
|
+
}
|
|
168
|
+
return clone
|
|
169
|
+
}
|
|
60
170
|
/** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */
|
|
61
171
|
export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {
|
|
62
172
|
let root: HTMLDivElement | undefined
|
|
@@ -66,11 +176,35 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
66
176
|
const prefix = createUniqueId()
|
|
67
177
|
let nextDomId = 0
|
|
68
178
|
let nextDragId = 0
|
|
179
|
+
let dragPreview: HTMLElement | undefined
|
|
69
180
|
const [drag, setDrag] = createSignal<{ id: string; token: string }>()
|
|
70
181
|
const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()
|
|
71
182
|
const clearDrag = () => {
|
|
72
183
|
setDrag(undefined)
|
|
73
184
|
setDrop(undefined)
|
|
185
|
+
dragPreview?.remove()
|
|
186
|
+
dragPreview = undefined
|
|
187
|
+
}
|
|
188
|
+
const createDragPreview = (source: HTMLElement) => {
|
|
189
|
+
dragPreview?.remove()
|
|
190
|
+
const preview = cloneDragPreviewNode(source) as HTMLElement
|
|
191
|
+
preview.className =
|
|
192
|
+
'pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl'
|
|
193
|
+
preview.removeAttribute('id')
|
|
194
|
+
preview.removeAttribute('style')
|
|
195
|
+
preview.removeAttribute('role')
|
|
196
|
+
preview.removeAttribute('aria-label')
|
|
197
|
+
preview.removeAttribute('data-pane-id')
|
|
198
|
+
preview.removeAttribute('data-focused')
|
|
199
|
+
preview.removeAttribute('data-drag-source')
|
|
200
|
+
preview.removeAttribute('data-drop-direction')
|
|
201
|
+
preview.removeAttribute('data-drop-placement')
|
|
202
|
+
preview.setAttribute('data-split-drag-preview', '')
|
|
203
|
+
preview.setAttribute('aria-hidden', 'true')
|
|
204
|
+
preview.inert = true
|
|
205
|
+
document.body.append(preview)
|
|
206
|
+
dragPreview = preview
|
|
207
|
+
return preview
|
|
74
208
|
}
|
|
75
209
|
const domId = (id: string) => {
|
|
76
210
|
const existing = domIds.get(id)
|
|
@@ -236,7 +370,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
236
370
|
props.onMove?.(current.id, id, next)
|
|
237
371
|
}}
|
|
238
372
|
style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}
|
|
239
|
-
class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
|
|
373
|
+
class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
|
|
240
374
|
onFocusIn={() => props.onFocus?.(id)}
|
|
241
375
|
>
|
|
242
376
|
<header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2">
|
|
@@ -245,6 +379,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
245
379
|
draggable={Boolean(props.onMove)}
|
|
246
380
|
class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {
|
|
247
381
|
'cursor-grab': Boolean(props.onMove),
|
|
382
|
+
'cursor-grabbing': drag()?.id === id,
|
|
248
383
|
})}
|
|
249
384
|
title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}
|
|
250
385
|
onDragStart={(event) => {
|
|
@@ -256,18 +391,23 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
|
|
|
256
391
|
setDrag({ id, token })
|
|
257
392
|
event.dataTransfer.setData(PANE_DRAG_TYPE, token)
|
|
258
393
|
event.dataTransfer.effectAllowed = 'move'
|
|
259
|
-
// The
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
// its source visibly recedes.
|
|
394
|
+
// The native drag image carries this pane's actual content
|
|
395
|
+
// in a bounded floating-window frame; the source recedes
|
|
396
|
+
// independently as the browser follows the image pointer.
|
|
263
397
|
const source = refs.get(id)
|
|
264
398
|
const frame = source?.getBoundingClientRect()
|
|
265
|
-
if (source && frame && typeof event.dataTransfer.setDragImage === 'function')
|
|
399
|
+
if (source && frame && typeof event.dataTransfer.setDragImage === 'function') {
|
|
400
|
+
const preview = createDragPreview(source)
|
|
401
|
+
const previewFrame = preview.getBoundingClientRect()
|
|
402
|
+
const scaleX = previewFrame.width / frame.width
|
|
403
|
+
const scaleY = previewFrame.height / frame.height
|
|
266
404
|
event.dataTransfer.setDragImage(
|
|
267
|
-
|
|
268
|
-
Math.max(0, Math.round(event.clientX - frame.left)),
|
|
269
|
-
Math.max(0, Math.round(event.clientY - frame.top))
|
|
405
|
+
preview,
|
|
406
|
+
Math.max(0, Math.round((event.clientX - frame.left) * scaleX)),
|
|
407
|
+
Math.max(0, Math.round((event.clientY - frame.top) * scaleY))
|
|
270
408
|
)
|
|
409
|
+
preview.setAttribute('data-native-drag-image', '')
|
|
410
|
+
}
|
|
271
411
|
}}
|
|
272
412
|
onDragEnd={clearDrag}
|
|
273
413
|
>
|