mtrl 0.2.9 → 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.
- package/index.ts +2 -0
- package/package.json +1 -1
- package/src/components/button/button.ts +34 -5
- package/src/components/navigation/system/core.ts +302 -0
- package/src/components/navigation/system/events.ts +240 -0
- package/src/components/navigation/system/index.ts +184 -0
- package/src/components/navigation/system/mobile.ts +278 -0
- package/src/components/navigation/system/state.ts +77 -0
- package/src/components/navigation/system/types.ts +364 -0
- package/src/components/slider/config.ts +2 -2
- package/src/components/slider/features/controller.ts +1 -25
- package/src/components/slider/features/handlers.ts +0 -1
- package/src/components/slider/features/range.ts +7 -7
- package/src/components/slider/{structure.ts → schema.ts} +2 -13
- package/src/components/slider/slider.ts +3 -2
- package/src/components/switch/api.ts +16 -0
- package/src/components/switch/config.ts +1 -18
- package/src/components/switch/features.ts +198 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.ts +3 -3
- package/src/components/switch/types.ts +14 -2
- package/src/core/composition/features/dom.ts +26 -14
- package/src/core/composition/features/icon.ts +18 -18
- package/src/core/composition/features/index.ts +3 -2
- package/src/core/composition/features/label.ts +16 -17
- package/src/core/composition/features/layout.ts +47 -0
- package/src/core/composition/index.ts +4 -4
- package/src/core/layout/README.md +350 -0
- package/src/core/layout/array.ts +181 -0
- package/src/core/layout/create.ts +55 -0
- package/src/core/layout/index.ts +26 -0
- package/src/core/layout/object.ts +124 -0
- package/src/core/layout/processor.ts +58 -0
- package/src/core/layout/result.ts +85 -0
- package/src/core/layout/types.ts +125 -0
- package/src/core/layout/utils.ts +136 -0
- package/src/styles/abstract/_variables.scss +28 -0
- package/src/styles/components/_switch.scss +133 -69
- package/src/styles/components/_textfield.scss +9 -16
- package/src/components/navigation/system-types.ts +0 -124
- package/src/components/navigation/system.ts +0 -776
- package/src/core/composition/features/structure.ts +0 -22
- package/src/core/layout/index.js +0 -95
- package/src/core/structure.ts +0 -288
|
@@ -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
|
+
};
|
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
28
|
-
|
|
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,33 +1,45 @@
|
|
|
1
1
|
// src/core/composition/features/dom.ts
|
|
2
|
-
import
|
|
2
|
+
import createLayout from '../../layout';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Creates DOM elements from
|
|
6
|
-
* This is a key feature that bridges the gap between declarative
|
|
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
|
|
12
|
-
if (!component.
|
|
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
|
|
18
|
-
const
|
|
29
|
+
// Use the createLayout function to build the DOM from schema
|
|
30
|
+
const layout = createLayout(component.schema);
|
|
19
31
|
|
|
20
|
-
// Use the
|
|
21
|
-
const components =
|
|
32
|
+
// Use the layout's utility functions to get components
|
|
33
|
+
const components = layout.getAll();
|
|
22
34
|
|
|
23
|
-
// Return enhanced component with DOM
|
|
35
|
+
// Return enhanced component with DOM layout
|
|
24
36
|
return {
|
|
25
37
|
...component,
|
|
26
|
-
element:
|
|
27
|
-
components
|
|
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
|
|
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
|
|
38
|
-
* Unlike the traditional withIcon, this modifies the
|
|
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
|
|
42
|
+
* @returns Component enhancer that adds icon to schema
|
|
43
43
|
*
|
|
44
44
|
* @example
|
|
45
45
|
* ```ts
|
|
46
|
-
* // Add icon to a
|
|
46
|
+
* // Add icon to a component schema
|
|
47
47
|
* const component = pipe(
|
|
48
48
|
* createBase,
|
|
49
|
-
*
|
|
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
|
|
56
|
-
if (!config.icon || !component.
|
|
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
|
|
66
|
-
const
|
|
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 =
|
|
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
|
-
|
|
81
|
+
schema.element.options.className = `${elementClasses} ${iconModifierClass}`.trim();
|
|
82
82
|
}
|
|
83
83
|
} else {
|
|
84
|
-
|
|
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 = { ...
|
|
113
|
-
|
|
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
|
-
|
|
119
|
+
schema.element.children.icon = iconDef;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
// Return component with updated
|
|
122
|
+
// Return component with updated schema
|
|
123
123
|
return {
|
|
124
124
|
...component,
|
|
125
|
-
|
|
125
|
+
schema
|
|
126
126
|
};
|
|
127
127
|
} catch (error) {
|
|
128
|
-
console.warn('Error enhancing
|
|
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
|
|
43
|
-
* Unlike the traditional withLabel, this modifies the
|
|
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
|
|
47
|
+
* @returns Component enhancer that adds label to schema
|
|
48
48
|
*
|
|
49
49
|
* @example
|
|
50
50
|
* ```ts
|
|
51
|
-
* // Add label to a
|
|
51
|
+
* // Add label to a component schema
|
|
52
52
|
* const component = pipe(
|
|
53
53
|
* createBase,
|
|
54
|
-
*
|
|
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
|
|
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
|
|
81
|
-
const
|
|
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
|
|
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
|
-
|
|
136
|
+
schema.element.children.label = labelDef;
|
|
138
137
|
} else {
|
|
139
138
|
// Add at beginning of root element (start/top)
|
|
140
|
-
const existingChildren = { ...
|
|
141
|
-
|
|
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
|
|
146
|
+
// Return component with updated schema
|
|
148
147
|
return {
|
|
149
148
|
...component,
|
|
150
|
-
|
|
149
|
+
schema
|
|
151
150
|
};
|
|
152
151
|
} catch (error) {
|
|
153
|
-
console.warn('Error enhancing
|
|
152
|
+
console.warn('Error enhancing schema with label:', error);
|
|
154
153
|
return component;
|
|
155
154
|
}
|
|
156
155
|
};
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
21
|
-
withDom
|
|
20
|
+
withLayout,
|
|
21
|
+
withDom,
|
|
22
22
|
withIcon,
|
|
23
23
|
withLabel,
|
|
24
24
|
} from './features';
|