mtrl 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +18 -0
- package/package.json +1 -1
- package/src/components/badge/_styles.scss +123 -115
- package/src/components/badge/api.ts +57 -59
- package/src/components/badge/badge.ts +16 -2
- package/src/components/badge/config.ts +65 -11
- package/src/components/badge/constants.ts +22 -12
- package/src/components/badge/features.ts +44 -40
- package/src/components/badge/types.ts +42 -30
- package/src/components/bottom-app-bar/_styles.scss +103 -0
- package/src/components/bottom-app-bar/bottom-app-bar.ts +196 -0
- package/src/components/bottom-app-bar/config.ts +73 -0
- package/src/components/bottom-app-bar/index.ts +11 -0
- package/src/components/bottom-app-bar/types.ts +108 -0
- package/src/components/button/_styles.scss +0 -66
- package/src/components/button/api.ts +5 -0
- package/src/components/button/button.ts +0 -2
- package/src/components/button/config.ts +5 -0
- package/src/components/button/constants.ts +0 -6
- package/src/components/button/index.ts +2 -2
- package/src/components/button/types.ts +7 -7
- package/src/components/card/_styles.scss +67 -25
- package/src/components/card/api.ts +54 -3
- package/src/components/card/card.ts +25 -6
- package/src/components/card/config.ts +189 -22
- package/src/components/card/constants.ts +20 -19
- package/src/components/card/content.ts +299 -2
- package/src/components/card/features.ts +158 -4
- package/src/components/card/index.ts +31 -9
- package/src/components/card/types.ts +166 -15
- package/src/components/checkbox/_styles.scss +0 -2
- package/src/components/chip/chip.ts +1 -9
- package/src/components/chip/constants.ts +0 -10
- package/src/components/chip/index.ts +1 -1
- package/src/components/chip/types.ts +1 -4
- package/src/components/datepicker/_styles.scss +358 -0
- package/src/components/datepicker/api.ts +272 -0
- package/src/components/datepicker/config.ts +144 -0
- package/src/components/datepicker/constants.ts +98 -0
- package/src/components/datepicker/datepicker.ts +346 -0
- package/src/components/datepicker/index.ts +9 -0
- package/src/components/datepicker/render.ts +452 -0
- package/src/components/datepicker/types.ts +268 -0
- package/src/components/datepicker/utils.ts +290 -0
- package/src/components/dialog/_styles.scss +174 -128
- package/src/components/dialog/api.ts +48 -13
- package/src/components/dialog/config.ts +9 -5
- package/src/components/dialog/dialog.ts +6 -3
- package/src/components/dialog/features.ts +290 -130
- package/src/components/dialog/types.ts +7 -4
- package/src/components/divider/_styles.scss +57 -0
- package/src/components/divider/config.ts +81 -0
- package/src/components/divider/divider.ts +37 -0
- package/src/components/divider/features.ts +207 -0
- package/src/components/divider/index.ts +5 -0
- package/src/components/divider/types.ts +55 -0
- package/src/components/extended-fab/_styles.scss +267 -0
- package/src/components/extended-fab/api.ts +141 -0
- package/src/components/extended-fab/config.ts +108 -0
- package/src/components/extended-fab/constants.ts +36 -0
- package/src/components/extended-fab/extended-fab.ts +125 -0
- package/src/components/extended-fab/index.ts +4 -0
- package/src/components/extended-fab/types.ts +287 -0
- package/src/components/fab/_styles.scss +225 -0
- package/src/components/fab/api.ts +97 -0
- package/src/components/fab/config.ts +94 -0
- package/src/components/fab/constants.ts +41 -0
- package/src/components/fab/fab.ts +67 -0
- package/src/components/fab/index.ts +4 -0
- package/src/components/fab/types.ts +234 -0
- package/src/components/navigation/_styles.scss +1 -0
- package/src/components/navigation/api.ts +78 -50
- package/src/components/navigation/features/items.ts +280 -0
- package/src/components/navigation/nav-item.ts +72 -23
- package/src/components/navigation/navigation.ts +54 -2
- package/src/components/navigation/types.ts +210 -188
- package/src/components/progress/_styles.scss +0 -65
- package/src/components/progress/config.ts +1 -2
- package/src/components/progress/constants.ts +0 -14
- package/src/components/progress/index.ts +1 -1
- package/src/components/progress/progress.ts +1 -4
- package/src/components/progress/types.ts +1 -4
- package/src/components/radios/_styles.scss +0 -45
- package/src/components/radios/api.ts +85 -60
- package/src/components/radios/config.ts +1 -2
- package/src/components/radios/constants.ts +0 -9
- package/src/components/radios/index.ts +1 -1
- package/src/components/radios/radio.ts +34 -11
- package/src/components/radios/radios.ts +2 -1
- package/src/components/radios/types.ts +1 -7
- package/src/components/search/_styles.scss +306 -0
- package/src/components/search/api.ts +203 -0
- package/src/components/search/config.ts +87 -0
- package/src/components/search/constants.ts +21 -0
- package/src/components/search/features/index.ts +4 -0
- package/src/components/search/features/search.ts +718 -0
- package/src/components/search/features/states.ts +165 -0
- package/src/components/search/features/structure.ts +198 -0
- package/src/components/search/index.ts +10 -0
- package/src/components/search/search.ts +52 -0
- package/src/components/search/types.ts +163 -0
- package/src/components/segmented-button/_styles.scss +117 -0
- package/src/components/segmented-button/config.ts +67 -0
- package/src/components/segmented-button/constants.ts +42 -0
- package/src/components/segmented-button/index.ts +4 -0
- package/src/components/segmented-button/segment.ts +155 -0
- package/src/components/segmented-button/segmented-button.ts +250 -0
- package/src/components/segmented-button/types.ts +219 -0
- package/src/components/slider/_styles.scss +221 -168
- package/src/components/slider/accessibility.md +59 -0
- package/src/components/slider/api.ts +41 -120
- package/src/components/slider/config.ts +51 -49
- package/src/components/slider/features/handlers.ts +495 -0
- package/src/components/slider/features/index.ts +1 -2
- package/src/components/slider/features/slider.ts +66 -84
- package/src/components/slider/features/states.ts +195 -0
- package/src/components/slider/features/structure.ts +141 -184
- package/src/components/slider/features/ui.ts +150 -201
- package/src/components/slider/index.ts +2 -11
- package/src/components/slider/slider.ts +9 -12
- package/src/components/slider/types.ts +39 -24
- package/src/components/switch/_styles.scss +0 -2
- package/src/components/tabs/_styles.scss +346 -154
- package/src/components/tabs/api.ts +178 -400
- package/src/components/tabs/config.ts +46 -52
- package/src/components/tabs/constants.ts +85 -8
- package/src/components/tabs/features.ts +403 -0
- package/src/components/tabs/index.ts +60 -3
- package/src/components/tabs/indicator.ts +285 -0
- package/src/components/tabs/responsive.ts +144 -0
- package/src/components/tabs/scroll-indicators.ts +149 -0
- package/src/components/tabs/state.ts +186 -0
- package/src/components/tabs/tab-api.ts +258 -0
- package/src/components/tabs/tab.ts +255 -0
- package/src/components/tabs/tabs.ts +50 -31
- package/src/components/tabs/types.ts +332 -128
- package/src/components/tabs/utils.ts +107 -0
- package/src/components/textfield/_styles.scss +0 -98
- package/src/components/textfield/config.ts +2 -3
- package/src/components/textfield/constants.ts +0 -14
- package/src/components/textfield/index.ts +2 -2
- package/src/components/textfield/textfield.ts +0 -2
- package/src/components/textfield/types.ts +1 -4
- package/src/components/timepicker/README.md +277 -0
- package/src/components/timepicker/_styles.scss +451 -0
- package/src/components/timepicker/api.ts +632 -0
- package/src/components/timepicker/clockdial.ts +482 -0
- package/src/components/timepicker/config.ts +130 -0
- package/src/components/timepicker/constants.ts +138 -0
- package/src/components/timepicker/index.ts +8 -0
- package/src/components/timepicker/render.ts +613 -0
- package/src/components/timepicker/timepicker.ts +117 -0
- package/src/components/timepicker/types.ts +336 -0
- package/src/components/timepicker/utils.ts +241 -0
- package/src/components/top-app-bar/_styles.scss +225 -0
- package/src/components/top-app-bar/config.ts +83 -0
- package/src/components/top-app-bar/index.ts +11 -0
- package/src/components/top-app-bar/top-app-bar.ts +316 -0
- package/src/components/top-app-bar/types.ts +140 -0
- package/src/core/build/_ripple.scss +6 -6
- package/src/core/build/ripple.ts +72 -95
- package/src/core/compose/component.ts +1 -1
- package/src/core/compose/features/badge.ts +79 -0
- package/src/core/compose/features/icon.ts +3 -1
- package/src/core/compose/features/index.ts +3 -1
- package/src/core/compose/features/ripple.ts +4 -1
- package/src/core/compose/features/textlabel.ts +26 -2
- package/src/core/dom/create.ts +5 -0
- package/src/index.ts +9 -0
- package/src/styles/abstract/_theme.scss +115 -3
- package/src/styles/themes/_autumn.scss +21 -0
- package/src/styles/themes/_base-theme.scss +61 -0
- package/src/styles/themes/_baseline.scss +58 -0
- package/src/styles/themes/_bluekhaki.scss +125 -0
- package/src/styles/themes/_brownbeige.scss +125 -0
- package/src/styles/themes/_browngreen.scss +125 -0
- package/src/styles/themes/_forest.scss +6 -0
- package/src/styles/themes/_greenbeige.scss +125 -0
- package/src/styles/themes/_material.scss +125 -0
- package/src/styles/themes/_ocean.scss +6 -0
- package/src/styles/themes/_sageivory.scss +125 -0
- package/src/styles/themes/_spring.scss +6 -0
- package/src/styles/themes/_summer.scss +5 -0
- package/src/styles/themes/_sunset.scss +5 -0
- package/src/styles/themes/_tealcaramel.scss +125 -0
- package/src/styles/themes/_winter.scss +6 -0
- package/src/components/card/actions.ts +0 -48
- package/src/components/card/header.ts +0 -88
- package/src/components/card/media.ts +0 -52
- package/src/components/navigation/features/items.js +0 -192
- package/src/components/slider/features/appearance.ts +0 -94
- package/src/components/slider/features/disabled.ts +0 -43
- package/src/components/slider/features/events.ts +0 -164
- package/src/components/slider/features/interactions.ts +0 -261
- package/src/components/slider/features/keyboard.ts +0 -112
- package/src/core/collection/adapters/mongodb.js +0 -232
|
@@ -3,6 +3,9 @@ import { PREFIX } from '../../core/config';
|
|
|
3
3
|
import { createElement } from '../../core/dom/create';
|
|
4
4
|
import { BaseComponent, CardComponent, LoadingFeature, ExpandableFeature, SwipeableFeature } from './types';
|
|
5
5
|
|
|
6
|
+
import { createCardHeader, createCardContent, createCardMedia, createCardActions } from './content';
|
|
7
|
+
import { BaseComponent, CardComponent, CardHeaderConfig, CardContentConfig, CardMediaConfig, CardActionsConfig, CardSchema, ButtonConfig } from './types';
|
|
8
|
+
|
|
6
9
|
interface LoadingConfig {
|
|
7
10
|
initialState?: boolean;
|
|
8
11
|
}
|
|
@@ -20,8 +23,21 @@ interface SwipeableConfig {
|
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* Higher-order function to add loading state to a card
|
|
26
|
+
*
|
|
23
27
|
* @param {LoadingConfig} config - Loading state configuration
|
|
24
28
|
* @returns {Function} Card component enhancer
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Create a card with loading state
|
|
33
|
+
* const card = pipe(
|
|
34
|
+
* createCard,
|
|
35
|
+
* withLoading({ initialState: true })
|
|
36
|
+
* )();
|
|
37
|
+
*
|
|
38
|
+
* // Toggle loading state
|
|
39
|
+
* setTimeout(() => card.loading.setLoading(false), 2000);
|
|
40
|
+
* ```
|
|
25
41
|
*/
|
|
26
42
|
export const withLoading = (config: LoadingConfig = {}) => (component: BaseComponent): BaseComponent & { loading: LoadingFeature } => {
|
|
27
43
|
const initialState = config.initialState || false;
|
|
@@ -36,7 +52,12 @@ export const withLoading = (config: LoadingConfig = {}) => (component: BaseCompo
|
|
|
36
52
|
loadingElement = createElement({
|
|
37
53
|
tag: 'div',
|
|
38
54
|
className: `${PREFIX}-card-loading-overlay`,
|
|
39
|
-
container: component.element
|
|
55
|
+
container: component.element,
|
|
56
|
+
attrs: {
|
|
57
|
+
'role': 'progressbar',
|
|
58
|
+
'aria-busy': 'true',
|
|
59
|
+
'aria-label': 'Loading'
|
|
60
|
+
}
|
|
40
61
|
});
|
|
41
62
|
|
|
42
63
|
// Add spinner
|
|
@@ -47,11 +68,13 @@ export const withLoading = (config: LoadingConfig = {}) => (component: BaseCompo
|
|
|
47
68
|
});
|
|
48
69
|
|
|
49
70
|
component.element.classList.add(`${PREFIX}-card--state-loading`);
|
|
71
|
+
component.element.setAttribute('aria-busy', 'true');
|
|
50
72
|
} else if (!loading && loadingElement) {
|
|
51
73
|
// Remove loading overlay
|
|
52
74
|
loadingElement.remove();
|
|
53
75
|
loadingElement = null;
|
|
54
76
|
component.element.classList.remove(`${PREFIX}-card--state-loading`);
|
|
77
|
+
component.element.setAttribute('aria-busy', 'false');
|
|
55
78
|
}
|
|
56
79
|
}
|
|
57
80
|
|
|
@@ -68,10 +91,61 @@ export const withLoading = (config: LoadingConfig = {}) => (component: BaseCompo
|
|
|
68
91
|
};
|
|
69
92
|
};
|
|
70
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Higher-order function to add elevation to a card based on its variant
|
|
96
|
+
*
|
|
97
|
+
* Sets the initial elevation CSS variable according to Material Design 3 guidelines:
|
|
98
|
+
* - Elevated variant: 1dp elevation
|
|
99
|
+
* - Filled and outlined variants: 0dp elevation
|
|
100
|
+
*
|
|
101
|
+
* @param {BaseComponent} component - Card component
|
|
102
|
+
* @returns {BaseComponent} Card component with elevation applied
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // Apply elevation in the composition chain
|
|
106
|
+
* const card = pipe(
|
|
107
|
+
* createBase,
|
|
108
|
+
* withElement(config),
|
|
109
|
+
* withElevation
|
|
110
|
+
* )(baseConfig);
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export const withElevation = (component: BaseComponent): BaseComponent => {
|
|
114
|
+
const config = component.config;
|
|
115
|
+
|
|
116
|
+
// Set initial elevation based on variant
|
|
117
|
+
if (config.variant === 'elevated') {
|
|
118
|
+
component.element.style.setProperty('--card-elevation', '1');
|
|
119
|
+
} else {
|
|
120
|
+
component.element.style.setProperty('--card-elevation', '0');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return component;
|
|
124
|
+
};
|
|
125
|
+
|
|
71
126
|
/**
|
|
72
127
|
* Higher-order function to add expandable behavior to a card
|
|
128
|
+
*
|
|
73
129
|
* @param {ExpandableConfig} config - Expandable configuration
|
|
74
130
|
* @returns {Function} Card component enhancer
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* // Create a card with expandable content
|
|
135
|
+
* const expandableContent = document.createElement('div');
|
|
136
|
+
* expandableContent.textContent = 'Additional content that can be expanded';
|
|
137
|
+
*
|
|
138
|
+
* const card = pipe(
|
|
139
|
+
* createCard,
|
|
140
|
+
* withExpandable({
|
|
141
|
+
* initialExpanded: false,
|
|
142
|
+
* expandableContent
|
|
143
|
+
* })
|
|
144
|
+
* )();
|
|
145
|
+
*
|
|
146
|
+
* // Later toggle the expanded state
|
|
147
|
+
* card.expandable.toggleExpanded();
|
|
148
|
+
* ```
|
|
75
149
|
*/
|
|
76
150
|
export const withExpandable = (config: ExpandableConfig = {}) => (component: BaseComponent): BaseComponent & { expandable: ExpandableFeature } => {
|
|
77
151
|
const initialExpanded = config.initialExpanded || false;
|
|
@@ -85,7 +159,8 @@ export const withExpandable = (config: ExpandableConfig = {}) => (component: Bas
|
|
|
85
159
|
className: `${PREFIX}-card-expand-button`,
|
|
86
160
|
attrs: {
|
|
87
161
|
'aria-expanded': isExpanded ? 'true' : 'false',
|
|
88
|
-
'aria-label': isExpanded ? 'Collapse' : 'Expand'
|
|
162
|
+
'aria-label': isExpanded ? 'Collapse content' : 'Expand content',
|
|
163
|
+
'aria-controls': expandableContent?.id || `${component.element.id || 'card'}-expandable-content`
|
|
89
164
|
}
|
|
90
165
|
}) as HTMLButtonElement;
|
|
91
166
|
|
|
@@ -98,7 +173,10 @@ export const withExpandable = (config: ExpandableConfig = {}) => (component: Bas
|
|
|
98
173
|
const newActionsContainer = createElement({
|
|
99
174
|
tag: 'div',
|
|
100
175
|
className: `${PREFIX}-card-actions`,
|
|
101
|
-
container: component.element
|
|
176
|
+
container: component.element,
|
|
177
|
+
attrs: {
|
|
178
|
+
'role': 'group'
|
|
179
|
+
}
|
|
102
180
|
});
|
|
103
181
|
newActionsContainer.appendChild(expandButton);
|
|
104
182
|
}
|
|
@@ -106,9 +184,19 @@ export const withExpandable = (config: ExpandableConfig = {}) => (component: Bas
|
|
|
106
184
|
// Set initial state
|
|
107
185
|
if (expandableContent) {
|
|
108
186
|
expandableContent.classList.add(`${PREFIX}-card-expandable-content`);
|
|
187
|
+
|
|
188
|
+
// Ensure the expandable content has an ID for ARIA controls
|
|
189
|
+
if (!expandableContent.id) {
|
|
190
|
+
expandableContent.id = `${component.element.id || 'card'}-expandable-content`;
|
|
191
|
+
}
|
|
192
|
+
|
|
109
193
|
if (!initialExpanded) {
|
|
110
194
|
expandableContent.style.display = 'none';
|
|
195
|
+
expandableContent.setAttribute('aria-hidden', 'true');
|
|
196
|
+
} else {
|
|
197
|
+
expandableContent.setAttribute('aria-hidden', 'false');
|
|
111
198
|
}
|
|
199
|
+
|
|
112
200
|
component.element.appendChild(expandableContent);
|
|
113
201
|
}
|
|
114
202
|
|
|
@@ -118,10 +206,11 @@ export const withExpandable = (config: ExpandableConfig = {}) => (component: Bas
|
|
|
118
206
|
|
|
119
207
|
if (expandableContent) {
|
|
120
208
|
expandableContent.style.display = expanded ? 'block' : 'none';
|
|
209
|
+
expandableContent.setAttribute('aria-hidden', expanded ? 'false' : 'true');
|
|
121
210
|
}
|
|
122
211
|
|
|
123
212
|
expandButton.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
|
124
|
-
expandButton.setAttribute('aria-label', expanded ? 'Collapse' : 'Expand');
|
|
213
|
+
expandButton.setAttribute('aria-label', expanded ? 'Collapse content' : 'Expand content');
|
|
125
214
|
|
|
126
215
|
if (expanded) {
|
|
127
216
|
component.element.classList.add(`${PREFIX}-card--expanded`);
|
|
@@ -143,6 +232,14 @@ export const withExpandable = (config: ExpandableConfig = {}) => (component: Bas
|
|
|
143
232
|
toggleExpanded();
|
|
144
233
|
});
|
|
145
234
|
|
|
235
|
+
// Add keyboard handler for accessibility
|
|
236
|
+
expandButton.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
237
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
toggleExpanded();
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
146
243
|
return {
|
|
147
244
|
...component,
|
|
148
245
|
expandable: {
|
|
@@ -155,13 +252,70 @@ export const withExpandable = (config: ExpandableConfig = {}) => (component: Bas
|
|
|
155
252
|
|
|
156
253
|
/**
|
|
157
254
|
* Higher-order function to add swipeable behavior to a card
|
|
255
|
+
*
|
|
158
256
|
* @param {SwipeableConfig} config - Swipeable configuration
|
|
159
257
|
* @returns {Function} Card component enhancer
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* // Create a swipeable card
|
|
262
|
+
* const card = pipe(
|
|
263
|
+
* createCard,
|
|
264
|
+
* withSwipeable({
|
|
265
|
+
* threshold: 100,
|
|
266
|
+
* onSwipeLeft: (card) => console.log('Swiped left'),
|
|
267
|
+
* onSwipeRight: (card) => console.log('Swiped right')
|
|
268
|
+
* })
|
|
269
|
+
* )({ variant: CardVariant.ELEVATED });
|
|
270
|
+
* ```
|
|
160
271
|
*/
|
|
161
272
|
export const withSwipeable = (config: SwipeableConfig = {}) => (component: BaseComponent): BaseComponent & { swipeable: SwipeableFeature } => {
|
|
162
273
|
const threshold = config.threshold || 100;
|
|
163
274
|
let startX = 0;
|
|
164
275
|
let currentX = 0;
|
|
276
|
+
|
|
277
|
+
// Add accessibility information for swipeable cards
|
|
278
|
+
component.element.setAttribute('aria-description', 'Swipeable card. Swipe left or right to perform actions.');
|
|
279
|
+
|
|
280
|
+
// Create hidden buttons for keyboard accessibility
|
|
281
|
+
const leftActionButton = createElement({
|
|
282
|
+
tag: 'button',
|
|
283
|
+
className: `${PREFIX}-card-swipe-left-action`,
|
|
284
|
+
text: 'Swipe Left Action',
|
|
285
|
+
container: component.element,
|
|
286
|
+
attrs: {
|
|
287
|
+
'aria-label': 'Perform swipe left action',
|
|
288
|
+
'style': 'position: absolute; left: -9999px; top: -9999px; visibility: hidden;' // Visually hidden but accessible
|
|
289
|
+
}
|
|
290
|
+
}) as HTMLButtonElement;
|
|
291
|
+
|
|
292
|
+
const rightActionButton = createElement({
|
|
293
|
+
tag: 'button',
|
|
294
|
+
className: `${PREFIX}-card-swipe-right-action`,
|
|
295
|
+
text: 'Swipe Right Action',
|
|
296
|
+
container: component.element,
|
|
297
|
+
attrs: {
|
|
298
|
+
'aria-label': 'Perform swipe right action',
|
|
299
|
+
'style': 'position: absolute; left: -9999px; top: -9999px; visibility: hidden;' // Visually hidden but accessible
|
|
300
|
+
}
|
|
301
|
+
}) as HTMLButtonElement;
|
|
302
|
+
|
|
303
|
+
// Add keyboard handlers to the hidden buttons
|
|
304
|
+
leftActionButton.addEventListener('click', () => {
|
|
305
|
+
if (config.onSwipeLeft) {
|
|
306
|
+
component.element.style.transform = 'translateX(-100%)';
|
|
307
|
+
component.element.style.transition = 'transform 0.3s ease';
|
|
308
|
+
config.onSwipeLeft(component as CardComponent);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
rightActionButton.addEventListener('click', () => {
|
|
313
|
+
if (config.onSwipeRight) {
|
|
314
|
+
component.element.style.transform = 'translateX(100%)';
|
|
315
|
+
component.element.style.transition = 'transform 0.3s ease';
|
|
316
|
+
config.onSwipeRight(component as CardComponent);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
165
319
|
|
|
166
320
|
function handleTouchStart(e: TouchEvent): void {
|
|
167
321
|
startX = e.touches[0].clientX;
|
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
// src/components/card/index.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import defaultCreateCard from './card';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Card Component Module
|
|
6
|
+
*
|
|
7
|
+
* This module exports the Card components and related utilities following
|
|
8
|
+
* Material Design 3 specifications.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Add default export
|
|
12
|
+
export default defaultCreateCard;
|
|
13
|
+
|
|
14
|
+
// Export components from content file where they are now merged
|
|
15
|
+
export {
|
|
16
|
+
createCardContent,
|
|
17
|
+
createCardHeader,
|
|
18
|
+
createCardActions,
|
|
19
|
+
createCardMedia
|
|
20
|
+
} from './content';
|
|
21
|
+
|
|
22
|
+
// Other exports
|
|
23
|
+
export {
|
|
24
|
+
withLoading,
|
|
25
|
+
withExpandable,
|
|
26
|
+
withSwipeable,
|
|
27
|
+
withElevation
|
|
28
|
+
} from './features';
|
|
29
|
+
|
|
7
30
|
export {
|
|
8
31
|
CardVariant,
|
|
9
32
|
CardElevation,
|
|
@@ -12,8 +35,7 @@ export {
|
|
|
12
35
|
CardContentConfig,
|
|
13
36
|
CardActionsConfig,
|
|
14
37
|
CardMediaConfig,
|
|
38
|
+
CardAriaAttributes,
|
|
15
39
|
CardComponent
|
|
16
|
-
} from './types'
|
|
17
|
-
|
|
18
|
-
// Export constants for backward compatibility
|
|
19
|
-
export { CARD_VARIANTS, CARD_ELEVATIONS, CARD_SCHEMA } from './constants'
|
|
40
|
+
} from './types';
|
|
41
|
+
export { CARD_VARIANTS, CARD_ELEVATIONS, CARD_WIDTHS, CARD_CORNER_RADIUS } from './constants';
|
|
@@ -1,174 +1,325 @@
|
|
|
1
1
|
// src/components/card/types.ts
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
|
-
* Card variant types following Material Design 3
|
|
3
|
+
* Card variant types following Material Design 3 specifications
|
|
4
|
+
* @enum {string}
|
|
5
5
|
*/
|
|
6
6
|
export enum CardVariant {
|
|
7
|
+
/** Elevated card with shadow */
|
|
7
8
|
ELEVATED = 'elevated',
|
|
9
|
+
/** Filled card with higher surface container color */
|
|
8
10
|
FILLED = 'filled',
|
|
11
|
+
/** Outlined card with border */
|
|
9
12
|
OUTLINED = 'outlined'
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
|
-
* Card elevation levels
|
|
16
|
+
* Card elevation levels based on MD3 guidelines
|
|
17
|
+
* @enum {number}
|
|
14
18
|
*/
|
|
15
19
|
export enum CardElevation {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
/** No elevation (for filled and outlined variants) */
|
|
21
|
+
LEVEL0 = 0,
|
|
22
|
+
/** Default elevation for elevated cards */
|
|
23
|
+
LEVEL1 = 1,
|
|
24
|
+
/** Elevation for hovered state */
|
|
25
|
+
LEVEL2 = 2,
|
|
26
|
+
/** Elevation for dragged state */
|
|
27
|
+
LEVEL4 = 4
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Button configuration interface for buttons shorthand
|
|
32
|
+
* @interface ButtonConfig
|
|
33
|
+
*/
|
|
34
|
+
export interface ButtonConfig {
|
|
35
|
+
/** Button text */
|
|
36
|
+
text?: string;
|
|
37
|
+
/** Button variant */
|
|
38
|
+
variant?: string;
|
|
39
|
+
/** Button icon */
|
|
40
|
+
icon?: string;
|
|
41
|
+
/** Additional button properties */
|
|
42
|
+
[key: string]: any;
|
|
19
43
|
}
|
|
20
44
|
|
|
21
45
|
/**
|
|
22
|
-
*
|
|
46
|
+
* Card configuration interface
|
|
47
|
+
* @interface CardSchema
|
|
23
48
|
*/
|
|
24
49
|
export interface CardSchema {
|
|
50
|
+
/** Card variant type (elevated, filled, outlined) */
|
|
25
51
|
variant?: CardVariant;
|
|
52
|
+
/** Whether the card is interactive */
|
|
26
53
|
interactive?: boolean;
|
|
54
|
+
/** Whether the card should take full width */
|
|
27
55
|
fullWidth?: boolean;
|
|
56
|
+
/** Whether the card is clickable (with ripple effect) */
|
|
28
57
|
clickable?: boolean;
|
|
58
|
+
/** Whether the card is draggable */
|
|
29
59
|
draggable?: boolean;
|
|
60
|
+
/** Additional CSS class(es) */
|
|
30
61
|
class?: string;
|
|
62
|
+
/** Header configuration */
|
|
31
63
|
headerConfig?: CardHeaderConfig;
|
|
64
|
+
/** Content configuration */
|
|
32
65
|
contentConfig?: CardContentConfig;
|
|
66
|
+
/** Actions configuration */
|
|
33
67
|
actionsConfig?: CardActionsConfig;
|
|
68
|
+
/** Media configuration */
|
|
34
69
|
mediaConfig?: CardMediaConfig;
|
|
70
|
+
/** ARIA attributes for accessibility */
|
|
71
|
+
aria?: CardAriaAttributes;
|
|
72
|
+
|
|
73
|
+
// New inline configuration options
|
|
74
|
+
/** Inline header configuration (alternative to headerConfig) */
|
|
75
|
+
header?: CardHeaderConfig;
|
|
76
|
+
/** Inline content configuration (alternative to contentConfig) */
|
|
77
|
+
content?: CardContentConfig;
|
|
78
|
+
/** Inline media configuration (alternative to mediaConfig) */
|
|
79
|
+
media?: CardMediaConfig;
|
|
80
|
+
/** Inline actions configuration (alternative to actionsConfig) */
|
|
81
|
+
actions?: CardActionsConfig;
|
|
82
|
+
/** Simple buttons array for actions (will be converted to actionsConfig) */
|
|
83
|
+
buttons?: ButtonConfig[];
|
|
84
|
+
|
|
85
|
+
/** Internal component name */
|
|
86
|
+
componentName?: string;
|
|
87
|
+
/** CSS class prefix */
|
|
88
|
+
prefix?: string;
|
|
89
|
+
/** Callback executed after component creation */
|
|
90
|
+
afterCreation?: (component: BaseComponent) => void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* ARIA attributes for card accessibility
|
|
95
|
+
* @interface CardAriaAttributes
|
|
96
|
+
*/
|
|
97
|
+
export interface CardAriaAttributes {
|
|
98
|
+
/** ARIA label */
|
|
99
|
+
label?: string;
|
|
100
|
+
/** ARIA labelledby */
|
|
101
|
+
labelledby?: string;
|
|
102
|
+
/** ARIA describedby */
|
|
103
|
+
describedby?: string;
|
|
104
|
+
/** ARIA role (default is 'region' for non-interactive, 'button' for interactive) */
|
|
105
|
+
role?: string;
|
|
106
|
+
/** Additional ARIA attributes as key-value pairs */
|
|
107
|
+
[key: string]: string | undefined;
|
|
35
108
|
}
|
|
36
109
|
|
|
37
110
|
/**
|
|
38
|
-
*
|
|
111
|
+
* Card header configuration
|
|
112
|
+
* @interface CardHeaderConfig
|
|
39
113
|
*/
|
|
40
114
|
export interface CardHeaderConfig {
|
|
115
|
+
/** Header title text */
|
|
41
116
|
title?: string;
|
|
117
|
+
/** Header subtitle text */
|
|
42
118
|
subtitle?: string;
|
|
119
|
+
/** Avatar element or HTML string */
|
|
43
120
|
avatar?: HTMLElement | string;
|
|
121
|
+
/** Action element or HTML string */
|
|
44
122
|
action?: HTMLElement | string;
|
|
123
|
+
/** Additional CSS class(es) */
|
|
45
124
|
class?: string;
|
|
46
125
|
}
|
|
47
126
|
|
|
48
127
|
/**
|
|
49
|
-
*
|
|
128
|
+
* Card content configuration
|
|
129
|
+
* @interface CardContentConfig
|
|
50
130
|
*/
|
|
51
131
|
export interface CardContentConfig {
|
|
132
|
+
/** Text content */
|
|
52
133
|
text?: string;
|
|
134
|
+
/** HTML content */
|
|
53
135
|
html?: string;
|
|
136
|
+
/** Child elements */
|
|
54
137
|
children?: HTMLElement[];
|
|
138
|
+
/** Whether to add padding (true by default) */
|
|
55
139
|
padding?: boolean;
|
|
140
|
+
/** Additional CSS class(es) */
|
|
56
141
|
class?: string;
|
|
57
142
|
}
|
|
58
143
|
|
|
59
144
|
/**
|
|
60
|
-
*
|
|
145
|
+
* Card actions configuration
|
|
146
|
+
* @interface CardActionsConfig
|
|
61
147
|
*/
|
|
62
148
|
export interface CardActionsConfig {
|
|
149
|
+
/** Action elements */
|
|
63
150
|
actions?: HTMLElement[];
|
|
151
|
+
/** Whether actions should be full-bleed */
|
|
64
152
|
fullBleed?: boolean;
|
|
153
|
+
/** Whether actions should be stacked vertically */
|
|
65
154
|
vertical?: boolean;
|
|
155
|
+
/** Horizontal alignment */
|
|
66
156
|
align?: 'start' | 'center' | 'end' | 'space-between';
|
|
157
|
+
/** Additional CSS class(es) */
|
|
67
158
|
class?: string;
|
|
68
159
|
}
|
|
69
160
|
|
|
70
161
|
/**
|
|
71
|
-
*
|
|
162
|
+
* Card media configuration
|
|
163
|
+
* @interface CardMediaConfig
|
|
72
164
|
*/
|
|
73
165
|
export interface CardMediaConfig {
|
|
166
|
+
/** Image source URL */
|
|
74
167
|
src?: string;
|
|
168
|
+
/** Image alt text (required for accessibility) */
|
|
75
169
|
alt?: string;
|
|
170
|
+
/** Custom element instead of image */
|
|
76
171
|
element?: HTMLElement;
|
|
172
|
+
/** Aspect ratio */
|
|
77
173
|
aspectRatio?: '16:9' | '4:3' | '1:1' | string;
|
|
174
|
+
/** Whether media should use object-fit: contain */
|
|
78
175
|
contain?: boolean;
|
|
176
|
+
/** Additional CSS class(es) */
|
|
79
177
|
class?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Position of the media in the card
|
|
180
|
+
* - 'top': Media appears at the top of the card (default)
|
|
181
|
+
* - 'bottom': Media appears after the content
|
|
182
|
+
*/
|
|
183
|
+
position?: 'top' | 'bottom';
|
|
80
184
|
}
|
|
81
185
|
|
|
82
186
|
/**
|
|
83
187
|
* Base component interface
|
|
188
|
+
* @interface BaseComponent
|
|
84
189
|
*/
|
|
85
190
|
export interface BaseComponent {
|
|
191
|
+
/** The DOM element */
|
|
86
192
|
element: HTMLElement;
|
|
193
|
+
/** Get class name with prefix */
|
|
87
194
|
getClass: (name?: string) => string;
|
|
195
|
+
/** Get modifier class */
|
|
88
196
|
getModifierClass: (base: string, modifier: string) => string;
|
|
197
|
+
/** Get element class */
|
|
89
198
|
getElementClass: (base: string, element: string) => string;
|
|
199
|
+
/** Add CSS class(es) */
|
|
90
200
|
addClass: (...classes: string[]) => BaseComponent;
|
|
201
|
+
/** Emit an event */
|
|
91
202
|
emit?: (event: string, data?: any) => void;
|
|
203
|
+
/** Component configuration */
|
|
92
204
|
config: CardComponentConfig;
|
|
205
|
+
/** Touch state for touch interactions */
|
|
93
206
|
touchState?: TouchState;
|
|
207
|
+
/** Update touch state */
|
|
94
208
|
updateTouchState?: (event: TouchEvent | MouseEvent, status: 'start' | 'end') => void;
|
|
209
|
+
/** Component lifecycle methods */
|
|
95
210
|
lifecycle?: ComponentLifecycle;
|
|
96
211
|
}
|
|
97
212
|
|
|
98
213
|
/**
|
|
99
|
-
* Touch state interface
|
|
214
|
+
* Touch state interface for touch interactions
|
|
215
|
+
* @interface TouchState
|
|
100
216
|
*/
|
|
101
217
|
export interface TouchState {
|
|
218
|
+
/** Timestamp when touch started */
|
|
102
219
|
startTime: number;
|
|
220
|
+
/** Starting position */
|
|
103
221
|
startPosition: { x: number; y: number };
|
|
222
|
+
/** Whether touch is active */
|
|
104
223
|
isTouching: boolean;
|
|
224
|
+
/** Current touch target */
|
|
105
225
|
activeTarget: EventTarget | null;
|
|
106
226
|
}
|
|
107
227
|
|
|
108
228
|
/**
|
|
109
229
|
* Component lifecycle interface
|
|
230
|
+
* @interface ComponentLifecycle
|
|
110
231
|
*/
|
|
111
232
|
export interface ComponentLifecycle {
|
|
233
|
+
/** Called when component is mounted to DOM */
|
|
112
234
|
mount?: () => void;
|
|
235
|
+
/** Called when component is updated */
|
|
113
236
|
update?: () => void;
|
|
237
|
+
/** Called when component is destroyed */
|
|
114
238
|
destroy: () => void;
|
|
115
239
|
}
|
|
116
240
|
|
|
117
241
|
/**
|
|
118
242
|
* Card component configuration
|
|
243
|
+
* @interface CardComponentConfig
|
|
244
|
+
* @extends CardSchema
|
|
119
245
|
*/
|
|
120
246
|
export interface CardComponentConfig extends CardSchema {
|
|
247
|
+
/** Component name */
|
|
121
248
|
componentName: string;
|
|
249
|
+
/** CSS class prefix */
|
|
122
250
|
prefix: string;
|
|
123
251
|
}
|
|
124
252
|
|
|
125
253
|
/**
|
|
126
|
-
* Card component interface
|
|
254
|
+
* Card component interface with methods
|
|
255
|
+
* @interface CardComponent
|
|
256
|
+
* @extends BaseComponent
|
|
127
257
|
*/
|
|
128
258
|
export interface CardComponent extends BaseComponent {
|
|
129
|
-
|
|
259
|
+
/** Add content to the card */
|
|
130
260
|
addContent: (contentElement: HTMLElement) => CardComponent;
|
|
261
|
+
/** Set the card header */
|
|
131
262
|
setHeader: (headerElement: HTMLElement) => CardComponent;
|
|
263
|
+
/** Add media to the card */
|
|
132
264
|
addMedia: (mediaElement: HTMLElement, position?: 'top' | 'bottom') => CardComponent;
|
|
265
|
+
/** Set the card actions */
|
|
133
266
|
setActions: (actionsElement: HTMLElement) => CardComponent;
|
|
267
|
+
/** Make the card draggable */
|
|
134
268
|
makeDraggable: (dragStartCallback?: (event: DragEvent) => void) => CardComponent;
|
|
269
|
+
/** Set focus to the card */
|
|
270
|
+
focus: () => CardComponent;
|
|
271
|
+
/** Destroy the card and clean up resources */
|
|
135
272
|
destroy: () => void;
|
|
136
273
|
|
|
137
|
-
|
|
274
|
+
/** Optional loading feature */
|
|
138
275
|
loading?: LoadingFeature;
|
|
276
|
+
/** Optional expandable feature */
|
|
139
277
|
expandable?: ExpandableFeature;
|
|
278
|
+
/** Optional swipeable feature */
|
|
140
279
|
swipeable?: SwipeableFeature;
|
|
141
280
|
}
|
|
142
281
|
|
|
143
282
|
/**
|
|
144
283
|
* Loading feature interface
|
|
284
|
+
* @interface LoadingFeature
|
|
145
285
|
*/
|
|
146
286
|
export interface LoadingFeature {
|
|
287
|
+
/** Check if loading state is active */
|
|
147
288
|
isLoading: () => boolean;
|
|
289
|
+
/** Set loading state */
|
|
148
290
|
setLoading: (loading: boolean) => void;
|
|
149
291
|
}
|
|
150
292
|
|
|
151
293
|
/**
|
|
152
294
|
* Expandable feature interface
|
|
295
|
+
* @interface ExpandableFeature
|
|
153
296
|
*/
|
|
154
297
|
export interface ExpandableFeature {
|
|
298
|
+
/** Check if expanded state is active */
|
|
155
299
|
isExpanded: () => boolean;
|
|
300
|
+
/** Set expanded state */
|
|
156
301
|
setExpanded: (expanded: boolean) => void;
|
|
302
|
+
/** Toggle expanded state */
|
|
157
303
|
toggleExpanded: () => void;
|
|
158
304
|
}
|
|
159
305
|
|
|
160
306
|
/**
|
|
161
307
|
* Swipeable feature interface
|
|
308
|
+
* @interface SwipeableFeature
|
|
162
309
|
*/
|
|
163
310
|
export interface SwipeableFeature {
|
|
311
|
+
/** Reset swipe position */
|
|
164
312
|
reset: () => void;
|
|
165
313
|
}
|
|
166
314
|
|
|
167
315
|
/**
|
|
168
316
|
* API options interface
|
|
317
|
+
* @interface ApiOptions
|
|
169
318
|
*/
|
|
170
319
|
export interface ApiOptions {
|
|
320
|
+
/** Lifecycle methods */
|
|
171
321
|
lifecycle: {
|
|
322
|
+
/** Destroy callback */
|
|
172
323
|
destroy: () => void;
|
|
173
324
|
};
|
|
174
325
|
}
|
|
@@ -7,19 +7,17 @@ import {
|
|
|
7
7
|
withText,
|
|
8
8
|
withIcon,
|
|
9
9
|
withVariant,
|
|
10
|
-
withSize,
|
|
11
10
|
withRipple,
|
|
12
11
|
withDisabled,
|
|
13
12
|
withLifecycle
|
|
14
13
|
} from '../../core/compose/features'
|
|
15
14
|
import { withAPI } from './api'
|
|
16
|
-
import { CHIP_VARIANTS
|
|
15
|
+
import { CHIP_VARIANTS } from './constants'
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Creates a new Chip component
|
|
20
19
|
* @param {Object} config - Chip configuration
|
|
21
20
|
* @param {string} [config.variant='filled'] - Chip variant
|
|
22
|
-
* @param {string} [config.size='medium'] - Chip size
|
|
23
21
|
* @param {boolean} [config.selected=false] - Whether the chip is initially selected
|
|
24
22
|
* @param {boolean} [config.disabled=false] - Whether the chip is initially disabled
|
|
25
23
|
* @param {string} [config.text] - Chip text content
|
|
@@ -37,7 +35,6 @@ const createChip = (config = {}) => {
|
|
|
37
35
|
const baseConfig = {
|
|
38
36
|
...config,
|
|
39
37
|
variant: config.variant || CHIP_VARIANTS.FILLED,
|
|
40
|
-
size: config.size || CHIP_SIZES.MEDIUM,
|
|
41
38
|
componentName: 'chip',
|
|
42
39
|
prefix: PREFIX,
|
|
43
40
|
ripple: config.ripple !== false
|
|
@@ -76,11 +73,6 @@ const createChip = (config = {}) => {
|
|
|
76
73
|
chip.element.classList.add(`${chip.getClass('chip')}--${config.variant}`)
|
|
77
74
|
}
|
|
78
75
|
|
|
79
|
-
// Manually add the size class
|
|
80
|
-
if (config.size) {
|
|
81
|
-
chip.element.classList.add(`${chip.getClass('chip')}--${config.size}`)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
76
|
// Add ripple if enabled
|
|
85
77
|
if (config.ripple) {
|
|
86
78
|
withRipple(baseConfig)(chip)
|