@pequity/squirrel 5.4.4 → 5.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { config } from "./config.js";
2
- import { _, a } from "./chunks/p-action-bar.js";
2
+ import { _ } from "./chunks/p-action-bar.js";
3
3
  import { default as default2 } from "./p-alert.js";
4
4
  import { default as default3 } from "./p-avatar.js";
5
5
  import { default as default4 } from "./p-btn.js";
@@ -11,7 +11,7 @@ import { _ as _3 } from "./chunks/p-date-picker.js";
11
11
  import { default as default8 } from "./p-drawer.js";
12
12
  import { default as default9 } from "./p-dropdown.js";
13
13
  import { _ as _imports_0$2 } from "./chunks/p-dropdown-select.js";
14
- import { a as a2 } from "./chunks/p-dropdown-select.js";
14
+ import { a } from "./chunks/p-dropdown-select.js";
15
15
  import { defineComponent, shallowRef, ref, computed, onMounted, openBlock, createElementBlock, normalizeClass, unref, toDisplayString, createCommentVNode, createElementVNode, withModifiers, createTextVNode, Fragment, renderList, withDirectives, vShow, useAttrs, resolveDirective, normalizeStyle, createVNode, isRef, renderSlot, provide, onBeforeUnmount, watch, mergeProps, toHandlers } from "vue";
16
16
  import { formatBytes, getFileExtension } from "./p-file-upload.js";
17
17
  import { uniq, kebabCase } from "lodash-es";
@@ -19,21 +19,22 @@ import { useInputClasses } from "./useInputClasses.js";
19
19
  import { useToast } from "vue-toastification";
20
20
  import { _ as _export_sfc } from "./chunks/_plugin-vue_export-helper.js";
21
21
  import { default as default10 } from "./p-table-filter-icon.js";
22
+ import { _ as _4 } from "./chunks/p-icon.js";
22
23
  import { default as default11 } from "./p-info-icon.js";
23
- import { _ as _4 } from "./chunks/p-inline-date-picker.js";
24
+ import { _ as _5 } from "./chunks/p-inline-date-picker.js";
24
25
  import { default as default12 } from "./p-input.js";
25
26
  import { default as default13 } from "./p-input-number.js";
26
- import { _ as _5 } from "./chunks/p-input-percent.js";
27
+ import { _ as _6 } from "./chunks/p-input-percent.js";
27
28
  import PInputSearch from "./p-input-search.js";
28
- import { _ as _6 } from "./chunks/p-link.js";
29
+ import { _ as _7 } from "./chunks/p-link.js";
29
30
  import { default as default14 } from "./p-loading.js";
30
31
  import { default as default15 } from "./p-modal.js";
31
- import { _ as _7 } from "./chunks/p-pagination.js";
32
- import { _ as _8 } from "./chunks/p-pagination-info.js";
32
+ import { _ as _8 } from "./chunks/p-pagination.js";
33
+ import { _ as _9 } from "./chunks/p-pagination-info.js";
33
34
  import { default as default16 } from "./p-progress-bar.js";
34
- import { _ as _9 } from "./chunks/p-ring-loader.js";
35
- import { _ as _10 } from "./chunks/p-select.js";
36
- import { _ as _11 } from "./chunks/p-select-btn.js";
35
+ import { _ as _10 } from "./chunks/p-ring-loader.js";
36
+ import { _ as _11 } from "./chunks/p-select.js";
37
+ import { _ as _12 } from "./chunks/p-select-btn.js";
37
38
  import { SIZES } from "./p-select-list.js";
38
39
  import { splitStringForHighlight } from "./text.js";
39
40
  import { toString } from "./string.js";
@@ -44,10 +45,10 @@ import PTableHeaderCell from "./p-table-header-cell.js";
44
45
  import { colsInjectionKey, isFirstColFixedInjectionKey, isLastColFixedInjectionKey, isColsResizableInjectionKey } from "./p-table.js";
45
46
  import { MIN_WIDTH_COL_RESIZE } from "./p-table.js";
46
47
  import { usePTableColResize } from "./usePTableColResize.js";
47
- import { _ as _12 } from "./chunks/p-table-loader.js";
48
+ import { _ as _13 } from "./chunks/p-table-loader.js";
48
49
  import { SORTING_TYPES } from "./p-table-sort.js";
49
50
  import { default as default19 } from "./p-table-td.js";
50
- import { _ as _13 } from "./chunks/p-tabs.js";
51
+ import { _ as _14 } from "./chunks/p-tabs.js";
51
52
  import { default as default20 } from "./p-textarea.js";
52
53
  import { default as default21 } from "./p-toggle.js";
53
54
  import { P_ICON_ALIASES } from "./p-icon.js";
@@ -452,6 +453,13 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
452
453
  focusSearchOnMount: {
453
454
  type: Boolean,
454
455
  default: true
456
+ },
457
+ /**
458
+ * Specify the key in the object to be used as item disabled prop.
459
+ */
460
+ disabledBy: {
461
+ type: String,
462
+ default: "disabled"
455
463
  }
456
464
  },
457
465
  emits: ["update:modelValue", "select"],
@@ -468,6 +476,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
468
476
  const {
469
477
  LIST_ITEM_CLASS,
470
478
  LIST_ITEM_ACTIVE_CLASS,
479
+ LIST_ITEM_DISABLED_CLASS,
471
480
  selectedItems,
472
481
  computedItems,
473
482
  computedItemSize,
@@ -478,6 +487,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
478
487
  getValue,
479
488
  getText,
480
489
  isSelected,
490
+ isDisabled,
481
491
  setupNavigationSvc,
482
492
  select,
483
493
  onFocus,
@@ -591,7 +601,13 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
591
601
  style: normalizeStyle({ height: `${row.size}px`, transform: `translateY(${row.start}px)` })
592
602
  }, [
593
603
  withDirectives((openBlock(), createElementBlock("div", {
594
- class: normalizeClass([unref(LIST_ITEM_CLASS), { [unref(LIST_ITEM_ACTIVE_CLASS)]: unref(isSelected)(unref(getValue)(row.index)) }]),
604
+ class: normalizeClass([
605
+ unref(LIST_ITEM_CLASS),
606
+ {
607
+ [unref(LIST_ITEM_ACTIVE_CLASS)]: unref(isSelected)(unref(getValue)(row.index)),
608
+ [unref(LIST_ITEM_DISABLED_CLASS)]: unref(isDisabled)(unref(computedItems)[row.index])
609
+ }
610
+ ]),
595
611
  "p-select-list-option-item": "",
596
612
  style: normalizeStyle(listItemStyle.value),
597
613
  onClick: ($event) => unref(select)($event, unref(getValue)(row.index))
@@ -959,34 +975,34 @@ export {
959
975
  _3 as PDatePicker,
960
976
  default8 as PDrawer,
961
977
  default9 as PDropdown,
962
- a2 as PDropdownSelect,
978
+ a as PDropdownSelect,
963
979
  pFileUpload as PFileUpload,
964
980
  default10 as PFilterIcon,
965
- a as PIcon,
981
+ _4 as PIcon,
966
982
  default11 as PInfoIcon,
967
- _4 as PInlineDatePicker,
983
+ _5 as PInlineDatePicker,
968
984
  default12 as PInput,
969
985
  default13 as PInputNumber,
970
- _5 as PInputPercent,
986
+ _6 as PInputPercent,
971
987
  PInputSearch,
972
- _6 as PLink,
988
+ _7 as PLink,
973
989
  default14 as PLoading,
974
990
  default15 as PModal,
975
- _7 as PPagination,
976
- _8 as PPaginationInfo,
991
+ _8 as PPagination,
992
+ _9 as PPaginationInfo,
977
993
  default16 as PProgressBar,
978
- _9 as PRingLoader,
979
- _10 as PSelect,
980
- _11 as PSelectBtn,
994
+ _10 as PRingLoader,
995
+ _11 as PSelect,
996
+ _12 as PSelectBtn,
981
997
  _sfc_main$2 as PSelectList,
982
998
  default17 as PSelectPill,
983
999
  default18 as PSkeletonLoader,
984
1000
  pTable as PTable,
985
1001
  PTableHeaderCell,
986
- _12 as PTableLoader,
1002
+ _13 as PTableLoader,
987
1003
  pTableSort as PTableSort,
988
1004
  default19 as PTableTd,
989
- _13 as PTabs,
1005
+ _14 as PTabs,
990
1006
  default20 as PTextarea,
991
1007
  default21 as PToggle,
992
1008
  P_ICON_ALIASES,
@@ -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,
@@ -307,6 +307,10 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
307
307
  type: BooleanConstructor;
308
308
  default: boolean;
309
309
  };
310
+ clearable: {
311
+ type: BooleanConstructor;
312
+ default: boolean;
313
+ };
310
314
  /**
311
315
  * Enables multiple selection
312
316
  */
@@ -335,6 +339,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
335
339
  type: BooleanConstructor;
336
340
  default: boolean;
337
341
  };
342
+ /**
343
+ * Specify the key in the object to be used as item disabled prop.
344
+ */
345
+ disabledBy: {
346
+ type: PropType<string>;
347
+ default: string;
348
+ };
338
349
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
339
350
  select: (...args: any[]) => void;
340
351
  "update:modelValue": (...args: any[]) => void;
@@ -426,6 +437,10 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
426
437
  type: BooleanConstructor;
427
438
  default: boolean;
428
439
  };
440
+ clearable: {
441
+ type: BooleanConstructor;
442
+ default: boolean;
443
+ };
429
444
  /**
430
445
  * Enables multiple selection
431
446
  */
@@ -454,6 +469,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
454
469
  type: BooleanConstructor;
455
470
  default: boolean;
456
471
  };
472
+ /**
473
+ * Specify the key in the object to be used as item disabled prop.
474
+ */
475
+ disabledBy: {
476
+ type: PropType<string>;
477
+ default: string;
478
+ };
457
479
  }>> & Readonly<{
458
480
  onSelect?: ((...args: any[]) => any) | undefined;
459
481
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
@@ -475,9 +497,11 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
475
497
  dropdownMenuStyle: Record<string, any>;
476
498
  pDropdownProps: Record<string, any>;
477
499
  searchable: boolean;
500
+ clearable: boolean;
478
501
  multiple: boolean;
479
502
  placeholderSearch: string;
480
503
  selectedTopShown: boolean;
504
+ disabledBy: string;
481
505
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
482
506
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
483
507
  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,7 +1,7 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "5.4.4",
4
+ "version": "5.4.6",
5
5
  "packageManager": "pnpm@9.12.2",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -95,7 +95,7 @@
95
95
  "resolve-tspaths": "^0.8.22",
96
96
  "rimraf": "^6.0.1",
97
97
  "sass": "^1.80.2",
98
- "semantic-release": "^24.1.2",
98
+ "semantic-release": "^24.1.3",
99
99
  "storybook": "^8.3.6",
100
100
  "svgo": "^3.3.2",
101
101
  "tailwindcss": "^3.4.14",
@@ -533,4 +533,91 @@ 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
+ cleanup(wrapper);
559
+ });
560
+
561
+ it('disables the item when the disabledBy prop is set', async () => {
562
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
563
+
564
+ const items = cloneDeep(filterListItems).slice(0, 10);
565
+ items[0].inactive = true;
566
+ items[1].inactive = true;
567
+ items[2].disabled = true;
568
+ const wrapper = createWrapper({ selected: [], items }, { multiple: true, disabledBy: 'inactive' });
569
+
570
+ const button = wrapper.find('button');
571
+
572
+ await button.trigger('click');
573
+
574
+ const listItems = wrapper.findAll('[p-select-list-option-item]');
575
+
576
+ listItems.forEach((item, i) => {
577
+ if (i === 0 || i === 1) {
578
+ expect(item.classes()).toContain('pointer-events-none');
579
+ } else {
580
+ expect(item.classes()).not.toContain('pointer-events-none');
581
+ }
582
+ });
583
+ cleanup(wrapper);
584
+ });
585
+
586
+ it('renders clear button when clearable is true and a value is selected', async () => {
587
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
588
+
589
+ const wrapper = createWrapper({ selected: 17 }, { clearable: true });
590
+
591
+ const clearButton = wrapper.find('button[aria-label="Clear selection"]');
592
+ expect(clearButton.exists()).toBe(true);
593
+
594
+ await clearButton.trigger('click');
595
+ expect(wrapper.vm.$data.selected).toEqual([]);
596
+
597
+ cleanup(wrapper);
598
+ });
599
+
600
+ it('does not render clear button when clearable is false', async () => {
601
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
602
+
603
+ const wrapper = createWrapper({ selected: 17 }, { clearable: false });
604
+
605
+ const clearButton = wrapper.find('button[aria-label="Clear selection"]');
606
+ expect(clearButton.exists()).toBe(false);
607
+
608
+ cleanup(wrapper);
609
+ });
610
+
611
+ it('clears multiple selections when clearable is true', async () => {
612
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
613
+
614
+ const wrapper = createWrapper({ selected: [4, 5] }, { multiple: true, clearable: true });
615
+
616
+ const clearButton = wrapper.find('button[aria-label="Clear selection"]');
617
+ expect(clearButton.exists()).toBe(true);
618
+
619
+ await clearButton.trigger('click');
620
+ expect(wrapper.vm.$data.selected).toEqual([]);
621
+ cleanup(wrapper);
622
+ });
536
623
  });
@@ -69,6 +69,10 @@ export default {
69
69
  }),
70
70
  argTypes: {
71
71
  ...fieldArgTypes,
72
+ clearable: {
73
+ control: 'boolean',
74
+ description: 'Whether the dropdown should be clearable',
75
+ },
72
76
  },
73
77
  parameters: {
74
78
  docs: {
@@ -289,3 +293,22 @@ export const SelectedOnTop = {
289
293
  selectedTopShown: true,
290
294
  },
291
295
  };
296
+
297
+ export const Clearable = {
298
+ render: createRenderFunction({ selectedVal: 17 }),
299
+ args: {
300
+ ...Default.args,
301
+ label: 'Clearable dropdown',
302
+ clearable: true,
303
+ },
304
+ };
305
+
306
+ export const ClearableMultiple = {
307
+ render: createRenderFunction({ selectedVal: [33, 34, 36] }),
308
+ args: {
309
+ ...Default.args,
310
+ label: 'Clearable multiple dropdown',
311
+ multiple: true,
312
+ clearable: true,
313
+ },
314
+ };
@@ -36,6 +36,16 @@
36
36
  }}
37
37
  </div>
38
38
  </slot>
39
+ <!-- Add clear button -->
40
+ <button
41
+ v-if="clearable && internalValue.length"
42
+ class="absolute top-1/2 flex -translate-y-1/2 items-center justify-center text-p-gray-40 hover:text-p-gray-60"
43
+ :class="[SIZES[size], CLEAR_BUTTON_SPACING[size]]"
44
+ aria-label="Clear selection"
45
+ @click.stop="clearAll"
46
+ >
47
+ <PIcon icon="fe:close" />
48
+ </button>
39
49
  </button>
40
50
  <!-- Dropdown -->
41
51
  <template #popper>
@@ -98,7 +108,13 @@
98
108
  >
99
109
  <div
100
110
  v-close-popper="!multiple"
101
- :class="[LIST_ITEM_CLASS, { [LIST_ITEM_ACTIVE_CLASS]: isSelected(getValue(row.index)) }]"
111
+ :class="[
112
+ LIST_ITEM_CLASS,
113
+ {
114
+ [LIST_ITEM_ACTIVE_CLASS]: isSelected(getValue(row.index)),
115
+ [LIST_ITEM_DISABLED_CLASS]: isDisabled(computedItems[row.index]),
116
+ },
117
+ ]"
102
118
  p-select-list-option-item
103
119
  :style="listItemStyle"
104
120
  @click="select($event, getValue(row.index))"
@@ -142,6 +158,7 @@
142
158
 
143
159
  <script setup lang="ts">
144
160
  import PDropdown from '@squirrel/components/p-dropdown/p-dropdown.vue';
161
+ import PIcon from '@squirrel/components/p-icon/p-icon.vue';
145
162
  import PInputSearch from '@squirrel/components/p-input-search/p-input-search.vue';
146
163
  import {
147
164
  type ComponentPublicInstance,
@@ -269,6 +286,10 @@ const props = defineProps({
269
286
  type: Boolean,
270
287
  default: false,
271
288
  },
289
+ clearable: {
290
+ type: Boolean,
291
+ default: false,
292
+ },
272
293
  /**
273
294
  * Enables multiple selection
274
295
  */
@@ -297,6 +318,13 @@ const props = defineProps({
297
318
  type: Boolean,
298
319
  default: false,
299
320
  },
321
+ /**
322
+ * Specify the key in the object to be used as item disabled prop.
323
+ */
324
+ disabledBy: {
325
+ type: String as PropType<string>,
326
+ default: 'disabled',
327
+ },
300
328
  });
301
329
 
302
330
  // Async helpers
@@ -313,6 +341,12 @@ const P_DROPDOWN_DEFAULTS = {
313
341
  enableArrowNavigation: false,
314
342
  };
315
343
 
344
+ const CLEAR_BUTTON_SPACING = {
345
+ sm: 'right-8',
346
+ md: 'right-9',
347
+ lg: 'right-10',
348
+ };
349
+
316
350
  const width = ref('auto');
317
351
  const listItemStyle = ref({ paddingTop: 0, paddingBottom: 0 });
318
352
  const dropdownShow = ref(false);
@@ -328,6 +362,7 @@ const { labelClasses, selectClasses, errorMsgClasses } = useInputClasses(props);
328
362
  const {
329
363
  LIST_ITEM_CLASS,
330
364
  LIST_ITEM_ACTIVE_CLASS,
365
+ LIST_ITEM_DISABLED_CLASS,
331
366
  selectedItems,
332
367
  computedItems,
333
368
  computedItemSize,
@@ -339,6 +374,7 @@ const {
339
374
  getValue,
340
375
  getText,
341
376
  isSelected,
377
+ isDisabled,
342
378
  setupNavigationSvc,
343
379
  destroyNavigationSvc,
344
380
  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,