mtrl 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/components/card/constants.ts +14 -0
- package/src/components/checkbox/constants.ts +5 -56
- package/src/components/chip/_styles.scss +83 -140
- package/src/components/chip/api.ts +231 -102
- package/src/components/chip/chip.ts +356 -44
- package/src/components/chip/constants.ts +3 -3
- package/src/components/chip/index.ts +3 -3
- package/src/components/switch/constants.ts +5 -5
- package/src/core/compose/component.ts +22 -14
- package/src/core/dom/create.ts +75 -12
- package/src/styles/abstract/_variables.scss +12 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// src/components/chip/chip.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
// src/components/chip/chip.js
|
|
2
|
+
import { PREFIX } from '../../core/config'
|
|
3
|
+
import { pipe } from '../../core/compose'
|
|
4
|
+
import { createBase, withElement } from '../../core/compose/component'
|
|
4
5
|
import {
|
|
5
6
|
withEvents,
|
|
6
7
|
withText,
|
|
@@ -10,72 +11,383 @@ import {
|
|
|
10
11
|
withRipple,
|
|
11
12
|
withDisabled,
|
|
12
13
|
withLifecycle
|
|
13
|
-
} from '../../core/compose/features'
|
|
14
|
-
import { withAPI } from './api'
|
|
15
|
-
import {
|
|
16
|
-
import { createBaseConfig, getElementConfig, getApiConfig } from './config';
|
|
14
|
+
} from '../../core/compose/features'
|
|
15
|
+
import { withAPI } from './api'
|
|
16
|
+
import { CHIP_VARIANTS, CHIP_SIZES } from './constants'
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Creates a new Chip component
|
|
20
|
-
* @param {
|
|
21
|
-
* @
|
|
20
|
+
* @param {Object} config - Chip configuration
|
|
21
|
+
* @param {string} [config.variant='filled'] - Chip variant
|
|
22
|
+
* @param {string} [config.size='medium'] - Chip size
|
|
23
|
+
* @param {boolean} [config.selected=false] - Whether the chip is initially selected
|
|
24
|
+
* @param {boolean} [config.disabled=false] - Whether the chip is initially disabled
|
|
25
|
+
* @param {string} [config.text] - Chip text content
|
|
26
|
+
* @param {string} [config.leadingIcon] - Leading icon HTML content
|
|
27
|
+
* @param {string} [config.trailingIcon] - Trailing icon HTML content
|
|
28
|
+
* @param {string} [config.class] - Additional CSS classes
|
|
29
|
+
* @param {string} [config.value] - Chip value
|
|
30
|
+
* @param {boolean} [config.ripple=true] - Whether to enable ripple effect
|
|
31
|
+
* @param {Function} [config.onTrailingIconClick] - Callback when trailing icon is clicked
|
|
32
|
+
* @param {Function} [config.onSelect] - Callback when chip is selected
|
|
33
|
+
* @param {Function} [config.onChange] - Callback when chip selection changes
|
|
34
|
+
* @returns {Object} Chip component instance
|
|
22
35
|
*/
|
|
23
|
-
const createChip = (config
|
|
24
|
-
const baseConfig =
|
|
36
|
+
const createChip = (config = {}) => {
|
|
37
|
+
const baseConfig = {
|
|
38
|
+
...config,
|
|
39
|
+
variant: config.variant || CHIP_VARIANTS.FILLED,
|
|
40
|
+
size: config.size || CHIP_SIZES.MEDIUM,
|
|
41
|
+
componentName: 'chip',
|
|
42
|
+
prefix: PREFIX,
|
|
43
|
+
ripple: config.ripple !== false
|
|
44
|
+
}
|
|
25
45
|
|
|
26
46
|
try {
|
|
47
|
+
// Create base component with core features
|
|
27
48
|
const chip = pipe(
|
|
28
49
|
createBase,
|
|
29
50
|
withEvents(),
|
|
30
|
-
withElement(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
withElement({
|
|
52
|
+
tag: 'div',
|
|
53
|
+
componentName: 'chip',
|
|
54
|
+
attrs: {
|
|
55
|
+
role: 'button',
|
|
56
|
+
tabindex: '0',
|
|
57
|
+
'aria-disabled': config.disabled ? 'true' : 'false',
|
|
58
|
+
'aria-selected': config.selected ? 'true' : 'false',
|
|
59
|
+
'data-value': config.value || ''
|
|
60
|
+
},
|
|
61
|
+
className: config.class,
|
|
62
|
+
forwardEvents: {
|
|
63
|
+
click: (component) => component.element.getAttribute('aria-disabled') !== 'true',
|
|
64
|
+
focus: true,
|
|
65
|
+
blur: true
|
|
66
|
+
}
|
|
38
67
|
}),
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
68
|
+
withLifecycle()
|
|
69
|
+
)(baseConfig)
|
|
70
|
+
|
|
71
|
+
// Track selected state
|
|
72
|
+
let isSelectedState = !!config.selected;
|
|
73
|
+
|
|
74
|
+
// Manually add the variant class
|
|
75
|
+
if (config.variant) {
|
|
76
|
+
chip.element.classList.add(`${chip.getClass('chip')}--${config.variant}`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Manually add the size class
|
|
80
|
+
if (config.size) {
|
|
81
|
+
chip.element.classList.add(`${chip.getClass('chip')}--${config.size}`)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Add ripple if enabled
|
|
85
|
+
if (config.ripple) {
|
|
86
|
+
withRipple(baseConfig)(chip)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Add disabled state if needed
|
|
90
|
+
if (config.disabled) {
|
|
91
|
+
withDisabled(baseConfig)(chip)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Add selected class if needed
|
|
95
|
+
if (config.selected) {
|
|
96
|
+
chip.element.classList.add(`${chip.getClass('chip')}--selected`)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Create a container for the chip content to ensure proper ordering
|
|
100
|
+
const contentContainer = document.createElement('div')
|
|
101
|
+
contentContainer.className = `${chip.getClass('chip')}-content`
|
|
102
|
+
contentContainer.style.display = 'flex'
|
|
103
|
+
contentContainer.style.alignItems = 'center'
|
|
104
|
+
contentContainer.style.justifyContent = 'center'
|
|
105
|
+
contentContainer.style.width = '100%'
|
|
106
|
+
chip.element.appendChild(contentContainer)
|
|
107
|
+
|
|
108
|
+
// Add leading icon if provided
|
|
109
|
+
if (config.leadingIcon) {
|
|
110
|
+
const leadingIconElement = document.createElement('span')
|
|
111
|
+
leadingIconElement.className = `${chip.getClass('chip')}-leading-icon`
|
|
112
|
+
leadingIconElement.innerHTML = config.leadingIcon
|
|
113
|
+
contentContainer.appendChild(leadingIconElement)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Add text element if provided
|
|
117
|
+
if (config.text) {
|
|
118
|
+
const textElement = document.createElement('span')
|
|
119
|
+
textElement.className = `${chip.getClass('chip')}-text`
|
|
120
|
+
textElement.textContent = config.text
|
|
121
|
+
contentContainer.appendChild(textElement)
|
|
122
|
+
}
|
|
44
123
|
|
|
45
124
|
// Add trailing icon if provided
|
|
46
125
|
if (config.trailingIcon) {
|
|
47
|
-
const trailingIconElement = document.createElement('span')
|
|
48
|
-
trailingIconElement.className = `${
|
|
49
|
-
trailingIconElement.innerHTML = config.trailingIcon
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Add event listener for remove/close action if needed
|
|
126
|
+
const trailingIconElement = document.createElement('span')
|
|
127
|
+
trailingIconElement.className = `${chip.getClass('chip')}-trailing-icon`
|
|
128
|
+
trailingIconElement.innerHTML = config.trailingIcon
|
|
129
|
+
|
|
130
|
+
// Add click handler for trailing icon
|
|
53
131
|
if (config.onTrailingIconClick) {
|
|
54
132
|
trailingIconElement.addEventListener('click', (e) => {
|
|
55
|
-
e.stopPropagation()
|
|
56
|
-
config.onTrailingIconClick
|
|
57
|
-
})
|
|
133
|
+
e.stopPropagation() // Prevent chip click event
|
|
134
|
+
config.onTrailingIconClick(enhancedChip)
|
|
135
|
+
})
|
|
58
136
|
}
|
|
137
|
+
|
|
138
|
+
contentContainer.appendChild(trailingIconElement)
|
|
59
139
|
}
|
|
60
140
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
141
|
+
// Create enhanced component with API
|
|
142
|
+
const enhancedChip = {
|
|
143
|
+
...chip,
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Checks if the chip is disabled
|
|
147
|
+
* @returns {boolean} True if the chip is disabled
|
|
148
|
+
*/
|
|
149
|
+
isDisabled() {
|
|
150
|
+
return chip.element.getAttribute('aria-disabled') === 'true';
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Checks if the chip is selected
|
|
155
|
+
* @returns {boolean} True if the chip is selected
|
|
156
|
+
*/
|
|
157
|
+
isSelected() {
|
|
158
|
+
return isSelectedState;
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sets the chip's selected state
|
|
163
|
+
* @param {boolean} selected - Whether the chip should be selected
|
|
164
|
+
* @returns {Object} The chip instance for chaining
|
|
165
|
+
*/
|
|
166
|
+
setSelected(selected) {
|
|
167
|
+
isSelectedState = !!selected;
|
|
168
|
+
|
|
169
|
+
if (selected) {
|
|
170
|
+
chip.element.classList.add(`${chip.getClass('chip')}--selected`);
|
|
171
|
+
chip.element.setAttribute('aria-selected', 'true');
|
|
172
|
+
} else {
|
|
173
|
+
chip.element.classList.remove(`${chip.getClass('chip')}--selected`);
|
|
174
|
+
chip.element.setAttribute('aria-selected', 'false');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return this;
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Toggles the chip's selected state
|
|
182
|
+
* @returns {Object} The chip instance for chaining
|
|
183
|
+
*/
|
|
184
|
+
toggleSelected() {
|
|
185
|
+
return this.setSelected(!isSelectedState);
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets the chip's value
|
|
190
|
+
* @returns {string} The chip's value
|
|
191
|
+
*/
|
|
192
|
+
getValue() {
|
|
193
|
+
return chip.element.getAttribute('data-value');
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Sets the chip's value
|
|
198
|
+
* @param {string} value - Value to set
|
|
199
|
+
* @returns {Object} The chip instance for chaining
|
|
200
|
+
*/
|
|
201
|
+
setValue(value) {
|
|
202
|
+
chip.element.setAttribute('data-value', value);
|
|
203
|
+
return this;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Enables the chip
|
|
208
|
+
* @returns {Object} The chip instance for chaining
|
|
209
|
+
*/
|
|
210
|
+
enable() {
|
|
211
|
+
chip.element.classList.remove(`${chip.getClass('chip')}--disabled`);
|
|
212
|
+
chip.element.setAttribute('aria-disabled', 'false');
|
|
213
|
+
chip.element.setAttribute('tabindex', '0');
|
|
214
|
+
return this;
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Disables the chip
|
|
219
|
+
* @returns {Object} The chip instance for chaining
|
|
220
|
+
*/
|
|
221
|
+
disable() {
|
|
222
|
+
chip.element.classList.add(`${chip.getClass('chip')}--disabled`);
|
|
223
|
+
chip.element.setAttribute('aria-disabled', 'true');
|
|
224
|
+
chip.element.setAttribute('tabindex', '-1');
|
|
225
|
+
return this;
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Sets the chip's text content
|
|
230
|
+
* @param {string} content - Text content
|
|
231
|
+
* @returns {Object} The chip instance for chaining
|
|
232
|
+
*/
|
|
233
|
+
setText(content) {
|
|
234
|
+
const textElement = chip.element.querySelector(`.${chip.getClass('chip')}-text`);
|
|
235
|
+
|
|
236
|
+
if (textElement) {
|
|
237
|
+
textElement.textContent = content;
|
|
238
|
+
} else if (content) {
|
|
239
|
+
const newTextElement = document.createElement('span');
|
|
240
|
+
newTextElement.className = `${chip.getClass('chip')}-text`;
|
|
241
|
+
newTextElement.textContent = content;
|
|
242
|
+
contentContainer.appendChild(newTextElement);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return this;
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Gets the chip's text content
|
|
250
|
+
* @returns {string} The chip's text content
|
|
251
|
+
*/
|
|
252
|
+
getText() {
|
|
253
|
+
const textElement = chip.element.querySelector(`.${chip.getClass('chip')}-text`);
|
|
254
|
+
return textElement ? textElement.textContent : '';
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Sets the chip's icon
|
|
259
|
+
* @param {string} icon - Icon HTML content
|
|
260
|
+
* @returns {Object} The chip instance for chaining
|
|
261
|
+
*/
|
|
262
|
+
setIcon(icon) {
|
|
263
|
+
return this.setLeadingIcon(icon);
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Gets the chip's icon
|
|
268
|
+
* @returns {string} The chip's icon HTML
|
|
269
|
+
*/
|
|
270
|
+
getIcon() {
|
|
271
|
+
const iconElement = chip.element.querySelector(`.${chip.getClass('chip')}-leading-icon`);
|
|
272
|
+
return iconElement ? iconElement.innerHTML : '';
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Sets the chip's leading icon
|
|
277
|
+
* @param {string} icon - Icon HTML content
|
|
278
|
+
* @returns {Object} The chip instance for chaining
|
|
279
|
+
*/
|
|
280
|
+
setLeadingIcon(icon) {
|
|
281
|
+
const leadingIconSelector = `.${chip.getClass('chip')}-leading-icon`;
|
|
282
|
+
let leadingIconElement = chip.element.querySelector(leadingIconSelector);
|
|
283
|
+
|
|
284
|
+
if (!leadingIconElement && icon) {
|
|
285
|
+
leadingIconElement = document.createElement('span');
|
|
286
|
+
leadingIconElement.className = `${chip.getClass('chip')}-leading-icon`;
|
|
287
|
+
|
|
288
|
+
// Insert at the beginning of the content container
|
|
289
|
+
contentContainer.insertBefore(leadingIconElement, contentContainer.firstChild);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (leadingIconElement) {
|
|
293
|
+
leadingIconElement.innerHTML = icon || '';
|
|
294
|
+
|
|
295
|
+
// Remove the element if icon is empty
|
|
296
|
+
if (!icon && leadingIconElement.parentNode) {
|
|
297
|
+
leadingIconElement.parentNode.removeChild(leadingIconElement);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return this;
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Sets the chip's trailing icon
|
|
306
|
+
* @param {string} icon - Icon HTML content
|
|
307
|
+
* @param {Function} [onClick] - Click handler for the trailing icon
|
|
308
|
+
* @returns {Object} The chip instance for chaining
|
|
309
|
+
*/
|
|
310
|
+
setTrailingIcon(icon, onClick) {
|
|
311
|
+
const trailingIconSelector = `.${chip.getClass('chip')}-trailing-icon`;
|
|
312
|
+
let trailingIconElement = chip.element.querySelector(trailingIconSelector);
|
|
313
|
+
|
|
314
|
+
if (!trailingIconElement && icon) {
|
|
315
|
+
trailingIconElement = document.createElement('span');
|
|
316
|
+
trailingIconElement.className = `${chip.getClass('chip')}-trailing-icon`;
|
|
317
|
+
|
|
318
|
+
// Add at the end of the content container
|
|
319
|
+
contentContainer.appendChild(trailingIconElement);
|
|
320
|
+
|
|
321
|
+
// Add click handler if provided
|
|
322
|
+
if (onClick) {
|
|
323
|
+
trailingIconElement.addEventListener('click', (e) => {
|
|
324
|
+
e.stopPropagation(); // Prevent chip click event
|
|
325
|
+
onClick(this);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (trailingIconElement) {
|
|
331
|
+
trailingIconElement.innerHTML = icon || '';
|
|
332
|
+
|
|
333
|
+
// Remove the element if icon is empty
|
|
334
|
+
if (!icon && trailingIconElement.parentNode) {
|
|
335
|
+
trailingIconElement.parentNode.removeChild(trailingIconElement);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return this;
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Destroys the chip component and cleans up resources
|
|
344
|
+
*/
|
|
345
|
+
destroy() {
|
|
346
|
+
chip.lifecycle && chip.lifecycle.destroy && chip.lifecycle.destroy();
|
|
347
|
+
chip.element.remove();
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
// Forward event methods from the original chip
|
|
351
|
+
on: chip.on,
|
|
352
|
+
off: chip.off,
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Add CSS classes to the chip element
|
|
356
|
+
* @param {...string} classes - CSS classes to add
|
|
357
|
+
* @returns {Object} The chip instance for chaining
|
|
358
|
+
*/
|
|
359
|
+
addClass(...classes) {
|
|
360
|
+
chip.element.classList.add(...classes);
|
|
361
|
+
return this;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
65
364
|
|
|
66
|
-
//
|
|
67
|
-
if (config.
|
|
365
|
+
// Add click handler for selection toggle
|
|
366
|
+
if (config.variant === CHIP_VARIANTS.FILTER ||
|
|
367
|
+
config.variant === CHIP_VARIANTS.ASSIST ||
|
|
368
|
+
config.selectable) {
|
|
369
|
+
|
|
68
370
|
chip.element.addEventListener('click', () => {
|
|
69
|
-
if (
|
|
70
|
-
|
|
371
|
+
if (enhancedChip.isDisabled()) return;
|
|
372
|
+
|
|
373
|
+
enhancedChip.toggleSelected();
|
|
374
|
+
|
|
375
|
+
// Call onChange callback if provided
|
|
376
|
+
if (config.onChange) {
|
|
377
|
+
config.onChange(enhancedChip);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Call onSelect callback if provided
|
|
381
|
+
if (config.onSelect) {
|
|
382
|
+
config.onSelect(enhancedChip);
|
|
71
383
|
}
|
|
72
384
|
});
|
|
73
385
|
}
|
|
74
386
|
|
|
75
|
-
return
|
|
387
|
+
return enhancedChip;
|
|
76
388
|
} catch (error) {
|
|
77
|
-
console.error('Chip creation error:', error
|
|
78
|
-
throw new Error(`Failed to create chip: ${error
|
|
389
|
+
console.error('Chip creation error:', error);
|
|
390
|
+
throw new Error(`Failed to create chip: ${error.message}`);
|
|
79
391
|
}
|
|
80
392
|
};
|
|
81
393
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/components/chip/constants.
|
|
1
|
+
// src/components/chip/constants.js
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Available variants for the Chip component
|
|
@@ -25,7 +25,7 @@ export const CHIP_VARIANTS = {
|
|
|
25
25
|
|
|
26
26
|
/** Suggestion chip for presenting options */
|
|
27
27
|
SUGGESTION: 'suggestion'
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Available sizes for the Chip component
|
|
@@ -35,4 +35,4 @@ export const CHIP_SIZES = {
|
|
|
35
35
|
SMALL: 'small',
|
|
36
36
|
MEDIUM: 'medium',
|
|
37
37
|
LARGE: 'large'
|
|
38
|
-
}
|
|
38
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/components/chip/index.
|
|
1
|
+
// src/components/chip/index.js
|
|
2
2
|
export { default } from './chip'
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
3
|
+
export { default as createChipSet } from './chip-set'
|
|
4
|
+
export { CHIP_VARIANTS, CHIP_SIZES } from './constants'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/components/switch/constants.
|
|
1
|
+
// src/components/switch/constants.ts
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Label position options
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export const SWITCH_LABEL_POSITION = {
|
|
7
7
|
START: 'start',
|
|
8
8
|
END: 'end'
|
|
9
|
-
}
|
|
9
|
+
} as const;
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Validation schema for switch configuration
|
|
@@ -56,7 +56,7 @@ export const SWITCH_SCHEMA = {
|
|
|
56
56
|
optional: true
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
} as const;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Switch state classes
|
|
@@ -65,7 +65,7 @@ export const SWITCH_STATES = {
|
|
|
65
65
|
CHECKED: 'checked',
|
|
66
66
|
DISABLED: 'disabled',
|
|
67
67
|
FOCUSED: 'focused'
|
|
68
|
-
}
|
|
68
|
+
} as const;
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Switch element classes
|
|
@@ -77,4 +77,4 @@ export const SWITCH_CLASSES = {
|
|
|
77
77
|
THUMB: 'thumb',
|
|
78
78
|
THUMB_ICON: 'thumb-icon',
|
|
79
79
|
LABEL: 'switch-label'
|
|
80
|
-
}
|
|
80
|
+
} as const;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @description Core utilities for component composition and creation with built-in mobile support
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { createElement, CreateElementOptions } from '../dom/create';
|
|
7
|
+
import { createElement, CreateElementOptions, removeEventHandlers } from '../dom/create';
|
|
8
8
|
import {
|
|
9
9
|
normalizeEvent,
|
|
10
10
|
hasTouchSupport,
|
|
@@ -138,7 +138,7 @@ export const createBase = (config: Record<string, any> = {}): BaseComponent => (
|
|
|
138
138
|
* @returns {Function} Component enhancer
|
|
139
139
|
*/
|
|
140
140
|
export const withElement = (options: WithElementOptions = {}) =>
|
|
141
|
-
(
|
|
141
|
+
<T extends BaseComponent>(component: T): T & ElementComponent => {
|
|
142
142
|
/**
|
|
143
143
|
* Handles the start of a touch interaction.
|
|
144
144
|
*/
|
|
@@ -146,8 +146,8 @@ export const withElement = (options: WithElementOptions = {}) =>
|
|
|
146
146
|
base.updateTouchState(event, 'start');
|
|
147
147
|
element.classList.add(`${base.getClass('touch-active')}`);
|
|
148
148
|
|
|
149
|
-
if (options.forwardEvents?.touchstart &&
|
|
150
|
-
|
|
149
|
+
if (options.forwardEvents?.touchstart && 'emit' in component) {
|
|
150
|
+
(component as any).emit('touchstart', normalizeEvent(event));
|
|
151
151
|
}
|
|
152
152
|
};
|
|
153
153
|
|
|
@@ -162,12 +162,12 @@ export const withElement = (options: WithElementOptions = {}) =>
|
|
|
162
162
|
base.updateTouchState(event, 'end');
|
|
163
163
|
|
|
164
164
|
// Emit tap event for short touches
|
|
165
|
-
if (touchDuration < TOUCH_CONFIG.TAP_THRESHOLD &&
|
|
166
|
-
|
|
165
|
+
if (touchDuration < TOUCH_CONFIG.TAP_THRESHOLD && 'emit' in component) {
|
|
166
|
+
(component as any).emit('tap', normalizeEvent(event));
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
if (options.forwardEvents?.touchend &&
|
|
170
|
-
|
|
169
|
+
if (options.forwardEvents?.touchend && 'emit' in component) {
|
|
170
|
+
(component as any).emit('touchend', normalizeEvent(event));
|
|
171
171
|
}
|
|
172
172
|
};
|
|
173
173
|
|
|
@@ -182,19 +182,22 @@ export const withElement = (options: WithElementOptions = {}) =>
|
|
|
182
182
|
const deltaY = normalized.clientY - base.touchState.startPosition.y;
|
|
183
183
|
|
|
184
184
|
// Detect and emit swipe gestures
|
|
185
|
-
if (Math.abs(deltaX) > TOUCH_CONFIG.SWIPE_THRESHOLD &&
|
|
186
|
-
|
|
185
|
+
if (Math.abs(deltaX) > TOUCH_CONFIG.SWIPE_THRESHOLD && 'emit' in component) {
|
|
186
|
+
(component as any).emit('swipe', {
|
|
187
187
|
direction: deltaX > 0 ? 'right' : 'left',
|
|
188
188
|
deltaX,
|
|
189
189
|
deltaY
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
if (options.forwardEvents?.touchmove &&
|
|
194
|
-
|
|
193
|
+
if (options.forwardEvents?.touchmove && 'emit' in component) {
|
|
194
|
+
(component as any).emit('touchmove', { ...normalized, deltaX, deltaY });
|
|
195
195
|
}
|
|
196
196
|
};
|
|
197
197
|
|
|
198
|
+
// Get the base component for reference
|
|
199
|
+
const base = component;
|
|
200
|
+
|
|
198
201
|
// Create element options from component options
|
|
199
202
|
const elementOptions: CreateElementOptions = {
|
|
200
203
|
tag: options.tag || 'div',
|
|
@@ -204,7 +207,8 @@ export const withElement = (options: WithElementOptions = {}) =>
|
|
|
204
207
|
options.className
|
|
205
208
|
].filter(Boolean),
|
|
206
209
|
attrs: options.attrs || {},
|
|
207
|
-
|
|
210
|
+
forwardEvents: options.forwardEvents || {},
|
|
211
|
+
context: component // Pass component as context for events
|
|
208
212
|
};
|
|
209
213
|
|
|
210
214
|
// Create the element with appropriate classes
|
|
@@ -218,7 +222,7 @@ export const withElement = (options: WithElementOptions = {}) =>
|
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
return {
|
|
221
|
-
...
|
|
225
|
+
...component,
|
|
222
226
|
element,
|
|
223
227
|
|
|
224
228
|
/**
|
|
@@ -241,6 +245,10 @@ export const withElement = (options: WithElementOptions = {}) =>
|
|
|
241
245
|
element.removeEventListener('touchend', handleTouchEnd);
|
|
242
246
|
element.removeEventListener('touchmove', handleTouchMove);
|
|
243
247
|
}
|
|
248
|
+
|
|
249
|
+
// Clean up any registered event handlers using our new utility
|
|
250
|
+
removeEventHandlers(element);
|
|
251
|
+
|
|
244
252
|
element.remove();
|
|
245
253
|
}
|
|
246
254
|
};
|