@pequity/squirrel 5.4.3 → 5.4.5

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.
@@ -151,6 +151,13 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
151
151
  selectedTopShown: {
152
152
  type: Boolean,
153
153
  default: false
154
+ },
155
+ /**
156
+ * Specify the key in the object to be used as item disabled prop.
157
+ */
158
+ disabledBy: {
159
+ type: String,
160
+ default: "disabled"
154
161
  }
155
162
  },
156
163
  emits: ["update:modelValue", "select"],
@@ -181,6 +188,7 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
181
188
  const {
182
189
  LIST_ITEM_CLASS,
183
190
  LIST_ITEM_ACTIVE_CLASS,
191
+ LIST_ITEM_DISABLED_CLASS,
184
192
  selectedItems,
185
193
  computedItems,
186
194
  computedItemSize,
@@ -192,6 +200,7 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
192
200
  getValue,
193
201
  getText,
194
202
  isSelected,
203
+ isDisabled,
195
204
  setupNavigationSvc,
196
205
  destroyNavigationSvc,
197
206
  select,
@@ -340,7 +349,13 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
340
349
  style: vue.normalizeStyle({ height: `${row.size}px`, transform: `translateY(${row.start}px)` })
341
350
  }, [
342
351
  vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", {
343
- class: vue.normalizeClass([vue.unref(LIST_ITEM_CLASS), { [vue.unref(LIST_ITEM_ACTIVE_CLASS)]: vue.unref(isSelected)(vue.unref(getValue)(row.index)) }]),
352
+ class: vue.normalizeClass([
353
+ vue.unref(LIST_ITEM_CLASS),
354
+ {
355
+ [vue.unref(LIST_ITEM_ACTIVE_CLASS)]: vue.unref(isSelected)(vue.unref(getValue)(row.index)),
356
+ [vue.unref(LIST_ITEM_DISABLED_CLASS)]: vue.unref(isDisabled)(vue.unref(computedItems)[row.index])
357
+ }
358
+ ]),
344
359
  "p-select-list-option-item": "",
345
360
  style: vue.normalizeStyle(listItemStyle.value),
346
361
  onClick: ($event) => vue.unref(select)($event, vue.unref(getValue)(row.index))
package/dist/cjs/index.js CHANGED
@@ -452,6 +452,13 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
452
452
  focusSearchOnMount: {
453
453
  type: Boolean,
454
454
  default: true
455
+ },
456
+ /**
457
+ * Specify the key in the object to be used as item disabled prop.
458
+ */
459
+ disabledBy: {
460
+ type: String,
461
+ default: "disabled"
455
462
  }
456
463
  },
457
464
  emits: ["update:modelValue", "select"],
@@ -468,6 +475,7 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
468
475
  const {
469
476
  LIST_ITEM_CLASS,
470
477
  LIST_ITEM_ACTIVE_CLASS,
478
+ LIST_ITEM_DISABLED_CLASS,
471
479
  selectedItems,
472
480
  computedItems,
473
481
  computedItemSize,
@@ -478,6 +486,7 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
478
486
  getValue,
479
487
  getText,
480
488
  isSelected,
489
+ isDisabled,
481
490
  setupNavigationSvc,
482
491
  select,
483
492
  onFocus,
@@ -591,7 +600,13 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
591
600
  style: vue.normalizeStyle({ height: `${row.size}px`, transform: `translateY(${row.start}px)` })
592
601
  }, [
593
602
  vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", {
594
- class: vue.normalizeClass([vue.unref(LIST_ITEM_CLASS), { [vue.unref(LIST_ITEM_ACTIVE_CLASS)]: vue.unref(isSelected)(vue.unref(getValue)(row.index)) }]),
603
+ class: vue.normalizeClass([
604
+ vue.unref(LIST_ITEM_CLASS),
605
+ {
606
+ [vue.unref(LIST_ITEM_ACTIVE_CLASS)]: vue.unref(isSelected)(vue.unref(getValue)(row.index)),
607
+ [vue.unref(LIST_ITEM_DISABLED_CLASS)]: vue.unref(isDisabled)(vue.unref(computedItems)[row.index])
608
+ }
609
+ ]),
595
610
  "p-select-list-option-item": "",
596
611
  style: vue.normalizeStyle(listItemStyle.value),
597
612
  onClick: ($event) => vue.unref(select)($event, vue.unref(getValue)(row.index))
@@ -20,10 +20,12 @@ const usePTableRowVirtualizer = (options) => {
20
20
  );
21
21
  const measureElement = (cmp) => {
22
22
  const el = vue.isRef(cmp) ? vue.unref(cmp) : cmp == null ? void 0 : cmp.$el;
23
- if (!el) {
24
- return;
25
- }
26
- virtualizer.value.measureElement(el);
23
+ vue.nextTick(() => {
24
+ if (!el) {
25
+ return;
26
+ }
27
+ virtualizer.value.measureElement(el);
28
+ });
27
29
  return void 0;
28
30
  };
29
31
  return { virtualizer, virtualRows, paddingTop, paddingBottom, measureElement };
@@ -30,6 +30,7 @@ const nextLoop = (ms) => new Promise((resolve) => setTimeout(resolve, ms || 0));
30
30
  const LIST_ITEM_SIZES = { sm: 32, md: 40, lg: 48 };
31
31
  const LIST_ITEM_CLASS = "clear-both block w-full cursor-pointer whitespace-nowrap py-1 px-3 text-left text-sm font-medium hover:bg-p-blue-10";
32
32
  const LIST_ITEM_ACTIVE_CLASS = "selected";
33
+ const LIST_ITEM_DISABLED_CLASS = "opacity-30 pointer-events-none";
33
34
  const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
34
35
  let navigationSvc = null;
35
36
  const internalItems = vue.ref([]);
@@ -151,6 +152,13 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
151
152
  const isSelected = (val) => {
152
153
  return internalValue.value.includes(val);
153
154
  };
155
+ const isDisabled = (val) => {
156
+ const disabledProp = props.disabledBy;
157
+ if (val && typeof val === "object" && disabledProp in val) {
158
+ return val[disabledProp];
159
+ }
160
+ return false;
161
+ };
154
162
  const setupNavigationSvc = () => {
155
163
  if (navigationSvc) {
156
164
  navigationSvc.init();
@@ -231,6 +239,7 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
231
239
  LIST_ITEM_SIZES,
232
240
  LIST_ITEM_CLASS,
233
241
  LIST_ITEM_ACTIVE_CLASS,
242
+ LIST_ITEM_DISABLED_CLASS,
234
243
  selectedItems,
235
244
  computedItems,
236
245
  computedItemSize,
@@ -242,6 +251,7 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
242
251
  getValue,
243
252
  getText,
244
253
  isSelected,
254
+ isDisabled,
245
255
  setupNavigationSvc,
246
256
  destroyNavigationSvc,
247
257
  select,
@@ -150,6 +150,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
150
150
  selectedTopShown: {
151
151
  type: Boolean,
152
152
  default: false
153
+ },
154
+ /**
155
+ * Specify the key in the object to be used as item disabled prop.
156
+ */
157
+ disabledBy: {
158
+ type: String,
159
+ default: "disabled"
153
160
  }
154
161
  },
155
162
  emits: ["update:modelValue", "select"],
@@ -180,6 +187,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
180
187
  const {
181
188
  LIST_ITEM_CLASS,
182
189
  LIST_ITEM_ACTIVE_CLASS,
190
+ LIST_ITEM_DISABLED_CLASS,
183
191
  selectedItems,
184
192
  computedItems,
185
193
  computedItemSize,
@@ -191,6 +199,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
191
199
  getValue,
192
200
  getText,
193
201
  isSelected,
202
+ isDisabled,
194
203
  setupNavigationSvc,
195
204
  destroyNavigationSvc,
196
205
  select,
@@ -339,7 +348,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
339
348
  style: normalizeStyle({ height: `${row.size}px`, transform: `translateY(${row.start}px)` })
340
349
  }, [
341
350
  withDirectives((openBlock(), createElementBlock("div", {
342
- class: normalizeClass([unref(LIST_ITEM_CLASS), { [unref(LIST_ITEM_ACTIVE_CLASS)]: unref(isSelected)(unref(getValue)(row.index)) }]),
351
+ class: normalizeClass([
352
+ unref(LIST_ITEM_CLASS),
353
+ {
354
+ [unref(LIST_ITEM_ACTIVE_CLASS)]: unref(isSelected)(unref(getValue)(row.index)),
355
+ [unref(LIST_ITEM_DISABLED_CLASS)]: unref(isDisabled)(unref(computedItems)[row.index])
356
+ }
357
+ ]),
343
358
  "p-select-list-option-item": "",
344
359
  style: normalizeStyle(listItemStyle.value),
345
360
  onClick: ($event) => unref(select)($event, unref(getValue)(row.index))
package/dist/es/index.js CHANGED
@@ -452,6 +452,13 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
452
452
  focusSearchOnMount: {
453
453
  type: Boolean,
454
454
  default: true
455
+ },
456
+ /**
457
+ * Specify the key in the object to be used as item disabled prop.
458
+ */
459
+ disabledBy: {
460
+ type: String,
461
+ default: "disabled"
455
462
  }
456
463
  },
457
464
  emits: ["update:modelValue", "select"],
@@ -468,6 +475,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
468
475
  const {
469
476
  LIST_ITEM_CLASS,
470
477
  LIST_ITEM_ACTIVE_CLASS,
478
+ LIST_ITEM_DISABLED_CLASS,
471
479
  selectedItems,
472
480
  computedItems,
473
481
  computedItemSize,
@@ -478,6 +486,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
478
486
  getValue,
479
487
  getText,
480
488
  isSelected,
489
+ isDisabled,
481
490
  setupNavigationSvc,
482
491
  select,
483
492
  onFocus,
@@ -591,7 +600,13 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
591
600
  style: normalizeStyle({ height: `${row.size}px`, transform: `translateY(${row.start}px)` })
592
601
  }, [
593
602
  withDirectives((openBlock(), createElementBlock("div", {
594
- class: normalizeClass([unref(LIST_ITEM_CLASS), { [unref(LIST_ITEM_ACTIVE_CLASS)]: unref(isSelected)(unref(getValue)(row.index)) }]),
603
+ class: normalizeClass([
604
+ unref(LIST_ITEM_CLASS),
605
+ {
606
+ [unref(LIST_ITEM_ACTIVE_CLASS)]: unref(isSelected)(unref(getValue)(row.index)),
607
+ [unref(LIST_ITEM_DISABLED_CLASS)]: unref(isDisabled)(unref(computedItems)[row.index])
608
+ }
609
+ ]),
595
610
  "p-select-list-option-item": "",
596
611
  style: normalizeStyle(listItemStyle.value),
597
612
  onClick: ($event) => unref(select)($event, unref(getValue)(row.index))
@@ -1,4 +1,4 @@
1
- import { ref, computed, isRef, unref } from "vue";
1
+ import { ref, computed, isRef, unref, nextTick } from "vue";
2
2
  import { useVirtualizer } from "@tanstack/vue-virtual";
3
3
  const usePTableRowVirtualizer = (options) => {
4
4
  if (!options.value) {
@@ -18,10 +18,12 @@ const usePTableRowVirtualizer = (options) => {
18
18
  );
19
19
  const measureElement = (cmp) => {
20
20
  const el = isRef(cmp) ? unref(cmp) : cmp == null ? void 0 : cmp.$el;
21
- if (!el) {
22
- return;
23
- }
24
- virtualizer.value.measureElement(el);
21
+ nextTick(() => {
22
+ if (!el) {
23
+ return;
24
+ }
25
+ virtualizer.value.measureElement(el);
26
+ });
25
27
  return void 0;
26
28
  };
27
29
  return { virtualizer, virtualRows, paddingTop, paddingBottom, measureElement };
@@ -28,6 +28,7 @@ const nextLoop = (ms) => new Promise((resolve) => setTimeout(resolve, ms || 0));
28
28
  const LIST_ITEM_SIZES = { sm: 32, md: 40, lg: 48 };
29
29
  const LIST_ITEM_CLASS = "clear-both block w-full cursor-pointer whitespace-nowrap py-1 px-3 text-left text-sm font-medium hover:bg-p-blue-10";
30
30
  const LIST_ITEM_ACTIVE_CLASS = "selected";
31
+ const LIST_ITEM_DISABLED_CLASS = "opacity-30 pointer-events-none";
31
32
  const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
32
33
  let navigationSvc = null;
33
34
  const internalItems = ref([]);
@@ -149,6 +150,13 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
149
150
  const isSelected = (val) => {
150
151
  return internalValue.value.includes(val);
151
152
  };
153
+ const isDisabled = (val) => {
154
+ const disabledProp = props.disabledBy;
155
+ if (val && typeof val === "object" && disabledProp in val) {
156
+ return val[disabledProp];
157
+ }
158
+ return false;
159
+ };
152
160
  const setupNavigationSvc = () => {
153
161
  if (navigationSvc) {
154
162
  navigationSvc.init();
@@ -229,6 +237,7 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
229
237
  LIST_ITEM_SIZES,
230
238
  LIST_ITEM_CLASS,
231
239
  LIST_ITEM_ACTIVE_CLASS,
240
+ LIST_ITEM_DISABLED_CLASS,
232
241
  selectedItems,
233
242
  computedItems,
234
243
  computedItemSize,
@@ -240,6 +249,7 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
240
249
  getValue,
241
250
  getText,
242
251
  isSelected,
252
+ isDisabled,
243
253
  setupNavigationSvc,
244
254
  destroyNavigationSvc,
245
255
  select,
@@ -335,6 +335,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
335
335
  type: BooleanConstructor;
336
336
  default: boolean;
337
337
  };
338
+ /**
339
+ * Specify the key in the object to be used as item disabled prop.
340
+ */
341
+ disabledBy: {
342
+ type: PropType<string>;
343
+ default: string;
344
+ };
338
345
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
339
346
  select: (...args: any[]) => void;
340
347
  "update:modelValue": (...args: any[]) => void;
@@ -454,6 +461,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
454
461
  type: BooleanConstructor;
455
462
  default: boolean;
456
463
  };
464
+ /**
465
+ * Specify the key in the object to be used as item disabled prop.
466
+ */
467
+ disabledBy: {
468
+ type: PropType<string>;
469
+ default: string;
470
+ };
457
471
  }>> & Readonly<{
458
472
  onSelect?: ((...args: any[]) => any) | undefined;
459
473
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
@@ -478,6 +492,7 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
478
492
  multiple: boolean;
479
493
  placeholderSearch: string;
480
494
  selectedTopShown: boolean;
495
+ disabledBy: string;
481
496
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
482
497
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
483
498
  export default _default;
@@ -331,6 +331,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
331
331
  type: BooleanConstructor;
332
332
  default: boolean;
333
333
  };
334
+ /**
335
+ * Specify the key in the object to be used as item disabled prop.
336
+ */
337
+ disabledBy: {
338
+ type: PropType<string>;
339
+ default: string;
340
+ };
334
341
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
335
342
  select: (...args: any[]) => void;
336
343
  "update:modelValue": (...args: any[]) => void;
@@ -449,6 +456,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
449
456
  type: BooleanConstructor;
450
457
  default: boolean;
451
458
  };
459
+ /**
460
+ * Specify the key in the object to be used as item disabled prop.
461
+ */
462
+ disabledBy: {
463
+ type: PropType<string>;
464
+ default: string;
465
+ };
452
466
  }>> & Readonly<{
453
467
  onSelect?: ((...args: any[]) => any) | undefined;
454
468
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
@@ -469,6 +483,7 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
469
483
  multiple: boolean;
470
484
  placeholderSearch: string;
471
485
  selectedTopShown: boolean;
486
+ disabledBy: string;
472
487
  topSectionClass: string;
473
488
  closePopperOnSelect: boolean;
474
489
  focusSearchOnMount: boolean;
@@ -16,6 +16,7 @@ type Props = {
16
16
  searchable: boolean;
17
17
  multiple: boolean;
18
18
  selectedTopShown: boolean;
19
+ disabledBy: string;
19
20
  };
20
21
  type InputSearch = Ref<ComponentPublicInstance | null>;
21
22
  type VirtualizerRef = Ref<HTMLElement | null>;
@@ -27,6 +28,7 @@ export declare const useSelectList: (props: Props, inputSearch: InputSearch, vir
27
28
  };
28
29
  LIST_ITEM_CLASS: string;
29
30
  LIST_ITEM_ACTIVE_CLASS: string;
31
+ LIST_ITEM_DISABLED_CLASS: string;
30
32
  selectedItems: import("vue").ComputedRef<AnyObject[]>;
31
33
  computedItems: import("vue").ComputedRef<AnyObject[]>;
32
34
  computedItemSize: import("vue").ComputedRef<number>;
@@ -38,6 +40,7 @@ export declare const useSelectList: (props: Props, inputSearch: InputSearch, vir
38
40
  getValue: (index: number) => AnyValue;
39
41
  getText: (index: number) => AnyValue;
40
42
  isSelected: (val: AnyValue) => boolean;
43
+ isDisabled: (val: AnyValue) => unknown;
41
44
  setupNavigationSvc: () => void;
42
45
  destroyNavigationSvc: () => void;
43
46
  select: (e: Event, val: AnyValue) => Promise<void>;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "5.4.3",
5
- "packageManager": "pnpm@9.12.1",
4
+ "version": "5.4.5",
5
+ "packageManager": "pnpm@9.12.2",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "preinstall": "npx only-allow pnpm",
@@ -53,26 +53,26 @@
53
53
  "@commitlint/cli": "^19.5.0",
54
54
  "@commitlint/config-conventional": "^19.5.0",
55
55
  "@pequity/eslint-config": "^0.0.13",
56
- "@playwright/test": "^1.48.0",
56
+ "@playwright/test": "^1.48.1",
57
57
  "@popperjs/core": "2.11.8",
58
58
  "@semantic-release/changelog": "^6.0.3",
59
59
  "@semantic-release/git": "^10.0.1",
60
- "@storybook/addon-a11y": "^8.3.5",
61
- "@storybook/addon-actions": "^8.3.5",
62
- "@storybook/addon-essentials": "^8.3.5",
63
- "@storybook/addon-interactions": "^8.3.5",
64
- "@storybook/addon-links": "^8.3.5",
65
- "@storybook/blocks": "^8.3.5",
66
- "@storybook/manager-api": "^8.3.5",
67
- "@storybook/test": "^8.3.5",
60
+ "@storybook/addon-a11y": "^8.3.6",
61
+ "@storybook/addon-actions": "^8.3.6",
62
+ "@storybook/addon-essentials": "^8.3.6",
63
+ "@storybook/addon-interactions": "^8.3.6",
64
+ "@storybook/addon-links": "^8.3.6",
65
+ "@storybook/blocks": "^8.3.6",
66
+ "@storybook/manager-api": "^8.3.6",
67
+ "@storybook/test": "^8.3.6",
68
68
  "@storybook/test-runner": "^0.19.1",
69
- "@storybook/theming": "^8.3.5",
70
- "@storybook/vue3": "^8.3.5",
71
- "@storybook/vue3-vite": "^8.3.5",
69
+ "@storybook/theming": "^8.3.6",
70
+ "@storybook/vue3": "^8.3.6",
71
+ "@storybook/vue3-vite": "^8.3.6",
72
72
  "@tanstack/vue-virtual": "3.10.8",
73
73
  "@types/jsdom": "^21.1.7",
74
74
  "@types/lodash-es": "^4.17.12",
75
- "@types/node": "^22.7.5",
75
+ "@types/node": "^22.7.6",
76
76
  "@vitejs/plugin-vue": "^5.1.4",
77
77
  "@vitest/coverage-v8": "^2.1.3",
78
78
  "@vue/compiler-sfc": "3.5.12",
@@ -94,9 +94,9 @@
94
94
  "prettier-plugin-tailwindcss": "^0.6.8",
95
95
  "resolve-tspaths": "^0.8.22",
96
96
  "rimraf": "^6.0.1",
97
- "sass": "^1.79.5",
98
- "semantic-release": "^24.1.2",
99
- "storybook": "^8.3.5",
97
+ "sass": "^1.80.2",
98
+ "semantic-release": "^24.1.3",
99
+ "storybook": "^8.3.6",
100
100
  "svgo": "^3.3.2",
101
101
  "tailwindcss": "^3.4.14",
102
102
  "typescript": "5.6.3",
@@ -533,4 +533,55 @@ describe('PDropdownSelect.vue', () => {
533
533
 
534
534
  expect(wrapper.vm.$data.selected).toEqual([1, 3]);
535
535
  });
536
+
537
+ it('disables the item when the disabled item prop is set', async () => {
538
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
539
+
540
+ const items = cloneDeep(filterListItems).slice(0, 10);
541
+ items[0].disabled = true;
542
+ items[1].disabled = true;
543
+ const wrapper = createWrapper({ selected: [], items }, { multiple: true });
544
+
545
+ const button = wrapper.find('button');
546
+
547
+ await button.trigger('click');
548
+
549
+ const listItems = wrapper.findAll('[p-select-list-option-item]');
550
+
551
+ listItems.forEach((item, i) => {
552
+ if (i === 0 || i === 1) {
553
+ expect(item.classes()).toContain('pointer-events-none');
554
+ } else {
555
+ expect(item.classes()).not.toContain('pointer-events-none');
556
+ }
557
+ });
558
+
559
+ cleanup(wrapper);
560
+ });
561
+
562
+ it('disables the item when the disabledBy prop is set', async () => {
563
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
564
+
565
+ const items = cloneDeep(filterListItems).slice(0, 10);
566
+ items[0].inactive = true;
567
+ items[1].inactive = true;
568
+ items[2].disabled = true;
569
+ const wrapper = createWrapper({ selected: [], items }, { multiple: true, disabledBy: 'inactive' });
570
+
571
+ const button = wrapper.find('button');
572
+
573
+ await button.trigger('click');
574
+
575
+ const listItems = wrapper.findAll('[p-select-list-option-item]');
576
+
577
+ listItems.forEach((item, i) => {
578
+ if (i === 0 || i === 1) {
579
+ expect(item.classes()).toContain('pointer-events-none');
580
+ } else {
581
+ expect(item.classes()).not.toContain('pointer-events-none');
582
+ }
583
+ });
584
+
585
+ cleanup(wrapper);
586
+ });
536
587
  });
@@ -98,7 +98,13 @@
98
98
  >
99
99
  <div
100
100
  v-close-popper="!multiple"
101
- :class="[LIST_ITEM_CLASS, { [LIST_ITEM_ACTIVE_CLASS]: isSelected(getValue(row.index)) }]"
101
+ :class="[
102
+ LIST_ITEM_CLASS,
103
+ {
104
+ [LIST_ITEM_ACTIVE_CLASS]: isSelected(getValue(row.index)),
105
+ [LIST_ITEM_DISABLED_CLASS]: isDisabled(computedItems[row.index]),
106
+ },
107
+ ]"
102
108
  p-select-list-option-item
103
109
  :style="listItemStyle"
104
110
  @click="select($event, getValue(row.index))"
@@ -297,6 +303,13 @@ const props = defineProps({
297
303
  type: Boolean,
298
304
  default: false,
299
305
  },
306
+ /**
307
+ * Specify the key in the object to be used as item disabled prop.
308
+ */
309
+ disabledBy: {
310
+ type: String as PropType<string>,
311
+ default: 'disabled',
312
+ },
300
313
  });
301
314
 
302
315
  // Async helpers
@@ -328,6 +341,7 @@ const { labelClasses, selectClasses, errorMsgClasses } = useInputClasses(props);
328
341
  const {
329
342
  LIST_ITEM_CLASS,
330
343
  LIST_ITEM_ACTIVE_CLASS,
344
+ LIST_ITEM_DISABLED_CLASS,
331
345
  selectedItems,
332
346
  computedItems,
333
347
  computedItemSize,
@@ -339,6 +353,7 @@ const {
339
353
  getValue,
340
354
  getText,
341
355
  isSelected,
356
+ isDisabled,
342
357
  setupNavigationSvc,
343
358
  destroyNavigationSvc,
344
359
  select,
@@ -479,4 +479,47 @@ describe('PSelectList.vue', () => {
479
479
 
480
480
  cleanup(wrapper);
481
481
  });
482
+
483
+ it('disables the item when the disabled item prop is set', async () => {
484
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
485
+
486
+ const items = cloneDeep(filterListItems).slice(0, 10);
487
+ items[0].disabled = true;
488
+ items[1].disabled = true;
489
+ const wrapper = createWrapper({ selected: [], items }, { multiple: true });
490
+
491
+ const listItems = wrapper.findAll('[p-select-list-option-item]');
492
+
493
+ listItems.forEach((item, i) => {
494
+ if (i === 0 || i === 1) {
495
+ expect(item.classes()).toContain('pointer-events-none');
496
+ } else {
497
+ expect(item.classes()).not.toContain('pointer-events-none');
498
+ }
499
+ });
500
+
501
+ cleanup(wrapper);
502
+ });
503
+
504
+ it('disables the item when the disabledBy prop is set', async () => {
505
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
506
+
507
+ const items = cloneDeep(filterListItems).slice(0, 10);
508
+ items[0].inactive = true;
509
+ items[1].inactive = true;
510
+ items[2].disabled = true;
511
+ const wrapper = createWrapper({ selected: [], items }, { multiple: true, disabledBy: 'inactive' });
512
+
513
+ const listItems = wrapper.findAll('[p-select-list-option-item]');
514
+
515
+ listItems.forEach((item, i) => {
516
+ if (i === 0 || i === 1) {
517
+ expect(item.classes()).toContain('pointer-events-none');
518
+ } else {
519
+ expect(item.classes()).not.toContain('pointer-events-none');
520
+ }
521
+ });
522
+
523
+ cleanup(wrapper);
524
+ });
482
525
  });
@@ -71,7 +71,13 @@
71
71
  >
72
72
  <div
73
73
  v-close-popper="closePopperOnSelect && !multiple"
74
- :class="[LIST_ITEM_CLASS, { [LIST_ITEM_ACTIVE_CLASS]: isSelected(getValue(row.index)) }]"
74
+ :class="[
75
+ LIST_ITEM_CLASS,
76
+ {
77
+ [LIST_ITEM_ACTIVE_CLASS]: isSelected(getValue(row.index)),
78
+ [LIST_ITEM_DISABLED_CLASS]: isDisabled(computedItems[row.index]),
79
+ },
80
+ ]"
75
81
  p-select-list-option-item
76
82
  :style="listItemStyle"
77
83
  @click="select($event, getValue(row.index))"
@@ -258,6 +264,13 @@ const props = defineProps({
258
264
  type: Boolean,
259
265
  default: true,
260
266
  },
267
+ /**
268
+ * Specify the key in the object to be used as item disabled prop.
269
+ */
270
+ disabledBy: {
271
+ type: String as PropType<string>,
272
+ default: 'disabled',
273
+ },
261
274
  });
262
275
 
263
276
  // Data
@@ -273,6 +286,7 @@ const virtualizerRef = ref<HTMLElement | null>(null);
273
286
  const {
274
287
  LIST_ITEM_CLASS,
275
288
  LIST_ITEM_ACTIVE_CLASS,
289
+ LIST_ITEM_DISABLED_CLASS,
276
290
  selectedItems,
277
291
  computedItems,
278
292
  computedItemSize,
@@ -283,6 +297,7 @@ const {
283
297
  getValue,
284
298
  getText,
285
299
  isSelected,
300
+ isDisabled,
286
301
  setupNavigationSvc,
287
302
  select,
288
303
  onFocus,
@@ -31,6 +31,7 @@ type Props = {
31
31
  searchable: boolean;
32
32
  multiple: boolean;
33
33
  selectedTopShown: boolean;
34
+ disabledBy: string;
34
35
  };
35
36
 
36
37
  type InputSearch = Ref<ComponentPublicInstance | null>;
@@ -45,6 +46,7 @@ const LIST_ITEM_SIZES = { sm: 32, md: 40, lg: 48 };
45
46
  const LIST_ITEM_CLASS =
46
47
  'clear-both block w-full cursor-pointer whitespace-nowrap py-1 px-3 text-left text-sm font-medium hover:bg-p-blue-10';
47
48
  const LIST_ITEM_ACTIVE_CLASS = 'selected';
49
+ const LIST_ITEM_DISABLED_CLASS = 'opacity-30 pointer-events-none';
48
50
 
49
51
  export const useSelectList = (props: Props, inputSearch: InputSearch, virtualizerRef: VirtualizerRef, emit: Emits) => {
50
52
  // Data
@@ -207,6 +209,16 @@ export const useSelectList = (props: Props, inputSearch: InputSearch, virtualize
207
209
  return internalValue.value.includes(val);
208
210
  };
209
211
 
212
+ const isDisabled = (val: AnyValue) => {
213
+ const disabledProp = props.disabledBy;
214
+
215
+ if (val && typeof val === 'object' && disabledProp in val) {
216
+ return val[disabledProp];
217
+ }
218
+
219
+ return false;
220
+ };
221
+
210
222
  const setupNavigationSvc = () => {
211
223
  if (navigationSvc) {
212
224
  navigationSvc.init();
@@ -311,6 +323,7 @@ export const useSelectList = (props: Props, inputSearch: InputSearch, virtualize
311
323
  LIST_ITEM_SIZES,
312
324
  LIST_ITEM_CLASS,
313
325
  LIST_ITEM_ACTIVE_CLASS,
326
+ LIST_ITEM_DISABLED_CLASS,
314
327
  selectedItems,
315
328
  computedItems,
316
329
  computedItemSize,
@@ -322,6 +335,7 @@ export const useSelectList = (props: Props, inputSearch: InputSearch, virtualize
322
335
  getValue,
323
336
  getText,
324
337
  isSelected,
338
+ isDisabled,
325
339
  setupNavigationSvc,
326
340
  destroyNavigationSvc,
327
341
  select,
@@ -1,4 +1,4 @@
1
- import { type ComponentPublicInstance, type ComputedRef, type Ref, computed, isRef, ref, unref } from 'vue';
1
+ import { type ComponentPublicInstance, type ComputedRef, type Ref, computed, isRef, nextTick, ref, unref } from 'vue';
2
2
  import { useVirtualizer } from '@tanstack/vue-virtual';
3
3
 
4
4
  type Options = ComputedRef<{
@@ -35,11 +35,13 @@ export const usePTableRowVirtualizer = (options: Options) => {
35
35
  const measureElement = (cmp: ComponentPublicInstance | Ref<HTMLElement>) => {
36
36
  const el = isRef(cmp) ? unref(cmp) : cmp?.$el;
37
37
 
38
- if (!el) {
39
- return;
40
- }
38
+ nextTick(() => {
39
+ if (!el) {
40
+ return;
41
+ }
41
42
 
42
- virtualizer.value.measureElement(el);
43
+ virtualizer.value.measureElement(el);
44
+ });
43
45
 
44
46
  return undefined;
45
47
  };