mtrl 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -22
- package/index.ts +33 -0
- package/package.json +14 -5
- package/src/components/button/{styles.scss → _styles.scss} +2 -2
- package/src/components/button/api.ts +89 -0
- package/src/components/button/button.ts +50 -0
- package/src/components/button/config.ts +75 -0
- package/src/components/button/constants.ts +17 -0
- package/src/components/button/index.ts +4 -0
- package/src/components/button/types.ts +118 -0
- package/src/components/card/{styles.scss → _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/index.ts +4 -0
- package/src/components/checkbox/types.ts +146 -0
- package/src/components/chip/_styles.scss +372 -0
- package/src/components/chip/api.ts +115 -0
- package/src/components/chip/chip-set.ts +225 -0
- package/src/components/chip/chip.ts +82 -0
- package/src/components/chip/config.ts +92 -0
- package/src/components/chip/constants.ts +38 -0
- package/src/components/chip/index.ts +4 -0
- package/src/components/chip/types.ts +172 -0
- package/src/components/list/api.ts +72 -0
- package/src/components/list/config.ts +43 -0
- package/src/components/list/{constants.js → constants.ts} +34 -7
- package/src/components/list/features.ts +224 -0
- package/src/components/list/index.ts +14 -0
- package/src/components/list/list-item.ts +120 -0
- package/src/components/list/list.ts +37 -0
- package/src/components/list/types.ts +179 -0
- package/src/components/list/utils.ts +47 -0
- package/src/components/menu/api.ts +119 -0
- package/src/components/menu/config.ts +54 -0
- package/src/components/menu/constants.ts +154 -0
- package/src/components/menu/features/items-manager.ts +457 -0
- package/src/components/menu/features/keyboard-navigation.ts +133 -0
- package/src/components/menu/features/positioning.ts +127 -0
- package/src/components/menu/features/{visibility.js → visibility.ts} +66 -64
- package/src/components/menu/index.ts +14 -0
- package/src/components/menu/menu-item.ts +43 -0
- package/src/components/menu/menu.ts +53 -0
- package/src/components/menu/types.ts +178 -0
- package/src/components/navigation/api.ts +79 -0
- package/src/components/navigation/config.ts +61 -0
- package/src/components/navigation/{constants.js → constants.ts} +10 -10
- package/src/components/navigation/index.ts +14 -0
- package/src/components/navigation/nav-item.ts +148 -0
- package/src/components/navigation/navigation.ts +50 -0
- package/src/components/navigation/types.ts +212 -0
- package/src/components/progress/_styles.scss +204 -0
- package/src/components/progress/api.ts +179 -0
- package/src/components/progress/config.ts +124 -0
- package/src/components/progress/constants.ts +43 -0
- package/src/components/progress/index.ts +5 -0
- package/src/components/progress/progress.ts +163 -0
- package/src/components/progress/types.ts +102 -0
- package/src/components/snackbar/api.ts +162 -0
- package/src/components/snackbar/config.ts +62 -0
- package/src/components/snackbar/{constants.js → constants.ts} +21 -4
- package/src/components/snackbar/features.ts +76 -0
- package/src/components/snackbar/index.ts +4 -0
- package/src/components/snackbar/position.ts +71 -0
- package/src/components/snackbar/queue.ts +76 -0
- package/src/components/snackbar/snackbar.ts +60 -0
- package/src/components/snackbar/types.ts +58 -0
- package/src/components/switch/api.ts +77 -0
- package/src/components/switch/config.ts +74 -0
- package/src/components/switch/index.ts +4 -0
- package/src/components/switch/switch.ts +52 -0
- package/src/components/switch/types.ts +142 -0
- package/src/components/textfield/api.ts +72 -0
- package/src/components/textfield/config.ts +54 -0
- package/src/components/textfield/{constants.js → constants.ts} +38 -5
- package/src/components/textfield/index.ts +4 -0
- package/src/components/textfield/textfield.ts +50 -0
- package/src/components/textfield/types.ts +139 -0
- package/src/core/compose/base.ts +43 -0
- package/src/core/compose/component.ts +247 -0
- package/src/core/compose/features/checkable.ts +155 -0
- package/src/core/compose/features/disabled.ts +116 -0
- package/src/core/compose/features/events.ts +65 -0
- package/src/core/compose/features/icon.ts +67 -0
- package/src/core/compose/features/index.ts +35 -0
- package/src/core/compose/features/input.ts +174 -0
- package/src/core/compose/features/lifecycle.ts +139 -0
- package/src/core/compose/features/position.ts +94 -0
- package/src/core/compose/features/ripple.ts +55 -0
- package/src/core/compose/features/size.ts +29 -0
- package/src/core/compose/features/style.ts +31 -0
- package/src/core/compose/features/text.ts +44 -0
- package/src/core/compose/features/textinput.ts +225 -0
- package/src/core/compose/features/textlabel.ts +92 -0
- package/src/core/compose/features/track.ts +84 -0
- package/src/core/compose/features/variant.ts +29 -0
- package/src/core/compose/features/withEvents.ts +137 -0
- package/src/core/compose/index.ts +54 -0
- package/src/core/compose/{pipe.js → pipe.ts} +16 -11
- package/src/core/config/component-config.ts +136 -0
- package/src/core/config.ts +211 -0
- package/src/core/dom/{attributes.js → attributes.ts} +11 -11
- package/src/core/dom/classes.ts +60 -0
- package/src/core/dom/create.ts +188 -0
- package/src/core/dom/events.ts +209 -0
- package/src/core/dom/index.ts +10 -0
- package/src/core/dom/utils.ts +97 -0
- package/src/core/index.ts +111 -0
- package/src/core/state/disabled.ts +81 -0
- package/src/core/state/emitter.ts +94 -0
- package/src/core/state/events.ts +88 -0
- package/src/core/state/index.ts +16 -0
- package/src/core/state/lifecycle.ts +131 -0
- package/src/core/state/store.ts +197 -0
- package/src/core/utils/index.ts +45 -0
- package/src/core/utils/{mobile.js → mobile.ts} +48 -24
- package/src/core/utils/object.ts +41 -0
- package/src/core/utils/validate.ts +234 -0
- package/src/{index.js → index.ts} +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/index.js +0 -2
- package/src/components/container/api.js +0 -42
- package/src/components/container/container.js +0 -45
- package/src/components/container/index.js +0 -2
- package/src/components/container/styles.scss +0 -66
- package/src/components/list/index.js +0 -2
- package/src/components/list/list-item.js +0 -147
- package/src/components/list/list.js +0 -267
- package/src/components/menu/api.js +0 -117
- package/src/components/menu/constants.js +0 -42
- package/src/components/menu/features/items-manager.js +0 -375
- package/src/components/menu/features/keyboard-navigation.js +0 -129
- package/src/components/menu/features/positioning.js +0 -125
- package/src/components/menu/index.js +0 -2
- package/src/components/menu/menu-item.js +0 -41
- package/src/components/menu/menu.js +0 -54
- package/src/components/navigation/api.js +0 -43
- package/src/components/navigation/index.js +0 -2
- package/src/components/navigation/nav-item.js +0 -137
- package/src/components/navigation/navigation.js +0 -55
- package/src/components/snackbar/api.js +0 -125
- package/src/components/snackbar/features.js +0 -69
- package/src/components/snackbar/index.js +0 -2
- package/src/components/snackbar/position.js +0 -63
- package/src/components/snackbar/queue.js +0 -74
- package/src/components/snackbar/snackbar.js +0 -70
- package/src/components/switch/api.js +0 -44
- package/src/components/switch/index.js +0 -2
- package/src/components/switch/switch.js +0 -71
- package/src/components/textfield/api.js +0 -49
- package/src/components/textfield/index.js +0 -2
- package/src/components/textfield/textfield.js +0 -68
- package/src/core/build/_ripple.scss +0 -79
- package/src/core/build/constants.js +0 -51
- package/src/core/build/icon.js +0 -78
- package/src/core/build/ripple.js +0 -159
- package/src/core/build/text.js +0 -54
- package/src/core/compose/base.js +0 -8
- package/src/core/compose/component.js +0 -225
- package/src/core/compose/features/checkable.js +0 -114
- package/src/core/compose/features/disabled.js +0 -64
- package/src/core/compose/features/events.js +0 -48
- package/src/core/compose/features/icon.js +0 -33
- package/src/core/compose/features/index.js +0 -20
- package/src/core/compose/features/input.js +0 -100
- package/src/core/compose/features/lifecycle.js +0 -69
- package/src/core/compose/features/position.js +0 -60
- package/src/core/compose/features/ripple.js +0 -32
- package/src/core/compose/features/size.js +0 -9
- package/src/core/compose/features/style.js +0 -12
- package/src/core/compose/features/text.js +0 -17
- package/src/core/compose/features/textinput.js +0 -114
- package/src/core/compose/features/textlabel.js +0 -28
- package/src/core/compose/features/track.js +0 -49
- package/src/core/compose/features/variant.js +0 -9
- package/src/core/compose/features/withEvents.js +0 -67
- package/src/core/compose/index.js +0 -16
- package/src/core/config.js +0 -140
- package/src/core/dom/classes.js +0 -70
- package/src/core/dom/create.js +0 -132
- package/src/core/dom/events.js +0 -175
- package/src/core/dom/index.js +0 -5
- package/src/core/dom/utils.js +0 -22
- package/src/core/index.js +0 -23
- package/src/core/state/disabled.js +0 -51
- package/src/core/state/emitter.js +0 -63
- package/src/core/state/events.js +0 -29
- package/src/core/state/index.js +0 -6
- package/src/core/state/lifecycle.js +0 -64
- package/src/core/state/store.js +0 -112
- package/src/core/utils/index.js +0 -39
- package/src/core/utils/object.js +0 -22
- package/src/core/utils/validate.js +0 -37
- /package/src/components/checkbox/{styles.scss → _styles.scss} +0 -0
- /package/src/components/checkbox/{constants.js → constants.ts} +0 -0
- /package/src/components/list/{styles.scss → _styles.scss} +0 -0
- /package/src/components/menu/{styles.scss → _styles.scss} +0 -0
- /package/src/components/navigation/{styles.scss → _styles.scss} +0 -0
- /package/src/components/snackbar/{styles.scss → _styles.scss} +0 -0
- /package/src/components/switch/{styles.scss → _styles.scss} +0 -0
- /package/src/components/switch/{constants.js → constants.ts} +0 -0
- /package/src/components/textfield/{styles.scss → _styles.scss} +0 -0
|
@@ -1,24 +1,35 @@
|
|
|
1
|
-
// src/components/card/features.
|
|
2
|
-
import { PREFIX } from '../../core/config'
|
|
3
|
-
import { createElement } from '../../core/dom/create'
|
|
1
|
+
// src/components/card/features.ts
|
|
2
|
+
import { PREFIX } from '../../core/config';
|
|
3
|
+
import { createElement } from '../../core/dom/create';
|
|
4
|
+
import { BaseComponent, CardComponent, LoadingFeature, ExpandableFeature, SwipeableFeature } from './types';
|
|
5
|
+
|
|
6
|
+
interface LoadingConfig {
|
|
7
|
+
initialState?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ExpandableConfig {
|
|
11
|
+
initialExpanded?: boolean;
|
|
12
|
+
expandableContent?: HTMLElement;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface SwipeableConfig {
|
|
16
|
+
onSwipeLeft?: (component: CardComponent) => void;
|
|
17
|
+
onSwipeRight?: (component: CardComponent) => void;
|
|
18
|
+
threshold?: number;
|
|
19
|
+
}
|
|
4
20
|
|
|
5
21
|
/**
|
|
6
22
|
* Higher-order function to add loading state to a card
|
|
7
|
-
* @param {
|
|
8
|
-
* @param {boolean} [config.initialState=false] - Initial loading state
|
|
23
|
+
* @param {LoadingConfig} config - Loading state configuration
|
|
9
24
|
* @returns {Function} Card component enhancer
|
|
10
25
|
*/
|
|
11
|
-
export const withLoading = (config = {}) => (component) => {
|
|
12
|
-
const initialState = config.initialState || false
|
|
13
|
-
let loadingElement = null
|
|
14
|
-
let isLoading = initialState
|
|
26
|
+
export const withLoading = (config: LoadingConfig = {}) => (component: BaseComponent): BaseComponent & { loading: LoadingFeature } => {
|
|
27
|
+
const initialState = config.initialState || false;
|
|
28
|
+
let loadingElement: HTMLElement | null = null;
|
|
29
|
+
let isLoading = initialState;
|
|
15
30
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function setLoading (loading) {
|
|
21
|
-
isLoading = loading
|
|
31
|
+
function setLoading(loading: boolean): void {
|
|
32
|
+
isLoading = loading;
|
|
22
33
|
|
|
23
34
|
if (loading && !loadingElement) {
|
|
24
35
|
// Create and add loading overlay
|
|
@@ -26,45 +37,47 @@ export const withLoading = (config = {}) => (component) => {
|
|
|
26
37
|
tag: 'div',
|
|
27
38
|
className: `${PREFIX}-card-loading-overlay`,
|
|
28
39
|
container: component.element
|
|
29
|
-
})
|
|
40
|
+
});
|
|
30
41
|
|
|
31
42
|
// Add spinner
|
|
32
43
|
createElement({
|
|
33
44
|
tag: 'div',
|
|
34
45
|
className: `${PREFIX}-card-loading-spinner`,
|
|
35
46
|
container: loadingElement
|
|
36
|
-
})
|
|
47
|
+
});
|
|
37
48
|
|
|
38
|
-
component.element.classList.add(`${PREFIX}-card--state-loading`)
|
|
49
|
+
component.element.classList.add(`${PREFIX}-card--state-loading`);
|
|
39
50
|
} else if (!loading && loadingElement) {
|
|
40
51
|
// Remove loading overlay
|
|
41
|
-
loadingElement.remove()
|
|
42
|
-
loadingElement = null
|
|
43
|
-
component.element.classList.remove(`${PREFIX}-card--state-loading`)
|
|
52
|
+
loadingElement.remove();
|
|
53
|
+
loadingElement = null;
|
|
54
|
+
component.element.classList.remove(`${PREFIX}-card--state-loading`);
|
|
44
55
|
}
|
|
45
56
|
}
|
|
46
57
|
|
|
58
|
+
if (initialState) {
|
|
59
|
+
setLoading(true);
|
|
60
|
+
}
|
|
61
|
+
|
|
47
62
|
return {
|
|
48
63
|
...component,
|
|
49
64
|
loading: {
|
|
50
65
|
isLoading: () => isLoading,
|
|
51
66
|
setLoading
|
|
52
67
|
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
68
|
+
};
|
|
69
|
+
};
|
|
55
70
|
|
|
56
71
|
/**
|
|
57
72
|
* Higher-order function to add expandable behavior to a card
|
|
58
|
-
* @param {
|
|
59
|
-
* @param {boolean} [config.initialExpanded=false] - Whether card is initially expanded
|
|
60
|
-
* @param {HTMLElement} [config.expandableContent] - Content to show when expanded
|
|
73
|
+
* @param {ExpandableConfig} config - Expandable configuration
|
|
61
74
|
* @returns {Function} Card component enhancer
|
|
62
75
|
*/
|
|
63
|
-
export const withExpandable = (config = {}) => (component) => {
|
|
64
|
-
const initialExpanded = config.initialExpanded || false
|
|
65
|
-
let isExpanded = initialExpanded
|
|
66
|
-
const expandableContent = config.expandableContent
|
|
67
|
-
let expandButton
|
|
76
|
+
export const withExpandable = (config: ExpandableConfig = {}) => (component: BaseComponent): BaseComponent & { expandable: ExpandableFeature } => {
|
|
77
|
+
const initialExpanded = config.initialExpanded || false;
|
|
78
|
+
let isExpanded = initialExpanded;
|
|
79
|
+
const expandableContent = config.expandableContent;
|
|
80
|
+
let expandButton: HTMLButtonElement;
|
|
68
81
|
|
|
69
82
|
// Create expand/collapse button
|
|
70
83
|
expandButton = createElement({
|
|
@@ -74,61 +87,61 @@ export const withExpandable = (config = {}) => (component) => {
|
|
|
74
87
|
'aria-expanded': isExpanded ? 'true' : 'false',
|
|
75
88
|
'aria-label': isExpanded ? 'Collapse' : 'Expand'
|
|
76
89
|
}
|
|
77
|
-
})
|
|
90
|
+
}) as HTMLButtonElement;
|
|
78
91
|
|
|
79
92
|
// Add to card as action if not already present
|
|
80
|
-
const actionsContainer = component.element.querySelector(`.${PREFIX}-card-actions`)
|
|
93
|
+
const actionsContainer = component.element.querySelector(`.${PREFIX}-card-actions`);
|
|
81
94
|
if (actionsContainer) {
|
|
82
|
-
actionsContainer.appendChild(expandButton)
|
|
95
|
+
actionsContainer.appendChild(expandButton);
|
|
83
96
|
} else {
|
|
84
97
|
// Create actions container if not present
|
|
85
98
|
const newActionsContainer = createElement({
|
|
86
99
|
tag: 'div',
|
|
87
100
|
className: `${PREFIX}-card-actions`,
|
|
88
101
|
container: component.element
|
|
89
|
-
})
|
|
90
|
-
newActionsContainer.appendChild(expandButton)
|
|
102
|
+
});
|
|
103
|
+
newActionsContainer.appendChild(expandButton);
|
|
91
104
|
}
|
|
92
105
|
|
|
93
106
|
// Set initial state
|
|
94
107
|
if (expandableContent) {
|
|
95
|
-
expandableContent.classList.add(`${PREFIX}-card-expandable-content`)
|
|
108
|
+
expandableContent.classList.add(`${PREFIX}-card-expandable-content`);
|
|
96
109
|
if (!initialExpanded) {
|
|
97
|
-
expandableContent.style.display = 'none'
|
|
110
|
+
expandableContent.style.display = 'none';
|
|
98
111
|
}
|
|
99
|
-
component.element.appendChild(expandableContent)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Toggle expanded state
|
|
103
|
-
function toggleExpanded () {
|
|
104
|
-
setExpanded(!isExpanded)
|
|
112
|
+
component.element.appendChild(expandableContent);
|
|
105
113
|
}
|
|
106
114
|
|
|
107
115
|
// Set expanded state
|
|
108
|
-
function setExpanded
|
|
109
|
-
isExpanded = expanded
|
|
116
|
+
function setExpanded(expanded: boolean): void {
|
|
117
|
+
isExpanded = expanded;
|
|
110
118
|
|
|
111
119
|
if (expandableContent) {
|
|
112
|
-
expandableContent.style.display = expanded ? 'block' : 'none'
|
|
120
|
+
expandableContent.style.display = expanded ? 'block' : 'none';
|
|
113
121
|
}
|
|
114
122
|
|
|
115
|
-
expandButton.setAttribute('aria-expanded', expanded ? 'true' : 'false')
|
|
116
|
-
expandButton.setAttribute('aria-label', expanded ? 'Collapse' : 'Expand')
|
|
123
|
+
expandButton.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
|
124
|
+
expandButton.setAttribute('aria-label', expanded ? 'Collapse' : 'Expand');
|
|
117
125
|
|
|
118
126
|
if (expanded) {
|
|
119
|
-
component.element.classList.add(`${PREFIX}-card--expanded`)
|
|
127
|
+
component.element.classList.add(`${PREFIX}-card--expanded`);
|
|
120
128
|
} else {
|
|
121
|
-
component.element.classList.remove(`${PREFIX}-card--expanded`)
|
|
129
|
+
component.element.classList.remove(`${PREFIX}-card--expanded`);
|
|
122
130
|
}
|
|
123
131
|
|
|
124
|
-
component.emit('expandedChanged', { expanded })
|
|
132
|
+
component.emit?.('expandedChanged', { expanded });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Toggle expanded state
|
|
136
|
+
function toggleExpanded(): void {
|
|
137
|
+
setExpanded(!isExpanded);
|
|
125
138
|
}
|
|
126
139
|
|
|
127
140
|
// Add click handler to toggle button
|
|
128
141
|
expandButton.addEventListener('click', (e) => {
|
|
129
|
-
e.stopPropagation()
|
|
130
|
-
toggleExpanded()
|
|
131
|
-
})
|
|
142
|
+
e.stopPropagation();
|
|
143
|
+
toggleExpanded();
|
|
144
|
+
});
|
|
132
145
|
|
|
133
146
|
return {
|
|
134
147
|
...component,
|
|
@@ -137,82 +150,79 @@ export const withExpandable = (config = {}) => (component) => {
|
|
|
137
150
|
setExpanded,
|
|
138
151
|
toggleExpanded
|
|
139
152
|
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
153
|
+
};
|
|
154
|
+
};
|
|
142
155
|
|
|
143
156
|
/**
|
|
144
157
|
* Higher-order function to add swipeable behavior to a card
|
|
145
|
-
* @param {
|
|
146
|
-
* @param {Function} [config.onSwipeLeft] - Callback when card is swiped left
|
|
147
|
-
* @param {Function} [config.onSwipeRight] - Callback when card is swiped right
|
|
148
|
-
* @param {number} [config.threshold=100] - Swipe distance threshold to trigger action
|
|
158
|
+
* @param {SwipeableConfig} config - Swipeable configuration
|
|
149
159
|
* @returns {Function} Card component enhancer
|
|
150
160
|
*/
|
|
151
|
-
export const withSwipeable = (config = {}) => (component) => {
|
|
152
|
-
const threshold = config.threshold || 100
|
|
153
|
-
let startX = 0
|
|
154
|
-
let currentX = 0
|
|
155
|
-
|
|
156
|
-
function handleTouchStart
|
|
157
|
-
startX = e.touches[0].clientX
|
|
158
|
-
component.element.style.transition = 'none'
|
|
161
|
+
export const withSwipeable = (config: SwipeableConfig = {}) => (component: BaseComponent): BaseComponent & { swipeable: SwipeableFeature } => {
|
|
162
|
+
const threshold = config.threshold || 100;
|
|
163
|
+
let startX = 0;
|
|
164
|
+
let currentX = 0;
|
|
165
|
+
|
|
166
|
+
function handleTouchStart(e: TouchEvent): void {
|
|
167
|
+
startX = e.touches[0].clientX;
|
|
168
|
+
component.element.style.transition = 'none';
|
|
159
169
|
}
|
|
160
170
|
|
|
161
|
-
function handleTouchMove
|
|
162
|
-
if (!startX) return
|
|
171
|
+
function handleTouchMove(e: TouchEvent): void {
|
|
172
|
+
if (!startX) return;
|
|
163
173
|
|
|
164
|
-
currentX = e.touches[0].clientX
|
|
165
|
-
const diffX = currentX - startX
|
|
174
|
+
currentX = e.touches[0].clientX;
|
|
175
|
+
const diffX = currentX - startX;
|
|
166
176
|
|
|
167
177
|
// Apply transform to move card
|
|
168
|
-
component.element.style.transform = `translateX(${diffX}px)
|
|
178
|
+
component.element.style.transform = `translateX(${diffX}px)`;
|
|
169
179
|
}
|
|
170
180
|
|
|
171
|
-
function handleTouchEnd
|
|
172
|
-
if (!startX) return
|
|
181
|
+
function handleTouchEnd(): void {
|
|
182
|
+
if (!startX) return;
|
|
173
183
|
|
|
174
|
-
component.element.style.transition = 'transform 0.3s ease'
|
|
175
|
-
const diffX = currentX - startX
|
|
184
|
+
component.element.style.transition = 'transform 0.3s ease';
|
|
185
|
+
const diffX = currentX - startX;
|
|
176
186
|
|
|
177
187
|
if (Math.abs(diffX) >= threshold) {
|
|
178
188
|
// Swipe threshold reached
|
|
179
189
|
if (diffX > 0 && config.onSwipeRight) {
|
|
180
190
|
// Swipe right
|
|
181
|
-
component.element.style.transform = 'translateX(100%)'
|
|
182
|
-
config.onSwipeRight(component)
|
|
191
|
+
component.element.style.transform = 'translateX(100%)';
|
|
192
|
+
config.onSwipeRight(component as CardComponent);
|
|
183
193
|
} else if (diffX < 0 && config.onSwipeLeft) {
|
|
184
194
|
// Swipe left
|
|
185
|
-
component.element.style.transform = 'translateX(-100%)'
|
|
186
|
-
config.onSwipeLeft(component)
|
|
195
|
+
component.element.style.transform = 'translateX(-100%)';
|
|
196
|
+
config.onSwipeLeft(component as CardComponent);
|
|
187
197
|
} else {
|
|
188
198
|
// Reset if no handler
|
|
189
|
-
component.element.style.transform = 'translateX(0)'
|
|
199
|
+
component.element.style.transform = 'translateX(0)';
|
|
190
200
|
}
|
|
191
201
|
} else {
|
|
192
202
|
// Reset if below threshold
|
|
193
|
-
component.element.style.transform = 'translateX(0)'
|
|
203
|
+
component.element.style.transform = 'translateX(0)';
|
|
194
204
|
}
|
|
195
205
|
|
|
196
|
-
startX = 0
|
|
197
|
-
currentX = 0
|
|
206
|
+
startX = 0;
|
|
207
|
+
currentX = 0;
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
// Add event listeners
|
|
201
|
-
component.element.addEventListener('touchstart', handleTouchStart)
|
|
202
|
-
component.element.addEventListener('touchmove', handleTouchMove)
|
|
203
|
-
component.element.addEventListener('touchend', handleTouchEnd)
|
|
211
|
+
component.element.addEventListener('touchstart', handleTouchStart as EventListener);
|
|
212
|
+
component.element.addEventListener('touchmove', handleTouchMove as EventListener);
|
|
213
|
+
component.element.addEventListener('touchend', handleTouchEnd);
|
|
204
214
|
|
|
205
215
|
// Add swipeable class
|
|
206
|
-
component.element.classList.add(`${PREFIX}-card--swipeable`)
|
|
216
|
+
component.element.classList.add(`${PREFIX}-card--swipeable`);
|
|
207
217
|
|
|
208
218
|
// Return enhanced component
|
|
209
219
|
return {
|
|
210
220
|
...component,
|
|
211
221
|
swipeable: {
|
|
212
222
|
reset: () => {
|
|
213
|
-
component.element.style.transition = 'transform 0.3s ease'
|
|
214
|
-
component.element.style.transform = 'translateX(0)'
|
|
223
|
+
component.element.style.transition = 'transform 0.3s ease';
|
|
224
|
+
component.element.style.transform = 'translateX(0)';
|
|
215
225
|
}
|
|
216
226
|
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
227
|
+
};
|
|
228
|
+
};
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
// src/components/card/header.
|
|
2
|
-
import { PREFIX } from '../../core/config'
|
|
3
|
-
import { pipe } from '../../core/compose'
|
|
4
|
-
import { createBase, withElement } from '../../core/compose/component'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
// src/components/card/header.ts
|
|
2
|
+
import { PREFIX } from '../../core/config';
|
|
3
|
+
import { pipe } from '../../core/compose';
|
|
4
|
+
import { createBase, withElement } from '../../core/compose/component';
|
|
5
|
+
import { createElement } from '../../core/dom/create';
|
|
6
|
+
import { CardHeaderConfig } from './types';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates a card header component
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {string} [config.title] - Title text
|
|
12
|
-
* @param {string} [config.subtitle] - Subtitle text
|
|
13
|
-
* @param {HTMLElement|string} [config.avatar] - Avatar element or HTML string
|
|
14
|
-
* @param {HTMLElement|string} [config.action] - Action element or HTML string
|
|
10
|
+
* @param {CardHeaderConfig} config - Header configuration
|
|
15
11
|
* @returns {HTMLElement} Card header element
|
|
16
12
|
*/
|
|
17
|
-
export const createCardHeader = (config = {}) => {
|
|
13
|
+
export const createCardHeader = (config: CardHeaderConfig = {}): HTMLElement => {
|
|
18
14
|
const baseConfig = {
|
|
19
15
|
...config,
|
|
20
16
|
componentName: 'card-header',
|
|
21
17
|
prefix: PREFIX
|
|
22
|
-
}
|
|
18
|
+
};
|
|
23
19
|
|
|
24
20
|
try {
|
|
25
21
|
const header = pipe(
|
|
@@ -29,14 +25,14 @@ export const createCardHeader = (config = {}) => {
|
|
|
29
25
|
componentName: 'card-header',
|
|
30
26
|
className: config.class
|
|
31
27
|
})
|
|
32
|
-
)(baseConfig)
|
|
28
|
+
)(baseConfig);
|
|
33
29
|
|
|
34
30
|
// Create text container
|
|
35
31
|
const textContainer = createElement({
|
|
36
32
|
tag: 'div',
|
|
37
33
|
className: `${PREFIX}-card-header-text`,
|
|
38
34
|
container: header.element
|
|
39
|
-
})
|
|
35
|
+
});
|
|
40
36
|
|
|
41
37
|
// Add title if provided
|
|
42
38
|
if (config.title) {
|
|
@@ -45,7 +41,7 @@ export const createCardHeader = (config = {}) => {
|
|
|
45
41
|
className: `${PREFIX}-card-header-title`,
|
|
46
42
|
text: config.title,
|
|
47
43
|
container: textContainer
|
|
48
|
-
})
|
|
44
|
+
});
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
// Add subtitle if provided
|
|
@@ -55,7 +51,7 @@ export const createCardHeader = (config = {}) => {
|
|
|
55
51
|
className: `${PREFIX}-card-header-subtitle`,
|
|
56
52
|
text: config.subtitle,
|
|
57
53
|
container: textContainer
|
|
58
|
-
})
|
|
54
|
+
});
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
// Add avatar if provided
|
|
@@ -66,9 +62,9 @@ export const createCardHeader = (config = {}) => {
|
|
|
66
62
|
className: `${PREFIX}-card-header-avatar`,
|
|
67
63
|
html: config.avatar
|
|
68
64
|
})
|
|
69
|
-
: config.avatar
|
|
65
|
+
: config.avatar;
|
|
70
66
|
|
|
71
|
-
header.element.insertBefore(avatarElement, header.element.firstChild)
|
|
67
|
+
header.element.insertBefore(avatarElement, header.element.firstChild);
|
|
72
68
|
}
|
|
73
69
|
|
|
74
70
|
// Add action if provided
|
|
@@ -79,14 +75,14 @@ export const createCardHeader = (config = {}) => {
|
|
|
79
75
|
className: `${PREFIX}-card-header-action`,
|
|
80
76
|
html: config.action
|
|
81
77
|
})
|
|
82
|
-
: config.action
|
|
78
|
+
: config.action;
|
|
83
79
|
|
|
84
|
-
header.element.appendChild(actionElement)
|
|
80
|
+
header.element.appendChild(actionElement);
|
|
85
81
|
}
|
|
86
82
|
|
|
87
|
-
return header.element
|
|
83
|
+
return header.element;
|
|
88
84
|
} catch (error) {
|
|
89
|
-
console.error('Card header creation error:', error)
|
|
90
|
-
throw new Error(`Failed to create card header: ${error.message}`)
|
|
85
|
+
console.error('Card header creation error:', error instanceof Error ? error.message : String(error));
|
|
86
|
+
throw new Error(`Failed to create card header: ${error instanceof Error ? error.message : String(error)}`);
|
|
91
87
|
}
|
|
92
|
-
}
|
|
88
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/components/card/index.ts
|
|
2
|
+
export { default } from './card'
|
|
3
|
+
export { createCardContent } from './content'
|
|
4
|
+
export { createCardHeader } from './header'
|
|
5
|
+
export { createCardActions } from './actions'
|
|
6
|
+
export { createCardMedia } from './media'
|
|
7
|
+
export {
|
|
8
|
+
CardVariant,
|
|
9
|
+
CardElevation,
|
|
10
|
+
CardSchema,
|
|
11
|
+
CardHeaderConfig,
|
|
12
|
+
CardContentConfig,
|
|
13
|
+
CardActionsConfig,
|
|
14
|
+
CardMediaConfig,
|
|
15
|
+
CardComponent
|
|
16
|
+
} from './types'
|
|
17
|
+
|
|
18
|
+
// Export constants for backward compatibility
|
|
19
|
+
export { CARD_VARIANTS, CARD_ELEVATIONS, CARD_SCHEMA } from './constants'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/components/card/media.ts
|
|
2
|
+
import { PREFIX } from '../../core/config';
|
|
3
|
+
import { pipe } from '../../core/compose';
|
|
4
|
+
import { createBase, withElement } from '../../core/compose/component';
|
|
5
|
+
import { CardMediaConfig } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a card media component
|
|
9
|
+
* @param {CardMediaConfig} config - Media configuration
|
|
10
|
+
* @returns {HTMLElement} Card media element
|
|
11
|
+
*/
|
|
12
|
+
export const createCardMedia = (config: CardMediaConfig = {}): HTMLElement => {
|
|
13
|
+
const baseConfig = {
|
|
14
|
+
...config,
|
|
15
|
+
componentName: 'card-media',
|
|
16
|
+
prefix: PREFIX
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const media = pipe(
|
|
21
|
+
createBase,
|
|
22
|
+
withElement({
|
|
23
|
+
tag: 'div',
|
|
24
|
+
componentName: 'card-media',
|
|
25
|
+
className: [
|
|
26
|
+
config.class,
|
|
27
|
+
config.aspectRatio ? `${PREFIX}-card-media--${config.aspectRatio.replace(':', '-')}` : null,
|
|
28
|
+
config.contain ? `${PREFIX}-card-media--contain` : null
|
|
29
|
+
]
|
|
30
|
+
})
|
|
31
|
+
)(baseConfig);
|
|
32
|
+
|
|
33
|
+
// If custom element is provided, use it
|
|
34
|
+
if (config.element instanceof HTMLElement) {
|
|
35
|
+
media.element.appendChild(config.element);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Otherwise create an image if src is provided
|
|
39
|
+
else if (config.src) {
|
|
40
|
+
const img = document.createElement('img');
|
|
41
|
+
img.src = config.src;
|
|
42
|
+
if (config.alt) img.alt = config.alt;
|
|
43
|
+
img.className = `${PREFIX}-card-media-img`;
|
|
44
|
+
media.element.appendChild(img);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return media.element;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Card media creation error:', error instanceof Error ? error.message : String(error));
|
|
50
|
+
throw new Error(`Failed to create card media: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// src/components/card/types.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Card variant types following Material Design 3
|
|
5
|
+
*/
|
|
6
|
+
export enum CardVariant {
|
|
7
|
+
ELEVATED = 'elevated',
|
|
8
|
+
FILLED = 'filled',
|
|
9
|
+
OUTLINED = 'outlined'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Card elevation levels
|
|
14
|
+
*/
|
|
15
|
+
export enum CardElevation {
|
|
16
|
+
RESTING = 1,
|
|
17
|
+
HOVERED = 2,
|
|
18
|
+
DRAGGED = 4
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Interface for card configuration
|
|
23
|
+
*/
|
|
24
|
+
export interface CardSchema {
|
|
25
|
+
variant?: CardVariant;
|
|
26
|
+
interactive?: boolean;
|
|
27
|
+
fullWidth?: boolean;
|
|
28
|
+
clickable?: boolean;
|
|
29
|
+
draggable?: boolean;
|
|
30
|
+
class?: string;
|
|
31
|
+
headerConfig?: CardHeaderConfig;
|
|
32
|
+
contentConfig?: CardContentConfig;
|
|
33
|
+
actionsConfig?: CardActionsConfig;
|
|
34
|
+
mediaConfig?: CardMediaConfig;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Interface for card header configuration
|
|
39
|
+
*/
|
|
40
|
+
export interface CardHeaderConfig {
|
|
41
|
+
title?: string;
|
|
42
|
+
subtitle?: string;
|
|
43
|
+
avatar?: HTMLElement | string;
|
|
44
|
+
action?: HTMLElement | string;
|
|
45
|
+
class?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Interface for card content configuration
|
|
50
|
+
*/
|
|
51
|
+
export interface CardContentConfig {
|
|
52
|
+
text?: string;
|
|
53
|
+
html?: string;
|
|
54
|
+
children?: HTMLElement[];
|
|
55
|
+
padding?: boolean;
|
|
56
|
+
class?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Interface for card actions configuration
|
|
61
|
+
*/
|
|
62
|
+
export interface CardActionsConfig {
|
|
63
|
+
actions?: HTMLElement[];
|
|
64
|
+
fullBleed?: boolean;
|
|
65
|
+
vertical?: boolean;
|
|
66
|
+
align?: 'start' | 'center' | 'end' | 'space-between';
|
|
67
|
+
class?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Interface for card media configuration
|
|
72
|
+
*/
|
|
73
|
+
export interface CardMediaConfig {
|
|
74
|
+
src?: string;
|
|
75
|
+
alt?: string;
|
|
76
|
+
element?: HTMLElement;
|
|
77
|
+
aspectRatio?: '16:9' | '4:3' | '1:1' | string;
|
|
78
|
+
contain?: boolean;
|
|
79
|
+
class?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Base component interface
|
|
84
|
+
*/
|
|
85
|
+
export interface BaseComponent {
|
|
86
|
+
element: HTMLElement;
|
|
87
|
+
getClass: (name?: string) => string;
|
|
88
|
+
getModifierClass: (base: string, modifier: string) => string;
|
|
89
|
+
getElementClass: (base: string, element: string) => string;
|
|
90
|
+
addClass: (...classes: string[]) => BaseComponent;
|
|
91
|
+
emit?: (event: string, data?: any) => void;
|
|
92
|
+
config: CardComponentConfig;
|
|
93
|
+
touchState?: TouchState;
|
|
94
|
+
updateTouchState?: (event: TouchEvent | MouseEvent, status: 'start' | 'end') => void;
|
|
95
|
+
lifecycle?: ComponentLifecycle;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Touch state interface
|
|
100
|
+
*/
|
|
101
|
+
export interface TouchState {
|
|
102
|
+
startTime: number;
|
|
103
|
+
startPosition: { x: number; y: number };
|
|
104
|
+
isTouching: boolean;
|
|
105
|
+
activeTarget: EventTarget | null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Component lifecycle interface
|
|
110
|
+
*/
|
|
111
|
+
export interface ComponentLifecycle {
|
|
112
|
+
mount?: () => void;
|
|
113
|
+
update?: () => void;
|
|
114
|
+
destroy: () => void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Card component configuration
|
|
119
|
+
*/
|
|
120
|
+
export interface CardComponentConfig extends CardSchema {
|
|
121
|
+
componentName: string;
|
|
122
|
+
prefix: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Card component interface
|
|
127
|
+
*/
|
|
128
|
+
export interface CardComponent extends BaseComponent {
|
|
129
|
+
// Card-specific methods
|
|
130
|
+
addContent: (contentElement: HTMLElement) => CardComponent;
|
|
131
|
+
setHeader: (headerElement: HTMLElement) => CardComponent;
|
|
132
|
+
addMedia: (mediaElement: HTMLElement, position?: 'top' | 'bottom') => CardComponent;
|
|
133
|
+
setActions: (actionsElement: HTMLElement) => CardComponent;
|
|
134
|
+
makeDraggable: (dragStartCallback?: (event: DragEvent) => void) => CardComponent;
|
|
135
|
+
destroy: () => void;
|
|
136
|
+
|
|
137
|
+
// Optional feature interfaces
|
|
138
|
+
loading?: LoadingFeature;
|
|
139
|
+
expandable?: ExpandableFeature;
|
|
140
|
+
swipeable?: SwipeableFeature;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Loading feature interface
|
|
145
|
+
*/
|
|
146
|
+
export interface LoadingFeature {
|
|
147
|
+
isLoading: () => boolean;
|
|
148
|
+
setLoading: (loading: boolean) => void;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Expandable feature interface
|
|
153
|
+
*/
|
|
154
|
+
export interface ExpandableFeature {
|
|
155
|
+
isExpanded: () => boolean;
|
|
156
|
+
setExpanded: (expanded: boolean) => void;
|
|
157
|
+
toggleExpanded: () => void;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Swipeable feature interface
|
|
162
|
+
*/
|
|
163
|
+
export interface SwipeableFeature {
|
|
164
|
+
reset: () => void;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* API options interface
|
|
169
|
+
*/
|
|
170
|
+
export interface ApiOptions {
|
|
171
|
+
lifecycle: {
|
|
172
|
+
destroy: () => void;
|
|
173
|
+
};
|
|
174
|
+
}
|