mtrl 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -22
- package/index.ts +33 -0
- package/package.json +14 -5
- package/src/components/button/{styles.scss → _styles.scss} +2 -2
- package/src/components/button/api.ts +89 -0
- package/src/components/button/button.ts +50 -0
- package/src/components/button/config.ts +75 -0
- package/src/components/button/constants.ts +17 -0
- package/src/components/button/index.ts +4 -0
- package/src/components/button/types.ts +118 -0
- package/src/components/card/{styles.scss → _styles.scss} +79 -7
- package/src/components/card/{actions.js → actions.ts} +15 -18
- package/src/components/card/{api.js → api.ts} +33 -33
- package/src/components/card/card.ts +41 -0
- package/src/components/card/config.ts +99 -0
- package/src/components/card/{constants.js → constants.ts} +11 -10
- package/src/components/card/{content.js → content.ts} +15 -18
- package/src/components/card/{features.js → features.ts} +104 -94
- package/src/components/card/{header.js → header.ts} +21 -25
- package/src/components/card/index.ts +19 -0
- package/src/components/card/media.ts +52 -0
- package/src/components/card/types.ts +174 -0
- package/src/components/checkbox/api.ts +82 -0
- package/src/components/checkbox/checkbox.ts +75 -0
- package/src/components/checkbox/config.ts +90 -0
- package/src/components/checkbox/constants.ts +37 -0
- package/src/components/checkbox/index.ts +4 -0
- package/src/components/checkbox/types.ts +146 -0
- package/src/components/chip/_styles.scss +372 -0
- package/src/components/chip/api.ts +115 -0
- package/src/components/chip/chip-set.ts +225 -0
- package/src/components/chip/chip.ts +82 -0
- package/src/components/chip/config.ts +92 -0
- package/src/components/chip/constants.ts +38 -0
- package/src/components/chip/index.ts +4 -0
- package/src/components/chip/types.ts +172 -0
- package/src/components/list/api.ts +72 -0
- package/src/components/list/config.ts +43 -0
- package/src/components/list/{constants.js → constants.ts} +34 -7
- package/src/components/list/features.ts +224 -0
- package/src/components/list/index.ts +14 -0
- package/src/components/list/list-item.ts +120 -0
- package/src/components/list/list.ts +37 -0
- package/src/components/list/types.ts +179 -0
- package/src/components/list/utils.ts +47 -0
- package/src/components/menu/api.ts +119 -0
- package/src/components/menu/config.ts +54 -0
- package/src/components/menu/constants.ts +154 -0
- package/src/components/menu/features/items-manager.ts +457 -0
- package/src/components/menu/features/keyboard-navigation.ts +133 -0
- package/src/components/menu/features/positioning.ts +127 -0
- package/src/components/menu/features/{visibility.js → visibility.ts} +66 -64
- package/src/components/menu/index.ts +14 -0
- package/src/components/menu/menu-item.ts +43 -0
- package/src/components/menu/menu.ts +53 -0
- package/src/components/menu/types.ts +178 -0
- package/src/components/navigation/api.ts +79 -0
- package/src/components/navigation/config.ts +61 -0
- package/src/components/navigation/{constants.js → constants.ts} +10 -10
- package/src/components/navigation/index.ts +14 -0
- package/src/components/navigation/nav-item.ts +148 -0
- package/src/components/navigation/navigation.ts +50 -0
- package/src/components/navigation/types.ts +212 -0
- package/src/components/progress/_styles.scss +204 -0
- package/src/components/progress/api.ts +179 -0
- package/src/components/progress/config.ts +124 -0
- package/src/components/progress/constants.ts +43 -0
- package/src/components/progress/index.ts +5 -0
- package/src/components/progress/progress.ts +163 -0
- package/src/components/progress/types.ts +102 -0
- package/src/components/snackbar/api.ts +162 -0
- package/src/components/snackbar/config.ts +62 -0
- package/src/components/snackbar/{constants.js → constants.ts} +21 -4
- package/src/components/snackbar/features.ts +76 -0
- package/src/components/snackbar/index.ts +4 -0
- package/src/components/snackbar/position.ts +71 -0
- package/src/components/snackbar/queue.ts +76 -0
- package/src/components/snackbar/snackbar.ts +60 -0
- package/src/components/snackbar/types.ts +58 -0
- package/src/components/switch/api.ts +77 -0
- package/src/components/switch/config.ts +74 -0
- package/src/components/switch/{constants.js → constants.ts} +5 -5
- package/src/components/switch/index.ts +4 -0
- package/src/components/switch/switch.ts +52 -0
- package/src/components/switch/types.ts +142 -0
- package/src/components/textfield/api.ts +72 -0
- package/src/components/textfield/config.ts +54 -0
- package/src/components/textfield/{constants.js → constants.ts} +38 -5
- package/src/components/textfield/index.ts +4 -0
- package/src/components/textfield/textfield.ts +50 -0
- package/src/components/textfield/types.ts +139 -0
- package/src/core/compose/base.ts +43 -0
- package/src/core/compose/component.ts +255 -0
- package/src/core/compose/features/checkable.ts +155 -0
- package/src/core/compose/features/disabled.ts +116 -0
- package/src/core/compose/features/events.ts +65 -0
- package/src/core/compose/features/icon.ts +67 -0
- package/src/core/compose/features/index.ts +35 -0
- package/src/core/compose/features/input.ts +174 -0
- package/src/core/compose/features/lifecycle.ts +139 -0
- package/src/core/compose/features/position.ts +94 -0
- package/src/core/compose/features/ripple.ts +55 -0
- package/src/core/compose/features/size.ts +29 -0
- package/src/core/compose/features/style.ts +31 -0
- package/src/core/compose/features/text.ts +44 -0
- package/src/core/compose/features/textinput.ts +225 -0
- package/src/core/compose/features/textlabel.ts +92 -0
- package/src/core/compose/features/track.ts +84 -0
- package/src/core/compose/features/variant.ts +29 -0
- package/src/core/compose/features/withEvents.ts +137 -0
- package/src/core/compose/index.ts +54 -0
- package/src/core/compose/{pipe.js → pipe.ts} +16 -11
- package/src/core/config/component-config.ts +136 -0
- package/src/core/config.ts +211 -0
- package/src/core/dom/{attributes.js → attributes.ts} +11 -11
- package/src/core/dom/classes.ts +60 -0
- package/src/core/dom/create.ts +251 -0
- package/src/core/dom/events.ts +209 -0
- package/src/core/dom/index.ts +10 -0
- package/src/core/dom/utils.ts +97 -0
- package/src/core/index.ts +111 -0
- package/src/core/state/disabled.ts +81 -0
- package/src/core/state/emitter.ts +94 -0
- package/src/core/state/events.ts +88 -0
- package/src/core/state/index.ts +16 -0
- package/src/core/state/lifecycle.ts +131 -0
- package/src/core/state/store.ts +197 -0
- package/src/core/utils/index.ts +45 -0
- package/src/core/utils/{mobile.js → mobile.ts} +48 -24
- package/src/core/utils/object.ts +41 -0
- package/src/core/utils/validate.ts +234 -0
- package/src/{index.js → index.ts} +3 -2
- package/index.js +0 -11
- package/src/components/button/api.js +0 -54
- package/src/components/button/button.js +0 -81
- package/src/components/button/config.js +0 -10
- package/src/components/button/constants.js +0 -63
- package/src/components/button/index.js +0 -2
- package/src/components/card/card.js +0 -102
- package/src/components/card/config.js +0 -16
- package/src/components/card/index.js +0 -7
- package/src/components/card/media.js +0 -56
- package/src/components/checkbox/api.js +0 -45
- package/src/components/checkbox/checkbox.js +0 -96
- package/src/components/checkbox/constants.js +0 -88
- package/src/components/checkbox/index.js +0 -2
- package/src/components/container/api.js +0 -42
- package/src/components/container/container.js +0 -45
- package/src/components/container/index.js +0 -2
- package/src/components/container/styles.scss +0 -66
- package/src/components/list/index.js +0 -2
- package/src/components/list/list-item.js +0 -147
- package/src/components/list/list.js +0 -267
- package/src/components/menu/api.js +0 -117
- package/src/components/menu/constants.js +0 -42
- package/src/components/menu/features/items-manager.js +0 -375
- package/src/components/menu/features/keyboard-navigation.js +0 -129
- package/src/components/menu/features/positioning.js +0 -125
- package/src/components/menu/index.js +0 -2
- package/src/components/menu/menu-item.js +0 -41
- package/src/components/menu/menu.js +0 -54
- package/src/components/navigation/api.js +0 -43
- package/src/components/navigation/index.js +0 -2
- package/src/components/navigation/nav-item.js +0 -137
- package/src/components/navigation/navigation.js +0 -55
- package/src/components/snackbar/api.js +0 -125
- package/src/components/snackbar/features.js +0 -69
- package/src/components/snackbar/index.js +0 -2
- package/src/components/snackbar/position.js +0 -63
- package/src/components/snackbar/queue.js +0 -74
- package/src/components/snackbar/snackbar.js +0 -70
- package/src/components/switch/api.js +0 -44
- package/src/components/switch/index.js +0 -2
- package/src/components/switch/switch.js +0 -71
- package/src/components/textfield/api.js +0 -49
- package/src/components/textfield/index.js +0 -2
- package/src/components/textfield/textfield.js +0 -68
- package/src/core/build/_ripple.scss +0 -79
- package/src/core/build/constants.js +0 -51
- package/src/core/build/icon.js +0 -78
- package/src/core/build/ripple.js +0 -159
- package/src/core/build/text.js +0 -54
- package/src/core/compose/base.js +0 -8
- package/src/core/compose/component.js +0 -225
- package/src/core/compose/features/checkable.js +0 -114
- package/src/core/compose/features/disabled.js +0 -64
- package/src/core/compose/features/events.js +0 -48
- package/src/core/compose/features/icon.js +0 -33
- package/src/core/compose/features/index.js +0 -20
- package/src/core/compose/features/input.js +0 -100
- package/src/core/compose/features/lifecycle.js +0 -69
- package/src/core/compose/features/position.js +0 -60
- package/src/core/compose/features/ripple.js +0 -32
- package/src/core/compose/features/size.js +0 -9
- package/src/core/compose/features/style.js +0 -12
- package/src/core/compose/features/text.js +0 -17
- package/src/core/compose/features/textinput.js +0 -114
- package/src/core/compose/features/textlabel.js +0 -28
- package/src/core/compose/features/track.js +0 -49
- package/src/core/compose/features/variant.js +0 -9
- package/src/core/compose/features/withEvents.js +0 -67
- package/src/core/compose/index.js +0 -16
- package/src/core/config.js +0 -140
- package/src/core/dom/classes.js +0 -70
- package/src/core/dom/create.js +0 -132
- package/src/core/dom/events.js +0 -175
- package/src/core/dom/index.js +0 -5
- package/src/core/dom/utils.js +0 -22
- package/src/core/index.js +0 -23
- package/src/core/state/disabled.js +0 -51
- package/src/core/state/emitter.js +0 -63
- package/src/core/state/events.js +0 -29
- package/src/core/state/index.js +0 -6
- package/src/core/state/lifecycle.js +0 -64
- package/src/core/state/store.js +0 -112
- package/src/core/utils/index.js +0 -39
- package/src/core/utils/object.js +0 -22
- package/src/core/utils/validate.js +0 -37
- /package/src/components/checkbox/{styles.scss → _styles.scss} +0 -0
- /package/src/components/list/{styles.scss → _styles.scss} +0 -0
- /package/src/components/menu/{styles.scss → _styles.scss} +0 -0
- /package/src/components/navigation/{styles.scss → _styles.scss} +0 -0
- /package/src/components/snackbar/{styles.scss → _styles.scss} +0 -0
- /package/src/components/switch/{styles.scss → _styles.scss} +0 -0
- /package/src/components/textfield/{styles.scss → _styles.scss} +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// src/components/progress/api.ts
|
|
2
|
+
|
|
3
|
+
import { ProgressComponent } from './types';
|
|
4
|
+
import { PROGRESS_EVENTS } from './constants';
|
|
5
|
+
|
|
6
|
+
interface ApiOptions {
|
|
7
|
+
value: {
|
|
8
|
+
getValue: () => number;
|
|
9
|
+
setValue: (value: number) => void;
|
|
10
|
+
getMax: () => number;
|
|
11
|
+
};
|
|
12
|
+
buffer: {
|
|
13
|
+
getBuffer: () => number;
|
|
14
|
+
setBuffer: (value: number) => void;
|
|
15
|
+
};
|
|
16
|
+
disabled: {
|
|
17
|
+
enable: () => void;
|
|
18
|
+
disable: () => void;
|
|
19
|
+
isDisabled: () => boolean;
|
|
20
|
+
};
|
|
21
|
+
label: {
|
|
22
|
+
show: () => void;
|
|
23
|
+
hide: () => void;
|
|
24
|
+
format: (formatter: (value: number, max: number) => string) => void;
|
|
25
|
+
setContent: (content: string) => void;
|
|
26
|
+
};
|
|
27
|
+
state: {
|
|
28
|
+
setIndeterminate: (indeterminate: boolean) => void;
|
|
29
|
+
isIndeterminate: () => boolean;
|
|
30
|
+
};
|
|
31
|
+
lifecycle: {
|
|
32
|
+
destroy: () => void;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ComponentWithElements {
|
|
37
|
+
element: HTMLElement;
|
|
38
|
+
trackElement: HTMLElement;
|
|
39
|
+
indicatorElement: HTMLElement;
|
|
40
|
+
labelElement?: HTMLElement;
|
|
41
|
+
getClass: (name: string) => string;
|
|
42
|
+
emit: (event: string, data?: any) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Enhances a progress component with API methods
|
|
47
|
+
* @param {ApiOptions} options - API configuration options
|
|
48
|
+
* @returns {Function} Higher-order function that adds API methods to component
|
|
49
|
+
* @internal This is an internal utility for the Progress component
|
|
50
|
+
*/
|
|
51
|
+
export const withAPI = (options: ApiOptions) =>
|
|
52
|
+
(component: ComponentWithElements): ProgressComponent => ({
|
|
53
|
+
...component as any,
|
|
54
|
+
|
|
55
|
+
setValue(value: number) {
|
|
56
|
+
options.value.setValue(value);
|
|
57
|
+
|
|
58
|
+
// Update the visual indicator
|
|
59
|
+
if (!options.state.isIndeterminate()) {
|
|
60
|
+
const percent = (value / options.value.getMax()) * 100;
|
|
61
|
+
|
|
62
|
+
if (component.getClass('progress').includes('linear')) {
|
|
63
|
+
component.indicatorElement.style.width = `${percent}%`;
|
|
64
|
+
} else {
|
|
65
|
+
// Circular progress calculation
|
|
66
|
+
const circumference = 2 * Math.PI * 45; // r=45 is the SVG circle radius
|
|
67
|
+
const strokeDasharray = circumference;
|
|
68
|
+
const strokeDashoffset = circumference * (1 - percent / 100);
|
|
69
|
+
|
|
70
|
+
if (component.indicatorElement instanceof SVGElement) {
|
|
71
|
+
component.indicatorElement.style.strokeDasharray = `${strokeDasharray}`;
|
|
72
|
+
component.indicatorElement.style.strokeDashoffset = `${strokeDashoffset}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Update label if present
|
|
78
|
+
if (component.labelElement) {
|
|
79
|
+
const formatter = (v: number, max: number) => `${Math.round((v / max) * 100)}%`;
|
|
80
|
+
component.labelElement.textContent = formatter(value, options.value.getMax());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
component.emit(PROGRESS_EVENTS.VALUE_CHANGE, { value });
|
|
84
|
+
return this;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
getValue() {
|
|
88
|
+
return options.value.getValue();
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
setBuffer(value: number) {
|
|
92
|
+
options.buffer.setBuffer(value);
|
|
93
|
+
|
|
94
|
+
if (component.getClass('progress').includes('linear')) {
|
|
95
|
+
const bufferElement = component.element.querySelector(`.${component.getClass('progress')}-buffer`);
|
|
96
|
+
if (bufferElement) {
|
|
97
|
+
const percent = (value / options.value.getMax()) * 100;
|
|
98
|
+
(bufferElement as HTMLElement).style.width = `${percent}%`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return this;
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
getBuffer() {
|
|
106
|
+
return options.buffer.getBuffer();
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
enable() {
|
|
110
|
+
options.disabled.enable();
|
|
111
|
+
component.element.removeAttribute('aria-disabled');
|
|
112
|
+
return this;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
disable() {
|
|
116
|
+
options.disabled.disable();
|
|
117
|
+
component.element.setAttribute('aria-disabled', 'true');
|
|
118
|
+
return this;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
isDisabled() {
|
|
122
|
+
return options.disabled.isDisabled();
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
showLabel() {
|
|
126
|
+
options.label.show();
|
|
127
|
+
return this;
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
hideLabel() {
|
|
131
|
+
options.label.hide();
|
|
132
|
+
return this;
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
setLabelFormatter(formatter: (value: number, max: number) => string) {
|
|
136
|
+
options.label.format(formatter);
|
|
137
|
+
|
|
138
|
+
// Update the current label with the new formatter
|
|
139
|
+
if (component.labelElement) {
|
|
140
|
+
const value = this.getValue();
|
|
141
|
+
const max = options.value.getMax();
|
|
142
|
+
component.labelElement.textContent = formatter(value, max);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return this;
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
setIndeterminate(indeterminate: boolean) {
|
|
149
|
+
const wasIndeterminate = options.state.isIndeterminate();
|
|
150
|
+
options.state.setIndeterminate(indeterminate);
|
|
151
|
+
|
|
152
|
+
// Toggle the indeterminate class
|
|
153
|
+
const indeterminateClass = `${component.getClass('progress')}--indeterminate`;
|
|
154
|
+
|
|
155
|
+
if (indeterminate) {
|
|
156
|
+
component.element.classList.add(indeterminateClass);
|
|
157
|
+
component.element.setAttribute('aria-valuenow', '');
|
|
158
|
+
} else {
|
|
159
|
+
component.element.classList.remove(indeterminateClass);
|
|
160
|
+
const value = this.getValue();
|
|
161
|
+
component.element.setAttribute('aria-valuenow', value.toString());
|
|
162
|
+
this.setValue(value); // Update the visual state
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (wasIndeterminate !== indeterminate) {
|
|
166
|
+
component.emit(PROGRESS_EVENTS.STATE_CHANGE, { indeterminate });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return this;
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
isIndeterminate() {
|
|
173
|
+
return options.state.isIndeterminate();
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
destroy() {
|
|
177
|
+
options.lifecycle.destroy();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// src/components/progress/config.ts
|
|
2
|
+
import {
|
|
3
|
+
createComponentConfig,
|
|
4
|
+
createElementConfig,
|
|
5
|
+
BaseComponentConfig
|
|
6
|
+
} from '../../core/config/component-config';
|
|
7
|
+
import { ProgressConfig } from './types';
|
|
8
|
+
import { PROGRESS_VARIANTS, PROGRESS_SIZES } from './constants';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default configuration for the Progress component
|
|
12
|
+
*/
|
|
13
|
+
export const defaultConfig: ProgressConfig = {
|
|
14
|
+
variant: PROGRESS_VARIANTS.LINEAR,
|
|
15
|
+
size: PROGRESS_SIZES.MEDIUM,
|
|
16
|
+
value: 0,
|
|
17
|
+
max: 100,
|
|
18
|
+
buffer: 0,
|
|
19
|
+
showLabel: false
|
|
20
|
+
// Don't set disabled: false as default - it should be undefined by default
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates the base configuration for Progress component
|
|
25
|
+
* @param {ProgressConfig} config - User provided configuration
|
|
26
|
+
* @returns {ProgressConfig} Complete configuration with defaults applied
|
|
27
|
+
*/
|
|
28
|
+
export const createBaseConfig = (config: ProgressConfig = {}): ProgressConfig =>
|
|
29
|
+
createComponentConfig(defaultConfig, config, 'progress') as ProgressConfig;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generates element configuration for the Progress component
|
|
33
|
+
* @param {ProgressConfig} config - Progress configuration
|
|
34
|
+
* @returns {Object} Element configuration object for withElement
|
|
35
|
+
*/
|
|
36
|
+
export const getElementConfig = (config: ProgressConfig) => {
|
|
37
|
+
const isIndeterminate = config.indeterminate === true;
|
|
38
|
+
|
|
39
|
+
// Create the attributes object
|
|
40
|
+
const attrs: Record<string, any> = {
|
|
41
|
+
role: 'progressbar',
|
|
42
|
+
'aria-valuemin': '0',
|
|
43
|
+
'aria-valuemax': (config.max || 100).toString()
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Only add aria-valuenow if not indeterminate
|
|
47
|
+
if (!isIndeterminate && config.value !== undefined) {
|
|
48
|
+
attrs['aria-valuenow'] = config.value.toString();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Only add disabled attribute if it's explicitly true
|
|
52
|
+
if (config.disabled === true) {
|
|
53
|
+
attrs['aria-disabled'] = 'true';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const isCircular = config.variant === PROGRESS_VARIANTS.CIRCULAR;
|
|
57
|
+
|
|
58
|
+
return createElementConfig(config, {
|
|
59
|
+
tag: 'div',
|
|
60
|
+
attrs,
|
|
61
|
+
className: config.class
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Creates API configuration for the Progress component
|
|
67
|
+
* @param {Object} comp - Component with state management features
|
|
68
|
+
* @param {Object} state - State object containing component state
|
|
69
|
+
* @returns {Object} API configuration object
|
|
70
|
+
*/
|
|
71
|
+
export const getApiConfig = (comp, state) => ({
|
|
72
|
+
value: {
|
|
73
|
+
getValue: () => state.value,
|
|
74
|
+
setValue: (value: number) => {
|
|
75
|
+
state.value = Math.max(0, Math.min(state.max, value));
|
|
76
|
+
},
|
|
77
|
+
getMax: () => state.max
|
|
78
|
+
},
|
|
79
|
+
buffer: {
|
|
80
|
+
getBuffer: () => state.buffer,
|
|
81
|
+
setBuffer: (value: number) => {
|
|
82
|
+
state.buffer = Math.max(0, Math.min(state.max, value));
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
disabled: {
|
|
86
|
+
enable: () => comp.disabled.enable(),
|
|
87
|
+
disable: () => comp.disabled.disable(),
|
|
88
|
+
isDisabled: () => comp.disabled.isDisabled()
|
|
89
|
+
},
|
|
90
|
+
label: {
|
|
91
|
+
show: () => {
|
|
92
|
+
if (!state.labelElement) {
|
|
93
|
+
const labelElement = document.createElement('div');
|
|
94
|
+
labelElement.className = `${comp.getClass('progress')}-label`;
|
|
95
|
+
labelElement.textContent = state.labelFormatter(state.value, state.max);
|
|
96
|
+
comp.element.appendChild(labelElement);
|
|
97
|
+
state.labelElement = labelElement;
|
|
98
|
+
comp.labelElement = labelElement;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
hide: () => {
|
|
102
|
+
if (state.labelElement) {
|
|
103
|
+
state.labelElement.remove();
|
|
104
|
+
state.labelElement = undefined;
|
|
105
|
+
comp.labelElement = undefined;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
format: (formatter) => { state.labelFormatter = formatter; },
|
|
109
|
+
setContent: (content) => {
|
|
110
|
+
if (state.labelElement) {
|
|
111
|
+
state.labelElement.textContent = content;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
state: {
|
|
116
|
+
setIndeterminate: (indeterminate: boolean) => { state.indeterminate = indeterminate; },
|
|
117
|
+
isIndeterminate: () => state.indeterminate
|
|
118
|
+
},
|
|
119
|
+
lifecycle: {
|
|
120
|
+
destroy: () => comp.lifecycle.destroy()
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export default defaultConfig;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/components/progress/constants.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Progress component variants
|
|
5
|
+
*/
|
|
6
|
+
export const PROGRESS_VARIANTS = {
|
|
7
|
+
/** Standard linear progress bar */
|
|
8
|
+
LINEAR: 'linear',
|
|
9
|
+
|
|
10
|
+
/** Circular progress indicator */
|
|
11
|
+
CIRCULAR: 'circular',
|
|
12
|
+
|
|
13
|
+
/** Progress with known value */
|
|
14
|
+
DETERMINATE: 'determinate',
|
|
15
|
+
|
|
16
|
+
/** Indeterminate progress animation */
|
|
17
|
+
INDETERMINATE: 'indeterminate'
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Progress component sizes
|
|
22
|
+
*/
|
|
23
|
+
export const PROGRESS_SIZES = {
|
|
24
|
+
/** Small progress indicator (2px height for linear, 16px diameter for circular) */
|
|
25
|
+
SMALL: 'small',
|
|
26
|
+
|
|
27
|
+
/** Medium progress indicator (4px height for linear, 24px diameter for circular) */
|
|
28
|
+
MEDIUM: 'medium',
|
|
29
|
+
|
|
30
|
+
/** Large progress indicator (8px height for linear, 48px diameter for circular) */
|
|
31
|
+
LARGE: 'large'
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Progress component events
|
|
36
|
+
*/
|
|
37
|
+
export const PROGRESS_EVENTS = {
|
|
38
|
+
/** Fired when value changes */
|
|
39
|
+
VALUE_CHANGE: 'valueChange',
|
|
40
|
+
|
|
41
|
+
/** Fired when component becomes determinate/indeterminate */
|
|
42
|
+
STATE_CHANGE: 'stateChange'
|
|
43
|
+
} as const;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/components/progress/progress.ts
|
|
2
|
+
|
|
3
|
+
import { PREFIX } from '../../core/config';
|
|
4
|
+
import { pipe } from '../../core/compose';
|
|
5
|
+
import { createBase, withElement } from '../../core/compose/component';
|
|
6
|
+
import {
|
|
7
|
+
withEvents,
|
|
8
|
+
withVariant,
|
|
9
|
+
withSize,
|
|
10
|
+
withDisabled,
|
|
11
|
+
withLifecycle
|
|
12
|
+
} from '../../core/compose/features';
|
|
13
|
+
import { withAPI } from './api';
|
|
14
|
+
import { ProgressConfig, ProgressComponent } from './types';
|
|
15
|
+
import { PROGRESS_VARIANTS, PROGRESS_SIZES, PROGRESS_EVENTS } from './constants';
|
|
16
|
+
import { createBaseConfig, getElementConfig, getApiConfig } from './config';
|
|
17
|
+
|
|
18
|
+
// Helper functions
|
|
19
|
+
const createLinearProgressDOM = (baseClass: string) => {
|
|
20
|
+
const track = document.createElement('div');
|
|
21
|
+
track.className = `${baseClass}-track`;
|
|
22
|
+
|
|
23
|
+
const indicator = document.createElement('div');
|
|
24
|
+
indicator.className = `${baseClass}-indicator`;
|
|
25
|
+
|
|
26
|
+
const buffer = document.createElement('div');
|
|
27
|
+
buffer.className = `${baseClass}-buffer`;
|
|
28
|
+
|
|
29
|
+
return { track, indicator, buffer };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const createCircularProgressDOM = (baseClass: string) => {
|
|
33
|
+
const size = 96; // Default SVG viewBox size
|
|
34
|
+
const track = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
35
|
+
track.setAttribute('cx', '48');
|
|
36
|
+
track.setAttribute('cy', '48');
|
|
37
|
+
track.setAttribute('r', '45');
|
|
38
|
+
track.setAttribute('fill', 'none');
|
|
39
|
+
track.setAttribute('stroke-width', '6');
|
|
40
|
+
track.className = `${baseClass}-track`;
|
|
41
|
+
|
|
42
|
+
const indicator = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
43
|
+
indicator.setAttribute('cx', '48');
|
|
44
|
+
indicator.setAttribute('cy', '48');
|
|
45
|
+
indicator.setAttribute('r', '45');
|
|
46
|
+
indicator.setAttribute('fill', 'none');
|
|
47
|
+
indicator.setAttribute('stroke-width', '6');
|
|
48
|
+
indicator.className = `${baseClass}-indicator`;
|
|
49
|
+
|
|
50
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
51
|
+
svg.setAttribute('viewBox', `0 0 ${size} ${size}`);
|
|
52
|
+
svg.appendChild(track);
|
|
53
|
+
svg.appendChild(indicator);
|
|
54
|
+
|
|
55
|
+
return { track, indicator, svg };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new Progress component
|
|
60
|
+
* @param {ProgressConfig} config - Progress configuration object
|
|
61
|
+
* @returns {ProgressComponent} Progress component instance
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* // Create a linear determinate progress bar
|
|
66
|
+
* const progress = createProgress({
|
|
67
|
+
* variant: 'linear',
|
|
68
|
+
* value: 42,
|
|
69
|
+
* showLabel: true
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* // Create an indeterminate circular progress
|
|
73
|
+
* const loader = createProgress({
|
|
74
|
+
* variant: 'circular',
|
|
75
|
+
* size: 'large',
|
|
76
|
+
* indeterminate: true
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
const createProgress = (config: ProgressConfig = {}): ProgressComponent => {
|
|
81
|
+
// Create base configuration
|
|
82
|
+
const baseConfig = createBaseConfig(config);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Create state container
|
|
86
|
+
const state = {
|
|
87
|
+
value: baseConfig.value || 0,
|
|
88
|
+
max: baseConfig.max || 100,
|
|
89
|
+
buffer: baseConfig.buffer || 0,
|
|
90
|
+
indeterminate: baseConfig.indeterminate || false,
|
|
91
|
+
labelFormatter: (v: number, m: number) => `${Math.round((v / m) * 100)}%`,
|
|
92
|
+
labelElement: undefined as HTMLElement | undefined
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
let trackElement: HTMLElement;
|
|
96
|
+
let indicatorElement: HTMLElement;
|
|
97
|
+
|
|
98
|
+
// Create the component with the pipe pattern
|
|
99
|
+
const component = pipe(
|
|
100
|
+
createBase,
|
|
101
|
+
withEvents(),
|
|
102
|
+
withElement(getElementConfig(baseConfig)),
|
|
103
|
+
withVariant(baseConfig),
|
|
104
|
+
withSize(baseConfig),
|
|
105
|
+
withDisabled(baseConfig),
|
|
106
|
+
withLifecycle(),
|
|
107
|
+
// Add DOM structure based on variant
|
|
108
|
+
(component) => {
|
|
109
|
+
const baseClass = component.getClass('progress');
|
|
110
|
+
const isCircular = baseConfig.variant === PROGRESS_VARIANTS.CIRCULAR;
|
|
111
|
+
|
|
112
|
+
if (isCircular) {
|
|
113
|
+
const { track, indicator, svg } = createCircularProgressDOM(baseClass);
|
|
114
|
+
component.element.appendChild(svg);
|
|
115
|
+
trackElement = track;
|
|
116
|
+
indicatorElement = indicator;
|
|
117
|
+
} else {
|
|
118
|
+
const { track, indicator, buffer } = createLinearProgressDOM(baseClass);
|
|
119
|
+
component.element.appendChild(buffer);
|
|
120
|
+
component.element.appendChild(track);
|
|
121
|
+
component.element.appendChild(indicator);
|
|
122
|
+
trackElement = track;
|
|
123
|
+
indicatorElement = indicator;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Add label if requested
|
|
127
|
+
if (baseConfig.showLabel) {
|
|
128
|
+
const labelElement = document.createElement('div');
|
|
129
|
+
labelElement.className = `${baseClass}-label`;
|
|
130
|
+
labelElement.textContent = state.labelFormatter(state.value, state.max);
|
|
131
|
+
component.element.appendChild(labelElement);
|
|
132
|
+
state.labelElement = labelElement;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
...component,
|
|
137
|
+
trackElement,
|
|
138
|
+
indicatorElement,
|
|
139
|
+
labelElement: state.labelElement
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
// Add API
|
|
143
|
+
comp => withAPI(getApiConfig(comp, state))(comp)
|
|
144
|
+
)(baseConfig);
|
|
145
|
+
|
|
146
|
+
// Initialize state based on configuration
|
|
147
|
+
if (baseConfig.indeterminate) {
|
|
148
|
+
component.setIndeterminate(true);
|
|
149
|
+
} else {
|
|
150
|
+
component.setValue(state.value);
|
|
151
|
+
if (baseConfig.buffer !== undefined) {
|
|
152
|
+
component.setBuffer(baseConfig.buffer);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return component;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error('Progress creation error:', error);
|
|
159
|
+
throw new Error(`Failed to create progress: ${(error as Error).message}`);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export default createProgress;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/components/progress/types.ts
|
|
2
|
+
import { PROGRESS_VARIANTS, PROGRESS_SIZES } from './constants';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration interface for the Progress component
|
|
6
|
+
*/
|
|
7
|
+
export interface ProgressConfig {
|
|
8
|
+
/** Progress variant (linear, circular) */
|
|
9
|
+
variant?: keyof typeof PROGRESS_VARIANTS | string;
|
|
10
|
+
|
|
11
|
+
/** Progress size (small, medium, large) */
|
|
12
|
+
size?: keyof typeof PROGRESS_SIZES | string;
|
|
13
|
+
|
|
14
|
+
/** Initial progress value (0-100) */
|
|
15
|
+
value?: number;
|
|
16
|
+
|
|
17
|
+
/** Whether the progress indicator is initially disabled */
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
|
|
20
|
+
/** Maximum value (defaults to 100) */
|
|
21
|
+
max?: number;
|
|
22
|
+
|
|
23
|
+
/** Custom buffer value for linear progress */
|
|
24
|
+
buffer?: number;
|
|
25
|
+
|
|
26
|
+
/** Additional CSS classes */
|
|
27
|
+
class?: string;
|
|
28
|
+
|
|
29
|
+
/** Whether to show text label with percentage */
|
|
30
|
+
showLabel?: boolean;
|
|
31
|
+
|
|
32
|
+
/** Whether progress is indeterminate */
|
|
33
|
+
indeterminate?: boolean;
|
|
34
|
+
|
|
35
|
+
/** Custom label format function */
|
|
36
|
+
labelFormatter?: (value: number, max: number) => string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Progress component interface
|
|
41
|
+
*/
|
|
42
|
+
export interface ProgressComponent {
|
|
43
|
+
/** The component's root element */
|
|
44
|
+
element: HTMLElement;
|
|
45
|
+
|
|
46
|
+
/** The track element */
|
|
47
|
+
trackElement: HTMLElement;
|
|
48
|
+
|
|
49
|
+
/** The indicator element */
|
|
50
|
+
indicatorElement: HTMLElement;
|
|
51
|
+
|
|
52
|
+
/** The label element (if enabled) */
|
|
53
|
+
labelElement?: HTMLElement;
|
|
54
|
+
|
|
55
|
+
/** Sets the current progress value */
|
|
56
|
+
setValue: (value: number) => ProgressComponent;
|
|
57
|
+
|
|
58
|
+
/** Gets the current progress value */
|
|
59
|
+
getValue: () => number;
|
|
60
|
+
|
|
61
|
+
/** Sets the buffer value (for linear variant) */
|
|
62
|
+
setBuffer: (value: number) => ProgressComponent;
|
|
63
|
+
|
|
64
|
+
/** Gets the current buffer value */
|
|
65
|
+
getBuffer: () => number;
|
|
66
|
+
|
|
67
|
+
/** Enables the progress component */
|
|
68
|
+
enable: () => ProgressComponent;
|
|
69
|
+
|
|
70
|
+
/** Disables the progress component */
|
|
71
|
+
disable: () => ProgressComponent;
|
|
72
|
+
|
|
73
|
+
/** Checks if the component is disabled */
|
|
74
|
+
isDisabled: () => boolean;
|
|
75
|
+
|
|
76
|
+
/** Shows the label */
|
|
77
|
+
showLabel: () => ProgressComponent;
|
|
78
|
+
|
|
79
|
+
/** Hides the label */
|
|
80
|
+
hideLabel: () => ProgressComponent;
|
|
81
|
+
|
|
82
|
+
/** Sets a custom formatter for the label */
|
|
83
|
+
setLabelFormatter: (formatter: (value: number, max: number) => string) => ProgressComponent;
|
|
84
|
+
|
|
85
|
+
/** Sets the indeterminate state */
|
|
86
|
+
setIndeterminate: (indeterminate: boolean) => ProgressComponent;
|
|
87
|
+
|
|
88
|
+
/** Checks if the component is indeterminate */
|
|
89
|
+
isIndeterminate: () => boolean;
|
|
90
|
+
|
|
91
|
+
/** Adds event listener */
|
|
92
|
+
on: (event: string, handler: Function) => ProgressComponent;
|
|
93
|
+
|
|
94
|
+
/** Removes event listener */
|
|
95
|
+
off: (event: string, handler: Function) => ProgressComponent;
|
|
96
|
+
|
|
97
|
+
/** Destroys the component and cleans up resources */
|
|
98
|
+
destroy: () => void;
|
|
99
|
+
|
|
100
|
+
/** Add CSS classes */
|
|
101
|
+
addClass: (...classes: string[]) => ProgressComponent;
|
|
102
|
+
}
|