@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/cjs/chunks/p-action-bar.js +4 -2181
- package/dist/cjs/chunks/p-dropdown-select.js +46 -9
- package/dist/cjs/chunks/p-icon.js +2180 -0
- package/dist/cjs/index.js +18 -2
- package/dist/cjs/useSelectList.js +10 -0
- package/dist/es/chunks/p-action-bar.js +3 -2180
- package/dist/es/chunks/p-dropdown-select.js +47 -10
- package/dist/es/chunks/p-icon.js +2181 -0
- package/dist/es/index.js +41 -25
- package/dist/es/useSelectList.js +10 -0
- package/dist/squirrel/components/p-dropdown-select/p-dropdown-select.vue.d.ts +24 -0
- package/dist/squirrel/components/p-select-list/p-select-list.vue.d.ts +15 -0
- package/dist/squirrel/components/p-select-list/useSelectList.d.ts +3 -0
- package/package.json +2 -2
- package/squirrel/components/p-dropdown-select/p-dropdown-select.spec.js +87 -0
- package/squirrel/components/p-dropdown-select/p-dropdown-select.stories.js +23 -0
- package/squirrel/components/p-dropdown-select/p-dropdown-select.vue +37 -1
- package/squirrel/components/p-select-list/p-select-list.spec.js +43 -0
- package/squirrel/components/p-select-list/p-select-list.vue +16 -1
- package/squirrel/components/p-select-list/useSelectList.ts +14 -0
package/dist/es/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { config } from "./config.js";
|
|
2
|
-
import { _
|
|
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
|
|
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
|
|
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
|
|
27
|
+
import { _ as _6 } from "./chunks/p-input-percent.js";
|
|
27
28
|
import PInputSearch from "./p-input-search.js";
|
|
28
|
-
import { _ as
|
|
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
|
|
32
|
-
import { _ as
|
|
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
|
|
35
|
-
import { _ as
|
|
36
|
-
import { _ as
|
|
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
|
|
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
|
|
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([
|
|
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
|
-
|
|
978
|
+
a as PDropdownSelect,
|
|
963
979
|
pFileUpload as PFileUpload,
|
|
964
980
|
default10 as PFilterIcon,
|
|
965
|
-
|
|
981
|
+
_4 as PIcon,
|
|
966
982
|
default11 as PInfoIcon,
|
|
967
|
-
|
|
983
|
+
_5 as PInlineDatePicker,
|
|
968
984
|
default12 as PInput,
|
|
969
985
|
default13 as PInputNumber,
|
|
970
|
-
|
|
986
|
+
_6 as PInputPercent,
|
|
971
987
|
PInputSearch,
|
|
972
|
-
|
|
988
|
+
_7 as PLink,
|
|
973
989
|
default14 as PLoading,
|
|
974
990
|
default15 as PModal,
|
|
975
|
-
|
|
976
|
-
|
|
991
|
+
_8 as PPagination,
|
|
992
|
+
_9 as PPaginationInfo,
|
|
977
993
|
default16 as PProgressBar,
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
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
|
-
|
|
1002
|
+
_13 as PTableLoader,
|
|
987
1003
|
pTableSort as PTableSort,
|
|
988
1004
|
default19 as PTableTd,
|
|
989
|
-
|
|
1005
|
+
_14 as PTabs,
|
|
990
1006
|
default20 as PTextarea,
|
|
991
1007
|
default21 as PToggle,
|
|
992
1008
|
P_ICON_ALIASES,
|
package/dist/es/useSelectList.js
CHANGED
|
@@ -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
|
+
"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.
|
|
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="[
|
|
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="[
|
|
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,
|