mtrl 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/index.ts +4 -0
  2. package/package.json +1 -1
  3. package/src/components/button/button.ts +34 -5
  4. package/src/components/navigation/api.ts +131 -96
  5. package/src/components/navigation/features/controller.ts +273 -0
  6. package/src/components/navigation/features/items.ts +133 -64
  7. package/src/components/navigation/navigation.ts +17 -2
  8. package/src/components/navigation/system/core.ts +302 -0
  9. package/src/components/navigation/system/events.ts +240 -0
  10. package/src/components/navigation/system/index.ts +184 -0
  11. package/src/components/navigation/system/mobile.ts +278 -0
  12. package/src/components/navigation/system/state.ts +77 -0
  13. package/src/components/navigation/system/types.ts +364 -0
  14. package/src/components/slider/config.ts +20 -2
  15. package/src/components/slider/features/controller.ts +737 -0
  16. package/src/components/slider/features/handlers.ts +18 -16
  17. package/src/components/slider/features/index.ts +3 -2
  18. package/src/components/slider/features/range.ts +104 -0
  19. package/src/components/slider/schema.ts +141 -0
  20. package/src/components/slider/slider.ts +34 -13
  21. package/src/components/switch/api.ts +16 -0
  22. package/src/components/switch/config.ts +1 -18
  23. package/src/components/switch/features.ts +198 -0
  24. package/src/components/switch/index.ts +1 -0
  25. package/src/components/switch/switch.ts +3 -3
  26. package/src/components/switch/types.ts +14 -2
  27. package/src/components/textfield/api.ts +53 -0
  28. package/src/components/textfield/features.ts +322 -0
  29. package/src/components/textfield/textfield.ts +8 -0
  30. package/src/components/textfield/types.ts +12 -3
  31. package/src/components/timepicker/clockdial.ts +1 -4
  32. package/src/core/compose/features/textinput.ts +15 -2
  33. package/src/core/composition/features/dom.ts +45 -0
  34. package/src/core/composition/features/icon.ts +131 -0
  35. package/src/core/composition/features/index.ts +12 -0
  36. package/src/core/composition/features/label.ts +155 -0
  37. package/src/core/composition/features/layout.ts +47 -0
  38. package/src/core/composition/index.ts +26 -0
  39. package/src/core/index.ts +1 -1
  40. package/src/core/layout/README.md +350 -0
  41. package/src/core/layout/array.ts +181 -0
  42. package/src/core/layout/create.ts +55 -0
  43. package/src/core/layout/index.ts +26 -0
  44. package/src/core/layout/object.ts +124 -0
  45. package/src/core/layout/processor.ts +58 -0
  46. package/src/core/layout/result.ts +85 -0
  47. package/src/core/layout/types.ts +125 -0
  48. package/src/core/layout/utils.ts +136 -0
  49. package/src/index.ts +1 -0
  50. package/src/styles/abstract/_variables.scss +28 -0
  51. package/src/styles/components/_navigation-mobile.scss +244 -0
  52. package/src/styles/components/_navigation-system.scss +151 -0
  53. package/src/styles/components/_switch.scss +133 -69
  54. package/src/styles/components/_textfield.scss +259 -27
  55. package/demo/build.ts +0 -349
  56. package/demo/index.html +0 -110
  57. package/demo/main.js +0 -448
  58. package/demo/styles.css +0 -239
  59. package/server.ts +0 -86
  60. package/src/components/slider/features/slider.ts +0 -318
  61. package/src/components/slider/features/structure.ts +0 -181
  62. package/src/components/slider/features/ui.ts +0 -388
  63. package/src/components/textfield/constants.ts +0 -100
  64. package/src/core/layout/index.js +0 -95
@@ -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';
package/src/core/index.ts CHANGED
@@ -11,7 +11,7 @@ export type {
11
11
  ComponentConfig,
12
12
  ThemedComponentConfig,
13
13
  VariantComponentConfig,
14
- StateComponentConfig
14
+ StateComponentConfig
15
15
  } from './config';
16
16
 
17
17
  // Build