mtrl-addons 0.2.1 → 0.2.3
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/{src/components/index.ts → dist/components/index.d.ts} +0 -2
- package/dist/components/vlist/config.d.ts +86 -0
- package/{src/components/vlist/constants.ts → dist/components/vlist/constants.d.ts} +10 -11
- package/dist/components/vlist/features/api.d.ts +7 -0
- package/{src/components/vlist/features/index.ts → dist/components/vlist/features/index.d.ts} +0 -2
- package/dist/components/vlist/features/selection.d.ts +6 -0
- package/dist/components/vlist/features/viewport.d.ts +9 -0
- package/dist/components/vlist/features.d.ts +31 -0
- package/{src/components/vlist/index.ts → dist/components/vlist/index.d.ts} +1 -9
- package/dist/components/vlist/types.d.ts +596 -0
- package/dist/components/vlist/vlist.d.ts +29 -0
- package/dist/core/compose/features/gestures/index.d.ts +86 -0
- package/dist/core/compose/features/gestures/longpress.d.ts +85 -0
- package/dist/core/compose/features/gestures/pan.d.ts +108 -0
- package/dist/core/compose/features/gestures/pinch.d.ts +111 -0
- package/dist/core/compose/features/gestures/rotate.d.ts +111 -0
- package/dist/core/compose/features/gestures/swipe.d.ts +149 -0
- package/dist/core/compose/features/gestures/tap.d.ts +79 -0
- package/{src/core/compose/features/index.ts → dist/core/compose/features/index.d.ts} +1 -2
- package/{src/core/compose/index.ts → dist/core/compose/index.d.ts} +2 -11
- package/{src/core/gestures/index.ts → dist/core/gestures/index.d.ts} +1 -20
- package/dist/core/gestures/longpress.d.ts +23 -0
- package/dist/core/gestures/manager.d.ts +14 -0
- package/dist/core/gestures/pan.d.ts +12 -0
- package/dist/core/gestures/pinch.d.ts +14 -0
- package/dist/core/gestures/rotate.d.ts +14 -0
- package/dist/core/gestures/swipe.d.ts +20 -0
- package/dist/core/gestures/tap.d.ts +12 -0
- package/dist/core/gestures/types.d.ts +320 -0
- package/dist/core/gestures/utils.d.ts +57 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/layout/config.d.ts +33 -0
- package/dist/core/layout/index.d.ts +51 -0
- package/dist/core/layout/jsx.d.ts +65 -0
- package/dist/core/layout/schema.d.ts +112 -0
- package/dist/core/layout/types.d.ts +69 -0
- package/dist/core/viewport/constants.d.ts +105 -0
- package/dist/core/viewport/features/base.d.ts +14 -0
- package/dist/core/viewport/features/collection.d.ts +41 -0
- package/dist/core/viewport/features/events.d.ts +13 -0
- package/{src/core/viewport/features/index.ts → dist/core/viewport/features/index.d.ts} +0 -7
- package/dist/core/viewport/features/item-size.d.ts +30 -0
- package/dist/core/viewport/features/loading.d.ts +34 -0
- package/dist/core/viewport/features/momentum.d.ts +17 -0
- package/dist/core/viewport/features/performance.d.ts +53 -0
- package/dist/core/viewport/features/placeholders.d.ts +38 -0
- package/dist/core/viewport/features/rendering.d.ts +16 -0
- package/dist/core/viewport/features/scrollbar.d.ts +26 -0
- package/dist/core/viewport/features/scrolling.d.ts +16 -0
- package/dist/core/viewport/features/utils.d.ts +43 -0
- package/dist/core/viewport/features/virtual.d.ts +18 -0
- package/{src/core/viewport/index.ts → dist/core/viewport/index.d.ts} +1 -17
- package/dist/core/viewport/types.d.ts +96 -0
- package/dist/core/viewport/utils/speed-tracker.d.ts +22 -0
- package/dist/core/viewport/viewport.d.ts +11 -0
- package/{src/index.ts → dist/index.d.ts} +0 -4
- package/dist/index.js +5143 -0
- package/dist/index.mjs +5111 -0
- package/dist/styles.css +254 -0
- package/dist/styles.css.map +1 -0
- package/package.json +6 -1
- package/src/styles/components/_vlist.scss +234 -213
- package/.cursorrules +0 -117
- package/AI.md +0 -241
- package/build.js +0 -201
- package/scripts/analyze-orphaned-functions.ts +0 -387
- package/scripts/debug/vlist-selection.ts +0 -121
- package/src/components/vlist/config.ts +0 -323
- package/src/components/vlist/features/api.ts +0 -322
- package/src/components/vlist/features/selection.ts +0 -444
- package/src/components/vlist/features/viewport.ts +0 -65
- package/src/components/vlist/features.ts +0 -112
- package/src/components/vlist/types.ts +0 -591
- package/src/components/vlist/vlist.ts +0 -92
- package/src/core/compose/features/gestures/index.ts +0 -227
- package/src/core/compose/features/gestures/longpress.ts +0 -383
- package/src/core/compose/features/gestures/pan.ts +0 -424
- package/src/core/compose/features/gestures/pinch.ts +0 -475
- package/src/core/compose/features/gestures/rotate.ts +0 -485
- package/src/core/compose/features/gestures/swipe.ts +0 -492
- package/src/core/compose/features/gestures/tap.ts +0 -334
- package/src/core/gestures/longpress.ts +0 -68
- package/src/core/gestures/manager.ts +0 -418
- package/src/core/gestures/pan.ts +0 -48
- package/src/core/gestures/pinch.ts +0 -58
- package/src/core/gestures/rotate.ts +0 -58
- package/src/core/gestures/swipe.ts +0 -66
- package/src/core/gestures/tap.ts +0 -45
- package/src/core/gestures/types.ts +0 -387
- package/src/core/gestures/utils.ts +0 -128
- package/src/core/index.ts +0 -43
- package/src/core/layout/config.ts +0 -102
- package/src/core/layout/index.ts +0 -168
- package/src/core/layout/jsx.ts +0 -174
- package/src/core/layout/schema.ts +0 -1001
- package/src/core/layout/types.ts +0 -95
- package/src/core/viewport/constants.ts +0 -140
- package/src/core/viewport/features/base.ts +0 -73
- package/src/core/viewport/features/collection.ts +0 -882
- package/src/core/viewport/features/events.ts +0 -130
- package/src/core/viewport/features/item-size.ts +0 -271
- package/src/core/viewport/features/loading.ts +0 -263
- package/src/core/viewport/features/momentum.ts +0 -260
- package/src/core/viewport/features/performance.ts +0 -161
- package/src/core/viewport/features/placeholders.ts +0 -335
- package/src/core/viewport/features/rendering.ts +0 -568
- package/src/core/viewport/features/scrollbar.ts +0 -434
- package/src/core/viewport/features/scrolling.ts +0 -618
- package/src/core/viewport/features/utils.ts +0 -88
- package/src/core/viewport/features/virtual.ts +0 -384
- package/src/core/viewport/types.ts +0 -133
- package/src/core/viewport/utils/speed-tracker.ts +0 -79
- package/src/core/viewport/viewport.ts +0 -246
- package/test/benchmarks/layout/advanced.test.ts +0 -656
- package/test/benchmarks/layout/comparison.test.ts +0 -519
- package/test/benchmarks/layout/performance-comparison.test.ts +0 -274
- package/test/benchmarks/layout/real-components.test.ts +0 -733
- package/test/benchmarks/layout/simple.test.ts +0 -321
- package/test/benchmarks/layout/stress.test.ts +0 -990
- package/test/collection/basic.test.ts +0 -304
- package/test/components/vlist-selection.test.ts +0 -240
- package/test/components/vlist.test.ts +0 -63
- package/test/core/collection/adapter.test.ts +0 -161
- package/test/core/collection/collection.test.ts +0 -394
- package/test/core/layout/layout.test.ts +0 -201
- package/test/utils/dom-helpers.ts +0 -275
- package/test/utils/performance-helpers.ts +0 -392
- package/tsconfig.json +0 -20
|
@@ -1,1001 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module core/layout/schema
|
|
3
|
-
* @description Unified layout schema processor with integrated optimizations
|
|
4
|
-
* Consolidates array, object, JSX, and template processing with built-in performance enhancements
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// ============================================================================
|
|
8
|
-
// TYPES (Essential only)
|
|
9
|
-
// ============================================================================
|
|
10
|
-
|
|
11
|
-
export interface LayoutConfig {
|
|
12
|
-
/** Base layout type */
|
|
13
|
-
type?: "stack" | "row" | "grid" | string;
|
|
14
|
-
/** Spacing between elements */
|
|
15
|
-
gap?: number | string;
|
|
16
|
-
/** Additional CSS classes */
|
|
17
|
-
class?: string;
|
|
18
|
-
/** Alignment of items along the cross axis */
|
|
19
|
-
align?: "start" | "center" | "end" | "stretch";
|
|
20
|
-
/** Alignment of items along the main axis */
|
|
21
|
-
justify?: "start" | "center" | "end" | "between" | "around" | "evenly";
|
|
22
|
-
/** Whether and how items should wrap */
|
|
23
|
-
wrap?: boolean | "reverse" | "nowrap";
|
|
24
|
-
/** Whether row items should stack vertically on mobile */
|
|
25
|
-
mobileStack?: boolean;
|
|
26
|
-
/** Whether row items should scroll horizontally on mobile */
|
|
27
|
-
mobileScroll?: boolean;
|
|
28
|
-
/** Number of columns or automatic sizing method */
|
|
29
|
-
columns?: number | "auto-fit" | "auto-fill";
|
|
30
|
-
/** Whether to use dense packing algorithm for grid */
|
|
31
|
-
dense?: boolean;
|
|
32
|
-
/** Whether grid items should adjust height automatically */
|
|
33
|
-
autoHeight?: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface LayoutItemConfig {
|
|
37
|
-
/** Column width in a 12-column grid */
|
|
38
|
-
width?: number;
|
|
39
|
-
/** Width on small screens */
|
|
40
|
-
sm?: number;
|
|
41
|
-
/** Width on medium screens */
|
|
42
|
-
md?: number;
|
|
43
|
-
/** Width on large screens */
|
|
44
|
-
lg?: number;
|
|
45
|
-
/** Width on extra-large screens */
|
|
46
|
-
xl?: number;
|
|
47
|
-
/** Number of grid columns to span */
|
|
48
|
-
span?: number;
|
|
49
|
-
/** Number of grid rows to span */
|
|
50
|
-
rowSpan?: number;
|
|
51
|
-
/** Display order */
|
|
52
|
-
order?: number | "first" | "last";
|
|
53
|
-
/** Self-alignment within container */
|
|
54
|
-
align?: "start" | "center" | "end" | "stretch";
|
|
55
|
-
/** Whether item should automatically size */
|
|
56
|
-
auto?: boolean;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface LayoutOptions {
|
|
60
|
-
/** Default creator function to use if not specified in schema */
|
|
61
|
-
creator?: Function;
|
|
62
|
-
/** Whether to apply CSS class prefix @default true */
|
|
63
|
-
prefix?: boolean;
|
|
64
|
-
/** Additional options */
|
|
65
|
-
[key: string]: any;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface LayoutResult {
|
|
69
|
-
/** The raw layout object with all components */
|
|
70
|
-
layout: Record<string, any>;
|
|
71
|
-
/** Reference to the root element for convenience */
|
|
72
|
-
element: HTMLElement | any;
|
|
73
|
-
/** Flattened component map */
|
|
74
|
-
component: Record<string, any>;
|
|
75
|
-
/** Gets a component by name */
|
|
76
|
-
get(name: string): any;
|
|
77
|
-
/** Gets all components in a flattened map */
|
|
78
|
-
getAll(): Record<string, any>;
|
|
79
|
-
/** Destroys the layout, cleaning up all components */
|
|
80
|
-
destroy(): void;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
type ComponentLike = { element: HTMLElement; [key: string]: any };
|
|
84
|
-
type SchemaItem = Function | string | Record<string, any> | SchemaItem[];
|
|
85
|
-
|
|
86
|
-
// ============================================================================
|
|
87
|
-
// BUILT-IN OPTIMIZATIONS
|
|
88
|
-
// ============================================================================
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Fragment Pool for efficient DocumentFragment reuse
|
|
92
|
-
* Reduces GC pressure in high-frequency layout creation scenarios
|
|
93
|
-
*/
|
|
94
|
-
class FragmentPool {
|
|
95
|
-
private pool: DocumentFragment[] = [];
|
|
96
|
-
private maxSize = 8; // Optimized size for memory efficiency
|
|
97
|
-
|
|
98
|
-
get(): DocumentFragment {
|
|
99
|
-
return this.pool.pop() || document.createDocumentFragment();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
release(fragment: DocumentFragment): void {
|
|
103
|
-
if (this.pool.length < this.maxSize && fragment.childNodes.length === 0) {
|
|
104
|
-
this.pool.push(fragment);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
clear(): void {
|
|
109
|
-
this.pool.length = 0;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const fragmentPool = new FragmentPool();
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Class name cache for layout configurations
|
|
117
|
-
* Reduces string operations by caching generated class names
|
|
118
|
-
*/
|
|
119
|
-
const classCache = new Map<string, string>();
|
|
120
|
-
|
|
121
|
-
// Configuration constants
|
|
122
|
-
const PREFIX = "mtrl"; // TODO: Make this configurable
|
|
123
|
-
const PREFIX_WITH_DASH = `${PREFIX}-`;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Gets a cached class name for layout configuration
|
|
127
|
-
* Optimized for different class naming patterns
|
|
128
|
-
*/
|
|
129
|
-
function getCachedClassName(
|
|
130
|
-
type: string,
|
|
131
|
-
property: string,
|
|
132
|
-
value: string | number
|
|
133
|
-
): string {
|
|
134
|
-
const key = `${type}-${property}-${value}`;
|
|
135
|
-
if (!classCache.has(key)) {
|
|
136
|
-
if (type === "item") {
|
|
137
|
-
classCache.set(
|
|
138
|
-
key,
|
|
139
|
-
property === ""
|
|
140
|
-
? `layout__item--${value}`
|
|
141
|
-
: `layout__item--${property}-${value}`
|
|
142
|
-
);
|
|
143
|
-
} else {
|
|
144
|
-
// For layout classes, align uses layout--{type}-{value}
|
|
145
|
-
// but justify and others use layout--{type}-{property}-{value}
|
|
146
|
-
if (property === "align") {
|
|
147
|
-
classCache.set(key, `layout--${type}-${value}`);
|
|
148
|
-
} else {
|
|
149
|
-
classCache.set(key, `layout--${type}-${property}-${value}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return classCache.get(key)!;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ============================================================================
|
|
157
|
-
// OPTIMIZED UTILITIES
|
|
158
|
-
// ============================================================================
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Checks if a value is a component object (has an element property)
|
|
162
|
-
*/
|
|
163
|
-
function isComponent(value: any): value is ComponentLike {
|
|
164
|
-
return value && typeof value === "object" && "element" in value;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Creates a document fragment using pooling for better performance
|
|
169
|
-
*/
|
|
170
|
-
function createFragment(): DocumentFragment {
|
|
171
|
-
return fragmentPool.get();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Releases a fragment back to the pool for reuse
|
|
176
|
-
*/
|
|
177
|
-
function releaseFragment(fragment: DocumentFragment): void {
|
|
178
|
-
fragmentPool.release(fragment);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Optimized class processing with minimal string operations
|
|
183
|
-
* Handles arrays, strings, className aliases, and rawClass efficiently
|
|
184
|
-
*/
|
|
185
|
-
function processClassNames(
|
|
186
|
-
options: Record<string, any>,
|
|
187
|
-
skipPrefix = false
|
|
188
|
-
): Record<string, any> {
|
|
189
|
-
if (!options) return options;
|
|
190
|
-
|
|
191
|
-
const hasRawClass = options.rawClass;
|
|
192
|
-
const hasRegularClass = options.class || options.className;
|
|
193
|
-
|
|
194
|
-
// Fast path: no class properties at all
|
|
195
|
-
if (!hasRawClass && !hasRegularClass) return options;
|
|
196
|
-
|
|
197
|
-
// Fast path: only rawClass and skipping prefix (most common rawClass scenario)
|
|
198
|
-
if (hasRawClass && !hasRegularClass && skipPrefix) {
|
|
199
|
-
const processed = { ...options };
|
|
200
|
-
delete processed.rawClass;
|
|
201
|
-
|
|
202
|
-
// Direct assignment for simple string
|
|
203
|
-
if (typeof hasRawClass === "string") {
|
|
204
|
-
processed.class = hasRawClass;
|
|
205
|
-
} else {
|
|
206
|
-
// Handle array case
|
|
207
|
-
processed.class = hasRawClass.join(" ");
|
|
208
|
-
}
|
|
209
|
-
return processed;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Full processing path (only when needed)
|
|
213
|
-
const processed = { ...options };
|
|
214
|
-
let finalClasses = "";
|
|
215
|
-
|
|
216
|
-
// Handle prefixed classes only if not skipping prefix
|
|
217
|
-
if (!skipPrefix && hasRegularClass) {
|
|
218
|
-
let prefixedString = "";
|
|
219
|
-
|
|
220
|
-
if (processed.class) {
|
|
221
|
-
prefixedString += Array.isArray(processed.class)
|
|
222
|
-
? processed.class.join(" ")
|
|
223
|
-
: processed.class;
|
|
224
|
-
}
|
|
225
|
-
if (processed.className) {
|
|
226
|
-
prefixedString += (prefixedString ? " " : "") + processed.className;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (prefixedString) {
|
|
230
|
-
finalClasses = prefixedString
|
|
231
|
-
.split(/\s+/)
|
|
232
|
-
.filter(Boolean)
|
|
233
|
-
.map((cls) =>
|
|
234
|
-
cls.startsWith(PREFIX_WITH_DASH) ? cls : PREFIX_WITH_DASH + cls
|
|
235
|
-
)
|
|
236
|
-
.join(" ");
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Handle rawClass (always processed when present)
|
|
241
|
-
if (hasRawClass) {
|
|
242
|
-
const rawString = Array.isArray(hasRawClass)
|
|
243
|
-
? hasRawClass.filter(Boolean).join(" ")
|
|
244
|
-
: hasRawClass;
|
|
245
|
-
|
|
246
|
-
finalClasses += (finalClasses ? " " : "") + rawString;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (finalClasses) {
|
|
250
|
-
processed.class = finalClasses;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Clean up in one operation
|
|
254
|
-
delete processed.className;
|
|
255
|
-
delete processed.rawClass;
|
|
256
|
-
|
|
257
|
-
return processed;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Optimized parameter extraction for array schemas
|
|
262
|
-
* Reduces multiple array lookups with batch processing
|
|
263
|
-
*/
|
|
264
|
-
interface ExtractedParameters {
|
|
265
|
-
creator: Function;
|
|
266
|
-
name?: string;
|
|
267
|
-
options: Record<string, any>;
|
|
268
|
-
consumed: number;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function extractParameters(
|
|
272
|
-
schema: SchemaItem[],
|
|
273
|
-
startIndex: number,
|
|
274
|
-
defaultCreator: Function
|
|
275
|
-
): ExtractedParameters {
|
|
276
|
-
const items = schema.slice(startIndex, startIndex + 3);
|
|
277
|
-
let creator, name, options;
|
|
278
|
-
let consumed = 1;
|
|
279
|
-
|
|
280
|
-
const [first, second, third] = items;
|
|
281
|
-
|
|
282
|
-
if (typeof first === "function") {
|
|
283
|
-
creator = first;
|
|
284
|
-
if (typeof second === "string") {
|
|
285
|
-
name = second;
|
|
286
|
-
consumed = 2;
|
|
287
|
-
if (isObject(third)) {
|
|
288
|
-
options = third;
|
|
289
|
-
consumed = 3;
|
|
290
|
-
}
|
|
291
|
-
} else if (isObject(second)) {
|
|
292
|
-
options = second;
|
|
293
|
-
consumed = 2;
|
|
294
|
-
}
|
|
295
|
-
} else if (typeof first === "string") {
|
|
296
|
-
creator = defaultCreator;
|
|
297
|
-
name = first;
|
|
298
|
-
if (isObject(second)) {
|
|
299
|
-
options = second;
|
|
300
|
-
consumed = 2;
|
|
301
|
-
}
|
|
302
|
-
} else if (isObject(first)) {
|
|
303
|
-
creator = defaultCreator;
|
|
304
|
-
options = first;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
creator: creator || defaultCreator,
|
|
309
|
-
name,
|
|
310
|
-
options: (options || {}) as Record<string, any>,
|
|
311
|
-
consumed,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Simple object type check
|
|
317
|
-
*/
|
|
318
|
-
function isObject(value: any): boolean {
|
|
319
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// ============================================================================
|
|
323
|
-
// DOM UTILITIES (Simplified)
|
|
324
|
-
// ============================================================================
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Simple DOM element creation
|
|
328
|
-
*/
|
|
329
|
-
function createElement(options: Record<string, any> = {}): HTMLElement {
|
|
330
|
-
const tag = options.tag || "div";
|
|
331
|
-
const element = document.createElement(tag);
|
|
332
|
-
|
|
333
|
-
if (options.class) {
|
|
334
|
-
element.className = options.class;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (options.style) {
|
|
338
|
-
if (typeof options.style === "string") {
|
|
339
|
-
element.setAttribute("style", options.style);
|
|
340
|
-
} else if (typeof options.style === "object") {
|
|
341
|
-
Object.assign(element.style, options.style);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (options.textContent) {
|
|
346
|
-
element.textContent = options.textContent;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (options.text) {
|
|
350
|
-
element.textContent = options.text;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return element;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Adds a CSS class to an element
|
|
358
|
-
*/
|
|
359
|
-
function addClass(element: HTMLElement, className: string): void {
|
|
360
|
-
if (element && className) {
|
|
361
|
-
element.classList.add(className);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Checks if element has a CSS class
|
|
367
|
-
*/
|
|
368
|
-
function hasClass(element: HTMLElement, className: string): boolean {
|
|
369
|
-
return element && element.classList.contains(className);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// ============================================================================
|
|
373
|
-
// UNIFIED COMPONENT CREATION (with destructuring optimization)
|
|
374
|
-
// ============================================================================
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Creates a component instance with optimized option processing
|
|
378
|
-
* Uses destructuring for cleaner separation of concerns
|
|
379
|
-
*/
|
|
380
|
-
function createComponentInstance(
|
|
381
|
-
Component: any,
|
|
382
|
-
options: Record<string, any> = {}
|
|
383
|
-
): any {
|
|
384
|
-
try {
|
|
385
|
-
// Destructure special configs in one operation
|
|
386
|
-
const {
|
|
387
|
-
layout: layoutConfig,
|
|
388
|
-
layoutItem: layoutItemConfig,
|
|
389
|
-
style: styleConfig,
|
|
390
|
-
attributes: attributesConfig,
|
|
391
|
-
events: eventsConfig,
|
|
392
|
-
event, // Legacy support
|
|
393
|
-
...cleanOptions
|
|
394
|
-
} = options;
|
|
395
|
-
|
|
396
|
-
// Use events over event (events is preferred)
|
|
397
|
-
const finalEventsConfig = eventsConfig || event;
|
|
398
|
-
|
|
399
|
-
// If style is a string, always pass it through to the component
|
|
400
|
-
if (styleConfig && typeof styleConfig === "string") {
|
|
401
|
-
cleanOptions.style = styleConfig;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Create component
|
|
405
|
-
const isClass =
|
|
406
|
-
typeof Component === "function" &&
|
|
407
|
-
Object.getOwnPropertyDescriptor(Component, "prototype")?.writable ===
|
|
408
|
-
false;
|
|
409
|
-
|
|
410
|
-
const component = isClass
|
|
411
|
-
? new Component(cleanOptions)
|
|
412
|
-
: Component(cleanOptions);
|
|
413
|
-
|
|
414
|
-
// Apply configurations if component has element
|
|
415
|
-
if (component) {
|
|
416
|
-
const element =
|
|
417
|
-
component.element ||
|
|
418
|
-
(component instanceof HTMLElement ? component : null);
|
|
419
|
-
if (element) {
|
|
420
|
-
// Apply layout classes
|
|
421
|
-
if (layoutConfig) applyLayoutClasses(element, layoutConfig);
|
|
422
|
-
|
|
423
|
-
// Apply layout item classes
|
|
424
|
-
if (layoutItemConfig) applyLayoutItemClasses(element, layoutItemConfig);
|
|
425
|
-
|
|
426
|
-
// Apply style
|
|
427
|
-
if (styleConfig && typeof styleConfig === "object") {
|
|
428
|
-
Object.assign(element.style, styleConfig);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Apply attributes
|
|
432
|
-
if (attributesConfig && typeof attributesConfig === "object") {
|
|
433
|
-
for (const [key, value] of Object.entries(attributesConfig)) {
|
|
434
|
-
if (value !== undefined && value !== null) {
|
|
435
|
-
element.setAttribute(key, value.toString());
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Apply events
|
|
441
|
-
if (finalEventsConfig && typeof finalEventsConfig === "object") {
|
|
442
|
-
if (Array.isArray(finalEventsConfig)) {
|
|
443
|
-
for (const eventDef of finalEventsConfig) {
|
|
444
|
-
if (Array.isArray(eventDef) && eventDef.length >= 2) {
|
|
445
|
-
const [eventName, handler] = eventDef;
|
|
446
|
-
if (
|
|
447
|
-
typeof eventName === "string" &&
|
|
448
|
-
typeof handler === "function"
|
|
449
|
-
) {
|
|
450
|
-
element.addEventListener(eventName, handler);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
} else {
|
|
455
|
-
for (const [eventName, handler] of Object.entries(
|
|
456
|
-
finalEventsConfig
|
|
457
|
-
)) {
|
|
458
|
-
if (typeof handler === "function") {
|
|
459
|
-
element.addEventListener(eventName, handler);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
return component;
|
|
468
|
-
} catch (error) {
|
|
469
|
-
console.error("Error creating component instance:", error);
|
|
470
|
-
return document.createElement("div");
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// ============================================================================
|
|
475
|
-
// INTEGRATED LAYOUT CONFIGURATION (with caching)
|
|
476
|
-
// ============================================================================
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Applies layout classes based on configuration
|
|
480
|
-
* Uses integrated caching for optimal performance
|
|
481
|
-
*/
|
|
482
|
-
function applyLayoutClasses(
|
|
483
|
-
element: HTMLElement,
|
|
484
|
-
layoutConfig: LayoutConfig
|
|
485
|
-
): void {
|
|
486
|
-
if (!element || !layoutConfig) return;
|
|
487
|
-
|
|
488
|
-
// Apply base layout type
|
|
489
|
-
if (layoutConfig.type) {
|
|
490
|
-
addClass(element, `${PREFIX_WITH_DASH}layout--${layoutConfig.type}`);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Apply properties with caching
|
|
494
|
-
const layoutType = layoutConfig.type || getLayoutType(element);
|
|
495
|
-
if (layoutType) {
|
|
496
|
-
if (layoutConfig.gap !== undefined) {
|
|
497
|
-
addClass(
|
|
498
|
-
element,
|
|
499
|
-
PREFIX_WITH_DASH +
|
|
500
|
-
getCachedClassName(layoutType, "gap", layoutConfig.gap)
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
if (layoutConfig.align) {
|
|
504
|
-
addClass(
|
|
505
|
-
element,
|
|
506
|
-
PREFIX_WITH_DASH +
|
|
507
|
-
getCachedClassName(layoutType, "align", layoutConfig.align)
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
if (layoutConfig.justify) {
|
|
511
|
-
addClass(
|
|
512
|
-
element,
|
|
513
|
-
PREFIX_WITH_DASH +
|
|
514
|
-
getCachedClassName(layoutType, "justify", layoutConfig.justify)
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// Grid-specific properties
|
|
520
|
-
if (layoutConfig.type === "grid" || getLayoutType(element) === "grid") {
|
|
521
|
-
if (typeof layoutConfig.columns === "number") {
|
|
522
|
-
addClass(
|
|
523
|
-
element,
|
|
524
|
-
PREFIX_WITH_DASH +
|
|
525
|
-
getCachedClassName("grid", "cols", layoutConfig.columns)
|
|
526
|
-
);
|
|
527
|
-
} else if (layoutConfig.columns === "auto-fill") {
|
|
528
|
-
addClass(
|
|
529
|
-
element,
|
|
530
|
-
PREFIX_WITH_DASH + getCachedClassName("grid", "fill", "auto")
|
|
531
|
-
);
|
|
532
|
-
} else if (layoutConfig.columns === "auto-fit") {
|
|
533
|
-
addClass(
|
|
534
|
-
element,
|
|
535
|
-
PREFIX_WITH_DASH + getCachedClassName("grid", "cols", "auto-fit")
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
if (layoutConfig.dense)
|
|
539
|
-
addClass(element, `${PREFIX_WITH_DASH}layout--grid-dense`);
|
|
540
|
-
if (layoutConfig.autoHeight)
|
|
541
|
-
addClass(element, `${PREFIX_WITH_DASH}layout--grid-auto-height`);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Row-specific properties
|
|
545
|
-
if (layoutConfig.type === "row" || getLayoutType(element) === "row") {
|
|
546
|
-
if (layoutConfig.wrap === false || layoutConfig.wrap === "nowrap") {
|
|
547
|
-
addClass(element, `${PREFIX_WITH_DASH}layout--row-nowrap`);
|
|
548
|
-
} else if (layoutConfig.wrap === "reverse") {
|
|
549
|
-
addClass(element, `${PREFIX_WITH_DASH}layout--row-wrap-reverse`);
|
|
550
|
-
}
|
|
551
|
-
if (layoutConfig.mobileStack)
|
|
552
|
-
addClass(element, `${PREFIX_WITH_DASH}layout--row-mobile-stack`);
|
|
553
|
-
if (layoutConfig.mobileScroll)
|
|
554
|
-
addClass(element, `${PREFIX_WITH_DASH}layout--row-mobile-scroll`);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Custom classes
|
|
558
|
-
if (layoutConfig.class) {
|
|
559
|
-
layoutConfig.class
|
|
560
|
-
.split(" ")
|
|
561
|
-
.filter(Boolean)
|
|
562
|
-
.forEach((cls) => element.classList.add(cls));
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Applies layout item classes based on configuration
|
|
568
|
-
* Uses integrated caching for optimal performance
|
|
569
|
-
*/
|
|
570
|
-
function applyLayoutItemClasses(
|
|
571
|
-
element: HTMLElement,
|
|
572
|
-
itemConfig: LayoutItemConfig
|
|
573
|
-
): void {
|
|
574
|
-
if (!element || !itemConfig) return;
|
|
575
|
-
|
|
576
|
-
addClass(element, `${PREFIX_WITH_DASH}layout__item`);
|
|
577
|
-
|
|
578
|
-
// Width and responsive classes with caching
|
|
579
|
-
if (itemConfig.width && itemConfig.width >= 1 && itemConfig.width <= 12) {
|
|
580
|
-
addClass(
|
|
581
|
-
element,
|
|
582
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "", itemConfig.width)
|
|
583
|
-
);
|
|
584
|
-
}
|
|
585
|
-
if (itemConfig.sm)
|
|
586
|
-
addClass(
|
|
587
|
-
element,
|
|
588
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "sm", itemConfig.sm)
|
|
589
|
-
);
|
|
590
|
-
if (itemConfig.md)
|
|
591
|
-
addClass(
|
|
592
|
-
element,
|
|
593
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "md", itemConfig.md)
|
|
594
|
-
);
|
|
595
|
-
if (itemConfig.lg)
|
|
596
|
-
addClass(
|
|
597
|
-
element,
|
|
598
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "lg", itemConfig.lg)
|
|
599
|
-
);
|
|
600
|
-
if (itemConfig.xl)
|
|
601
|
-
addClass(
|
|
602
|
-
element,
|
|
603
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "xl", itemConfig.xl)
|
|
604
|
-
);
|
|
605
|
-
|
|
606
|
-
// Grid span classes
|
|
607
|
-
if (itemConfig.span)
|
|
608
|
-
addClass(
|
|
609
|
-
element,
|
|
610
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "span", itemConfig.span)
|
|
611
|
-
);
|
|
612
|
-
if (itemConfig.rowSpan)
|
|
613
|
-
addClass(
|
|
614
|
-
element,
|
|
615
|
-
PREFIX_WITH_DASH +
|
|
616
|
-
getCachedClassName("item", "row-span", itemConfig.rowSpan)
|
|
617
|
-
);
|
|
618
|
-
|
|
619
|
-
// Order and alignment
|
|
620
|
-
if (itemConfig.order)
|
|
621
|
-
addClass(
|
|
622
|
-
element,
|
|
623
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "order", itemConfig.order)
|
|
624
|
-
);
|
|
625
|
-
if (itemConfig.align)
|
|
626
|
-
addClass(
|
|
627
|
-
element,
|
|
628
|
-
PREFIX_WITH_DASH + getCachedClassName("item", "self", itemConfig.align)
|
|
629
|
-
);
|
|
630
|
-
if (itemConfig.auto)
|
|
631
|
-
addClass(element, `${PREFIX_WITH_DASH}layout__item--auto`);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Gets the layout type from element classes
|
|
636
|
-
*/
|
|
637
|
-
function getLayoutType(element: HTMLElement): string {
|
|
638
|
-
return hasClass(element, `${PREFIX_WITH_DASH}layout--stack`)
|
|
639
|
-
? "stack"
|
|
640
|
-
: hasClass(element, `${PREFIX_WITH_DASH}layout--row`)
|
|
641
|
-
? "row"
|
|
642
|
-
: hasClass(element, `${PREFIX_WITH_DASH}layout--grid`)
|
|
643
|
-
? "grid"
|
|
644
|
-
: "";
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// ============================================================================
|
|
648
|
-
// UNIFIED SCHEMA PROCESSORS
|
|
649
|
-
// ============================================================================
|
|
650
|
-
|
|
651
|
-
/**
|
|
652
|
-
* Processes array-based schema definitions
|
|
653
|
-
* Optimized with parameter extraction and integrated configuration
|
|
654
|
-
*/
|
|
655
|
-
function processArraySchema(
|
|
656
|
-
schema: SchemaItem[] | any,
|
|
657
|
-
parentElement: HTMLElement | null = null,
|
|
658
|
-
level: number = 0,
|
|
659
|
-
options: LayoutOptions = {}
|
|
660
|
-
): LayoutResult {
|
|
661
|
-
level++;
|
|
662
|
-
const layout: Record<string, any> = {};
|
|
663
|
-
const components: Array<[string, any]> = [];
|
|
664
|
-
const fragment = createFragment();
|
|
665
|
-
let component = null;
|
|
666
|
-
|
|
667
|
-
if (!Array.isArray(schema)) {
|
|
668
|
-
return createLayoutResult(layout);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const defaultCreator = (options as any).creator || createElement;
|
|
672
|
-
|
|
673
|
-
for (let i = 0; i < schema.length; i++) {
|
|
674
|
-
const item = schema[i];
|
|
675
|
-
if (!item) continue;
|
|
676
|
-
|
|
677
|
-
// Handle nested arrays
|
|
678
|
-
if (Array.isArray(item)) {
|
|
679
|
-
const container = component || parentElement;
|
|
680
|
-
const result = processArraySchema(item, container, level, options);
|
|
681
|
-
Object.assign(layout, result.layout);
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Use optimized parameter extraction
|
|
686
|
-
const {
|
|
687
|
-
creator,
|
|
688
|
-
name,
|
|
689
|
-
options: itemOptions,
|
|
690
|
-
consumed,
|
|
691
|
-
} = extractParameters(schema, i, defaultCreator);
|
|
692
|
-
|
|
693
|
-
if (!creator) {
|
|
694
|
-
console.warn("Skipping unsupported item type:", item);
|
|
695
|
-
continue;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// Default to div for createElement
|
|
699
|
-
if (creator === createElement && !("tag" in itemOptions)) {
|
|
700
|
-
itemOptions.tag = "div";
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// Advance index by consumed items minus 1 (loop increment handles the +1)
|
|
704
|
-
i += consumed - 1;
|
|
705
|
-
|
|
706
|
-
// Process options with prefix - optimized decision logic
|
|
707
|
-
const shouldApplyPrefix =
|
|
708
|
-
"prefix" in itemOptions ? itemOptions.prefix : options.prefix !== false;
|
|
709
|
-
|
|
710
|
-
// Fast path: process only when needed
|
|
711
|
-
const processedOptions =
|
|
712
|
-
shouldApplyPrefix || itemOptions.rawClass
|
|
713
|
-
? processClassNames(itemOptions, !shouldApplyPrefix)
|
|
714
|
-
: itemOptions; // No copy needed if no processing
|
|
715
|
-
|
|
716
|
-
// Add name to options if needed
|
|
717
|
-
if (
|
|
718
|
-
name &&
|
|
719
|
-
!("name" in processedOptions) &&
|
|
720
|
-
!(creator === createElement || (creator as any).isElement)
|
|
721
|
-
) {
|
|
722
|
-
processedOptions.name = name;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// Create component
|
|
726
|
-
component = createComponentInstance(creator, processedOptions);
|
|
727
|
-
const element = isComponent(component) ? component.element : component;
|
|
728
|
-
|
|
729
|
-
if (level === 1) layout.element = element;
|
|
730
|
-
if (name) {
|
|
731
|
-
layout[name] = component;
|
|
732
|
-
components.push([name, component]);
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// Append to DOM
|
|
736
|
-
if (component) {
|
|
737
|
-
if ("insert" in component && typeof component.insert === "function") {
|
|
738
|
-
component.insert(fragment);
|
|
739
|
-
} else {
|
|
740
|
-
fragment.appendChild(element);
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
if (parentElement) {
|
|
744
|
-
component._container = parentElement;
|
|
745
|
-
if (
|
|
746
|
-
"onInserted" in component &&
|
|
747
|
-
typeof component.onInserted === "function"
|
|
748
|
-
) {
|
|
749
|
-
component.onInserted(parentElement);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// Append fragment to parent
|
|
756
|
-
if (parentElement && fragment.hasChildNodes()) {
|
|
757
|
-
const wrapper = isComponent(parentElement)
|
|
758
|
-
? parentElement.element
|
|
759
|
-
: parentElement;
|
|
760
|
-
wrapper.appendChild(fragment);
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// Release fragment back to pool
|
|
764
|
-
releaseFragment(fragment);
|
|
765
|
-
|
|
766
|
-
layout.components = components;
|
|
767
|
-
return createLayoutResult(layout);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
/**
|
|
771
|
-
* Processes object-based schema definitions
|
|
772
|
-
* Simplified and optimized for better performance
|
|
773
|
-
*/
|
|
774
|
-
function processObjectSchema(
|
|
775
|
-
schema: Record<string, any> | string,
|
|
776
|
-
parentElement: HTMLElement | null = null,
|
|
777
|
-
options: LayoutOptions = {}
|
|
778
|
-
): LayoutResult {
|
|
779
|
-
const layout: Record<string, any> = {};
|
|
780
|
-
const defaultCreator = options.creator || createElement;
|
|
781
|
-
|
|
782
|
-
// Handle root element creation
|
|
783
|
-
if ((schema as any).element && !parentElement) {
|
|
784
|
-
const elementDef = (schema as any).element;
|
|
785
|
-
const createElementFn = elementDef.creator || defaultCreator;
|
|
786
|
-
|
|
787
|
-
const elementOptions = elementDef.options || {};
|
|
788
|
-
const processedOptions =
|
|
789
|
-
options.prefix !== false
|
|
790
|
-
? processClassNames(elementOptions)
|
|
791
|
-
: { ...elementOptions };
|
|
792
|
-
|
|
793
|
-
const rootComponent = createComponentInstance(
|
|
794
|
-
createElementFn,
|
|
795
|
-
processedOptions
|
|
796
|
-
);
|
|
797
|
-
layout.element = rootComponent;
|
|
798
|
-
if (elementDef.name) layout[elementDef.name] = rootComponent;
|
|
799
|
-
|
|
800
|
-
// Process children
|
|
801
|
-
if (elementDef.children) {
|
|
802
|
-
const rootElement = isComponent(rootComponent)
|
|
803
|
-
? rootComponent.element
|
|
804
|
-
: rootComponent;
|
|
805
|
-
const childResult = processObjectSchema(
|
|
806
|
-
elementDef.children,
|
|
807
|
-
rootElement,
|
|
808
|
-
options
|
|
809
|
-
);
|
|
810
|
-
Object.assign(layout, childResult.layout);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
return createLayoutResult(layout);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
// Process normal schema elements
|
|
817
|
-
const fragment = parentElement ? createFragment() : null;
|
|
818
|
-
|
|
819
|
-
for (const key in schema as Record<string, any>) {
|
|
820
|
-
const def = (schema as Record<string, any>)[key];
|
|
821
|
-
if (!def) continue;
|
|
822
|
-
|
|
823
|
-
const elementCreator = def.creator || defaultCreator;
|
|
824
|
-
const elementOptions = def.options || {};
|
|
825
|
-
const shouldApplyPrefix =
|
|
826
|
-
"prefix" in elementOptions
|
|
827
|
-
? elementOptions.prefix
|
|
828
|
-
: options.prefix !== false;
|
|
829
|
-
const processedOptions = shouldApplyPrefix
|
|
830
|
-
? processClassNames(elementOptions)
|
|
831
|
-
: { ...elementOptions };
|
|
832
|
-
|
|
833
|
-
if (!def.name && key !== "element") {
|
|
834
|
-
def.name = key;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
const created = createComponentInstance(elementCreator, processedOptions);
|
|
838
|
-
layout[key] = created;
|
|
839
|
-
if (def.name && def.name !== key) layout[def.name] = created;
|
|
840
|
-
|
|
841
|
-
const element = isComponent(created) ? created.element : created;
|
|
842
|
-
if (fragment) fragment.appendChild(element);
|
|
843
|
-
|
|
844
|
-
// Process children
|
|
845
|
-
if (def.children) {
|
|
846
|
-
const childResult = processObjectSchema(def.children, element, options);
|
|
847
|
-
Object.assign(layout, childResult.layout);
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// Append to parent
|
|
852
|
-
if (parentElement && fragment) {
|
|
853
|
-
const parentDom = isComponent(parentElement)
|
|
854
|
-
? parentElement.element
|
|
855
|
-
: parentElement;
|
|
856
|
-
parentDom.appendChild(fragment);
|
|
857
|
-
releaseFragment(fragment);
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
return createLayoutResult(layout);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
// ============================================================================
|
|
864
|
-
// LAYOUT RESULT CREATION
|
|
865
|
-
// ============================================================================
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* Flattens a nested layout into a simple object with element and component references
|
|
869
|
-
*/
|
|
870
|
-
function flattenLayout(layout: Record<string, any>): Record<string, any> {
|
|
871
|
-
const flattened: Record<string, any> = {};
|
|
872
|
-
|
|
873
|
-
if (!layout || typeof layout !== "object") return flattened;
|
|
874
|
-
|
|
875
|
-
for (const key in layout) {
|
|
876
|
-
const value = layout[key];
|
|
877
|
-
if (
|
|
878
|
-
value &&
|
|
879
|
-
typeof value !== "function" &&
|
|
880
|
-
(value instanceof HTMLElement ||
|
|
881
|
-
(typeof SVGElement !== "undefined" && value instanceof SVGElement) ||
|
|
882
|
-
isComponent(value))
|
|
883
|
-
) {
|
|
884
|
-
flattened[key] = value;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
return flattened;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
/**
|
|
892
|
-
* Creates a layout result object with utility functions
|
|
893
|
-
*/
|
|
894
|
-
function createLayoutResult(layout: Record<string, any>): LayoutResult {
|
|
895
|
-
const flattenedComponents = flattenLayout(layout);
|
|
896
|
-
|
|
897
|
-
return {
|
|
898
|
-
layout,
|
|
899
|
-
element: layout.element,
|
|
900
|
-
component: flattenedComponents,
|
|
901
|
-
|
|
902
|
-
get(name: string): any {
|
|
903
|
-
return layout[name] ?? null;
|
|
904
|
-
},
|
|
905
|
-
|
|
906
|
-
getAll(): Record<string, any> {
|
|
907
|
-
return flattenedComponents;
|
|
908
|
-
},
|
|
909
|
-
|
|
910
|
-
destroy(): void {
|
|
911
|
-
// Call destroy on components
|
|
912
|
-
for (const key in layout) {
|
|
913
|
-
const component = layout[key];
|
|
914
|
-
if (component && typeof component.destroy === "function") {
|
|
915
|
-
component.destroy();
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
// Remove root element from DOM
|
|
920
|
-
if (layout.element) {
|
|
921
|
-
const element = isComponent(layout.element)
|
|
922
|
-
? layout.element.element
|
|
923
|
-
: layout.element;
|
|
924
|
-
if (element && element.parentNode) {
|
|
925
|
-
element.parentNode.removeChild(element);
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
},
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// ============================================================================
|
|
933
|
-
// UNIFIED ENTRY POINT
|
|
934
|
-
// ============================================================================
|
|
935
|
-
|
|
936
|
-
/**
|
|
937
|
-
* Creates a layout from various schema formats
|
|
938
|
-
* Unified processor for arrays, objects, JSX, and HTML strings
|
|
939
|
-
*/
|
|
940
|
-
export function createLayout(
|
|
941
|
-
schema: any,
|
|
942
|
-
parentElement: HTMLElement | null = null,
|
|
943
|
-
options: LayoutOptions = {}
|
|
944
|
-
): LayoutResult {
|
|
945
|
-
// Handle function schemas
|
|
946
|
-
if (typeof schema === "function") {
|
|
947
|
-
schema = schema();
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
// Handle HTML string schemas
|
|
951
|
-
if (typeof schema === "string") {
|
|
952
|
-
const template = document.createElement("template");
|
|
953
|
-
template.innerHTML = schema.trim();
|
|
954
|
-
const fragment = template.content;
|
|
955
|
-
|
|
956
|
-
if (parentElement && fragment.hasChildNodes()) {
|
|
957
|
-
parentElement.appendChild(fragment);
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
const layout = { element: fragment.firstElementChild as HTMLElement };
|
|
961
|
-
return createLayoutResult(layout);
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// Handle JSX-like schemas (array with function, string, object pattern)
|
|
965
|
-
if (
|
|
966
|
-
Array.isArray(schema) &&
|
|
967
|
-
schema.length >= 3 &&
|
|
968
|
-
typeof schema[0] === "function" &&
|
|
969
|
-
typeof schema[1] === "string" &&
|
|
970
|
-
isObject(schema[2])
|
|
971
|
-
) {
|
|
972
|
-
return processArraySchema(schema, parentElement, 0, options);
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// Route to appropriate processor
|
|
976
|
-
return Array.isArray(schema)
|
|
977
|
-
? processArraySchema(schema, parentElement, 0, options)
|
|
978
|
-
: processObjectSchema(schema, parentElement, options);
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// ============================================================================
|
|
982
|
-
// EXPORTS
|
|
983
|
-
// ============================================================================
|
|
984
|
-
|
|
985
|
-
// Clear functions for the unified system
|
|
986
|
-
export function clearClassCache(): void {
|
|
987
|
-
classCache.clear();
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
export function clearFragmentPool(): void {
|
|
991
|
-
fragmentPool.clear();
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
export {
|
|
995
|
-
processClassNames,
|
|
996
|
-
isComponent,
|
|
997
|
-
flattenLayout,
|
|
998
|
-
applyLayoutClasses,
|
|
999
|
-
applyLayoutItemClasses,
|
|
1000
|
-
createLayoutResult,
|
|
1001
|
-
};
|