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
@@ -48,6 +48,22 @@ export const withAPI = ({ disabled, lifecycle, checkable }: ApiOptions) =>
48
48
  getLabel(): string {
49
49
  return component.text?.getText() || '';
50
50
  },
51
+
52
+ // Supporting text management (if present)
53
+ supportingTextElement: component.supportingTextElement || null,
54
+ setSupportingText(text: string, isError?: boolean): SwitchComponent {
55
+ if (component.setSupportingText) {
56
+ component.setSupportingText(text, isError);
57
+ }
58
+ return this;
59
+ },
60
+
61
+ removeSupportingText(): SwitchComponent {
62
+ if (component.removeSupportingText) {
63
+ component.removeSupportingText();
64
+ }
65
+ return this;
66
+ },
51
67
 
52
68
  // Event handling
53
69
  on(event: string, handler: Function): SwitchComponent {
@@ -9,9 +9,7 @@ import { SwitchConfig, BaseComponent, ApiOptions } from './types';
9
9
  /**
10
10
  * Default configuration for the Switch component
11
11
  */
12
- export const defaultConfig: SwitchConfig = {
13
- labelPosition: 'end'
14
- };
12
+ export const defaultConfig: SwitchConfig = {};
15
13
 
16
14
  /**
17
15
  * Creates the base configuration for Switch component
@@ -34,21 +32,6 @@ export const getElementConfig = (config: SwitchConfig) =>
34
32
  interactive: true
35
33
  });
36
34
 
37
- /**
38
- * Applies label position class to the component
39
- * @param {SwitchConfig} config - Component configuration
40
- */
41
- export const withLabelPosition = (config: SwitchConfig) => (component: BaseComponent): BaseComponent => {
42
- if (!config.label) return component;
43
-
44
- const position = config.labelPosition || 'end';
45
- const positionClass = `${config.prefix}-switch--label-${position}`;
46
-
47
- component.element.classList.add(positionClass);
48
-
49
- return component;
50
- };
51
-
52
35
  /**
53
36
  * Creates API configuration for the Switch component
54
37
  * @param {BaseComponent} comp - Component with disabled, lifecycle, and checkable features
@@ -0,0 +1,198 @@
1
+ // src/components/switch/features.ts
2
+ import { BaseComponent, ElementComponent } from '../../core/compose/component';
3
+
4
+ /**
5
+ * Configuration for supporting text feature
6
+ */
7
+ export interface SupportingTextConfig {
8
+ /**
9
+ * Supporting text content
10
+ */
11
+ supportingText?: string;
12
+
13
+ /**
14
+ * Whether supporting text indicates an error
15
+ */
16
+ error?: boolean;
17
+
18
+ /**
19
+ * CSS class prefix
20
+ */
21
+ prefix?: string;
22
+
23
+ /**
24
+ * Component name
25
+ */
26
+ componentName?: string;
27
+
28
+ [key: string]: any;
29
+ }
30
+
31
+ /**
32
+ * Component with supporting text capabilities
33
+ */
34
+ export interface SupportingTextComponent extends BaseComponent {
35
+ /**
36
+ * Supporting text element
37
+ */
38
+ supportingTextElement: HTMLElement | null;
39
+
40
+ /**
41
+ * Sets supporting text content
42
+ * @param text - Text content
43
+ * @param isError - Whether text represents an error
44
+ * @returns Component instance for chaining
45
+ */
46
+ setSupportingText: (text: string, isError?: boolean) => SupportingTextComponent;
47
+
48
+ /**
49
+ * Removes supporting text
50
+ * @returns Component instance for chaining
51
+ */
52
+ removeSupportingText: () => SupportingTextComponent;
53
+ }
54
+
55
+ /**
56
+ * Helper to ensure the switch has the proper container/content structure
57
+ * @param component - The component to enhance
58
+ * @param prefix - CSS class prefix
59
+ * @param componentName - Component name
60
+ * @returns The container and content elements
61
+ */
62
+ const ensureSwitchStructure = (
63
+ component: ElementComponent,
64
+ prefix: string,
65
+ componentName: string
66
+ ) => {
67
+ const PREFIX = prefix || 'mtrl';
68
+ const COMPONENT = componentName || 'switch';
69
+
70
+ // Create or find container
71
+ let container = component.element.querySelector(`.${PREFIX}-${COMPONENT}-container`);
72
+ if (!container) {
73
+ container = document.createElement('div');
74
+ container.className = `${PREFIX}-${COMPONENT}-container`;
75
+
76
+ // Find input and track to move them to container
77
+ const input = component.element.querySelector(`.${PREFIX}-${COMPONENT}-input`);
78
+ const track = component.element.querySelector(`.${PREFIX}-${COMPONENT}-track`);
79
+
80
+ // Gather all elements except container
81
+ const elementsToMove = [];
82
+ if (input) elementsToMove.push(input);
83
+ if (track) elementsToMove.push(track);
84
+
85
+ // Create content wrapper
86
+ const contentWrapper = document.createElement('div');
87
+ contentWrapper.className = `${PREFIX}-${COMPONENT}-content`;
88
+
89
+ // Find label and move to content
90
+ const label = component.element.querySelector(`.${PREFIX}-${COMPONENT}-label`);
91
+ if (label) {
92
+ contentWrapper.appendChild(label);
93
+ }
94
+
95
+ // Add content wrapper to container first
96
+ container.appendChild(contentWrapper);
97
+
98
+ // Add other elements to container
99
+ elementsToMove.forEach(el => container.appendChild(el));
100
+
101
+ // Add container to component
102
+ component.element.appendChild(container);
103
+
104
+ return { container, contentWrapper };
105
+ }
106
+
107
+ // Container exists, find or create content wrapper
108
+ let contentWrapper = component.element.querySelector(`.${PREFIX}-${COMPONENT}-content`);
109
+ if (!contentWrapper) {
110
+ contentWrapper = document.createElement('div');
111
+ contentWrapper.className = `${PREFIX}-${COMPONENT}-content`;
112
+
113
+ // Find label to move to content
114
+ const label = component.element.querySelector(`.${PREFIX}-${COMPONENT}-label`);
115
+ if (label) {
116
+ contentWrapper.appendChild(label);
117
+ }
118
+
119
+ // Insert content wrapper at beginning of container
120
+ container.insertBefore(contentWrapper, container.firstChild);
121
+ }
122
+
123
+ return { container, contentWrapper };
124
+ };
125
+
126
+ /**
127
+ * Creates and manages supporting text for a component
128
+ * @param config - Configuration object with supporting text settings
129
+ * @returns Function that enhances a component with supporting text functionality
130
+ */
131
+ export const withSupportingText = <T extends SupportingTextConfig>(config: T) =>
132
+ <C extends ElementComponent>(component: C): C & SupportingTextComponent => {
133
+ const PREFIX = config.prefix || 'mtrl';
134
+ const COMPONENT = config.componentName || 'switch';
135
+
136
+ // Ensure we have the proper container/content structure
137
+ const { contentWrapper } = ensureSwitchStructure(component, PREFIX, COMPONENT);
138
+
139
+ // Create supporting text element if needed
140
+ let supportingElement = null;
141
+ if (config.supportingText) {
142
+ supportingElement = document.createElement('div');
143
+ supportingElement.className = `${PREFIX}-${COMPONENT}-helper`;
144
+ supportingElement.textContent = config.supportingText;
145
+
146
+ if (config.error) {
147
+ supportingElement.classList.add(`${PREFIX}-${COMPONENT}-helper--error`);
148
+ component.element.classList.add(`${PREFIX}-${COMPONENT}--error`);
149
+ }
150
+
151
+ // Add supporting text to the content wrapper
152
+ contentWrapper.appendChild(supportingElement);
153
+ }
154
+
155
+ // Add lifecycle integration if available
156
+ if ('lifecycle' in component && component.lifecycle?.destroy && supportingElement) {
157
+ const originalDestroy = component.lifecycle.destroy;
158
+ component.lifecycle.destroy = () => {
159
+ if (supportingElement) supportingElement.remove();
160
+ originalDestroy.call(component.lifecycle);
161
+ };
162
+ }
163
+
164
+ return {
165
+ ...component,
166
+ supportingTextElement: supportingElement,
167
+
168
+ setSupportingText(text: string, isError = false) {
169
+ const { contentWrapper } = ensureSwitchStructure(component, PREFIX, COMPONENT);
170
+ let supportingElement = this.supportingTextElement;
171
+
172
+ if (!supportingElement) {
173
+ // Create if it doesn't exist
174
+ supportingElement = document.createElement('div');
175
+ supportingElement.className = `${PREFIX}-${COMPONENT}-helper`;
176
+ contentWrapper.appendChild(supportingElement);
177
+ this.supportingTextElement = supportingElement;
178
+ }
179
+
180
+ supportingElement.textContent = text;
181
+
182
+ // Handle error state
183
+ supportingElement.classList.toggle(`${PREFIX}-${COMPONENT}-helper--error`, isError);
184
+ component.element.classList.toggle(`${PREFIX}-${COMPONENT}--error`, isError);
185
+
186
+ return this;
187
+ },
188
+
189
+ removeSupportingText() {
190
+ if (this.supportingTextElement && this.supportingTextElement.parentNode) {
191
+ this.supportingTextElement.remove();
192
+ this.supportingTextElement = null;
193
+ component.element.classList.remove(`${PREFIX}-${COMPONENT}--error`);
194
+ }
195
+ return this;
196
+ }
197
+ };
198
+ };
@@ -1,3 +1,8 @@
1
1
  // src/components/switch/index.ts
2
2
  export { default } from './switch'
3
- export { SwitchConfig, SwitchComponent } from './types'
3
+ export {
4
+ SwitchConfig,
5
+ SwitchComponent,
6
+ SWITCH_LABEL_POSITION
7
+ } from './types'
8
+ 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(),
@@ -2,6 +2,19 @@
2
2
 
3
3
  export type SwitchPosition = 'center' | 'start' | 'end';
4
4
 
5
+ /**
6
+ * Switch label position types
7
+ */
8
+ export type SwitchLabelPosition = 'start' | 'end';
9
+
10
+ /**
11
+ * Switch label positions
12
+ */
13
+ export const SWITCH_LABEL_POSITION = {
14
+ START: 'start',
15
+ END: 'end'
16
+ } as const;
17
+
5
18
  /**
6
19
  * Configuration interface for the Switch component
7
20
  */
@@ -24,8 +37,11 @@ export interface SwitchConfig {
24
37
  /** Label text */
25
38
  label?: string;
26
39
 
27
- /** Label position (start/end) */
28
- labelPosition?: SwitchPosition | string;
40
+ /** Supporting text content */
41
+ supportingText?: string;
42
+
43
+ /** Whether supporting text indicates an error */
44
+ error?: boolean;
29
45
 
30
46
  /** Additional CSS classes */
31
47
  class?: string;
@@ -77,6 +93,15 @@ export interface SwitchComponent {
77
93
  /** Gets the switch's label text */
78
94
  getLabel: () => string;
79
95
 
96
+ /** Supporting text element */
97
+ supportingTextElement: HTMLElement | null;
98
+
99
+ /** Sets supporting text content */
100
+ setSupportingText: (text: string, isError?: boolean) => SwitchComponent;
101
+
102
+ /** Removes supporting text */
103
+ removeSupportingText: () => SwitchComponent;
104
+
80
105
  /** Adds event listener */
81
106
  on: (event: string, handler: Function) => SwitchComponent;
82
107
 
@@ -1,3 +1,9 @@
1
1
  // src/components/textfield/index.ts
2
2
  export { default } from './textfield'
3
- export { TextfieldConfig, TextfieldComponent } from './types'
3
+ export {
4
+ TextfieldConfig,
5
+ TextfieldComponent,
6
+ TEXTFIELD_VARIANTS,
7
+ TEXTFIELD_SIZES,
8
+ TEXTFIELD_TYPES
9
+ } from './types'
@@ -5,6 +5,28 @@
5
5
  */
6
6
  export type TextfieldVariant = 'filled' | 'outlined';
7
7
 
8
+ /**
9
+ * Textfield variant constants
10
+ */
11
+ export const TEXTFIELD_VARIANTS = {
12
+ FILLED: 'filled',
13
+ OUTLINED: 'outlined'
14
+ } as const;
15
+
16
+ /**
17
+ * Available Textfield sizes
18
+ */
19
+ export type TextfieldSize = 'small' | 'medium' | 'large';
20
+
21
+ /**
22
+ * Textfield size constants
23
+ */
24
+ export const TEXTFIELD_SIZES = {
25
+ SMALL: 'small',
26
+ MEDIUM: 'medium',
27
+ LARGE: 'large'
28
+ } as const;
29
+
8
30
  /**
9
31
  * Available Textfield states
10
32
  */
@@ -15,6 +37,20 @@ export type TextfieldStates = 'active' | 'inactive' | 'disabled';
15
37
  */
16
38
  export type TextfieldTypes = 'text' | 'password' | 'email' | 'number' | 'tel' | 'url' | 'search' | 'multiline';
17
39
 
40
+ /**
41
+ * Textfield type constants
42
+ */
43
+ export const TEXTFIELD_TYPES = {
44
+ TEXT: 'text',
45
+ PASSWORD: 'password',
46
+ EMAIL: 'email',
47
+ NUMBER: 'number',
48
+ TEL: 'tel',
49
+ URL: 'url',
50
+ SEARCH: 'search',
51
+ MULTILINE: 'multiline'
52
+ } as const;
53
+
18
54
  /**
19
55
  * Configuration interface for the Textfield component
20
56
  */
@@ -1,33 +1,45 @@
1
1
  // src/core/composition/features/dom.ts
2
- import { createStructure, flattenStructure } from '../../structure';
2
+ import createLayout from '../../layout';
3
3
 
4
4
  /**
5
- * Creates DOM elements from structure definition using the core createStructure utility
6
- * This is a key feature that bridges the gap between declarative structure and actual DOM
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
7
  *
8
- * @returns Component enhancer that creates DOM structure
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
+ * ```
9
21
  */
10
22
  export const withDom = () => component => {
11
- // Return unmodified component if no structure definition
12
- if (!component.structureDefinition) {
23
+ // Return unmodified component if no schema
24
+ if (!component.schema) {
13
25
  return component;
14
26
  }
15
27
 
16
28
  try {
17
- // Use the existing createStructure function to build the DOM
18
- const structure = createStructure(component.structureDefinition);
29
+ // Use the createLayout function to build the DOM from schema
30
+ const layout = createLayout(component.schema);
19
31
 
20
- // Use the existing flattenStructure function to create a flat reference map
21
- const components = structure.getAll();
32
+ // Use the layout's utility functions to get components
33
+ const components = layout.getAll();
22
34
 
23
- // Return enhanced component with DOM structure
35
+ // Return enhanced component with DOM layout
24
36
  return {
25
37
  ...component,
26
- element: structure.element, // Root element
27
- components // All component elements
38
+ element: layout.element, // Root element
39
+ components // All component elements
28
40
  };
29
41
  } catch (error) {
30
42
  console.error('Failed to create DOM structure:', error);
31
- throw new Error(`Failed to create slider DOM: ${error.message}`);
43
+ throw new Error(`Failed to create component DOM: ${error.message}`);
32
44
  }
33
45
  };
@@ -34,26 +34,26 @@ export interface IconConfig {
34
34
  }
35
35
 
36
36
  /**
37
- * Enhances structure definition with an icon element
38
- * Unlike the traditional withIcon, this modifies the structure definition
37
+ * Enhances component schema with an icon element
38
+ * Unlike the traditional withIcon, this modifies the schema
39
39
  * without creating actual DOM elements.
40
40
  *
41
41
  * @param config Configuration containing icon information
42
- * @returns Component enhancer that adds icon to structure definition
42
+ * @returns Component enhancer that adds icon to schema
43
43
  *
44
44
  * @example
45
45
  * ```ts
46
- * // Add icon to a structure definition
46
+ * // Add icon to a component schema
47
47
  * const component = pipe(
48
48
  * createBase,
49
- * withStructure(config),
49
+ * withLayout(config),
50
50
  * withIcon(config)
51
51
  * )(config);
52
52
  * ```
53
53
  */
54
54
  export const withIcon = (config: IconConfig) => component => {
55
- // If no icon or missing structure definition, return unmodified
56
- if (!config.icon || !component.structureDefinition) {
55
+ // If no icon or missing schema, return unmodified
56
+ if (!config.icon || !component.schema) {
57
57
  return component;
58
58
  }
59
59
 
@@ -62,14 +62,14 @@ export const withIcon = (config: IconConfig) => component => {
62
62
  const prefix = config.prefix || component.config?.prefix || 'mtrl';
63
63
  const componentName = config.componentName || component.componentName || 'component';
64
64
 
65
- // Clone the structure definition
66
- const structureDefinition = JSON.parse(JSON.stringify(component.structureDefinition));
65
+ // Clone the schema
66
+ const schema = JSON.parse(JSON.stringify(component.schema));
67
67
 
68
68
  // Determine icon position
69
69
  const position = config.iconPosition || 'start';
70
70
 
71
71
  // Add the --icon modifier class to the main element
72
- const elementClasses = structureDefinition.element.options.className || [];
72
+ const elementClasses = schema.element.options.className || [];
73
73
  const iconModifierClass = `${prefix}-${componentName}--icon`;
74
74
 
75
75
  if (Array.isArray(elementClasses)) {
@@ -78,10 +78,10 @@ export const withIcon = (config: IconConfig) => component => {
78
78
  }
79
79
  } else if (typeof elementClasses === 'string') {
80
80
  if (!elementClasses.includes(iconModifierClass)) {
81
- structureDefinition.element.options.className = `${elementClasses} ${iconModifierClass}`.trim();
81
+ schema.element.options.className = `${elementClasses} ${iconModifierClass}`.trim();
82
82
  }
83
83
  } else {
84
- structureDefinition.element.options.className = [iconModifierClass];
84
+ schema.element.options.className = [iconModifierClass];
85
85
  }
86
86
 
87
87
  // Create icon element definition with component-specific class
@@ -109,23 +109,23 @@ export const withIcon = (config: IconConfig) => component => {
109
109
  // Add icon directly to the main element's children
110
110
  if (position === 'start') {
111
111
  // Create new children object with icon first
112
- const existingChildren = { ...structureDefinition.element.children };
113
- structureDefinition.element.children = {
112
+ const existingChildren = { ...schema.element.children };
113
+ schema.element.children = {
114
114
  icon: iconDef,
115
115
  ...existingChildren
116
116
  };
117
117
  } else {
118
118
  // Add icon after existing children
119
- structureDefinition.element.children.icon = iconDef;
119
+ schema.element.children.icon = iconDef;
120
120
  }
121
121
 
122
- // Return component with updated structure definition
122
+ // Return component with updated schema
123
123
  return {
124
124
  ...component,
125
- structureDefinition
125
+ schema
126
126
  };
127
127
  } catch (error) {
128
- console.warn('Error enhancing structure with icon:', error);
128
+ console.warn('Error enhancing schema with icon:', error);
129
129
  return component;
130
130
  }
131
131
  };
@@ -1,11 +1,12 @@
1
1
  // src/core/composition/features/index.ts
2
2
 
3
3
  // Export composition features
4
+ export { withLayout } from './layout';
4
5
  export { withIcon } from './icon';
5
6
  export { withLabel } from './label';
6
7
  export { withDom } from './dom';
7
- export { withStructure } from './structure';
8
8
 
9
9
  // Re-export interface types for better developer experience
10
10
  export type { IconConfig } from './icon';
11
- export type { LabelConfig } from './label';
11
+ export type { LabelConfig } from './label';
12
+ export type { LayoutConfig } from './layout';
@@ -39,27 +39,26 @@ export interface LabelConfig {
39
39
  }
40
40
 
41
41
  /**
42
- * Enhances structure definition with a label element
43
- * Unlike the traditional withLabel, this modifies the structure definition
42
+ * Enhances component schema with a label element
43
+ * Unlike the traditional withLabel, this modifies the schema
44
44
  * without creating actual DOM elements.
45
45
  *
46
46
  * @param config Configuration containing label information
47
- * @returns Component enhancer that adds label to structure definition
47
+ * @returns Component enhancer that adds label to schema
48
48
  *
49
49
  * @example
50
50
  * ```ts
51
- * // Add label to a structure definition
51
+ * // Add label to a component schema
52
52
  * const component = pipe(
53
53
  * createBase,
54
- * withStructure(config),
54
+ * withLayout(config),
55
55
  * withLabel(config)
56
56
  * )(config);
57
57
  * ```
58
58
  */
59
59
  export const withLabel = (config: LabelConfig) => component => {
60
- // If no label or missing structure definition, return unmodified
61
-
62
- if (!config.label || !component.structureDefinition) {
60
+ // If no label or missing schema, return unmodified
61
+ if (!config.label || !component.schema) {
63
62
  return component;
64
63
  }
65
64
 
@@ -77,8 +76,8 @@ export const withLabel = (config: LabelConfig) => component => {
77
76
  const prefix = config.prefix || component.config?.prefix || 'mtrl';
78
77
  const componentName = config.componentName || component.componentName || 'component';
79
78
 
80
- // Clone the structure definition
81
- const structureDefinition = JSON.parse(JSON.stringify(component.structureDefinition));
79
+ // Clone the schema
80
+ const schema = JSON.parse(JSON.stringify(component.schema));
82
81
 
83
82
  // Determine position (default to 'start')
84
83
  const position = config.labelPosition || 'start';
@@ -89,7 +88,7 @@ export const withLabel = (config: LabelConfig) => component => {
89
88
 
90
89
  // Add required indicator if specified
91
90
  if (config.required) {
92
- // For structure definition we need to define a child element
91
+ // For schema we need to define a child element
93
92
  labelChildren = {
94
93
  requiredIndicator: {
95
94
  name: 'requiredIndicator',
@@ -134,23 +133,23 @@ export const withLabel = (config: LabelConfig) => component => {
134
133
  // Add label to root element's children
135
134
  if (position === 'end' || position === 'bottom') {
136
135
  // Add at end of root element
137
- structureDefinition.element.children.label = labelDef;
136
+ schema.element.children.label = labelDef;
138
137
  } else {
139
138
  // Add at beginning of root element (start/top)
140
- const existingChildren = { ...structureDefinition.element.children };
141
- structureDefinition.element.children = {
139
+ const existingChildren = { ...schema.element.children };
140
+ schema.element.children = {
142
141
  label: labelDef,
143
142
  ...existingChildren
144
143
  };
145
144
  }
146
145
 
147
- // Return component with updated structure definition
146
+ // Return component with updated schema
148
147
  return {
149
148
  ...component,
150
- structureDefinition
149
+ schema
151
150
  };
152
151
  } catch (error) {
153
- console.warn('Error enhancing structure with label:', error);
152
+ console.warn('Error enhancing schema with label:', error);
154
153
  return component;
155
154
  }
156
155
  };