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.
Files changed (128) hide show
  1. package/{src/components/index.ts → dist/components/index.d.ts} +0 -2
  2. package/dist/components/vlist/config.d.ts +86 -0
  3. package/{src/components/vlist/constants.ts → dist/components/vlist/constants.d.ts} +10 -11
  4. package/dist/components/vlist/features/api.d.ts +7 -0
  5. package/{src/components/vlist/features/index.ts → dist/components/vlist/features/index.d.ts} +0 -2
  6. package/dist/components/vlist/features/selection.d.ts +6 -0
  7. package/dist/components/vlist/features/viewport.d.ts +9 -0
  8. package/dist/components/vlist/features.d.ts +31 -0
  9. package/{src/components/vlist/index.ts → dist/components/vlist/index.d.ts} +1 -9
  10. package/dist/components/vlist/types.d.ts +596 -0
  11. package/dist/components/vlist/vlist.d.ts +29 -0
  12. package/dist/core/compose/features/gestures/index.d.ts +86 -0
  13. package/dist/core/compose/features/gestures/longpress.d.ts +85 -0
  14. package/dist/core/compose/features/gestures/pan.d.ts +108 -0
  15. package/dist/core/compose/features/gestures/pinch.d.ts +111 -0
  16. package/dist/core/compose/features/gestures/rotate.d.ts +111 -0
  17. package/dist/core/compose/features/gestures/swipe.d.ts +149 -0
  18. package/dist/core/compose/features/gestures/tap.d.ts +79 -0
  19. package/{src/core/compose/features/index.ts → dist/core/compose/features/index.d.ts} +1 -2
  20. package/{src/core/compose/index.ts → dist/core/compose/index.d.ts} +2 -11
  21. package/{src/core/gestures/index.ts → dist/core/gestures/index.d.ts} +1 -20
  22. package/dist/core/gestures/longpress.d.ts +23 -0
  23. package/dist/core/gestures/manager.d.ts +14 -0
  24. package/dist/core/gestures/pan.d.ts +12 -0
  25. package/dist/core/gestures/pinch.d.ts +14 -0
  26. package/dist/core/gestures/rotate.d.ts +14 -0
  27. package/dist/core/gestures/swipe.d.ts +20 -0
  28. package/dist/core/gestures/tap.d.ts +12 -0
  29. package/dist/core/gestures/types.d.ts +320 -0
  30. package/dist/core/gestures/utils.d.ts +57 -0
  31. package/dist/core/index.d.ts +13 -0
  32. package/dist/core/layout/config.d.ts +33 -0
  33. package/dist/core/layout/index.d.ts +51 -0
  34. package/dist/core/layout/jsx.d.ts +65 -0
  35. package/dist/core/layout/schema.d.ts +112 -0
  36. package/dist/core/layout/types.d.ts +69 -0
  37. package/dist/core/viewport/constants.d.ts +105 -0
  38. package/dist/core/viewport/features/base.d.ts +14 -0
  39. package/dist/core/viewport/features/collection.d.ts +41 -0
  40. package/dist/core/viewport/features/events.d.ts +13 -0
  41. package/{src/core/viewport/features/index.ts → dist/core/viewport/features/index.d.ts} +0 -7
  42. package/dist/core/viewport/features/item-size.d.ts +30 -0
  43. package/dist/core/viewport/features/loading.d.ts +34 -0
  44. package/dist/core/viewport/features/momentum.d.ts +17 -0
  45. package/dist/core/viewport/features/performance.d.ts +53 -0
  46. package/dist/core/viewport/features/placeholders.d.ts +38 -0
  47. package/dist/core/viewport/features/rendering.d.ts +16 -0
  48. package/dist/core/viewport/features/scrollbar.d.ts +26 -0
  49. package/dist/core/viewport/features/scrolling.d.ts +16 -0
  50. package/dist/core/viewport/features/utils.d.ts +43 -0
  51. package/dist/core/viewport/features/virtual.d.ts +18 -0
  52. package/{src/core/viewport/index.ts → dist/core/viewport/index.d.ts} +1 -17
  53. package/dist/core/viewport/types.d.ts +96 -0
  54. package/dist/core/viewport/utils/speed-tracker.d.ts +22 -0
  55. package/dist/core/viewport/viewport.d.ts +11 -0
  56. package/{src/index.ts → dist/index.d.ts} +0 -4
  57. package/dist/index.js +5143 -0
  58. package/dist/index.mjs +5111 -0
  59. package/dist/styles.css +254 -0
  60. package/dist/styles.css.map +1 -0
  61. package/package.json +6 -1
  62. package/src/styles/components/_vlist.scss +234 -213
  63. package/.cursorrules +0 -117
  64. package/AI.md +0 -241
  65. package/build.js +0 -201
  66. package/scripts/analyze-orphaned-functions.ts +0 -387
  67. package/scripts/debug/vlist-selection.ts +0 -121
  68. package/src/components/vlist/config.ts +0 -323
  69. package/src/components/vlist/features/api.ts +0 -322
  70. package/src/components/vlist/features/selection.ts +0 -444
  71. package/src/components/vlist/features/viewport.ts +0 -65
  72. package/src/components/vlist/features.ts +0 -112
  73. package/src/components/vlist/types.ts +0 -591
  74. package/src/components/vlist/vlist.ts +0 -92
  75. package/src/core/compose/features/gestures/index.ts +0 -227
  76. package/src/core/compose/features/gestures/longpress.ts +0 -383
  77. package/src/core/compose/features/gestures/pan.ts +0 -424
  78. package/src/core/compose/features/gestures/pinch.ts +0 -475
  79. package/src/core/compose/features/gestures/rotate.ts +0 -485
  80. package/src/core/compose/features/gestures/swipe.ts +0 -492
  81. package/src/core/compose/features/gestures/tap.ts +0 -334
  82. package/src/core/gestures/longpress.ts +0 -68
  83. package/src/core/gestures/manager.ts +0 -418
  84. package/src/core/gestures/pan.ts +0 -48
  85. package/src/core/gestures/pinch.ts +0 -58
  86. package/src/core/gestures/rotate.ts +0 -58
  87. package/src/core/gestures/swipe.ts +0 -66
  88. package/src/core/gestures/tap.ts +0 -45
  89. package/src/core/gestures/types.ts +0 -387
  90. package/src/core/gestures/utils.ts +0 -128
  91. package/src/core/index.ts +0 -43
  92. package/src/core/layout/config.ts +0 -102
  93. package/src/core/layout/index.ts +0 -168
  94. package/src/core/layout/jsx.ts +0 -174
  95. package/src/core/layout/schema.ts +0 -1001
  96. package/src/core/layout/types.ts +0 -95
  97. package/src/core/viewport/constants.ts +0 -140
  98. package/src/core/viewport/features/base.ts +0 -73
  99. package/src/core/viewport/features/collection.ts +0 -882
  100. package/src/core/viewport/features/events.ts +0 -130
  101. package/src/core/viewport/features/item-size.ts +0 -271
  102. package/src/core/viewport/features/loading.ts +0 -263
  103. package/src/core/viewport/features/momentum.ts +0 -260
  104. package/src/core/viewport/features/performance.ts +0 -161
  105. package/src/core/viewport/features/placeholders.ts +0 -335
  106. package/src/core/viewport/features/rendering.ts +0 -568
  107. package/src/core/viewport/features/scrollbar.ts +0 -434
  108. package/src/core/viewport/features/scrolling.ts +0 -618
  109. package/src/core/viewport/features/utils.ts +0 -88
  110. package/src/core/viewport/features/virtual.ts +0 -384
  111. package/src/core/viewport/types.ts +0 -133
  112. package/src/core/viewport/utils/speed-tracker.ts +0 -79
  113. package/src/core/viewport/viewport.ts +0 -246
  114. package/test/benchmarks/layout/advanced.test.ts +0 -656
  115. package/test/benchmarks/layout/comparison.test.ts +0 -519
  116. package/test/benchmarks/layout/performance-comparison.test.ts +0 -274
  117. package/test/benchmarks/layout/real-components.test.ts +0 -733
  118. package/test/benchmarks/layout/simple.test.ts +0 -321
  119. package/test/benchmarks/layout/stress.test.ts +0 -990
  120. package/test/collection/basic.test.ts +0 -304
  121. package/test/components/vlist-selection.test.ts +0 -240
  122. package/test/components/vlist.test.ts +0 -63
  123. package/test/core/collection/adapter.test.ts +0 -161
  124. package/test/core/collection/collection.test.ts +0 -394
  125. package/test/core/layout/layout.test.ts +0 -201
  126. package/test/utils/dom-helpers.ts +0 -275
  127. package/test/utils/performance-helpers.ts +0 -392
  128. 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
- };