@milaboratories/uikit 2.2.95 → 2.2.97

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 (82) hide show
  1. package/.turbo/turbo-build.log +32 -27
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +17 -0
  4. package/dist/components/DataTable/TableComponent.vue.js +1 -1
  5. package/dist/components/PlAccordion/PlAccordionSection.vue2.js +21 -21
  6. package/dist/components/PlAccordion/PlAccordionSection.vue2.js.map +1 -1
  7. package/dist/components/PlAutocomplete/PlAutocomplete.vue.js.map +1 -1
  8. package/dist/components/PlClipboard/PlClipboard.vue.d.ts +1 -1
  9. package/dist/components/PlDropdown/OptionList.vue.d.ts +77 -0
  10. package/dist/components/PlDropdown/OptionList.vue.d.ts.map +1 -0
  11. package/dist/components/PlDropdown/OptionList.vue.js +88 -0
  12. package/dist/components/PlDropdown/OptionList.vue.js.map +1 -0
  13. package/dist/components/PlDropdown/OptionList.vue2.js +5 -0
  14. package/dist/components/PlDropdown/OptionList.vue2.js.map +1 -0
  15. package/dist/components/PlDropdown/PlDropdown.vue.d.ts.map +1 -1
  16. package/dist/components/PlDropdown/PlDropdown.vue.js +110 -122
  17. package/dist/components/PlDropdown/PlDropdown.vue.js.map +1 -1
  18. package/dist/components/PlDropdown/types.d.ts +7 -0
  19. package/dist/components/PlDropdown/types.d.ts.map +1 -0
  20. package/dist/components/PlDropdown/useGroupBy.d.ts +7 -0
  21. package/dist/components/PlDropdown/useGroupBy.d.ts.map +1 -0
  22. package/dist/components/PlDropdown/useGroupBy.js +36 -0
  23. package/dist/components/PlDropdown/useGroupBy.js.map +1 -0
  24. package/dist/components/PlDropdownRef/PlDropdownRef.vue.d.ts +1 -1
  25. package/dist/components/PlDropdownRef/PlDropdownRef.vue.d.ts.map +1 -1
  26. package/dist/components/PlDropdownRef/PlDropdownRef.vue.js +11 -10
  27. package/dist/components/PlDropdownRef/PlDropdownRef.vue.js.map +1 -1
  28. package/dist/components/PlElementList/PlElementList.vue.d.ts +19 -27
  29. package/dist/components/PlElementList/PlElementList.vue.d.ts.map +1 -1
  30. package/dist/components/PlElementList/PlElementList.vue2.js +155 -174
  31. package/dist/components/PlElementList/PlElementList.vue2.js.map +1 -1
  32. package/dist/components/PlElementList/PlElementListItem.vue.d.ts.map +1 -1
  33. package/dist/components/PlElementList/PlElementListItem.vue2.js +1 -1
  34. package/dist/components/PlElementList/PlElementListItem.vue2.js.map +1 -1
  35. package/dist/components/PlElementList/PlElementListItem.vue3.js +24 -24
  36. package/dist/components/PlElementList/utils.d.ts +0 -1
  37. package/dist/components/PlElementList/utils.d.ts.map +1 -1
  38. package/dist/components/PlElementList/utils.js +5 -11
  39. package/dist/components/PlElementList/utils.js.map +1 -1
  40. package/dist/components/PlErrorBoundary/PlErrorBoundary.vue.js +8 -8
  41. package/dist/components/PlFileInput/PlFileInput.vue.js +8 -8
  42. package/dist/components/PlIcon16/PlIcon16.vue.d.ts +1 -1
  43. package/dist/components/PlIcon24/PlIcon24.vue.d.ts +1 -1
  44. package/dist/components/PlSlideModal/PlSlideModal.vue.js +1 -1
  45. package/dist/components/PlSvg/PlSvg.vue.d.ts +1 -1
  46. package/dist/components/PlSvg/PlSvg.vue.d.ts.map +1 -1
  47. package/dist/components/PlSvg/PlSvg.vue2.js +24 -22
  48. package/dist/components/PlSvg/PlSvg.vue2.js.map +1 -1
  49. package/dist/composition/useWatchFetch.js +10 -10
  50. package/dist/helpers/utils.d.ts +1 -0
  51. package/dist/helpers/utils.d.ts.map +1 -1
  52. package/dist/helpers/utils.js +2 -1
  53. package/dist/helpers/utils.js.map +1 -1
  54. package/dist/lib/util/helpers/dist/index.js +108 -104
  55. package/dist/lib/util/helpers/dist/index.js.map +1 -1
  56. package/dist/sdk/model/dist/index.js +1 -1
  57. package/dist/sdk/model/dist/index.js.map +1 -1
  58. package/dist/types.d.ts +4 -14
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/utils/TextLabel.vue.d.ts +18 -0
  61. package/dist/utils/TextLabel.vue.d.ts.map +1 -0
  62. package/dist/utils/TextLabel.vue.js +26 -0
  63. package/dist/utils/TextLabel.vue.js.map +1 -0
  64. package/dist/utils/TextLabel.vue2.js +13 -0
  65. package/dist/utils/TextLabel.vue2.js.map +1 -0
  66. package/package.json +4 -4
  67. package/src/components/PlAccordion/PlAccordionSection.vue +3 -3
  68. package/src/components/PlAutocomplete/PlAutocomplete.vue +1 -1
  69. package/src/components/PlDropdown/OptionList.vue +71 -0
  70. package/src/components/PlDropdown/PlDropdown.vue +29 -25
  71. package/src/components/PlDropdown/pl-dropdown.scss +4 -0
  72. package/src/components/PlDropdown/types.ts +3 -0
  73. package/src/components/PlDropdown/useGroupBy.ts +63 -0
  74. package/src/components/PlDropdownRef/PlDropdownRef.vue +1 -0
  75. package/src/components/PlElementList/PlElementList.vue +116 -135
  76. package/src/components/PlElementList/PlElementListItem.vue +9 -1
  77. package/src/components/PlElementList/README.md +325 -72
  78. package/src/components/PlElementList/utils.ts +0 -9
  79. package/src/components/PlSvg/PlSvg.vue +2 -2
  80. package/src/helpers/utils.ts +1 -0
  81. package/src/types.ts +5 -15
  82. package/src/utils/TextLabel.vue +43 -0
@@ -9,21 +9,22 @@ export default {
9
9
 
10
10
  <script lang="ts" setup generic="M = unknown">
11
11
  import './pl-dropdown.scss';
12
- import { computed, reactive, ref, unref, useSlots, useTemplateRef, watch, watchPostEffect } from 'vue';
12
+ import { computed, reactive, ref, unref, useTemplateRef, watch, watchPostEffect } from 'vue';
13
13
  import { tap } from '../../helpers/functions';
14
14
  import { PlTooltip } from '../PlTooltip';
15
15
  import DoubleContour from '../../utils/DoubleContour.vue';
16
16
  import { useLabelNotch } from '../../utils/useLabelNotch';
17
17
  import type { ListOption, ListOptionNormalized } from '../../types';
18
18
  import { deepEqual } from '../../helpers/objects';
19
- import DropdownListItem from '../DropdownListItem.vue';
20
19
  import LongText from '../LongText.vue';
21
20
  import { normalizeListOptions } from '../../helpers/utils';
22
21
  import { PlIcon16 } from '../PlIcon16';
23
22
  import { PlMaskIcon24 } from '../PlMaskIcon24';
24
- import { DropdownOverlay } from '../../utils/DropdownOverlay';
25
23
  import SvgRequired from '../../generated/components/svg/images/SvgRequired.vue';
26
24
  import { getErrorMessage } from '../../helpers/error.ts';
25
+ import OptionList from './OptionList.vue';
26
+ import { useGroupBy } from './useGroupBy';
27
+ import type { LOption } from './types';
27
28
 
28
29
  const emit = defineEmits<{
29
30
  /**
@@ -110,7 +111,7 @@ const slots = defineSlots<{
110
111
  const rootRef = ref<HTMLElement | undefined>();
111
112
  const input = ref<HTMLInputElement | undefined>();
112
113
 
113
- const overlayRef = useTemplateRef('overlay');
114
+ const optionListRef = useTemplateRef<InstanceType<typeof OptionList>>('optionListRef');
114
115
 
115
116
  const data = reactive({
116
117
  search: '',
@@ -121,7 +122,7 @@ const data = reactive({
121
122
 
122
123
  const findActiveIndex = () =>
123
124
  tap(
124
- filteredRef.value.findIndex((o) => deepEqual(o.value, props.modelValue)),
125
+ orderedRef.value.findIndex((o) => deepEqual(o.value, props.modelValue)),
125
126
  (v) => (v < 0 ? 0 : v),
126
127
  );
127
128
 
@@ -159,7 +160,7 @@ const computedError = computed(() => {
159
160
  return undefined;
160
161
  });
161
162
 
162
- const optionsRef = computed(() =>
163
+ const optionsRef = computed<LOption<M>[]>(() =>
163
164
  normalizeListOptions(props.options ?? []).map((opt, index) => ({
164
165
  ...opt,
165
166
  index,
@@ -214,6 +215,8 @@ const filteredRef = computed(() => {
214
215
  return options;
215
216
  });
216
217
 
218
+ const { orderedRef, groupsRef, restRef } = useGroupBy(filteredRef, 'group');
219
+
217
220
  const tabindex = computed(() => (isDisabled.value ? undefined : '0'));
218
221
 
219
222
  const selectOption = (v: M | undefined) => {
@@ -223,6 +226,10 @@ const selectOption = (v: M | undefined) => {
223
226
  rootRef?.value?.focus();
224
227
  };
225
228
 
229
+ const selectOptionWrapper = (v: unknown) => {
230
+ selectOption(v as M | undefined);
231
+ };
232
+
226
233
  const clear = () => emit('update:modelValue', undefined);
227
234
 
228
235
  const setFocusOnInput = () => input.value?.focus();
@@ -239,7 +246,7 @@ const onInputFocus = () => (data.open = true);
239
246
  const onFocusOut = (event: FocusEvent) => {
240
247
  const relatedTarget = event.relatedTarget as Node | null;
241
248
 
242
- if (!rootRef.value?.contains(relatedTarget) && !overlayRef.value?.listRef?.contains(relatedTarget)) {
249
+ if (!rootRef.value?.contains(relatedTarget) && !optionListRef.value?.listRef?.contains(relatedTarget)) {
243
250
  data.search = '';
244
251
  data.open = false;
245
252
  }
@@ -266,25 +273,25 @@ const handleKeydown = (e: { code: string; preventDefault(): void }) => {
266
273
  rootRef.value?.focus();
267
274
  }
268
275
 
269
- const filtered = unref(filteredRef);
276
+ const ordered = orderedRef.value;
270
277
 
271
- const { length } = filtered;
278
+ const { length } = ordered;
272
279
 
273
280
  if (!length) {
274
281
  return;
275
282
  }
276
283
 
277
284
  if (e.code === 'Enter') {
278
- selectOption(filtered.find((it) => it.index === activeIndex)?.value);
285
+ selectOption(ordered.find((it) => it.index === activeIndex)?.value);
279
286
  }
280
287
 
281
- const localIndex = filtered.findIndex((it) => it.index === activeIndex) ?? -1;
288
+ const localIndex = ordered.findIndex((it) => it.index === activeIndex) ?? -1;
282
289
 
283
290
  const delta = e.code === 'ArrowDown' ? 1 : e.code === 'ArrowUp' ? -1 : 0;
284
291
 
285
292
  const newIndex = Math.abs(localIndex + delta + length) % length;
286
293
 
287
- data.activeIndex = filteredRef.value[newIndex].index ?? -1;
294
+ data.activeIndex = ordered[newIndex].index ?? -1;
288
295
  };
289
296
 
290
297
  useLabelNotch(rootRef);
@@ -301,7 +308,7 @@ watchPostEffect(() => {
301
308
  data.search; // to watch
302
309
 
303
310
  if (data.activeIndex >= 0 && data.open) {
304
- overlayRef.value?.scrollIntoActive();
311
+ optionListRef.value?.scrollIntoActive();
305
312
  }
306
313
  });
307
314
  </script>
@@ -354,18 +361,15 @@ watchPostEffect(() => {
354
361
  </template>
355
362
  </PlTooltip>
356
363
  </label>
357
- <DropdownOverlay v-if="data.open" ref="overlay" :root="rootRef" class="pl-dropdown__options" tabindex="-1" :gap="3">
358
- <DropdownListItem
359
- v-for="(item, index) in filteredRef"
360
- :key="index"
361
- :option="item"
362
- :is-selected="item.isSelected"
363
- :is-hovered="item.isActive"
364
- :size="optionSize"
365
- @click.stop="selectOption(item.value)"
366
- />
367
- <div v-if="!filteredRef.length" class="nothing-found">Nothing found</div>
368
- </DropdownOverlay>
364
+ <OptionList
365
+ v-if="data.open"
366
+ ref="optionListRef"
367
+ :root-ref="rootRef!"
368
+ :groups="groupsRef"
369
+ :rest="restRef"
370
+ :option-size="optionSize"
371
+ :select-option="selectOptionWrapper"
372
+ />
369
373
  <DoubleContour class="pl-dropdown__contour" />
370
374
  </div>
371
375
  </div>
@@ -22,6 +22,10 @@
22
22
  font-style: italic;
23
23
  }
24
24
 
25
+ .group-container {
26
+ padding: 4px 0;
27
+ }
28
+
25
29
  .option {
26
30
  position: relative;
27
31
  padding: 0 30px 0 10px;
@@ -0,0 +1,3 @@
1
+ import type { ListOptionNormalized } from '../../types';
2
+
3
+ export type LOption<M = unknown> = ListOptionNormalized<M> & { isSelected: boolean; isActive: boolean; index: number };
@@ -0,0 +1,63 @@
1
+ import type { Ref } from 'vue';
2
+ import { computed } from 'vue';
3
+
4
+ function groupBy<T, K extends keyof T>(
5
+ list: T[],
6
+ groupBy: K,
7
+ ): {
8
+ grouped: Map<NonNullable<T[K]>, T[]>;
9
+ rest: T[];
10
+ ordered: T[];
11
+ } {
12
+ const grouped: Map<NonNullable<T[K]>, T[]> = new Map();
13
+
14
+ if (!list) {
15
+ return {
16
+ grouped,
17
+ rest: [],
18
+ ordered: [],
19
+ };
20
+ }
21
+
22
+ // Group items by the specified key
23
+ for (const item of list) {
24
+ const key = item[groupBy];
25
+ if (key === undefined) continue;
26
+ if (key === null) continue;
27
+ if (!grouped.has(key)) grouped.set(key, []);
28
+ grouped.get(key)?.push(item);
29
+ }
30
+
31
+ // Items without a group key
32
+ const rest = list.filter((item: T) => {
33
+ const key = item[groupBy];
34
+ return key === undefined || key === null;
35
+ });
36
+
37
+ const ordered = [...Array.from(grouped.values()).flat(), ...rest];
38
+
39
+ return {
40
+ grouped,
41
+ rest,
42
+ ordered,
43
+ };
44
+ }
45
+
46
+ export function useGroupBy<T, K extends keyof T>(
47
+ list: Ref<T[]>,
48
+ byKey: K,
49
+ ) {
50
+ const result = computed(() => groupBy(list.value, byKey));
51
+
52
+ const orderedRef = computed(() => result.value.ordered);
53
+
54
+ const groupsRef = computed(() => result.value.grouped);
55
+
56
+ const restRef = computed(() => result.value.rest);
57
+
58
+ return {
59
+ orderedRef,
60
+ groupsRef,
61
+ restRef,
62
+ };
63
+ }
@@ -88,6 +88,7 @@ const options = computed(() =>
88
88
  props.options?.map((opt) => ({
89
89
  label: opt.label,
90
90
  value: opt.ref,
91
+ group: opt.group,
91
92
  })),
92
93
  );
93
94
 
@@ -1,62 +1,72 @@
1
1
  <script generic="T extends unknown = unknown, K extends number | string = number | string" lang="ts" setup>
2
2
  import type { ShallowRef } from 'vue';
3
3
  import { computed, shallowRef, watch } from 'vue';
4
- import { isNil, shallowHash } from '@milaboratories/helpers';
4
+ import { isFunction, shallowHash } from '@milaboratories/helpers';
5
5
  import { useSortable } from '@vueuse/integrations/useSortable';
6
6
  import { type SortableEvent } from 'sortablejs';
7
- import { moveElements, optionalUpdateRef } from './utils.ts';
7
+ import { moveElements } from './utils.ts';
8
8
  import PlElementListItem from './PlElementListItem.vue';
9
9
 
10
10
  const itemsRef = defineModel<T[]>('items', { required: true });
11
- const draggableSetRef = defineModel<Set<K>>('draggableItems');
12
- const removableSetRef = defineModel<Set<K>>('removableItems');
13
-
14
- const expandableSetRef = defineModel<Set<K>>('expandableItems');
15
- const expandedSetRef = defineModel<Set<K>>('expandedItems');
16
-
17
- const pinnableSetRef = defineModel<Set<K>>('pinnableItems');
18
- const pinnedSetRef = defineModel<Set<K>>('pinnedItems');
19
-
20
- const toggableSetRef = defineModel<Set<K>>('toggableItems');
21
- const toggledSetRef = defineModel<Set<K>>('toggledItems');
22
11
 
23
12
  const props = withDefaults(
24
13
  defineProps<{
25
- getItemKey: (item: T, index: number) => K;
14
+ getItemKey?: (item: T, index: number) => K;
26
15
 
27
16
  itemClass?: string | string[] | ((item: T, index: number) => string | string[]);
28
- activeItems?: Set<K>;
17
+ isActive?: (item: T, index: number) => boolean;
29
18
 
30
- enableDragging?: boolean;
19
+ disableDragging?: boolean;
20
+ isDraggable?: (item: T, index: number) => boolean;
31
21
  onDragEnd?: (oldIndex: number, newIndex: number) => void | boolean;
32
22
  onSort?: (oldIndex: number, newIndex: number) => void | boolean;
33
23
 
34
- enableExpanding?: boolean;
35
- onExpand?: (item: T, index: number) => void | boolean;
36
-
37
- enableRemoving?: boolean;
24
+ disableRemoving?: boolean;
25
+ isRemovable?: (item: T, index: number) => boolean;
38
26
  onRemove?: (item: T, index: number) => void | boolean;
39
27
 
40
- enableToggling?: boolean;
41
- onToggle?: (item: T, index: number) => void | boolean;
28
+ disableExpanding?: boolean;
29
+ isExpandable?: (item: T, index: number) => boolean;
30
+ isExpanded?: (item: T, index: number) => boolean;
31
+ onExpand?: (item: T, index: number) => unknown;
32
+
33
+ disableToggling?: boolean;
34
+ isToggable?: (item: T, index: number) => boolean;
35
+ isToggled?: (item: T, index: number) => boolean;
36
+ onToggle?: (item: T, index: number) => unknown;
42
37
 
43
- enablePinning?: boolean;
38
+ disablePinning?: boolean;
39
+ isPinnable?: (item: T, index: number) => boolean;
40
+ isPinned?: (item: T, index: number) => boolean;
44
41
  onPin?: (item: T, index: number) => void | boolean;
45
42
  }>(), {
46
- itemClass: undefined,
47
- activeItems: undefined,
43
+ getItemKey: (item: T) => JSON.stringify(item) as K,
48
44
 
49
- enableDragging: undefined,
50
- enableRemoving: undefined,
51
- enableExpanding: undefined,
52
- enableToggling: undefined,
53
- enablePinning: undefined,
45
+ itemClass: undefined,
46
+ isActive: undefined,
54
47
 
48
+ disableDragging: undefined,
49
+ isDraggable: undefined,
55
50
  onDragEnd: undefined,
56
51
  onSort: undefined,
52
+
53
+ disableRemoving: undefined,
54
+ isRemovable: undefined,
57
55
  onRemove: undefined,
56
+
57
+ disableExpanding: undefined,
58
+ isExpandable: undefined,
59
+ isExpanded: undefined,
58
60
  onExpand: undefined,
61
+
62
+ disableToggling: undefined,
63
+ isToggable: undefined,
64
+ isToggled: undefined,
59
65
  onToggle: undefined,
66
+
67
+ disablePinning: undefined,
68
+ isPinnable: undefined,
69
+ isPinned: undefined,
60
70
  onPin: undefined,
61
71
  },
62
72
  );
@@ -71,30 +81,45 @@ const slots = defineSlots<{
71
81
  }>();
72
82
 
73
83
  const dndSortingEnabled = computed((): boolean => {
74
- return props.enableDragging !== false;
84
+ return props.disableDragging !== true;
75
85
  });
76
86
 
77
- const pinnedItemsRef = computed(() => itemsRef.value.filter(isPinned));
87
+ const pinnedItemsRef = computed(() => itemsRef.value.filter(isPinnedItem));
78
88
  const hasPinnedItems = computed(() => pinnedItemsRef.value.length > 0);
79
89
 
80
- const unpinnedItemsRef = computed(() => itemsRef.value.filter((item, index) => !isPinned(item, index)));
90
+ const unpinnedItemsRef = computed(() => itemsRef.value.filter((item, index) => !isPinnedItem(item, index)));
81
91
  const hasUnpinnedItems = computed(() => unpinnedItemsRef.value.length > 0);
82
92
 
83
93
  const domProjectionItemsRef = shallowRef<undefined | T[]>();
84
94
  const pinnedContainerRef = shallowRef<HTMLElement>();
85
95
  const unpinnedContainerRef = shallowRef<HTMLElement>();
86
96
 
97
+ // version fix problem with sync between data and rendered values
98
+ const getKey = (item: T, index: number) => {
99
+ return `${versionRef.value}-${props.getItemKey(item, index)}`;
100
+ };
101
+ const pinnedKeysRef = computed(() => pinnedItemsRef.value.map(getKey));
102
+ const unpinnedKeysRef = computed(() => unpinnedItemsRef.value.map(getKey));
103
+
87
104
  // version fix problem with sync between data and rendered values when items have been changed
88
105
  const versionRef = computed<number>((oldVersion) => {
89
- const currentVersion = shallowHash(...itemsRef.value.map(props.getItemKey));
106
+ const currentKeys = itemsRef.value.map(props.getItemKey);
90
107
 
91
- if (domProjectionItemsRef.value === undefined) return oldVersion ?? currentVersion;
108
+ if (domProjectionItemsRef.value === undefined) return oldVersion ?? shallowHash(...currentKeys);
109
+ if (currentKeys.length !== domProjectionItemsRef.value.length) return shallowHash(...currentKeys);
92
110
 
93
- const lastSortedVersion = shallowHash(...domProjectionItemsRef.value.map(props.getItemKey));
111
+ const domProjectionKeys = domProjectionItemsRef.value.map(props.getItemKey);
112
+ const domProjectionKeysSet = new Set(domProjectionKeys);
94
113
 
95
- if (currentVersion === lastSortedVersion) return oldVersion ?? currentVersion;
114
+ for (let i = 0; i < currentKeys.length; i++) {
115
+ const hasInconsistentPosition = domProjectionKeysSet.has(currentKeys[i]) && domProjectionKeys[i] !== currentKeys[i];
96
116
 
97
- return oldVersion !== currentVersion ? currentVersion : lastSortedVersion;
117
+ if (hasInconsistentPosition) {
118
+ return shallowHash(...currentKeys);
119
+ }
120
+ }
121
+
122
+ return oldVersion ?? shallowHash(...currentKeys);
98
123
  });
99
124
 
100
125
  createSortable(hasPinnedItems, pinnedContainerRef, pinnedItemsRef, () => 0);
@@ -117,7 +142,9 @@ function createSortable(toggler: ShallowRef<boolean>, elRef: ShallowRef<undefine
117
142
  }
118
143
  },
119
144
  });
120
- watch(toggler, (on) => on ? sortable.start() : sortable.stop());
145
+ watch([() => props.disableDragging, toggler], ([disabled, on]) => disabled || !on ? sortable.stop() : sortable.start(), {
146
+ immediate: true,
147
+ });
121
148
 
122
149
  return sortable;
123
150
  }
@@ -133,77 +160,56 @@ function moveItems(oldIndex: number, newIndex: number, afterUpdateDom: boolean)
133
160
 
134
161
  if (!preventDefault) {
135
162
  moveElements(itemsRef.value, oldIndex, newIndex);
136
- optionalUpdateRef(itemsRef);
137
163
  }
138
164
  }
139
165
 
140
- function isActive(item: T, index: number): boolean {
141
- const k = props.getItemKey(item, index);
142
- return props.activeItems?.has(k) ?? false;
166
+ function isActiveItem(item: T, index: number): boolean {
167
+ return props.isActive?.(item, index) ?? false;
143
168
  }
144
169
 
145
- function isDraggable(item: T, index: number): boolean {
146
- const k = props.getItemKey(item, index);
147
- if (props.enableDragging === false) return false;
148
- return (draggableSetRef.value?.has(k) ?? true);
170
+ function isDraggableItem(item: T, index: number): boolean {
171
+ if (props.disableDragging === true) return false;
172
+ return (props.isDraggable?.(item, index) ?? true);
149
173
  }
150
174
 
151
- function isToggable(item: T, index: number): boolean {
152
- const k = props.getItemKey(item, index);
153
- if (props.enableToggling === false) return false;
154
- return !isNil(toggledSetRef.value) && (toggableSetRef.value?.has(k) ?? true);
175
+ function isRemovableItem(item: T, index: number): boolean {
176
+ if (props.disableRemoving === true) return false;
177
+ return (props.isRemovable?.(item, index) ?? true);
155
178
  }
156
179
 
157
- function isToggled(item: T, index: number): boolean {
158
- const k = props.getItemKey(item, index);
159
- return toggledSetRef.value?.has(k) ?? false;
180
+ function isToggableItem(item: T, index: number): boolean {
181
+ if (props.disableToggling === true) return false;
182
+ return (props.isToggable?.(item, index) ?? (isFunction(props.isToggled) || isFunction(props.onToggle)));
160
183
  }
161
184
 
162
- function isPinnable(item: T, index: number): boolean {
163
- const k = props.getItemKey(item, index);
164
- if (props.enablePinning === false) return false;
165
- return !isNil(pinnedSetRef.value) && (pinnableSetRef.value?.has(k) ?? true);
185
+ function isToggledItem(item: T, index: number): boolean {
186
+ return props.isToggled?.(item, index) ?? false;
166
187
  }
167
188
 
168
- function isPinned(item: T, index: number): boolean {
169
- const k = props.getItemKey(item, index);
170
- return pinnedSetRef.value?.has(k) ?? false;
189
+ function isPinnableItem(item: T, index: number): boolean {
190
+ if (props.disablePinning === true) return false;
191
+ return (props.isPinnable?.(item, index) ?? (isFunction(props.isPinned) || isFunction(props.onPin)));
171
192
  }
172
193
 
173
- function isExpandable(item: T, index: number): boolean {
174
- const k = props.getItemKey(item, index);
175
- if (props.enableExpanding === false) return false;
176
- return !isNil(expandedSetRef.value) && (expandableSetRef.value?.has(k) ?? true);
194
+ function isPinnedItem(item: T, index: number): boolean {
195
+ return props.isPinned?.(item, index) ?? false;
177
196
  }
178
197
 
179
- function isExpanded(item: T, index: number): boolean {
180
- const k = props.getItemKey(item, index);
181
- return expandedSetRef.value?.has(k) ?? false;
198
+ function isExpandableItem(item: T, index: number): boolean {
199
+ if (props.disableExpanding === true) return false;
200
+ return (props.isExpandable?.(item, index) ?? (isFunction(props.isExpanded) || isFunction(props.onExpand)));
182
201
  }
183
202
 
184
- function isRemovable(item: T, index: number): boolean {
185
- const k = props.getItemKey(item, index);
186
- if (props.enableRemoving === false) return false;
187
- if (removableSetRef.value?.has(k) === false) return false;
188
- return props.enableRemoving === true || typeof props.onRemove === 'function';
203
+ function isExpandedItem(item: T, index: number): boolean {
204
+ return props.isExpanded?.(item, index) ?? false;
189
205
  }
190
206
 
191
207
  function handleExpand(item: T, index: number) {
192
- if (props.onExpand?.(item, index) === false || isNil(expandedSetRef.value)) return;
193
- const k = props.getItemKey(item, index);
194
- const expanded = expandedSetRef.value;
195
- if (expanded.has(k)) expanded.delete(k);
196
- else expanded.add(k);
197
- optionalUpdateRef(expandedSetRef);
208
+ props.onExpand?.(item, index);
198
209
  }
199
210
 
200
211
  function handleToggle(item: T, index: number) {
201
- if (props.onToggle?.(item, index) === false || isNil(toggledSetRef.value)) return;
202
- const k = props.getItemKey(item, index);
203
- const toggled = toggledSetRef.value;
204
- if (toggled.has(k)) toggled.delete(k);
205
- else toggled.add(k);
206
- optionalUpdateRef(toggledSetRef);
212
+ props.onToggle?.(item, index);
207
213
  }
208
214
 
209
215
  function handlePin(item: T, index: number) {
@@ -211,43 +217,18 @@ function handlePin(item: T, index: number) {
211
217
  throw new Error('Pinnable item not found');
212
218
  }
213
219
 
214
- if (props.onPin?.(item, index) === false || isNil(pinnedSetRef.value)) return;
220
+ const alreadyPinned = pinnedItemsRef.value.includes(item);
221
+
222
+ if (props.onPin?.(item, index) === false) return;
215
223
 
216
- const k = props.getItemKey(item, index);
217
- const pinned = pinnedSetRef.value;
218
- const alreadyPinned = pinned.has(k);
219
- if (alreadyPinned) pinned.delete(k);
220
- else pinned.add(k);
221
- optionalUpdateRef(pinnedSetRef);
222
- moveItems(index, pinned.size + (alreadyPinned ? 0 : -1), false);
224
+ moveItems(index, pinnedItemsRef.value.length + (alreadyPinned ? 0 : -1), false);
223
225
  }
224
226
 
225
227
  function handleRemove(item: T, index: number) {
226
- if (props.onRemove?.(item, index) !== false) {
227
- const k = props.getItemKey(item, index);
228
-
229
- itemsRef.value.splice(index, 1);
230
- optionalUpdateRef(itemsRef);
231
-
232
- if (pinnedSetRef.value?.has(k)) {
233
- pinnedSetRef.value.delete(k);
234
- optionalUpdateRef(pinnedSetRef);
235
- }
236
-
237
- if (toggledSetRef.value?.has(k)) {
238
- toggledSetRef.value.delete(k);
239
- optionalUpdateRef(toggledSetRef);
240
- }
241
- }
228
+ if (props.onRemove?.(item, index) === false) return;
229
+ itemsRef.value.splice(index, 1);
242
230
  }
243
231
 
244
- // version fix problem with sync between data and rendered values
245
- const getKey = (item: T, index: number) => {
246
- return `${versionRef.value}-${props.getItemKey(item, index)}`;
247
- };
248
- const pinnedKeysRef = computed(() => pinnedItemsRef.value.map(getKey));
249
- const unpinnedKeysRef = computed(() => unpinnedItemsRef.value.map(getKey));
250
-
251
232
  const getItemClass = (item: T, index: number): null | string | string[] => {
252
233
  if (typeof props.itemClass === 'function') {
253
234
  return props.itemClass(item, index);
@@ -267,15 +248,15 @@ const getItemClass = (item: T, index: number): null | string | string[] => {
267
248
  :index="pinnedIndex"
268
249
  :item="pinnedItem"
269
250
  :showDragHandle="dndSortingEnabled"
270
- :isActive="isActive(pinnedItem, pinnedIndex)"
271
- :isDraggable="isDraggable(pinnedItem, pinnedIndex)"
272
- :isRemovable="isRemovable(pinnedItem, pinnedIndex)"
273
- :isToggable="isToggable(pinnedItem, pinnedIndex)"
274
- :isToggled="isToggled(pinnedItem, pinnedIndex)"
275
- :isPinnable="isPinnable(pinnedItem, pinnedIndex)"
276
- :isPinned="isPinned(pinnedItem, pinnedIndex)"
277
- :isExpandable="isExpandable(pinnedItem, pinnedIndex)"
278
- :isExpanded="isExpanded(pinnedItem, pinnedIndex)"
251
+ :isActive="isActiveItem(pinnedItem, pinnedIndex)"
252
+ :isDraggable="isDraggableItem(pinnedItem, pinnedIndex)"
253
+ :isRemovable="isRemovableItem(pinnedItem, pinnedIndex)"
254
+ :isToggable="isToggableItem(pinnedItem, pinnedIndex)"
255
+ :isToggled="isToggledItem(pinnedItem, pinnedIndex)"
256
+ :isPinnable="isPinnableItem(pinnedItem, pinnedIndex)"
257
+ :isPinned="true"
258
+ :isExpandable="isExpandableItem(pinnedItem, pinnedIndex)"
259
+ :isExpanded="isExpandedItem(pinnedItem, pinnedIndex)"
279
260
 
280
261
  @click="emits('itemClick', pinnedItem)"
281
262
  @remove="handleRemove"
@@ -296,18 +277,18 @@ const getItemClass = (item: T, index: number): null | string | string[] => {
296
277
  v-for="(unpinnedItem, unpinnedIndex) in unpinnedItemsRef" :key="unpinnedKeysRef[unpinnedIndex]"
297
278
  :class="[$style.item, getItemClass(unpinnedItem, unpinnedIndex)]"
298
279
 
299
- :index="unpinnedIndex + (pinnedSetRef?.size ?? 0)"
280
+ :index="unpinnedIndex + (pinnedItemsRef?.length ?? 0)"
300
281
  :item="unpinnedItem"
301
282
  :showDragHandle="dndSortingEnabled"
302
- :isActive="isActive(unpinnedItem, unpinnedIndex)"
303
- :isDraggable="isDraggable(unpinnedItem, unpinnedIndex)"
304
- :isRemovable="isRemovable(unpinnedItem, unpinnedIndex)"
305
- :isToggable="isToggable(unpinnedItem, unpinnedIndex)"
306
- :isToggled="isToggled(unpinnedItem, unpinnedIndex)"
307
- :isPinnable="isPinnable(unpinnedItem, unpinnedIndex)"
308
- :isPinned="isPinned(unpinnedItem, unpinnedIndex)"
309
- :isExpandable="isExpandable(unpinnedItem, unpinnedIndex)"
310
- :isExpanded="isExpanded(unpinnedItem, unpinnedIndex)"
283
+ :isActive="isActiveItem(unpinnedItem, unpinnedIndex)"
284
+ :isDraggable="isDraggableItem(unpinnedItem, unpinnedIndex)"
285
+ :isRemovable="isRemovableItem(unpinnedItem, unpinnedIndex)"
286
+ :isToggable="isToggableItem(unpinnedItem, unpinnedIndex)"
287
+ :isToggled="isToggledItem(unpinnedItem, unpinnedIndex)"
288
+ :isPinnable="isPinnableItem(unpinnedItem, unpinnedIndex)"
289
+ :isPinned="false"
290
+ :isExpandable="isExpandableItem(unpinnedItem, unpinnedIndex)"
291
+ :isExpanded="isExpandedItem(unpinnedItem, unpinnedIndex)"
311
292
 
312
293
  @click="emits('itemClick', unpinnedItem)"
313
294
  @remove="handleRemove"
@@ -86,7 +86,10 @@ const emit = defineEmits<{
86
86
  </div>
87
87
  </div>
88
88
  </div>
89
- <div v-if="hasContentSlot && props.isExpanded" :class="$style.body">
89
+ <div
90
+ v-if="hasContentSlot && props.isExpanded"
91
+ :class="[$style.body, { [$style.disabled]: props.isToggled }]"
92
+ >
90
93
  <slot name="content" :item="props.item" :index="props.index" />
91
94
  </div>
92
95
  </div>
@@ -144,6 +147,7 @@ const emit = defineEmits<{
144
147
  display: flex;
145
148
  align-items: center;
146
149
  padding: 8px;
150
+ min-height: 40px;
147
151
  border-radius: var(--border-radius) var(--border-radius) 0 0;
148
152
  background: var(--head-background);
149
153
 
@@ -180,6 +184,10 @@ const emit = defineEmits<{
180
184
  gap: 12px;
181
185
  padding: 24px;
182
186
  border-radius: 0 0 var(--border-radius) var(--border-radius);
187
+
188
+ &.disabled {
189
+ pointer-events: none;
190
+ }
183
191
  }
184
192
 
185
193
  .actions {