@sveltia/ui 0.35.0 → 0.35.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/dist/components/calendar/calendar.svelte +17 -25
- package/dist/components/select/combobox.svelte +10 -7
- package/dist/components/text-editor/constants.test.d.ts +1 -0
- package/dist/components/text-editor/constants.test.js +98 -0
- package/dist/components/text-editor/store.svelte.test.d.ts +1 -0
- package/dist/components/text-editor/store.svelte.test.js +196 -0
- package/dist/components/text-editor/transformers/hr.test.d.ts +1 -0
- package/dist/components/text-editor/transformers/hr.test.js +108 -0
- package/dist/components/text-editor/transformers/table.test.d.ts +1 -0
- package/dist/components/text-editor/transformers/table.test.js +28 -0
- package/dist/components/text-field/text-input.svelte +12 -6
- package/dist/components/toast/toast.svelte +7 -3
- package/dist/services/events.svelte.js +66 -8
- package/dist/services/events.test.d.ts +1 -0
- package/dist/services/events.test.js +221 -0
- package/dist/services/group.svelte.d.ts +1 -0
- package/dist/services/group.svelte.js +15 -10
- package/dist/services/group.test.d.ts +1 -0
- package/dist/services/group.test.js +763 -0
- package/dist/services/i18n.d.ts +6 -0
- package/dist/services/i18n.js +4 -2
- package/dist/services/i18n.test.d.ts +1 -0
- package/dist/services/i18n.test.js +106 -0
- package/dist/services/popup.svelte.d.ts +1 -0
- package/dist/services/popup.svelte.js +11 -2
- package/dist/services/popup.test.d.ts +1 -0
- package/dist/services/popup.test.js +536 -0
- package/dist/services/select.test.d.ts +1 -0
- package/dist/services/select.test.js +69 -0
- package/package.json +10 -9
|
@@ -2,13 +2,23 @@
|
|
|
2
2
|
* @import { ActionReturn } from 'svelte/action';
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
/** @type {boolean | undefined} */
|
|
6
|
+
let _isMac;
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* Check if the user agent is macOS.
|
|
7
10
|
* @returns {boolean} Result.
|
|
8
11
|
*/
|
|
9
|
-
export const isMac = () =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
export const isMac = () => {
|
|
13
|
+
_isMac ??=
|
|
14
|
+
/** @type {any} */ (navigator).userAgentData?.platform === 'macOS' ||
|
|
15
|
+
navigator.platform.startsWith('Mac');
|
|
16
|
+
|
|
17
|
+
return _isMac;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const MODIFIER_KEYS = ['Ctrl', 'Meta', 'Alt', 'Shift'];
|
|
21
|
+
const CODE_RE = /^(?:Digit|Key)(.)$/;
|
|
12
22
|
|
|
13
23
|
/**
|
|
14
24
|
* Whether the event matches the given keyboard shortcuts.
|
|
@@ -19,7 +29,13 @@ export const isMac = () =>
|
|
|
19
29
|
*/
|
|
20
30
|
export const matchShortcuts = (event, shortcuts) => {
|
|
21
31
|
const { ctrlKey, metaKey, altKey, shiftKey, code } = event;
|
|
22
|
-
|
|
32
|
+
|
|
33
|
+
// The `code` property can be `undefined` in some cases
|
|
34
|
+
if (!code) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const key = code.replace(CODE_RE, '$1');
|
|
23
39
|
|
|
24
40
|
return shortcuts.split(/\s+/).some((shortcut) => {
|
|
25
41
|
const keys = shortcut.split('+');
|
|
@@ -45,7 +61,7 @@ export const matchShortcuts = (event, shortcuts) => {
|
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
return keys
|
|
48
|
-
.filter((_key) => !
|
|
64
|
+
.filter((_key) => !MODIFIER_KEYS.includes(_key))
|
|
49
65
|
.every((_key) => _key.toUpperCase() === key.toUpperCase());
|
|
50
66
|
});
|
|
51
67
|
};
|
|
@@ -61,6 +77,12 @@ export const matchShortcuts = (event, shortcuts) => {
|
|
|
61
77
|
export const activateKeyShortcuts = (element, shortcuts = '') => {
|
|
62
78
|
/** @type {string | undefined} */
|
|
63
79
|
let platformKeyShortcuts;
|
|
80
|
+
/**
|
|
81
|
+
* Pre-parsed shortcuts for fast per-event matching without string allocations.
|
|
82
|
+
* @type {{ ctrl: boolean, meta: boolean, alt: boolean, shift: boolean, nonModifierKeys: string[]
|
|
83
|
+
* }[] | undefined}
|
|
84
|
+
*/
|
|
85
|
+
let parsedShortcuts;
|
|
64
86
|
|
|
65
87
|
/**
|
|
66
88
|
* Handle the event.
|
|
@@ -68,13 +90,33 @@ export const activateKeyShortcuts = (element, shortcuts = '') => {
|
|
|
68
90
|
*/
|
|
69
91
|
const handler = (event) => {
|
|
70
92
|
const { disabled, offsetParent } = element;
|
|
71
|
-
const { top, left } = element.getBoundingClientRect();
|
|
72
93
|
|
|
73
|
-
// Check
|
|
74
|
-
if (
|
|
94
|
+
// Check shortcut match and visibility first — no layout reflow until needed
|
|
95
|
+
if (
|
|
96
|
+
!offsetParent ||
|
|
97
|
+
!parsedShortcuts?.some(({ ctrl, meta, alt, shift, nonModifierKeys }) => {
|
|
98
|
+
const { ctrlKey, metaKey, altKey, shiftKey, code } = event;
|
|
99
|
+
|
|
100
|
+
if (!code) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const key = code.replace(CODE_RE, '$1').toUpperCase();
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
ctrl === ctrlKey &&
|
|
108
|
+
meta === metaKey &&
|
|
109
|
+
alt === altKey &&
|
|
110
|
+
shift === shiftKey &&
|
|
111
|
+
nonModifierKeys.every((k) => k === key)
|
|
112
|
+
);
|
|
113
|
+
})
|
|
114
|
+
) {
|
|
75
115
|
return;
|
|
76
116
|
}
|
|
77
117
|
|
|
118
|
+
const { top, left } = element.getBoundingClientRect();
|
|
119
|
+
|
|
78
120
|
if (disabled) {
|
|
79
121
|
// Make sure `elementsFromPoint()` works as expected
|
|
80
122
|
element.style.setProperty('pointer-events', 'auto');
|
|
@@ -113,8 +155,24 @@ export const activateKeyShortcuts = (element, shortcuts = '') => {
|
|
|
113
155
|
: undefined;
|
|
114
156
|
|
|
115
157
|
if (platformKeyShortcuts) {
|
|
158
|
+
parsedShortcuts = platformKeyShortcuts.split(/\s+/).map((shortcut) => {
|
|
159
|
+
const parts = shortcut.split('+');
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
ctrl: parts.includes('Ctrl'),
|
|
163
|
+
meta: parts.includes('Meta'),
|
|
164
|
+
alt: parts.includes('Alt'),
|
|
165
|
+
shift: parts.includes('Shift'),
|
|
166
|
+
nonModifierKeys: parts
|
|
167
|
+
.filter((k) => !MODIFIER_KEYS.includes(k))
|
|
168
|
+
.map((k) => k.toUpperCase()),
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
|
|
116
172
|
globalThis.addEventListener('keydown', handler, { capture: true });
|
|
117
173
|
element.setAttribute('aria-keyshortcuts', platformKeyShortcuts);
|
|
174
|
+
} else {
|
|
175
|
+
parsedShortcuts = undefined;
|
|
118
176
|
}
|
|
119
177
|
};
|
|
120
178
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { activateKeyShortcuts, isMac, matchShortcuts } from './events.svelte.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Helper to create a minimal KeyboardEvent-like object.
|
|
6
|
+
* @param {Partial<KeyboardEvent>} overrides Event property overrides.
|
|
7
|
+
* @returns {KeyboardEvent} A fake keyboard event.
|
|
8
|
+
*/
|
|
9
|
+
const makeEvent = (overrides = {}) =>
|
|
10
|
+
/** @type {KeyboardEvent} */ ({
|
|
11
|
+
ctrlKey: false,
|
|
12
|
+
metaKey: false,
|
|
13
|
+
altKey: false,
|
|
14
|
+
shiftKey: false,
|
|
15
|
+
code: 'KeyA',
|
|
16
|
+
...overrides,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('matchShortcuts', () => {
|
|
20
|
+
it('should match a plain key shortcut', () => {
|
|
21
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyS' }), 'S')).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should not match when the key differs', () => {
|
|
25
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyA' }), 'S')).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should match Ctrl+S', () => {
|
|
29
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyS', ctrlKey: true }), 'Ctrl+S')).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should not match Ctrl+S when Ctrl is not pressed', () => {
|
|
33
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyS' }), 'Ctrl+S')).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should not match Ctrl+S when an extra modifier is pressed', () => {
|
|
37
|
+
expect(
|
|
38
|
+
matchShortcuts(makeEvent({ code: 'KeyS', ctrlKey: true, shiftKey: true }), 'Ctrl+S'),
|
|
39
|
+
).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should match Shift+A', () => {
|
|
43
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyA', shiftKey: true }), 'Shift+A')).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should match Alt+F4', () => {
|
|
47
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyF', altKey: true }), 'Alt+F')).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should match any of multiple space-separated shortcuts', () => {
|
|
51
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyZ', ctrlKey: true }), 'Ctrl+Z Ctrl+Y')).toBe(true);
|
|
52
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyY', ctrlKey: true }), 'Ctrl+Z Ctrl+Y')).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should return false when code is empty', () => {
|
|
56
|
+
expect(matchShortcuts(makeEvent({ code: '' }), 'S')).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should match digit keys using Digit prefix', () => {
|
|
60
|
+
expect(matchShortcuts(makeEvent({ code: 'Digit1' }), '1')).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should be case-insensitive for the key', () => {
|
|
64
|
+
expect(matchShortcuts(makeEvent({ code: 'KeyS' }), 's')).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('isMac', () => {
|
|
69
|
+
it('should return a boolean', () => {
|
|
70
|
+
expect(typeof isMac()).toBe('boolean');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('activateKeyShortcuts', () => {
|
|
75
|
+
/** @type {HTMLButtonElement} */
|
|
76
|
+
let button;
|
|
77
|
+
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
button = /** @type {HTMLButtonElement} */ (document.createElement('button'));
|
|
80
|
+
document.body.appendChild(button);
|
|
81
|
+
|
|
82
|
+
// happy-dom doesn't expose document.elementsFromPoint as a configurable property;
|
|
83
|
+
// define a stub so vi.spyOn can wrap it in handler tests.
|
|
84
|
+
if (!document.elementsFromPoint) {
|
|
85
|
+
Object.defineProperty(document, 'elementsFromPoint', {
|
|
86
|
+
configurable: true,
|
|
87
|
+
writable: true,
|
|
88
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
89
|
+
value: () => /** @type {Element[]} */ ([]),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
afterEach(() => {
|
|
95
|
+
button.remove();
|
|
96
|
+
vi.restoreAllMocks();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should set aria-keyshortcuts when shortcuts are provided', () => {
|
|
100
|
+
const action = activateKeyShortcuts(button, 'Ctrl+S');
|
|
101
|
+
|
|
102
|
+
expect(button.getAttribute('aria-keyshortcuts')).toBe('Ctrl+S');
|
|
103
|
+
action.destroy?.();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should not set aria-keyshortcuts when no shortcuts are provided', () => {
|
|
107
|
+
const action = activateKeyShortcuts(button);
|
|
108
|
+
|
|
109
|
+
expect(button.getAttribute('aria-keyshortcuts')).toBeNull();
|
|
110
|
+
action.destroy?.();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should remove aria-keyshortcuts after destroy', () => {
|
|
114
|
+
const action = activateKeyShortcuts(button, 'Ctrl+S');
|
|
115
|
+
|
|
116
|
+
action.destroy?.();
|
|
117
|
+
expect(button.getAttribute('aria-keyshortcuts')).toBeNull();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should replace Accel with Meta or Ctrl depending on platform', () => {
|
|
121
|
+
const action = activateKeyShortcuts(button, 'Accel+S');
|
|
122
|
+
const attr = button.getAttribute('aria-keyshortcuts');
|
|
123
|
+
|
|
124
|
+
expect(attr === 'Meta+S' || attr === 'Ctrl+S').toBe(true);
|
|
125
|
+
action.destroy?.();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should re-register the original shortcuts when update() is called', () => {
|
|
129
|
+
// update() re-applies the same original shortcuts (param is intentionally ignored)
|
|
130
|
+
const action = activateKeyShortcuts(button, 'Ctrl+S');
|
|
131
|
+
|
|
132
|
+
/** @type {any} */ (action).update('Ctrl+Z');
|
|
133
|
+
expect(button.getAttribute('aria-keyshortcuts')).toBe('Ctrl+S');
|
|
134
|
+
action.destroy?.();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should keep no aria-keyshortcuts when update() is called on a no-shortcut action', () => {
|
|
138
|
+
const action = activateKeyShortcuts(button);
|
|
139
|
+
|
|
140
|
+
/** @type {any} */ (action).update('Ctrl+Z'); // original shortcuts was '' so still none
|
|
141
|
+
expect(button.getAttribute('aria-keyshortcuts')).toBeNull();
|
|
142
|
+
action.destroy?.();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should trigger click on element when matching shortcut key is pressed', () => {
|
|
146
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
147
|
+
Object.defineProperty(button, 'offsetParent', { configurable: true, get: () => document.body });
|
|
148
|
+
vi.spyOn(document, 'elementsFromPoint').mockReturnValue(/** @type {any} */ ([button]));
|
|
149
|
+
|
|
150
|
+
const clickSpy = vi.fn();
|
|
151
|
+
|
|
152
|
+
button.addEventListener('click', clickSpy);
|
|
153
|
+
activateKeyShortcuts(button, 'Ctrl+S');
|
|
154
|
+
globalThis.dispatchEvent(
|
|
155
|
+
new KeyboardEvent('keydown', { code: 'KeyS', ctrlKey: true, bubbles: true }),
|
|
156
|
+
);
|
|
157
|
+
expect(clickSpy).toHaveBeenCalledOnce();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should not trigger click when a non-matching key is pressed', () => {
|
|
161
|
+
// Handler returns early before reaching elementsFromPoint when shortcut doesn't match
|
|
162
|
+
const clickSpy = vi.fn();
|
|
163
|
+
|
|
164
|
+
button.addEventListener('click', clickSpy);
|
|
165
|
+
activateKeyShortcuts(button, 'Ctrl+S');
|
|
166
|
+
globalThis.dispatchEvent(
|
|
167
|
+
new KeyboardEvent('keydown', { code: 'KeyZ', ctrlKey: true, bubbles: true }),
|
|
168
|
+
);
|
|
169
|
+
expect(clickSpy).not.toHaveBeenCalled();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should not trigger click when element is not in elementsFromPoint result', () => {
|
|
173
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
174
|
+
Object.defineProperty(button, 'offsetParent', { configurable: true, get: () => document.body });
|
|
175
|
+
vi.spyOn(document, 'elementsFromPoint').mockReturnValue(/** @type {any} */ ([]));
|
|
176
|
+
|
|
177
|
+
const clickSpy = vi.fn();
|
|
178
|
+
|
|
179
|
+
button.addEventListener('click', clickSpy);
|
|
180
|
+
activateKeyShortcuts(button, 'Ctrl+S');
|
|
181
|
+
globalThis.dispatchEvent(
|
|
182
|
+
new KeyboardEvent('keydown', { code: 'KeyS', ctrlKey: true, bubbles: true }),
|
|
183
|
+
);
|
|
184
|
+
expect(clickSpy).not.toHaveBeenCalled();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should not trigger click when the event code is empty (covers inner return false branch)', () => {
|
|
188
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
189
|
+
Object.defineProperty(button, 'offsetParent', { configurable: true, get: () => document.body });
|
|
190
|
+
activateKeyShortcuts(button, 'Ctrl+S');
|
|
191
|
+
|
|
192
|
+
const clickSpy = vi.fn();
|
|
193
|
+
|
|
194
|
+
button.addEventListener('click', clickSpy);
|
|
195
|
+
// Dispatch with empty code — the parsedShortcuts.some() callback returns false (line 101)
|
|
196
|
+
globalThis.dispatchEvent(
|
|
197
|
+
new KeyboardEvent('keydown', { code: '', ctrlKey: true, bubbles: true }),
|
|
198
|
+
);
|
|
199
|
+
expect(clickSpy).not.toHaveBeenCalled();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should manipulate pointer-events for disabled button but not trigger click', () => {
|
|
203
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
204
|
+
Object.defineProperty(button, 'offsetParent', { configurable: true, get: () => document.body });
|
|
205
|
+
vi.spyOn(document, 'elementsFromPoint').mockReturnValue(/** @type {any} */ ([button]));
|
|
206
|
+
button.disabled = true;
|
|
207
|
+
|
|
208
|
+
const clickSpy = vi.fn();
|
|
209
|
+
const setPropertySpy = vi.spyOn(button.style, 'setProperty');
|
|
210
|
+
const removePropertySpy = vi.spyOn(button.style, 'removeProperty');
|
|
211
|
+
|
|
212
|
+
button.addEventListener('click', clickSpy);
|
|
213
|
+
activateKeyShortcuts(button, 'Ctrl+S');
|
|
214
|
+
globalThis.dispatchEvent(
|
|
215
|
+
new KeyboardEvent('keydown', { code: 'KeyS', ctrlKey: true, bubbles: true }),
|
|
216
|
+
);
|
|
217
|
+
expect(clickSpy).not.toHaveBeenCalled();
|
|
218
|
+
expect(setPropertySpy).toHaveBeenCalledWith('pointer-events', 'auto');
|
|
219
|
+
expect(removePropertySpy).toHaveBeenCalledWith('pointer-events');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -8,24 +8,28 @@ import { get } from 'svelte/store';
|
|
|
8
8
|
import { isRTL } from './i18n.js';
|
|
9
9
|
import { getSelectedItemDetail } from './select.svelte.js';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Diacritic characters regex for normalization. We use a regex instead of `Intl` APIs for better
|
|
13
|
+
* performance, since `transliterate` is slow and we only need basic normalization.
|
|
14
|
+
*/
|
|
15
|
+
const DIACRITIC_RE = /\p{Diacritic}/gu;
|
|
16
|
+
|
|
11
17
|
/**
|
|
12
18
|
* Normalize the given string for search value comparison. Since `transliterate` is slow, we only
|
|
13
19
|
* apply basic normalization.
|
|
20
|
+
* @internal
|
|
14
21
|
* @param {string} value Original value.
|
|
15
22
|
* @returns {string} Normalized value.
|
|
16
23
|
* @todo Move this to `@sveltia/utils`.
|
|
17
24
|
*/
|
|
18
|
-
const normalize = (value) => {
|
|
25
|
+
export const normalize = (value) => {
|
|
19
26
|
value = value.trim();
|
|
20
27
|
|
|
21
28
|
if (!value) {
|
|
22
29
|
return '';
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
return value
|
|
26
|
-
.normalize('NFD')
|
|
27
|
-
.replace(/\p{Diacritic}/gu, '')
|
|
28
|
-
.toLocaleLowerCase();
|
|
32
|
+
return value.normalize('NFD').replace(DIACRITIC_RE, '').toLocaleLowerCase();
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
/**
|
|
@@ -472,8 +476,8 @@ class Group {
|
|
|
472
476
|
newTarget = activeMembers[index - 1];
|
|
473
477
|
}
|
|
474
478
|
|
|
475
|
-
if (index
|
|
476
|
-
// Last member
|
|
479
|
+
if (index <= 0) {
|
|
480
|
+
// Last member (also handles the case when nothing is focused, index === -1)
|
|
477
481
|
newTarget = activeMembers[activeMembers.length - 1];
|
|
478
482
|
}
|
|
479
483
|
}
|
|
@@ -503,8 +507,9 @@ class Group {
|
|
|
503
507
|
onUpdate({ searchTerms }) {
|
|
504
508
|
const terms = normalize(searchTerms);
|
|
505
509
|
const _terms = terms ? terms.split(/\s+/) : [];
|
|
510
|
+
const { allMembers, parent } = this;
|
|
506
511
|
|
|
507
|
-
const matched =
|
|
512
|
+
const matched = allMembers
|
|
508
513
|
.map((member) => {
|
|
509
514
|
const searchValue = normalize(
|
|
510
515
|
member.dataset.searchValue ??
|
|
@@ -522,8 +527,8 @@ class Group {
|
|
|
522
527
|
})
|
|
523
528
|
.filter((hidden) => !hidden).length;
|
|
524
529
|
|
|
525
|
-
|
|
526
|
-
new CustomEvent('Filter', { detail: { matched, total:
|
|
530
|
+
parent.dispatchEvent(
|
|
531
|
+
new CustomEvent('Filter', { detail: { matched, total: allMembers.length } }),
|
|
527
532
|
);
|
|
528
533
|
}
|
|
529
534
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|