@composy/layout-runtime 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +101 -17
  3. package/dist/index.js +68 -20
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.js +191 -1
  6. package/dist/index.min.js.map +1 -0
  7. package/es/.dts-output-manifest.json +1 -0
  8. package/es/.dts.tsbuildinfo +1 -0
  9. package/es/index.d.ts +0 -1
  10. package/es/index.js +1 -1
  11. package/es/material-renderer.d.ts +0 -1
  12. package/es/material-renderer.js +40 -7
  13. package/es/material-renderer.js.map +1 -1
  14. package/es/runtime.d.ts +0 -1
  15. package/es/runtime.js +27 -12
  16. package/es/runtime.js.map +1 -1
  17. package/es/styles.d.ts +0 -1
  18. package/es/styles.js +3 -3
  19. package/es/styles.js.map +1 -1
  20. package/esm/index.js +13 -0
  21. package/esm/material-renderer.js +102 -0
  22. package/esm/material-renderer.js.map +1 -0
  23. package/esm/runtime.js +171 -0
  24. package/esm/runtime.js.map +1 -0
  25. package/esm/styles.js +39 -0
  26. package/esm/styles.js.map +1 -0
  27. package/lib/index.cjs +1 -1
  28. package/lib/{index.d.ts → index.d.cts} +0 -1
  29. package/lib/material-renderer.cjs +40 -7
  30. package/lib/material-renderer.cjs.map +1 -1
  31. package/lib/{material-renderer.d.ts → material-renderer.d.cts} +0 -1
  32. package/lib/runtime.cjs +27 -12
  33. package/lib/runtime.cjs.map +1 -1
  34. package/lib/{runtime.d.ts → runtime.d.cts} +0 -1
  35. package/lib/styles.cjs +3 -3
  36. package/lib/styles.cjs.map +1 -1
  37. package/lib/{styles.d.ts → styles.d.cts} +0 -1
  38. package/package.json +13 -7
  39. package/es/index.d.ts.map +0 -1
  40. package/es/material-renderer.d.ts.map +0 -1
  41. package/es/runtime.d.ts.map +0 -1
  42. package/es/styles.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.2] - 2026-10-12
4
+
5
+ ### ✨ 新功能
6
+
7
+ - **layout**: 添加 LDesign 项目配置文件 ([f61ae51](https://gitee.com/swimly/ldesign-layout/commit/f61ae51279dff52720216e15e7dffc6fd0b8c579)) - @swimly
8
+
9
+ ### ♻️ 代码重构
10
+
11
+ - 重构项目为pnpm monorepo并初始化全量布局包 ([d6dda52](https://gitee.com/swimly/ldesign-layout/commit/d6dda528e132eff6f316f9dcb37c4dd11d4e98c7)) - @swimly
12
+
13
+ ### 📝 文档更新
14
+
15
+ - 更新各包 README 文档 ([41ac588](https://gitee.com/swimly/ldesign-layout/commit/41ac588a7f0d949bc172b94318bccd1bf1ed0d49)) - @swimly
16
+
17
+ ### 📦 构建系统
18
+
19
+ - **tsconfig**: 标准化所有包的tsconfig配置并添加声明生成配置 ([3e34f79](https://gitee.com/swimly/ldesign-layout/commit/3e34f79d84f2335710c86f2503dadec050de329e)) - @swimly
20
+
21
+ ### 🔧 其他
22
+
23
+ - **layout**: 更新项目配置及依赖版本 ([5344532](https://gitee.com/swimly/ldesign-layout/commit/534453298534e85a3f6776f908290bffdb15b8a6)) - @swimly
24
+ - **layout**: 统一构建配置与文档体系,新增 Playground 示例 ([d8691a0](https://gitee.com/swimly/ldesign-layout/commit/d8691a0721bdf088b6c5585ac2826b1b851d746d)) - @swimly
25
+ - 修复多个package.json文件末尾缺失换行符的问题 ([df9b977](https://gitee.com/swimly/ldesign-layout/commit/df9b977cca8d88cb4df00583f6eea31fd0e8c51b)) - @swimly
26
+ - 更新各包依赖版本 ([1ce08a9](https://gitee.com/swimly/ldesign-layout/commit/1ce08a9e677780eb38298c4cabe92e5693424fc2)) - @swimly
27
+ - update layout (92 files) ([9c28da2](https://gitee.com/swimly/ldesign-layout/commit/9c28da243aebea0d2c1c20ccb4c996aae9f031fc)) - @swimly
28
+
29
+ ### 👥 Contributors
30
+
31
+ swimly
32
+
33
+ ### 📊 Statistics
34
+
35
+ - Total Commits: **9**
36
+ - Contributors: **1**
37
+
3
38
  ## 1.0.0-alpha.0
4
39
 
5
40
  - Added the DOM-free CSS Grid runtime and responsive style serialization helpers.
package/README.md CHANGED
@@ -1,17 +1,101 @@
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.
1
+ # @composy/layout-runtime
2
+
3
+ LDesign 布局运行时,把 `LayoutDocument`
4
+ 渲染到真实 DOM 容器,自动处理断点切换、Resize 观察与 item 生命周期。
5
+
6
+ ## 安装
7
+
8
+ ```bash
9
+ pnpm add @composy/layout-runtime @composy/layout-core
10
+ ```
11
+
12
+ ## 目录结构
13
+
14
+ ```
15
+ src/
16
+ ├── index.ts
17
+ ├── runtime.ts # createLayoutRuntime + LayoutRuntime
18
+ ├── styles.ts # resolveGridStyle / resolveItemStyle
19
+ └── material-renderer.ts
20
+ ```
21
+
22
+ ## 核心 API
23
+
24
+ ### 接口:LayoutRuntime
25
+
26
+ | 方法 | 签名 | 说明 |
27
+ | --------------- | --------------------------------- | ---------- |
28
+ | `mount` | `() => void` | 挂载 |
29
+ | `update` | `(doc: LayoutDocument) => void` | 更新文档 |
30
+ | `setBreakpoint` | `(id?: BreakpointId) => void` | 切换断点 |
31
+ | `getBreakpoint` | `() => BreakpointId \| undefined` | 取当前断点 |
32
+ | `getContainer` | `() => HTMLElement` | 取容器 |
33
+ | `destroy` | `() => void` | 销毁 |
34
+
35
+ ### 函数
36
+
37
+ | 名称 | 签名 | 用途 |
38
+ | --------------------- | ---------------------------------------------------------------- | -------------- |
39
+ | `createLayoutRuntime` | `(options: LayoutRuntimeOptions) => LayoutRuntime` | 创建运行时 |
40
+ | `resolveGridStyle` | `(doc, breakpointId) => { gridTemplateColumns, gap, direction }` | 计算容器网格 |
41
+ | `resolveItemStyle` | `(item, placement, gridStyle) => CSSStyleDeclaration` | 计算 item 样式 |
42
+
43
+ ### LayoutRuntimeOptions
44
+
45
+ | 字段 | 类型 | 说明 |
46
+ | -------------------- | ------------------------------------------- | --------------------- |
47
+ | `container` | `HTMLElement` | 必需 |
48
+ | `document` | `LayoutDocument?` | 初始文档 |
49
+ | `getItemShell` | `(item) => HTMLElement` | 由宿主提供 item shell |
50
+ | `renderItem` | `(ctx) => LayoutRenderHandle` | 自定义渲染钩子 |
51
+ | `materials` | `MaterialRegistry \| MaterialDefinition[]?` | 物料注册表 |
52
+ | `observeResize` | `boolean?` | 是否监听 Resize |
53
+ | `onBreakpointChange` | `(id) => void` | 断点切换回调 |
54
+ | `onError` | `(error) => void` | 错误回调 |
55
+
56
+ ## 使用示例
57
+
58
+ ```ts
59
+ import { createLayoutRuntime } from '@composy/layout-runtime'
60
+ import { createLayoutDocument } from '@composy/layout-core'
61
+
62
+ const container = document.getElementById('layout') as HTMLElement
63
+ const runtime = createLayoutRuntime({
64
+ container,
65
+ observeResize: true,
66
+ renderItem: ctx => {
67
+ ctx.shell.innerHTML = ctx.item.label ?? ''
68
+ return { update: n => (n.shell.innerHTML = n.item.label ?? '') }
69
+ },
70
+ })
71
+
72
+ runtime.mount()
73
+ runtime.update(
74
+ createLayoutDocument({
75
+ version: 1,
76
+ name: 'App',
77
+ defaultBreakpointId: 'lg',
78
+ breakpoints: [{ id: 'lg', min: 1024 }],
79
+ items: [
80
+ { id: 'a', type: 'panel', label: 'A', placement: { grid: [0, 0, 4, 3] } },
81
+ { id: 'b', type: 'panel', label: 'B', placement: { grid: [0, 3, 4, 6] } },
82
+ ],
83
+ })
84
+ )
85
+ ```
86
+
87
+ ## 构建脚本 & 验证命令
88
+
89
+ ```bash
90
+ pnpm build
91
+ pnpm type-check
92
+ ```
93
+
94
+ ## 产物说明
95
+
96
+ - `es/` / `lib/` / `dist/`。
97
+
98
+ ## 备注
99
+
100
+ - 依赖 `@composy/layout-core`。
101
+ - `observeResize: true` 时用 `ResizeObserver` 自动切换断点。
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/layout-runtime v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:42:14 *
5
+ * Build time: 2024-10-12 17:44:18 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -23,8 +23,8 @@
23
23
  gap: `${grid.gap}px`,
24
24
  direction: grid.direction
25
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`;
26
+ if (document.grid.minRows !== void 0) style.minHeight = `${Math.max(0, document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap)}px`;
27
+ if (document.grid.maxRows !== void 0) style.maxHeight = `${Math.max(0, document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap)}px`;
28
28
  if (breakpoint?.canvasWidth) style.gridTemplateColumns = `repeat(${grid.columns}, minmax(0, 1fr))`;
29
29
  return style;
30
30
  }
@@ -57,17 +57,26 @@
57
57
  placement: context.placement,
58
58
  breakpointId: context.breakpointId
59
59
  });
60
- if (rendered instanceof Node) node = rendered;
60
+ if (isDomNode(rendered)) node = rendered;
61
61
  else if (typeof rendered === "string") {
62
62
  const wrapper = host.createElement("div");
63
- wrapper.innerHTML = sanitizeMarkup(rendered);
63
+ wrapper.innerHTML = sanitizeMarkup(host, rendered);
64
64
  node = wrapper;
65
65
  }
66
66
  }
67
67
  if (!node) return void 0;
68
+ const appliedProps = /* @__PURE__ */ new Set();
68
69
  const applyProps = (item) => {
69
- if (!(node instanceof HTMLElement)) return;
70
- for (const [key, value] of Object.entries(item.props ?? {})) {
70
+ if (!isElement(node, host)) return;
71
+ const nextProps = item.props ?? {};
72
+ for (const key of appliedProps) {
73
+ if (key in nextProps) continue;
74
+ node.removeAttribute(key);
75
+ delete node.dataset[`prop${toDataKey(key)}`];
76
+ appliedProps.delete(key);
77
+ }
78
+ for (const [key, value] of Object.entries(nextProps)) {
79
+ appliedProps.add(key);
71
80
  try {
72
81
  node[key] = value;
73
82
  } catch {}
@@ -90,8 +99,32 @@
90
99
  function toDataKey(value) {
91
100
  return value.replace(/[^a-zA-Z0-9]/g, "");
92
101
  }
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, "");
102
+ function isDomNode(value) {
103
+ return Boolean(value && typeof value === "object" && "nodeType" in value && typeof value.cloneNode === "function");
104
+ }
105
+ function isElement(value, document) {
106
+ if (!value || value.nodeType !== 1) return false;
107
+ const ElementConstructor = document.defaultView?.HTMLElement;
108
+ return ElementConstructor ? value instanceof ElementConstructor : typeof value.setAttribute === "function";
109
+ }
110
+ function sanitizeMarkup(document, value) {
111
+ const template = document.createElement("template");
112
+ template.innerHTML = value;
113
+ template.content.querySelectorAll("script,style,iframe,object,embed,foreignObject").forEach((node) => node.remove());
114
+ template.content.querySelectorAll("*").forEach((element) => {
115
+ for (const attribute of Array.from(element.attributes)) {
116
+ const name = attribute.name.toLowerCase();
117
+ const content = attribute.value.trim().toLowerCase();
118
+ if (name.startsWith("on") || [
119
+ "href",
120
+ "src",
121
+ "xlink:href",
122
+ "formaction",
123
+ "srcdoc"
124
+ ].includes(name) && /^(?:javascript:|data:text\/html)/i.test(content)) element.removeAttribute(attribute.name);
125
+ }
126
+ });
127
+ return template.innerHTML;
95
128
  }
96
129
  //#endregion
97
130
  //#region src/runtime.ts
@@ -119,15 +152,11 @@
119
152
  const currentIds = new Set(document.items.map((item) => item.id));
120
153
  for (const [id, handle] of handles) {
121
154
  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
- }
155
+ disposeHandle(id, handle);
156
+ const shell = shells.get(id);
157
+ if (shell && ownedShells.has(shell)) shell.remove();
158
+ if (shell) ownedShells.delete(shell);
159
+ shells.delete(id);
131
160
  }
132
161
  for (const item of document.items) {
133
162
  const placement = item.layout[breakpointId];
@@ -145,7 +174,11 @@
145
174
  shells.set(item.id, shell);
146
175
  }
147
176
  Object.assign(shell.style, resolveItemStyle(item, breakpointId));
148
- if (!placement) continue;
177
+ if (!placement) {
178
+ const handle = handles.get(item.id);
179
+ if (handle) disposeHandle(item.id, handle);
180
+ continue;
181
+ }
149
182
  const context = {
150
183
  shell,
151
184
  item,
@@ -163,6 +196,15 @@
163
196
  }
164
197
  }
165
198
  }
199
+ function disposeHandle(id, handle = handles.get(id)) {
200
+ if (!handle) return;
201
+ handles.delete(id);
202
+ try {
203
+ handle.destroy?.();
204
+ } catch (error) {
205
+ options.onError?.(error);
206
+ }
207
+ }
166
208
  function updateBreakpoint(width) {
167
209
  if (!document || manualBreakpoint) return;
168
210
  const next = (0, _composy_layout_core.resolveBreakpoint)(document, width).id;
@@ -203,6 +245,10 @@
203
245
  render();
204
246
  },
205
247
  setBreakpoint(id) {
248
+ if (id && document && !document.breakpoints.some((bp) => bp.id === id)) {
249
+ options.onError?.(/* @__PURE__ */ new Error(`Unknown breakpoint ${id}.`));
250
+ return;
251
+ }
206
252
  manualBreakpoint = id;
207
253
  if (id) activeBreakpoint = id;
208
254
  else updateBreakpointFromContainer();
@@ -218,7 +264,7 @@
218
264
  if (!mounted) return;
219
265
  observer?.disconnect();
220
266
  observer = void 0;
221
- for (const handle of handles.values()) handle.destroy?.();
267
+ for (const [id, handle] of handles) disposeHandle(id, handle);
222
268
  handles.clear();
223
269
  for (const shell of shells.values()) if (ownedShells.has(shell)) shell.remove();
224
270
  shells.clear();
@@ -230,6 +276,8 @@
230
276
  options.container.style.removeProperty("direction");
231
277
  options.container.style.removeProperty("min-height");
232
278
  options.container.style.removeProperty("max-height");
279
+ manualBreakpoint = void 0;
280
+ activeBreakpoint = void 0;
233
281
  mounted = false;
234
282
  }
235
283
  };
package/dist/index.js.map CHANGED
@@ -1 +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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"}
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 = `${Math.max(\n 0,\n document.grid.minRows * (grid.rowSize + grid.gap) - grid.gap\n )}px`\n if (document.grid.maxRows !== undefined)\n style.maxHeight = `${Math.max(\n 0,\n document.grid.maxRows * (grid.rowSize + grid.gap) - grid.gap\n )}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 (isDomNode(rendered)) node = rendered\n else if (typeof rendered === 'string') {\n const wrapper = host.createElement('div')\n wrapper.innerHTML = sanitizeMarkup(host, rendered)\n node = wrapper\n }\n }\n if (!node) return undefined\n\n const appliedProps = new Set<string>()\n const applyProps = (item: LayoutNode): void => {\n if (!isElement(node, host)) return\n const nextProps = item.props ?? {}\n for (const key of appliedProps) {\n if (key in nextProps) continue\n node.removeAttribute(key)\n delete node.dataset[`prop${toDataKey(key)}`]\n appliedProps.delete(key)\n }\n for (const [key, value] of Object.entries(nextProps)) {\n appliedProps.add(key)\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 isDomNode(value: unknown): value is Node {\n return Boolean(\n value &&\n typeof value === 'object' &&\n 'nodeType' in value &&\n typeof (value as { cloneNode?: unknown }).cloneNode === 'function'\n )\n}\n\nfunction isElement(\n value: Node | undefined,\n document: Document\n): value is HTMLElement {\n if (!value || value.nodeType !== 1) return false\n const ElementConstructor = document.defaultView?.HTMLElement\n return ElementConstructor\n ? value instanceof ElementConstructor\n : typeof (value as Element).setAttribute === 'function'\n}\n\nfunction sanitizeMarkup(document: Document, value: string): string {\n const template = document.createElement('template')\n template.innerHTML = value\n template.content\n .querySelectorAll('script,style,iframe,object,embed,foreignObject')\n .forEach(node => node.remove())\n template.content.querySelectorAll<HTMLElement>('*').forEach(element => {\n for (const attribute of Array.from(element.attributes)) {\n const name = attribute.name.toLowerCase()\n const content = attribute.value.trim().toLowerCase()\n if (\n name.startsWith('on') ||\n (['href', 'src', 'xlink:href', 'formaction', 'srcdoc'].includes(name) &&\n /^(?:javascript:|data:text\\/html)/i.test(content))\n )\n element.removeAttribute(attribute.name)\n }\n })\n return template.innerHTML\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 disposeHandle(id, handle)\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 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) {\n const handle = handles.get(item.id)\n if (handle) disposeHandle(item.id, handle)\n continue\n }\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 disposeHandle(id: string, handle = handles.get(id)): void {\n if (!handle) return\n handles.delete(id)\n try {\n handle.destroy?.()\n } catch (error) {\n options.onError?.(error)\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 if (id && document && !document.breakpoints.some(bp => bp.id === id)) {\n options.onError?.(new Error(`Unknown breakpoint ${id}.`))\n return\n }\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 [id, handle] of handles) disposeHandle(id, handle)\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 manualBreakpoint = undefined\n activeBreakpoint = undefined\n mounted = false\n },\n }\n}\n\nexport { resolveGridStyle, resolveItemStyle }\n"],"mappings":"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"}
package/dist/index.min.js CHANGED
@@ -1 +1,191 @@
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;});
1
+ /*!
2
+ * ***********************************
3
+ * @composy/layout-runtime v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-10-12 17:44:18 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ "use strict";
11
+ (function(e, t) {
12
+ "object" == typeof exports && "undefined" != typeof module ? t(exports, require("@composy/layout-core")) : "function" == typeof define && define.amd ? define(["exports", "@composy/layout-core"], t) : t((e = "undefined" != typeof globalThis ? globalThis : e || self).LDesignLayoutRuntime = {}, e.ComposyLayoutCore);
13
+ })(this, function(exports, e) {
14
+ function t(t, n) {
15
+ const o = (0, e.getEffectiveGrid)(t, n), r = (0, e.getBreakpoint)(t, n), i = {
16
+ display: "grid",
17
+ gridTemplateColumns: `repeat(${o.columns}, minmax(0, 1fr))`,
18
+ gridAutoRows: `${o.rowSize}px`,
19
+ gap: `${o.gap}px`,
20
+ direction: o.direction
21
+ };
22
+ return void 0 !== t.grid.minRows && (i.minHeight = `${Math.max(0, t.grid.minRows * (o.rowSize + o.gap) - o.gap)}px`), void 0 !== t.grid.maxRows && (i.maxHeight = `${Math.max(0, t.grid.maxRows * (o.rowSize + o.gap) - o.gap)}px`), r?.canvasWidth && (i.gridTemplateColumns = `repeat(${o.columns}, minmax(0, 1fr))`), i;
23
+ }
24
+ function n(e, t) {
25
+ const n = e.layout[t];
26
+ return n ? {
27
+ gridColumn: `${n.x + 1} / span ${n.w}`,
28
+ gridRow: `${n.y + 1} / span ${n.h}`,
29
+ display: n.hidden ? "none" : ""
30
+ } : { display: "none" };
31
+ }
32
+ function o(e, t) {
33
+ const n = t?.get(e.item.type), o = n?.renderers?.webcomponent ?? n?.renderers?.html;
34
+ if (!n || !o) return;
35
+ const i = e.shell.ownerDocument;
36
+ let a;
37
+ if (o.tagName) a = i.createElement(o.tagName);
38
+ else if (o.render) {
39
+ const t = o.render({
40
+ item: e.item,
41
+ placement: e.placement,
42
+ breakpointId: e.breakpointId
43
+ });
44
+ if (s = t, Boolean(s && "object" == typeof s && "nodeType" in s && "function" == typeof s.cloneNode)) a = t;
45
+ else if ("string" == typeof t) {
46
+ const e = i.createElement("div");
47
+ e.innerHTML = function(e, t) {
48
+ const n = e.createElement("template");
49
+ return n.innerHTML = t, n.content.querySelectorAll("script,style,iframe,object,embed,foreignObject").forEach((e) => e.remove()), n.content.querySelectorAll("*").forEach((e) => {
50
+ for (const t of Array.from(e.attributes)) {
51
+ const n = t.name.toLowerCase(), o = t.value.trim().toLowerCase();
52
+ (n.startsWith("on") || [
53
+ "href",
54
+ "src",
55
+ "xlink:href",
56
+ "formaction",
57
+ "srcdoc"
58
+ ].includes(n) && /^(?:javascript:|data:text\/html)/i.test(o)) && e.removeAttribute(t.name);
59
+ }
60
+ }), n.innerHTML;
61
+ }(i, t), a = e;
62
+ }
63
+ }
64
+ var s;
65
+ if (!a) return;
66
+ const c = /* @__PURE__ */ new Set(), l = (e) => {
67
+ if (!function(e, t) {
68
+ if (!e || 1 !== e.nodeType) return !1;
69
+ const n = t.defaultView?.HTMLElement;
70
+ return n ? e instanceof n : "function" == typeof e.setAttribute;
71
+ }(a, i)) return;
72
+ const t = e.props ?? {};
73
+ for (const e of c) e in t || (a.removeAttribute(e), delete a.dataset[`prop${r(e)}`], c.delete(e));
74
+ for (const [e, n] of Object.entries(t)) {
75
+ c.add(e);
76
+ try {
77
+ a[e] = n;
78
+ } catch {}
79
+ null == n || "object" == typeof n || "function" == typeof n ? null != n && (a.dataset[`prop${r(e)}`] = JSON.stringify(n)) : a.setAttribute(e, String(n));
80
+ }
81
+ };
82
+ return l(e.item), e.shell.replaceChildren(a), {
83
+ update(e) {
84
+ l(e.item);
85
+ },
86
+ destroy() {
87
+ a?.parentNode === e.shell && e.shell.removeChild(a);
88
+ }
89
+ };
90
+ }
91
+ function r(e) {
92
+ return e.replace(/[^a-zA-Z0-9]/g, "");
93
+ }
94
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }), exports.createLayoutRuntime = function(r) {
95
+ let i, a, s, c = r.document, l = !1;
96
+ const d = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Set(), p = function(t) {
97
+ if (t) return Array.isArray(t) ? (0, e.createMaterialRegistry)(t) : t;
98
+ }(r.materials);
99
+ function f() {
100
+ if (!l || !c) return;
101
+ const s = (0, e.validateLayoutDocument)(c);
102
+ if (!s.valid) return void r.onError?.(new Error(s.issues[0]?.message ?? "Invalid layout document"));
103
+ const f = i ?? a ?? c.defaultBreakpointId;
104
+ a = f;
105
+ const g = t(c, f);
106
+ Object.assign(r.container.style, g);
107
+ const h = new Set(c.items.map((e) => e.id));
108
+ for (const [e, t] of d) {
109
+ if (h.has(e)) continue;
110
+ y(e, t);
111
+ const n = u.get(e);
112
+ n && m.has(n) && n.remove(), n && m.delete(n), u.delete(e);
113
+ }
114
+ for (const e of c.items) {
115
+ const t = e.layout[f];
116
+ let i = u.get(e.id);
117
+ if (i || (i = r.getItemShell?.(e), i || (i = r.container.ownerDocument.createElement("div"), i.className = "ldesign-layout-item", i.dataset.layoutItemId = e.id, i.dataset.layoutType = e.type, m.add(i), r.container.appendChild(i)), u.set(e.id, i)), Object.assign(i.style, n(e, f)), !t) {
118
+ const t = d.get(e.id);
119
+ t && y(e.id, t);
120
+ continue;
121
+ }
122
+ const a = {
123
+ shell: i,
124
+ item: e,
125
+ placement: t,
126
+ breakpointId: f
127
+ };
128
+ try {
129
+ let t = d.get(e.id);
130
+ t ? t.update?.(a) : (t = r.renderItem ? r.renderItem(a) ?? {} : o(a, p) ?? {}, d.set(e.id, t));
131
+ } catch (e) {
132
+ r.onError?.(e);
133
+ }
134
+ }
135
+ }
136
+ function y(e, t = d.get(e)) {
137
+ if (t) {
138
+ d.delete(e);
139
+ try {
140
+ t.destroy?.();
141
+ } catch (e) {
142
+ r.onError?.(e);
143
+ }
144
+ }
145
+ }
146
+ function g(t) {
147
+ if (!c || i) return;
148
+ const n = (0, e.resolveBreakpoint)(c, t).id;
149
+ if (n !== a) {
150
+ const e = a;
151
+ a = n, r.onBreakpointChange?.(n), e !== n && f();
152
+ }
153
+ }
154
+ function h() {
155
+ if (!c || i) return;
156
+ const e = r.container.getBoundingClientRect().width || r.container.clientWidth;
157
+ if (e > 0) g(e);
158
+ else if (a !== c.defaultBreakpointId) {
159
+ const e = a;
160
+ a = c.defaultBreakpointId, e && r.onBreakpointChange?.(a);
161
+ }
162
+ }
163
+ return {
164
+ mount() {
165
+ l || (l = !0, !1 !== r.observeResize && "undefined" != typeof ResizeObserver && (s = new ResizeObserver((e) => {
166
+ const t = e[0]?.contentRect.width ?? r.container.clientWidth;
167
+ t > 0 ? g(t) : h();
168
+ }), s.observe(r.container)), h(), f());
169
+ },
170
+ update(e) {
171
+ c = e, f();
172
+ },
173
+ setBreakpoint(e) {
174
+ e && c && !c.breakpoints.some((t) => t.id === e) ? r.onError?.(/* @__PURE__ */ new Error(`Unknown breakpoint ${e}.`)) : (i = e, e ? a = e : h(), f());
175
+ },
176
+ getBreakpoint: () => a,
177
+ getContainer: () => r.container,
178
+ destroy() {
179
+ if (l) {
180
+ s?.disconnect(), s = void 0;
181
+ for (const [e, t] of d) y(e, t);
182
+ d.clear();
183
+ for (const e of u.values()) m.has(e) && e.remove();
184
+ u.clear(), m.clear(), r.container.style.removeProperty("display"), r.container.style.removeProperty("grid-template-columns"), r.container.style.removeProperty("grid-auto-rows"), r.container.style.removeProperty("gap"), r.container.style.removeProperty("direction"), r.container.style.removeProperty("min-height"), r.container.style.removeProperty("max-height"), i = void 0, a = void 0, l = !1;
185
+ }
186
+ }
187
+ };
188
+ }, exports.resolveGridStyle = t, exports.resolveItemStyle = n;
189
+ });
190
+ /*! End of @composy/layout-runtime | Powered by @composy/builder */
191
+ //# sourceMappingURL=index.min.js.map