mtrl 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/index.ts +4 -0
  2. package/package.json +1 -1
  3. package/src/components/button/button.ts +34 -5
  4. package/src/components/navigation/api.ts +131 -96
  5. package/src/components/navigation/features/controller.ts +273 -0
  6. package/src/components/navigation/features/items.ts +133 -64
  7. package/src/components/navigation/navigation.ts +17 -2
  8. package/src/components/navigation/system/core.ts +302 -0
  9. package/src/components/navigation/system/events.ts +240 -0
  10. package/src/components/navigation/system/index.ts +184 -0
  11. package/src/components/navigation/system/mobile.ts +278 -0
  12. package/src/components/navigation/system/state.ts +77 -0
  13. package/src/components/navigation/system/types.ts +364 -0
  14. package/src/components/slider/config.ts +20 -2
  15. package/src/components/slider/features/controller.ts +737 -0
  16. package/src/components/slider/features/handlers.ts +18 -16
  17. package/src/components/slider/features/index.ts +3 -2
  18. package/src/components/slider/features/range.ts +104 -0
  19. package/src/components/slider/schema.ts +141 -0
  20. package/src/components/slider/slider.ts +34 -13
  21. package/src/components/switch/api.ts +16 -0
  22. package/src/components/switch/config.ts +1 -18
  23. package/src/components/switch/features.ts +198 -0
  24. package/src/components/switch/index.ts +1 -0
  25. package/src/components/switch/switch.ts +3 -3
  26. package/src/components/switch/types.ts +14 -2
  27. package/src/components/textfield/api.ts +53 -0
  28. package/src/components/textfield/features.ts +322 -0
  29. package/src/components/textfield/textfield.ts +8 -0
  30. package/src/components/textfield/types.ts +12 -3
  31. package/src/components/timepicker/clockdial.ts +1 -4
  32. package/src/core/compose/features/textinput.ts +15 -2
  33. package/src/core/composition/features/dom.ts +45 -0
  34. package/src/core/composition/features/icon.ts +131 -0
  35. package/src/core/composition/features/index.ts +12 -0
  36. package/src/core/composition/features/label.ts +155 -0
  37. package/src/core/composition/features/layout.ts +47 -0
  38. package/src/core/composition/index.ts +26 -0
  39. package/src/core/index.ts +1 -1
  40. package/src/core/layout/README.md +350 -0
  41. package/src/core/layout/array.ts +181 -0
  42. package/src/core/layout/create.ts +55 -0
  43. package/src/core/layout/index.ts +26 -0
  44. package/src/core/layout/object.ts +124 -0
  45. package/src/core/layout/processor.ts +58 -0
  46. package/src/core/layout/result.ts +85 -0
  47. package/src/core/layout/types.ts +125 -0
  48. package/src/core/layout/utils.ts +136 -0
  49. package/src/index.ts +1 -0
  50. package/src/styles/abstract/_variables.scss +28 -0
  51. package/src/styles/components/_navigation-mobile.scss +244 -0
  52. package/src/styles/components/_navigation-system.scss +151 -0
  53. package/src/styles/components/_switch.scss +133 -69
  54. package/src/styles/components/_textfield.scss +259 -27
  55. package/demo/build.ts +0 -349
  56. package/demo/index.html +0 -110
  57. package/demo/main.js +0 -448
  58. package/demo/styles.css +0 -239
  59. package/server.ts +0 -86
  60. package/src/components/slider/features/slider.ts +0 -318
  61. package/src/components/slider/features/structure.ts +0 -181
  62. package/src/components/slider/features/ui.ts +0 -388
  63. package/src/components/textfield/constants.ts +0 -100
  64. package/src/core/layout/index.js +0 -95
@@ -1,3 +1,4 @@
1
1
  // src/components/switch/index.ts
2
2
  export { default } from './switch'
3
3
  export { SwitchConfig, SwitchComponent } from './types'
4
+ export { withSupportingText, SupportingTextComponent } from './features'
@@ -11,11 +11,11 @@ import {
11
11
  withCheckable
12
12
  } from '../../core/compose/features';
13
13
  import { withAPI } from './api';
14
+ import { withSupportingText } from './features';
14
15
  import { SwitchConfig, SwitchComponent, BaseComponent } from './types';
15
16
  import {
16
17
  createBaseConfig,
17
18
  getElementConfig,
18
- withLabelPosition,
19
19
  getApiConfig
20
20
  } from './config';
21
21
 
@@ -32,10 +32,10 @@ const createSwitch = (config: SwitchConfig = {}): SwitchComponent => {
32
32
  createBase,
33
33
  withEvents(), // Move events first to ensure system is available
34
34
  withElement(getElementConfig(baseConfig)),
35
+ withTextLabel(baseConfig),
35
36
  withInput(baseConfig),
36
37
  withTrack(baseConfig),
37
- withTextLabel(baseConfig),
38
- withLabelPosition(baseConfig),
38
+ withSupportingText(baseConfig),
39
39
  withCheckable(baseConfig),
40
40
  withDisabled(baseConfig),
41
41
  withLifecycle(),
@@ -24,8 +24,11 @@ export interface SwitchConfig {
24
24
  /** Label text */
25
25
  label?: string;
26
26
 
27
- /** Label position (start/end) */
28
- labelPosition?: SwitchPosition | string;
27
+ /** Supporting text content */
28
+ supportingText?: string;
29
+
30
+ /** Whether supporting text indicates an error */
31
+ error?: boolean;
29
32
 
30
33
  /** Additional CSS classes */
31
34
  class?: string;
@@ -77,6 +80,15 @@ export interface SwitchComponent {
77
80
  /** Gets the switch's label text */
78
81
  getLabel: () => string;
79
82
 
83
+ /** Supporting text element */
84
+ supportingTextElement: HTMLElement | null;
85
+
86
+ /** Sets supporting text content */
87
+ setSupportingText: (text: string, isError?: boolean) => SwitchComponent;
88
+
89
+ /** Removes supporting text */
90
+ removeSupportingText: () => SwitchComponent;
91
+
80
92
  /** Adds event listener */
81
93
  on: (event: string, handler: Function) => SwitchComponent;
82
94
 
@@ -1,6 +1,11 @@
1
1
  // src/components/textfield/api.ts
2
2
  import { BaseComponent, TextfieldComponent, ApiOptions } from './types';
3
3
 
4
+ /**
5
+ * Enhances textfield component with API methods
6
+ * @param {ApiOptions} options - API configuration
7
+ * @returns {Function} Higher-order function that adds API methods to component
8
+ */
4
9
  /**
5
10
  * Enhances textfield component with API methods
6
11
  * @param {ApiOptions} options - API configuration
@@ -43,6 +48,54 @@ export const withAPI = ({ disabled, lifecycle }: ApiOptions) =>
43
48
  getLabel(): string {
44
49
  return component.label?.getText() || '';
45
50
  },
51
+
52
+ // Leading icon management (if present)
53
+ leadingIcon: component.leadingIcon || null,
54
+ setLeadingIcon(html: string): TextfieldComponent {
55
+ if (component.setLeadingIcon) {
56
+ component.setLeadingIcon(html);
57
+ }
58
+ return this;
59
+ },
60
+
61
+ removeLeadingIcon(): TextfieldComponent {
62
+ if (component.removeLeadingIcon) {
63
+ component.removeLeadingIcon();
64
+ }
65
+ return this;
66
+ },
67
+
68
+ // Trailing icon management (if present)
69
+ trailingIcon: component.trailingIcon || null,
70
+ setTrailingIcon(html: string): TextfieldComponent {
71
+ if (component.setTrailingIcon) {
72
+ component.setTrailingIcon(html);
73
+ }
74
+ return this;
75
+ },
76
+
77
+ removeTrailingIcon(): TextfieldComponent {
78
+ if (component.removeTrailingIcon) {
79
+ component.removeTrailingIcon();
80
+ }
81
+ return this;
82
+ },
83
+
84
+ // Supporting text management (if present)
85
+ supportingTextElement: component.supportingTextElement || null,
86
+ setSupportingText(text: string, isError?: boolean): TextfieldComponent {
87
+ if (component.setSupportingText) {
88
+ component.setSupportingText(text, isError);
89
+ }
90
+ return this;
91
+ },
92
+
93
+ removeSupportingText(): TextfieldComponent {
94
+ if (component.removeSupportingText) {
95
+ component.removeSupportingText();
96
+ }
97
+ return this;
98
+ },
46
99
 
47
100
  // Event handling
48
101
  on(event: string, handler: Function): TextfieldComponent {
@@ -0,0 +1,322 @@
1
+ // src/components/textfield/features.ts
2
+ import { BaseComponent, ElementComponent } from '../../core/compose/component';
3
+
4
+ /**
5
+ * Configuration for leading icon feature
6
+ */
7
+ export interface LeadingIconConfig {
8
+ /**
9
+ * Leading icon HTML content
10
+ */
11
+ leadingIcon?: string;
12
+
13
+ /**
14
+ * CSS class prefix
15
+ */
16
+ prefix?: string;
17
+
18
+ /**
19
+ * Component name
20
+ */
21
+ componentName?: string;
22
+
23
+ [key: string]: any;
24
+ }
25
+
26
+ /**
27
+ * Configuration for trailing icon feature
28
+ */
29
+ export interface TrailingIconConfig {
30
+ /**
31
+ * Trailing icon HTML content
32
+ */
33
+ trailingIcon?: string;
34
+
35
+ /**
36
+ * CSS class prefix
37
+ */
38
+ prefix?: string;
39
+
40
+ /**
41
+ * Component name
42
+ */
43
+ componentName?: string;
44
+
45
+ [key: string]: any;
46
+ }
47
+
48
+ /**
49
+ * Configuration for supporting text feature
50
+ */
51
+ export interface SupportingTextConfig {
52
+ /**
53
+ * Supporting text content
54
+ */
55
+ supportingText?: string;
56
+
57
+ /**
58
+ * Whether supporting text indicates an error
59
+ */
60
+ error?: boolean;
61
+
62
+ /**
63
+ * CSS class prefix
64
+ */
65
+ prefix?: string;
66
+
67
+ /**
68
+ * Component name
69
+ */
70
+ componentName?: string;
71
+
72
+ [key: string]: any;
73
+ }
74
+
75
+ /**
76
+ * Component with leading icon capabilities
77
+ */
78
+ export interface LeadingIconComponent extends BaseComponent {
79
+ /**
80
+ * Leading icon element
81
+ */
82
+ leadingIcon: HTMLElement | null;
83
+
84
+ /**
85
+ * Sets leading icon content
86
+ * @param html - HTML content for the icon
87
+ * @returns Component instance for chaining
88
+ */
89
+ setLeadingIcon: (html: string) => LeadingIconComponent;
90
+
91
+ /**
92
+ * Removes leading icon
93
+ * @returns Component instance for chaining
94
+ */
95
+ removeLeadingIcon: () => LeadingIconComponent;
96
+ }
97
+
98
+ /**
99
+ * Component with trailing icon capabilities
100
+ */
101
+ export interface TrailingIconComponent extends BaseComponent {
102
+ /**
103
+ * Trailing icon element
104
+ */
105
+ trailingIcon: HTMLElement | null;
106
+
107
+ /**
108
+ * Sets trailing icon content
109
+ * @param html - HTML content for the icon
110
+ * @returns Component instance for chaining
111
+ */
112
+ setTrailingIcon: (html: string) => TrailingIconComponent;
113
+
114
+ /**
115
+ * Removes trailing icon
116
+ * @returns Component instance for chaining
117
+ */
118
+ removeTrailingIcon: () => TrailingIconComponent;
119
+ }
120
+
121
+ /**
122
+ * Component with supporting text capabilities
123
+ */
124
+ export interface SupportingTextComponent extends BaseComponent {
125
+ /**
126
+ * Supporting text element
127
+ */
128
+ supportingTextElement: HTMLElement | null;
129
+
130
+ /**
131
+ * Sets supporting text content
132
+ * @param text - Text content
133
+ * @param isError - Whether text represents an error
134
+ * @returns Component instance for chaining
135
+ */
136
+ setSupportingText: (text: string, isError?: boolean) => SupportingTextComponent;
137
+
138
+ /**
139
+ * Removes supporting text
140
+ * @returns Component instance for chaining
141
+ */
142
+ removeSupportingText: () => SupportingTextComponent;
143
+ }
144
+
145
+ /**
146
+ * Creates and manages a leading icon for a component
147
+ * @param config - Configuration object with leading icon settings
148
+ * @returns Function that enhances a component with leading icon functionality
149
+ */
150
+ export const withLeadingIcon = <T extends LeadingIconConfig>(config: T) =>
151
+ <C extends ElementComponent>(component: C): C & LeadingIconComponent => {
152
+ if (!config.leadingIcon) {
153
+ return component as C & LeadingIconComponent;
154
+ }
155
+
156
+ // Create icon element
157
+ const PREFIX = config.prefix || 'mtrl';
158
+ const iconElement = document.createElement('span');
159
+ iconElement.className = `${PREFIX}-${config.componentName || 'textfield'}-leading-icon`;
160
+ iconElement.innerHTML = config.leadingIcon;
161
+
162
+ // Add leading icon to the component
163
+ component.element.appendChild(iconElement);
164
+
165
+ // Add leading-icon class to the component
166
+ component.element.classList.add(`${PREFIX}-${config.componentName || 'textfield'}--with-leading-icon`);
167
+
168
+ // When there's a leading icon, adjust input padding
169
+ if (component.input) {
170
+ component.input.classList.add(`${PREFIX}-${config.componentName || 'textfield'}-input--with-leading-icon`);
171
+ }
172
+
173
+ // Add lifecycle integration if available
174
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
175
+ const originalDestroy = component.lifecycle.destroy;
176
+ component.lifecycle.destroy = () => {
177
+ iconElement.remove();
178
+ originalDestroy.call(component.lifecycle);
179
+ };
180
+ }
181
+
182
+ return {
183
+ ...component,
184
+ leadingIcon: iconElement,
185
+
186
+ setLeadingIcon(html: string) {
187
+ iconElement.innerHTML = html;
188
+ return this;
189
+ },
190
+
191
+ removeLeadingIcon() {
192
+ if (iconElement.parentNode) {
193
+ iconElement.remove();
194
+ component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-leading-icon`);
195
+ if (component.input) {
196
+ component.input.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}-input--with-leading-icon`);
197
+ }
198
+ this.leadingIcon = null;
199
+ }
200
+ return this;
201
+ }
202
+ };
203
+ };
204
+
205
+ /**
206
+ * Creates and manages a trailing icon for a component
207
+ * @param config - Configuration object with trailing icon settings
208
+ * @returns Function that enhances a component with trailing icon functionality
209
+ */
210
+ export const withTrailingIcon = <T extends TrailingIconConfig>(config: T) =>
211
+ <C extends ElementComponent>(component: C): C & TrailingIconComponent => {
212
+ if (!config.trailingIcon) {
213
+ return component as C & TrailingIconComponent;
214
+ }
215
+
216
+ // Create icon element
217
+ const PREFIX = config.prefix || 'mtrl';
218
+ const iconElement = document.createElement('span');
219
+ iconElement.className = `${PREFIX}-${config.componentName || 'textfield'}-trailing-icon`;
220
+ iconElement.innerHTML = config.trailingIcon;
221
+
222
+ // Add trailing icon to the component
223
+ component.element.appendChild(iconElement);
224
+
225
+ // Add trailing-icon class to the component
226
+ component.element.classList.add(`${PREFIX}-${config.componentName || 'textfield'}--with-trailing-icon`);
227
+
228
+ // When there's a trailing icon, adjust input padding
229
+ if (component.input) {
230
+ component.input.classList.add(`${PREFIX}-${config.componentName || 'textfield'}-input--with-trailing-icon`);
231
+ }
232
+
233
+ // Add lifecycle integration if available
234
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
235
+ const originalDestroy = component.lifecycle.destroy;
236
+ component.lifecycle.destroy = () => {
237
+ iconElement.remove();
238
+ originalDestroy.call(component.lifecycle);
239
+ };
240
+ }
241
+
242
+ return {
243
+ ...component,
244
+ trailingIcon: iconElement,
245
+
246
+ setTrailingIcon(html: string) {
247
+ iconElement.innerHTML = html;
248
+ return this;
249
+ },
250
+
251
+ removeTrailingIcon() {
252
+ if (iconElement.parentNode) {
253
+ iconElement.remove();
254
+ component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-trailing-icon`);
255
+ if (component.input) {
256
+ component.input.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}-input--with-trailing-icon`);
257
+ }
258
+ this.trailingIcon = null;
259
+ }
260
+ return this;
261
+ }
262
+ };
263
+ };
264
+
265
+ /**
266
+ * Creates and manages supporting text for a component
267
+ * @param config - Configuration object with supporting text settings
268
+ * @returns Function that enhances a component with supporting text functionality
269
+ */
270
+ export const withSupportingText = <T extends SupportingTextConfig>(config: T) =>
271
+ <C extends ElementComponent>(component: C): C & SupportingTextComponent => {
272
+ if (!config.supportingText) {
273
+ return component as C & SupportingTextComponent;
274
+ }
275
+
276
+ // Create supporting text element
277
+ const PREFIX = config.prefix || 'mtrl';
278
+ const supportingElement = document.createElement('div');
279
+ supportingElement.className = `${PREFIX}-${config.componentName || 'textfield'}-helper`;
280
+ supportingElement.textContent = config.supportingText;
281
+
282
+ if (config.error) {
283
+ supportingElement.classList.add(`${PREFIX}-${config.componentName || 'textfield'}-helper--error`);
284
+ component.element.classList.add(`${PREFIX}-${config.componentName || 'textfield'}--error`);
285
+ }
286
+
287
+ // Add supporting text to the component
288
+ component.element.appendChild(supportingElement);
289
+
290
+ // Add lifecycle integration if available
291
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
292
+ const originalDestroy = component.lifecycle.destroy;
293
+ component.lifecycle.destroy = () => {
294
+ supportingElement.remove();
295
+ originalDestroy.call(component.lifecycle);
296
+ };
297
+ }
298
+
299
+ return {
300
+ ...component,
301
+ supportingTextElement: supportingElement,
302
+
303
+ setSupportingText(text: string, isError = false) {
304
+ supportingElement.textContent = text;
305
+
306
+ // Handle error state
307
+ supportingElement.classList.toggle(`${PREFIX}-${config.componentName || 'textfield'}-helper--error`, isError);
308
+ component.element.classList.toggle(`${PREFIX}-${config.componentName || 'textfield'}--error`, isError);
309
+
310
+ return this;
311
+ },
312
+
313
+ removeSupportingText() {
314
+ if (supportingElement.parentNode) {
315
+ supportingElement.remove();
316
+ this.supportingTextElement = null;
317
+ component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--error`);
318
+ }
319
+ return this;
320
+ }
321
+ };
322
+ };
@@ -10,6 +10,11 @@ import {
10
10
  withTextLabel
11
11
  } from '../../core/compose/features';
12
12
  import { withAPI } from './api';
13
+ import {
14
+ withLeadingIcon,
15
+ withTrailingIcon,
16
+ withSupportingText
17
+ } from './features';
13
18
  import { TextfieldConfig, TextfieldComponent } from './types';
14
19
  import {
15
20
  createBaseConfig,
@@ -33,6 +38,9 @@ const createTextfield = (config: TextfieldConfig = {}): TextfieldComponent => {
33
38
  withVariant(baseConfig),
34
39
  withTextInput(baseConfig),
35
40
  withTextLabel(baseConfig),
41
+ withLeadingIcon(baseConfig),
42
+ withTrailingIcon(baseConfig),
43
+ withSupportingText(baseConfig),
36
44
  withDisabled(baseConfig),
37
45
  withLifecycle(),
38
46
  comp => withAPI(getApiConfig(comp))(comp)
@@ -31,9 +31,6 @@ export interface TextfieldConfig {
31
31
  /** Label text */
32
32
  label?: string;
33
33
 
34
- /** Placeholder text */
35
- placeholder?: string;
36
-
37
34
  /** Initial value */
38
35
  value?: string;
39
36
 
@@ -52,6 +49,18 @@ export interface TextfieldConfig {
52
49
  /** Autocomplete attribute */
53
50
  autocomplete?: string;
54
51
 
52
+ /** Leading icon HTML content */
53
+ leadingIcon?: string;
54
+
55
+ /** Trailing icon HTML content */
56
+ trailingIcon?: string;
57
+
58
+ /** Supporting text content */
59
+ supportingText?: string;
60
+
61
+ /** Whether supporting text indicates an error */
62
+ error?: boolean;
63
+
55
64
  /** Additional CSS classes */
56
65
  class?: string;
57
66
 
@@ -48,12 +48,9 @@ const CLOCK_CONSTANTS = {
48
48
  function getThemeColors(prefix: string): ThemeColors {
49
49
  const root = document.documentElement;
50
50
  const styles = getComputedStyle(root);
51
- console.log('styles', styles)
51
+
52
52
  // Extract primary color
53
53
  const primaryColor = styles.getPropertyValue(`--${prefix}-sys-color-primary`).trim() || '#6750A4';
54
-
55
-
56
- console.log('primaryColor', primaryColor)
57
54
 
58
55
  // Extract on-primary color
59
56
  const onPrimaryColor = styles.getPropertyValue(`--${prefix}-sys-color-on-primary`).trim() || '#FFFFFF';
@@ -112,14 +112,14 @@ export interface TextInputComponent extends ElementComponent {
112
112
  */
113
113
  export const withTextInput = <T extends TextInputConfig>(config: T = {} as T) =>
114
114
  <C extends ElementComponent>(component: C): C & TextInputComponent => {
115
- const input = document.createElement(config.multiline ? 'textarea' : 'input') as
115
+ const isMultiline = config.multiline || config.type === 'multiline';
116
+ const input = document.createElement(isMultiline ? 'textarea' : 'input') as
116
117
  HTMLInputElement | HTMLTextAreaElement;
117
118
 
118
119
  input.className = `${component.getClass('textfield')}-input`;
119
120
 
120
121
  // Set input attributes
121
122
  const attributes: Record<string, string | number | boolean | undefined> = {
122
- type: config.multiline ? undefined : (config.type || 'text'),
123
123
  name: config.name,
124
124
  required: config.required,
125
125
  disabled: config.disabled,
@@ -129,6 +129,14 @@ export const withTextInput = <T extends TextInputConfig>(config: T = {} as T) =>
129
129
  value: config.value || ''
130
130
  };
131
131
 
132
+ // Only set type attribute for input elements, not for textarea
133
+ if (!isMultiline) {
134
+ attributes.type = config.type || 'text';
135
+ } else {
136
+ // For textarea, add a data attribute to identify it as multiline
137
+ attributes['data-type'] = 'multiline';
138
+ }
139
+
132
140
  Object.entries(attributes).forEach(([key, value]) => {
133
141
  if (value !== null && value !== undefined) {
134
142
  if (typeof value === 'boolean') {
@@ -188,6 +196,11 @@ export const withTextInput = <T extends TextInputConfig>(config: T = {} as T) =>
188
196
  // Initial state
189
197
  updateInputState();
190
198
 
199
+ // Add multiline class to the component if it's a textarea
200
+ if (isMultiline) {
201
+ component.element.classList.add(`${component.getClass('textfield')}--multiline`);
202
+ }
203
+
191
204
  component.element.appendChild(input);
192
205
 
193
206
  // Cleanup
@@ -0,0 +1,45 @@
1
+ // src/core/composition/features/dom.ts
2
+ import createLayout from '../../layout';
3
+
4
+ /**
5
+ * Creates DOM elements from component schema using the core createLayout utility
6
+ * This is a key feature that bridges the gap between declarative schema and actual DOM
7
+ *
8
+ * @returns Component enhancer that creates DOM structure from schema
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // Materialize component schema into DOM
13
+ * const component = pipe(
14
+ * createBase,
15
+ * withLayout(config),
16
+ * withIcon(config),
17
+ * withLabel(config),
18
+ * withDom()
19
+ * )(config);
20
+ * ```
21
+ */
22
+ export const withDom = () => component => {
23
+ // Return unmodified component if no schema
24
+ if (!component.schema) {
25
+ return component;
26
+ }
27
+
28
+ try {
29
+ // Use the createLayout function to build the DOM from schema
30
+ const layout = createLayout(component.schema);
31
+
32
+ // Use the layout's utility functions to get components
33
+ const components = layout.getAll();
34
+
35
+ // Return enhanced component with DOM layout
36
+ return {
37
+ ...component,
38
+ element: layout.element, // Root element
39
+ components // All component elements
40
+ };
41
+ } catch (error) {
42
+ console.error('Failed to create DOM structure:', error);
43
+ throw new Error(`Failed to create component DOM: ${error.message}`);
44
+ }
45
+ };