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.
Files changed (225) hide show
  1. package/README.md +70 -22
  2. package/index.ts +33 -0
  3. package/package.json +14 -5
  4. package/src/components/button/{styles.scss → _styles.scss} +2 -2
  5. package/src/components/button/api.ts +89 -0
  6. package/src/components/button/button.ts +50 -0
  7. package/src/components/button/config.ts +75 -0
  8. package/src/components/button/constants.ts +17 -0
  9. package/src/components/button/index.ts +4 -0
  10. package/src/components/button/types.ts +118 -0
  11. package/src/components/card/{styles.scss → _styles.scss} +79 -7
  12. package/src/components/card/{actions.js → actions.ts} +15 -18
  13. package/src/components/card/{api.js → api.ts} +33 -33
  14. package/src/components/card/card.ts +41 -0
  15. package/src/components/card/config.ts +99 -0
  16. package/src/components/card/{constants.js → constants.ts} +11 -10
  17. package/src/components/card/{content.js → content.ts} +15 -18
  18. package/src/components/card/{features.js → features.ts} +104 -94
  19. package/src/components/card/{header.js → header.ts} +21 -25
  20. package/src/components/card/index.ts +19 -0
  21. package/src/components/card/media.ts +52 -0
  22. package/src/components/card/types.ts +174 -0
  23. package/src/components/checkbox/api.ts +82 -0
  24. package/src/components/checkbox/checkbox.ts +75 -0
  25. package/src/components/checkbox/config.ts +90 -0
  26. package/src/components/checkbox/constants.ts +37 -0
  27. package/src/components/checkbox/index.ts +4 -0
  28. package/src/components/checkbox/types.ts +146 -0
  29. package/src/components/chip/_styles.scss +372 -0
  30. package/src/components/chip/api.ts +115 -0
  31. package/src/components/chip/chip-set.ts +225 -0
  32. package/src/components/chip/chip.ts +82 -0
  33. package/src/components/chip/config.ts +92 -0
  34. package/src/components/chip/constants.ts +38 -0
  35. package/src/components/chip/index.ts +4 -0
  36. package/src/components/chip/types.ts +172 -0
  37. package/src/components/list/api.ts +72 -0
  38. package/src/components/list/config.ts +43 -0
  39. package/src/components/list/{constants.js → constants.ts} +34 -7
  40. package/src/components/list/features.ts +224 -0
  41. package/src/components/list/index.ts +14 -0
  42. package/src/components/list/list-item.ts +120 -0
  43. package/src/components/list/list.ts +37 -0
  44. package/src/components/list/types.ts +179 -0
  45. package/src/components/list/utils.ts +47 -0
  46. package/src/components/menu/api.ts +119 -0
  47. package/src/components/menu/config.ts +54 -0
  48. package/src/components/menu/constants.ts +154 -0
  49. package/src/components/menu/features/items-manager.ts +457 -0
  50. package/src/components/menu/features/keyboard-navigation.ts +133 -0
  51. package/src/components/menu/features/positioning.ts +127 -0
  52. package/src/components/menu/features/{visibility.js → visibility.ts} +66 -64
  53. package/src/components/menu/index.ts +14 -0
  54. package/src/components/menu/menu-item.ts +43 -0
  55. package/src/components/menu/menu.ts +53 -0
  56. package/src/components/menu/types.ts +178 -0
  57. package/src/components/navigation/api.ts +79 -0
  58. package/src/components/navigation/config.ts +61 -0
  59. package/src/components/navigation/{constants.js → constants.ts} +10 -10
  60. package/src/components/navigation/index.ts +14 -0
  61. package/src/components/navigation/nav-item.ts +148 -0
  62. package/src/components/navigation/navigation.ts +50 -0
  63. package/src/components/navigation/types.ts +212 -0
  64. package/src/components/progress/_styles.scss +204 -0
  65. package/src/components/progress/api.ts +179 -0
  66. package/src/components/progress/config.ts +124 -0
  67. package/src/components/progress/constants.ts +43 -0
  68. package/src/components/progress/index.ts +5 -0
  69. package/src/components/progress/progress.ts +163 -0
  70. package/src/components/progress/types.ts +102 -0
  71. package/src/components/snackbar/api.ts +162 -0
  72. package/src/components/snackbar/config.ts +62 -0
  73. package/src/components/snackbar/{constants.js → constants.ts} +21 -4
  74. package/src/components/snackbar/features.ts +76 -0
  75. package/src/components/snackbar/index.ts +4 -0
  76. package/src/components/snackbar/position.ts +71 -0
  77. package/src/components/snackbar/queue.ts +76 -0
  78. package/src/components/snackbar/snackbar.ts +60 -0
  79. package/src/components/snackbar/types.ts +58 -0
  80. package/src/components/switch/api.ts +77 -0
  81. package/src/components/switch/config.ts +74 -0
  82. package/src/components/switch/{constants.js → constants.ts} +5 -5
  83. package/src/components/switch/index.ts +4 -0
  84. package/src/components/switch/switch.ts +52 -0
  85. package/src/components/switch/types.ts +142 -0
  86. package/src/components/textfield/api.ts +72 -0
  87. package/src/components/textfield/config.ts +54 -0
  88. package/src/components/textfield/{constants.js → constants.ts} +38 -5
  89. package/src/components/textfield/index.ts +4 -0
  90. package/src/components/textfield/textfield.ts +50 -0
  91. package/src/components/textfield/types.ts +139 -0
  92. package/src/core/compose/base.ts +43 -0
  93. package/src/core/compose/component.ts +255 -0
  94. package/src/core/compose/features/checkable.ts +155 -0
  95. package/src/core/compose/features/disabled.ts +116 -0
  96. package/src/core/compose/features/events.ts +65 -0
  97. package/src/core/compose/features/icon.ts +67 -0
  98. package/src/core/compose/features/index.ts +35 -0
  99. package/src/core/compose/features/input.ts +174 -0
  100. package/src/core/compose/features/lifecycle.ts +139 -0
  101. package/src/core/compose/features/position.ts +94 -0
  102. package/src/core/compose/features/ripple.ts +55 -0
  103. package/src/core/compose/features/size.ts +29 -0
  104. package/src/core/compose/features/style.ts +31 -0
  105. package/src/core/compose/features/text.ts +44 -0
  106. package/src/core/compose/features/textinput.ts +225 -0
  107. package/src/core/compose/features/textlabel.ts +92 -0
  108. package/src/core/compose/features/track.ts +84 -0
  109. package/src/core/compose/features/variant.ts +29 -0
  110. package/src/core/compose/features/withEvents.ts +137 -0
  111. package/src/core/compose/index.ts +54 -0
  112. package/src/core/compose/{pipe.js → pipe.ts} +16 -11
  113. package/src/core/config/component-config.ts +136 -0
  114. package/src/core/config.ts +211 -0
  115. package/src/core/dom/{attributes.js → attributes.ts} +11 -11
  116. package/src/core/dom/classes.ts +60 -0
  117. package/src/core/dom/create.ts +251 -0
  118. package/src/core/dom/events.ts +209 -0
  119. package/src/core/dom/index.ts +10 -0
  120. package/src/core/dom/utils.ts +97 -0
  121. package/src/core/index.ts +111 -0
  122. package/src/core/state/disabled.ts +81 -0
  123. package/src/core/state/emitter.ts +94 -0
  124. package/src/core/state/events.ts +88 -0
  125. package/src/core/state/index.ts +16 -0
  126. package/src/core/state/lifecycle.ts +131 -0
  127. package/src/core/state/store.ts +197 -0
  128. package/src/core/utils/index.ts +45 -0
  129. package/src/core/utils/{mobile.js → mobile.ts} +48 -24
  130. package/src/core/utils/object.ts +41 -0
  131. package/src/core/utils/validate.ts +234 -0
  132. package/src/{index.js → index.ts} +3 -2
  133. package/index.js +0 -11
  134. package/src/components/button/api.js +0 -54
  135. package/src/components/button/button.js +0 -81
  136. package/src/components/button/config.js +0 -10
  137. package/src/components/button/constants.js +0 -63
  138. package/src/components/button/index.js +0 -2
  139. package/src/components/card/card.js +0 -102
  140. package/src/components/card/config.js +0 -16
  141. package/src/components/card/index.js +0 -7
  142. package/src/components/card/media.js +0 -56
  143. package/src/components/checkbox/api.js +0 -45
  144. package/src/components/checkbox/checkbox.js +0 -96
  145. package/src/components/checkbox/constants.js +0 -88
  146. package/src/components/checkbox/index.js +0 -2
  147. package/src/components/container/api.js +0 -42
  148. package/src/components/container/container.js +0 -45
  149. package/src/components/container/index.js +0 -2
  150. package/src/components/container/styles.scss +0 -66
  151. package/src/components/list/index.js +0 -2
  152. package/src/components/list/list-item.js +0 -147
  153. package/src/components/list/list.js +0 -267
  154. package/src/components/menu/api.js +0 -117
  155. package/src/components/menu/constants.js +0 -42
  156. package/src/components/menu/features/items-manager.js +0 -375
  157. package/src/components/menu/features/keyboard-navigation.js +0 -129
  158. package/src/components/menu/features/positioning.js +0 -125
  159. package/src/components/menu/index.js +0 -2
  160. package/src/components/menu/menu-item.js +0 -41
  161. package/src/components/menu/menu.js +0 -54
  162. package/src/components/navigation/api.js +0 -43
  163. package/src/components/navigation/index.js +0 -2
  164. package/src/components/navigation/nav-item.js +0 -137
  165. package/src/components/navigation/navigation.js +0 -55
  166. package/src/components/snackbar/api.js +0 -125
  167. package/src/components/snackbar/features.js +0 -69
  168. package/src/components/snackbar/index.js +0 -2
  169. package/src/components/snackbar/position.js +0 -63
  170. package/src/components/snackbar/queue.js +0 -74
  171. package/src/components/snackbar/snackbar.js +0 -70
  172. package/src/components/switch/api.js +0 -44
  173. package/src/components/switch/index.js +0 -2
  174. package/src/components/switch/switch.js +0 -71
  175. package/src/components/textfield/api.js +0 -49
  176. package/src/components/textfield/index.js +0 -2
  177. package/src/components/textfield/textfield.js +0 -68
  178. package/src/core/build/_ripple.scss +0 -79
  179. package/src/core/build/constants.js +0 -51
  180. package/src/core/build/icon.js +0 -78
  181. package/src/core/build/ripple.js +0 -159
  182. package/src/core/build/text.js +0 -54
  183. package/src/core/compose/base.js +0 -8
  184. package/src/core/compose/component.js +0 -225
  185. package/src/core/compose/features/checkable.js +0 -114
  186. package/src/core/compose/features/disabled.js +0 -64
  187. package/src/core/compose/features/events.js +0 -48
  188. package/src/core/compose/features/icon.js +0 -33
  189. package/src/core/compose/features/index.js +0 -20
  190. package/src/core/compose/features/input.js +0 -100
  191. package/src/core/compose/features/lifecycle.js +0 -69
  192. package/src/core/compose/features/position.js +0 -60
  193. package/src/core/compose/features/ripple.js +0 -32
  194. package/src/core/compose/features/size.js +0 -9
  195. package/src/core/compose/features/style.js +0 -12
  196. package/src/core/compose/features/text.js +0 -17
  197. package/src/core/compose/features/textinput.js +0 -114
  198. package/src/core/compose/features/textlabel.js +0 -28
  199. package/src/core/compose/features/track.js +0 -49
  200. package/src/core/compose/features/variant.js +0 -9
  201. package/src/core/compose/features/withEvents.js +0 -67
  202. package/src/core/compose/index.js +0 -16
  203. package/src/core/config.js +0 -140
  204. package/src/core/dom/classes.js +0 -70
  205. package/src/core/dom/create.js +0 -132
  206. package/src/core/dom/events.js +0 -175
  207. package/src/core/dom/index.js +0 -5
  208. package/src/core/dom/utils.js +0 -22
  209. package/src/core/index.js +0 -23
  210. package/src/core/state/disabled.js +0 -51
  211. package/src/core/state/emitter.js +0 -63
  212. package/src/core/state/events.js +0 -29
  213. package/src/core/state/index.js +0 -6
  214. package/src/core/state/lifecycle.js +0 -64
  215. package/src/core/state/store.js +0 -112
  216. package/src/core/utils/index.js +0 -39
  217. package/src/core/utils/object.js +0 -22
  218. package/src/core/utils/validate.js +0 -37
  219. /package/src/components/checkbox/{styles.scss → _styles.scss} +0 -0
  220. /package/src/components/list/{styles.scss → _styles.scss} +0 -0
  221. /package/src/components/menu/{styles.scss → _styles.scss} +0 -0
  222. /package/src/components/navigation/{styles.scss → _styles.scss} +0 -0
  223. /package/src/components/snackbar/{styles.scss → _styles.scss} +0 -0
  224. /package/src/components/switch/{styles.scss → _styles.scss} +0 -0
  225. /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,5 @@
1
+ // src/components/progress/index.ts
2
+
3
+ export { default } from './progress';
4
+ export { PROGRESS_VARIANTS, PROGRESS_SIZES, PROGRESS_EVENTS } from './constants';
5
+ export { ProgressConfig, ProgressComponent } from './types';
@@ -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
+ }