@composy/layout-runtime 0.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0-alpha.0
4
+
5
+ - Added the DOM-free CSS Grid runtime and responsive style serialization helpers.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LDesign Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # @composy/layout-runtime
2
+
3
+ DOM runtime for rendering a `LayoutDocument` with CSS Grid. It owns breakpoint resolution,
4
+ responsive resize observation, item shell lifecycle, and renderer cleanup without importing
5
+ GridStack or a UI framework.
6
+
7
+ ```ts
8
+ import { createLayoutRuntime } from '@composy/layout-runtime'
9
+
10
+ const runtime = createLayoutRuntime({ container, document })
11
+ runtime.mount()
12
+ ```
13
+
14
+ 运行时也可接受 `materials`。没有提供 `renderItem` 时,优先创建物料声明的 Web
15
+ Component,其次使用 HTML 标签或 `html.render`;提供 `renderItem` 时由宿主完全接管渲染。
16
+
17
+ Use `update`, `setBreakpoint`, and `destroy` to control the runtime lifecycle.
package/dist/index.js ADDED
@@ -0,0 +1,243 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ "use strict";
11
+ (function(global, factory) {
12
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@composy/layout-core")) : typeof define === "function" && define.amd ? define(["exports", "@composy/layout-core"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.LDesignLayoutRuntime = {}, global.ComposyLayoutCore));
13
+ })(this, function(exports, _composy_layout_core) {
14
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
15
+ //#region src/styles.ts
16
+ function resolveGridStyle(document, breakpointId) {
17
+ const grid = (0, _composy_layout_core.getEffectiveGrid)(document, breakpointId);
18
+ const breakpoint = (0, _composy_layout_core.getBreakpoint)(document, breakpointId);
19
+ const style = {
20
+ display: "grid",
21
+ gridTemplateColumns: `repeat(${grid.columns}, minmax(0, 1fr))`,
22
+ gridAutoRows: `${grid.rowSize}px`,
23
+ gap: `${grid.gap}px`,
24
+ direction: grid.direction
25
+ };
26
+ if (document.grid.minRows !== void 0) style.minHeight = `${document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap}px`;
27
+ if (document.grid.maxRows !== void 0) style.maxHeight = `${document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap}px`;
28
+ if (breakpoint?.canvasWidth) style.gridTemplateColumns = `repeat(${grid.columns}, minmax(0, 1fr))`;
29
+ return style;
30
+ }
31
+ function resolveItemStyle(node, breakpointId) {
32
+ const placement = node.layout[breakpointId];
33
+ if (!placement) return { display: "none" };
34
+ return {
35
+ gridColumn: `${placement.x + 1} / span ${placement.w}`,
36
+ gridRow: `${placement.y + 1} / span ${placement.h}`,
37
+ display: placement.hidden ? "none" : ""
38
+ };
39
+ }
40
+ //#endregion
41
+ //#region src/material-renderer.ts
42
+ function normalizeMaterialSource(source) {
43
+ if (!source) return void 0;
44
+ if (Array.isArray(source)) return (0, _composy_layout_core.createMaterialRegistry)(source);
45
+ return source;
46
+ }
47
+ function renderRuntimeMaterial(context, materials) {
48
+ const material = materials?.get(context.item.type);
49
+ const renderer = material?.renderers?.webcomponent ?? material?.renderers?.html;
50
+ if (!material || !renderer) return void 0;
51
+ const host = context.shell.ownerDocument;
52
+ let node;
53
+ if (renderer.tagName) node = host.createElement(renderer.tagName);
54
+ else if (renderer.render) {
55
+ const rendered = renderer.render({
56
+ item: context.item,
57
+ placement: context.placement,
58
+ breakpointId: context.breakpointId
59
+ });
60
+ if (rendered instanceof Node) node = rendered;
61
+ else if (typeof rendered === "string") {
62
+ const wrapper = host.createElement("div");
63
+ wrapper.innerHTML = sanitizeMarkup(rendered);
64
+ node = wrapper;
65
+ }
66
+ }
67
+ if (!node) return void 0;
68
+ const applyProps = (item) => {
69
+ if (!(node instanceof HTMLElement)) return;
70
+ for (const [key, value] of Object.entries(item.props ?? {})) {
71
+ try {
72
+ node[key] = value;
73
+ } catch {}
74
+ if (value === null || value === void 0 || typeof value === "object" || typeof value === "function") {
75
+ if (value !== null && value !== void 0) node.dataset[`prop${toDataKey(key)}`] = JSON.stringify(value);
76
+ } else node.setAttribute(key, String(value));
77
+ }
78
+ };
79
+ applyProps(context.item);
80
+ context.shell.replaceChildren(node);
81
+ return {
82
+ update(next) {
83
+ applyProps(next.item);
84
+ },
85
+ destroy() {
86
+ if (node?.parentNode === context.shell) context.shell.removeChild(node);
87
+ }
88
+ };
89
+ }
90
+ function toDataKey(value) {
91
+ return value.replace(/[^a-zA-Z0-9]/g, "");
92
+ }
93
+ function sanitizeMarkup(value) {
94
+ return value.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi, "").replace(/\s+on[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "").replace(/\s+(?:href|src)\s*=\s*("|')\s*javascript:[^"']*\1/gi, "");
95
+ }
96
+ //#endregion
97
+ //#region src/runtime.ts
98
+ function createLayoutRuntime(options) {
99
+ let document = options.document;
100
+ let mounted = false;
101
+ let manualBreakpoint;
102
+ let activeBreakpoint;
103
+ let observer;
104
+ const handles = /* @__PURE__ */ new Map();
105
+ const shells = /* @__PURE__ */ new Map();
106
+ const ownedShells = /* @__PURE__ */ new Set();
107
+ const materials = normalizeMaterialSource(options.materials);
108
+ function render() {
109
+ if (!mounted || !document) return;
110
+ const result = (0, _composy_layout_core.validateLayoutDocument)(document);
111
+ if (!result.valid) {
112
+ options.onError?.(new Error(result.issues[0]?.message ?? "Invalid layout document"));
113
+ return;
114
+ }
115
+ const breakpointId = manualBreakpoint ?? activeBreakpoint ?? document.defaultBreakpointId;
116
+ activeBreakpoint = breakpointId;
117
+ const gridStyle = resolveGridStyle(document, breakpointId);
118
+ Object.assign(options.container.style, gridStyle);
119
+ const currentIds = new Set(document.items.map((item) => item.id));
120
+ for (const [id, handle] of handles) {
121
+ if (currentIds.has(id)) continue;
122
+ try {
123
+ handle.destroy?.();
124
+ } finally {
125
+ handles.delete(id);
126
+ const shell = shells.get(id);
127
+ if (shell && ownedShells.has(shell)) shell.remove();
128
+ if (shell) ownedShells.delete(shell);
129
+ shells.delete(id);
130
+ }
131
+ }
132
+ for (const item of document.items) {
133
+ const placement = item.layout[breakpointId];
134
+ let shell = shells.get(item.id);
135
+ if (!shell) {
136
+ shell = options.getItemShell?.(item);
137
+ if (!shell) {
138
+ shell = options.container.ownerDocument.createElement("div");
139
+ shell.className = "ldesign-layout-item";
140
+ shell.dataset.layoutItemId = item.id;
141
+ shell.dataset.layoutType = item.type;
142
+ ownedShells.add(shell);
143
+ options.container.appendChild(shell);
144
+ }
145
+ shells.set(item.id, shell);
146
+ }
147
+ Object.assign(shell.style, resolveItemStyle(item, breakpointId));
148
+ if (!placement) continue;
149
+ const context = {
150
+ shell,
151
+ item,
152
+ placement,
153
+ breakpointId
154
+ };
155
+ try {
156
+ let handle = handles.get(item.id);
157
+ if (!handle) {
158
+ handle = options.renderItem ? options.renderItem(context) ?? {} : renderRuntimeMaterial(context, materials) ?? {};
159
+ handles.set(item.id, handle);
160
+ } else handle.update?.(context);
161
+ } catch (error) {
162
+ options.onError?.(error);
163
+ }
164
+ }
165
+ }
166
+ function updateBreakpoint(width) {
167
+ if (!document || manualBreakpoint) return;
168
+ const next = (0, _composy_layout_core.resolveBreakpoint)(document, width).id;
169
+ if (next !== activeBreakpoint) {
170
+ const previous = activeBreakpoint;
171
+ activeBreakpoint = next;
172
+ options.onBreakpointChange?.(next);
173
+ if (previous !== next) render();
174
+ }
175
+ }
176
+ function updateBreakpointFromContainer() {
177
+ if (!document || manualBreakpoint) return;
178
+ const width = options.container.getBoundingClientRect().width || options.container.clientWidth;
179
+ if (width > 0) updateBreakpoint(width);
180
+ else if (activeBreakpoint !== document.defaultBreakpointId) {
181
+ const previous = activeBreakpoint;
182
+ activeBreakpoint = document.defaultBreakpointId;
183
+ if (previous) options.onBreakpointChange?.(activeBreakpoint);
184
+ }
185
+ }
186
+ return {
187
+ mount() {
188
+ if (mounted) return;
189
+ mounted = true;
190
+ if (options.observeResize !== false && typeof ResizeObserver !== "undefined") {
191
+ observer = new ResizeObserver((entries) => {
192
+ const width = entries[0]?.contentRect.width ?? options.container.clientWidth;
193
+ if (width > 0) updateBreakpoint(width);
194
+ else updateBreakpointFromContainer();
195
+ });
196
+ observer.observe(options.container);
197
+ }
198
+ updateBreakpointFromContainer();
199
+ render();
200
+ },
201
+ update(next) {
202
+ document = next;
203
+ render();
204
+ },
205
+ setBreakpoint(id) {
206
+ manualBreakpoint = id;
207
+ if (id) activeBreakpoint = id;
208
+ else updateBreakpointFromContainer();
209
+ render();
210
+ },
211
+ getBreakpoint() {
212
+ return activeBreakpoint;
213
+ },
214
+ getContainer() {
215
+ return options.container;
216
+ },
217
+ destroy() {
218
+ if (!mounted) return;
219
+ observer?.disconnect();
220
+ observer = void 0;
221
+ for (const handle of handles.values()) handle.destroy?.();
222
+ handles.clear();
223
+ for (const shell of shells.values()) if (ownedShells.has(shell)) shell.remove();
224
+ shells.clear();
225
+ ownedShells.clear();
226
+ options.container.style.removeProperty("display");
227
+ options.container.style.removeProperty("grid-template-columns");
228
+ options.container.style.removeProperty("grid-auto-rows");
229
+ options.container.style.removeProperty("gap");
230
+ options.container.style.removeProperty("direction");
231
+ options.container.style.removeProperty("min-height");
232
+ options.container.style.removeProperty("max-height");
233
+ mounted = false;
234
+ }
235
+ };
236
+ }
237
+ //#endregion
238
+ exports.createLayoutRuntime = createLayoutRuntime;
239
+ exports.resolveGridStyle = resolveGridStyle;
240
+ exports.resolveItemStyle = resolveItemStyle;
241
+ });
242
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
243
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/styles.ts","../src/material-renderer.ts","../src/runtime.ts"],"sourcesContent":["import type {\n BreakpointId,\n LayoutDocument,\n LayoutNode,\n LayoutPlacement,\n} from '@composy/layout-core'\nimport { getEffectiveGrid, getBreakpoint } from '@composy/layout-core'\n\nexport interface GridStyleResult {\n display: 'grid'\n gridTemplateColumns: string\n gridAutoRows: string\n gap: string\n direction: 'ltr' | 'rtl'\n minHeight?: string\n maxHeight?: string\n}\n\nexport function resolveGridStyle(\n document: LayoutDocument,\n breakpointId: BreakpointId\n): GridStyleResult {\n const grid = getEffectiveGrid(document, breakpointId)\n const breakpoint = getBreakpoint(document, breakpointId)\n const style: GridStyleResult = {\n display: 'grid',\n gridTemplateColumns: `repeat(${grid.columns}, minmax(0, 1fr))`,\n gridAutoRows: `${grid.rowSize}px`,\n gap: `${grid.gap}px`,\n direction: grid.direction,\n }\n if (document.grid.minRows !== undefined)\n style.minHeight = `${document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap}px`\n if (document.grid.maxRows !== undefined)\n style.maxHeight = `${document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap}px`\n if (breakpoint?.canvasWidth)\n style.gridTemplateColumns = `repeat(${grid.columns}, minmax(0, 1fr))`\n return style\n}\n\nexport function resolveItemStyle(\n node: LayoutNode,\n breakpointId: BreakpointId\n): Record<string, string> {\n const placement = node.layout[breakpointId]\n if (!placement) return { display: 'none' }\n return {\n gridColumn: `${placement.x + 1} / span ${placement.w}`,\n gridRow: `${placement.y + 1} / span ${placement.h}`,\n display: placement.hidden ? 'none' : '',\n }\n}\n","import {\n createMaterialRegistry,\n type LayoutNode,\n type MaterialDefinition,\n type MaterialRegistry,\n} from '@composy/layout-core'\nimport type { LayoutRenderContext, LayoutRenderHandle } from './runtime'\n\nexport type RuntimeMaterialSource =\n MaterialRegistry | readonly MaterialDefinition[] | undefined\n\nexport function normalizeMaterialSource(\n source: RuntimeMaterialSource\n): MaterialRegistry | undefined {\n if (!source) return undefined\n if (Array.isArray(source)) return createMaterialRegistry(source)\n return source as MaterialRegistry\n}\n\nexport function renderRuntimeMaterial(\n context: LayoutRenderContext,\n materials: MaterialRegistry | undefined\n): LayoutRenderHandle | undefined {\n const material = materials?.get(context.item.type)\n const renderer =\n material?.renderers?.webcomponent ?? material?.renderers?.html\n if (!material || !renderer) return undefined\n\n const host = context.shell.ownerDocument\n let node: Node | undefined\n if (renderer.tagName) {\n node = host.createElement(renderer.tagName)\n } else if (renderer.render) {\n const rendered = renderer.render({\n item: context.item,\n placement: context.placement,\n breakpointId: context.breakpointId,\n })\n if (rendered instanceof Node) node = rendered\n else if (typeof rendered === 'string') {\n const wrapper = host.createElement('div')\n wrapper.innerHTML = sanitizeMarkup(rendered)\n node = wrapper\n }\n }\n if (!node) return undefined\n\n const applyProps = (item: LayoutNode): void => {\n if (!(node instanceof HTMLElement)) return\n for (const [key, value] of Object.entries(item.props ?? {})) {\n try {\n ;(node as unknown as Record<string, unknown>)[key] = value\n } catch {\n // Some custom elements expose readonly properties. The attribute is a\n // useful fallback for string-like values.\n }\n if (\n value === null ||\n value === undefined ||\n typeof value === 'object' ||\n typeof value === 'function'\n ) {\n if (value !== null && value !== undefined)\n node.dataset[`prop${toDataKey(key)}`] = JSON.stringify(value)\n } else node.setAttribute(key, String(value))\n }\n }\n applyProps(context.item)\n context.shell.replaceChildren(node)\n return {\n update(next) {\n applyProps(next.item)\n },\n destroy() {\n if (node?.parentNode === context.shell) context.shell.removeChild(node)\n },\n }\n}\n\nfunction toDataKey(value: string): string {\n return value.replace(/[^a-zA-Z0-9]/g, '')\n}\n\nfunction sanitizeMarkup(value: string): string {\n return value\n .replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script\\s*>/gi, '')\n .replace(/\\s+on[a-z]+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi, '')\n .replace(/\\s+(?:href|src)\\s*=\\s*(\"|')\\s*javascript:[^\"']*\\1/gi, '')\n}\n","import type {\n BreakpointId,\n LayoutDocument,\n LayoutNode,\n LayoutPlacement,\n MaterialDefinition,\n MaterialRegistry,\n} from '@composy/layout-core'\nimport { resolveBreakpoint, validateLayoutDocument } from '@composy/layout-core'\nimport { resolveGridStyle, resolveItemStyle } from './styles'\nimport {\n normalizeMaterialSource,\n renderRuntimeMaterial,\n} from './material-renderer'\n\nexport interface LayoutRenderContext {\n shell: HTMLElement\n item: LayoutNode\n placement: LayoutPlacement\n breakpointId: BreakpointId\n}\nexport interface LayoutRenderHandle {\n update?(context: LayoutRenderContext): void\n destroy?(): void\n}\nexport interface LayoutRuntimeOptions {\n container: HTMLElement\n document?: LayoutDocument\n /** Lets a host adapter provide an existing light-DOM item shell. */\n getItemShell?: (item: LayoutNode) => HTMLElement | undefined\n renderItem?: (context: LayoutRenderContext) => LayoutRenderHandle | void\n /** Material registry used by the framework-neutral default renderer. */\n materials?: MaterialRegistry | readonly MaterialDefinition[]\n observeResize?: boolean\n onBreakpointChange?: (id: BreakpointId) => void\n onError?: (error: unknown) => void\n}\n\nexport interface LayoutRuntime {\n mount(): void\n update(document: LayoutDocument): void\n setBreakpoint(id?: BreakpointId): void\n getBreakpoint(): BreakpointId | undefined\n getContainer(): HTMLElement\n destroy(): void\n}\n\nexport function createLayoutRuntime(\n options: LayoutRuntimeOptions\n): LayoutRuntime {\n let document = options.document\n let mounted = false\n let manualBreakpoint: BreakpointId | undefined\n let activeBreakpoint: BreakpointId | undefined\n let observer: ResizeObserver | undefined\n const handles = new Map<string, LayoutRenderHandle>()\n const shells = new Map<string, HTMLElement>()\n const ownedShells = new Set<HTMLElement>()\n const materials = normalizeMaterialSource(options.materials)\n\n function render(): void {\n if (!mounted || !document) return\n const result = validateLayoutDocument(document)\n if (!result.valid) {\n options.onError?.(\n new Error(result.issues[0]?.message ?? 'Invalid layout document')\n )\n return\n }\n const breakpointId =\n manualBreakpoint ?? activeBreakpoint ?? document.defaultBreakpointId\n activeBreakpoint = breakpointId\n const gridStyle = resolveGridStyle(document, breakpointId)\n Object.assign(options.container.style, gridStyle)\n const currentIds = new Set(document.items.map(item => item.id))\n for (const [id, handle] of handles) {\n if (currentIds.has(id)) continue\n try {\n handle.destroy?.()\n } finally {\n handles.delete(id)\n const shell = shells.get(id)\n if (shell && ownedShells.has(shell)) shell.remove()\n if (shell) ownedShells.delete(shell)\n shells.delete(id)\n }\n }\n for (const item of document.items) {\n const placement = item.layout[breakpointId]\n let shell = shells.get(item.id)\n if (!shell) {\n shell = options.getItemShell?.(item)\n if (!shell) {\n shell = options.container.ownerDocument.createElement('div')\n shell.className = 'ldesign-layout-item'\n shell.dataset.layoutItemId = item.id\n shell.dataset.layoutType = item.type\n ownedShells.add(shell)\n options.container.appendChild(shell)\n }\n shells.set(item.id, shell)\n }\n Object.assign(shell.style, resolveItemStyle(item, breakpointId))\n if (!placement) continue\n const context = { shell, item, placement, breakpointId }\n try {\n let handle = handles.get(item.id)\n if (!handle) {\n handle = options.renderItem\n ? (options.renderItem(context) ?? {})\n : (renderRuntimeMaterial(context, materials) ?? {})\n handles.set(item.id, handle)\n } else handle.update?.(context)\n } catch (error) {\n options.onError?.(error)\n }\n }\n }\n\n function updateBreakpoint(width: number): void {\n if (!document || manualBreakpoint) return\n const next = resolveBreakpoint(document, width).id\n if (next !== activeBreakpoint) {\n const previous = activeBreakpoint\n activeBreakpoint = next\n options.onBreakpointChange?.(next)\n if (previous !== next) render()\n }\n }\n\n function updateBreakpointFromContainer(): void {\n if (!document || manualBreakpoint) return\n const width =\n options.container.getBoundingClientRect().width ||\n options.container.clientWidth\n // A hidden or detached container has no measurable width. Keep the\n // document default until the first real resize instead of selecting the\n // smallest breakpoint for width 0.\n if (width > 0) updateBreakpoint(width)\n else if (activeBreakpoint !== document.defaultBreakpointId) {\n const previous = activeBreakpoint\n activeBreakpoint = document.defaultBreakpointId\n if (previous) options.onBreakpointChange?.(activeBreakpoint)\n }\n }\n\n return {\n mount() {\n if (mounted) return\n mounted = true\n if (\n options.observeResize !== false &&\n typeof ResizeObserver !== 'undefined'\n ) {\n observer = new ResizeObserver(entries => {\n const width =\n entries[0]?.contentRect.width ?? options.container.clientWidth\n if (width > 0) updateBreakpoint(width)\n else updateBreakpointFromContainer()\n })\n observer.observe(options.container)\n }\n updateBreakpointFromContainer()\n render()\n },\n update(next) {\n document = next\n render()\n },\n setBreakpoint(id) {\n manualBreakpoint = id\n if (id) activeBreakpoint = id\n else updateBreakpointFromContainer()\n render()\n },\n getBreakpoint() {\n return activeBreakpoint\n },\n getContainer() {\n return options.container\n },\n destroy() {\n if (!mounted) return\n observer?.disconnect()\n observer = undefined\n for (const handle of handles.values()) handle.destroy?.()\n handles.clear()\n for (const shell of shells.values())\n if (ownedShells.has(shell)) shell.remove()\n shells.clear()\n ownedShells.clear()\n options.container.style.removeProperty('display')\n options.container.style.removeProperty('grid-template-columns')\n options.container.style.removeProperty('grid-auto-rows')\n options.container.style.removeProperty('gap')\n options.container.style.removeProperty('direction')\n options.container.style.removeProperty('min-height')\n options.container.style.removeProperty('max-height')\n mounted = false\n },\n }\n}\n\nexport { resolveGridStyle, resolveItemStyle }\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ "use strict";(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports,require("@composy/layout-core")):typeof define==="function"&&define.amd?define(["exports","@composy/layout-core"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.LDesignLayoutRuntime={},global.ComposyLayoutCore));})(this,function(exports,_composy_layout_core){Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function resolveGridStyle(document,breakpointId){const grid=(0,_composy_layout_core.getEffectiveGrid)(document,breakpointId);const breakpoint=(0,_composy_layout_core.getBreakpoint)(document,breakpointId);const style={display:"grid",gridTemplateColumns:`repeat(${grid.columns},minmax(0,1fr))`,gridAutoRows:`${grid.rowSize}px`,gap:`${grid.gap}px`,direction:grid.direction};if(document.grid.minRows!==void 0)style.minHeight=`${document.grid.minRows*(grid.rowSize+grid.gap)-grid.gap}px`;if(document.grid.maxRows!==void 0)style.maxHeight=`${document.grid.maxRows*(grid.rowSize+grid.gap)-grid.gap}px`;if(breakpoint?.canvasWidth)style.gridTemplateColumns=`repeat(${grid.columns},minmax(0,1fr))`;return style;}function resolveItemStyle(node,breakpointId){const placement=node.layout[breakpointId];if(!placement)return{display:"none"};return{gridColumn:`${placement.x+1}/span ${placement.w}`,gridRow:`${placement.y+1}/span ${placement.h}`,display:placement.hidden?"none":""};}function normalizeMaterialSource(source){if(!source)return void 0;if(Array.isArray(source))return(0,_composy_layout_core.createMaterialRegistry)(source);return source;}function renderRuntimeMaterial(context,materials){const material=materials?.get(context.item.type);const renderer=material?.renderers?.webcomponent??material?.renderers?.html;if(!material||!renderer)return void 0;const host=context.shell.ownerDocument;let node;if(renderer.tagName)node=host.createElement(renderer.tagName);else if(renderer.render){const rendered=renderer.render({item:context.item,placement:context.placement,breakpointId:context.breakpointId});if(rendered instanceof Node)node=rendered;else if(typeof rendered==="string"){const wrapper=host.createElement("div");wrapper.innerHTML=sanitizeMarkup(rendered);node=wrapper;}}if(!node)return void 0;const applyProps=(item)=>{if(!(node instanceof HTMLElement))return;for(const[key,value]of Object.entries(item.props??{})){try{node[key]=value;}catch{}if(value===null||value===void 0||typeof value==="object"||typeof value==="function"){if(value!==null&&value!==void 0)node.dataset[`prop${toDataKey(key)}`]=JSON.stringify(value);}else node.setAttribute(key,String(value));}};applyProps(context.item);context.shell.replaceChildren(node);return{update(next){applyProps(next.item);},destroy(){if(node?.parentNode===context.shell)context.shell.removeChild(node);}};}function toDataKey(value){return value.replace(/[^a-zA-Z0-9]/g,"");}function sanitizeMarkup(value){return value.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi,"").replace(/\s+on[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi,"").replace(/\s+(?:href|src)\s*=\s*("|')\s*javascript:[^"']*\1/gi,"");}function createLayoutRuntime(options){let document=options.document;let mounted=false;let manualBreakpoint;let activeBreakpoint;let observer;const handles=new Map();const shells=new Map();const ownedShells=new Set();const materials=normalizeMaterialSource(options.materials);function render(){if(!mounted||!document)return;const result=(0,_composy_layout_core.validateLayoutDocument)(document);if(!result.valid){options.onError?.(new Error(result.issues[0]?.message??"Invalid layout document"));return;}const breakpointId=manualBreakpoint??activeBreakpoint??document.defaultBreakpointId;activeBreakpoint=breakpointId;const gridStyle=resolveGridStyle(document,breakpointId);Object.assign(options.container.style,gridStyle);const currentIds=new Set(document.items.map((item)=>item.id));for(const[id,handle]of handles){if(currentIds.has(id))continue;try{handle.destroy?.();}finally{handles.delete(id);const shell=shells.get(id);if(shell&&ownedShells.has(shell))shell.remove();if(shell)ownedShells.delete(shell);shells.delete(id);}}for(const item of document.items){const placement=item.layout[breakpointId];let shell=shells.get(item.id);if(!shell){shell=options.getItemShell?.(item);if(!shell){shell=options.container.ownerDocument.createElement("div");shell.className="ldesign-layout-item";shell.dataset.layoutItemId=item.id;shell.dataset.layoutType=item.type;ownedShells.add(shell);options.container.appendChild(shell);}shells.set(item.id,shell);}Object.assign(shell.style,resolveItemStyle(item,breakpointId));if(!placement)continue;const context={shell,item,placement,breakpointId};try{let handle=handles.get(item.id);if(!handle){handle=options.renderItem?options.renderItem(context)??{}:renderRuntimeMaterial(context,materials)??{};handles.set(item.id,handle);}else handle.update?.(context);}catch(error){options.onError?.(error);}}}function updateBreakpoint(width){if(!document||manualBreakpoint)return;const next=(0,_composy_layout_core.resolveBreakpoint)(document,width).id;if(next!==activeBreakpoint){const previous=activeBreakpoint;activeBreakpoint=next;options.onBreakpointChange?.(next);if(previous!==next)render();}}function updateBreakpointFromContainer(){if(!document||manualBreakpoint)return;const width=options.container.getBoundingClientRect().width||options.container.clientWidth;if(width>0)updateBreakpoint(width);else if(activeBreakpoint!==document.defaultBreakpointId){const previous=activeBreakpoint;activeBreakpoint=document.defaultBreakpointId;if(previous)options.onBreakpointChange?.(activeBreakpoint);}}return{mount(){if(mounted)return;mounted=true;if(options.observeResize!==false&&typeof ResizeObserver!=="undefined"){observer=new ResizeObserver((entries)=>{const width=entries[0]?.contentRect.width??options.container.clientWidth;if(width>0)updateBreakpoint(width);else updateBreakpointFromContainer();});observer.observe(options.container);}updateBreakpointFromContainer();render();},update(next){document=next;render();},setBreakpoint(id){manualBreakpoint=id;if(id)activeBreakpoint=id;else updateBreakpointFromContainer();render();},getBreakpoint(){return activeBreakpoint;},getContainer(){return options.container;},destroy(){if(!mounted)return;observer?.disconnect();observer=void 0;for(const handle of handles.values())handle.destroy?.();handles.clear();for(const shell of shells.values())if(ownedShells.has(shell))shell.remove();shells.clear();ownedShells.clear();options.container.style.removeProperty("display");options.container.style.removeProperty("grid-template-columns");options.container.style.removeProperty("grid-auto-rows");options.container.style.removeProperty("gap");options.container.style.removeProperty("direction");options.container.style.removeProperty("min-height");options.container.style.removeProperty("max-height");mounted=false;}};}exports.createLayoutRuntime=createLayoutRuntime;exports.resolveGridStyle=resolveGridStyle;exports.resolveItemStyle=resolveItemStyle;});
package/es/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './styles';
2
+ export * from './runtime';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA"}
package/es/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ import { resolveGridStyle, resolveItemStyle } from "./styles.js";
11
+ import { createLayoutRuntime } from "./runtime.js";
12
+ export { createLayoutRuntime, resolveGridStyle, resolveItemStyle };
13
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
@@ -0,0 +1,6 @@
1
+ import { type MaterialDefinition, type MaterialRegistry } from '@composy/layout-core';
2
+ import type { LayoutRenderContext, LayoutRenderHandle } from './runtime';
3
+ export type RuntimeMaterialSource = MaterialRegistry | readonly MaterialDefinition[] | undefined;
4
+ export declare function normalizeMaterialSource(source: RuntimeMaterialSource): MaterialRegistry | undefined;
5
+ export declare function renderRuntimeMaterial(context: LayoutRenderContext, materials: MaterialRegistry | undefined): LayoutRenderHandle | undefined;
6
+ //# sourceMappingURL=material-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"material-renderer.d.ts","sourceRoot":"","sources":["../src/material-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAExE,MAAM,MAAM,qBAAqB,GAC/B,gBAAgB,GAAG,SAAS,kBAAkB,EAAE,GAAG,SAAS,CAAA;AAE9D,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,qBAAqB,GAC5B,gBAAgB,GAAG,SAAS,CAI9B;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,gBAAgB,GAAG,SAAS,GACtC,kBAAkB,GAAG,SAAS,CAuDhC"}
@@ -0,0 +1,69 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ import { createMaterialRegistry } from "@composy/layout-core";
11
+ //#region src/material-renderer.ts
12
+ function normalizeMaterialSource(source) {
13
+ if (!source) return void 0;
14
+ if (Array.isArray(source)) return createMaterialRegistry(source);
15
+ return source;
16
+ }
17
+ function renderRuntimeMaterial(context, materials) {
18
+ const material = materials?.get(context.item.type);
19
+ const renderer = material?.renderers?.webcomponent ?? material?.renderers?.html;
20
+ if (!material || !renderer) return void 0;
21
+ const host = context.shell.ownerDocument;
22
+ let node;
23
+ if (renderer.tagName) node = host.createElement(renderer.tagName);
24
+ else if (renderer.render) {
25
+ const rendered = renderer.render({
26
+ item: context.item,
27
+ placement: context.placement,
28
+ breakpointId: context.breakpointId
29
+ });
30
+ if (rendered instanceof Node) node = rendered;
31
+ else if (typeof rendered === "string") {
32
+ const wrapper = host.createElement("div");
33
+ wrapper.innerHTML = sanitizeMarkup(rendered);
34
+ node = wrapper;
35
+ }
36
+ }
37
+ if (!node) return void 0;
38
+ const applyProps = (item) => {
39
+ if (!(node instanceof HTMLElement)) return;
40
+ for (const [key, value] of Object.entries(item.props ?? {})) {
41
+ try {
42
+ node[key] = value;
43
+ } catch {}
44
+ if (value === null || value === void 0 || typeof value === "object" || typeof value === "function") {
45
+ if (value !== null && value !== void 0) node.dataset[`prop${toDataKey(key)}`] = JSON.stringify(value);
46
+ } else node.setAttribute(key, String(value));
47
+ }
48
+ };
49
+ applyProps(context.item);
50
+ context.shell.replaceChildren(node);
51
+ return {
52
+ update(next) {
53
+ applyProps(next.item);
54
+ },
55
+ destroy() {
56
+ if (node?.parentNode === context.shell) context.shell.removeChild(node);
57
+ }
58
+ };
59
+ }
60
+ function toDataKey(value) {
61
+ return value.replace(/[^a-zA-Z0-9]/g, "");
62
+ }
63
+ function sanitizeMarkup(value) {
64
+ return value.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi, "").replace(/\s+on[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "").replace(/\s+(?:href|src)\s*=\s*("|')\s*javascript:[^"']*\1/gi, "");
65
+ }
66
+ //#endregion
67
+ export { normalizeMaterialSource, renderRuntimeMaterial };
68
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
69
+ //# sourceMappingURL=material-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"material-renderer.js","names":[],"sources":["../src/material-renderer.ts"],"sourcesContent":["import {\n createMaterialRegistry,\n type LayoutNode,\n type MaterialDefinition,\n type MaterialRegistry,\n} from '@composy/layout-core'\nimport type { LayoutRenderContext, LayoutRenderHandle } from './runtime'\n\nexport type RuntimeMaterialSource =\n MaterialRegistry | readonly MaterialDefinition[] | undefined\n\nexport function normalizeMaterialSource(\n source: RuntimeMaterialSource\n): MaterialRegistry | undefined {\n if (!source) return undefined\n if (Array.isArray(source)) return createMaterialRegistry(source)\n return source as MaterialRegistry\n}\n\nexport function renderRuntimeMaterial(\n context: LayoutRenderContext,\n materials: MaterialRegistry | undefined\n): LayoutRenderHandle | undefined {\n const material = materials?.get(context.item.type)\n const renderer =\n material?.renderers?.webcomponent ?? material?.renderers?.html\n if (!material || !renderer) return undefined\n\n const host = context.shell.ownerDocument\n let node: Node | undefined\n if (renderer.tagName) {\n node = host.createElement(renderer.tagName)\n } else if (renderer.render) {\n const rendered = renderer.render({\n item: context.item,\n placement: context.placement,\n breakpointId: context.breakpointId,\n })\n if (rendered instanceof Node) node = rendered\n else if (typeof rendered === 'string') {\n const wrapper = host.createElement('div')\n wrapper.innerHTML = sanitizeMarkup(rendered)\n node = wrapper\n }\n }\n if (!node) return undefined\n\n const applyProps = (item: LayoutNode): void => {\n if (!(node instanceof HTMLElement)) return\n for (const [key, value] of Object.entries(item.props ?? {})) {\n try {\n ;(node as unknown as Record<string, unknown>)[key] = value\n } catch {\n // Some custom elements expose readonly properties. The attribute is a\n // useful fallback for string-like values.\n }\n if (\n value === null ||\n value === undefined ||\n typeof value === 'object' ||\n typeof value === 'function'\n ) {\n if (value !== null && value !== undefined)\n node.dataset[`prop${toDataKey(key)}`] = JSON.stringify(value)\n } else node.setAttribute(key, String(value))\n }\n }\n applyProps(context.item)\n context.shell.replaceChildren(node)\n return {\n update(next) {\n applyProps(next.item)\n },\n destroy() {\n if (node?.parentNode === context.shell) context.shell.removeChild(node)\n },\n }\n}\n\nfunction toDataKey(value: string): string {\n return value.replace(/[^a-zA-Z0-9]/g, '')\n}\n\nfunction sanitizeMarkup(value: string): string {\n return value\n .replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script\\s*>/gi, '')\n .replace(/\\s+on[a-z]+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi, '')\n .replace(/\\s+(?:href|src)\\s*=\\s*(\"|')\\s*javascript:[^\"']*\\1/gi, '')\n}\n"],"mappings":";;;;;;;;;;;AAWO,SAAS,wBACd,QAC8B;CAC9B,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO,uBAAuB,MAAM;CAC/D,OAAO;AACT;AAEO,SAAS,sBACd,SACA,WACgC;CAChC,MAAM,WAAW,WAAW,IAAI,QAAQ,KAAK,IAAI;CACjD,MAAM,WACJ,UAAU,WAAW,gBAAgB,UAAU,WAAW;CAC5D,IAAI,CAAC,YAAY,CAAC,UAAU,OAAO,KAAA;CAEnC,MAAM,OAAO,QAAQ,MAAM;CAC3B,IAAI;CACJ,IAAI,SAAS,SACX,OAAO,KAAK,cAAc,SAAS,OAAO;MAC5C,IAAW,SAAS,QAAQ;EAC1B,MAAM,WAAW,SAAS,OAAO;GAC/B,MAAM,QAAQ;GACd,WAAW,QAAQ;GACnB,cAAc,QAAQ;EACxB,CAAC;EACD,IAAI,oBAAoB,MAAM,OAAO;OAAA,IAC5B,OAAO,aAAa,UAAU;GACrC,MAAM,UAAU,KAAK,cAAc,KAAK;GACxC,QAAQ,YAAY,eAAe,QAAQ;GAC3C,OAAO;EACT;CACF;CACA,IAAI,CAAC,MAAM,OAAO,KAAA;CAElB,MAAM,cAAc,SAA2B;EAC7C,IAAI,EAAE,gBAAgB,cAAc;EACpC,KAAA,MAAW,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,GAAG;GAC3D,IAAI;IACA,KAA4C,OAAO;GACvD,QAAQ,CAGR;GACA,IACE,UAAU,QACV,UAAU,KAAA,KACV,OAAO,UAAU,YACjB,OAAO,UAAU;QAEb,UAAU,QAAQ,UAAU,KAAA,GAC9B,KAAK,QAAQ,OAAO,UAAU,GAAG,OAAO,KAAK,UAAU,KAAK;GAAA,OACzD,KAAK,aAAa,KAAK,OAAO,KAAK,CAAC;EAC7C;CACF;CACA,WAAW,QAAQ,IAAI;CACvB,QAAQ,MAAM,gBAAgB,IAAI;CAClC,OAAO;EACL,OAAO,MAAM;GACX,WAAW,KAAK,IAAI;EACtB;EACA,UAAU;GACR,IAAI,MAAM,eAAe,QAAQ,OAAO,QAAQ,MAAM,YAAY,IAAI;EACxE;CACF;AACF;AAEA,SAAS,UAAU,OAAuB;CACxC,OAAO,MAAM,QAAQ,iBAAiB,EAAE;AAC1C;AAEA,SAAS,eAAe,OAAuB;CAC7C,OAAO,MACJ,QAAQ,0CAA0C,EAAE,CAAA,CACpD,QAAQ,mDAAmD,EAAE,CAAA,CAC7D,QAAQ,uDAAuD,EAAE;AACtE"}
@@ -0,0 +1,35 @@
1
+ import type { BreakpointId, LayoutDocument, LayoutNode, LayoutPlacement, MaterialDefinition, MaterialRegistry } from '@composy/layout-core';
2
+ import { resolveGridStyle, resolveItemStyle } from './styles';
3
+ export interface LayoutRenderContext {
4
+ shell: HTMLElement;
5
+ item: LayoutNode;
6
+ placement: LayoutPlacement;
7
+ breakpointId: BreakpointId;
8
+ }
9
+ export interface LayoutRenderHandle {
10
+ update?(context: LayoutRenderContext): void;
11
+ destroy?(): void;
12
+ }
13
+ export interface LayoutRuntimeOptions {
14
+ container: HTMLElement;
15
+ document?: LayoutDocument;
16
+ /** Lets a host adapter provide an existing light-DOM item shell. */
17
+ getItemShell?: (item: LayoutNode) => HTMLElement | undefined;
18
+ renderItem?: (context: LayoutRenderContext) => LayoutRenderHandle | void;
19
+ /** Material registry used by the framework-neutral default renderer. */
20
+ materials?: MaterialRegistry | readonly MaterialDefinition[];
21
+ observeResize?: boolean;
22
+ onBreakpointChange?: (id: BreakpointId) => void;
23
+ onError?: (error: unknown) => void;
24
+ }
25
+ export interface LayoutRuntime {
26
+ mount(): void;
27
+ update(document: LayoutDocument): void;
28
+ setBreakpoint(id?: BreakpointId): void;
29
+ getBreakpoint(): BreakpointId | undefined;
30
+ getContainer(): HTMLElement;
31
+ destroy(): void;
32
+ }
33
+ export declare function createLayoutRuntime(options: LayoutRuntimeOptions): LayoutRuntime;
34
+ export { resolveGridStyle, resolveItemStyle };
35
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAM7D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,eAAe,CAAA;IAC1B,YAAY,EAAE,YAAY,CAAA;CAC3B;AACD,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;IAC3C,OAAO,CAAC,IAAI,IAAI,CAAA;CACjB;AACD,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,WAAW,CAAA;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,WAAW,GAAG,SAAS,CAAA;IAC5D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,kBAAkB,GAAG,IAAI,CAAA;IACxE,wEAAwE;IACxE,SAAS,CAAC,EAAE,gBAAgB,GAAG,SAAS,kBAAkB,EAAE,CAAA;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAAA;IAC/C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,IAAI,IAAI,CAAA;IACb,MAAM,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IACtC,aAAa,CAAC,EAAE,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;IACtC,aAAa,IAAI,YAAY,GAAG,SAAS,CAAA;IACzC,YAAY,IAAI,WAAW,CAAA;IAC3B,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAwJf;AAED,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAA"}
package/es/runtime.js ADDED
@@ -0,0 +1,156 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ import { resolveGridStyle, resolveItemStyle } from "./styles.js";
11
+ import { normalizeMaterialSource, renderRuntimeMaterial } from "./material-renderer.js";
12
+ import { resolveBreakpoint, validateLayoutDocument } from "@composy/layout-core";
13
+ //#region src/runtime.ts
14
+ function createLayoutRuntime(options) {
15
+ let document = options.document;
16
+ let mounted = false;
17
+ let manualBreakpoint;
18
+ let activeBreakpoint;
19
+ let observer;
20
+ const handles = /* @__PURE__ */ new Map();
21
+ const shells = /* @__PURE__ */ new Map();
22
+ const ownedShells = /* @__PURE__ */ new Set();
23
+ const materials = normalizeMaterialSource(options.materials);
24
+ function render() {
25
+ if (!mounted || !document) return;
26
+ const result = validateLayoutDocument(document);
27
+ if (!result.valid) {
28
+ options.onError?.(new Error(result.issues[0]?.message ?? "Invalid layout document"));
29
+ return;
30
+ }
31
+ const breakpointId = manualBreakpoint ?? activeBreakpoint ?? document.defaultBreakpointId;
32
+ activeBreakpoint = breakpointId;
33
+ const gridStyle = resolveGridStyle(document, breakpointId);
34
+ Object.assign(options.container.style, gridStyle);
35
+ const currentIds = new Set(document.items.map((item) => item.id));
36
+ for (const [id, handle] of handles) {
37
+ if (currentIds.has(id)) continue;
38
+ try {
39
+ handle.destroy?.();
40
+ } finally {
41
+ handles.delete(id);
42
+ const shell = shells.get(id);
43
+ if (shell && ownedShells.has(shell)) shell.remove();
44
+ if (shell) ownedShells.delete(shell);
45
+ shells.delete(id);
46
+ }
47
+ }
48
+ for (const item of document.items) {
49
+ const placement = item.layout[breakpointId];
50
+ let shell = shells.get(item.id);
51
+ if (!shell) {
52
+ shell = options.getItemShell?.(item);
53
+ if (!shell) {
54
+ shell = options.container.ownerDocument.createElement("div");
55
+ shell.className = "ldesign-layout-item";
56
+ shell.dataset.layoutItemId = item.id;
57
+ shell.dataset.layoutType = item.type;
58
+ ownedShells.add(shell);
59
+ options.container.appendChild(shell);
60
+ }
61
+ shells.set(item.id, shell);
62
+ }
63
+ Object.assign(shell.style, resolveItemStyle(item, breakpointId));
64
+ if (!placement) continue;
65
+ const context = {
66
+ shell,
67
+ item,
68
+ placement,
69
+ breakpointId
70
+ };
71
+ try {
72
+ let handle = handles.get(item.id);
73
+ if (!handle) {
74
+ handle = options.renderItem ? options.renderItem(context) ?? {} : renderRuntimeMaterial(context, materials) ?? {};
75
+ handles.set(item.id, handle);
76
+ } else handle.update?.(context);
77
+ } catch (error) {
78
+ options.onError?.(error);
79
+ }
80
+ }
81
+ }
82
+ function updateBreakpoint(width) {
83
+ if (!document || manualBreakpoint) return;
84
+ const next = resolveBreakpoint(document, width).id;
85
+ if (next !== activeBreakpoint) {
86
+ const previous = activeBreakpoint;
87
+ activeBreakpoint = next;
88
+ options.onBreakpointChange?.(next);
89
+ if (previous !== next) render();
90
+ }
91
+ }
92
+ function updateBreakpointFromContainer() {
93
+ if (!document || manualBreakpoint) return;
94
+ const width = options.container.getBoundingClientRect().width || options.container.clientWidth;
95
+ if (width > 0) updateBreakpoint(width);
96
+ else if (activeBreakpoint !== document.defaultBreakpointId) {
97
+ const previous = activeBreakpoint;
98
+ activeBreakpoint = document.defaultBreakpointId;
99
+ if (previous) options.onBreakpointChange?.(activeBreakpoint);
100
+ }
101
+ }
102
+ return {
103
+ mount() {
104
+ if (mounted) return;
105
+ mounted = true;
106
+ if (options.observeResize !== false && typeof ResizeObserver !== "undefined") {
107
+ observer = new ResizeObserver((entries) => {
108
+ const width = entries[0]?.contentRect.width ?? options.container.clientWidth;
109
+ if (width > 0) updateBreakpoint(width);
110
+ else updateBreakpointFromContainer();
111
+ });
112
+ observer.observe(options.container);
113
+ }
114
+ updateBreakpointFromContainer();
115
+ render();
116
+ },
117
+ update(next) {
118
+ document = next;
119
+ render();
120
+ },
121
+ setBreakpoint(id) {
122
+ manualBreakpoint = id;
123
+ if (id) activeBreakpoint = id;
124
+ else updateBreakpointFromContainer();
125
+ render();
126
+ },
127
+ getBreakpoint() {
128
+ return activeBreakpoint;
129
+ },
130
+ getContainer() {
131
+ return options.container;
132
+ },
133
+ destroy() {
134
+ if (!mounted) return;
135
+ observer?.disconnect();
136
+ observer = void 0;
137
+ for (const handle of handles.values()) handle.destroy?.();
138
+ handles.clear();
139
+ for (const shell of shells.values()) if (ownedShells.has(shell)) shell.remove();
140
+ shells.clear();
141
+ ownedShells.clear();
142
+ options.container.style.removeProperty("display");
143
+ options.container.style.removeProperty("grid-template-columns");
144
+ options.container.style.removeProperty("grid-auto-rows");
145
+ options.container.style.removeProperty("gap");
146
+ options.container.style.removeProperty("direction");
147
+ options.container.style.removeProperty("min-height");
148
+ options.container.style.removeProperty("max-height");
149
+ mounted = false;
150
+ }
151
+ };
152
+ }
153
+ //#endregion
154
+ export { createLayoutRuntime, resolveGridStyle, resolveItemStyle };
155
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
156
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import type {\n BreakpointId,\n LayoutDocument,\n LayoutNode,\n LayoutPlacement,\n MaterialDefinition,\n MaterialRegistry,\n} from '@composy/layout-core'\nimport { resolveBreakpoint, validateLayoutDocument } from '@composy/layout-core'\nimport { resolveGridStyle, resolveItemStyle } from './styles'\nimport {\n normalizeMaterialSource,\n renderRuntimeMaterial,\n} from './material-renderer'\n\nexport interface LayoutRenderContext {\n shell: HTMLElement\n item: LayoutNode\n placement: LayoutPlacement\n breakpointId: BreakpointId\n}\nexport interface LayoutRenderHandle {\n update?(context: LayoutRenderContext): void\n destroy?(): void\n}\nexport interface LayoutRuntimeOptions {\n container: HTMLElement\n document?: LayoutDocument\n /** Lets a host adapter provide an existing light-DOM item shell. */\n getItemShell?: (item: LayoutNode) => HTMLElement | undefined\n renderItem?: (context: LayoutRenderContext) => LayoutRenderHandle | void\n /** Material registry used by the framework-neutral default renderer. */\n materials?: MaterialRegistry | readonly MaterialDefinition[]\n observeResize?: boolean\n onBreakpointChange?: (id: BreakpointId) => void\n onError?: (error: unknown) => void\n}\n\nexport interface LayoutRuntime {\n mount(): void\n update(document: LayoutDocument): void\n setBreakpoint(id?: BreakpointId): void\n getBreakpoint(): BreakpointId | undefined\n getContainer(): HTMLElement\n destroy(): void\n}\n\nexport function createLayoutRuntime(\n options: LayoutRuntimeOptions\n): LayoutRuntime {\n let document = options.document\n let mounted = false\n let manualBreakpoint: BreakpointId | undefined\n let activeBreakpoint: BreakpointId | undefined\n let observer: ResizeObserver | undefined\n const handles = new Map<string, LayoutRenderHandle>()\n const shells = new Map<string, HTMLElement>()\n const ownedShells = new Set<HTMLElement>()\n const materials = normalizeMaterialSource(options.materials)\n\n function render(): void {\n if (!mounted || !document) return\n const result = validateLayoutDocument(document)\n if (!result.valid) {\n options.onError?.(\n new Error(result.issues[0]?.message ?? 'Invalid layout document')\n )\n return\n }\n const breakpointId =\n manualBreakpoint ?? activeBreakpoint ?? document.defaultBreakpointId\n activeBreakpoint = breakpointId\n const gridStyle = resolveGridStyle(document, breakpointId)\n Object.assign(options.container.style, gridStyle)\n const currentIds = new Set(document.items.map(item => item.id))\n for (const [id, handle] of handles) {\n if (currentIds.has(id)) continue\n try {\n handle.destroy?.()\n } finally {\n handles.delete(id)\n const shell = shells.get(id)\n if (shell && ownedShells.has(shell)) shell.remove()\n if (shell) ownedShells.delete(shell)\n shells.delete(id)\n }\n }\n for (const item of document.items) {\n const placement = item.layout[breakpointId]\n let shell = shells.get(item.id)\n if (!shell) {\n shell = options.getItemShell?.(item)\n if (!shell) {\n shell = options.container.ownerDocument.createElement('div')\n shell.className = 'ldesign-layout-item'\n shell.dataset.layoutItemId = item.id\n shell.dataset.layoutType = item.type\n ownedShells.add(shell)\n options.container.appendChild(shell)\n }\n shells.set(item.id, shell)\n }\n Object.assign(shell.style, resolveItemStyle(item, breakpointId))\n if (!placement) continue\n const context = { shell, item, placement, breakpointId }\n try {\n let handle = handles.get(item.id)\n if (!handle) {\n handle = options.renderItem\n ? (options.renderItem(context) ?? {})\n : (renderRuntimeMaterial(context, materials) ?? {})\n handles.set(item.id, handle)\n } else handle.update?.(context)\n } catch (error) {\n options.onError?.(error)\n }\n }\n }\n\n function updateBreakpoint(width: number): void {\n if (!document || manualBreakpoint) return\n const next = resolveBreakpoint(document, width).id\n if (next !== activeBreakpoint) {\n const previous = activeBreakpoint\n activeBreakpoint = next\n options.onBreakpointChange?.(next)\n if (previous !== next) render()\n }\n }\n\n function updateBreakpointFromContainer(): void {\n if (!document || manualBreakpoint) return\n const width =\n options.container.getBoundingClientRect().width ||\n options.container.clientWidth\n // A hidden or detached container has no measurable width. Keep the\n // document default until the first real resize instead of selecting the\n // smallest breakpoint for width 0.\n if (width > 0) updateBreakpoint(width)\n else if (activeBreakpoint !== document.defaultBreakpointId) {\n const previous = activeBreakpoint\n activeBreakpoint = document.defaultBreakpointId\n if (previous) options.onBreakpointChange?.(activeBreakpoint)\n }\n }\n\n return {\n mount() {\n if (mounted) return\n mounted = true\n if (\n options.observeResize !== false &&\n typeof ResizeObserver !== 'undefined'\n ) {\n observer = new ResizeObserver(entries => {\n const width =\n entries[0]?.contentRect.width ?? options.container.clientWidth\n if (width > 0) updateBreakpoint(width)\n else updateBreakpointFromContainer()\n })\n observer.observe(options.container)\n }\n updateBreakpointFromContainer()\n render()\n },\n update(next) {\n document = next\n render()\n },\n setBreakpoint(id) {\n manualBreakpoint = id\n if (id) activeBreakpoint = id\n else updateBreakpointFromContainer()\n render()\n },\n getBreakpoint() {\n return activeBreakpoint\n },\n getContainer() {\n return options.container\n },\n destroy() {\n if (!mounted) return\n observer?.disconnect()\n observer = undefined\n for (const handle of handles.values()) handle.destroy?.()\n handles.clear()\n for (const shell of shells.values())\n if (ownedShells.has(shell)) shell.remove()\n shells.clear()\n ownedShells.clear()\n options.container.style.removeProperty('display')\n options.container.style.removeProperty('grid-template-columns')\n options.container.style.removeProperty('grid-auto-rows')\n options.container.style.removeProperty('gap')\n options.container.style.removeProperty('direction')\n options.container.style.removeProperty('min-height')\n options.container.style.removeProperty('max-height')\n mounted = false\n },\n }\n}\n\nexport { resolveGridStyle, resolveItemStyle }\n"],"mappings":"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"}
package/es/styles.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { BreakpointId, LayoutDocument, LayoutNode } from '@composy/layout-core';
2
+ export interface GridStyleResult {
3
+ display: 'grid';
4
+ gridTemplateColumns: string;
5
+ gridAutoRows: string;
6
+ gap: string;
7
+ direction: 'ltr' | 'rtl';
8
+ minHeight?: string;
9
+ maxHeight?: string;
10
+ }
11
+ export declare function resolveGridStyle(document: LayoutDocument, breakpointId: BreakpointId): GridStyleResult;
12
+ export declare function resolveItemStyle(node: LayoutNode, breakpointId: BreakpointId): Record<string, string>;
13
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,UAAU,EAEX,MAAM,sBAAsB,CAAA;AAG7B,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,mBAAmB,EAAE,MAAM,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,KAAK,GAAG,KAAK,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,cAAc,EACxB,YAAY,EAAE,YAAY,GACzB,eAAe,CAiBjB;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,YAAY,EAAE,YAAY,GACzB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB"}
package/es/styles.js ADDED
@@ -0,0 +1,39 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ import { getBreakpoint, getEffectiveGrid } from "@composy/layout-core";
11
+ //#region src/styles.ts
12
+ function resolveGridStyle(document, breakpointId) {
13
+ const grid = getEffectiveGrid(document, breakpointId);
14
+ const breakpoint = getBreakpoint(document, breakpointId);
15
+ const style = {
16
+ display: "grid",
17
+ gridTemplateColumns: `repeat(${grid.columns}, minmax(0, 1fr))`,
18
+ gridAutoRows: `${grid.rowSize}px`,
19
+ gap: `${grid.gap}px`,
20
+ direction: grid.direction
21
+ };
22
+ if (document.grid.minRows !== void 0) style.minHeight = `${document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap}px`;
23
+ if (document.grid.maxRows !== void 0) style.maxHeight = `${document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap}px`;
24
+ if (breakpoint?.canvasWidth) style.gridTemplateColumns = `repeat(${grid.columns}, minmax(0, 1fr))`;
25
+ return style;
26
+ }
27
+ function resolveItemStyle(node, breakpointId) {
28
+ const placement = node.layout[breakpointId];
29
+ if (!placement) return { display: "none" };
30
+ return {
31
+ gridColumn: `${placement.x + 1} / span ${placement.w}`,
32
+ gridRow: `${placement.y + 1} / span ${placement.h}`,
33
+ display: placement.hidden ? "none" : ""
34
+ };
35
+ }
36
+ //#endregion
37
+ export { resolveGridStyle, resolveItemStyle };
38
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
39
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","names":[],"sources":["../src/styles.ts"],"sourcesContent":["import type {\n BreakpointId,\n LayoutDocument,\n LayoutNode,\n LayoutPlacement,\n} from '@composy/layout-core'\nimport { getEffectiveGrid, getBreakpoint } from '@composy/layout-core'\n\nexport interface GridStyleResult {\n display: 'grid'\n gridTemplateColumns: string\n gridAutoRows: string\n gap: string\n direction: 'ltr' | 'rtl'\n minHeight?: string\n maxHeight?: string\n}\n\nexport function resolveGridStyle(\n document: LayoutDocument,\n breakpointId: BreakpointId\n): GridStyleResult {\n const grid = getEffectiveGrid(document, breakpointId)\n const breakpoint = getBreakpoint(document, breakpointId)\n const style: GridStyleResult = {\n display: 'grid',\n gridTemplateColumns: `repeat(${grid.columns}, minmax(0, 1fr))`,\n gridAutoRows: `${grid.rowSize}px`,\n gap: `${grid.gap}px`,\n direction: grid.direction,\n }\n if (document.grid.minRows !== undefined)\n style.minHeight = `${document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap}px`\n if (document.grid.maxRows !== undefined)\n style.maxHeight = `${document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap}px`\n if (breakpoint?.canvasWidth)\n style.gridTemplateColumns = `repeat(${grid.columns}, minmax(0, 1fr))`\n return style\n}\n\nexport function resolveItemStyle(\n node: LayoutNode,\n breakpointId: BreakpointId\n): Record<string, string> {\n const placement = node.layout[breakpointId]\n if (!placement) return { display: 'none' }\n return {\n gridColumn: `${placement.x + 1} / span ${placement.w}`,\n gridRow: `${placement.y + 1} / span ${placement.h}`,\n display: placement.hidden ? 'none' : '',\n }\n}\n"],"mappings":";;;;;;;;;;;AAkBO,SAAS,iBACd,UACA,cACiB;CACjB,MAAM,OAAO,iBAAiB,UAAU,YAAY;CACpD,MAAM,aAAa,cAAc,UAAU,YAAY;CACvD,MAAM,QAAyB;EAC7B,SAAS;EACT,qBAAqB,UAAU,KAAK,QAAO;EAC3C,cAAc,GAAG,KAAK,QAAO;EAC7B,KAAK,GAAG,KAAK,IAAG;EAChB,WAAW,KAAK;CAClB;CACA,IAAI,SAAS,KAAK,YAAY,KAAA,GAC5B,MAAM,YAAY,GAAG,SAAS,KAAK,WAAW,KAAK,UAAU,KAAK,OAAO,KAAK,IAAG;CACnF,IAAI,SAAS,KAAK,YAAY,KAAA,GAC5B,MAAM,YAAY,GAAG,SAAS,KAAK,WAAW,KAAK,UAAU,KAAK,OAAO,KAAK,IAAG;CACnF,IAAI,YAAY,aACd,MAAM,sBAAsB,UAAU,KAAK,QAAO;CACpD,OAAO;AACT;AAEO,SAAS,iBACd,MACA,cACwB;CACxB,MAAM,YAAY,KAAK,OAAO;CAC9B,IAAI,CAAC,WAAW,OAAO,EAAE,SAAS,OAAO;CACzC,OAAO;EACL,YAAY,GAAG,UAAU,IAAI,EAAC,UAAW,UAAU;EACnD,SAAS,GAAG,UAAU,IAAI,EAAC,UAAW,UAAU;EAChD,SAAS,UAAU,SAAS,SAAS;CACvC;AACF"}
package/lib/index.cjs ADDED
@@ -0,0 +1,17 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ "use strict";
11
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
12
+ const require_styles = require("./styles.cjs");
13
+ const require_runtime = require("./runtime.cjs");
14
+ exports.createLayoutRuntime = require_runtime.createLayoutRuntime;
15
+ exports.resolveGridStyle = require_styles.resolveGridStyle;
16
+ exports.resolveItemStyle = require_styles.resolveItemStyle;
17
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './styles';
2
+ export * from './runtime';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,72 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ "use strict";
11
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
12
+ let _composy_layout_core = require("@composy/layout-core");
13
+ //#region src/material-renderer.ts
14
+ function normalizeMaterialSource(source) {
15
+ if (!source) return void 0;
16
+ if (Array.isArray(source)) return (0, _composy_layout_core.createMaterialRegistry)(source);
17
+ return source;
18
+ }
19
+ function renderRuntimeMaterial(context, materials) {
20
+ const material = materials?.get(context.item.type);
21
+ const renderer = material?.renderers?.webcomponent ?? material?.renderers?.html;
22
+ if (!material || !renderer) return void 0;
23
+ const host = context.shell.ownerDocument;
24
+ let node;
25
+ if (renderer.tagName) node = host.createElement(renderer.tagName);
26
+ else if (renderer.render) {
27
+ const rendered = renderer.render({
28
+ item: context.item,
29
+ placement: context.placement,
30
+ breakpointId: context.breakpointId
31
+ });
32
+ if (rendered instanceof Node) node = rendered;
33
+ else if (typeof rendered === "string") {
34
+ const wrapper = host.createElement("div");
35
+ wrapper.innerHTML = sanitizeMarkup(rendered);
36
+ node = wrapper;
37
+ }
38
+ }
39
+ if (!node) return void 0;
40
+ const applyProps = (item) => {
41
+ if (!(node instanceof HTMLElement)) return;
42
+ for (const [key, value] of Object.entries(item.props ?? {})) {
43
+ try {
44
+ node[key] = value;
45
+ } catch {}
46
+ if (value === null || value === void 0 || typeof value === "object" || typeof value === "function") {
47
+ if (value !== null && value !== void 0) node.dataset[`prop${toDataKey(key)}`] = JSON.stringify(value);
48
+ } else node.setAttribute(key, String(value));
49
+ }
50
+ };
51
+ applyProps(context.item);
52
+ context.shell.replaceChildren(node);
53
+ return {
54
+ update(next) {
55
+ applyProps(next.item);
56
+ },
57
+ destroy() {
58
+ if (node?.parentNode === context.shell) context.shell.removeChild(node);
59
+ }
60
+ };
61
+ }
62
+ function toDataKey(value) {
63
+ return value.replace(/[^a-zA-Z0-9]/g, "");
64
+ }
65
+ function sanitizeMarkup(value) {
66
+ return value.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi, "").replace(/\s+on[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "").replace(/\s+(?:href|src)\s*=\s*("|')\s*javascript:[^"']*\1/gi, "");
67
+ }
68
+ //#endregion
69
+ exports.normalizeMaterialSource = normalizeMaterialSource;
70
+ exports.renderRuntimeMaterial = renderRuntimeMaterial;
71
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
72
+ //# sourceMappingURL=material-renderer.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"material-renderer.cjs","names":[],"sources":["../src/material-renderer.ts"],"sourcesContent":["import {\n createMaterialRegistry,\n type LayoutNode,\n type MaterialDefinition,\n type MaterialRegistry,\n} from '@composy/layout-core'\nimport type { LayoutRenderContext, LayoutRenderHandle } from './runtime'\n\nexport type RuntimeMaterialSource =\n MaterialRegistry | readonly MaterialDefinition[] | undefined\n\nexport function normalizeMaterialSource(\n source: RuntimeMaterialSource\n): MaterialRegistry | undefined {\n if (!source) return undefined\n if (Array.isArray(source)) return createMaterialRegistry(source)\n return source as MaterialRegistry\n}\n\nexport function renderRuntimeMaterial(\n context: LayoutRenderContext,\n materials: MaterialRegistry | undefined\n): LayoutRenderHandle | undefined {\n const material = materials?.get(context.item.type)\n const renderer =\n material?.renderers?.webcomponent ?? material?.renderers?.html\n if (!material || !renderer) return undefined\n\n const host = context.shell.ownerDocument\n let node: Node | undefined\n if (renderer.tagName) {\n node = host.createElement(renderer.tagName)\n } else if (renderer.render) {\n const rendered = renderer.render({\n item: context.item,\n placement: context.placement,\n breakpointId: context.breakpointId,\n })\n if (rendered instanceof Node) node = rendered\n else if (typeof rendered === 'string') {\n const wrapper = host.createElement('div')\n wrapper.innerHTML = sanitizeMarkup(rendered)\n node = wrapper\n }\n }\n if (!node) return undefined\n\n const applyProps = (item: LayoutNode): void => {\n if (!(node instanceof HTMLElement)) return\n for (const [key, value] of Object.entries(item.props ?? {})) {\n try {\n ;(node as unknown as Record<string, unknown>)[key] = value\n } catch {\n // Some custom elements expose readonly properties. The attribute is a\n // useful fallback for string-like values.\n }\n if (\n value === null ||\n value === undefined ||\n typeof value === 'object' ||\n typeof value === 'function'\n ) {\n if (value !== null && value !== undefined)\n node.dataset[`prop${toDataKey(key)}`] = JSON.stringify(value)\n } else node.setAttribute(key, String(value))\n }\n }\n applyProps(context.item)\n context.shell.replaceChildren(node)\n return {\n update(next) {\n applyProps(next.item)\n },\n destroy() {\n if (node?.parentNode === context.shell) context.shell.removeChild(node)\n },\n }\n}\n\nfunction toDataKey(value: string): string {\n return value.replace(/[^a-zA-Z0-9]/g, '')\n}\n\nfunction sanitizeMarkup(value: string): string {\n return value\n .replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script\\s*>/gi, '')\n .replace(/\\s+on[a-z]+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi, '')\n .replace(/\\s+(?:href|src)\\s*=\\s*(\"|')\\s*javascript:[^\"']*\\1/gi, '')\n}\n"],"mappings":";;;;;;;;;;;;;AAWO,SAAS,wBACd,QAC8B;CAC9B,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,IAAI,MAAM,QAAQ,MAAM,GAAG,QAAA,GAAA,qBAAA,uBAAA,CAA8B,MAAM;CAC/D,OAAO;AACT;AAEO,SAAS,sBACd,SACA,WACgC;CAChC,MAAM,WAAW,WAAW,IAAI,QAAQ,KAAK,IAAI;CACjD,MAAM,WACJ,UAAU,WAAW,gBAAgB,UAAU,WAAW;CAC5D,IAAI,CAAC,YAAY,CAAC,UAAU,OAAO,KAAA;CAEnC,MAAM,OAAO,QAAQ,MAAM;CAC3B,IAAI;CACJ,IAAI,SAAS,SACX,OAAO,KAAK,cAAc,SAAS,OAAO;MAC5C,IAAW,SAAS,QAAQ;EAC1B,MAAM,WAAW,SAAS,OAAO;GAC/B,MAAM,QAAQ;GACd,WAAW,QAAQ;GACnB,cAAc,QAAQ;EACxB,CAAC;EACD,IAAI,oBAAoB,MAAM,OAAO;OAAA,IAC5B,OAAO,aAAa,UAAU;GACrC,MAAM,UAAU,KAAK,cAAc,KAAK;GACxC,QAAQ,YAAY,eAAe,QAAQ;GAC3C,OAAO;EACT;CACF;CACA,IAAI,CAAC,MAAM,OAAO,KAAA;CAElB,MAAM,cAAc,SAA2B;EAC7C,IAAI,EAAE,gBAAgB,cAAc;EACpC,KAAA,MAAW,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,GAAG;GAC3D,IAAI;IACA,KAA4C,OAAO;GACvD,QAAQ,CAGR;GACA,IACE,UAAU,QACV,UAAU,KAAA,KACV,OAAO,UAAU,YACjB,OAAO,UAAU;QAEb,UAAU,QAAQ,UAAU,KAAA,GAC9B,KAAK,QAAQ,OAAO,UAAU,GAAG,OAAO,KAAK,UAAU,KAAK;GAAA,OACzD,KAAK,aAAa,KAAK,OAAO,KAAK,CAAC;EAC7C;CACF;CACA,WAAW,QAAQ,IAAI;CACvB,QAAQ,MAAM,gBAAgB,IAAI;CAClC,OAAO;EACL,OAAO,MAAM;GACX,WAAW,KAAK,IAAI;EACtB;EACA,UAAU;GACR,IAAI,MAAM,eAAe,QAAQ,OAAO,QAAQ,MAAM,YAAY,IAAI;EACxE;CACF;AACF;AAEA,SAAS,UAAU,OAAuB;CACxC,OAAO,MAAM,QAAQ,iBAAiB,EAAE;AAC1C;AAEA,SAAS,eAAe,OAAuB;CAC7C,OAAO,MACJ,QAAQ,0CAA0C,EAAE,CAAA,CACpD,QAAQ,mDAAmD,EAAE,CAAA,CAC7D,QAAQ,uDAAuD,EAAE;AACtE"}
@@ -0,0 +1,6 @@
1
+ import { type MaterialDefinition, type MaterialRegistry } from '@composy/layout-core';
2
+ import type { LayoutRenderContext, LayoutRenderHandle } from './runtime';
3
+ export type RuntimeMaterialSource = MaterialRegistry | readonly MaterialDefinition[] | undefined;
4
+ export declare function normalizeMaterialSource(source: RuntimeMaterialSource): MaterialRegistry | undefined;
5
+ export declare function renderRuntimeMaterial(context: LayoutRenderContext, materials: MaterialRegistry | undefined): LayoutRenderHandle | undefined;
6
+ //# sourceMappingURL=material-renderer.d.ts.map
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,160 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ "use strict";
11
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
12
+ const require_styles = require("./styles.cjs");
13
+ const require_material_renderer = require("./material-renderer.cjs");
14
+ let _composy_layout_core = require("@composy/layout-core");
15
+ //#region src/runtime.ts
16
+ function createLayoutRuntime(options) {
17
+ let document = options.document;
18
+ let mounted = false;
19
+ let manualBreakpoint;
20
+ let activeBreakpoint;
21
+ let observer;
22
+ const handles = /* @__PURE__ */ new Map();
23
+ const shells = /* @__PURE__ */ new Map();
24
+ const ownedShells = /* @__PURE__ */ new Set();
25
+ const materials = require_material_renderer.normalizeMaterialSource(options.materials);
26
+ function render() {
27
+ if (!mounted || !document) return;
28
+ const result = (0, _composy_layout_core.validateLayoutDocument)(document);
29
+ if (!result.valid) {
30
+ options.onError?.(new Error(result.issues[0]?.message ?? "Invalid layout document"));
31
+ return;
32
+ }
33
+ const breakpointId = manualBreakpoint ?? activeBreakpoint ?? document.defaultBreakpointId;
34
+ activeBreakpoint = breakpointId;
35
+ const gridStyle = require_styles.resolveGridStyle(document, breakpointId);
36
+ Object.assign(options.container.style, gridStyle);
37
+ const currentIds = new Set(document.items.map((item) => item.id));
38
+ for (const [id, handle] of handles) {
39
+ if (currentIds.has(id)) continue;
40
+ try {
41
+ handle.destroy?.();
42
+ } finally {
43
+ handles.delete(id);
44
+ const shell = shells.get(id);
45
+ if (shell && ownedShells.has(shell)) shell.remove();
46
+ if (shell) ownedShells.delete(shell);
47
+ shells.delete(id);
48
+ }
49
+ }
50
+ for (const item of document.items) {
51
+ const placement = item.layout[breakpointId];
52
+ let shell = shells.get(item.id);
53
+ if (!shell) {
54
+ shell = options.getItemShell?.(item);
55
+ if (!shell) {
56
+ shell = options.container.ownerDocument.createElement("div");
57
+ shell.className = "ldesign-layout-item";
58
+ shell.dataset.layoutItemId = item.id;
59
+ shell.dataset.layoutType = item.type;
60
+ ownedShells.add(shell);
61
+ options.container.appendChild(shell);
62
+ }
63
+ shells.set(item.id, shell);
64
+ }
65
+ Object.assign(shell.style, require_styles.resolveItemStyle(item, breakpointId));
66
+ if (!placement) continue;
67
+ const context = {
68
+ shell,
69
+ item,
70
+ placement,
71
+ breakpointId
72
+ };
73
+ try {
74
+ let handle = handles.get(item.id);
75
+ if (!handle) {
76
+ handle = options.renderItem ? options.renderItem(context) ?? {} : require_material_renderer.renderRuntimeMaterial(context, materials) ?? {};
77
+ handles.set(item.id, handle);
78
+ } else handle.update?.(context);
79
+ } catch (error) {
80
+ options.onError?.(error);
81
+ }
82
+ }
83
+ }
84
+ function updateBreakpoint(width) {
85
+ if (!document || manualBreakpoint) return;
86
+ const next = (0, _composy_layout_core.resolveBreakpoint)(document, width).id;
87
+ if (next !== activeBreakpoint) {
88
+ const previous = activeBreakpoint;
89
+ activeBreakpoint = next;
90
+ options.onBreakpointChange?.(next);
91
+ if (previous !== next) render();
92
+ }
93
+ }
94
+ function updateBreakpointFromContainer() {
95
+ if (!document || manualBreakpoint) return;
96
+ const width = options.container.getBoundingClientRect().width || options.container.clientWidth;
97
+ if (width > 0) updateBreakpoint(width);
98
+ else if (activeBreakpoint !== document.defaultBreakpointId) {
99
+ const previous = activeBreakpoint;
100
+ activeBreakpoint = document.defaultBreakpointId;
101
+ if (previous) options.onBreakpointChange?.(activeBreakpoint);
102
+ }
103
+ }
104
+ return {
105
+ mount() {
106
+ if (mounted) return;
107
+ mounted = true;
108
+ if (options.observeResize !== false && typeof ResizeObserver !== "undefined") {
109
+ observer = new ResizeObserver((entries) => {
110
+ const width = entries[0]?.contentRect.width ?? options.container.clientWidth;
111
+ if (width > 0) updateBreakpoint(width);
112
+ else updateBreakpointFromContainer();
113
+ });
114
+ observer.observe(options.container);
115
+ }
116
+ updateBreakpointFromContainer();
117
+ render();
118
+ },
119
+ update(next) {
120
+ document = next;
121
+ render();
122
+ },
123
+ setBreakpoint(id) {
124
+ manualBreakpoint = id;
125
+ if (id) activeBreakpoint = id;
126
+ else updateBreakpointFromContainer();
127
+ render();
128
+ },
129
+ getBreakpoint() {
130
+ return activeBreakpoint;
131
+ },
132
+ getContainer() {
133
+ return options.container;
134
+ },
135
+ destroy() {
136
+ if (!mounted) return;
137
+ observer?.disconnect();
138
+ observer = void 0;
139
+ for (const handle of handles.values()) handle.destroy?.();
140
+ handles.clear();
141
+ for (const shell of shells.values()) if (ownedShells.has(shell)) shell.remove();
142
+ shells.clear();
143
+ ownedShells.clear();
144
+ options.container.style.removeProperty("display");
145
+ options.container.style.removeProperty("grid-template-columns");
146
+ options.container.style.removeProperty("grid-auto-rows");
147
+ options.container.style.removeProperty("gap");
148
+ options.container.style.removeProperty("direction");
149
+ options.container.style.removeProperty("min-height");
150
+ options.container.style.removeProperty("max-height");
151
+ mounted = false;
152
+ }
153
+ };
154
+ }
155
+ //#endregion
156
+ exports.createLayoutRuntime = createLayoutRuntime;
157
+ exports.resolveGridStyle = require_styles.resolveGridStyle;
158
+ exports.resolveItemStyle = require_styles.resolveItemStyle;
159
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
160
+ //# sourceMappingURL=runtime.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.cjs","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import type {\n BreakpointId,\n LayoutDocument,\n LayoutNode,\n LayoutPlacement,\n MaterialDefinition,\n MaterialRegistry,\n} from '@composy/layout-core'\nimport { resolveBreakpoint, validateLayoutDocument } from '@composy/layout-core'\nimport { resolveGridStyle, resolveItemStyle } from './styles'\nimport {\n normalizeMaterialSource,\n renderRuntimeMaterial,\n} from './material-renderer'\n\nexport interface LayoutRenderContext {\n shell: HTMLElement\n item: LayoutNode\n placement: LayoutPlacement\n breakpointId: BreakpointId\n}\nexport interface LayoutRenderHandle {\n update?(context: LayoutRenderContext): void\n destroy?(): void\n}\nexport interface LayoutRuntimeOptions {\n container: HTMLElement\n document?: LayoutDocument\n /** Lets a host adapter provide an existing light-DOM item shell. */\n getItemShell?: (item: LayoutNode) => HTMLElement | undefined\n renderItem?: (context: LayoutRenderContext) => LayoutRenderHandle | void\n /** Material registry used by the framework-neutral default renderer. */\n materials?: MaterialRegistry | readonly MaterialDefinition[]\n observeResize?: boolean\n onBreakpointChange?: (id: BreakpointId) => void\n onError?: (error: unknown) => void\n}\n\nexport interface LayoutRuntime {\n mount(): void\n update(document: LayoutDocument): void\n setBreakpoint(id?: BreakpointId): void\n getBreakpoint(): BreakpointId | undefined\n getContainer(): HTMLElement\n destroy(): void\n}\n\nexport function createLayoutRuntime(\n options: LayoutRuntimeOptions\n): LayoutRuntime {\n let document = options.document\n let mounted = false\n let manualBreakpoint: BreakpointId | undefined\n let activeBreakpoint: BreakpointId | undefined\n let observer: ResizeObserver | undefined\n const handles = new Map<string, LayoutRenderHandle>()\n const shells = new Map<string, HTMLElement>()\n const ownedShells = new Set<HTMLElement>()\n const materials = normalizeMaterialSource(options.materials)\n\n function render(): void {\n if (!mounted || !document) return\n const result = validateLayoutDocument(document)\n if (!result.valid) {\n options.onError?.(\n new Error(result.issues[0]?.message ?? 'Invalid layout document')\n )\n return\n }\n const breakpointId =\n manualBreakpoint ?? activeBreakpoint ?? document.defaultBreakpointId\n activeBreakpoint = breakpointId\n const gridStyle = resolveGridStyle(document, breakpointId)\n Object.assign(options.container.style, gridStyle)\n const currentIds = new Set(document.items.map(item => item.id))\n for (const [id, handle] of handles) {\n if (currentIds.has(id)) continue\n try {\n handle.destroy?.()\n } finally {\n handles.delete(id)\n const shell = shells.get(id)\n if (shell && ownedShells.has(shell)) shell.remove()\n if (shell) ownedShells.delete(shell)\n shells.delete(id)\n }\n }\n for (const item of document.items) {\n const placement = item.layout[breakpointId]\n let shell = shells.get(item.id)\n if (!shell) {\n shell = options.getItemShell?.(item)\n if (!shell) {\n shell = options.container.ownerDocument.createElement('div')\n shell.className = 'ldesign-layout-item'\n shell.dataset.layoutItemId = item.id\n shell.dataset.layoutType = item.type\n ownedShells.add(shell)\n options.container.appendChild(shell)\n }\n shells.set(item.id, shell)\n }\n Object.assign(shell.style, resolveItemStyle(item, breakpointId))\n if (!placement) continue\n const context = { shell, item, placement, breakpointId }\n try {\n let handle = handles.get(item.id)\n if (!handle) {\n handle = options.renderItem\n ? (options.renderItem(context) ?? {})\n : (renderRuntimeMaterial(context, materials) ?? {})\n handles.set(item.id, handle)\n } else handle.update?.(context)\n } catch (error) {\n options.onError?.(error)\n }\n }\n }\n\n function updateBreakpoint(width: number): void {\n if (!document || manualBreakpoint) return\n const next = resolveBreakpoint(document, width).id\n if (next !== activeBreakpoint) {\n const previous = activeBreakpoint\n activeBreakpoint = next\n options.onBreakpointChange?.(next)\n if (previous !== next) render()\n }\n }\n\n function updateBreakpointFromContainer(): void {\n if (!document || manualBreakpoint) return\n const width =\n options.container.getBoundingClientRect().width ||\n options.container.clientWidth\n // A hidden or detached container has no measurable width. Keep the\n // document default until the first real resize instead of selecting the\n // smallest breakpoint for width 0.\n if (width > 0) updateBreakpoint(width)\n else if (activeBreakpoint !== document.defaultBreakpointId) {\n const previous = activeBreakpoint\n activeBreakpoint = document.defaultBreakpointId\n if (previous) options.onBreakpointChange?.(activeBreakpoint)\n }\n }\n\n return {\n mount() {\n if (mounted) return\n mounted = true\n if (\n options.observeResize !== false &&\n typeof ResizeObserver !== 'undefined'\n ) {\n observer = new ResizeObserver(entries => {\n const width =\n entries[0]?.contentRect.width ?? options.container.clientWidth\n if (width > 0) updateBreakpoint(width)\n else updateBreakpointFromContainer()\n })\n observer.observe(options.container)\n }\n updateBreakpointFromContainer()\n render()\n },\n update(next) {\n document = next\n render()\n },\n setBreakpoint(id) {\n manualBreakpoint = id\n if (id) activeBreakpoint = id\n else updateBreakpointFromContainer()\n render()\n },\n getBreakpoint() {\n return activeBreakpoint\n },\n getContainer() {\n return options.container\n },\n destroy() {\n if (!mounted) return\n observer?.disconnect()\n observer = undefined\n for (const handle of handles.values()) handle.destroy?.()\n handles.clear()\n for (const shell of shells.values())\n if (ownedShells.has(shell)) shell.remove()\n shells.clear()\n ownedShells.clear()\n options.container.style.removeProperty('display')\n options.container.style.removeProperty('grid-template-columns')\n options.container.style.removeProperty('grid-auto-rows')\n options.container.style.removeProperty('gap')\n options.container.style.removeProperty('direction')\n options.container.style.removeProperty('min-height')\n options.container.style.removeProperty('max-height')\n mounted = false\n },\n }\n}\n\nexport { resolveGridStyle, resolveItemStyle }\n"],"mappings":";;;;;;;;;;;;;;;AA+CO,SAAS,oBACd,SACe;CACf,IAAI,WAAW,QAAQ;CACvB,IAAI,UAAU;CACd,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,0BAAU,IAAI,IAAgC;CACpD,MAAM,yBAAS,IAAI,IAAyB;CAC5C,MAAM,8BAAc,IAAI,IAAiB;CACzC,MAAM,YAAY,0BAAA,wBAAwB,QAAQ,SAAS;CAE3D,SAAS,SAAe;EACtB,IAAI,CAAC,WAAW,CAAC,UAAU;EAC3B,MAAM,UAAA,GAAA,qBAAA,uBAAA,CAAgC,QAAQ;EAC9C,IAAI,CAAC,OAAO,OAAO;GACjB,QAAQ,UACN,IAAI,MAAM,OAAO,OAAO,EAAC,EAAG,WAAW,yBAAyB,CAClE;GACA;EACF;EACA,MAAM,eACJ,oBAAoB,oBAAoB,SAAS;EACnD,mBAAmB;EACnB,MAAM,YAAY,eAAA,iBAAiB,UAAU,YAAY;EACzD,OAAO,OAAO,QAAQ,UAAU,OAAO,SAAS;EAChD,MAAM,aAAa,IAAI,IAAI,SAAS,MAAM,KAAI,SAAQ,KAAK,EAAE,CAAC;EAC9D,KAAA,MAAW,CAAC,IAAI,WAAW,SAAS;GAClC,IAAI,WAAW,IAAI,EAAE,GAAG;GACxB,IAAI;IACF,OAAO,UAAU;GACnB,UAAE;IACA,QAAQ,OAAO,EAAE;IACjB,MAAM,QAAQ,OAAO,IAAI,EAAE;IAC3B,IAAI,SAAS,YAAY,IAAI,KAAK,GAAG,MAAM,OAAO;IAClD,IAAI,OAAO,YAAY,OAAO,KAAK;IACnC,OAAO,OAAO,EAAE;GAClB;EACF;EACA,KAAA,MAAW,QAAQ,SAAS,OAAO;GACjC,MAAM,YAAY,KAAK,OAAO;GAC9B,IAAI,QAAQ,OAAO,IAAI,KAAK,EAAE;GAC9B,IAAI,CAAC,OAAO;IACV,QAAQ,QAAQ,eAAe,IAAI;IACnC,IAAI,CAAC,OAAO;KACV,QAAQ,QAAQ,UAAU,cAAc,cAAc,KAAK;KAC3D,MAAM,YAAY;KAClB,MAAM,QAAQ,eAAe,KAAK;KAClC,MAAM,QAAQ,aAAa,KAAK;KAChC,YAAY,IAAI,KAAK;KACrB,QAAQ,UAAU,YAAY,KAAK;IACrC;IACA,OAAO,IAAI,KAAK,IAAI,KAAK;GAC3B;GACA,OAAO,OAAO,MAAM,OAAO,eAAA,iBAAiB,MAAM,YAAY,CAAC;GAC/D,IAAI,CAAC,WAAW;GAChB,MAAM,UAAU;IAAE;IAAO;IAAM;IAAW;GAAa;GACvD,IAAI;IACF,IAAI,SAAS,QAAQ,IAAI,KAAK,EAAE;IAChC,IAAI,CAAC,QAAQ;KACX,SAAS,QAAQ,aACZ,QAAQ,WAAW,OAAO,KAAK,CAAC,IAChC,0BAAA,sBAAsB,SAAS,SAAS,KAAK,CAAC;KACnD,QAAQ,IAAI,KAAK,IAAI,MAAM;IAC7B,OAAO,OAAO,SAAS,OAAO;GAChC,SAAS,OAAO;IACd,QAAQ,UAAU,KAAK;GACzB;EACF;CACF;CAEA,SAAS,iBAAiB,OAAqB;EAC7C,IAAI,CAAC,YAAY,kBAAkB;EACnC,MAAM,QAAA,GAAA,qBAAA,kBAAA,CAAyB,UAAU,KAAK,CAAA,CAAE;EAChD,IAAI,SAAS,kBAAkB;GAC7B,MAAM,WAAW;GACjB,mBAAmB;GACnB,QAAQ,qBAAqB,IAAI;GACjC,IAAI,aAAa,MAAM,OAAO;EAChC;CACF;CAEA,SAAS,gCAAsC;EAC7C,IAAI,CAAC,YAAY,kBAAkB;EACnC,MAAM,QACJ,QAAQ,UAAU,sBAAsB,CAAA,CAAE,SAC1C,QAAQ,UAAU;EAIpB,IAAI,QAAQ,GAAG,iBAAiB,KAAK;OAAA,IAC5B,qBAAqB,SAAS,qBAAqB;GAC1D,MAAM,WAAW;GACjB,mBAAmB,SAAS;GAC5B,IAAI,UAAU,QAAQ,qBAAqB,gBAAgB;EAC7D;CACF;CAEA,OAAO;EACL,QAAQ;GACN,IAAI,SAAS;GACb,UAAU;GACV,IACE,QAAQ,kBAAkB,SAC1B,OAAO,mBAAmB,aAC1B;IACA,WAAW,IAAI,gBAAe,YAAW;KACvC,MAAM,QACJ,QAAQ,EAAC,EAAG,YAAY,SAAS,QAAQ,UAAU;KACrD,IAAI,QAAQ,GAAG,iBAAiB,KAAK;UAChC,8BAA8B;IACrC,CAAC;IACD,SAAS,QAAQ,QAAQ,SAAS;GACpC;GACA,8BAA8B;GAC9B,OAAO;EACT;EACA,OAAO,MAAM;GACX,WAAW;GACX,OAAO;EACT;EACA,cAAc,IAAI;GAChB,mBAAmB;GACnB,IAAI,IAAI,mBAAmB;QACtB,8BAA8B;GACnC,OAAO;EACT;EACA,gBAAgB;GACd,OAAO;EACT;EACA,eAAe;GACb,OAAO,QAAQ;EACjB;EACA,UAAU;GACR,IAAI,CAAC,SAAS;GACd,UAAU,WAAW;GACrB,WAAW,KAAA;GACX,KAAA,MAAW,UAAU,QAAQ,OAAO,GAAG,OAAO,UAAU;GACxD,QAAQ,MAAM;GACd,KAAA,MAAW,SAAS,OAAO,OAAO,GAChC,IAAI,YAAY,IAAI,KAAK,GAAG,MAAM,OAAO;GAC3C,OAAO,MAAM;GACb,YAAY,MAAM;GAClB,QAAQ,UAAU,MAAM,eAAe,SAAS;GAChD,QAAQ,UAAU,MAAM,eAAe,uBAAuB;GAC9D,QAAQ,UAAU,MAAM,eAAe,gBAAgB;GACvD,QAAQ,UAAU,MAAM,eAAe,KAAK;GAC5C,QAAQ,UAAU,MAAM,eAAe,WAAW;GAClD,QAAQ,UAAU,MAAM,eAAe,YAAY;GACnD,QAAQ,UAAU,MAAM,eAAe,YAAY;GACnD,UAAU;EACZ;CACF;AACF"}
@@ -0,0 +1,35 @@
1
+ import type { BreakpointId, LayoutDocument, LayoutNode, LayoutPlacement, MaterialDefinition, MaterialRegistry } from '@composy/layout-core';
2
+ import { resolveGridStyle, resolveItemStyle } from './styles';
3
+ export interface LayoutRenderContext {
4
+ shell: HTMLElement;
5
+ item: LayoutNode;
6
+ placement: LayoutPlacement;
7
+ breakpointId: BreakpointId;
8
+ }
9
+ export interface LayoutRenderHandle {
10
+ update?(context: LayoutRenderContext): void;
11
+ destroy?(): void;
12
+ }
13
+ export interface LayoutRuntimeOptions {
14
+ container: HTMLElement;
15
+ document?: LayoutDocument;
16
+ /** Lets a host adapter provide an existing light-DOM item shell. */
17
+ getItemShell?: (item: LayoutNode) => HTMLElement | undefined;
18
+ renderItem?: (context: LayoutRenderContext) => LayoutRenderHandle | void;
19
+ /** Material registry used by the framework-neutral default renderer. */
20
+ materials?: MaterialRegistry | readonly MaterialDefinition[];
21
+ observeResize?: boolean;
22
+ onBreakpointChange?: (id: BreakpointId) => void;
23
+ onError?: (error: unknown) => void;
24
+ }
25
+ export interface LayoutRuntime {
26
+ mount(): void;
27
+ update(document: LayoutDocument): void;
28
+ setBreakpoint(id?: BreakpointId): void;
29
+ getBreakpoint(): BreakpointId | undefined;
30
+ getContainer(): HTMLElement;
31
+ destroy(): void;
32
+ }
33
+ export declare function createLayoutRuntime(options: LayoutRuntimeOptions): LayoutRuntime;
34
+ export { resolveGridStyle, resolveItemStyle };
35
+ //# sourceMappingURL=runtime.d.ts.map
package/lib/styles.cjs ADDED
@@ -0,0 +1,42 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:42:14 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ "use strict";
11
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
12
+ let _composy_layout_core = require("@composy/layout-core");
13
+ //#region src/styles.ts
14
+ function resolveGridStyle(document, breakpointId) {
15
+ const grid = (0, _composy_layout_core.getEffectiveGrid)(document, breakpointId);
16
+ const breakpoint = (0, _composy_layout_core.getBreakpoint)(document, breakpointId);
17
+ const style = {
18
+ display: "grid",
19
+ gridTemplateColumns: `repeat(${grid.columns}, minmax(0, 1fr))`,
20
+ gridAutoRows: `${grid.rowSize}px`,
21
+ gap: `${grid.gap}px`,
22
+ direction: grid.direction
23
+ };
24
+ if (document.grid.minRows !== void 0) style.minHeight = `${document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap}px`;
25
+ if (document.grid.maxRows !== void 0) style.maxHeight = `${document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap}px`;
26
+ if (breakpoint?.canvasWidth) style.gridTemplateColumns = `repeat(${grid.columns}, minmax(0, 1fr))`;
27
+ return style;
28
+ }
29
+ function resolveItemStyle(node, breakpointId) {
30
+ const placement = node.layout[breakpointId];
31
+ if (!placement) return { display: "none" };
32
+ return {
33
+ gridColumn: `${placement.x + 1} / span ${placement.w}`,
34
+ gridRow: `${placement.y + 1} / span ${placement.h}`,
35
+ display: placement.hidden ? "none" : ""
36
+ };
37
+ }
38
+ //#endregion
39
+ exports.resolveGridStyle = resolveGridStyle;
40
+ exports.resolveItemStyle = resolveItemStyle;
41
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
42
+ //# sourceMappingURL=styles.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.cjs","names":[],"sources":["../src/styles.ts"],"sourcesContent":["import type {\n BreakpointId,\n LayoutDocument,\n LayoutNode,\n LayoutPlacement,\n} from '@composy/layout-core'\nimport { getEffectiveGrid, getBreakpoint } from '@composy/layout-core'\n\nexport interface GridStyleResult {\n display: 'grid'\n gridTemplateColumns: string\n gridAutoRows: string\n gap: string\n direction: 'ltr' | 'rtl'\n minHeight?: string\n maxHeight?: string\n}\n\nexport function resolveGridStyle(\n document: LayoutDocument,\n breakpointId: BreakpointId\n): GridStyleResult {\n const grid = getEffectiveGrid(document, breakpointId)\n const breakpoint = getBreakpoint(document, breakpointId)\n const style: GridStyleResult = {\n display: 'grid',\n gridTemplateColumns: `repeat(${grid.columns}, minmax(0, 1fr))`,\n gridAutoRows: `${grid.rowSize}px`,\n gap: `${grid.gap}px`,\n direction: grid.direction,\n }\n if (document.grid.minRows !== undefined)\n style.minHeight = `${document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap}px`\n if (document.grid.maxRows !== undefined)\n style.maxHeight = `${document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap}px`\n if (breakpoint?.canvasWidth)\n style.gridTemplateColumns = `repeat(${grid.columns}, minmax(0, 1fr))`\n return style\n}\n\nexport function resolveItemStyle(\n node: LayoutNode,\n breakpointId: BreakpointId\n): Record<string, string> {\n const placement = node.layout[breakpointId]\n if (!placement) return { display: 'none' }\n return {\n gridColumn: `${placement.x + 1} / span ${placement.w}`,\n gridRow: `${placement.y + 1} / span ${placement.h}`,\n display: placement.hidden ? 'none' : '',\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAkBO,SAAS,iBACd,UACA,cACiB;CACjB,MAAM,QAAA,GAAA,qBAAA,iBAAA,CAAwB,UAAU,YAAY;CACpD,MAAM,cAAA,GAAA,qBAAA,cAAA,CAA2B,UAAU,YAAY;CACvD,MAAM,QAAyB;EAC7B,SAAS;EACT,qBAAqB,UAAU,KAAK,QAAO;EAC3C,cAAc,GAAG,KAAK,QAAO;EAC7B,KAAK,GAAG,KAAK,IAAG;EAChB,WAAW,KAAK;CAClB;CACA,IAAI,SAAS,KAAK,YAAY,KAAA,GAC5B,MAAM,YAAY,GAAG,SAAS,KAAK,WAAW,KAAK,UAAU,KAAK,OAAO,KAAK,IAAG;CACnF,IAAI,SAAS,KAAK,YAAY,KAAA,GAC5B,MAAM,YAAY,GAAG,SAAS,KAAK,WAAW,KAAK,UAAU,KAAK,OAAO,KAAK,IAAG;CACnF,IAAI,YAAY,aACd,MAAM,sBAAsB,UAAU,KAAK,QAAO;CACpD,OAAO;AACT;AAEO,SAAS,iBACd,MACA,cACwB;CACxB,MAAM,YAAY,KAAK,OAAO;CAC9B,IAAI,CAAC,WAAW,OAAO,EAAE,SAAS,OAAO;CACzC,OAAO;EACL,YAAY,GAAG,UAAU,IAAI,EAAC,UAAW,UAAU;EACnD,SAAS,GAAG,UAAU,IAAI,EAAC,UAAW,UAAU;EAChD,SAAS,UAAU,SAAS,SAAS;CACvC;AACF"}
@@ -0,0 +1,13 @@
1
+ import type { BreakpointId, LayoutDocument, LayoutNode } from '@composy/layout-core';
2
+ export interface GridStyleResult {
3
+ display: 'grid';
4
+ gridTemplateColumns: string;
5
+ gridAutoRows: string;
6
+ gap: string;
7
+ direction: 'ltr' | 'rtl';
8
+ minHeight?: string;
9
+ maxHeight?: string;
10
+ }
11
+ export declare function resolveGridStyle(document: LayoutDocument, breakpointId: BreakpointId): GridStyleResult;
12
+ export declare function resolveItemStyle(node: LayoutNode, breakpointId: BreakpointId): Record<string, string>;
13
+ //# sourceMappingURL=styles.d.ts.map
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@composy/layout-runtime",
3
+ "version": "0.0.1",
4
+ "description": "DOM-free CSS Grid style resolvers and runtime for LDesign Layout",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "types": "./es/index.d.ts",
11
+ "import": "./es/index.js",
12
+ "require": "./lib/index.cjs",
13
+ "default": "./es/index.js"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "main": "./lib/index.cjs",
18
+ "module": "./es/index.js",
19
+ "types": "./es/index.d.ts",
20
+ "unpkg": "./dist/index.min.js",
21
+ "jsdelivr": "./dist/index.min.js",
22
+ "files": [
23
+ "README.md",
24
+ "LICENSE",
25
+ "CHANGELOG.md",
26
+ "es",
27
+ "lib",
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "build": "ldesign-builder build",
32
+ "type-check": "tsc --noEmit",
33
+ "test:run": "ltesting run:unit"
34
+ },
35
+ "dependencies": {
36
+ "@composy/layout-core": "^0.0.1"
37
+ },
38
+ "devDependencies": {
39
+ "@composy/builder": "^0.0.1",
40
+ "@composy/testing": "^0.0.1",
41
+ "@composy/layout-shared-test": "^0.0.1",
42
+ "typescript": "5.9.3",
43
+ "vitest": "4.1.10"
44
+ },
45
+ "publishConfig": {
46
+ "registry": "http://npm.longrise.cn:6286/"
47
+ },
48
+ "typings": "./es/index.d.ts",
49
+ "browser": "./dist/index.js"
50
+ }