@flux-ui/components 3.0.0-next.57 → 3.0.0-next.58

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flux-ui/components",
3
3
  "description": "A set of opiniated UI components.",
4
- "version": "3.0.0-next.57",
4
+ "version": "3.0.0-next.58",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/basmilius",
@@ -49,8 +49,8 @@
49
49
  "dependencies": {
50
50
  "@basmilius/common": "^3.21.0",
51
51
  "@basmilius/utils": "^3.21.0",
52
- "@flux-ui/internals": "3.0.0-next.57",
53
- "@flux-ui/types": "3.0.0-next.57",
52
+ "@flux-ui/internals": "3.0.0-next.58",
53
+ "@flux-ui/types": "3.0.0-next.58",
54
54
  "@fortawesome/fontawesome-common-types": "^7.2.0",
55
55
  "clsx": "^2.1.1",
56
56
  "imask": "^7.6.1",
@@ -1,25 +1,103 @@
1
1
  <template>
2
- <div :class="$style.kanban">
2
+ <div
3
+ ref="root"
4
+ role="application"
5
+ aria-roledescription="Kanban board"
6
+ :aria-label="ariaLabel"
7
+ :class="[$style.kanban, isDragging && $style.isBoardDragging]">
3
8
  <slot/>
9
+
10
+ <div
11
+ aria-live="polite"
12
+ aria-atomic="true"
13
+ :class="$style.kanbanLiveRegion">
14
+ {{ liveMessage }}
15
+ </div>
4
16
  </div>
5
17
  </template>
6
18
 
7
19
  <script
8
20
  lang="ts"
9
21
  setup>
10
- import type { FluxKanbanMoveEvent } from '@flux-ui/types';
11
- import { provide } from 'vue';
12
- import { FluxKanbanInjectionKey } from '$flux/data/di';
22
+ import type { FluxKanbanMoveColumnEvent, FluxKanbanMoveEvent } from '@flux-ui/types';
23
+ import { computed, onBeforeUnmount, onMounted, provide, ref, toRef, useTemplateRef, watch } from 'vue';
24
+ import { FluxDisabledInjectionKey, FluxKanbanInjectionKey } from '$flux/data/di';
13
25
  import { useKanban } from '$flux/composable/private/useKanban';
14
26
  import $style from '$flux/css/component/FluxKanban.module.scss';
15
27
 
28
+ const {
29
+ ariaLabel,
30
+ canMove,
31
+ disabled = false,
32
+ reorderableColumns = false
33
+ } = defineProps<{
34
+ readonly ariaLabel?: string;
35
+ readonly canMove?: (event: FluxKanbanMoveEvent) => boolean;
36
+ readonly disabled?: boolean;
37
+ readonly reorderableColumns?: boolean;
38
+ }>();
39
+
16
40
  const emit = defineEmits<{
17
41
  move: [FluxKanbanMoveEvent];
42
+ moveColumn: [FluxKanbanMoveColumnEvent];
18
43
  }>();
19
44
 
20
45
  defineSlots<{
21
46
  default?(): any;
22
47
  }>();
23
48
 
24
- provide(FluxKanbanInjectionKey, useKanban(event => emit('move', event)));
49
+ const root = useTemplateRef('root');
50
+ const liveMessage = ref('');
51
+
52
+ const disabledRef = toRef(() => disabled);
53
+ const reorderableColumnsRef = toRef(() => reorderableColumns);
54
+ const canMoveRef = toRef(() => canMove);
55
+
56
+ const kanban = useKanban({
57
+ disabled: disabledRef,
58
+ reorderableColumns: reorderableColumnsRef,
59
+ canMove: canMoveRef,
60
+ onMove: event => emit('move', event),
61
+ onMoveColumn: event => emit('moveColumn', event),
62
+ onAnnounce: message => {
63
+ liveMessage.value = '';
64
+ requestAnimationFrame(() => {
65
+ liveMessage.value = message;
66
+ });
67
+ }
68
+ });
69
+
70
+ const isDragging = computed(() => kanban.dragState.value !== null || kanban.columnDragState.value !== null);
71
+
72
+ function onPointerMove(evt: DragEvent | PointerEvent): void {
73
+ kanban.onPointerMove(evt.clientX, evt.clientY);
74
+ }
75
+
76
+ function onWindowDrop(): void {
77
+ kanban.endDrag();
78
+ kanban.endColumnDrag();
79
+ }
80
+
81
+ onMounted(() => {
82
+ kanban.setBoardElement(root.value);
83
+ window.addEventListener('drag', onPointerMove);
84
+ window.addEventListener('dragend', onWindowDrop);
85
+ });
86
+
87
+ onBeforeUnmount(() => {
88
+ kanban.setBoardElement(null);
89
+ window.removeEventListener('drag', onPointerMove);
90
+ window.removeEventListener('dragend', onWindowDrop);
91
+ });
92
+
93
+ watch(() => disabled, value => {
94
+ if (value) {
95
+ kanban.endDrag();
96
+ kanban.endColumnDrag();
97
+ kanban.cancelKeyboardDrop();
98
+ }
99
+ });
100
+
101
+ provide(FluxKanbanInjectionKey, kanban);
102
+ provide(FluxDisabledInjectionKey, disabledRef);
25
103
  </script>
@@ -2,15 +2,25 @@
2
2
  <div
3
3
  ref="root"
4
4
  data-kanban-card
5
+ role="listitem"
6
+ aria-roledescription="Kanban card"
7
+ :aria-disabled="disabledState ? true : undefined"
8
+ :aria-grabbed="isGrabbed ? true : undefined"
5
9
  :class="[
6
10
  $style.kanbanCard,
7
11
  isDragging && $style.isDragging,
8
- isDropBefore && $style.isDropBefore
12
+ isGrabbed && $style.isGrabbed,
13
+ isDropBefore && $style.isDropBefore,
14
+ isDropBefore && !kanban.isDropAllowed.value && $style.isDropBeforeDisallowed,
15
+ disabledState && $style.isDisabled
9
16
  ]"
10
- draggable="true"
17
+ :draggable="!disabledState"
18
+ :tabindex="disabledState ? -1 : 0"
11
19
  @dragstart="onDragStart"
12
20
  @dragend="onDragEnd"
13
- @dragover.prevent.stop="onDragOver">
21
+ @dragover.stop="onDragOver"
22
+ @focus="onFocus"
23
+ @keydown="onKeyDown">
14
24
  <slot/>
15
25
  </div>
16
26
  </template>
@@ -18,13 +28,19 @@
18
28
  <script
19
29
  lang="ts"
20
30
  setup>
21
- import { computed, inject, onMounted, onUnmounted, unref, useTemplateRef } from 'vue';
31
+ import { computed, inject, onBeforeUnmount, onMounted, toRef, unref, useTemplateRef, watch } from 'vue';
22
32
  import { FluxKanbanInjectionKey } from '$flux/data/di';
23
- import $style from '$flux/css/component/FluxKanbanCard.module.scss';
24
-
25
- const {cardId, columnId} = defineProps<{
33
+ import useDisabled from '$flux/composable/useDisabled';
34
+ import $style from '$flux/css/component/FluxKanban.module.scss';
35
+
36
+ const {
37
+ cardId,
38
+ columnId,
39
+ disabled = false
40
+ } = defineProps<{
26
41
  readonly cardId: string | number;
27
42
  readonly columnId: string | number;
43
+ readonly disabled?: boolean;
28
44
  }>();
29
45
 
30
46
  defineSlots<{
@@ -33,8 +49,10 @@
33
49
 
34
50
  const kanban = inject(FluxKanbanInjectionKey)!;
35
51
  const root = useTemplateRef('root');
52
+ const disabledState = useDisabled(toRef(() => disabled));
36
53
 
37
- const isDragging = computed(() => unref(kanban.dragState)?.cardId === cardId);
54
+ const isDragging = computed(() => unref(kanban.dragState)?.cardId === cardId && unref(kanban.dragState)?.mode === 'pointer');
55
+ const isGrabbed = computed(() => kanban.isCardGrabbed(cardId));
38
56
 
39
57
  const isDropBefore = computed(() => {
40
58
  const state = unref(kanban.dragState);
@@ -50,19 +68,42 @@
50
68
  if (root.value) {
51
69
  kanban.registerCard(root.value, cardId);
52
70
  }
71
+
72
+ if (kanban.isCardGrabbed(cardId)) {
73
+ root.value?.focus();
74
+ }
53
75
  });
54
76
 
55
- onUnmounted(() => {
77
+ onBeforeUnmount(() => {
56
78
  if (root.value) {
57
79
  kanban.unregisterCard(root.value);
58
80
  }
59
81
  });
60
82
 
83
+ watch(() => cardId, (newId, oldId) => {
84
+ if (root.value && oldId !== undefined) {
85
+ kanban.unregisterCard(root.value);
86
+ kanban.registerCard(root.value, newId);
87
+ }
88
+ });
89
+
61
90
  function onDragStart(evt: DragEvent): void {
91
+ if (unref(disabledState)) {
92
+ evt.preventDefault();
93
+ return;
94
+ }
95
+
62
96
  kanban.startDrag(cardId, columnId);
63
97
 
64
98
  if (evt.dataTransfer) {
65
99
  evt.dataTransfer.effectAllowed = 'move';
100
+ evt.dataTransfer.setData('text/plain', `card:${String(cardId)}`);
101
+
102
+ if (root.value) {
103
+ const offsetX = evt.offsetX || root.value.getBoundingClientRect().width / 2;
104
+ const offsetY = evt.offsetY || 12;
105
+ evt.dataTransfer.setDragImage(root.value, offsetX, offsetY);
106
+ }
66
107
  }
67
108
  }
68
109
 
@@ -70,21 +111,25 @@
70
111
  kanban.endDrag();
71
112
  }
72
113
 
114
+ function onFocus(): void {
115
+ root.value?.scrollIntoView({block: 'nearest', inline: 'nearest', behavior: 'smooth'});
116
+ }
117
+
73
118
  function onDragOver(evt: DragEvent): void {
74
119
  const state = unref(kanban.dragState);
75
120
 
76
- if (!state) {
121
+ if (!state || unref(disabledState)) {
77
122
  return;
78
123
  }
79
124
 
125
+ evt.preventDefault();
126
+
80
127
  const cardEl = evt.currentTarget as Element;
81
128
  const rect = cardEl.getBoundingClientRect();
82
129
 
83
130
  if (evt.clientY < rect.top + rect.height / 2) {
84
- // Drop before this card
85
131
  kanban.updateDropTarget(columnId, cardId);
86
132
  } else {
87
- // Drop after this card = before the next sibling card
88
133
  let next = cardEl.nextElementSibling;
89
134
 
90
135
  while (next) {
@@ -98,8 +143,57 @@
98
143
  next = next.nextElementSibling;
99
144
  }
100
145
 
101
- // No next sibling → append to end of column
102
146
  kanban.updateDropTarget(columnId, null);
103
147
  }
104
148
  }
149
+
150
+ function onKeyDown(evt: KeyboardEvent): void {
151
+ if (unref(disabledState)) {
152
+ return;
153
+ }
154
+
155
+ const state = unref(kanban.dragState);
156
+ const grabbed = state !== null && state.mode === 'keyboard' && state.cardId === cardId;
157
+
158
+ if (!grabbed) {
159
+ if (evt.key === ' ' || evt.key === 'Enter') {
160
+ evt.preventDefault();
161
+ kanban.grabCard(cardId, columnId);
162
+ }
163
+ return;
164
+ }
165
+
166
+ switch (evt.key) {
167
+ case 'ArrowUp':
168
+ evt.preventDefault();
169
+ kanban.moveKeyboard('up');
170
+ break;
171
+
172
+ case 'ArrowDown':
173
+ evt.preventDefault();
174
+ kanban.moveKeyboard('down');
175
+ break;
176
+
177
+ case 'ArrowLeft':
178
+ evt.preventDefault();
179
+ kanban.moveKeyboard('left');
180
+ break;
181
+
182
+ case 'ArrowRight':
183
+ evt.preventDefault();
184
+ kanban.moveKeyboard('right');
185
+ break;
186
+
187
+ case ' ':
188
+ case 'Enter':
189
+ evt.preventDefault();
190
+ kanban.commitKeyboardDrop();
191
+ break;
192
+
193
+ case 'Escape':
194
+ evt.preventDefault();
195
+ kanban.cancelKeyboardDrop();
196
+ break;
197
+ }
198
+ }
105
199
  </script>
@@ -1,8 +1,27 @@
1
1
  <template>
2
2
  <div
3
- data-kanban-column
4
- :class="[$style.kanbanColumn, isOver && $style.isOver]">
5
- <div :class="$style.kanbanColumnHeader">
3
+ ref="root"
4
+ role="list"
5
+ aria-roledescription="Kanban column"
6
+ :aria-label="label"
7
+ :aria-disabled="disabledState ? true : undefined"
8
+ :class="[
9
+ $style.kanbanColumn,
10
+ isOver && $style.isOver,
11
+ isOver && !kanban.isDropAllowed.value && $style.isDropDisallowed,
12
+ isReorderable && $style.isReorderable,
13
+ isColumnDragging && $style.isColumnDragging,
14
+ isColumnDropBefore && $style.isColumnDropBefore,
15
+ disabledState && $style.isDisabled
16
+ ]">
17
+ <div
18
+ :class="$style.kanbanColumnHeader"
19
+ :draggable="isReorderable && !disabledState"
20
+ :tabindex="isReorderable && !disabledState ? 0 : undefined"
21
+ @dragstart="onColumnDragStart"
22
+ @dragend="onColumnDragEnd"
23
+ @dragover="onColumnDragOver"
24
+ @drop="onColumnDrop">
6
25
  <slot name="header">
7
26
  <span :class="$style.kanbanColumnLabel">{{ label }}</span>
8
27
  </slot>
@@ -11,16 +30,32 @@
11
30
  </div>
12
31
 
13
32
  <div
33
+ ref="body"
14
34
  :class="$style.kanbanColumnBody"
15
35
  @dragenter="onDragEnter"
16
36
  @dragleave="onDragLeave"
17
- @dragover.prevent="onDragOver"
18
- @drop.prevent="onDrop">
37
+ @dragover="onDragOver"
38
+ @drop="onDrop">
19
39
  <slot/>
20
40
 
21
41
  <div
22
- v-if="isDropEnd"
23
- :class="$style.kanbanDropIndicator"/>
42
+ v-if="isEmpty"
43
+ :class="$style.kanbanColumnEmpty">
44
+ <slot name="empty"/>
45
+ </div>
46
+
47
+ <div
48
+ :class="[
49
+ $style.kanbanDropIndicator,
50
+ isDropEnd && $style.isDropEndActive,
51
+ isDropEnd && !kanban.isDropAllowed.value && $style.isDropEndDisallowed
52
+ ]"/>
53
+ </div>
54
+
55
+ <div
56
+ v-if="hasFooter"
57
+ :class="$style.kanbanColumnFooter">
58
+ <slot name="footer"/>
24
59
  </div>
25
60
  </div>
26
61
  </template>
@@ -28,12 +63,19 @@
28
63
  <script
29
64
  lang="ts"
30
65
  setup>
31
- import { computed, inject, ref, unref } from 'vue';
32
- import { FluxKanbanInjectionKey } from '$flux/data/di';
33
- import $style from '$flux/css/component/FluxKanbanColumn.module.scss';
66
+ import { Comment, Text, computed, inject, onBeforeUnmount, onMounted, provide, ref, toRef, unref, useSlots, useTemplateRef, watch } from 'vue';
67
+ import { flattenVNodeTree } from '@flux-ui/internals';
68
+ import { FluxDisabledInjectionKey, FluxKanbanInjectionKey } from '$flux/data/di';
69
+ import useDisabled from '$flux/composable/useDisabled';
70
+ import $style from '$flux/css/component/FluxKanban.module.scss';
34
71
 
35
- const {columnId, label} = defineProps<{
72
+ const {
73
+ columnId,
74
+ disabled = false,
75
+ label
76
+ } = defineProps<{
36
77
  readonly columnId: string | number;
78
+ readonly disabled?: boolean;
37
79
  readonly label: string;
38
80
  }>();
39
81
 
@@ -41,19 +83,62 @@
41
83
  default?(): any;
42
84
  header?(): any;
43
85
  actions?(): any;
86
+ empty?(): any;
87
+ footer?(): any;
44
88
  }>();
45
89
 
46
90
  const kanban = inject(FluxKanbanInjectionKey)!;
91
+ const root = useTemplateRef('root');
92
+ const body = useTemplateRef('body');
93
+ const slots = useSlots();
94
+
95
+ const disabledState = useDisabled(toRef(() => disabled));
96
+ provide(FluxDisabledInjectionKey, disabledState);
47
97
 
48
98
  let dragEnterCount = 0;
49
99
  const isOver = ref(false);
50
100
 
101
+ const isReorderable = computed(() => unref(kanban.reorderableColumns) && !unref(disabledState));
102
+
51
103
  const isDropEnd = computed(() => {
52
104
  const state = unref(kanban.dragState);
53
105
  return state !== null && state.dropColumnId === columnId && state.beforeCardId === null;
54
106
  });
55
107
 
108
+ const isColumnDragging = computed(() => unref(kanban.columnDragState)?.columnId === columnId);
109
+
110
+ const isColumnDropBefore = computed(() => {
111
+ const state = unref(kanban.columnDragState);
112
+
113
+ if (!state || state.dropBeforeColumnId !== columnId || state.columnId === columnId) {
114
+ return false;
115
+ }
116
+
117
+ return true;
118
+ });
119
+
120
+ const isEmpty = computed(() => {
121
+ if (!slots.empty) {
122
+ return false;
123
+ }
124
+
125
+ const defaultSlot = slots.default?.();
126
+
127
+ if (!defaultSlot || defaultSlot.length === 0) {
128
+ return true;
129
+ }
130
+
131
+ const hasContent = flattenVNodeTree(defaultSlot).some(vnode => vnode.type !== Comment && vnode.type !== Text);
132
+ return !hasContent;
133
+ });
134
+
135
+ const hasFooter = computed(() => !!slots.footer);
136
+
56
137
  function onDragEnter(): void {
138
+ if (unref(disabledState)) {
139
+ return;
140
+ }
141
+
57
142
  dragEnterCount++;
58
143
  isOver.value = true;
59
144
  }
@@ -67,7 +152,12 @@
67
152
  }
68
153
 
69
154
  function onDragOver(evt: DragEvent): void {
70
- // Only handle empty-area drops; cards stop propagation on their own dragover
155
+ if (unref(disabledState) || !unref(kanban.dragState)) {
156
+ return;
157
+ }
158
+
159
+ evt.preventDefault();
160
+
71
161
  const target = evt.target as Element;
72
162
  const isOverCard = !!target.closest('[data-kanban-card]');
73
163
 
@@ -76,9 +166,106 @@
76
166
  }
77
167
  }
78
168
 
79
- function onDrop(): void {
169
+ function onDrop(evt: DragEvent): void {
80
170
  dragEnterCount = 0;
81
171
  isOver.value = false;
172
+
173
+ if (unref(disabledState) || !unref(kanban.dragState)) {
174
+ return;
175
+ }
176
+
177
+ evt.preventDefault();
82
178
  kanban.commitDrop();
83
179
  }
180
+
181
+ function onColumnDragStart(evt: DragEvent): void {
182
+ if (!unref(isReorderable)) {
183
+ return;
184
+ }
185
+
186
+ if (evt.dataTransfer) {
187
+ evt.dataTransfer.effectAllowed = 'move';
188
+ evt.dataTransfer.setData('text/plain', `column:${String(columnId)}`);
189
+ }
190
+
191
+ kanban.startColumnDrag(columnId);
192
+ }
193
+
194
+ function onColumnDragEnd(): void {
195
+ kanban.endColumnDrag();
196
+ }
197
+
198
+ function onColumnDragOver(evt: DragEvent): void {
199
+ const state = unref(kanban.columnDragState);
200
+
201
+ if (!state || state.columnId === columnId) {
202
+ return;
203
+ }
204
+
205
+ evt.preventDefault();
206
+
207
+ const headerEl = evt.currentTarget as Element;
208
+ const rect = headerEl.getBoundingClientRect();
209
+
210
+ if (evt.clientX < rect.left + rect.width / 2) {
211
+ kanban.updateColumnDropTarget(columnId);
212
+ } else {
213
+ let next = root.value?.nextElementSibling ?? null;
214
+
215
+ while (next) {
216
+ const info = kanban.getColumnInfo(next);
217
+
218
+ if (info) {
219
+ kanban.updateColumnDropTarget(info.columnId);
220
+ return;
221
+ }
222
+
223
+ next = next.nextElementSibling;
224
+ }
225
+
226
+ kanban.updateColumnDropTarget(null);
227
+ }
228
+ }
229
+
230
+ function onColumnDrop(evt: DragEvent): void {
231
+ if (!unref(kanban.columnDragState)) {
232
+ return;
233
+ }
234
+
235
+ evt.preventDefault();
236
+ kanban.commitColumnDrop();
237
+ }
238
+
239
+ onMounted(() => {
240
+ if (root.value) {
241
+ kanban.registerColumn(root.value, columnId);
242
+ }
243
+
244
+ if (body.value) {
245
+ kanban.setColumnBodyElement(columnId, body.value);
246
+ }
247
+ });
248
+
249
+ onBeforeUnmount(() => {
250
+ if (root.value) {
251
+ kanban.unregisterColumn(root.value);
252
+ }
253
+
254
+ kanban.setColumnBodyElement(columnId, null);
255
+ });
256
+
257
+ watch(() => columnId, (newId, oldId) => {
258
+ if (oldId !== undefined) {
259
+ kanban.setColumnBodyElement(oldId, null);
260
+ }
261
+
262
+ if (root.value) {
263
+ kanban.unregisterColumn(root.value);
264
+ kanban.registerColumn(root.value, newId);
265
+ }
266
+
267
+ if (body.value) {
268
+ kanban.setColumnBodyElement(newId, body.value);
269
+ }
270
+ });
84
271
  </script>
@@ -1,6 +1,6 @@
1
1
  import { useDebouncedRef, useLoaded } from '@basmilius/common';
2
2
  import type { FluxFilterOptionRow, FluxFilterValue } from '@flux-ui/types';
3
- import { type ComputedRef, type ModelRef, computed, type Ref, ref, unref, watch } from 'vue';
3
+ import { computed, type ComputedRef, type ModelRef, type Ref, ref, unref, watch } from 'vue';
4
4
  import { isFluxFilterOptionItem } from '$flux/data';
5
5
 
6
6
  type UseAsyncFilterOptionsParams = {
@@ -11,25 +11,19 @@ type UseAsyncFilterOptionsParams = {
11
11
  fetchSearch(searchQuery: string): Promise<FluxFilterOptionRow[]>;
12
12
  };
13
13
 
14
- export default function ({
15
- currentValueIds,
16
- modelSearch,
17
- fetchOptions: fetchOptionsProp,
18
- fetchRelevant: fetchRelevantProp,
19
- fetchSearch: fetchSearchProp
20
- }: UseAsyncFilterOptionsParams) {
14
+ export default function (params: UseAsyncFilterOptionsParams) {
21
15
  const {isLoading, loaded} = useLoaded();
22
- const debouncedModelSearch = useDebouncedRef(modelSearch, 150) as unknown as Ref<string>;
23
- const fetchOptions = computed(() => loaded(fetchOptionsProp));
24
- const fetchRelevant = computed(() => loaded(fetchRelevantProp));
25
- const fetchSearch = computed(() => loaded(fetchSearchProp));
16
+ const debouncedModelSearch = useDebouncedRef(params.modelSearch, 150) as unknown as Ref<string>;
17
+ const fetchOptions = computed(() => loaded(params.fetchOptions));
18
+ const fetchRelevant = computed(() => loaded(params.fetchRelevant));
19
+ const fetchSearch = computed(() => loaded(params.fetchSearch));
26
20
 
27
21
  const selectedOptions = ref<FluxFilterOptionRow[]>([]);
28
22
  const visibleOptions = ref<FluxFilterOptionRow[]>([]);
29
23
 
30
24
  const options = computed(() => {
31
25
  const options: FluxFilterOptionRow[] = [];
32
- const search = unref(modelSearch);
26
+ const search = unref(params.modelSearch);
33
27
  const selected = unref(selectedOptions);
34
28
  const visible = unref(visibleOptions);
35
29
 
@@ -50,7 +44,7 @@ export default function ({
50
44
  return options;
51
45
  });
52
46
 
53
- watch(currentValueIds, async ids => {
47
+ watch(params.currentValueIds, async ids => {
54
48
  if (ids.length === 0) {
55
49
  return;
56
50
  }
@@ -1,5 +1,5 @@
1
1
  import type { FluxFilterValue, FluxFilterValueSingle } from '@flux-ui/types';
2
- import { type ComputedRef, computed, unref } from 'vue';
2
+ import { computed, type ComputedRef, unref } from 'vue';
3
3
  import { useFilterInjection } from '$flux/composable';
4
4
 
5
5
  export type FilterOptionSingle = {