mtrl 0.2.7 → 0.2.9
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 +14 -3
- package/src/components/badge/api.ts +23 -14
- package/src/components/badge/badge.ts +2 -2
- package/src/components/badge/config.ts +10 -11
- package/src/components/badge/features.ts +15 -10
- package/src/components/badge/index.ts +27 -2
- package/src/components/badge/types.ts +28 -8
- package/src/components/bottom-app-bar/bottom-app-bar.ts +2 -44
- package/src/components/bottom-app-bar/config.ts +1 -45
- package/src/components/bottom-app-bar/index.ts +7 -1
- package/src/components/bottom-app-bar/types.ts +7 -1
- package/src/components/button/button.ts +0 -1
- package/src/components/button/config.ts +1 -2
- package/src/components/button/index.ts +10 -2
- package/src/components/button/types.ts +14 -2
- package/src/components/card/config.ts +17 -9
- package/src/components/card/content.ts +8 -10
- package/src/components/card/features.ts +4 -6
- package/src/components/card/index.ts +29 -2
- package/src/components/card/types.ts +6 -23
- package/src/components/checkbox/config.ts +3 -4
- package/src/components/checkbox/index.ts +1 -2
- package/src/components/checkbox/types.ts +12 -3
- package/src/components/chip/api.ts +170 -221
- package/src/components/chip/chip.ts +34 -302
- package/src/components/chip/config.ts +1 -2
- package/src/components/chip/index.ts +10 -2
- package/src/components/chip/types.ts +224 -35
- package/src/components/datepicker/api.ts +18 -25
- package/src/components/datepicker/config.ts +9 -12
- package/src/components/datepicker/datepicker.ts +7 -12
- package/src/components/datepicker/index.ts +10 -7
- package/src/components/datepicker/render.ts +16 -18
- package/src/components/datepicker/types.ts +164 -35
- package/src/components/datepicker/utils.ts +1 -2
- package/src/components/dialog/api.ts +7 -8
- package/src/components/dialog/config.ts +3 -4
- package/src/components/dialog/features.ts +56 -22
- package/src/components/dialog/index.ts +38 -8
- package/src/components/dialog/types.ts +33 -10
- package/src/components/divider/index.ts +5 -1
- package/src/components/extended-fab/config.ts +6 -2
- package/src/components/extended-fab/index.ts +7 -2
- package/src/components/extended-fab/types.ts +21 -4
- package/src/components/fab/config.ts +3 -4
- package/src/components/fab/fab.ts +1 -1
- package/src/components/fab/index.ts +7 -2
- package/src/components/fab/types.ts +21 -4
- package/src/components/list/config.ts +4 -5
- package/src/components/list/features.ts +6 -7
- package/src/components/list/index.ts +7 -9
- package/src/components/list/list-item.ts +12 -13
- package/src/components/list/types.ts +50 -5
- package/src/components/list/utils.ts +30 -3
- package/src/components/menu/features/items-manager.ts +9 -9
- package/src/components/menu/features/positioning.ts +7 -7
- package/src/components/menu/features/visibility.ts +7 -7
- package/src/components/menu/index.ts +7 -9
- package/src/components/menu/menu-item.ts +6 -6
- package/src/components/menu/menu.ts +22 -0
- package/src/components/menu/types.ts +29 -10
- package/src/components/menu/utils.ts +67 -0
- package/src/components/navigation/api.ts +131 -96
- package/src/components/navigation/config.ts +22 -10
- package/src/components/navigation/features/controller.ts +273 -0
- package/src/components/navigation/features/items.ts +160 -87
- package/src/components/navigation/index.ts +0 -6
- package/src/components/navigation/nav-item.ts +12 -24
- package/src/components/navigation/navigation.ts +21 -8
- package/src/components/navigation/system-types.ts +124 -0
- package/src/components/navigation/system.ts +776 -0
- package/src/components/navigation/types.ts +228 -203
- package/src/components/progress/api.ts +2 -3
- package/src/components/progress/config.ts +2 -3
- package/src/components/progress/index.ts +0 -1
- package/src/components/progress/progress.ts +1 -2
- package/src/components/progress/types.ts +186 -33
- package/src/components/radios/config.ts +1 -1
- package/src/components/radios/index.ts +0 -1
- package/src/components/radios/types.ts +0 -7
- package/src/components/search/config.ts +1 -2
- package/src/components/search/features/search.ts +14 -15
- package/src/components/search/features/states.ts +5 -1
- package/src/components/search/features/structure.ts +3 -4
- package/src/components/search/index.ts +0 -3
- package/src/components/search/types.ts +18 -6
- package/src/components/segmented-button/config.ts +20 -7
- package/src/components/segmented-button/segment.ts +6 -7
- package/src/components/segmented-button/segmented-button.ts +4 -5
- package/src/components/segmented-button/types.ts +37 -2
- package/src/components/slider/config.ts +20 -2
- package/src/components/slider/features/controller.ts +761 -0
- package/src/components/slider/features/handlers.ts +18 -15
- package/src/components/slider/features/index.ts +3 -2
- package/src/components/slider/features/range.ts +104 -0
- package/src/components/slider/slider.ts +34 -14
- package/src/components/slider/structure.ts +152 -0
- package/src/components/slider/types.ts +34 -8
- package/src/components/snackbar/config.ts +2 -3
- package/src/components/snackbar/constants.ts +0 -32
- package/src/components/snackbar/index.ts +0 -1
- package/src/components/snackbar/position.ts +9 -1
- package/src/components/snackbar/types.ts +122 -46
- package/src/components/switch/config.ts +2 -3
- package/src/components/switch/index.ts +0 -1
- package/src/components/switch/types.ts +3 -2
- package/src/components/tabs/config.ts +3 -4
- package/src/components/tabs/index.ts +0 -15
- package/src/components/tabs/tab-api.ts +12 -4
- package/src/components/tabs/tab.ts +18 -6
- package/src/components/tabs/types.ts +13 -3
- package/src/components/textfield/api.ts +53 -0
- package/src/components/textfield/config.ts +2 -3
- package/src/components/textfield/features.ts +322 -0
- package/src/components/textfield/index.ts +0 -1
- package/src/components/textfield/textfield.ts +8 -0
- package/src/components/textfield/types.ts +29 -6
- package/src/components/timepicker/api.ts +1 -1
- package/src/components/timepicker/clockdial.ts +2 -5
- package/src/components/timepicker/config.ts +102 -4
- package/src/components/timepicker/index.ts +1 -6
- package/src/components/timepicker/render.ts +1 -1
- package/src/components/timepicker/timepicker.ts +1 -1
- package/src/components/tooltip/api.ts +1 -1
- package/src/components/tooltip/config.ts +27 -6
- package/src/components/tooltip/index.ts +0 -1
- package/src/components/tooltip/types.ts +13 -3
- package/src/core/compose/features/textinput.ts +15 -2
- package/src/core/compose/features/textlabel.ts +0 -3
- package/src/core/composition/features/dom.ts +33 -0
- package/src/core/composition/features/icon.ts +131 -0
- package/src/core/composition/features/index.ts +11 -0
- package/src/core/composition/features/label.ts +156 -0
- package/src/core/composition/features/structure.ts +22 -0
- package/src/core/composition/index.ts +26 -0
- package/src/core/index.ts +1 -1
- package/src/core/structure.ts +288 -0
- package/src/index.ts +1 -0
- package/src/styles/components/_navigation-mobile.scss +244 -0
- package/src/styles/components/_navigation-system.scss +151 -0
- package/src/{components/tabs/_styles.scss → styles/components/_tabs.scss} +1 -1
- package/src/{components/textfield/_styles.scss → styles/components/_textfield.scss} +314 -72
- package/src/styles/main.scss +98 -49
- package/src/components/badge/constants.ts +0 -40
- package/src/components/button/constants.ts +0 -11
- package/src/components/card/constants.ts +0 -84
- package/src/components/datepicker/constants.ts +0 -98
- package/src/components/dialog/constants.ts +0 -32
- package/src/components/extended-fab/constants.ts +0 -36
- package/src/components/fab/constants.ts +0 -41
- package/src/components/menu/constants.ts +0 -154
- package/src/components/navigation/constants.ts +0 -200
- package/src/components/progress/constants.ts +0 -29
- package/src/components/search/constants.ts +0 -21
- package/src/components/segmented-button/constants.ts +0 -42
- package/src/components/slider/features/slider.ts +0 -318
- package/src/components/slider/features/structure.ts +0 -181
- package/src/components/slider/features/ui.ts +0 -388
- package/src/components/switch/constants.ts +0 -80
- package/src/components/tabs/constants.ts +0 -89
- package/src/components/textfield/constants.ts +0 -100
- package/src/components/timepicker/constants.ts +0 -138
- /package/src/{components/badge/_styles.scss → styles/components/_badge.scss} +0 -0
- /package/src/{components/bottom-app-bar/_styles.scss → styles/components/_bottom-app-bar.scss} +0 -0
- /package/src/{components/button/_styles.scss → styles/components/_button.scss} +0 -0
- /package/src/{components/card/_styles.scss → styles/components/_card.scss} +0 -0
- /package/src/{components/carousel/_styles.scss → styles/components/_carousel.scss} +0 -0
- /package/src/{components/checkbox/_styles.scss → styles/components/_checkbox.scss} +0 -0
- /package/src/{components/chip/_styles.scss → styles/components/_chip.scss} +0 -0
- /package/src/{components/datepicker/_styles.scss → styles/components/_datepicker.scss} +0 -0
- /package/src/{components/dialog/_styles.scss → styles/components/_dialog.scss} +0 -0
- /package/src/{components/divider/_styles.scss → styles/components/_divider.scss} +0 -0
- /package/src/{components/extended-fab/_styles.scss → styles/components/_extended-fab.scss} +0 -0
- /package/src/{components/fab/_styles.scss → styles/components/_fab.scss} +0 -0
- /package/src/{components/list/_styles.scss → styles/components/_list.scss} +0 -0
- /package/src/{components/menu/_styles.scss → styles/components/_menu.scss} +0 -0
- /package/src/{components/navigation/_styles.scss → styles/components/_navigation.scss} +0 -0
- /package/src/{components/progress/_styles.scss → styles/components/_progress.scss} +0 -0
- /package/src/{components/radios/_styles.scss → styles/components/_radios.scss} +0 -0
- /package/src/{components/search/_styles.scss → styles/components/_search.scss} +0 -0
- /package/src/{components/segmented-button/_styles.scss → styles/components/_segmented-button.scss} +0 -0
- /package/src/{components/sheet/_styles.scss → styles/components/_sheet.scss} +0 -0
- /package/src/{components/slider/_styles.scss → styles/components/_slider.scss} +0 -0
- /package/src/{components/snackbar/_styles.scss → styles/components/_snackbar.scss} +0 -0
- /package/src/{components/switch/_styles.scss → styles/components/_switch.scss} +0 -0
- /package/src/{components/timepicker/_styles.scss → styles/components/_timepicker.scss} +0 -0
- /package/src/{components/tooltip/_styles.scss → styles/components/_tooltip.scss} +0 -0
- /package/src/{components/top-app-bar/_styles.scss → styles/components/_top-app-bar.scss} +0 -0
- /package/src/styles/utilities/{_color.scss → _colors.scss} +0 -0
|
@@ -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
|
|
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
|
|
@@ -89,9 +89,6 @@ export const withTextLabel = <T extends TextLabelConfig>(config: T = {} as T) =>
|
|
|
89
89
|
component.element.appendChild(labelElement);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
console.log('componentName', component.componentName)
|
|
93
|
-
console.log('position', position)
|
|
94
|
-
|
|
95
92
|
if (position && component.componentName !== 'slider') {
|
|
96
93
|
component.element.classList.add(`${config.prefix}-${config.componentName}--label-${position}`);
|
|
97
94
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/core/composition/features/dom.ts
|
|
2
|
+
import { createStructure, flattenStructure } from '../../structure';
|
|
3
|
+
|
|
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
|
|
7
|
+
*
|
|
8
|
+
* @returns Component enhancer that creates DOM structure
|
|
9
|
+
*/
|
|
10
|
+
export const withDom = () => component => {
|
|
11
|
+
// Return unmodified component if no structure definition
|
|
12
|
+
if (!component.structureDefinition) {
|
|
13
|
+
return component;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
// Use the existing createStructure function to build the DOM
|
|
18
|
+
const structure = createStructure(component.structureDefinition);
|
|
19
|
+
|
|
20
|
+
// Use the existing flattenStructure function to create a flat reference map
|
|
21
|
+
const components = structure.getAll();
|
|
22
|
+
|
|
23
|
+
// Return enhanced component with DOM structure
|
|
24
|
+
return {
|
|
25
|
+
...component,
|
|
26
|
+
element: structure.element, // Root element
|
|
27
|
+
components // All component elements
|
|
28
|
+
};
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Failed to create DOM structure:', error);
|
|
31
|
+
throw new Error(`Failed to create slider DOM: ${error.message}`);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/core/composition/features/icon.ts
|
|
2
|
+
import { createElement } from '../../dom/create';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for icon feature
|
|
6
|
+
*/
|
|
7
|
+
export interface IconConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Icon HTML content
|
|
10
|
+
*/
|
|
11
|
+
icon?: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Position of the icon ('start' or 'end')
|
|
15
|
+
*/
|
|
16
|
+
iconPosition?: 'start' | 'end';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Size variant for the icon
|
|
20
|
+
*/
|
|
21
|
+
iconSize?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* CSS class prefix
|
|
25
|
+
*/
|
|
26
|
+
prefix?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Component name for class generation
|
|
30
|
+
*/
|
|
31
|
+
componentName?: string;
|
|
32
|
+
|
|
33
|
+
[key: string]: any;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Enhances structure definition with an icon element
|
|
38
|
+
* Unlike the traditional withIcon, this modifies the structure definition
|
|
39
|
+
* without creating actual DOM elements.
|
|
40
|
+
*
|
|
41
|
+
* @param config Configuration containing icon information
|
|
42
|
+
* @returns Component enhancer that adds icon to structure definition
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* // Add icon to a structure definition
|
|
47
|
+
* const component = pipe(
|
|
48
|
+
* createBase,
|
|
49
|
+
* withStructure(config),
|
|
50
|
+
* withIcon(config)
|
|
51
|
+
* )(config);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const withIcon = (config: IconConfig) => component => {
|
|
55
|
+
// If no icon or missing structure definition, return unmodified
|
|
56
|
+
if (!config.icon || !component.structureDefinition) {
|
|
57
|
+
return component;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
// Get component details for class names
|
|
62
|
+
const prefix = config.prefix || component.config?.prefix || 'mtrl';
|
|
63
|
+
const componentName = config.componentName || component.componentName || 'component';
|
|
64
|
+
|
|
65
|
+
// Clone the structure definition
|
|
66
|
+
const structureDefinition = JSON.parse(JSON.stringify(component.structureDefinition));
|
|
67
|
+
|
|
68
|
+
// Determine icon position
|
|
69
|
+
const position = config.iconPosition || 'start';
|
|
70
|
+
|
|
71
|
+
// Add the --icon modifier class to the main element
|
|
72
|
+
const elementClasses = structureDefinition.element.options.className || [];
|
|
73
|
+
const iconModifierClass = `${prefix}-${componentName}--icon`;
|
|
74
|
+
|
|
75
|
+
if (Array.isArray(elementClasses)) {
|
|
76
|
+
if (!elementClasses.includes(iconModifierClass)) {
|
|
77
|
+
elementClasses.push(iconModifierClass);
|
|
78
|
+
}
|
|
79
|
+
} else if (typeof elementClasses === 'string') {
|
|
80
|
+
if (!elementClasses.includes(iconModifierClass)) {
|
|
81
|
+
structureDefinition.element.options.className = `${elementClasses} ${iconModifierClass}`.trim();
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
structureDefinition.element.options.className = [iconModifierClass];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Create icon element definition with component-specific class
|
|
88
|
+
const iconDef = {
|
|
89
|
+
name: 'icon',
|
|
90
|
+
creator: createElement,
|
|
91
|
+
options: {
|
|
92
|
+
tag: 'span',
|
|
93
|
+
className: [
|
|
94
|
+
`${prefix}-${componentName}-icon`,
|
|
95
|
+
`${prefix}-${componentName}-icon--${position}`
|
|
96
|
+
],
|
|
97
|
+
html: config.icon
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Add size class if specified
|
|
102
|
+
if (config.iconSize) {
|
|
103
|
+
const classes = iconDef.options.className;
|
|
104
|
+
if (Array.isArray(classes)) {
|
|
105
|
+
classes.push(`${prefix}-${componentName}-icon--${config.iconSize}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Add icon directly to the main element's children
|
|
110
|
+
if (position === 'start') {
|
|
111
|
+
// Create new children object with icon first
|
|
112
|
+
const existingChildren = { ...structureDefinition.element.children };
|
|
113
|
+
structureDefinition.element.children = {
|
|
114
|
+
icon: iconDef,
|
|
115
|
+
...existingChildren
|
|
116
|
+
};
|
|
117
|
+
} else {
|
|
118
|
+
// Add icon after existing children
|
|
119
|
+
structureDefinition.element.children.icon = iconDef;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Return component with updated structure definition
|
|
123
|
+
return {
|
|
124
|
+
...component,
|
|
125
|
+
structureDefinition
|
|
126
|
+
};
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.warn('Error enhancing structure with icon:', error);
|
|
129
|
+
return component;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/core/composition/features/index.ts
|
|
2
|
+
|
|
3
|
+
// Export composition features
|
|
4
|
+
export { withIcon } from './icon';
|
|
5
|
+
export { withLabel } from './label';
|
|
6
|
+
export { withDom } from './dom';
|
|
7
|
+
export { withStructure } from './structure';
|
|
8
|
+
|
|
9
|
+
// Re-export interface types for better developer experience
|
|
10
|
+
export type { IconConfig } from './icon';
|
|
11
|
+
export type { LabelConfig } from './label';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// src/core/composition/features/label.ts
|
|
2
|
+
import { createElement } from '../../dom/create';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for label feature
|
|
6
|
+
*/
|
|
7
|
+
export interface LabelConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Label text content
|
|
10
|
+
*/
|
|
11
|
+
label?: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Position of the label ('start', 'end', 'top', or 'bottom')
|
|
15
|
+
*/
|
|
16
|
+
labelPosition?: 'start' | 'end' | 'top' | 'bottom';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* CSS class prefix
|
|
20
|
+
*/
|
|
21
|
+
prefix?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Component name for class generation
|
|
25
|
+
*/
|
|
26
|
+
componentName?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* ID for the input element this label is associated with
|
|
30
|
+
*/
|
|
31
|
+
id?: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whether the labeled element is required
|
|
35
|
+
*/
|
|
36
|
+
required?: boolean;
|
|
37
|
+
|
|
38
|
+
[key: string]: any;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Enhances structure definition with a label element
|
|
43
|
+
* Unlike the traditional withLabel, this modifies the structure definition
|
|
44
|
+
* without creating actual DOM elements.
|
|
45
|
+
*
|
|
46
|
+
* @param config Configuration containing label information
|
|
47
|
+
* @returns Component enhancer that adds label to structure definition
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* // Add label to a structure definition
|
|
52
|
+
* const component = pipe(
|
|
53
|
+
* createBase,
|
|
54
|
+
* withStructure(config),
|
|
55
|
+
* withLabel(config)
|
|
56
|
+
* )(config);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export const withLabel = (config: LabelConfig) => component => {
|
|
60
|
+
// If no label or missing structure definition, return unmodified
|
|
61
|
+
|
|
62
|
+
if (!config.label || !component.structureDefinition) {
|
|
63
|
+
return component;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// Get class name generator
|
|
68
|
+
const getClass = (className) => {
|
|
69
|
+
if (typeof component.getClass === 'function') {
|
|
70
|
+
return component.getClass(className);
|
|
71
|
+
}
|
|
72
|
+
const prefix = config.prefix || 'mtrl';
|
|
73
|
+
return `${prefix}-${className}`;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Get component details
|
|
77
|
+
const prefix = config.prefix || component.config?.prefix || 'mtrl';
|
|
78
|
+
const componentName = config.componentName || component.componentName || 'component';
|
|
79
|
+
|
|
80
|
+
// Clone the structure definition
|
|
81
|
+
const structureDefinition = JSON.parse(JSON.stringify(component.structureDefinition));
|
|
82
|
+
|
|
83
|
+
// Determine position (default to 'start')
|
|
84
|
+
const position = config.labelPosition || 'start';
|
|
85
|
+
|
|
86
|
+
// Create the label element definition
|
|
87
|
+
let labelContent = config.label;
|
|
88
|
+
let labelChildren = null;
|
|
89
|
+
|
|
90
|
+
// Add required indicator if specified
|
|
91
|
+
if (config.required) {
|
|
92
|
+
// For structure definition we need to define a child element
|
|
93
|
+
labelChildren = {
|
|
94
|
+
requiredIndicator: {
|
|
95
|
+
name: 'requiredIndicator',
|
|
96
|
+
creator: createElement,
|
|
97
|
+
options: {
|
|
98
|
+
tag: 'span',
|
|
99
|
+
className: `${prefix}-${componentName}-label-required`,
|
|
100
|
+
text: '*',
|
|
101
|
+
attrs: {
|
|
102
|
+
'aria-hidden': 'true'
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Clear the content since we'll use children instead
|
|
109
|
+
labelContent = config.label;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Create label element definition
|
|
113
|
+
const labelDef = {
|
|
114
|
+
name: 'label',
|
|
115
|
+
creator: createElement,
|
|
116
|
+
options: {
|
|
117
|
+
tag: 'label',
|
|
118
|
+
className: [
|
|
119
|
+
`${prefix}-${componentName}-label`,
|
|
120
|
+
`${prefix}-${componentName}-label--${position}`
|
|
121
|
+
],
|
|
122
|
+
attrs: {
|
|
123
|
+
'for': config.id // Optional connection to input by ID
|
|
124
|
+
},
|
|
125
|
+
text: labelContent
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Add children if we have them
|
|
130
|
+
if (labelChildren) {
|
|
131
|
+
labelDef.children = labelChildren;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add label to root element's children
|
|
135
|
+
if (position === 'end' || position === 'bottom') {
|
|
136
|
+
// Add at end of root element
|
|
137
|
+
structureDefinition.element.children.label = labelDef;
|
|
138
|
+
} else {
|
|
139
|
+
// Add at beginning of root element (start/top)
|
|
140
|
+
const existingChildren = { ...structureDefinition.element.children };
|
|
141
|
+
structureDefinition.element.children = {
|
|
142
|
+
label: labelDef,
|
|
143
|
+
...existingChildren
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Return component with updated structure definition
|
|
148
|
+
return {
|
|
149
|
+
...component,
|
|
150
|
+
structureDefinition
|
|
151
|
+
};
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.warn('Error enhancing structure with label:', error);
|
|
154
|
+
return component;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/core/composition/features/structure.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds structure definition to component without creating DOM
|
|
5
|
+
* Now uses the structure from the baseConfig
|
|
6
|
+
*
|
|
7
|
+
* @param configuration
|
|
8
|
+
* @returns Component enhancer with structure definition
|
|
9
|
+
*/
|
|
10
|
+
export const withStructure = (config: SliderConfig) => component => {
|
|
11
|
+
// Use the structure definition from the config
|
|
12
|
+
if (!config.structureDefinition) {
|
|
13
|
+
console.warn('No structure definition found in slider config');
|
|
14
|
+
return component;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Return enhanced component with structure definition
|
|
18
|
+
return {
|
|
19
|
+
...component,
|
|
20
|
+
structureDefinition: config.structureDefinition
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/core/composition/index.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module core/composition
|
|
5
|
+
* @description Composition utilities for creating components using the structure-based approach
|
|
6
|
+
*
|
|
7
|
+
* The composition module provides features that work with the structure definition
|
|
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.
|
|
10
|
+
*
|
|
11
|
+
* This approach provides several advantages:
|
|
12
|
+
* - Clearer separation between structure definition and DOM creation
|
|
13
|
+
* - More predictable component creation process
|
|
14
|
+
* - Better support for server-side rendering
|
|
15
|
+
* - Enhanced testability
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Export features
|
|
19
|
+
export {
|
|
20
|
+
withStructure
|
|
21
|
+
withDom
|
|
22
|
+
withIcon,
|
|
23
|
+
withLabel,
|
|
24
|
+
} from './features';
|
|
25
|
+
|
|
26
|
+
export type { IconConfig, LabelConfig } from './features';
|