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.
- package/index.ts +4 -0
- package/package.json +1 -1
- package/src/components/button/button.ts +34 -5
- package/src/components/navigation/api.ts +131 -96
- package/src/components/navigation/features/controller.ts +273 -0
- package/src/components/navigation/features/items.ts +133 -64
- package/src/components/navigation/navigation.ts +17 -2
- 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 +20 -2
- package/src/components/slider/features/controller.ts +737 -0
- package/src/components/slider/features/handlers.ts +18 -16
- package/src/components/slider/features/index.ts +3 -2
- package/src/components/slider/features/range.ts +104 -0
- package/src/components/slider/schema.ts +141 -0
- package/src/components/slider/slider.ts +34 -13
- 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/components/textfield/api.ts +53 -0
- package/src/components/textfield/features.ts +322 -0
- package/src/components/textfield/textfield.ts +8 -0
- package/src/components/textfield/types.ts +12 -3
- package/src/components/timepicker/clockdial.ts +1 -4
- package/src/core/compose/features/textinput.ts +15 -2
- package/src/core/composition/features/dom.ts +45 -0
- package/src/core/composition/features/icon.ts +131 -0
- package/src/core/composition/features/index.ts +12 -0
- package/src/core/composition/features/label.ts +155 -0
- package/src/core/composition/features/layout.ts +47 -0
- package/src/core/composition/index.ts +26 -0
- package/src/core/index.ts +1 -1
- 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/index.ts +1 -0
- package/src/styles/abstract/_variables.scss +28 -0
- package/src/styles/components/_navigation-mobile.scss +244 -0
- package/src/styles/components/_navigation-system.scss +151 -0
- package/src/styles/components/_switch.scss +133 -69
- package/src/styles/components/_textfield.scss +259 -27
- package/demo/build.ts +0 -349
- package/demo/index.html +0 -110
- package/demo/main.js +0 -448
- package/demo/styles.css +0 -239
- package/server.ts +0 -86
- 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/textfield/constants.ts +0 -100
- package/src/core/layout/index.js +0 -95
|
@@ -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 component schema with an icon element
|
|
38
|
+
* Unlike the traditional withIcon, this modifies the schema
|
|
39
|
+
* without creating actual DOM elements.
|
|
40
|
+
*
|
|
41
|
+
* @param config Configuration containing icon information
|
|
42
|
+
* @returns Component enhancer that adds icon to schema
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* // Add icon to a component schema
|
|
47
|
+
* const component = pipe(
|
|
48
|
+
* createBase,
|
|
49
|
+
* withLayout(config),
|
|
50
|
+
* withIcon(config)
|
|
51
|
+
* )(config);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const withIcon = (config: IconConfig) => component => {
|
|
55
|
+
// If no icon or missing schema, return unmodified
|
|
56
|
+
if (!config.icon || !component.schema) {
|
|
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 schema
|
|
66
|
+
const schema = JSON.parse(JSON.stringify(component.schema));
|
|
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 = schema.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
|
+
schema.element.options.className = `${elementClasses} ${iconModifierClass}`.trim();
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
schema.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 = { ...schema.element.children };
|
|
113
|
+
schema.element.children = {
|
|
114
|
+
icon: iconDef,
|
|
115
|
+
...existingChildren
|
|
116
|
+
};
|
|
117
|
+
} else {
|
|
118
|
+
// Add icon after existing children
|
|
119
|
+
schema.element.children.icon = iconDef;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Return component with updated schema
|
|
123
|
+
return {
|
|
124
|
+
...component,
|
|
125
|
+
schema
|
|
126
|
+
};
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.warn('Error enhancing schema with icon:', error);
|
|
129
|
+
return component;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/core/composition/features/index.ts
|
|
2
|
+
|
|
3
|
+
// Export composition features
|
|
4
|
+
export { withLayout } from './layout';
|
|
5
|
+
export { withIcon } from './icon';
|
|
6
|
+
export { withLabel } from './label';
|
|
7
|
+
export { withDom } from './dom';
|
|
8
|
+
|
|
9
|
+
// Re-export interface types for better developer experience
|
|
10
|
+
export type { IconConfig } from './icon';
|
|
11
|
+
export type { LabelConfig } from './label';
|
|
12
|
+
export type { LayoutConfig } from './layout';
|
|
@@ -0,0 +1,155 @@
|
|
|
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 component schema with a label element
|
|
43
|
+
* Unlike the traditional withLabel, this modifies the schema
|
|
44
|
+
* without creating actual DOM elements.
|
|
45
|
+
*
|
|
46
|
+
* @param config Configuration containing label information
|
|
47
|
+
* @returns Component enhancer that adds label to schema
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* // Add label to a component schema
|
|
52
|
+
* const component = pipe(
|
|
53
|
+
* createBase,
|
|
54
|
+
* withLayout(config),
|
|
55
|
+
* withLabel(config)
|
|
56
|
+
* )(config);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export const withLabel = (config: LabelConfig) => component => {
|
|
60
|
+
// If no label or missing schema, return unmodified
|
|
61
|
+
if (!config.label || !component.schema) {
|
|
62
|
+
return component;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Get class name generator
|
|
67
|
+
const getClass = (className) => {
|
|
68
|
+
if (typeof component.getClass === 'function') {
|
|
69
|
+
return component.getClass(className);
|
|
70
|
+
}
|
|
71
|
+
const prefix = config.prefix || 'mtrl';
|
|
72
|
+
return `${prefix}-${className}`;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Get component details
|
|
76
|
+
const prefix = config.prefix || component.config?.prefix || 'mtrl';
|
|
77
|
+
const componentName = config.componentName || component.componentName || 'component';
|
|
78
|
+
|
|
79
|
+
// Clone the schema
|
|
80
|
+
const schema = JSON.parse(JSON.stringify(component.schema));
|
|
81
|
+
|
|
82
|
+
// Determine position (default to 'start')
|
|
83
|
+
const position = config.labelPosition || 'start';
|
|
84
|
+
|
|
85
|
+
// Create the label element definition
|
|
86
|
+
let labelContent = config.label;
|
|
87
|
+
let labelChildren = null;
|
|
88
|
+
|
|
89
|
+
// Add required indicator if specified
|
|
90
|
+
if (config.required) {
|
|
91
|
+
// For schema we need to define a child element
|
|
92
|
+
labelChildren = {
|
|
93
|
+
requiredIndicator: {
|
|
94
|
+
name: 'requiredIndicator',
|
|
95
|
+
creator: createElement,
|
|
96
|
+
options: {
|
|
97
|
+
tag: 'span',
|
|
98
|
+
className: `${prefix}-${componentName}-label-required`,
|
|
99
|
+
text: '*',
|
|
100
|
+
attrs: {
|
|
101
|
+
'aria-hidden': 'true'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Clear the content since we'll use children instead
|
|
108
|
+
labelContent = config.label;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create label element definition
|
|
112
|
+
const labelDef = {
|
|
113
|
+
name: 'label',
|
|
114
|
+
creator: createElement,
|
|
115
|
+
options: {
|
|
116
|
+
tag: 'label',
|
|
117
|
+
className: [
|
|
118
|
+
`${prefix}-${componentName}-label`,
|
|
119
|
+
`${prefix}-${componentName}-label--${position}`
|
|
120
|
+
],
|
|
121
|
+
attrs: {
|
|
122
|
+
'for': config.id // Optional connection to input by ID
|
|
123
|
+
},
|
|
124
|
+
text: labelContent
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Add children if we have them
|
|
129
|
+
if (labelChildren) {
|
|
130
|
+
labelDef.children = labelChildren;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add label to root element's children
|
|
134
|
+
if (position === 'end' || position === 'bottom') {
|
|
135
|
+
// Add at end of root element
|
|
136
|
+
schema.element.children.label = labelDef;
|
|
137
|
+
} else {
|
|
138
|
+
// Add at beginning of root element (start/top)
|
|
139
|
+
const existingChildren = { ...schema.element.children };
|
|
140
|
+
schema.element.children = {
|
|
141
|
+
label: labelDef,
|
|
142
|
+
...existingChildren
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Return component with updated schema
|
|
147
|
+
return {
|
|
148
|
+
...component,
|
|
149
|
+
schema
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.warn('Error enhancing schema with label:', error);
|
|
153
|
+
return component;
|
|
154
|
+
}
|
|
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
|
+
};
|
|
@@ -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 layout schema that is later used to create DOM elements.
|
|
10
|
+
*
|
|
11
|
+
* This approach provides several advantages:
|
|
12
|
+
* - Clearer separation between layout 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
|
+
withLayout,
|
|
21
|
+
withDom,
|
|
22
|
+
withIcon,
|
|
23
|
+
withLabel,
|
|
24
|
+
} from './features';
|
|
25
|
+
|
|
26
|
+
export type { IconConfig, LabelConfig } from './features';
|