@trunkjs/content-pane 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,81 @@
1
+ # 1.0.0 (2025-08-11)
2
+
3
+ This was a version bump only for content-pane to align it with other projects, there were no code changes.
4
+
5
+ ## 0.0.14 (2025-08-11)
6
+
7
+ ### 🧱 Updated Dependencies
8
+
9
+ - Updated browser-utils to 1.0.12
10
+
11
+ ## 0.0.13 (2025-08-11)
12
+
13
+ ### 🧱 Updated Dependencies
14
+
15
+ - Updated browser-utils to 1.0.11
16
+
17
+ ## 0.0.12 (2025-08-11)
18
+
19
+ ### 🧱 Updated Dependencies
20
+
21
+ - Updated browser-utils to 1.0.10
22
+
23
+ ## 0.0.11 (2025-08-11)
24
+
25
+ ### 🧱 Updated Dependencies
26
+
27
+ - Updated browser-utils to 1.0.9
28
+
29
+ ## 0.0.10 (2025-08-11)
30
+
31
+ ### 🧱 Updated Dependencies
32
+
33
+ - Updated browser-utils to 1.0.8
34
+
35
+ ## 0.0.9 (2025-08-07)
36
+
37
+ ### 🧱 Updated Dependencies
38
+
39
+ - Updated browser-utils to 1.0.7
40
+
41
+ ## 0.0.8 (2025-08-07)
42
+
43
+ ### 🧱 Updated Dependencies
44
+
45
+ - Updated browser-utils to 1.0.6
46
+
47
+ ## 0.0.7 (2025-08-07)
48
+
49
+ ### 🧱 Updated Dependencies
50
+
51
+ - Updated browser-utils to 1.0.5
52
+
53
+ ## 0.0.6 (2025-08-07)
54
+
55
+ ### 🧱 Updated Dependencies
56
+
57
+ - Updated browser-utils to 1.0.4
58
+
59
+ ## 0.0.5 (2025-08-07)
60
+
61
+ ### 🧱 Updated Dependencies
62
+
63
+ - Updated browser-utils to 1.0.3
64
+
65
+ ## 0.0.4 (2025-08-07)
66
+
67
+ ### 🧱 Updated Dependencies
68
+
69
+ - Updated browser-utils to 1.0.2
70
+
71
+ ## 0.0.3 (2025-08-07)
72
+
73
+ ### 🧱 Updated Dependencies
74
+
75
+ - Updated browser-utils to 1.0.1
76
+
77
+ ## 0.0.2 (2025-08-07)
78
+
79
+ ### 🧱 Updated Dependencies
80
+
81
+ - Updated browser-utils to 1.0.0
package/README.md ADDED
@@ -0,0 +1,215 @@
1
+ # content-pane
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ A lightweight toolkit for turning a flat block of HTML content into a, styled “content pane.” It provides:
6
+
7
+ - A Web Component (<content-area2>) that automatically:
8
+ - Builds a nested section structure from your headings and horizontal rules.
9
+ - Applies layout transformations based on a compact layout attribute syntax.
10
+ - A small set of utilities you can call programmatically:
11
+ - SectionTreeBuilder: wraps related content into nested <section> containers.
12
+ - applyLayout: transforms elements based on a layout string (tag#id.class1.class2).
13
+ - attrAssign: helper to assign attributes to selected children.
14
+
15
+ Status: experimental / evolving API.
16
+
17
+ ## Why use content-pane?
18
+
19
+ If you have document-like content (headings, paragraphs, images, etc.) and want:
20
+ - Automatic, semantic grouping of content into sections
21
+ - Declarative, inline layout instructions per element
22
+ - A no-shadow-DOM approach that keeps your stylesheets simple
23
+
24
+ …content-pane is for you.
25
+
26
+ ## Packages and exports
27
+
28
+ Install or import from:
29
+ - @trunkjs/content-pane
30
+
31
+ Exports:
32
+ - Web Component: registered as custom element content-area2
33
+ - Functions:
34
+ - applyLayout(elementOrElements, { recursive = true })
35
+ - attrAssign(element, multiQuerySelector, attributes)
36
+ - SectionTreeBuilder class
37
+
38
+ Import examples:
39
+ - Register the custom element (side-effect import): import '@trunkjs/content-pane'
40
+ - Use utilities: import { applyLayout, attrAssign } from '@trunkjs/content-pane'
41
+
42
+ ## The <content-area2> Web Component
43
+
44
+ The component is defined without Shadow DOM (createRenderRoot returns this), so your global CSS can style the generated structure directly.
45
+
46
+ What it does on connect:
47
+ 1. Waits for DOMContentLoaded
48
+ 2. Builds a section tree from its direct children using SectionTreeBuilder
49
+ 3. Applies layout transformations to itself and its descendants using applyLayout
50
+
51
+ Tag name: content-area2
52
+
53
+ Note: The internal class is ContentAreaElement2. The static is getter returns 'tj-content-area' but the actual registered tag is content-area2.
54
+
55
+ ### Quick start (browser)
56
+
57
+ - Include content-area2 on your page
58
+ - Ensure the module is imported so the element is defined
59
+ - Add headings and content as children
60
+
61
+ Example:
62
+ <!-- somewhere in your app bootstrap -->
63
+ <script type="module">
64
+ import '@trunkjs/content-pane'; // registers <content-area2>
65
+ </script>
66
+
67
+ <content-area2>
68
+ <h2 layout="2">Introduction</h2>
69
+ <p>This is the intro paragraph.</p>
70
+
71
+ <h3>Details</h3>
72
+ <p>Some detailed text.</p>
73
+
74
+ <hr> <!-- becomes a mid-level divider -->
75
+ <h2 layout="+2">More</h2>
76
+ <p>Additional information appended at the same level as previous H2.</p>
77
+
78
+ <!-- An element with layout that transforms into an <aside> -->
79
+ <div layout="2.5;aside#toc.toc right">
80
+ <p>Table of contents here…</p>
81
+ </div>
82
+ </content-area2>
83
+
84
+ ## The SectionTreeBuilder
85
+
86
+ SectionTreeBuilder rearranges a flat list of nodes into nested <section> elements based on:
87
+ - Heading levels (H1–H6) mapped to a 10s scale (H2 → 20, H3 → 30, …). H1 is treated as H2.
88
+ - Horizontal rules (HR) which become implicit dividers at lastFixedI + 5.
89
+ - Optional layout prefix directives on elements to influence grouping.
90
+
91
+ It processes only element nodes (other node types are appended as-is) and respects a small “index” (i) model for nesting.
92
+
93
+ ### Layout prefix for sectioning
94
+
95
+ If a node has a layout attribute, the prefix can shape the sectioning behavior:
96
+
97
+ Pattern: ^(\+|-|)([0-9]?\.?[0-9]?|)(;|$)
98
+
99
+ - Variant:
100
+ - + → append: place the node inside the existing container at the computed level
101
+ - - → skip: do not start a new section for this node; it’s appended to the current container
102
+ - (none) → new: create a new section container at this level
103
+ - Level number:
104
+ - Optional number (e.g., 2, 2.5) scaled to 10s internally (2 → 20). If absent, heading tags provide the default.
105
+ - Decimals allow interleaving content between heading levels (e.g., 2.5 sits between H2 (20) and H3 (30)).
106
+
107
+ When creating a new <section>, SectionTreeBuilder:
108
+ - Moves attributes beginning with layout from the original node onto the new section and removes them from the original.
109
+ - Copies attributes starting with section- and converts section-* class names to classes on the section wrapper (prefix removed).
110
+ - For HR elements, copies all attributes to the section wrapper.
111
+
112
+ Note: The attribute handling is intentionally conservative to avoid disrupting your original elements more than necessary.
113
+
114
+ ### Programmatic usage
115
+
116
+ import { SectionTreeBuilder } from '@trunkjs/content-pane';
117
+
118
+ const container = document.querySelector('#my-content') as HTMLElement;
119
+ const stb = new SectionTreeBuilder(container);
120
+ stb.arrange(Array.from(container.children));
121
+
122
+ This will restructure the container’s children into nested <section> elements.
123
+
124
+ ## applyLayout
125
+
126
+ applyLayout transforms elements based on a compact layout attribute:
127
+ - Syntax: [prefix][;]selector
128
+ - Prefix: the same prefix as used by SectionTreeBuilder, typically used for sectioning (see above). applyLayout strips this prefix before applying the selector.
129
+ - Selector: a simplified CSS-like “element definition” of the form tag#id.class1.class2. If tag is omitted, defaults to div.
130
+
131
+ Behavior:
132
+ - For each element with a layout attribute, applyLayout:
133
+ - Parses the layout string, ignoring any leading sectioning prefix
134
+ - Creates a replacement element using the parsed tag, id, and classes
135
+ - Moves the original element’s children into the replacement element
136
+ - Replaces the original element in the DOM
137
+ - If the selector’s tag is a custom element name (contains -) and that element is not registered, applyLayout replaces it with an error element (TjErrorElement) to prevent infinite recursion and to make the issue visible in the DOM.
138
+
139
+ Options:
140
+ - recursive (default: true): apply the transformation to descendants as well.
141
+
142
+ Return value:
143
+ - An array of HTMLElements that were processed or produced by replacements.
144
+
145
+ Example:
146
+ import { applyLayout } from '@trunkjs/content-pane';
147
+
148
+ const el = document.querySelector('#article')!;
149
+ applyLayout(el, { recursive: true });
150
+
151
+ <!-- Before -->
152
+ <div id="sidebar" layout="2;aside#right.aside-panel">
153
+ <p>Sidebar content</p>
154
+ </div>
155
+
156
+ <!-- After (conceptually) -->
157
+ <aside id="right" class="aside-panel" layout="2;aside#right.aside-panel">
158
+ <p>Sidebar content</p>
159
+ </aside>
160
+
161
+ Note: applyLayout preserves the original layout attribute on the replacement element so you can re-run the layout step if needed or inspect the intent.
162
+
163
+ ### Manual before-layout hook
164
+
165
+ If a custom element used as a replacement implements a beforeLayoutCallback(origElement, instance, children) method, it will be called before children are attached and layout is applied to descendants. Returning false from this method will skip recursive layout for that element, allowing it to manage its own internal layout.
166
+
167
+ Type signature:
168
+ interface ManualBeforeLayoutElement {
169
+ beforeLayoutCallback(origElement: HTMLElement, instance: this, children: Element[]): void | boolean;
170
+ }
171
+
172
+ ## attrAssign
173
+
174
+ Utility to assign attributes to multiple selected descendants using a simple multi-selector separated by |.
175
+
176
+ Signature:
177
+ attrAssign(element: HTMLElement, multiQuerySelector: string, attributes: Record<string, string>): HTMLElement[]
178
+
179
+ - multiQuerySelector example: ':scope > .aside | :scope > *:has(img)'
180
+ - Returns an array of HTMLElements that received the attributes.
181
+
182
+ Example:
183
+ import { attrAssign } from '@trunkjs/content-pane';
184
+
185
+ const host = document.querySelector('section')!;
186
+ attrAssign(host, ':scope > img | :scope > figure', { slot: 'media' });
187
+
188
+ ## Styling the generated structure
189
+
190
+ Because <content-area2> does not use Shadow DOM, you can style:
191
+ - section wrappers produced by SectionTreeBuilder
192
+ - any elements produced by applyLayout
193
+ - your original content elements
194
+
195
+ Typical patterns:
196
+ - Target sections based on heading-derived structure
197
+ - Use IDs and classes assigned via layout or attrAssign
198
+ - Combine with CSS container queries for responsive layouts
199
+
200
+ ## Notes and caveats
201
+
202
+ - H1 is treated as H2 (i = 20) to keep top-level content consistent.
203
+ - HR becomes a divider at half-steps (+5) between major heading levels.
204
+ - The package assumes you will either:
205
+ - Use <content-area2>, which orchestrates both steps, or
206
+ - Manually call SectionTreeBuilder then applyLayout in that order.
207
+ - If you reference an unregistered custom element in a layout selector, an error element is inserted to make the problem visible and to avoid infinite recursion.
208
+
209
+ ## Building
210
+
211
+ Run `nx build content-pane` to build the library.
212
+
213
+ ## Running unit tests
214
+
215
+ Run `nx test content-pane` to execute the unit tests via [Vitest](https://vitest.dev/).
@@ -0,0 +1,16 @@
1
+ import { ReactiveElement } from 'lit';
2
+ declare const ContentAreaElement2_base: (abstract new (...args: any[]) => {
3
+ "__#2364@#debugCached": boolean | null;
4
+ invalidateDebugCache(): void;
5
+ readonly _debug: boolean | null;
6
+ log(...args: any[]): void;
7
+ warn(...args: any[]): void;
8
+ error(...args: any[]): void;
9
+ }) & typeof ReactiveElement;
10
+ export declare class ContentAreaElement2 extends ContentAreaElement2_base {
11
+ static get is(): string;
12
+ createRenderRoot(): HTMLElement | DocumentFragment;
13
+ constructor();
14
+ connectedCallback(): Promise<void>;
15
+ }
16
+ export {};
@@ -0,0 +1,9 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class TjErrorElement extends LitElement {
3
+ static styles: import('lit').CSSResult[];
4
+ private originalCode?;
5
+ private message;
6
+ static get is(): string;
7
+ constructor(message?: string, originalCode?: string);
8
+ render(): import('lit-html').TemplateResult<1>;
9
+ }
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './components/tj-content-pane/TjContentPane';
2
+ export * from './lib/apply-layout';
3
+ export * from './lib/attrAssign';
package/index.js ADDED
@@ -0,0 +1,219 @@
1
+ var C = Object.defineProperty;
2
+ var E = (n, t, r) => t in n ? C(n, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : n[t] = r;
3
+ var h = (n, t, r) => E(n, typeof t != "symbol" ? t + "" : t, r);
4
+ import { unsafeCSS as A, LitElement as N, html as _, ReactiveElement as L } from "lit";
5
+ import { property as k, customElement as x } from "lit/decorators.js";
6
+ import { create_element as v, LoggingMixin as I, Stopwatch as T, waitForDomContentLoaded as P } from "@trunkjs/browser-utils";
7
+ class S {
8
+ constructor(t, r = !1) {
9
+ h(this, "rootNode");
10
+ h(this, "currentContainerNode", null);
11
+ h(this, "containerPath", []);
12
+ h(this, "containerIndex", [0]);
13
+ h(this, "lastFixedI", 20);
14
+ this.debug = r, this.currentContainerNode = this.rootNode = t, this.containerPath.push(this.rootNode);
15
+ }
16
+ getI(t) {
17
+ const r = t.tagName, o = t.getAttribute("layout"), e = { i: -99, variant: "new", tag: "hr", hi: null };
18
+ if (o) {
19
+ const s = /^(\+|-|)([0-9]\.?[0-9]?|)(;|$)/, i = o.match(s);
20
+ i && (console.debug("Layout matches", i), e.variant = i[1] === "+" ? "append" : i[1] === "-" ? "skip" : "new", i[2] !== "" && (e.i = parseFloat(i[2]) * 10));
21
+ }
22
+ if (r.startsWith("H") && r.length === 2) {
23
+ let s = r.substring(1);
24
+ return e.tag = "h", e.hi = parseInt(s), s === "1" && (s = "2"), e.i === -99 && (e.i = parseInt(s) * 10, this.lastFixedI = e.i), e;
25
+ }
26
+ return e.i === -99 && r === "HR" ? (e.i = this.lastFixedI + 5, e) : null;
27
+ }
28
+ getAttributeRecords(t, r = !1) {
29
+ const o = {};
30
+ for (const e of t.attributes)
31
+ e.name.startsWith("section-") ? o[e.name] = e.value.replace(/^section-/, "") : e.name.startsWith("layout") ? (o[e.name] = e.value, t.removeAttribute(e.name)) : r && (o[e.name] = e.value);
32
+ if (!r)
33
+ for (const e of Array.from(t.classList))
34
+ e.startsWith("section-") && (o.class = (o.class || "") + " " + e.replace(/^section-/, ""), t.classList.remove(e));
35
+ return o;
36
+ }
37
+ createNewContainerNode(t, r) {
38
+ const o = this.getAttributeRecords(t, t.tagName === "HR"), e = v("section", o);
39
+ return e.__IT = r, e;
40
+ }
41
+ arrangeSingleNode(t, r) {
42
+ r.i;
43
+ let o = 0;
44
+ for (o = 0; o < this.containerIndex.length && !(this.containerIndex[o] >= r.i); o++)
45
+ ;
46
+ let e = null;
47
+ r.variant === "append" ? (console.log("Appending to container at index", o, "with i", r.i), e = this.containerPath[o]) : e = this.createNewContainerNode(t, r);
48
+ const s = this.containerPath[o - 1];
49
+ this.containerPath.length = o, this.containerIndex.length = o, e.appendChild(t), s.appendChild(e), this.containerPath.push(e), this.containerIndex.push(r.i), this.currentContainerNode = e;
50
+ }
51
+ appendToCurrentContainer(t) {
52
+ if (this.currentContainerNode === null)
53
+ throw new Error("No current container node set");
54
+ this.currentContainerNode.appendChild(t);
55
+ }
56
+ arrange(t) {
57
+ for (let r of t) {
58
+ if (r.nodeType !== Node.ELEMENT_NODE) {
59
+ this.appendToCurrentContainer(r);
60
+ continue;
61
+ }
62
+ const o = r, e = this.getI(o);
63
+ if (!e || e.variant === "skip") {
64
+ this.appendToCurrentContainer(r);
65
+ continue;
66
+ }
67
+ this.arrangeSingleNode(o, e);
68
+ }
69
+ }
70
+ }
71
+ const $ = ":host{--border-color: red;--background-color: lightgray;font-family:Arial,sans-serif}#error-fixed-indicator{position:fixed;top:10px;right:10px;cursor:pointer;z-index:100000;padding:5px 10px;width:auto;max-width:90vw;min-width:100px;height:auto;box-shadow:0 4px 8px #0003;border:5px solid white;color:#fff;background-color:red;animation:blink 1s infinite;border-radius:15px;font-size:20px;font-weight:700;font-family:Arial,sans-serif}@keyframes blink{0%,to{background-color:#000}50%{background-color:red}}#error{background-color:var(--background-color);border:3px solid var(--border-color);padding:10px;margin:10px;border-radius:5px}h1{color:red;font-size:24px;margin:0}.error-details{font-size:14px;max-height:200px;overflow:auto}";
72
+ var w = Object.defineProperty, j = Object.getOwnPropertyDescriptor, O = (n, t, r) => t in n ? w(n, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : n[t] = r, y = (n, t, r, o) => {
73
+ for (var e = o > 1 ? void 0 : o ? j(t, r) : t, s = n.length - 1, i; s >= 0; s--)
74
+ (i = n[s]) && (e = (o ? i(t, r, e) : i(e)) || e);
75
+ return o && e && w(t, r, e), e;
76
+ }, R = (n, t, r) => O(n, t + "", r);
77
+ let p = class extends N {
78
+ constructor(t = "An error occurred", r) {
79
+ super();
80
+ h(this, "originalCode");
81
+ h(this, "message");
82
+ this.message = t, this.originalCode = r;
83
+ }
84
+ static get is() {
85
+ return "tj-error-element";
86
+ }
87
+ render() {
88
+ return _`
89
+ <div id="error-fixed-indicator" @click=${() => this.scrollIntoView({ behavior: "smooth" })}>
90
+ Err: ${this.message}
91
+ </div>
92
+ <div id="error">
93
+ <h1>Error: ${this.message}</h1>
94
+ <pre class="error-details">
95
+ ${this.originalCode ? this.originalCode : "No code provided."}
96
+ </pre
97
+ >
98
+
99
+ <slot></slot>
100
+ </div>
101
+ `;
102
+ }
103
+ };
104
+ R(p, "styles", [A($)]);
105
+ y([
106
+ k({ type: String, reflect: !0 })
107
+ ], p.prototype, "message", 2);
108
+ p = y([
109
+ x("tj-error-element")
110
+ ], p);
111
+ function D(n, { allowAttributes: t = !0, ignoreGaps: r = !0 } = {}) {
112
+ let o = "div", e = null, s = [], i = [], l = {};
113
+ const c = /(^[a-z][\w-]*)|#[\w-]+|\.[\w:-]+|\[\s*([\w-]+)(?:\s*=\s*(['"]?)(.*?)\3)?\s*\]/gi;
114
+ let a = 0;
115
+ for (; ; ) {
116
+ const u = c.exec(n);
117
+ if (!u || u.index !== a) {
118
+ if (!r && u && u.index > a)
119
+ break;
120
+ break;
121
+ }
122
+ const d = u[0];
123
+ if (d[0] === "#") e = d.slice(1);
124
+ else if (d[0] === ".") s.push(d.slice(1));
125
+ else if (d[0] === "[") {
126
+ if (!t) throw new Error(`Attributes not allowed: '${d}'`);
127
+ const g = u[2], m = u[4] || void 0;
128
+ i.push({ name: g, value: m }), l[g] = m;
129
+ } else o = d;
130
+ a += d.length;
131
+ }
132
+ return { tag: o, id: e, classes: s, attrs: i, attrsMap: l, length: a, rest: n.slice(a) };
133
+ }
134
+ function M(n) {
135
+ return typeof n.beforeLayoutCallback == "function";
136
+ }
137
+ function F(n, t, r) {
138
+ console.log("Applying layout to element:", n, "with layout:", r);
139
+ const o = /^(\+|-|)([0-9]+\.?[0-9]*);?/, e = r.replace(o, ""), s = D(e);
140
+ let i = {};
141
+ i.class !== void 0 && (i.class += " "), i.class += s.classes.join(" "), i.id = s.id;
142
+ let l = s.tag || "div", c = !1, a = v(l, { ...i, layoutOrig: r });
143
+ if (l.includes("-") && !customElements.get(l))
144
+ console.warn(`Custom element <${l}> is not registered.`), a = new p(`Custom element <${l}> is not registered.`, n.outerHTML), n.replaceWith(a), a.append(n), c = !0;
145
+ else {
146
+ let u = Array.from(n.children);
147
+ M(a) && (c = a.beforeLayoutCallback(n, a, u) === !1), console.log(
148
+ "Replacement element created:",
149
+ a,
150
+ "with children:",
151
+ u,
152
+ "skipChildren:",
153
+ c
154
+ ), a.__ORIG_ELEMENT__ = n, a.append(...Array.from(n.children)), n.replaceWith(a);
155
+ }
156
+ return {
157
+ replacementElement: a,
158
+ skipChildren: c
159
+ };
160
+ }
161
+ function f(n, t = {}) {
162
+ console.log("applyLayout called with element:", n, "and options:", t);
163
+ const { recursive: r = !0 } = t;
164
+ let o = [];
165
+ if (Array.isArray(n))
166
+ return n.forEach((l) => o.push(...f(l, t))), o;
167
+ if (!(n instanceof HTMLElement))
168
+ return [];
169
+ const e = n.getAttribute("layout");
170
+ let s = !1, i = n;
171
+ if (e && ({ replacementElement: i, skipChildren: s } = F(n, t, e)), r && !s) {
172
+ const l = Array.from(i.children);
173
+ console.log("Applying layout to children:", l, "of element:", i), l.forEach((c) => o.push(...f(c, t)));
174
+ }
175
+ return o;
176
+ }
177
+ var W = Object.getOwnPropertyDescriptor, z = (n, t, r, o) => {
178
+ for (var e = o > 1 ? void 0 : o ? W(t, r) : t, s = n.length - 1, i; s >= 0; s--)
179
+ (i = n[s]) && (e = i(e) || e);
180
+ return e;
181
+ };
182
+ let b = class extends I(L) {
183
+ static get is() {
184
+ return "tj-content-pane";
185
+ }
186
+ createRenderRoot() {
187
+ return this;
188
+ }
189
+ constructor() {
190
+ super();
191
+ }
192
+ async connectedCallback() {
193
+ const n = new T("SectionTreeBuilder");
194
+ await P(), super.connectedCallback();
195
+ const t = new S(this), r = Array.from(this.children);
196
+ t.arrange(r), f(Array.from(this.children), { recursive: !0 }), n.lap("after arrange");
197
+ }
198
+ };
199
+ b = z([
200
+ x("tj-content-pane")
201
+ ], b);
202
+ function V(n, t, r) {
203
+ const o = [], e = t.split("|");
204
+ for (const s of e) {
205
+ const i = n.querySelectorAll(s.trim());
206
+ if (i.length > 0)
207
+ for (const l of i) {
208
+ o.push(l);
209
+ for (const [c, a] of Object.entries(r))
210
+ l.setAttribute(c, a);
211
+ }
212
+ }
213
+ return o;
214
+ }
215
+ export {
216
+ b as ContentAreaElement2,
217
+ f as applyLayout,
218
+ V as attrAssign
219
+ };
@@ -0,0 +1,28 @@
1
+ export type IType = {
2
+ /**
3
+ * Number 10 - 60 - 2.5 -> 25
4
+ */
5
+ i: number;
6
+ variant: "append" | "new" | "skip";
7
+ tag: "hr" | "h";
8
+ hi?: number | null;
9
+ };
10
+ export interface SectionTreeElement {
11
+ __IT: IType;
12
+ }
13
+ export declare function isSectionTreeElement(obj: any): obj is SectionTreeElement;
14
+ export declare class SectionTreeBuilder {
15
+ debug: boolean;
16
+ private rootNode;
17
+ private currentContainerNode;
18
+ private containerPath;
19
+ private containerIndex;
20
+ constructor(rootNode: HTMLElement, debug?: boolean);
21
+ private lastFixedI;
22
+ private getI;
23
+ protected getAttributeRecords(originalNode: HTMLElement, isHR?: boolean): Record<string, string>;
24
+ protected createNewContainerNode(originalNode: HTMLElement, it: IType): HTMLElement;
25
+ protected arrangeSingleNode(node: HTMLElement, it: IType): void;
26
+ private appendToCurrentContainer;
27
+ arrange(nodes: Node[]): void;
28
+ }
@@ -0,0 +1,28 @@
1
+ type ApplyLayoutOptions = {
2
+ recursive?: boolean;
3
+ };
4
+ export interface ManualBeforeLayoutElement {
5
+ /**
6
+ * This callback is called before the layout is applied to the element. So the orgiginal element is still attached to the DOM
7
+ * and all its children are still present.
8
+ *
9
+ * You should only
10
+ *
11
+ * If the callback returns `false`, the layout engine will not apply the layout to child elements, effectively skipping the layout for this element.
12
+ * In this case, the element has to handle applyLayout() itself, or it will not be applied at all.
13
+ *
14
+ * It can be used to modify the element or its children before the layout is applied.
15
+ * @param origElement The original element that is being replaced
16
+ * @param instance The new element that is being created
17
+ * @param children The children of the original element
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * beforeLayoutCallback(origElement, instance, elements) {
22
+ * // Modify the instance or its children before the layout is applied
23
+ * attrAssign(origElement, ":scope > .aside | :scope > *:has(img)", {slot: "aside"});
24
+ */
25
+ beforeLayoutCallback(origElement: HTMLElement, instance: this, children: Element[]): void | boolean;
26
+ }
27
+ export declare function applyLayout(element: HTMLElement | Element | Element[], options?: ApplyLayoutOptions): HTMLElement[];
28
+ export {};
@@ -0,0 +1 @@
1
+ export declare function attrAssign(element: HTMLElement, multiQuerySelector: string, attributes: Record<string, string>): HTMLElement[];
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@trunkjs/content-pane",
3
+ "version": "1.0.0",
4
+ "main": "./index.js",
5
+ "dependencies": {
6
+ "@trunkjs/browser-utils": "^1.0.12",
7
+ "lit": "^3.3.1"
8
+ },
9
+ "type": "module",
10
+ "types": "./index.d.ts"
11
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Parse simple CSS selector prefix from start of string
3
+ * - Tag optional (defaults to div)
4
+ * - Supports #id, multiple .classes (with - and :)
5
+ * - Optional attribute parsing; throws if disabled and attribute found
6
+ * - Returns attrs array + attrsMap for direct lookup
7
+ * - Returns rest of string that was not parsed
8
+ *
9
+ * @param sel Selector string
10
+ * @param options { allowAttributes?: boolean, ignoreGaps?: boolean }
11
+ *
12
+ * @example
13
+ * parseSelector('div.main#app.other[data-id=123] foo', { ignoreGaps: false })
14
+ * // {
15
+ * // tag:'div', id:'app', classes:['main','other'],
16
+ * // attrs:[{name:'data-id',value:'123'}],
17
+ * // attrsMap:{'data-id':'123'},
18
+ * // length:29, rest:' foo'
19
+ * // }
20
+ */
21
+ export declare function parseSelector(sel: string, { allowAttributes, ignoreGaps }?: {
22
+ allowAttributes?: boolean | undefined;
23
+ ignoreGaps?: boolean | undefined;
24
+ }): {
25
+ tag: string;
26
+ id: string | null;
27
+ classes: string[];
28
+ attrs: {
29
+ name: string;
30
+ value?: string;
31
+ }[];
32
+ attrsMap: Record<string, string | undefined>;
33
+ length: number;
34
+ rest: string;
35
+ };