mtrl 0.1.2 → 0.2.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/README.md +70 -22
- package/index.ts +33 -0
- package/package.json +14 -5
- package/src/components/button/{styles.scss → _styles.scss} +2 -2
- package/src/components/button/api.ts +89 -0
- package/src/components/button/button.ts +50 -0
- package/src/components/button/config.ts +75 -0
- package/src/components/button/constants.ts +17 -0
- package/src/components/button/index.ts +4 -0
- package/src/components/button/types.ts +118 -0
- package/src/components/card/_styles.scss +359 -0
- package/src/components/card/actions.ts +48 -0
- package/src/components/card/api.ts +102 -0
- package/src/components/card/card.ts +41 -0
- package/src/components/card/config.ts +99 -0
- package/src/components/card/constants.ts +69 -0
- package/src/components/card/content.ts +48 -0
- package/src/components/card/features.ts +228 -0
- package/src/components/card/header.ts +88 -0
- package/src/components/card/index.ts +19 -0
- package/src/components/card/media.ts +52 -0
- package/src/components/card/types.ts +174 -0
- package/src/components/checkbox/api.ts +82 -0
- package/src/components/checkbox/checkbox.ts +75 -0
- package/src/components/checkbox/config.ts +90 -0
- package/src/components/checkbox/index.ts +4 -0
- package/src/components/checkbox/types.ts +146 -0
- package/src/components/chip/_styles.scss +372 -0
- package/src/components/chip/api.ts +115 -0
- package/src/components/chip/chip-set.ts +225 -0
- package/src/components/chip/chip.ts +82 -0
- package/src/components/chip/config.ts +92 -0
- package/src/components/chip/constants.ts +38 -0
- package/src/components/chip/index.ts +4 -0
- package/src/components/chip/types.ts +172 -0
- package/src/components/list/api.ts +72 -0
- package/src/components/list/config.ts +43 -0
- package/src/components/list/{constants.js → constants.ts} +34 -7
- package/src/components/list/features.ts +224 -0
- package/src/components/list/index.ts +14 -0
- package/src/components/list/list-item.ts +120 -0
- package/src/components/list/list.ts +37 -0
- package/src/components/list/types.ts +179 -0
- package/src/components/list/utils.ts +47 -0
- package/src/components/menu/api.ts +119 -0
- package/src/components/menu/config.ts +54 -0
- package/src/components/menu/constants.ts +154 -0
- package/src/components/menu/features/items-manager.ts +457 -0
- package/src/components/menu/features/keyboard-navigation.ts +133 -0
- package/src/components/menu/features/positioning.ts +127 -0
- package/src/components/menu/features/{visibility.js → visibility.ts} +66 -64
- package/src/components/menu/index.ts +14 -0
- package/src/components/menu/menu-item.ts +43 -0
- package/src/components/menu/menu.ts +53 -0
- package/src/components/menu/types.ts +178 -0
- package/src/components/navigation/api.ts +79 -0
- package/src/components/navigation/config.ts +61 -0
- package/src/components/navigation/{constants.js → constants.ts} +10 -10
- package/src/components/navigation/index.ts +14 -0
- package/src/components/navigation/nav-item.ts +148 -0
- package/src/components/navigation/navigation.ts +50 -0
- package/src/components/navigation/types.ts +212 -0
- package/src/components/progress/_styles.scss +204 -0
- package/src/components/progress/api.ts +179 -0
- package/src/components/progress/config.ts +124 -0
- package/src/components/progress/constants.ts +43 -0
- package/src/components/progress/index.ts +5 -0
- package/src/components/progress/progress.ts +163 -0
- package/src/components/progress/types.ts +102 -0
- package/src/components/snackbar/api.ts +162 -0
- package/src/components/snackbar/config.ts +62 -0
- package/src/components/snackbar/{constants.js → constants.ts} +21 -4
- package/src/components/snackbar/features.ts +76 -0
- package/src/components/snackbar/index.ts +4 -0
- package/src/components/snackbar/position.ts +71 -0
- package/src/components/snackbar/queue.ts +76 -0
- package/src/components/snackbar/snackbar.ts +60 -0
- package/src/components/snackbar/types.ts +58 -0
- package/src/components/switch/api.ts +77 -0
- package/src/components/switch/config.ts +74 -0
- package/src/components/switch/index.ts +4 -0
- package/src/components/switch/switch.ts +52 -0
- package/src/components/switch/types.ts +142 -0
- package/src/components/textfield/api.ts +72 -0
- package/src/components/textfield/config.ts +54 -0
- package/src/components/textfield/{constants.js → constants.ts} +38 -5
- package/src/components/textfield/index.ts +4 -0
- package/src/components/textfield/textfield.ts +50 -0
- package/src/components/textfield/types.ts +139 -0
- package/src/core/compose/base.ts +43 -0
- package/src/core/compose/component.ts +247 -0
- package/src/core/compose/features/checkable.ts +155 -0
- package/src/core/compose/features/disabled.ts +116 -0
- package/src/core/compose/features/events.ts +65 -0
- package/src/core/compose/features/icon.ts +67 -0
- package/src/core/compose/features/index.ts +35 -0
- package/src/core/compose/features/input.ts +174 -0
- package/src/core/compose/features/lifecycle.ts +139 -0
- package/src/core/compose/features/position.ts +94 -0
- package/src/core/compose/features/ripple.ts +55 -0
- package/src/core/compose/features/size.ts +29 -0
- package/src/core/compose/features/style.ts +31 -0
- package/src/core/compose/features/text.ts +44 -0
- package/src/core/compose/features/textinput.ts +225 -0
- package/src/core/compose/features/textlabel.ts +92 -0
- package/src/core/compose/features/track.ts +84 -0
- package/src/core/compose/features/variant.ts +29 -0
- package/src/core/compose/features/withEvents.ts +137 -0
- package/src/core/compose/index.ts +54 -0
- package/src/core/compose/{pipe.js → pipe.ts} +16 -11
- package/src/core/config/component-config.ts +136 -0
- package/src/core/config.ts +211 -0
- package/src/core/dom/{attributes.js → attributes.ts} +11 -11
- package/src/core/dom/classes.ts +60 -0
- package/src/core/dom/create.ts +188 -0
- package/src/core/dom/events.ts +209 -0
- package/src/core/dom/index.ts +10 -0
- package/src/core/dom/utils.ts +97 -0
- package/src/core/index.ts +111 -0
- package/src/core/state/disabled.ts +81 -0
- package/src/core/state/emitter.ts +94 -0
- package/src/core/state/events.ts +88 -0
- package/src/core/state/index.ts +16 -0
- package/src/core/state/lifecycle.ts +131 -0
- package/src/core/state/store.ts +197 -0
- package/src/core/utils/index.ts +45 -0
- package/src/core/utils/{mobile.js → mobile.ts} +48 -24
- package/src/core/utils/object.ts +41 -0
- package/src/core/utils/validate.ts +234 -0
- package/src/{index.js → index.ts} +4 -2
- package/index.js +0 -11
- package/src/components/button/api.js +0 -54
- package/src/components/button/button.js +0 -81
- package/src/components/button/config.js +0 -10
- package/src/components/button/constants.js +0 -63
- package/src/components/button/index.js +0 -2
- package/src/components/checkbox/api.js +0 -45
- package/src/components/checkbox/checkbox.js +0 -96
- package/src/components/checkbox/index.js +0 -2
- package/src/components/container/api.js +0 -42
- package/src/components/container/container.js +0 -45
- package/src/components/container/index.js +0 -2
- package/src/components/container/styles.scss +0 -66
- package/src/components/list/index.js +0 -2
- package/src/components/list/list-item.js +0 -147
- package/src/components/list/list.js +0 -267
- package/src/components/menu/api.js +0 -117
- package/src/components/menu/constants.js +0 -42
- package/src/components/menu/features/items-manager.js +0 -375
- package/src/components/menu/features/keyboard-navigation.js +0 -129
- package/src/components/menu/features/positioning.js +0 -125
- package/src/components/menu/index.js +0 -2
- package/src/components/menu/menu-item.js +0 -41
- package/src/components/menu/menu.js +0 -54
- package/src/components/navigation/api.js +0 -43
- package/src/components/navigation/index.js +0 -2
- package/src/components/navigation/nav-item.js +0 -137
- package/src/components/navigation/navigation.js +0 -55
- package/src/components/snackbar/api.js +0 -125
- package/src/components/snackbar/features.js +0 -69
- package/src/components/snackbar/index.js +0 -2
- package/src/components/snackbar/position.js +0 -63
- package/src/components/snackbar/queue.js +0 -74
- package/src/components/snackbar/snackbar.js +0 -70
- package/src/components/switch/api.js +0 -44
- package/src/components/switch/index.js +0 -2
- package/src/components/switch/switch.js +0 -71
- package/src/components/textfield/api.js +0 -49
- package/src/components/textfield/index.js +0 -2
- package/src/components/textfield/textfield.js +0 -68
- package/src/core/build/_ripple.scss +0 -79
- package/src/core/build/constants.js +0 -51
- package/src/core/build/icon.js +0 -78
- package/src/core/build/ripple.js +0 -159
- package/src/core/build/text.js +0 -54
- package/src/core/compose/base.js +0 -8
- package/src/core/compose/component.js +0 -225
- package/src/core/compose/features/checkable.js +0 -114
- package/src/core/compose/features/disabled.js +0 -64
- package/src/core/compose/features/events.js +0 -48
- package/src/core/compose/features/icon.js +0 -33
- package/src/core/compose/features/index.js +0 -20
- package/src/core/compose/features/input.js +0 -100
- package/src/core/compose/features/lifecycle.js +0 -69
- package/src/core/compose/features/position.js +0 -60
- package/src/core/compose/features/ripple.js +0 -32
- package/src/core/compose/features/size.js +0 -9
- package/src/core/compose/features/style.js +0 -12
- package/src/core/compose/features/text.js +0 -17
- package/src/core/compose/features/textinput.js +0 -114
- package/src/core/compose/features/textlabel.js +0 -28
- package/src/core/compose/features/track.js +0 -49
- package/src/core/compose/features/variant.js +0 -9
- package/src/core/compose/features/withEvents.js +0 -67
- package/src/core/compose/index.js +0 -16
- package/src/core/config.js +0 -140
- package/src/core/dom/classes.js +0 -70
- package/src/core/dom/create.js +0 -132
- package/src/core/dom/events.js +0 -175
- package/src/core/dom/index.js +0 -5
- package/src/core/dom/utils.js +0 -22
- package/src/core/index.js +0 -23
- package/src/core/state/disabled.js +0 -51
- package/src/core/state/emitter.js +0 -63
- package/src/core/state/events.js +0 -29
- package/src/core/state/index.js +0 -6
- package/src/core/state/lifecycle.js +0 -64
- package/src/core/state/store.js +0 -112
- package/src/core/utils/index.js +0 -39
- package/src/core/utils/object.js +0 -22
- package/src/core/utils/validate.js +0 -37
- /package/src/components/checkbox/{styles.scss → _styles.scss} +0 -0
- /package/src/components/checkbox/{constants.js → constants.ts} +0 -0
- /package/src/components/list/{styles.scss → _styles.scss} +0 -0
- /package/src/components/menu/{styles.scss → _styles.scss} +0 -0
- /package/src/components/navigation/{styles.scss → _styles.scss} +0 -0
- /package/src/components/snackbar/{styles.scss → _styles.scss} +0 -0
- /package/src/components/switch/{styles.scss → _styles.scss} +0 -0
- /package/src/components/switch/{constants.js → constants.ts} +0 -0
- /package/src/components/textfield/{styles.scss → _styles.scss} +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// src/core/dom/events.ts
|
|
2
|
+
/**
|
|
3
|
+
* @module core/dom
|
|
4
|
+
* @description DOM manipulation utilities
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Event manager interface for handling DOM events
|
|
9
|
+
*/
|
|
10
|
+
export interface EventManager {
|
|
11
|
+
/**
|
|
12
|
+
* Add an event listener with options
|
|
13
|
+
* @param event - Event name
|
|
14
|
+
* @param handler - Event handler
|
|
15
|
+
* @param options - addEventListener options
|
|
16
|
+
* @returns EventManager instance for chaining
|
|
17
|
+
*/
|
|
18
|
+
on: <T extends Event>(event: string, handler: (e: T) => void, options?: AddEventListenerOptions) => EventManager;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Remove an event listener
|
|
22
|
+
* @param event - Event name
|
|
23
|
+
* @param handler - Event handler
|
|
24
|
+
* @returns EventManager instance for chaining
|
|
25
|
+
*/
|
|
26
|
+
off: <T extends Event>(event: string, handler: (e: T) => void) => EventManager;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Temporarily disable all event listeners
|
|
30
|
+
* @returns EventManager instance for chaining
|
|
31
|
+
*/
|
|
32
|
+
pause: () => EventManager;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Re-enable all event listeners
|
|
36
|
+
* @returns EventManager instance for chaining
|
|
37
|
+
*/
|
|
38
|
+
resume: () => EventManager;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Remove all event listeners and clean up
|
|
42
|
+
*/
|
|
43
|
+
destroy: () => void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get all active handlers
|
|
47
|
+
* @returns Map of active handlers
|
|
48
|
+
*/
|
|
49
|
+
getHandlers: () => Map<string, HandlerInfo>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a specific handler exists
|
|
53
|
+
* @param event - Event name
|
|
54
|
+
* @param handler - Event handler
|
|
55
|
+
* @returns Whether handler exists
|
|
56
|
+
*/
|
|
57
|
+
hasHandler: <T extends Event>(event: string, handler: (e: T) => void) => boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Handler information
|
|
62
|
+
*/
|
|
63
|
+
interface HandlerInfo {
|
|
64
|
+
original: EventListener;
|
|
65
|
+
enhanced: EventListener;
|
|
66
|
+
event: string;
|
|
67
|
+
options: AddEventListenerOptions;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates an event manager to handle DOM events with enhanced functionality.
|
|
72
|
+
* Provides a robust interface for managing event listeners with error handling,
|
|
73
|
+
* cleanup, and lifecycle management.
|
|
74
|
+
*
|
|
75
|
+
* @param element - DOM element to attach events to
|
|
76
|
+
* @returns Event manager interface
|
|
77
|
+
*/
|
|
78
|
+
export const createEventManager = (element: HTMLElement): EventManager => {
|
|
79
|
+
// Store handlers with their metadata
|
|
80
|
+
const handlers = new Map<string, HandlerInfo>();
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Creates a unique handler identifier
|
|
84
|
+
* @param event - Event name
|
|
85
|
+
* @param handler - EventListener
|
|
86
|
+
* @returns Unique identifier
|
|
87
|
+
*/
|
|
88
|
+
const createHandlerId = (event: string, handler: EventListener): string =>
|
|
89
|
+
`${event}_${handler.toString()}`;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wraps an event handler with error boundary and logging
|
|
93
|
+
* @param handler - Original event handler
|
|
94
|
+
* @param event - Event name for error context
|
|
95
|
+
* @returns Enhanced handler with error boundary
|
|
96
|
+
*/
|
|
97
|
+
const enhanceHandler = (handler: EventListener, event: string): EventListener => (e: Event) => {
|
|
98
|
+
try {
|
|
99
|
+
handler(e);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`Error in ${event} handler:`, error);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Safely removes event listener
|
|
107
|
+
* @param event - Event name
|
|
108
|
+
* @param handler - Event handler
|
|
109
|
+
*/
|
|
110
|
+
const safeRemoveListener = (event: string, handler: EventListener): void => {
|
|
111
|
+
try {
|
|
112
|
+
element.removeEventListener(event, handler);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.warn(`Failed to remove ${event} listener:`, error);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
/**
|
|
120
|
+
* Adds an event listener with options
|
|
121
|
+
* @param event - Event name
|
|
122
|
+
* @param handler - Event handler
|
|
123
|
+
* @param options - addEventListener options
|
|
124
|
+
* @returns EventManager instance for chaining
|
|
125
|
+
*/
|
|
126
|
+
on<T extends Event>(event: string, handler: (e: T) => void, options: AddEventListenerOptions = {}): EventManager {
|
|
127
|
+
const enhanced = enhanceHandler(handler as EventListener, event);
|
|
128
|
+
const id = createHandlerId(event, handler as EventListener);
|
|
129
|
+
|
|
130
|
+
handlers.set(id, {
|
|
131
|
+
original: handler as EventListener,
|
|
132
|
+
enhanced,
|
|
133
|
+
event,
|
|
134
|
+
options
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
element.addEventListener(event, enhanced, options);
|
|
138
|
+
return this;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Removes an event listener
|
|
143
|
+
* @param event - Event name
|
|
144
|
+
* @param handler - Event handler
|
|
145
|
+
* @returns EventManager instance for chaining
|
|
146
|
+
*/
|
|
147
|
+
off<T extends Event>(event: string, handler: (e: T) => void): EventManager {
|
|
148
|
+
const id = createHandlerId(event, handler as EventListener);
|
|
149
|
+
const stored = handlers.get(id);
|
|
150
|
+
|
|
151
|
+
if (stored) {
|
|
152
|
+
safeRemoveListener(event, stored.enhanced);
|
|
153
|
+
handlers.delete(id);
|
|
154
|
+
}
|
|
155
|
+
return this;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Temporarily disables all event listeners
|
|
160
|
+
* @returns EventManager instance for chaining
|
|
161
|
+
*/
|
|
162
|
+
pause(): EventManager {
|
|
163
|
+
handlers.forEach(({ enhanced, event }) => {
|
|
164
|
+
safeRemoveListener(event, enhanced);
|
|
165
|
+
});
|
|
166
|
+
return this;
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Re-enables all event listeners
|
|
171
|
+
* @returns EventManager instance for chaining
|
|
172
|
+
*/
|
|
173
|
+
resume(): EventManager {
|
|
174
|
+
handlers.forEach(({ enhanced, event, options }) => {
|
|
175
|
+
element.addEventListener(event, enhanced, options);
|
|
176
|
+
});
|
|
177
|
+
return this;
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Removes all event listeners and cleans up
|
|
182
|
+
*/
|
|
183
|
+
destroy(): void {
|
|
184
|
+
handlers.forEach(({ enhanced, event }) => {
|
|
185
|
+
safeRemoveListener(event, enhanced);
|
|
186
|
+
});
|
|
187
|
+
handlers.clear();
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Gets all active handlers
|
|
192
|
+
* @returns Map of active handlers
|
|
193
|
+
*/
|
|
194
|
+
getHandlers(): Map<string, HandlerInfo> {
|
|
195
|
+
return new Map(handlers);
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Checks if a specific handler exists
|
|
200
|
+
* @param event - Event name
|
|
201
|
+
* @param handler - Event handler
|
|
202
|
+
* @returns Whether handler exists
|
|
203
|
+
*/
|
|
204
|
+
hasHandler<T extends Event>(event: string, handler: (e: T) => void): boolean {
|
|
205
|
+
const id = createHandlerId(event, handler as EventListener);
|
|
206
|
+
return handlers.has(id);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/core/dom/index.ts
|
|
2
|
+
|
|
3
|
+
export { createElement, withAttributes, withClasses, withContent } from './create';
|
|
4
|
+
export type { CreateElementOptions } from './create';
|
|
5
|
+
|
|
6
|
+
export { setAttributes, removeAttributes } from './attributes';
|
|
7
|
+
export { addClass, removeClass, toggleClass, hasClass } from './classes';
|
|
8
|
+
|
|
9
|
+
export { createEventManager } from './events';
|
|
10
|
+
export type { EventManager } from './events';
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/core/dom/utils.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes class names input by handling various formats:
|
|
5
|
+
* - String with space-separated classes
|
|
6
|
+
* - Array of strings
|
|
7
|
+
* - Mixed array of strings and space-separated classes
|
|
8
|
+
*
|
|
9
|
+
* @param classes - Classes to normalize
|
|
10
|
+
* @returns Array of unique, non-empty class names
|
|
11
|
+
*/
|
|
12
|
+
export const normalizeClasses = (...classes: (string | string[])[]): string[] => {
|
|
13
|
+
return [...new Set(
|
|
14
|
+
classes
|
|
15
|
+
.flat()
|
|
16
|
+
.reduce((acc: string[], cls) => {
|
|
17
|
+
if (typeof cls === 'string') {
|
|
18
|
+
// Split space-separated classes and add them individually
|
|
19
|
+
acc.push(...cls.split(/\s+/));
|
|
20
|
+
}
|
|
21
|
+
return acc;
|
|
22
|
+
}, [])
|
|
23
|
+
.filter(Boolean) // Remove empty strings
|
|
24
|
+
)];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates a DOM element with attributes
|
|
29
|
+
*
|
|
30
|
+
* @param tag - Element tag name
|
|
31
|
+
* @param attributes - Element attributes
|
|
32
|
+
* @returns Created element
|
|
33
|
+
*/
|
|
34
|
+
export const createElement = <T extends HTMLElement>(tag: string, attributes: Record<string, any> = {}): T => {
|
|
35
|
+
const element = document.createElement(tag) as T;
|
|
36
|
+
|
|
37
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
38
|
+
if (key === 'className') {
|
|
39
|
+
element.className = value;
|
|
40
|
+
} else if (key === 'style' && typeof value === 'object') {
|
|
41
|
+
Object.assign(element.style, value);
|
|
42
|
+
} else if (key === 'data' && typeof value === 'object') {
|
|
43
|
+
Object.entries(value).forEach(([dataKey, dataValue]) => {
|
|
44
|
+
element.dataset[dataKey] = String(dataValue);
|
|
45
|
+
});
|
|
46
|
+
} else if (key === 'children' && Array.isArray(value)) {
|
|
47
|
+
value.forEach(child => {
|
|
48
|
+
if (child instanceof Node) {
|
|
49
|
+
element.appendChild(child);
|
|
50
|
+
} else if (child != null) {
|
|
51
|
+
element.appendChild(document.createTextNode(String(child)));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
} else if (key.startsWith('on') && typeof value === 'function') {
|
|
55
|
+
const eventName = key.slice(2).toLowerCase();
|
|
56
|
+
element.addEventListener(eventName, value);
|
|
57
|
+
} else if (value !== null && value !== undefined) {
|
|
58
|
+
element.setAttribute(key, String(value));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return element;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Sets inline styles on an element
|
|
67
|
+
*
|
|
68
|
+
* @param element - Target element
|
|
69
|
+
* @param styles - Styles to set
|
|
70
|
+
* @returns Modified element
|
|
71
|
+
*/
|
|
72
|
+
export const setStyles = <T extends HTMLElement>(element: T, styles: Partial<CSSStyleDeclaration>): T => {
|
|
73
|
+
Object.assign(element.style, styles);
|
|
74
|
+
return element;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Checks if an element matches a selector
|
|
79
|
+
*
|
|
80
|
+
* @param element - Element to check
|
|
81
|
+
* @param selector - CSS selector
|
|
82
|
+
* @returns Whether element matches selector
|
|
83
|
+
*/
|
|
84
|
+
export const matches = (element: Element, selector: string): boolean => {
|
|
85
|
+
return element.matches(selector);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Finds the closest ancestor matching a selector
|
|
90
|
+
*
|
|
91
|
+
* @param element - Starting element
|
|
92
|
+
* @param selector - CSS selector
|
|
93
|
+
* @returns Matching ancestor or null
|
|
94
|
+
*/
|
|
95
|
+
export const closest = <T extends HTMLElement>(element: Element, selector: string): T | null => {
|
|
96
|
+
return element.closest(selector) as T | null;
|
|
97
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/core/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* @module core
|
|
4
|
+
* @description Core utilities and building blocks for the component system
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Config
|
|
8
|
+
export { PREFIX, COMPONENTS, STATES, classNames } from './config';
|
|
9
|
+
export type {
|
|
10
|
+
ThemeConfig,
|
|
11
|
+
ComponentConfig,
|
|
12
|
+
ThemedComponentConfig,
|
|
13
|
+
VariantComponentConfig,
|
|
14
|
+
StateComponentConfig
|
|
15
|
+
} from './config';
|
|
16
|
+
|
|
17
|
+
// Build
|
|
18
|
+
export { createText } from './build/text';
|
|
19
|
+
export { createIcon } from './build/icon';
|
|
20
|
+
export { createRipple } from './build/ripple';
|
|
21
|
+
export { RIPPLE_TIMING } from './build/constants';
|
|
22
|
+
|
|
23
|
+
// DOM manipulation
|
|
24
|
+
export { createElement, withAttributes, withClasses, withContent } from './dom/create';
|
|
25
|
+
export { setAttributes, removeAttributes } from './dom/attributes';
|
|
26
|
+
export { addClass, removeClass, toggleClass, hasClass } from './dom/classes';
|
|
27
|
+
|
|
28
|
+
// State Management
|
|
29
|
+
export { createDisabled } from './state/disabled';
|
|
30
|
+
export { createEventManager } from './state/events';
|
|
31
|
+
export { createLifecycle } from './state/lifecycle';
|
|
32
|
+
export { createEmitter } from './state/emitter';
|
|
33
|
+
export { createStore } from './state/store';
|
|
34
|
+
|
|
35
|
+
// Composition Utilities
|
|
36
|
+
export { pipe, compose, transform } from './compose/pipe';
|
|
37
|
+
export { createBase, withElement } from './compose/component';
|
|
38
|
+
export {
|
|
39
|
+
withEvents,
|
|
40
|
+
withText,
|
|
41
|
+
withIcon,
|
|
42
|
+
withVariant,
|
|
43
|
+
withSize,
|
|
44
|
+
withPosition,
|
|
45
|
+
withDisabled,
|
|
46
|
+
withLifecycle,
|
|
47
|
+
withRipple
|
|
48
|
+
} from './compose/features';
|
|
49
|
+
|
|
50
|
+
// Utilities
|
|
51
|
+
export {
|
|
52
|
+
normalizeClasses,
|
|
53
|
+
when,
|
|
54
|
+
classNames as joinClasses,
|
|
55
|
+
isObject,
|
|
56
|
+
byString,
|
|
57
|
+
hasTouchSupport,
|
|
58
|
+
isMobileDevice,
|
|
59
|
+
normalizeEvent
|
|
60
|
+
} from './utils';
|
|
61
|
+
|
|
62
|
+
// Component feature interfaces for better developer experience
|
|
63
|
+
export type {
|
|
64
|
+
BaseComponent,
|
|
65
|
+
ElementComponent,
|
|
66
|
+
TouchState
|
|
67
|
+
} from './compose/component';
|
|
68
|
+
|
|
69
|
+
export type {
|
|
70
|
+
EventComponent,
|
|
71
|
+
TextComponent,
|
|
72
|
+
IconComponent,
|
|
73
|
+
LifecycleComponent,
|
|
74
|
+
Lifecycle,
|
|
75
|
+
DisabledComponent,
|
|
76
|
+
DisabledManager,
|
|
77
|
+
RippleComponent
|
|
78
|
+
} from './compose/features';
|
|
79
|
+
|
|
80
|
+
// Other interfaces
|
|
81
|
+
export type {
|
|
82
|
+
CreateElementOptions
|
|
83
|
+
} from './dom/create';
|
|
84
|
+
|
|
85
|
+
export type {
|
|
86
|
+
TextManager,
|
|
87
|
+
TextConfig
|
|
88
|
+
} from './build/text';
|
|
89
|
+
|
|
90
|
+
export type {
|
|
91
|
+
IconManager,
|
|
92
|
+
IconConfig
|
|
93
|
+
} from './build/icon';
|
|
94
|
+
|
|
95
|
+
export type {
|
|
96
|
+
RippleController,
|
|
97
|
+
RippleConfig
|
|
98
|
+
} from './build/ripple';
|
|
99
|
+
|
|
100
|
+
export type {
|
|
101
|
+
Emitter,
|
|
102
|
+
EventCallback
|
|
103
|
+
} from './state/emitter';
|
|
104
|
+
|
|
105
|
+
export type {
|
|
106
|
+
DisabledState
|
|
107
|
+
} from './state/disabled';
|
|
108
|
+
|
|
109
|
+
export type {
|
|
110
|
+
NormalizedEvent
|
|
111
|
+
} from './utils/mobile';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/core/state/disabled.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Disabled state manager interface
|
|
5
|
+
*/
|
|
6
|
+
export interface DisabledState {
|
|
7
|
+
/**
|
|
8
|
+
* Enables the element
|
|
9
|
+
* @returns DisabledState instance for chaining
|
|
10
|
+
*/
|
|
11
|
+
enable(): DisabledState;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Disables the element
|
|
15
|
+
* @returns DisabledState instance for chaining
|
|
16
|
+
*/
|
|
17
|
+
disable(): DisabledState;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Toggles the disabled state
|
|
21
|
+
* @returns DisabledState instance for chaining
|
|
22
|
+
*/
|
|
23
|
+
toggle(): DisabledState;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if the element is disabled
|
|
27
|
+
* @returns true if disabled
|
|
28
|
+
*/
|
|
29
|
+
isDisabled(): boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a controller for managing the disabled state of an element
|
|
34
|
+
*
|
|
35
|
+
* @param element - The element to control
|
|
36
|
+
* @returns Disabled state controller
|
|
37
|
+
*/
|
|
38
|
+
export const createDisabled = (element: HTMLElement): DisabledState => {
|
|
39
|
+
return {
|
|
40
|
+
/**
|
|
41
|
+
* Enables the element
|
|
42
|
+
* @returns DisabledState instance for chaining
|
|
43
|
+
*/
|
|
44
|
+
enable() {
|
|
45
|
+
(element as HTMLButtonElement | HTMLInputElement).disabled = false;
|
|
46
|
+
element.removeAttribute('disabled');
|
|
47
|
+
return this;
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Disables the element
|
|
52
|
+
* @returns DisabledState instance for chaining
|
|
53
|
+
*/
|
|
54
|
+
disable() {
|
|
55
|
+
(element as HTMLButtonElement | HTMLInputElement).disabled = true;
|
|
56
|
+
element.setAttribute('disabled', 'true');
|
|
57
|
+
return this;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Toggles the disabled state
|
|
62
|
+
* @returns DisabledState instance for chaining
|
|
63
|
+
*/
|
|
64
|
+
toggle() {
|
|
65
|
+
if ((element as HTMLButtonElement | HTMLInputElement).disabled) {
|
|
66
|
+
this.enable();
|
|
67
|
+
} else {
|
|
68
|
+
this.disable();
|
|
69
|
+
}
|
|
70
|
+
return this;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Checks if the element is disabled
|
|
75
|
+
* @returns true if disabled
|
|
76
|
+
*/
|
|
77
|
+
isDisabled() {
|
|
78
|
+
return (element as HTMLButtonElement | HTMLInputElement).disabled === true;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/core/state/emitter.ts
|
|
2
|
+
/**
|
|
3
|
+
* @module core/state
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Type definition for event callback functions
|
|
8
|
+
*/
|
|
9
|
+
export type EventCallback = (...args: any[]) => void;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Interface for the event emitter
|
|
13
|
+
*/
|
|
14
|
+
export interface Emitter {
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe to an event
|
|
17
|
+
* @param event - Event name
|
|
18
|
+
* @param callback - Event handler
|
|
19
|
+
* @returns Unsubscribe function
|
|
20
|
+
*/
|
|
21
|
+
on(event: string, callback: EventCallback): () => void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Unsubscribe from an event
|
|
25
|
+
* @param event - Event name
|
|
26
|
+
* @param callback - Event handler to remove
|
|
27
|
+
*/
|
|
28
|
+
off(event: string, callback: EventCallback): void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Emit an event
|
|
32
|
+
* @param event - Event name
|
|
33
|
+
* @param args - Event arguments
|
|
34
|
+
*/
|
|
35
|
+
emit(event: string, ...args: any[]): void;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Clear all event listeners
|
|
39
|
+
*/
|
|
40
|
+
clear(): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates an event emitter with subscription management
|
|
45
|
+
* @returns Event emitter interface
|
|
46
|
+
*/
|
|
47
|
+
export const createEmitter = (): Emitter => {
|
|
48
|
+
const events = new Map<string, EventCallback[]>();
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
/**
|
|
52
|
+
* Subscribe to an event
|
|
53
|
+
* @param event - Event name
|
|
54
|
+
* @param callback - Event handler
|
|
55
|
+
* @returns Unsubscribe function
|
|
56
|
+
*/
|
|
57
|
+
on: (event: string, callback: EventCallback): (() => void) => {
|
|
58
|
+
const callbacks = events.get(event) || [];
|
|
59
|
+
events.set(event, [...callbacks, callback]);
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
const callbacks = events.get(event) || [];
|
|
63
|
+
events.set(event, callbacks.filter(cb => cb !== callback));
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Unsubscribe from an event
|
|
69
|
+
* @param event - Event name
|
|
70
|
+
* @param callback - Event handler to remove
|
|
71
|
+
*/
|
|
72
|
+
off(event: string, callback: EventCallback): void {
|
|
73
|
+
const callbacks = events.get(event) || [];
|
|
74
|
+
events.set(event, callbacks.filter(cb => cb !== callback));
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Emit an event
|
|
79
|
+
* @param event - Event name
|
|
80
|
+
* @param args - Event arguments
|
|
81
|
+
*/
|
|
82
|
+
emit: (event: string, ...args: any[]): void => {
|
|
83
|
+
const callbacks = events.get(event) || [];
|
|
84
|
+
callbacks.forEach(cb => cb(...args));
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Clear all event listeners
|
|
89
|
+
*/
|
|
90
|
+
clear: (): void => {
|
|
91
|
+
events.clear();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/core/state/events.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Event manager interface for handling component events
|
|
5
|
+
*/
|
|
6
|
+
export interface EventManagerState {
|
|
7
|
+
/**
|
|
8
|
+
* Adds an event listener
|
|
9
|
+
* @param event - Event name
|
|
10
|
+
* @param handler - Event handler
|
|
11
|
+
* @returns EventManagerState instance for chaining
|
|
12
|
+
*/
|
|
13
|
+
on: (event: string, handler: (...args: any[]) => void) => EventManagerState;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Removes an event listener
|
|
17
|
+
* @param event - Event name
|
|
18
|
+
* @param handler - Event handler
|
|
19
|
+
* @returns EventManagerState instance for chaining
|
|
20
|
+
*/
|
|
21
|
+
off: (event: string, handler: (...args: any[]) => void) => EventManagerState;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Removes all event listeners and cleans up
|
|
25
|
+
*/
|
|
26
|
+
destroy: () => void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Gets all active handlers
|
|
30
|
+
* @returns Map of event names to handlers
|
|
31
|
+
*/
|
|
32
|
+
getHandlers: () => Map<(...args: any[]) => void, string>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates an event manager for a component
|
|
37
|
+
* Simple event handling mechanism for components
|
|
38
|
+
*
|
|
39
|
+
* @param element - Component's DOM element
|
|
40
|
+
* @returns Event manager interface
|
|
41
|
+
*/
|
|
42
|
+
export const createEventManager = (element: HTMLElement): EventManagerState => {
|
|
43
|
+
const handlers = new Map<(...args: any[]) => void, string>();
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
/**
|
|
47
|
+
* Adds an event listener
|
|
48
|
+
* @param event - Event name
|
|
49
|
+
* @param handler - Event handler
|
|
50
|
+
* @returns EventManagerState instance for chaining
|
|
51
|
+
*/
|
|
52
|
+
on(event: string, handler: (...args: any[]) => void): EventManagerState {
|
|
53
|
+
element.addEventListener(event, handler as EventListener);
|
|
54
|
+
handlers.set(handler, event);
|
|
55
|
+
return this;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Removes an event listener
|
|
60
|
+
* @param event - Event name
|
|
61
|
+
* @param handler - Event handler
|
|
62
|
+
* @returns EventManagerState instance for chaining
|
|
63
|
+
*/
|
|
64
|
+
off(event: string, handler: (...args: any[]) => void): EventManagerState {
|
|
65
|
+
element.removeEventListener(event, handler as EventListener);
|
|
66
|
+
handlers.delete(handler);
|
|
67
|
+
return this;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Removes all event listeners and cleans up
|
|
72
|
+
*/
|
|
73
|
+
destroy(): void {
|
|
74
|
+
handlers.forEach((event, handler) => {
|
|
75
|
+
element.removeEventListener(event, handler as EventListener);
|
|
76
|
+
});
|
|
77
|
+
handlers.clear();
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Gets all active handlers
|
|
82
|
+
* @returns Map of handlers to event names
|
|
83
|
+
*/
|
|
84
|
+
getHandlers(): Map<(...args: any[]) => void, string> {
|
|
85
|
+
return new Map(handlers);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/core/state/index.ts
|
|
2
|
+
|
|
3
|
+
export { createEmitter } from './emitter';
|
|
4
|
+
export type { Emitter, EventCallback } from './emitter';
|
|
5
|
+
|
|
6
|
+
export { createStore, loggingMiddleware, deriveFiltered } from './store';
|
|
7
|
+
export type { Store, StoreOptions, Selector, Computation, Updater } from './store';
|
|
8
|
+
|
|
9
|
+
export { createLifecycle } from './lifecycle';
|
|
10
|
+
export type { LifecycleManager, LifecycleManagers } from './lifecycle';
|
|
11
|
+
|
|
12
|
+
export { createDisabled } from './disabled';
|
|
13
|
+
export type { DisabledState } from './disabled';
|
|
14
|
+
|
|
15
|
+
export { createEventManager } from './events';
|
|
16
|
+
export type { EventManagerState } from './events';
|