mtrl 0.3.3 → 0.3.6

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 (41) hide show
  1. package/package.json +1 -1
  2. package/src/components/menu/api.ts +143 -268
  3. package/src/components/menu/config.ts +84 -40
  4. package/src/components/menu/features/anchor.ts +159 -0
  5. package/src/components/menu/features/controller.ts +970 -0
  6. package/src/components/menu/features/index.ts +4 -0
  7. package/src/components/menu/index.ts +31 -63
  8. package/src/components/menu/menu.ts +107 -97
  9. package/src/components/menu/types.ts +263 -447
  10. package/src/components/segmented-button/config.ts +59 -20
  11. package/src/components/segmented-button/index.ts +1 -1
  12. package/src/components/segmented-button/segment.ts +51 -97
  13. package/src/components/segmented-button/segmented-button.ts +114 -2
  14. package/src/components/segmented-button/types.ts +52 -0
  15. package/src/core/compose/features/icon.ts +15 -13
  16. package/src/core/dom/classes.ts +81 -9
  17. package/src/core/dom/create.ts +30 -19
  18. package/src/core/layout/README.md +531 -166
  19. package/src/core/layout/array.ts +3 -4
  20. package/src/core/layout/config.ts +193 -0
  21. package/src/core/layout/create.ts +1 -2
  22. package/src/core/layout/index.ts +12 -2
  23. package/src/core/layout/object.ts +2 -3
  24. package/src/core/layout/processor.ts +60 -12
  25. package/src/core/layout/result.ts +1 -2
  26. package/src/core/layout/types.ts +105 -50
  27. package/src/core/layout/utils.ts +69 -61
  28. package/src/index.ts +2 -1
  29. package/src/styles/components/_button.scss +6 -0
  30. package/src/styles/components/_chip.scss +4 -5
  31. package/src/styles/components/_menu.scss +20 -8
  32. package/src/styles/components/_segmented-button.scss +173 -63
  33. package/src/styles/main.scss +23 -23
  34. package/src/styles/utilities/_layout.scss +665 -0
  35. package/src/components/menu/features/items-manager.ts +0 -457
  36. package/src/components/menu/features/keyboard-navigation.ts +0 -133
  37. package/src/components/menu/features/positioning.ts +0 -127
  38. package/src/components/menu/features/visibility.ts +0 -230
  39. package/src/components/menu/menu-item.ts +0 -86
  40. package/src/components/menu/utils.ts +0 -67
  41. /package/src/{core/build → styles/utilities}/_ripple.scss +0 -0
@@ -98,10 +98,9 @@ export function processArraySchema(
98
98
  itemOptions = {};
99
99
  }
100
100
 
101
- // We should NOT automatically set the tag to match the name
102
- // If tag is not provided in options, a default like 'div' should be used
101
+ // Default to div if no tag is specified
103
102
  if (creator === createElement && !('tag' in itemOptions)) {
104
- itemOptions.tag = 'div'; // Default to div if no tag is specified
103
+ itemOptions.tag = 'div';
105
104
  }
106
105
  }
107
106
  // Case 3: Item is an object (options for default creator with no name)
@@ -178,4 +177,4 @@ export function processArraySchema(
178
177
 
179
178
  layout.components = components;
180
179
  return createLayoutResult(layout);
181
- }
180
+ }
@@ -0,0 +1,193 @@
1
+ // src/core/layout/config.ts
2
+ /**
3
+ * @module core/layout
4
+ * @description Layout configuration utilities
5
+ */
6
+
7
+ import { addClass, removeClass, hasClass } from '../dom/classes';
8
+
9
+ /**
10
+ * Helper function to clean up previous layout classes from an element
11
+ *
12
+ * @param element - Element to clean layout classes from
13
+ */
14
+ export function cleanupLayoutClasses(element: HTMLElement): void {
15
+ if (!element) return;
16
+
17
+ // Get all classes from the element
18
+ const classList = Array.from(element.classList);
19
+
20
+ // Find and remove layout-related classes
21
+ const layoutClasses = classList.filter(cls =>
22
+ cls.startsWith('layout--') ||
23
+ cls.includes('-layout--')
24
+ );
25
+
26
+ // Remove each layout class
27
+ layoutClasses.forEach(cls => {
28
+ element.classList.remove(cls);
29
+ });
30
+ }
31
+
32
+ /**
33
+ * Helper function to get the layout type from element classes
34
+ *
35
+ * @param element - Element to check
36
+ * @returns Layout type if found, empty string otherwise
37
+ */
38
+ export function getLayoutType(element: HTMLElement): string {
39
+ return hasClass(element, 'layout--stack') ? 'stack' :
40
+ hasClass(element, 'layout--row') ? 'row' :
41
+ hasClass(element, 'layout--grid') ? 'grid' : '';
42
+ }
43
+
44
+ /**
45
+ * Applies layout classes based on the layout configuration
46
+ *
47
+ * @param element - Element to apply layout classes to
48
+ * @param layoutConfig - Layout configuration
49
+ * @param cleanupFirst - Whether to clean up previous layout classes first
50
+ */
51
+ export function applyLayoutClasses(
52
+ element: HTMLElement,
53
+ layoutConfig: any,
54
+ cleanupFirst: boolean = true
55
+ ): void {
56
+ if (!element || !layoutConfig) return;
57
+
58
+ // First remove any existing layout classes to avoid accumulation
59
+ if (cleanupFirst) {
60
+ cleanupLayoutClasses(element);
61
+ }
62
+
63
+ // Apply base layout type
64
+ if (layoutConfig.type) {
65
+ addClass(element, `layout--${layoutConfig.type}`);
66
+ }
67
+
68
+ // Apply common layout properties
69
+ if (layoutConfig.gap !== undefined) {
70
+ // Get current layout type for prefixed gap class
71
+ const layoutType = layoutConfig.type || getLayoutType(element);
72
+
73
+ if (layoutType) {
74
+ // Add the new gap class
75
+ addClass(element, `layout--${layoutType}-gap-${layoutConfig.gap}`);
76
+ }
77
+ }
78
+
79
+ // Apply alignment properties
80
+ if (layoutConfig.align) {
81
+ const layoutType = layoutConfig.type || getLayoutType(element);
82
+
83
+ if (layoutType) {
84
+ addClass(element, `layout--${layoutType}-${layoutConfig.align}`);
85
+ }
86
+ }
87
+
88
+ // Apply justification
89
+ if (layoutConfig.justify) {
90
+ const layoutType = layoutConfig.type || getLayoutType(element);
91
+
92
+ if (layoutType) {
93
+ addClass(element, `layout--${layoutType}-justify-${layoutConfig.justify}`);
94
+ }
95
+ }
96
+
97
+ // Apply grid-specific properties
98
+ if (layoutConfig.type === 'grid' || getLayoutType(element) === 'grid') {
99
+ // Grid columns
100
+ if (typeof layoutConfig.columns === 'number') {
101
+ addClass(element, `layout--grid-cols-${layoutConfig.columns}`);
102
+ } else if (layoutConfig.columns === 'auto-fill') {
103
+ addClass(element, 'layout--grid-fill');
104
+ } else if (layoutConfig.columns === 'auto-fit') {
105
+ addClass(element, 'layout--grid-cols-auto-fit');
106
+ }
107
+
108
+ // Other grid properties
109
+ if (layoutConfig.dense) {
110
+ addClass(element, 'layout--grid-dense');
111
+ }
112
+
113
+ if (layoutConfig.autoHeight) {
114
+ addClass(element, 'layout--grid-auto-height');
115
+ }
116
+ }
117
+
118
+ // Apply row-specific properties
119
+ if (layoutConfig.type === 'row' || getLayoutType(element) === 'row') {
120
+ // Wrap behavior
121
+ if (layoutConfig.wrap === false || layoutConfig.wrap === 'nowrap') {
122
+ addClass(element, 'layout--row-nowrap');
123
+ } else if (layoutConfig.wrap === 'reverse') {
124
+ addClass(element, 'layout--row-wrap-reverse');
125
+ }
126
+
127
+ // Mobile responsiveness
128
+ if (layoutConfig.mobileStack) {
129
+ addClass(element, 'layout--row-mobile-stack');
130
+ }
131
+
132
+ if (layoutConfig.mobileScroll) {
133
+ addClass(element, 'layout--row-mobile-scroll');
134
+ }
135
+ }
136
+
137
+ // No masonry, split or sidebar specific properties anymore
138
+
139
+ // Add any additional custom classes
140
+ if (layoutConfig.class) {
141
+ layoutConfig.class.split(' ').filter(Boolean).forEach(cls => {
142
+ // Don't prefix these classes as they're user-defined
143
+ element.classList.add(cls);
144
+ });
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Applies layout item classes based on the configuration
150
+ *
151
+ * @param element - Element to apply layout item classes to
152
+ * @param itemConfig - Layout item configuration
153
+ */
154
+ export function applyLayoutItemClasses(element: HTMLElement, itemConfig: any): void {
155
+ if (!element || !itemConfig) return;
156
+
157
+ // Add the base layout item class
158
+ addClass(element, 'layout__item');
159
+
160
+ // Add width classes
161
+ if (itemConfig.width && itemConfig.width >= 1 && itemConfig.width <= 12) {
162
+ addClass(element, `layout__item--${itemConfig.width}`);
163
+ }
164
+
165
+ // Add responsive classes
166
+ if (itemConfig.sm) addClass(element, `layout__item--sm-${itemConfig.sm}`);
167
+ if (itemConfig.md) addClass(element, `layout__item--md-${itemConfig.md}`);
168
+ if (itemConfig.lg) addClass(element, `layout__item--lg-${itemConfig.lg}`);
169
+ if (itemConfig.xl) addClass(element, `layout__item--xl-${itemConfig.xl}`);
170
+
171
+ // Add grid span classes
172
+ if (itemConfig.span) addClass(element, `layout__item--span-${itemConfig.span}`);
173
+ if (itemConfig.rowSpan) addClass(element, `layout__item--row-span-${itemConfig.rowSpan}`);
174
+
175
+ // Add ordering
176
+ if (itemConfig.order) {
177
+ if (typeof itemConfig.order === 'number') {
178
+ addClass(element, `layout__item--order-${itemConfig.order}`);
179
+ } else {
180
+ addClass(element, `layout__item--order-${itemConfig.order}`);
181
+ }
182
+ }
183
+
184
+ // Add alignment
185
+ if (itemConfig.align) {
186
+ addClass(element, `layout__item--self-${itemConfig.align}`);
187
+ }
188
+
189
+ // Add auto class
190
+ if (itemConfig.auto) {
191
+ addClass(element, 'layout__item--auto');
192
+ }
193
+ }
@@ -1,7 +1,7 @@
1
1
  // src/core/layout/create.ts
2
2
  /**
3
3
  * @module core/layout
4
- * @description Main layout creation functionality with optimized DOM operations
4
+ * @description Main layout creation functionality
5
5
  */
6
6
 
7
7
  import { Schema, LayoutResult, LayoutOptions } from './types';
@@ -10,7 +10,6 @@ import { createLayoutResult } from './result';
10
10
 
11
11
  /**
12
12
  * Creates a DOM or component layout based on a layout definition
13
- * Uses batched DOM operations for better performance
14
13
  *
15
14
  * @param schema - Layout definition (array-based, object-based, or HTML string)
16
15
  * @param parentElement - Optional parent element to attach layout to
@@ -1,7 +1,7 @@
1
1
  // src/core/layout/index.ts
2
2
  /**
3
3
  * @module core/layout
4
- * @description Optimized layout creation system with simplified API
4
+ * @description Layout creation system with simplified API
5
5
  */
6
6
 
7
7
  // Export essential types
@@ -10,7 +10,9 @@ export type {
10
10
  ElementDefinition,
11
11
  Schema,
12
12
  LayoutResult,
13
- LayoutOptions
13
+ LayoutOptions,
14
+ LayoutConfig,
15
+ LayoutItemConfig
14
16
  } from './types';
15
17
 
16
18
  // Export utility functions
@@ -21,6 +23,14 @@ export { createLayout } from './create';
21
23
  export { createLayoutResult } from './result';
22
24
  export { processSchema, createComponentInstance } from './processor';
23
25
 
26
+ // Export configuration utilities
27
+ export {
28
+ applyLayoutClasses,
29
+ applyLayoutItemClasses,
30
+ getLayoutType,
31
+ cleanupLayoutClasses
32
+ } from './config';
33
+
24
34
  // Default export for backward compatibility and simpler usage
25
35
  import { createLayout } from './create';
26
36
  export default createLayout;
@@ -8,8 +8,7 @@ import { createElement } from '../dom/create';
8
8
  import { Schema, LayoutResult, LayoutOptions } from './types';
9
9
  import { isComponent, createFragment, processClassNames } from './utils';
10
10
  import { createLayoutResult } from './result';
11
- import { createComponentInstance } from './processor';
12
- import { processArraySchema } from './array';
11
+ import { createComponentInstance } from './processor';
13
12
 
14
13
  /**
15
14
  * Processes an object-based layout definition
@@ -121,4 +120,4 @@ export function processObjectSchema(
121
120
  }
122
121
 
123
122
  return createLayoutResult(layout);
124
- }
123
+ }
@@ -1,7 +1,7 @@
1
1
  // src/core/layout/processor.ts
2
2
  /**
3
3
  * @module core/layout
4
- * @description Lightweight processor for layout creation, optimized for bundle size
4
+ * @description Processor for layout creation
5
5
  */
6
6
 
7
7
  import { Schema, LayoutResult, LayoutOptions } from './types';
@@ -9,6 +9,7 @@ import { isComponent } from './utils';
9
9
  import { processArraySchema } from './array';
10
10
  import { processObjectSchema } from './object';
11
11
  import { isObject } from '../utils';
12
+ import { applyLayoutClasses, applyLayoutItemClasses } from './config';
12
13
 
13
14
  /**
14
15
  * Creates a component from a constructor or factory function
@@ -23,22 +24,55 @@ export function createComponentInstance(
23
24
  options: Record<string, any> = {},
24
25
  layoutOptions: LayoutOptions = {}
25
26
  ): any {
26
- // Check if Component is a class constructor
27
- const isClass = typeof Component === 'function' &&
28
- Component.prototype &&
29
- Component.prototype.constructor === Component &&
30
- // Exclude native constructors like Object, Array, etc.
31
- Object.getPrototypeOf(Component) !== Function.prototype;
27
+ try {
28
+ // Save layout and layoutItem configs before creating component
29
+ const layoutConfig = options.layout;
30
+ const layoutItemConfig = options.layoutItem;
31
+
32
+ // Remove layout and layoutItem from options to prevent them becoming attributes
33
+ const cleanOptions = { ...options };
34
+ delete cleanOptions.layout;
35
+ delete cleanOptions.layoutItem;
36
+
37
+ // Check if Component is a class constructor
38
+ const isClass = typeof Component === 'function' &&
39
+ Component.prototype &&
40
+ Component.prototype.constructor === Component &&
41
+ // Exclude native constructors like Object, Array, etc.
42
+ Object.getPrototypeOf(Component) !== Function.prototype;
32
43
 
33
- // Use 'new' for class constructors, call directly for function factories
34
- return isClass
35
- ? new Component(options)
36
- : Component(options);
44
+ // Create the component with clean options
45
+ const component = isClass
46
+ ? new Component(cleanOptions)
47
+ : Component(cleanOptions);
48
+
49
+ // Apply layout configuration to the created component
50
+ if (component) {
51
+ // Get the actual DOM element
52
+ const element = component.element || (component instanceof HTMLElement ? component : null);
53
+ if (element) {
54
+ // Apply layout classes if layout config exists
55
+ if (layoutConfig) {
56
+ applyLayoutClasses(element, layoutConfig);
57
+ }
58
+
59
+ // Apply layout item classes if layoutItem config exists
60
+ if (layoutItemConfig) {
61
+ applyLayoutItemClasses(element, layoutItemConfig);
62
+ }
63
+ }
64
+ }
65
+
66
+ return component;
67
+ } catch (error) {
68
+ console.error('Error creating component instance:', error);
69
+ // Return a basic element as fallback
70
+ return document.createElement('div');
71
+ }
37
72
  }
38
73
 
39
74
  /**
40
75
  * Processes any type of layout definition (array or object)
41
- * This is the main entry point for schema processing
42
76
  *
43
77
  * @param schema - Layout schema to process
44
78
  * @param parentElement - Parent element to attach to
@@ -52,6 +86,20 @@ export function processSchema(
52
86
  level: number = 0,
53
87
  options: LayoutOptions = {}
54
88
  ): LayoutResult {
89
+ // Validate input to provide helpful error messages
90
+ if (!schema) {
91
+ console.warn('Empty schema provided to layout processor');
92
+ return {
93
+ layout: {},
94
+ element: document.createElement('div'),
95
+ component: {},
96
+ get: () => null,
97
+ getAll: () => ({}),
98
+ destroy: () => {}
99
+ };
100
+ }
101
+
102
+ // Process based on schema type
55
103
  return Array.isArray(schema)
56
104
  ? processArraySchema(schema, parentElement, level, options)
57
105
  : processObjectSchema(schema, parentElement, options);
@@ -1,7 +1,7 @@
1
1
  // src/core/layout/result.ts
2
2
  /**
3
3
  * @module core/layout
4
- * @description Simplified layout result creation and management
4
+ * @description Layout result creation and management
5
5
  */
6
6
 
7
7
  import { LayoutResult } from './types';
@@ -9,7 +9,6 @@ import { isComponent, flattenLayout } from './utils';
9
9
 
10
10
  /**
11
11
  * Creates a result object with the layout and utility functions
12
- * Simplified API for better usability and reduced overhead
13
12
  *
14
13
  * @param layout - The raw layout object
15
14
  * @returns Result object with layout and utility functions
@@ -1,51 +1,124 @@
1
1
  // src/core/layout/types.ts
2
2
  /**
3
3
  * @module core/layout
4
- * @description Optimized type definitions for layout creation system
4
+ * @description Type definitions for layout creation system
5
5
  */
6
6
 
7
+ /**
8
+ * Layout configuration options
9
+ */
10
+ export interface LayoutConfig {
11
+ /** Base layout type */
12
+ type?: 'stack' | 'row' | 'grid' | string;
13
+
14
+ /** Spacing between elements */
15
+ gap?: number | string;
16
+
17
+ /** Additional CSS classes */
18
+ class?: string;
19
+
20
+ /** Alignment of items along the cross axis */
21
+ align?: 'start' | 'center' | 'end' | 'stretch';
22
+
23
+ /** Alignment of items along the main axis */
24
+ justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
25
+
26
+ /** Whether and how items should wrap */
27
+ wrap?: boolean | 'reverse' | 'nowrap';
28
+
29
+ /** Whether row items should stack vertically on mobile */
30
+ mobileStack?: boolean;
31
+
32
+ /** Whether row items should scroll horizontally on mobile */
33
+ mobileScroll?: boolean;
34
+
35
+ /** Number of columns or automatic sizing method */
36
+ columns?: number | 'auto-fit' | 'auto-fill';
37
+
38
+ /** Minimum item width for grid layouts */
39
+ minWidth?: string | number;
40
+
41
+ /** Whether to use dense packing algorithm for grid */
42
+ dense?: boolean;
43
+
44
+ /** Whether grid items should adjust height automatically */
45
+ autoHeight?: boolean;
46
+ }
47
+
48
+ /**
49
+ * Configuration for individual layout items
50
+ */
51
+ export interface LayoutItemConfig {
52
+ /** Column width in a 12-column grid */
53
+ width?: number;
54
+
55
+ /** Width on small screens */
56
+ sm?: number;
57
+
58
+ /** Width on medium screens */
59
+ md?: number;
60
+
61
+ /** Width on large screens */
62
+ lg?: number;
63
+
64
+ /** Width on extra-large screens */
65
+ xl?: number;
66
+
67
+ /** Number of grid columns to span */
68
+ span?: number;
69
+
70
+ /** Number of grid rows to span */
71
+ rowSpan?: number;
72
+
73
+ /** Display order */
74
+ order?: number | 'first' | 'last';
75
+
76
+ /** Self-alignment within container */
77
+ align?: 'start' | 'center' | 'end' | 'stretch';
78
+
79
+ /** Whether item should automatically size */
80
+ auto?: boolean;
81
+ }
82
+
7
83
  /**
8
84
  * Interface for component-like objects
9
85
  */
10
86
  export interface ComponentLike {
11
- /**
12
- * DOM element reference
13
- */
87
+ /** DOM element reference */
14
88
  element: HTMLElement;
15
89
 
16
- /**
17
- * Optional method to clean up resources
18
- */
90
+ /** Optional method to clean up resources */
19
91
  destroy?: () => void;
20
92
 
21
- /**
22
- * Allow additional properties
23
- */
93
+ /** Allow additional properties */
24
94
  [key: string]: any;
25
95
  }
26
96
 
97
+ /**
98
+ * Extended options for element creation
99
+ */
100
+ export interface ElementOptions extends Record<string, any> {
101
+ /** Layout configuration for the element */
102
+ layout?: LayoutConfig;
103
+
104
+ /** Layout item configuration */
105
+ layoutItem?: LayoutItemConfig;
106
+ }
107
+
27
108
  /**
28
109
  * Definition for a single element in the layout
29
110
  */
30
111
  export interface ElementDefinition {
31
- /**
32
- * Optional name to reference the element
33
- */
112
+ /** Optional name to reference the element */
34
113
  name?: string;
35
114
 
36
- /**
37
- * Creator function that produces an HTMLElement or ComponentLike
38
- */
115
+ /** Creator function that produces an HTMLElement or ComponentLike */
39
116
  creator?: (options?: Record<string, any>) => HTMLElement | ComponentLike;
40
117
 
41
- /**
42
- * Options to pass to the creator function
43
- */
44
- options?: Record<string, any>;
118
+ /** Options to pass to the creator function */
119
+ options?: ElementOptions;
45
120
 
46
- /**
47
- * Child elements to create and attach
48
- */
121
+ /** Child elements to create and attach */
49
122
  children?: Record<string, ElementDefinition>;
50
123
  }
51
124
 
@@ -53,14 +126,10 @@ export interface ElementDefinition {
53
126
  * Schema for layout creation
54
127
  */
55
128
  export interface Schema {
56
- /**
57
- * Root element definition
58
- */
129
+ /** Root element definition */
59
130
  element?: ElementDefinition;
60
131
 
61
- /**
62
- * Additional elements
63
- */
132
+ /** Additional elements */
64
133
  [key: string]: ElementDefinition | undefined;
65
134
  }
66
135
 
@@ -68,41 +137,27 @@ export interface Schema {
68
137
  * Options for layout creation
69
138
  */
70
139
  export interface LayoutOptions {
71
- /**
72
- * Default creator function to use if not specified in schema
73
- */
140
+ /** Default creator function to use if not specified in schema */
74
141
  creator?: (options?: Record<string, any>) => HTMLElement | ComponentLike;
75
142
 
76
- /**
77
- * Whether to apply CSS class prefix
78
- * @default true
79
- */
143
+ /** Whether to apply CSS class prefix @default true */
80
144
  prefix?: boolean;
81
-
82
- /**
83
- * Additional options
84
- */
145
+
146
+ /** Additional options */
85
147
  [key: string]: any;
86
148
  }
87
149
 
88
150
  /**
89
151
  * Result object returned after creating a layout
90
- * Simplified API with essential methods
91
152
  */
92
153
  export interface LayoutResult {
93
- /**
94
- * The raw layout object with all components
95
- */
154
+ /** The raw layout object with all components */
96
155
  layout: Record<string, any>;
97
156
 
98
- /**
99
- * Reference to the root element for convenience
100
- */
157
+ /** Reference to the root element for convenience */
101
158
  element: HTMLElement | ComponentLike;
102
159
 
103
- /**
104
- * Flattened component map
105
- */
160
+ /** Flattened component map */
106
161
  component: Record<string, any>;
107
162
 
108
163
  /**