mtrl 0.1.3 → 0.2.1
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 → _styles.scss} +79 -7
- package/src/components/card/{actions.js → actions.ts} +15 -18
- package/src/components/card/{api.js → api.ts} +33 -33
- package/src/components/card/card.ts +41 -0
- package/src/components/card/config.ts +99 -0
- package/src/components/card/{constants.js → constants.ts} +11 -10
- package/src/components/card/{content.js → content.ts} +15 -18
- package/src/components/card/{features.js → features.ts} +104 -94
- package/src/components/card/{header.js → header.ts} +21 -25
- 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/constants.ts +37 -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/{constants.js → constants.ts} +5 -5
- 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 +255 -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 +251 -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} +3 -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/card/card.js +0 -102
- package/src/components/card/config.js +0 -16
- package/src/components/card/index.js +0 -7
- package/src/components/card/media.js +0 -56
- package/src/components/checkbox/api.js +0 -45
- package/src/components/checkbox/checkbox.js +0 -96
- package/src/components/checkbox/constants.js +0 -88
- 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/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/textfield/{styles.scss → _styles.scss} +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/core/compose/features/index.ts
|
|
2
|
+
|
|
3
|
+
// Core features
|
|
4
|
+
export { withEvents } from './events';
|
|
5
|
+
export { withText } from './text';
|
|
6
|
+
export { withIcon } from './icon';
|
|
7
|
+
export { withVariant } from './variant';
|
|
8
|
+
export { withSize } from './size';
|
|
9
|
+
export { withPosition } from './position';
|
|
10
|
+
export { withRipple } from './ripple';
|
|
11
|
+
export { withInput } from './input';
|
|
12
|
+
export { withCheckable } from './checkable';
|
|
13
|
+
export { withStyle } from './style';
|
|
14
|
+
export { withTextInput } from './textinput';
|
|
15
|
+
export { withTextLabel } from './textlabel';
|
|
16
|
+
export { withTrack } from './track';
|
|
17
|
+
export { withEvents as withEnhancedEvents } from './withEvents';
|
|
18
|
+
|
|
19
|
+
// State management features
|
|
20
|
+
export { withDisabled } from './disabled';
|
|
21
|
+
export { withLifecycle } from './lifecycle';
|
|
22
|
+
|
|
23
|
+
// Re-export interfaces for better developer experience
|
|
24
|
+
export type { EventComponent } from './events';
|
|
25
|
+
export type { TextComponent } from './text';
|
|
26
|
+
export type { IconComponent } from './icon';
|
|
27
|
+
export type { LifecycleComponent, Lifecycle } from './lifecycle';
|
|
28
|
+
export type { DisabledComponent, DisabledManager } from './disabled';
|
|
29
|
+
export type { RippleComponent } from './ripple';
|
|
30
|
+
export type { InputComponent } from './input';
|
|
31
|
+
export type { CheckableComponent, CheckableManager } from './checkable';
|
|
32
|
+
export type { TextInputComponent } from './textinput';
|
|
33
|
+
export type { LabelComponent, LabelManager } from './textlabel';
|
|
34
|
+
export type { TrackComponent } from './track';
|
|
35
|
+
export type { EnhancedEventComponent } from './withEvents';
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// src/core/compose/features/input.ts
|
|
2
|
+
|
|
3
|
+
import { BaseComponent, ElementComponent } from '../component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Input configuration options
|
|
7
|
+
*/
|
|
8
|
+
export interface InputConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Input name attribute
|
|
11
|
+
*/
|
|
12
|
+
name?: string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initial checked state
|
|
16
|
+
*/
|
|
17
|
+
checked?: boolean;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Whether input is required
|
|
21
|
+
*/
|
|
22
|
+
required?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether input is disabled
|
|
26
|
+
*/
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Input value attribute
|
|
31
|
+
*/
|
|
32
|
+
value?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Accessibility label text
|
|
36
|
+
*/
|
|
37
|
+
label?: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Alternative accessibility label
|
|
41
|
+
*/
|
|
42
|
+
ariaLabel?: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Component name for classes
|
|
46
|
+
*/
|
|
47
|
+
componentName?: string;
|
|
48
|
+
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Component with input element and related methods
|
|
54
|
+
*/
|
|
55
|
+
export interface InputComponent extends ElementComponent {
|
|
56
|
+
/**
|
|
57
|
+
* Input element
|
|
58
|
+
*/
|
|
59
|
+
input: HTMLInputElement;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Gets the current input value
|
|
63
|
+
* @returns Current value
|
|
64
|
+
*/
|
|
65
|
+
getValue: () => string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Sets the input value and emits a value event
|
|
69
|
+
* @param value - New value to set
|
|
70
|
+
* @returns Component instance for chaining
|
|
71
|
+
*/
|
|
72
|
+
setValue: (value: string) => InputComponent;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Event emission method if available
|
|
76
|
+
*/
|
|
77
|
+
emit?: (event: string, data: any) => InputComponent;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Creates an input element and adds it to a component
|
|
82
|
+
* Handles both input creation and event emission for state changes
|
|
83
|
+
*
|
|
84
|
+
* @param config - Input configuration
|
|
85
|
+
* @returns Function that enhances a component with input functionality
|
|
86
|
+
*/
|
|
87
|
+
export const withInput = <T extends InputConfig>(config: T = {} as T) =>
|
|
88
|
+
<C extends ElementComponent>(component: C): C & InputComponent => {
|
|
89
|
+
const input = document.createElement('input');
|
|
90
|
+
const name = component.componentName || 'component';
|
|
91
|
+
input.type = 'checkbox';
|
|
92
|
+
input.className = `${component.getClass(name)}-input`;
|
|
93
|
+
|
|
94
|
+
// Ensure input can receive focus
|
|
95
|
+
input.style.position = 'absolute';
|
|
96
|
+
input.style.opacity = '0';
|
|
97
|
+
input.style.cursor = 'pointer';
|
|
98
|
+
// Don't use display: none or visibility: hidden as they prevent focus
|
|
99
|
+
|
|
100
|
+
// The input itself should be focusable, not the wrapper
|
|
101
|
+
component.element.setAttribute('role', 'presentation');
|
|
102
|
+
input.setAttribute('role', name);
|
|
103
|
+
|
|
104
|
+
const attributes: Record<string, string | boolean | undefined> = {
|
|
105
|
+
name: config.name,
|
|
106
|
+
checked: config.checked,
|
|
107
|
+
required: config.required,
|
|
108
|
+
disabled: config.disabled,
|
|
109
|
+
value: config.value || 'on',
|
|
110
|
+
'aria-label': config.label || config.ariaLabel
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
114
|
+
if (value !== null && value !== undefined) {
|
|
115
|
+
if (key === 'disabled' && value === true) {
|
|
116
|
+
input.disabled = true;
|
|
117
|
+
input.setAttribute('disabled', 'true');
|
|
118
|
+
// Note: We don't add the class here because that's handled by withDisabled
|
|
119
|
+
} else if (value === true) {
|
|
120
|
+
input.setAttribute(key, key);
|
|
121
|
+
} else {
|
|
122
|
+
input.setAttribute(key, String(value));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Bridge native checkbox events to our event system
|
|
128
|
+
input.addEventListener('change', (event) => {
|
|
129
|
+
if (component.emit) {
|
|
130
|
+
component.emit('change', {
|
|
131
|
+
checked: input.checked,
|
|
132
|
+
value: input.value,
|
|
133
|
+
nativeEvent: event
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Add keyboard handling
|
|
139
|
+
input.addEventListener('keydown', (event) => {
|
|
140
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
if (!input.disabled) {
|
|
143
|
+
input.checked = !input.checked;
|
|
144
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
component.element.appendChild(input);
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
...component,
|
|
153
|
+
input,
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Gets the current input value
|
|
157
|
+
* @returns Current value
|
|
158
|
+
*/
|
|
159
|
+
getValue: () => input.value,
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sets the input value and emits a value event
|
|
163
|
+
* @param value - New value to set
|
|
164
|
+
* @returns Component instance for chaining
|
|
165
|
+
*/
|
|
166
|
+
setValue(value: string) {
|
|
167
|
+
input.value = value;
|
|
168
|
+
if (component.emit) {
|
|
169
|
+
component.emit('value', { value });
|
|
170
|
+
}
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// src/core/compose/features/lifecycle.ts
|
|
2
|
+
|
|
3
|
+
import { createEmitter, Emitter } from '../../state/emitter';
|
|
4
|
+
import { BaseComponent, ElementComponent } from '../component';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Component managers that can be passed to lifecycle
|
|
8
|
+
*/
|
|
9
|
+
export interface ComponentManagers {
|
|
10
|
+
events?: {
|
|
11
|
+
destroy: () => void;
|
|
12
|
+
};
|
|
13
|
+
text?: {
|
|
14
|
+
getElement: () => HTMLElement | null;
|
|
15
|
+
};
|
|
16
|
+
icon?: {
|
|
17
|
+
getElement: () => HTMLElement | null;
|
|
18
|
+
};
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Lifecycle methods interface
|
|
24
|
+
*/
|
|
25
|
+
export interface Lifecycle {
|
|
26
|
+
/**
|
|
27
|
+
* Registers a handler for mount event
|
|
28
|
+
* @param handler - Function to call when component is mounted
|
|
29
|
+
* @returns Unsubscribe function
|
|
30
|
+
*/
|
|
31
|
+
onMount: (handler: () => void) => () => void;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Registers a handler for unmount event
|
|
35
|
+
* @param handler - Function to call when component is unmounted
|
|
36
|
+
* @returns Unsubscribe function
|
|
37
|
+
*/
|
|
38
|
+
onUnmount: (handler: () => void) => () => void;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Mounts the component
|
|
42
|
+
*/
|
|
43
|
+
mount: () => void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Unmounts the component
|
|
47
|
+
*/
|
|
48
|
+
unmount: () => void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks if component is mounted
|
|
52
|
+
*/
|
|
53
|
+
isMounted: () => boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Destroys the component
|
|
57
|
+
*/
|
|
58
|
+
destroy: () => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Component with lifecycle capabilities
|
|
63
|
+
*/
|
|
64
|
+
export interface LifecycleComponent extends BaseComponent {
|
|
65
|
+
lifecycle: Lifecycle;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Adds lifecycle management to a component
|
|
70
|
+
*
|
|
71
|
+
* @returns Function that enhances a component with lifecycle management
|
|
72
|
+
*/
|
|
73
|
+
export const withLifecycle = () =>
|
|
74
|
+
<T extends ElementComponent>(component: T): T & LifecycleComponent => {
|
|
75
|
+
let mounted = false;
|
|
76
|
+
const emitter: Emitter = createEmitter();
|
|
77
|
+
|
|
78
|
+
const lifecycle: Lifecycle = {
|
|
79
|
+
// Mount/Unmount state management
|
|
80
|
+
onMount: (handler: () => void) => emitter.on('mount', handler),
|
|
81
|
+
onUnmount: (handler: () => void) => emitter.on('unmount', handler),
|
|
82
|
+
|
|
83
|
+
mount: () => {
|
|
84
|
+
if (!mounted) {
|
|
85
|
+
mounted = true;
|
|
86
|
+
emitter.emit('mount');
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
unmount: () => {
|
|
91
|
+
if (mounted) {
|
|
92
|
+
mounted = false;
|
|
93
|
+
emitter.emit('unmount');
|
|
94
|
+
emitter.clear();
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
isMounted: () => mounted,
|
|
99
|
+
|
|
100
|
+
// Cleanup and destruction
|
|
101
|
+
destroy() {
|
|
102
|
+
// First trigger unmount
|
|
103
|
+
if (mounted) {
|
|
104
|
+
this.unmount();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Clean up all event listeners
|
|
108
|
+
if ('events' in component && component.events?.destroy) {
|
|
109
|
+
component.events.destroy();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Clean up text element
|
|
113
|
+
if ('text' in component && component.text?.getElement) {
|
|
114
|
+
const textElement = component.text.getElement();
|
|
115
|
+
if (textElement) {
|
|
116
|
+
textElement.remove();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Clean up icon element
|
|
121
|
+
if ('icon' in component && component.icon?.getElement) {
|
|
122
|
+
const iconElement = component.icon.getElement();
|
|
123
|
+
if (iconElement) {
|
|
124
|
+
iconElement.remove();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Remove the main element
|
|
129
|
+
if (component.element) {
|
|
130
|
+
component.element.remove();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
...component,
|
|
137
|
+
lifecycle
|
|
138
|
+
};
|
|
139
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/core/compose/features/position.ts
|
|
2
|
+
|
|
3
|
+
import { BaseComponent, ElementComponent } from '../component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Available position values
|
|
7
|
+
*/
|
|
8
|
+
export enum POSITIONS {
|
|
9
|
+
LEFT = 'left',
|
|
10
|
+
RIGHT = 'right',
|
|
11
|
+
TOP = 'top',
|
|
12
|
+
BOTTOM = 'bottom',
|
|
13
|
+
START = 'start',
|
|
14
|
+
END = 'end',
|
|
15
|
+
CENTER = 'center'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for position feature
|
|
20
|
+
*/
|
|
21
|
+
export interface PositionConfig {
|
|
22
|
+
position?: string;
|
|
23
|
+
prefix?: string;
|
|
24
|
+
componentName?: string;
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Position manager interface
|
|
30
|
+
*/
|
|
31
|
+
export interface PositionManager {
|
|
32
|
+
/**
|
|
33
|
+
* Sets the component's position
|
|
34
|
+
* @param newPosition - New position value
|
|
35
|
+
* @returns Position manager for chaining
|
|
36
|
+
*/
|
|
37
|
+
setPosition: (newPosition: string) => PositionManager;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Gets the current position
|
|
41
|
+
* @returns Current position
|
|
42
|
+
*/
|
|
43
|
+
getPosition: () => string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Component with position capabilities
|
|
48
|
+
*/
|
|
49
|
+
export interface PositionComponent extends BaseComponent {
|
|
50
|
+
position: PositionManager;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Adds positioning functionality to a component
|
|
55
|
+
*
|
|
56
|
+
* @param config - Configuration object containing position information
|
|
57
|
+
* @returns Function that enhances a component with position capabilities
|
|
58
|
+
*/
|
|
59
|
+
export const withPosition = <T extends PositionConfig>(config: T) =>
|
|
60
|
+
<C extends ElementComponent>(component: C): C & PositionComponent => {
|
|
61
|
+
if (!config.position || !component.element) {
|
|
62
|
+
return component as C & PositionComponent;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Get the position value, normalizing from enum if provided as uppercase
|
|
66
|
+
const position =
|
|
67
|
+
POSITIONS[config.position.toUpperCase() as keyof typeof POSITIONS] ||
|
|
68
|
+
config.position;
|
|
69
|
+
|
|
70
|
+
const className = `${config.prefix}-${config.componentName}--${position}`;
|
|
71
|
+
component.element.classList.add(className);
|
|
72
|
+
|
|
73
|
+
const positionManager: PositionManager = {
|
|
74
|
+
setPosition(newPosition: string) {
|
|
75
|
+
const oldPosition = position;
|
|
76
|
+
const oldClassName = `${config.prefix}-${config.componentName}--${oldPosition}`;
|
|
77
|
+
const newClassName = `${config.prefix}-${config.componentName}--${newPosition}`;
|
|
78
|
+
|
|
79
|
+
component.element.classList.remove(oldClassName);
|
|
80
|
+
component.element.classList.add(newClassName);
|
|
81
|
+
|
|
82
|
+
return this;
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
getPosition() {
|
|
86
|
+
return position;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
...component,
|
|
92
|
+
position: positionManager
|
|
93
|
+
};
|
|
94
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/core/compose/features/ripple.ts
|
|
2
|
+
|
|
3
|
+
import { createRipple, RippleController, RippleConfig } from '../../build/ripple';
|
|
4
|
+
import { BaseComponent, ElementComponent } from '../component';
|
|
5
|
+
import { LifecycleComponent } from './lifecycle';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for ripple feature
|
|
9
|
+
*/
|
|
10
|
+
export interface RippleFeatureConfig {
|
|
11
|
+
ripple?: boolean;
|
|
12
|
+
rippleConfig?: RippleConfig;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Component with ripple capabilities
|
|
18
|
+
*/
|
|
19
|
+
export interface RippleComponent extends BaseComponent {
|
|
20
|
+
ripple: RippleController;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Adds ripple effect functionality to a component
|
|
25
|
+
*
|
|
26
|
+
* @param config - Configuration object
|
|
27
|
+
* @returns Function that enhances a component with ripple effect
|
|
28
|
+
*/
|
|
29
|
+
export const withRipple = <T extends RippleFeatureConfig>(config: T) =>
|
|
30
|
+
<C extends ElementComponent & Partial<LifecycleComponent>>(component: C): C & RippleComponent => {
|
|
31
|
+
if (!config.ripple) return component as C & RippleComponent;
|
|
32
|
+
|
|
33
|
+
const rippleInstance = createRipple(config.rippleConfig);
|
|
34
|
+
|
|
35
|
+
// If component has lifecycle methods, integrate ripple with them
|
|
36
|
+
if (component.lifecycle) {
|
|
37
|
+
const originalMount = component.lifecycle.mount;
|
|
38
|
+
const originalDestroy = component.lifecycle.destroy;
|
|
39
|
+
|
|
40
|
+
component.lifecycle.mount = () => {
|
|
41
|
+
originalMount.call(component.lifecycle);
|
|
42
|
+
rippleInstance.mount(component.element);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
component.lifecycle.destroy = () => {
|
|
46
|
+
rippleInstance.unmount(component.element);
|
|
47
|
+
originalDestroy.call(component.lifecycle);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
...component,
|
|
53
|
+
ripple: rippleInstance
|
|
54
|
+
};
|
|
55
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/core/compose/features/size.ts
|
|
2
|
+
|
|
3
|
+
import { BaseComponent, ElementComponent } from '../component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for size feature
|
|
7
|
+
*/
|
|
8
|
+
export interface SizeConfig {
|
|
9
|
+
size?: string;
|
|
10
|
+
prefix?: string;
|
|
11
|
+
componentName?: string;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Adds a size class to a component
|
|
17
|
+
*
|
|
18
|
+
* @param config - Configuration object containing size information
|
|
19
|
+
* @returns Function that enhances a component with the size class
|
|
20
|
+
*/
|
|
21
|
+
export const withSize = <T extends SizeConfig>(config: T) =>
|
|
22
|
+
<C extends ElementComponent>(component: C): C => {
|
|
23
|
+
if (config.size && component.element) {
|
|
24
|
+
// Use config.componentName since we know it's there
|
|
25
|
+
const className = `${config.prefix}-${config.componentName}--${config.size}`;
|
|
26
|
+
component.element.classList.add(className);
|
|
27
|
+
}
|
|
28
|
+
return component;
|
|
29
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/core/compose/features/style.ts
|
|
2
|
+
|
|
3
|
+
import { ElementComponent } from '../component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for style feature
|
|
7
|
+
*/
|
|
8
|
+
export interface StyleConfig {
|
|
9
|
+
variant?: string;
|
|
10
|
+
size?: string;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Adds style classes to a component based on configuration
|
|
16
|
+
*
|
|
17
|
+
* @param config - Configuration object containing style information
|
|
18
|
+
* @returns Function that enhances a component with style classes
|
|
19
|
+
*/
|
|
20
|
+
export const withStyle = <T extends StyleConfig>(config: T = {} as T) =>
|
|
21
|
+
<C extends ElementComponent>(component: C): C => {
|
|
22
|
+
if (config.variant) {
|
|
23
|
+
component.element.classList.add(`${component.getClass('button')}--${config.variant}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (config.size) {
|
|
27
|
+
component.element.classList.add(`${component.getClass('button')}--${config.size}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return component;
|
|
31
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/core/compose/features/text.ts
|
|
2
|
+
|
|
3
|
+
import { createText, TextManager } from '../../build/text';
|
|
4
|
+
import { BaseComponent, ElementComponent } from '../component';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for text feature
|
|
8
|
+
*/
|
|
9
|
+
export interface TextConfig {
|
|
10
|
+
text?: string;
|
|
11
|
+
prefix?: string;
|
|
12
|
+
componentName?: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Component with text capabilities
|
|
18
|
+
*/
|
|
19
|
+
export interface TextComponent extends BaseComponent {
|
|
20
|
+
text: TextManager;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Adds text management to a component
|
|
25
|
+
*
|
|
26
|
+
* @param config - Configuration object containing text information
|
|
27
|
+
* @returns Function that enhances a component with text capabilities
|
|
28
|
+
*/
|
|
29
|
+
export const withText = <T extends TextConfig>(config: T) =>
|
|
30
|
+
<C extends ElementComponent>(component: C): C & TextComponent => {
|
|
31
|
+
const text = createText(component.element, {
|
|
32
|
+
prefix: config.prefix,
|
|
33
|
+
type: config.componentName || 'component'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (config.text) {
|
|
37
|
+
text.setText(config.text);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
...component,
|
|
42
|
+
text
|
|
43
|
+
};
|
|
44
|
+
};
|