mtrl 0.2.9 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CLAUDE.md +33 -0
  2. package/package.json +3 -1
  3. package/src/components/button/button.ts +34 -5
  4. package/src/components/navigation/index.ts +4 -1
  5. package/src/components/navigation/system/core.ts +302 -0
  6. package/src/components/navigation/system/events.ts +240 -0
  7. package/src/components/navigation/system/index.ts +184 -0
  8. package/src/components/navigation/system/mobile.ts +278 -0
  9. package/src/components/navigation/system/state.ts +77 -0
  10. package/src/components/navigation/system/types.ts +364 -0
  11. package/src/components/navigation/types.ts +33 -0
  12. package/src/components/slider/config.ts +2 -2
  13. package/src/components/slider/features/controller.ts +1 -25
  14. package/src/components/slider/features/handlers.ts +0 -1
  15. package/src/components/slider/features/range.ts +7 -7
  16. package/src/components/slider/{structure.ts → schema.ts} +2 -13
  17. package/src/components/slider/slider.ts +3 -2
  18. package/src/components/snackbar/index.ts +7 -1
  19. package/src/components/snackbar/types.ts +25 -0
  20. package/src/components/switch/api.ts +16 -0
  21. package/src/components/switch/config.ts +1 -18
  22. package/src/components/switch/features.ts +198 -0
  23. package/src/components/switch/index.ts +6 -1
  24. package/src/components/switch/switch.ts +3 -3
  25. package/src/components/switch/types.ts +27 -2
  26. package/src/components/textfield/index.ts +7 -1
  27. package/src/components/textfield/types.ts +36 -0
  28. package/src/core/composition/features/dom.ts +26 -14
  29. package/src/core/composition/features/icon.ts +18 -18
  30. package/src/core/composition/features/index.ts +3 -2
  31. package/src/core/composition/features/label.ts +16 -17
  32. package/src/core/composition/features/layout.ts +47 -0
  33. package/src/core/composition/index.ts +4 -4
  34. package/src/core/layout/README.md +350 -0
  35. package/src/core/layout/array.ts +181 -0
  36. package/src/core/layout/create.ts +55 -0
  37. package/src/core/layout/index.ts +26 -0
  38. package/src/core/layout/object.ts +124 -0
  39. package/src/core/layout/processor.ts +58 -0
  40. package/src/core/layout/result.ts +85 -0
  41. package/src/core/layout/types.ts +125 -0
  42. package/src/core/layout/utils.ts +136 -0
  43. package/src/styles/abstract/_variables.scss +28 -0
  44. package/src/styles/components/_switch.scss +133 -69
  45. package/src/styles/components/_textfield.scss +9 -16
  46. package/test/components/badge.test.ts +545 -0
  47. package/test/components/bottom-app-bar.test.ts +303 -0
  48. package/test/components/button.test.ts +233 -0
  49. package/test/components/card.test.ts +560 -0
  50. package/test/components/carousel.test.ts +951 -0
  51. package/test/components/checkbox.test.ts +462 -0
  52. package/test/components/chip.test.ts +692 -0
  53. package/test/components/datepicker.test.ts +1124 -0
  54. package/test/components/dialog.test.ts +990 -0
  55. package/test/components/divider.test.ts +412 -0
  56. package/test/components/extended-fab.test.ts +672 -0
  57. package/test/components/fab.test.ts +561 -0
  58. package/test/components/list.test.ts +365 -0
  59. package/test/components/menu.test.ts +718 -0
  60. package/test/components/navigation.test.ts +186 -0
  61. package/test/components/progress.test.ts +567 -0
  62. package/test/components/radios.test.ts +699 -0
  63. package/test/components/search.test.ts +1135 -0
  64. package/test/components/segmented-button.test.ts +732 -0
  65. package/test/components/sheet.test.ts +641 -0
  66. package/test/components/slider.test.ts +1220 -0
  67. package/test/components/snackbar.test.ts +461 -0
  68. package/test/components/switch.test.ts +452 -0
  69. package/test/components/tabs.test.ts +1369 -0
  70. package/test/components/textfield.test.ts +400 -0
  71. package/test/components/timepicker.test.ts +592 -0
  72. package/test/components/tooltip.test.ts +630 -0
  73. package/test/components/top-app-bar.test.ts +566 -0
  74. package/test/core/dom.attributes.test.ts +148 -0
  75. package/test/core/dom.classes.test.ts +152 -0
  76. package/test/core/dom.events.test.ts +243 -0
  77. package/test/core/emitter.test.ts +141 -0
  78. package/test/core/ripple.test.ts +99 -0
  79. package/test/core/state.store.test.ts +189 -0
  80. package/test/core/utils.normalize.test.ts +61 -0
  81. package/test/core/utils.object.test.ts +120 -0
  82. package/test/setup.ts +451 -0
  83. package/tsconfig.json +2 -2
  84. package/src/components/navigation/system-types.ts +0 -124
  85. package/src/components/navigation/system.ts +0 -776
  86. package/src/components/snackbar/constants.ts +0 -26
  87. package/src/core/composition/features/structure.ts +0 -22
  88. package/src/core/layout/index.js +0 -95
  89. package/src/core/structure.ts +0 -288
  90. package/test/components/button.test.js +0 -170
  91. package/test/components/checkbox.test.js +0 -238
  92. package/test/components/list.test.js +0 -105
  93. package/test/components/menu.test.js +0 -385
  94. package/test/components/navigation.test.js +0 -227
  95. package/test/components/snackbar.test.js +0 -234
  96. package/test/components/switch.test.js +0 -186
  97. package/test/components/textfield.test.js +0 -314
  98. package/test/core/emitter.test.js +0 -141
  99. package/test/core/ripple.test.js +0 -66
@@ -0,0 +1,47 @@
1
+ // src/core/composition/features/layout.ts
2
+
3
+ /**
4
+ * Configuration that includes a component schema
5
+ */
6
+ export interface LayoutConfig {
7
+ /**
8
+ * Component schema definition
9
+ */
10
+ schema?: any;
11
+
12
+ [key: string]: any;
13
+ }
14
+
15
+ /**
16
+ * Adds schema definition to component without creating DOM
17
+ * This establishes the blueprint for the component's layout
18
+ * before materializing it with withDom
19
+ *
20
+ * @param config Configuration containing schema definition
21
+ * @returns Component enhancer with schema definition
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * // Add layout to a component
26
+ * const component = pipe(
27
+ * createBase,
28
+ * withLayout(config),
29
+ * withIcon(config),
30
+ * withLabel(config),
31
+ * withDom()
32
+ * )(config);
33
+ * ```
34
+ */
35
+ export const withLayout = (config: LayoutConfig) => component => {
36
+ // Use the schema definition from the config
37
+ if (!config.schema) {
38
+ console.warn('No schema definition found in component config');
39
+ return component;
40
+ }
41
+
42
+ // Return enhanced component with schema definition
43
+ return {
44
+ ...component,
45
+ schema: config.schema
46
+ };
47
+ };
@@ -6,10 +6,10 @@
6
6
  *
7
7
  * The composition module provides features that work with the structure definition
8
8
  * mechanism. Unlike traditional features that directly modify the DOM, these
9
- * features modify a structure definition that is later used to create DOM elements.
9
+ * features modify a layout schema that is later used to create DOM elements.
10
10
  *
11
11
  * This approach provides several advantages:
12
- * - Clearer separation between structure definition and DOM creation
12
+ * - Clearer separation between layout and DOM creation
13
13
  * - More predictable component creation process
14
14
  * - Better support for server-side rendering
15
15
  * - Enhanced testability
@@ -17,8 +17,8 @@
17
17
 
18
18
  // Export features
19
19
  export {
20
- withStructure
21
- withDom
20
+ withLayout,
21
+ withDom,
22
22
  withIcon,
23
23
  withLabel,
24
24
  } from './features';
@@ -0,0 +1,350 @@
1
+ # Layout Module
2
+
3
+ A lightweight, flexible system for creating and managing visual arrangements and component hierarchies.
4
+
5
+ ## Overview
6
+
7
+ The Layout Module provides a declarative approach to building UI layouts using either arrays or objects. It efficiently handles DOM operations, component instantiation, and visual arrangement in a bundle-optimized way.
8
+
9
+ ## Features
10
+
11
+ - **Multiple Schema Formats** - Support for array-based, object-based, and HTML string schemas
12
+ - **Efficient DOM Operations** - Batched DOM manipulations with DocumentFragment
13
+ - **Component Management** - Easy access to component instances via consistent API
14
+ - **Customizable Creation** - Control class prefixing and specify default creators
15
+ - **Optimized for Bundle Size** - Minimal footprint with maximum functionality
16
+ - **TypeScript Support** - Full type definitions for developer experience
17
+
18
+ ## Basic Usage
19
+
20
+ ### Array-based Layout
21
+
22
+ ```javascript
23
+ import { createLayout, createButton, createDialog, createList, createListItem } from 'mtrl';
24
+
25
+ const layout = createLayout([
26
+ // Root level contains primary components
27
+ createButton, 'submitButton', { text: 'Submit', variant: 'primary' },
28
+
29
+ // Dialog is a root component, not nested inside other elements
30
+ createDialog, 'confirmDialog', {
31
+ title: 'Confirm Action',
32
+ closeOnBackdrop: true,
33
+ width: '350px'
34
+ }
35
+ ]);
36
+
37
+ // Add content to the dialog separately
38
+ const dialogContent = createLayout([
39
+ createList, 'actionsList', {},
40
+ [
41
+ createListItem, 'confirmAction', { text: 'Confirm', leading: 'check' },
42
+ createListItem, 'cancelAction', { text: 'Cancel', leading: 'close' }
43
+ ]
44
+ ], layout.get('confirmDialog').contentElement);
45
+
46
+ // Access components
47
+ const submitButton = layout.get('submitButton');
48
+ const confirmDialog = layout.get('confirmDialog');
49
+
50
+ // Handle events
51
+ submitButton.on('click', () => confirmDialog.open());
52
+ ```
53
+
54
+ ### Object-based Layout
55
+
56
+ ```javascript
57
+ import { createLayout, createTopAppBar, createNavigation, createList, createListItem, createTextfield, createButton } from 'mtrl';
58
+
59
+ const layout = createLayout({
60
+ element: {
61
+ creator: createTopAppBar,
62
+ options: {
63
+ title: 'Profile Settings',
64
+ variant: 'small'
65
+ },
66
+ children: {
67
+ navigation: {
68
+ creator: createNavigation,
69
+ options: { variant: 'drawer', persistent: true },
70
+ children: {
71
+ navList: {
72
+ creator: createList,
73
+ options: { interactive: true },
74
+ children: {
75
+ profileLink: {
76
+ creator: createListItem,
77
+ options: { text: 'Profile', leading: 'person' }
78
+ },
79
+ settingsLink: {
80
+ creator: createListItem,
81
+ options: { text: 'Settings', leading: 'settings' }
82
+ },
83
+ logoutLink: {
84
+ creator: createListItem,
85
+ options: { text: 'Logout', leading: 'logout' }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ },
91
+ form: {
92
+ creator: createElement,
93
+ options: { tag: 'form', className: 'profile-form' },
94
+ children: {
95
+ nameField: {
96
+ creator: createTextfield,
97
+ options: { label: 'Full Name', required: true }
98
+ },
99
+ emailField: {
100
+ creator: createTextfield,
101
+ options: { label: 'Email', type: 'email', required: true }
102
+ },
103
+ saveButton: {
104
+ creator: createButton,
105
+ options: { text: 'Save Changes', variant: 'filled' }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ });
112
+
113
+ // Access components
114
+ const topAppBar = layout.element;
115
+ const nameField = layout.get('nameField');
116
+ const saveButton = layout.get('saveButton');
117
+
118
+ // Use the components
119
+ nameField.setValue('John Doe');
120
+ saveButton.on('click', () => console.log('Profile updated'));
121
+ ```
122
+
123
+ ### HTML String Layout
124
+
125
+ ```javascript
126
+ import createLayout from 'core/layout';
127
+
128
+ const layout = createLayout(`
129
+ <div class="notification">
130
+ <h3>Welcome!</h3>
131
+ <p>Thank you for joining our platform.</p>
132
+ </div>
133
+ `);
134
+
135
+ // Access the root element
136
+ const notification = layout.element;
137
+ document.body.appendChild(notification);
138
+ ```
139
+
140
+ ### Using Options Parameter
141
+
142
+ ```javascript
143
+ import { createLayout, createButton, createTextfield, createCard, createChip, createTopAppBar, createBottomAppBar } from 'mtrl';
144
+
145
+ // With default creator and disabled prefix
146
+ const formLayout = createLayout(
147
+ [
148
+ // Using string keys relies on the default creator
149
+ 'nameField', { label: 'Name', required: true },
150
+ 'emailField', { label: 'Email', type: 'email', required: true },
151
+ 'phoneField', { label: 'Phone', type: 'tel' },
152
+
153
+ // Explicitly override the default creator
154
+ createButton, 'submitButton', { text: 'Submit', variant: 'filled' }
155
+ ],
156
+ document.getElementById('form-container'),
157
+ {
158
+ creator: createTextfield, // Default creator for all elements without a specific constructor
159
+ prefix: false // Disable automatic class prefixing
160
+ }
161
+ );
162
+
163
+ // With theme options for a complete dashboard layout
164
+ const dashboardLayout = createLayout(
165
+ [
166
+ createTopAppBar, 'header', {
167
+ title: 'Dashboard',
168
+ actions: ['notifications', 'account']
169
+ },
170
+
171
+ createCard, 'statsCard', {
172
+ title: 'Performance Metrics',
173
+ outlined: true
174
+ },
175
+ [
176
+ createChip, 'visitsChip', { text: 'Visits: 1.2K', leadingIcon: 'visibility' },
177
+ createChip, 'conversionChip', { text: 'Conversion: 5.4%', leadingIcon: 'trending_up' }
178
+ ],
179
+
180
+ createCard, 'activityCard', {
181
+ title: 'Recent Activity',
182
+ outlined: true
183
+ },
184
+
185
+ createBottomAppBar, 'footer', {
186
+ actions: [
187
+ { icon: 'home', label: 'Home' },
188
+ { icon: 'search', label: 'Search' },
189
+ { icon: 'settings', label: 'Settings' }
190
+ ]
191
+ }
192
+ ],
193
+ document.getElementById('app'),
194
+ {
195
+ theme: 'dark', // Custom theme option
196
+ density: 'comfortable', // Custom density option
197
+ animations: true // Custom animation option
198
+ }
199
+ );
200
+
201
+ // Access and use the layout
202
+ const nameField = formLayout.get('nameField');
203
+ nameField.setValue('John Doe');
204
+
205
+ const statsCard = dashboardLayout.get('statsCard');
206
+ statsCard.setTitle('Updated Metrics - ' + new Date().toLocaleDateString());
207
+ ```
208
+
209
+ ## API Reference
210
+
211
+ ### Core Functions
212
+
213
+ #### `createLayout(schema, parentElement?, options?)`
214
+
215
+ Creates a layout from a schema definition.
216
+
217
+ - **Parameters**:
218
+ - `schema`: Array, object, HTML string, or function returning one of these
219
+ - `parentElement` (optional): Parent element to attach the layout to
220
+ - `options` (optional): Configuration options for layout creation:
221
+ - `creator`: Default creator function to use when not specified in schema
222
+ - `prefix`: Boolean to control whether CSS class prefixing is applied (default: true)
223
+ - Custom options can be added and accessed in component creators
224
+ - **Returns**: Layout result object with components and utility methods
225
+
226
+ #### `processSchema(schema, parentElement?, level?, options?)`
227
+
228
+ Low-level function for processing schemas directly.
229
+
230
+ - **Parameters**:
231
+ - `schema`: Array or object schema
232
+ - `parentElement` (optional): Parent element to attach to
233
+ - `level` (optional): Current recursion level
234
+ - `options` (optional): Layout creation options
235
+ - **Returns**: Layout result object
236
+
237
+ #### `createComponentInstance(Component, options?, layoutOptions?)`
238
+
239
+ Creates a component instance from a constructor or factory function.
240
+
241
+ - **Parameters**:
242
+ - `Component`: Constructor or factory function
243
+ - `options` (optional): Options to pass to the component
244
+ - `layoutOptions` (optional): Global layout options
245
+ - **Returns**: Component instance
246
+
247
+ ### Utility Functions
248
+
249
+ #### `isComponent(value)`
250
+
251
+ Checks if a value is a component-like object.
252
+
253
+ - **Parameters**:
254
+ - `value`: Value to check
255
+ - **Returns**: Boolean indicating if the value is a component
256
+
257
+ #### `processClassNames(options, skipPrefix?)`
258
+
259
+ Processes class names in options to add prefixes.
260
+
261
+ - **Parameters**:
262
+ - `options`: Element options containing className
263
+ - `skipPrefix` (optional): Whether to skip adding prefixes
264
+ - **Returns**: Updated options with prefixed classNames
265
+
266
+ #### `flattenLayout(layout)`
267
+
268
+ Flattens a nested layout for easier component access.
269
+
270
+ - **Parameters**:
271
+ - `layout`: Layout object to flatten
272
+ - **Returns**: Flattened layout with components
273
+
274
+ ### Result Object
275
+
276
+ The layout result object contains:
277
+
278
+ - `layout`: Raw layout object with all components
279
+ - `element`: Reference to the root element
280
+ - `component`: Flattened component map for easy access
281
+ - `get(name)`: Function to get a component by name
282
+ - `getAll()`: Function to get all components
283
+ - `destroy()`: Function to clean up the layout
284
+
285
+ ## Integrating with Layout Manager
286
+
287
+ This module works well with the Layout Manager for advanced application layouts:
288
+
289
+ ```javascript
290
+ import createLayout from 'core/layout';
291
+ import createLayoutManager from 'client/core/layout/layout-manager';
292
+
293
+ // Create application layout
294
+ const appLayout = createLayout([
295
+ // Application components...
296
+ ]);
297
+
298
+ // Create layout manager with the layout
299
+ const layoutManager = createLayoutManager({
300
+ layout: appLayout.layout,
301
+ layoutAPI: appLayout
302
+ });
303
+
304
+ // Use layout manager API
305
+ layoutManager.setContent('<h1>Welcome to the app</h1>');
306
+ layoutManager.setPageTitle('Dashboard');
307
+ ```
308
+
309
+ ## Performance Considerations
310
+
311
+ ### Schema Format Performance
312
+
313
+ **Array-based schemas** generally outperform object-based schemas:
314
+
315
+ - **Faster processing**: 15-30% faster for large layouts
316
+ - **Lower memory usage**: Requires less memory without property names
317
+ - **Better bundle size**: More compact representation in code
318
+ - **Efficient iteration**: Arrays are optimized for sequential access
319
+
320
+ **Object-based schemas** excel in:
321
+
322
+ - **Readability**: More explicit structure with named properties
323
+ - **Maintainability**: Easier to understand complex nested structures
324
+ - **Self-documentation**: Property names describe the layout's purpose
325
+
326
+ **Recommendations**:
327
+ - For **performance-critical** applications, prefer array-based schemas
328
+ - For **complex, deeply nested** structures where maintainability is key, consider object-based schemas
329
+ - For the **best balance**, use array-based schemas for large structures and object-based for complex configurations
330
+
331
+ ### Options Performance Considerations
332
+
333
+ - Setting `prefix: false` can improve performance slightly by avoiding class name processing
334
+ - Providing a `creator` function in options is more efficient than having many duplicate creator references in the schema
335
+ - Consider memoizing layout creation for frequently used UI patterns with the same options
336
+
337
+ ### General Optimization Tips
338
+
339
+ - Use DocumentFragment for batch DOM operations
340
+ - Create components only when needed
341
+ - Consider memoizing frequently created layouts
342
+ - For large applications, lazy-load secondary layouts
343
+
344
+ ## Browser Compatibility
345
+
346
+ The Layout Module is compatible with all modern browsers (Chrome, Firefox, Safari, Edge).
347
+
348
+ ## License
349
+
350
+ MIT
@@ -0,0 +1,181 @@
1
+ // src/core/layout/array.ts
2
+ /**
3
+ * @module core/layout
4
+ * @description Processor for array-based layout schemas
5
+ */
6
+
7
+ import { createElement } from '../dom/create';
8
+ import { LayoutResult, LayoutOptions } from './types';
9
+ import { isComponent, createFragment, processClassNames } from './utils';
10
+ import { createLayoutResult } from './result';
11
+ import { createComponentInstance } from './processor';
12
+ import { isObject } from '../utils';
13
+
14
+ /**
15
+ * Processes an array-based layout definition
16
+ *
17
+ * @param schema - Array-based layout definition
18
+ * @param parentElement - Optional parent element to attach layout to
19
+ * @param level - Current recursion level
20
+ * @param options - Layout creation options
21
+ * @returns Layout result object
22
+ */
23
+ export function processArraySchema(
24
+ schema: any[],
25
+ parentElement: HTMLElement | null = null,
26
+ level: number = 0,
27
+ options: LayoutOptions = {}
28
+ ): LayoutResult {
29
+ level++;
30
+ const layout = {};
31
+ const components = [];
32
+ const fragment = createFragment();
33
+ let component = null;
34
+
35
+ if (!Array.isArray(schema)) {
36
+ console.error('Schema is not an array!', parentElement, level, schema);
37
+ return createLayoutResult(layout);
38
+ }
39
+
40
+ // Get default creator function from options or use createElement
41
+ const defaultCreator = options.creator || createElement;
42
+
43
+ for (let i = 0; i < schema.length; i++) {
44
+ const item = schema[i];
45
+ if (!item) continue;
46
+
47
+ // Handle nested arrays recursively
48
+ if (Array.isArray(item)) {
49
+ const container = component || parentElement;
50
+ // Use this function for recursion, not the external processSchema
51
+ const result = processArraySchema(item, container, level, options);
52
+ Object.assign(layout, result.layout);
53
+ continue;
54
+ }
55
+
56
+ // Handle different item types
57
+ let creator, name, itemOptions;
58
+
59
+ // Case 1: Item is a function (component creator)
60
+ if (typeof item === 'function') {
61
+ creator = item;
62
+
63
+ // Check next items for name (string) and options (object)
64
+ const nextItem = schema[i+1];
65
+ const afterNextItem = schema[i+2];
66
+
67
+ if (typeof nextItem === 'string') {
68
+ name = nextItem;
69
+ i++; // Skip the name on next iteration
70
+
71
+ // Check if options are provided after the name
72
+ if (isObject(afterNextItem)) {
73
+ itemOptions = afterNextItem;
74
+ i++; // Skip the options on next iteration
75
+ } else {
76
+ itemOptions = {};
77
+ }
78
+ } else if (isObject(nextItem)) {
79
+ // No name provided, just options
80
+ itemOptions = nextItem;
81
+ i++; // Skip the options on next iteration
82
+ } else {
83
+ // No name or options
84
+ itemOptions = {};
85
+ }
86
+ }
87
+ // Case 2: Item is a string (component name using default creator)
88
+ else if (typeof item === 'string') {
89
+ creator = defaultCreator;
90
+ name = item;
91
+
92
+ // Check next item for options
93
+ const nextItem = schema[i+1];
94
+ if (isObject(nextItem)) {
95
+ itemOptions = nextItem;
96
+ i++; // Skip the options on next iteration
97
+ } else {
98
+ itemOptions = {};
99
+ }
100
+
101
+ // We should NOT automatically set the tag to match the name
102
+ // If tag is not provided in options, a default like 'div' should be used
103
+ if (creator === createElement && !('tag' in itemOptions)) {
104
+ itemOptions.tag = 'div'; // Default to div if no tag is specified
105
+ }
106
+ }
107
+ // Case 3: Item is an object (options for default creator with no name)
108
+ else if (isObject(item)) {
109
+ creator = defaultCreator;
110
+ itemOptions = item;
111
+ }
112
+ else {
113
+ // Skip unsupported item types
114
+ console.warn('Skipping unsupported item type:', item);
115
+ continue;
116
+ }
117
+
118
+ // Check for additional options object after main options
119
+ const maybeAdditionalOptions = schema[i+1];
120
+ if (isObject(maybeAdditionalOptions) && !Array.isArray(maybeAdditionalOptions) &&
121
+ typeof maybeAdditionalOptions.prefix !== 'undefined') {
122
+ // Merge the additional options into main options
123
+ Object.assign(itemOptions, maybeAdditionalOptions);
124
+ i++; // Skip on next iteration
125
+ }
126
+
127
+ // Process options based on prefix setting
128
+ const shouldApplyPrefix =
129
+ // Use item-specific prefix setting if available
130
+ 'prefix' in itemOptions ?
131
+ itemOptions.prefix :
132
+ // Otherwise use global options prefix setting (default to true)
133
+ options.prefix !== false;
134
+
135
+ const processedOptions = shouldApplyPrefix ?
136
+ processClassNames(itemOptions) :
137
+ { ...itemOptions };
138
+
139
+ // Add name to options if needed and not a DOM element tag
140
+ if (name && !('name' in processedOptions) &&
141
+ !(creator === createElement || creator.isElement)) {
142
+ processedOptions.name = name;
143
+ }
144
+
145
+ // Create and store component
146
+ component = createComponentInstance(creator, processedOptions, options);
147
+ const element = isComponent(component) ? component.element : component;
148
+
149
+ if (level === 1) layout.element = element;
150
+ if (name) {
151
+ layout[name] = component;
152
+ components.push([name, component]);
153
+ }
154
+
155
+ // Append to DOM
156
+ if (component) {
157
+ if ('insert' in component && typeof component.insert === 'function') {
158
+ component.insert(fragment);
159
+ } else {
160
+ fragment.appendChild(element);
161
+ }
162
+
163
+ if (parentElement) {
164
+ component._container = parentElement;
165
+
166
+ if ('onInserted' in component && typeof component.onInserted === 'function') {
167
+ component.onInserted(parentElement);
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ // Append fragment to parent in a single operation
174
+ if (parentElement && fragment.hasChildNodes()) {
175
+ const wrapper = isComponent(parentElement) ? parentElement.element : parentElement;
176
+ wrapper.appendChild(fragment);
177
+ }
178
+
179
+ layout.components = components;
180
+ return createLayoutResult(layout);
181
+ }
@@ -0,0 +1,55 @@
1
+ // src/core/layout/create.ts
2
+ /**
3
+ * @module core/layout
4
+ * @description Main layout creation functionality with optimized DOM operations
5
+ */
6
+
7
+ import { Schema, LayoutResult, LayoutOptions } from './types';
8
+ import { processSchema } from './processor';
9
+ import { createLayoutResult } from './result';
10
+
11
+ /**
12
+ * Creates a DOM or component layout based on a layout definition
13
+ * Uses batched DOM operations for better performance
14
+ *
15
+ * @param schema - Layout definition (array-based, object-based, or HTML string)
16
+ * @param parentElement - Optional parent element to attach layout to
17
+ * @param options - Additional options for layout creation
18
+ * @returns Object containing the layout and utility functions
19
+ */
20
+ export function createLayout(
21
+ schema: Schema | any[] | string | Function,
22
+ parentElement: HTMLElement | null = null,
23
+ options: LayoutOptions = {}
24
+ ): LayoutResult {
25
+ // If schema is a function, execute it to get the actual schema
26
+ if (typeof schema === 'function') {
27
+ schema = schema();
28
+ }
29
+
30
+ // Parse HTML string into a layout if needed
31
+ if (typeof schema === 'string') {
32
+ // Create a temporary element to parse HTML
33
+ const template = document.createElement('template');
34
+ template.innerHTML = schema.trim();
35
+
36
+ // Use the parsed DOM structure directly
37
+ const fragment = template.content;
38
+
39
+ if (parentElement && fragment.hasChildNodes()) {
40
+ // Batch DOM operation - append all nodes at once
41
+ parentElement.appendChild(fragment);
42
+ }
43
+
44
+ // Create a basic layout for HTML string
45
+ const layout = {
46
+ element: fragment.firstElementChild as HTMLElement
47
+ };
48
+
49
+ // Create layout result with HTML content
50
+ return createLayoutResult(layout);
51
+ }
52
+
53
+ // Process the schema using our unified processor with options
54
+ return processSchema(schema, parentElement, 0, options);
55
+ }
@@ -0,0 +1,26 @@
1
+ // src/core/layout/index.ts
2
+ /**
3
+ * @module core/layout
4
+ * @description Optimized layout creation system with simplified API
5
+ */
6
+
7
+ // Export essential types
8
+ export type {
9
+ ComponentLike,
10
+ ElementDefinition,
11
+ Schema,
12
+ LayoutResult,
13
+ LayoutOptions
14
+ } from './types';
15
+
16
+ // Export utility functions
17
+ export { isComponent, processClassNames, flattenLayout } from './utils';
18
+
19
+ // Export core functionality
20
+ export { createLayout } from './create';
21
+ export { createLayoutResult } from './result';
22
+ export { processSchema, createComponentInstance } from './processor';
23
+
24
+ // Default export for backward compatibility and simpler usage
25
+ import { createLayout } from './create';
26
+ export default createLayout;