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