@flux-ui/components 3.0.0-next.33 → 3.0.0-next.35

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 (44) hide show
  1. package/dist/component/FluxBoxedIcon.vue.d.ts +1 -5
  2. package/dist/component/FluxCommandPalette.vue.d.ts +52 -0
  3. package/dist/component/FluxCommandPaletteGroup.vue.d.ts +8 -0
  4. package/dist/component/FluxCommandPaletteItem.vue.d.ts +18 -0
  5. package/dist/component/FluxFormDateRangeInput.vue.d.ts +1 -5
  6. package/dist/component/FluxTag.vue.d.ts +1 -0
  7. package/dist/component/index.d.ts +4 -1
  8. package/dist/composable/private/useCommandPalette.d.ts +38 -0
  9. package/dist/index.css +320 -0
  10. package/dist/index.js +686 -120
  11. package/dist/index.js.map +1 -1
  12. package/package.json +10 -10
  13. package/src/component/FluxBoxedIcon.vue +0 -4
  14. package/src/component/FluxCalendar.vue +2 -1
  15. package/src/component/FluxChip.vue +1 -1
  16. package/src/component/FluxColorPicker.vue +1 -1
  17. package/src/component/FluxColorSelect.vue +3 -3
  18. package/src/component/FluxCommandPalette.vue +290 -0
  19. package/src/component/FluxCommandPaletteGroup.vue +23 -0
  20. package/src/component/FluxCommandPaletteItem.vue +60 -0
  21. package/src/component/FluxDatePicker.vue +0 -1
  22. package/src/component/FluxFader.vue +2 -2
  23. package/src/component/FluxFocalPointEditor.vue +3 -3
  24. package/src/component/FluxFormDateRangeInput.vue +0 -5
  25. package/src/component/FluxFormDateTimeInput.vue +2 -2
  26. package/src/component/FluxFormSelect.vue +2 -2
  27. package/src/component/FluxFormTextArea.vue +1 -0
  28. package/src/component/FluxQuantitySelector.vue +3 -6
  29. package/src/component/FluxSnackbar.vue +5 -3
  30. package/src/component/FluxTableActions.vue +3 -3
  31. package/src/component/FluxTag.vue +3 -1
  32. package/src/component/index.ts +4 -1
  33. package/src/component/primitive/CoordinatePicker.vue +10 -11
  34. package/src/component/primitive/FilterItem.vue +11 -2
  35. package/src/component/primitive/SelectBase.vue +2 -2
  36. package/src/component/primitive/SliderBase.vue +10 -13
  37. package/src/composable/private/useCommandPalette.ts +405 -0
  38. package/src/composable/private/useFormSelect.ts +2 -2
  39. package/src/css/component/Badge.module.scss +7 -0
  40. package/src/css/component/CommandPalette.module.scss +332 -0
  41. package/src/css/reset.scss +0 -107
  42. package/src/css/variables.scss +0 -26
  43. /package/dist/component/{FluxActions.vue.d.ts → FluxActionStack.vue.d.ts} +0 -0
  44. /package/src/component/{FluxActions.vue → FluxActionStack.vue} +0 -0
@@ -1,7 +1,7 @@
1
1
  export { default as FluxAction } from './FluxAction.vue';
2
2
  export { default as FluxActionBar } from './FluxActionBar.vue';
3
3
  export { default as FluxActionPane } from './FluxActionPane.vue';
4
- export { default as FluxActions } from './FluxActions.vue';
4
+ export { default as FluxActionStack } from './FluxActionStack.vue';
5
5
  export { default as FluxAnimatedColors } from './FluxAnimatedColors.vue';
6
6
  export { default as FluxAspectRatio } from './FluxAspectRatio.vue';
7
7
  export { default as FluxAutoGrid } from './FluxAutoGrid.vue';
@@ -18,6 +18,9 @@ export { default as FluxCalendarEvent } from './FluxCalendarEvent.vue';
18
18
  export { default as FluxCheckbox } from './FluxCheckbox.vue';
19
19
  export { default as FluxChip } from './FluxChip.vue';
20
20
  export { default as FluxClickablePane } from './FluxClickablePane.vue';
21
+ export { default as FluxCommandPalette } from './FluxCommandPalette.vue';
22
+ export { default as FluxCommandPaletteGroup } from './FluxCommandPaletteGroup.vue';
23
+ export { default as FluxCommandPaletteItem } from './FluxCommandPaletteItem.vue';
21
24
  export { default as FluxComment } from './FluxComment.vue';
22
25
  export { default as FluxColorPicker } from './FluxColorPicker.vue';
23
26
  export { default as FluxColorSelect } from './FluxColorSelect.vue';
@@ -19,7 +19,7 @@
19
19
  setup>
20
20
  import { roundStep } from '@basmilius/utils';
21
21
  import { unrefTemplateElement } from '@flux-ui/internals';
22
- import { computed, onMounted, onUnmounted, ref, toRef, unref, useTemplateRef, watch } from 'vue';
22
+ import { computed, onUnmounted, ref, toRef, unref, useTemplateRef, watch } from 'vue';
23
23
  import { useDisabled } from '$flux/composable';
24
24
  import CoordinatePickerThumb from './CoordinatePickerThumb.vue';
25
25
  import $style from '$flux/css/component/primitive/CoordinatePicker.module.scss';
@@ -58,16 +58,6 @@
58
58
  (unref(modelValue)[1] - unref(min)[1]) / (unref(max)[1] - unref(min)[1])
59
59
  ]);
60
60
 
61
- onMounted(() => {
62
- document.addEventListener('pointermove', onPointerMove);
63
- document.addEventListener('pointerup', onPointerUp, {passive: true});
64
- });
65
-
66
- onUnmounted(() => {
67
- document.removeEventListener('pointermove', onPointerMove);
68
- document.removeEventListener('pointerup', onPointerUp);
69
- });
70
-
71
61
  function onDecrement(x: boolean, y: boolean): void {
72
62
  if (unref(disabled)) {
73
63
  return;
@@ -116,6 +106,8 @@
116
106
  }
117
107
 
118
108
  isDragging.value = true;
109
+ document.addEventListener('pointermove', onPointerMove);
110
+ document.addEventListener('pointerup', onPointerUp, {passive: true});
119
111
  requestAnimationFrame(() => onPointerMove(evt));
120
112
  }
121
113
 
@@ -149,7 +141,14 @@
149
141
 
150
142
  function onPointerUp(): void {
151
143
  isDragging.value = false;
144
+ document.removeEventListener('pointermove', onPointerMove);
145
+ document.removeEventListener('pointerup', onPointerUp);
152
146
  }
153
147
 
154
148
  watch(isDragging, isDragging => emit('dragging', isDragging));
149
+
150
+ onUnmounted(() => {
151
+ document.removeEventListener('pointermove', onPointerMove);
152
+ document.removeEventListener('pointerup', onPointerUp);
153
+ });
155
154
  </script>
@@ -38,7 +38,16 @@
38
38
  emit('click', evt);
39
39
  }
40
40
 
41
- watch(() => item, async () => {
42
- valueLabel.value = await unref(getValueLabel)(value) ?? undefined;
41
+ watch([() => item, () => value], async ([, nextValue], _prev, onCleanup) => {
42
+ let cancelled = false;
43
+ onCleanup(() => {
44
+ cancelled = true;
45
+ });
46
+
47
+ const nextLabel = await unref(getValueLabel)(nextValue);
48
+
49
+ if (!cancelled) {
50
+ valueLabel.value = nextLabel ?? undefined;
51
+ }
43
52
  }, {deep: true, immediate: true});
44
53
  </script>
@@ -286,8 +286,8 @@
286
286
  return;
287
287
 
288
288
  default:
289
- if (evt.key.match(/[a-z]/)) {
290
- highlightedIndex.value = unref(rawOptions).findIndex(o => o.label.toLowerCase().startsWith(evt.key));
289
+ if (evt.key.length === 1) {
290
+ highlightedIndex.value = unref(rawOptions).findIndex(o => o.label.toLowerCase().startsWith(evt.key.toLowerCase()));
291
291
  } else {
292
292
  highlightedIndex.value = -1;
293
293
  }
@@ -22,7 +22,7 @@
22
22
  setup>
23
23
  import { unrefTemplateElement } from '@flux-ui/internals';
24
24
  import { clsx } from 'clsx';
25
- import { onMounted, onUnmounted, toRef, unref, useTemplateRef, watch } from 'vue';
25
+ import { onUnmounted, toRef, unref, useTemplateRef } from 'vue';
26
26
  import { useDisabled } from '$flux/composable';
27
27
  import FluxTicks from '$flux/component/FluxTicks.vue';
28
28
  import $style from '$flux/css/component/primitive/Slider.module.scss';
@@ -47,22 +47,14 @@
47
47
  const disabled = useDisabled(toRef(() => componentDisabled));
48
48
  const rootRef = useTemplateRef('root');
49
49
 
50
- onMounted(() => {
51
- document.addEventListener('pointermove', onPointerMove);
52
- document.addEventListener('pointerup', onPointerUp, {passive: true});
53
- });
54
-
55
- onUnmounted(() => {
56
- document.removeEventListener('pointermove', onPointerMove);
57
- document.removeEventListener('pointerup', onPointerUp);
58
- });
59
-
60
50
  function onPointerDown(evt: PointerEvent): void {
61
51
  if (unref(disabled)) {
62
52
  return;
63
53
  }
64
54
 
65
55
  emit('dragging', true);
56
+ document.addEventListener('pointermove', onPointerMove);
57
+ document.addEventListener('pointerup', onPointerUp, {passive: true});
66
58
  requestAnimationFrame(() => onPointerMove(evt));
67
59
  }
68
60
 
@@ -77,13 +69,18 @@
77
69
  left += 6; // margin.
78
70
  width -= 12; // margin times two.
79
71
 
80
- emit('update', Math.max(0, Math.min(1, (evt.pageX - left) / width)));
72
+ emit('update', Math.max(0, Math.min(1, (evt.clientX - left) / width)));
81
73
  evt.preventDefault();
82
74
  }
83
75
 
84
76
  function onPointerUp(): void {
85
77
  emit('dragging', false);
78
+ document.removeEventListener('pointermove', onPointerMove);
79
+ document.removeEventListener('pointerup', onPointerUp);
86
80
  }
87
81
 
88
- watch(() => isDragging, () => emit('dragging', isDragging));
82
+ onUnmounted(() => {
83
+ document.removeEventListener('pointermove', onPointerMove);
84
+ document.removeEventListener('pointerup', onPointerUp);
85
+ });
89
86
  </script>
@@ -0,0 +1,405 @@
1
+ import type { FluxCommandSource, FluxCommandSourceItem, FluxCommandSubAction } from '@flux-ui/types';
2
+ import { useDebouncedRef } from '@basmilius/common';
3
+ import { computed, nextTick, type Ref, ref, unref, watch } from 'vue';
4
+
5
+ export type CommandPaletteResultItem = {
6
+ readonly globalIndex: number;
7
+ readonly sourceKey: string;
8
+ readonly sourceLabel: string;
9
+ readonly item: FluxCommandSourceItem;
10
+ };
11
+
12
+ export type CommandPaletteGroup = {
13
+ readonly sourceKey: string;
14
+ readonly sourceLabel: string;
15
+ readonly startIndex: number;
16
+ readonly items: CommandPaletteResultItem[];
17
+ };
18
+
19
+ export function useCommandPalette(params: {
20
+ readonly sources: Ref<FluxCommandSource[]>;
21
+ readonly itemRefs: Readonly<Ref<Array<{ readonly $el: HTMLElement; }> | null | undefined>>;
22
+ }): {
23
+ readonly search: Ref<string>;
24
+ readonly activeTab: Ref<string | null>;
25
+ readonly highlightedIndex: Ref<number>;
26
+ readonly isLoading: Ref<boolean>;
27
+ readonly isTransitioningBack: Ref<boolean>;
28
+ readonly subActionTarget: Ref<FluxCommandSourceItem | null>;
29
+ readonly filteredItems: Ref<CommandPaletteResultItem[]>;
30
+ readonly groupedItems: Ref<CommandPaletteGroup[]>;
31
+ readonly subActions: Ref<FluxCommandSubAction[]>;
32
+ readonly activeTabSource: Ref<FluxCommandSource | null>;
33
+ readonly tabs: Ref<FluxCommandSource[]>;
34
+ readonly totalItems: Ref<number>;
35
+ setSearch: (value: string) => void;
36
+ setActiveTab: (key: string | null) => void;
37
+ enterSubActions: (item: FluxCommandSourceItem) => void;
38
+ onKeyNavigate: (evt: KeyboardEvent, onClose: () => void, onActivate: (item: FluxCommandSourceItem) => void) => void;
39
+ reset: () => void;
40
+ } {
41
+ const search = ref('');
42
+ const activeTab = ref<string | null>(null);
43
+ const highlightedIndex = ref(-1);
44
+ const subActionTarget = ref<FluxCommandSourceItem | null>(null);
45
+ const isKeyboardNav = ref(false);
46
+ const isLoading = ref(false);
47
+ const isTransitioningBack = ref(false);
48
+ const savedState = ref<{ readonly search: string; readonly highlightedIndex: number; } | null>(null);
49
+ const asyncResults = ref<Map<string, FluxCommandSourceItem[]>>(new Map());
50
+ const debouncedSearch = useDebouncedRef(search, 300);
51
+ let fetchGeneration = 0;
52
+
53
+ const filteredItems = computed<CommandPaletteResultItem[]>(() => {
54
+ const query = unref(search).toLowerCase().trim();
55
+ const tab = unref(activeTab);
56
+ const sources = unref(params.sources);
57
+ const asyncMap = unref(asyncResults);
58
+ const results: CommandPaletteResultItem[] = [];
59
+
60
+ for (const source of sources) {
61
+ if (tab && source.key !== tab) {
62
+ continue;
63
+ }
64
+
65
+ if (source.fetchSearch) {
66
+ const items = query ? asyncMap.get(source.key) : source.items;
67
+
68
+ if (items) {
69
+ for (const item of items) {
70
+ results.push({
71
+ globalIndex: results.length,
72
+ sourceKey: source.key,
73
+ sourceLabel: source.label,
74
+ item
75
+ });
76
+ }
77
+ }
78
+
79
+ continue;
80
+ }
81
+
82
+ for (const item of source.items) {
83
+ if (query && !item.label.toLowerCase().includes(query) && !(item.subLabel?.toLowerCase().includes(query) ?? false)) {
84
+ continue;
85
+ }
86
+
87
+ results.push({
88
+ globalIndex: results.length,
89
+ sourceKey: source.key,
90
+ sourceLabel: source.label,
91
+ item
92
+ });
93
+ }
94
+ }
95
+
96
+ return results;
97
+ });
98
+
99
+ const groupedItems = computed<CommandPaletteGroup[]>(() => {
100
+ const items = unref(filteredItems);
101
+ const groups = new Map<string, CommandPaletteGroup>();
102
+
103
+ for (const result of items) {
104
+ if (!groups.has(result.sourceKey)) {
105
+ groups.set(result.sourceKey, {
106
+ sourceKey: result.sourceKey,
107
+ sourceLabel: result.sourceLabel,
108
+ startIndex: result.globalIndex,
109
+ items: []
110
+ });
111
+ }
112
+
113
+ (groups.get(result.sourceKey)!.items as CommandPaletteResultItem[]).push(result);
114
+ }
115
+
116
+ return Array.from(groups.values());
117
+ });
118
+
119
+ const tabs = computed(() => unref(params.sources).filter(source => source.tab));
120
+
121
+ const activeTabSource = computed<FluxCommandSource | null>(() => {
122
+ const tab = unref(activeTab);
123
+
124
+ if (!tab) {
125
+ return null;
126
+ }
127
+
128
+ return unref(tabs).find(source => source.key === tab) ?? null;
129
+ });
130
+
131
+ const subActions = computed(() => {
132
+ const actions = unref(subActionTarget)?.subActions ?? [];
133
+ const query = unref(search).toLowerCase().trim();
134
+
135
+ if (!query) {
136
+ return actions;
137
+ }
138
+
139
+ return actions.filter(action => action.label.toLowerCase().includes(query));
140
+ });
141
+
142
+ const totalItems = computed(() => {
143
+ if (unref(subActionTarget)) {
144
+ return unref(subActions).length;
145
+ }
146
+
147
+ return unref(filteredItems).length;
148
+ });
149
+
150
+ function setSearch(value: string): void {
151
+ search.value = value;
152
+ highlightedIndex.value = value.trim() ? 0 : -1;
153
+
154
+ if (value.trim() && !unref(subActionTarget) && unref(params.sources).some(s => s.fetchSearch)) {
155
+ isLoading.value = true;
156
+ } else {
157
+ isLoading.value = false;
158
+ }
159
+ }
160
+
161
+ function setActiveTab(key: string | null): void {
162
+ const allTabs = unref(tabs);
163
+ const tabKeys: (string | null)[] = [null, ...allTabs.map(t => t.key)];
164
+
165
+ isTransitioningBack.value = tabKeys.indexOf(key) < tabKeys.indexOf(unref(activeTab));
166
+ activeTab.value = key;
167
+ search.value = '';
168
+ highlightedIndex.value = -1;
169
+ subActionTarget.value = null;
170
+ }
171
+
172
+ function selectHighlighted(onClose: () => void, onActivate: (item: FluxCommandSourceItem) => void): void {
173
+ const index = unref(highlightedIndex);
174
+ const target = unref(subActionTarget);
175
+
176
+ if (target) {
177
+ const action = unref(subActions)[index];
178
+
179
+ if (action) {
180
+ action.onActivate();
181
+ onClose();
182
+ }
183
+
184
+ return;
185
+ }
186
+
187
+ const result = unref(filteredItems)[index];
188
+
189
+ if (!result) {
190
+ return;
191
+ }
192
+
193
+ if (result.item.subActions?.length) {
194
+ enterSubActions(result.item);
195
+ } else {
196
+ result.item.onActivate();
197
+ onActivate(result.item);
198
+ }
199
+ }
200
+
201
+ function enterSubActions(item: FluxCommandSourceItem): void {
202
+ savedState.value = {search: unref(search), highlightedIndex: unref(highlightedIndex)};
203
+ subActionTarget.value = item;
204
+ search.value = '';
205
+ highlightedIndex.value = 0;
206
+ }
207
+
208
+ function navigateTab(direction: number): void {
209
+ const allTabs = unref(tabs);
210
+
211
+ if (allTabs.length === 0) {
212
+ return;
213
+ }
214
+
215
+ const tabKeys: (string | null)[] = [null, ...allTabs.map(tab => tab.key)];
216
+ const currentIndex = tabKeys.indexOf(unref(activeTab));
217
+ const nextIndex = (currentIndex + direction + tabKeys.length) % tabKeys.length;
218
+
219
+ setActiveTab(tabKeys[nextIndex]);
220
+ }
221
+
222
+ function restoreState(): void {
223
+ subActionTarget.value = null;
224
+ const state = unref(savedState);
225
+
226
+ if (state) {
227
+ search.value = state.search;
228
+ isKeyboardNav.value = true;
229
+ highlightedIndex.value = state.highlightedIndex;
230
+ savedState.value = null;
231
+ } else {
232
+ highlightedIndex.value = -1;
233
+ }
234
+ }
235
+
236
+ function onKeyNavigate(evt: KeyboardEvent, onClose: () => void, onActivate: (item: FluxCommandSourceItem) => void): void {
237
+ const total = unref(totalItems);
238
+ const current = unref(highlightedIndex);
239
+
240
+ switch (evt.key) {
241
+ case 'ArrowDown':
242
+ evt.preventDefault();
243
+ isKeyboardNav.value = true;
244
+ highlightedIndex.value = Math.min(total - 1, current + 1);
245
+ break;
246
+
247
+ case 'ArrowUp':
248
+ evt.preventDefault();
249
+ isKeyboardNav.value = true;
250
+ highlightedIndex.value = total === 0 ? -1 : Math.max(0, current - 1);
251
+ break;
252
+
253
+ case 'ArrowLeft':
254
+ if (!unref(search) && !unref(subActionTarget)) {
255
+ evt.preventDefault();
256
+ navigateTab(-1);
257
+ }
258
+
259
+ break;
260
+
261
+ case 'ArrowRight':
262
+ if (!unref(search) && !unref(subActionTarget)) {
263
+ evt.preventDefault();
264
+ navigateTab(1);
265
+ }
266
+
267
+ break;
268
+
269
+ case 'Enter':
270
+ evt.preventDefault();
271
+ selectHighlighted(onClose, onActivate);
272
+ break;
273
+
274
+ case 'Escape':
275
+ evt.preventDefault();
276
+
277
+ if (unref(subActionTarget)) {
278
+ restoreState();
279
+ } else {
280
+ onClose();
281
+ }
282
+
283
+ break;
284
+
285
+ case 'Backspace':
286
+ if (!unref(search)) {
287
+ if (unref(subActionTarget)) {
288
+ evt.preventDefault();
289
+ restoreState();
290
+ } else if (unref(activeTab)) {
291
+ evt.preventDefault();
292
+ activeTab.value = null;
293
+ highlightedIndex.value = -1;
294
+ }
295
+ }
296
+
297
+ break;
298
+ }
299
+ }
300
+
301
+ function reset(): void {
302
+ search.value = '';
303
+ activeTab.value = null;
304
+ highlightedIndex.value = -1;
305
+ subActionTarget.value = null;
306
+ asyncResults.value = new Map();
307
+ isLoading.value = false;
308
+ fetchGeneration++;
309
+ }
310
+
311
+ watch(debouncedSearch, async (query) => {
312
+ if (unref(subActionTarget)) {
313
+ return;
314
+ }
315
+
316
+ const trimmed = query.trim();
317
+
318
+ if (!trimmed) {
319
+ asyncResults.value = new Map();
320
+ isLoading.value = false;
321
+ return;
322
+ }
323
+
324
+ const tab = unref(activeTab);
325
+ const sources = unref(params.sources);
326
+ const asyncSources = sources.filter(s => {
327
+ if (!s.fetchSearch) {
328
+ return false;
329
+ }
330
+
331
+ return !tab || s.key === tab;
332
+ });
333
+
334
+ if (asyncSources.length === 0) {
335
+ isLoading.value = false;
336
+ return;
337
+ }
338
+
339
+ const generation = ++fetchGeneration;
340
+ isLoading.value = true;
341
+
342
+ try {
343
+ const fetched = await Promise.all(
344
+ asyncSources.map(async (source) => ({
345
+ key: source.key,
346
+ items: await source.fetchSearch!(trimmed)
347
+ }))
348
+ );
349
+
350
+ if (generation !== fetchGeneration) {
351
+ return;
352
+ }
353
+
354
+ const map = new Map<string, FluxCommandSourceItem[]>();
355
+
356
+ for (const {key, items} of fetched) {
357
+ map.set(key, items);
358
+ }
359
+
360
+ asyncResults.value = map;
361
+ } finally {
362
+ if (generation === fetchGeneration) {
363
+ isLoading.value = false;
364
+ }
365
+ }
366
+ });
367
+
368
+ watch(highlightedIndex, (index) => {
369
+ if (index < 0 || !unref(isKeyboardNav)) {
370
+ return;
371
+ }
372
+
373
+ isKeyboardNav.value = false;
374
+
375
+ nextTick(() => unref(params.itemRefs)?.[index]?.$el?.scrollIntoView({block: 'nearest'}));
376
+ });
377
+
378
+ watch(totalItems, (total) => {
379
+ const current = unref(highlightedIndex);
380
+
381
+ if (current >= total) {
382
+ highlightedIndex.value = Math.max(-1, total - 1);
383
+ }
384
+ });
385
+
386
+ return {
387
+ search,
388
+ activeTab,
389
+ activeTabSource,
390
+ highlightedIndex,
391
+ isLoading,
392
+ isTransitioningBack,
393
+ subActionTarget,
394
+ filteredItems,
395
+ groupedItems,
396
+ subActions,
397
+ tabs,
398
+ totalItems,
399
+ setSearch,
400
+ setActiveTab,
401
+ enterSubActions,
402
+ onKeyNavigate,
403
+ reset
404
+ };
405
+ }
@@ -14,8 +14,8 @@ export default function (modelValue: Ref<FluxFormSelectValue>, isMultiple: boole
14
14
  const search = unref(searchQuery)?.trim().toLowerCase();
15
15
 
16
16
  const available = unref(options)
17
- .filter(o => !('value' in o) || (!search || o.label.toLowerCase().includes(search)))
18
- .filter(o => !('value' in o) || !isMultiple || !unref(selected).find(s => s.value === o.value));
17
+ .filter(o => isFluxFormSelectGroup(o) || (!search || o.label.toLowerCase().includes(search)))
18
+ .filter(o => isFluxFormSelectGroup(o) || !isMultiple || !unref(selected).find(s => s.value === o.value));
19
19
 
20
20
  if (available.length === 0) {
21
21
  return [];
@@ -142,6 +142,13 @@
142
142
  font-weight: 400;
143
143
  }
144
144
 
145
+ .tagKeyboardShortcut {
146
+ composes: tag;
147
+
148
+ padding-left: 3px;
149
+ padding-right: 3px;
150
+ }
151
+
145
152
  .tagClose {
146
153
  composes: badgeClose;
147
154