design-system-next 2.11.0 → 2.11.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.
- package/dist/design-system-next.es.js +5923 -5977
- package/dist/design-system-next.es.js.gz +0 -0
- package/dist/design-system-next.umd.js +12 -12
- package/dist/design-system-next.umd.js.gz +0 -0
- package/dist/main.css +1 -1
- package/dist/main.css.gz +0 -0
- package/dist/package.json.d.ts +6 -5
- package/package.json +6 -5
- package/src/App.vue +44 -1
- package/src/components/button/button-dropdown/button-dropdown.ts +11 -13
- package/src/components/button/button-dropdown/button-dropdown.vue +27 -14
- package/src/components/button/button-dropdown/use-button-dropdown.ts +24 -25
- package/src/components/dropdown/dropdown.ts +8 -2
- package/src/components/dropdown/dropdown.vue +3 -0
- package/src/components/list/list.ts +18 -5
- package/src/components/list/list.vue +28 -33
- package/src/components/list/use-list.ts +62 -3
- package/src/components/select/select-multiple/select-multiple.ts +18 -0
- package/src/components/select/select-multiple/select-multiple.vue +19 -16
- package/src/components/select/select-multiple/use-select-multiple.ts +52 -33
- package/src/components/sidenav/use-sidenav.ts +43 -26
- package/src/vite-env.d.ts +0 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { ExtractPropTypes, PropType } from
|
|
2
|
-
import { buttonPropTypes } from
|
|
3
|
-
import type { MenuListType } from
|
|
4
|
-
import { PLACEMENTS_TYPES } from
|
|
1
|
+
import type { ExtractPropTypes, PropType } from 'vue';
|
|
2
|
+
import { buttonPropTypes } from '../button';
|
|
3
|
+
import type { MenuListType } from '@/components/list/list';
|
|
4
|
+
import { PLACEMENTS_TYPES } from '@/components/dropdown/dropdown';
|
|
5
5
|
|
|
6
6
|
const BUTTON_DROPDOWN_VARIANTS = ['primary', 'secondary'] as const;
|
|
7
7
|
const BUTTON_DROPDOWN_TONES = ['neutral', 'success'] as const;
|
|
@@ -13,22 +13,20 @@ export const buttonDropdownProps = {
|
|
|
13
13
|
default: [],
|
|
14
14
|
},
|
|
15
15
|
...buttonPropTypes, // Inherit button properties
|
|
16
|
-
/**
|
|
17
|
-
* @description Button tone
|
|
18
|
-
*/
|
|
19
16
|
tone: {
|
|
20
17
|
type: String as PropType<(typeof BUTTON_DROPDOWN_TONES)[number]>,
|
|
21
18
|
validator: (value: (typeof BUTTON_DROPDOWN_TONES)[number]) => BUTTON_DROPDOWN_TONES.includes(value),
|
|
22
19
|
default: 'neutral',
|
|
23
20
|
},
|
|
24
|
-
/**
|
|
25
|
-
* @description Button Dropdown Variant
|
|
26
|
-
*/
|
|
27
21
|
variant: {
|
|
28
22
|
type: String as PropType<(typeof BUTTON_DROPDOWN_VARIANTS)[number]>,
|
|
29
23
|
validator: (value: (typeof BUTTON_DROPDOWN_VARIANTS)[number]) => BUTTON_DROPDOWN_VARIANTS.includes(value),
|
|
30
24
|
default: 'primary',
|
|
31
25
|
},
|
|
26
|
+
dropdownId: {
|
|
27
|
+
type: String,
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
32
30
|
menuList: {
|
|
33
31
|
type: Array as PropType<MenuListType[] | string[] | Record<string, unknown>[]>,
|
|
34
32
|
required: true,
|
|
@@ -47,10 +45,10 @@ export const buttonDropdownProps = {
|
|
|
47
45
|
type: String,
|
|
48
46
|
default: 'fit-content',
|
|
49
47
|
},
|
|
50
|
-
|
|
48
|
+
popperInnerWidth: {
|
|
51
49
|
type: String,
|
|
52
|
-
|
|
53
|
-
}
|
|
50
|
+
default: 'unset',
|
|
51
|
+
},
|
|
54
52
|
};
|
|
55
53
|
|
|
56
54
|
export const buttonDropdownEmits = ['click', 'update:modelValue'];
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="spr-flex spr-
|
|
3
|
-
<spr-button
|
|
2
|
+
<div class="spr-flex spr-h-fit spr-flex-row">
|
|
3
|
+
<spr-button
|
|
4
|
+
v-bind="props"
|
|
5
|
+
:class="buttonDropdownClasses.mainButtonClasses"
|
|
6
|
+
@click="(event) => emits('click', event)"
|
|
7
|
+
>
|
|
4
8
|
<slot />
|
|
5
9
|
</spr-button>
|
|
6
|
-
<spr-dropdown
|
|
7
|
-
:id="props.dropdownId"
|
|
8
|
-
v-model="selectedMenu"
|
|
9
|
-
:menu-list="menuList"
|
|
10
|
-
:width="props.width"
|
|
10
|
+
<spr-dropdown
|
|
11
|
+
:id="props.dropdownId"
|
|
12
|
+
v-model="selectedMenu"
|
|
13
|
+
:menu-list="menuList"
|
|
14
|
+
:width="props.width"
|
|
11
15
|
:popper-width="props.popperWidth"
|
|
16
|
+
:popper-inner-width="props.popperInnerWidth"
|
|
17
|
+
:placement="props.placement"
|
|
12
18
|
no-check-in-list
|
|
13
19
|
>
|
|
14
|
-
<spr-button
|
|
20
|
+
<spr-button
|
|
21
|
+
v-bind="props"
|
|
22
|
+
:fullwidth="false"
|
|
23
|
+
:has-icon="true"
|
|
24
|
+
:class="buttonDropdownClasses.dropdownButtonClasses"
|
|
25
|
+
>
|
|
15
26
|
<icon icon="ph:caret-down" />
|
|
16
27
|
</spr-button>
|
|
17
28
|
</spr-dropdown>
|
|
@@ -19,14 +30,16 @@
|
|
|
19
30
|
</template>
|
|
20
31
|
|
|
21
32
|
<script setup lang="ts">
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
33
|
+
import { Icon } from '@iconify/vue';
|
|
34
|
+
|
|
35
|
+
import { buttonDropdownEmits, buttonDropdownProps } from './button-dropdown';
|
|
36
|
+
import { useButtonDropdown } from './use-button-dropdown';
|
|
37
|
+
|
|
38
|
+
import SprButton from '@/components/button/button.vue';
|
|
39
|
+
import SprDropdown from '@/components/dropdown/dropdown.vue';
|
|
27
40
|
|
|
28
41
|
const props = defineProps(buttonDropdownProps);
|
|
29
|
-
const emits = defineEmits(buttonDropdownEmits)
|
|
42
|
+
const emits = defineEmits(buttonDropdownEmits);
|
|
30
43
|
|
|
31
44
|
const { selectedMenu, buttonDropdownClasses } = useButtonDropdown(props, emits);
|
|
32
45
|
</script>
|
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
import { computed, toRefs, type SetupContext } from
|
|
2
|
-
import type { ButtonDropdownEmitTypes, ButtonDropdownPropTypes } from "./button-dropdown";
|
|
3
|
-
import classNames from "classnames";
|
|
1
|
+
import { computed, toRefs, type SetupContext } from 'vue';
|
|
4
2
|
import { useVModel } from '@vueuse/core';
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
|
|
6
|
+
import type { ButtonDropdownEmitTypes, ButtonDropdownPropTypes } from './button-dropdown';
|
|
7
|
+
|
|
8
|
+
export const useButtonDropdown = (
|
|
9
|
+
props: ButtonDropdownPropTypes,
|
|
10
|
+
emits: SetupContext<ButtonDropdownEmitTypes>['emit'],
|
|
11
|
+
) => {
|
|
7
12
|
const { tone, variant, disabled } = toRefs(props);
|
|
8
13
|
|
|
9
|
-
const selectedMenu = useVModel(props,
|
|
14
|
+
const selectedMenu = useVModel(props, 'modelValue', emits);
|
|
10
15
|
|
|
11
16
|
const buttonDropdownClasses = computed(() => {
|
|
12
|
-
const mainButtonClasses = classNames(
|
|
13
|
-
'spr-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
'spr-border-l-transparent': variant.value !== "secondary",
|
|
25
|
-
}
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return {mainButtonClasses, dropdownButtonClasses};
|
|
17
|
+
const mainButtonClasses = classNames('spr-rounded-r-none spr-border-r', {
|
|
18
|
+
'!spr-border-solid spr-border-l-0 spr-border-t-0 spr-border-b-0': disabled.value && variant.value !== 'secondary',
|
|
19
|
+
'spr-border-r-kangkong-800': tone.value === 'success' && !disabled.value,
|
|
20
|
+
'spr-border-r-mushroom-200': tone.value !== 'success' || (tone.value === 'success' && disabled.value),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const dropdownButtonClasses = classNames('spr-rounded-l-none', {
|
|
24
|
+
'spr-border-solid spr-border-l-0': variant.value === 'secondary',
|
|
25
|
+
'spr-border-l-transparent': variant.value !== 'secondary',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return { mainButtonClasses, dropdownButtonClasses };
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
32
|
selectedMenu,
|
|
33
|
-
buttonDropdownClasses
|
|
34
|
-
}
|
|
33
|
+
buttonDropdownClasses,
|
|
34
|
+
};
|
|
35
35
|
};
|
|
36
|
-
|
|
@@ -31,7 +31,9 @@ export const dropdownPropTypes = {
|
|
|
31
31
|
required: true,
|
|
32
32
|
},
|
|
33
33
|
modelValue: {
|
|
34
|
-
type: [String, Number, Object, Array] as PropType<
|
|
34
|
+
type: [String, Number, Object, Array] as PropType<
|
|
35
|
+
string | number | Record<string, unknown> | (string | number | Record<string, unknown>)[]
|
|
36
|
+
>,
|
|
35
37
|
default: () => [],
|
|
36
38
|
},
|
|
37
39
|
menuList: {
|
|
@@ -80,6 +82,10 @@ export const dropdownPropTypes = {
|
|
|
80
82
|
type: String,
|
|
81
83
|
default: '100%',
|
|
82
84
|
},
|
|
85
|
+
popperInnerWidth: {
|
|
86
|
+
type: String,
|
|
87
|
+
default: 'unset',
|
|
88
|
+
},
|
|
83
89
|
popperStrategy: {
|
|
84
90
|
type: String,
|
|
85
91
|
validator: (value: 'fixed' | 'absolute') => POPPER_STRATEGY_TYPES.includes(value),
|
|
@@ -100,7 +106,7 @@ export const dropdownPropTypes = {
|
|
|
100
106
|
noCheckInList: {
|
|
101
107
|
type: Boolean,
|
|
102
108
|
default: false,
|
|
103
|
-
}
|
|
109
|
+
},
|
|
104
110
|
};
|
|
105
111
|
|
|
106
112
|
export const dropdownEmitTypes = {
|
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
(!props.ladderized || isLadderizedSearch) && 'spr-p-2',
|
|
37
37
|
'spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden',
|
|
38
38
|
]"
|
|
39
|
+
:style="{
|
|
40
|
+
width: props.popperInnerWidth,
|
|
41
|
+
}"
|
|
39
42
|
>
|
|
40
43
|
<template v-if="dropdownMenuList.length > 0">
|
|
41
44
|
<spr-list
|
|
@@ -13,10 +13,10 @@ export type MenuListType = {
|
|
|
13
13
|
group?: string;
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
_originalObject?: Record<string, unknown>; // Store original object reference when mapping complex objects
|
|
16
|
-
icon?: string
|
|
17
|
-
iconColor?: string
|
|
18
|
-
textColor?: string
|
|
19
|
-
onClickFn?: () => void
|
|
16
|
+
icon?: string; // String value for Iconify
|
|
17
|
+
iconColor?: string;
|
|
18
|
+
textColor?: string;
|
|
19
|
+
onClickFn?: () => void;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export type GroupedMenuListType = {
|
|
@@ -29,6 +29,10 @@ export const listPropTypes = {
|
|
|
29
29
|
type: Array as PropType<MenuListType[]>,
|
|
30
30
|
default: [],
|
|
31
31
|
},
|
|
32
|
+
searchValue: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: '',
|
|
35
|
+
},
|
|
32
36
|
menuList: {
|
|
33
37
|
type: Array as PropType<MenuListType[]>,
|
|
34
38
|
required: true,
|
|
@@ -64,14 +68,23 @@ export const listPropTypes = {
|
|
|
64
68
|
type: Boolean,
|
|
65
69
|
default: false,
|
|
66
70
|
},
|
|
71
|
+
disabledLocalSearch: {
|
|
72
|
+
type: Boolean,
|
|
73
|
+
default: false,
|
|
74
|
+
},
|
|
75
|
+
loading: {
|
|
76
|
+
type: Boolean,
|
|
77
|
+
default: false,
|
|
78
|
+
},
|
|
67
79
|
noCheck: {
|
|
68
80
|
type: Boolean,
|
|
69
81
|
default: false,
|
|
70
|
-
}
|
|
82
|
+
},
|
|
71
83
|
};
|
|
72
84
|
|
|
73
85
|
export const listEmitTypes = {
|
|
74
86
|
'update:modelValue': (value: MenuListType[]) => value,
|
|
87
|
+
'update:search': (value: string) => typeof value === 'string',
|
|
75
88
|
};
|
|
76
89
|
|
|
77
90
|
export type ListPropTypes = ExtractPropTypes<typeof listPropTypes>;
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="spr-font-main">
|
|
3
3
|
<div v-if="props.searchableMenu" class="spr-mb-3 spr-grid spr-gap-3">
|
|
4
|
-
<spr-input
|
|
5
|
-
v-model="searchText"
|
|
6
|
-
:placeholder="props.searchableMenuPlaceholder"
|
|
7
|
-
autocomplete="off"
|
|
8
|
-
@keyup="handleSearch"
|
|
9
|
-
/>
|
|
4
|
+
<spr-input-search v-model="searchText" :placeholder="props.searchableMenuPlaceholder" autocomplete="off" />
|
|
10
5
|
|
|
11
6
|
<div v-if="isParentMenu" class="spr-background-color-surface spr-h-[1px]"></div>
|
|
12
7
|
</div>
|
|
13
8
|
|
|
14
|
-
<template v-if="props.groupItemsBy">
|
|
9
|
+
<template v-if="props.groupItemsBy && groupedMenuList.length > 0">
|
|
15
10
|
<div class="spr-grid spr-gap-2">
|
|
16
11
|
<div v-for="(list, listIndex) in groupedMenuList" :key="listIndex" class="spr-grid spr-gap-0.5">
|
|
17
12
|
<div
|
|
@@ -29,14 +24,11 @@
|
|
|
29
24
|
@click="handleSelectedItem(item)"
|
|
30
25
|
>
|
|
31
26
|
<spr-checkbox v-if="props.multiSelect" :checked="isItemSelected(item)" />
|
|
32
|
-
<div
|
|
33
|
-
<span
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{{ item.subtext }}
|
|
38
|
-
</span>
|
|
39
|
-
</div>
|
|
27
|
+
<div class="spr-flex spr-flex-auto spr-flex-col spr-justify-start">
|
|
28
|
+
<span class="spr-text-left spr-text-xs">{{ item.text }}</span>
|
|
29
|
+
<span v-if="item.subtext" class="spr-body-xs-regular spr-text-color-base spr-text-left">{{
|
|
30
|
+
item.subtext
|
|
31
|
+
}}</span>
|
|
40
32
|
</div>
|
|
41
33
|
<Icon
|
|
42
34
|
v-if="isItemSelected(item) && !props.multiSelect"
|
|
@@ -52,7 +44,7 @@
|
|
|
52
44
|
</div>
|
|
53
45
|
</div>
|
|
54
46
|
</template>
|
|
55
|
-
<template v-
|
|
47
|
+
<template v-if="localizedMenuList.length > 0">
|
|
56
48
|
<div
|
|
57
49
|
v-for="(item, index) in localizedMenuList"
|
|
58
50
|
:key="index"
|
|
@@ -60,27 +52,24 @@
|
|
|
60
52
|
@click="handleSelectedItem(item)"
|
|
61
53
|
>
|
|
62
54
|
<spr-checkbox v-if="props.multiSelect" :disabled="item.disabled" :checked="isItemSelected(item)" />
|
|
63
|
-
<div
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
<div
|
|
56
|
+
:class="[
|
|
57
|
+
'spr-flex spr-flex-auto spr-flex-col spr-justify-start',
|
|
58
|
+
{ 'spr-text-color-disabled': item.disabled },
|
|
59
|
+
]"
|
|
60
|
+
>
|
|
61
|
+
<span class="spr-text-left spr-text-xs">{{ item.text }}</span>
|
|
62
|
+
<span
|
|
63
|
+
v-if="item.subtext"
|
|
66
64
|
:class="[
|
|
67
|
-
'spr-
|
|
65
|
+
'spr-body-xs-regular spr-text-color-base spr-text-left',
|
|
68
66
|
{ 'spr-text-color-disabled': item.disabled },
|
|
69
67
|
]"
|
|
68
|
+
>{{ item.subtext }}</span
|
|
70
69
|
>
|
|
71
|
-
<span class="spr-text-left spr-text-xs">{{ item.text }}</span>
|
|
72
|
-
<span
|
|
73
|
-
v-if="item.subtext"
|
|
74
|
-
:class="[
|
|
75
|
-
'spr-body-xs-regular spr-text-color-base spr-text-left',
|
|
76
|
-
{ 'spr-text-color-disabled': item.disabled },
|
|
77
|
-
]"
|
|
78
|
-
>{{ item.subtext }}</span
|
|
79
|
-
>
|
|
80
|
-
</div>
|
|
81
70
|
</div>
|
|
82
71
|
<Icon
|
|
83
|
-
v-if="isItemSelected(item) && !props.multiSelect
|
|
72
|
+
v-if="isItemSelected(item) && !props.multiSelect"
|
|
84
73
|
class="spr-text-color-brand-base spr-w-[1.39em]"
|
|
85
74
|
icon="ph:check"
|
|
86
75
|
/>
|
|
@@ -91,6 +80,13 @@
|
|
|
91
80
|
/>
|
|
92
81
|
</div>
|
|
93
82
|
</template>
|
|
83
|
+
|
|
84
|
+
<template v-else>
|
|
85
|
+
<div v-if="props.loading" class="spr-skeletal-loader spr-h-8 spr-w-full spr-rounded-md" />
|
|
86
|
+
<div v-else class="spr-flex spr-items-center spr-justify-center spr-p-2 spr-text-center">
|
|
87
|
+
<span class="spr-body-sm-regular spr-m-0">No results found</span>
|
|
88
|
+
</div>
|
|
89
|
+
</template>
|
|
94
90
|
</div>
|
|
95
91
|
</template>
|
|
96
92
|
|
|
@@ -101,7 +97,7 @@ import { listPropTypes, listEmitTypes } from './list';
|
|
|
101
97
|
import { useList } from './use-list';
|
|
102
98
|
|
|
103
99
|
import SprCheckbox from '@/components/checkbox/checkbox.vue';
|
|
104
|
-
import
|
|
100
|
+
import SprInputSearch from '@/components/input/input-search/input-search.vue';
|
|
105
101
|
|
|
106
102
|
const props = defineProps(listPropTypes);
|
|
107
103
|
const emit = defineEmits(listEmitTypes);
|
|
@@ -113,7 +109,6 @@ const {
|
|
|
113
109
|
isParentMenu,
|
|
114
110
|
isItemSelected,
|
|
115
111
|
getListItemClasses,
|
|
116
|
-
handleSearch,
|
|
117
112
|
handleSelectedItem,
|
|
118
113
|
} = useList(props, emit);
|
|
119
114
|
</script>
|
|
@@ -12,8 +12,10 @@ interface ListClasses {
|
|
|
12
12
|
|
|
13
13
|
export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>['emit']) => {
|
|
14
14
|
const selectedItems = useVModel(props, 'modelValue', emit);
|
|
15
|
+
const searchString = useVModel(props, 'searchValue', emit);
|
|
15
16
|
|
|
16
|
-
const { menuList, menuLevel, groupItemsBy, multiSelect, preSelectedItems, noCheck } =
|
|
17
|
+
const { menuList, menuLevel, groupItemsBy, multiSelect, preSelectedItems, disabledLocalSearch, noCheck } =
|
|
18
|
+
toRefs(props);
|
|
17
19
|
|
|
18
20
|
const listClasses: ComputedRef<ListClasses> = computed(() => {
|
|
19
21
|
const listItemClasses = classNames(
|
|
@@ -29,6 +31,7 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
29
31
|
const searchText = ref<string>('');
|
|
30
32
|
|
|
31
33
|
const localizedMenuList = ref<MenuListType[]>([]);
|
|
34
|
+
const apiSelectedList = ref<MenuListType[]>([]);
|
|
32
35
|
const groupedMenuList = ref<GroupedMenuListType[]>([
|
|
33
36
|
{
|
|
34
37
|
groupLabel: 'no-group',
|
|
@@ -103,6 +106,15 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
103
106
|
});
|
|
104
107
|
}
|
|
105
108
|
|
|
109
|
+
const previousSelected = apiSelectedList.value.some((selectedItem) => {
|
|
110
|
+
return selectedItem.value === item.value;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (previousSelected) {
|
|
114
|
+
handleSelectedItem(item);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
106
118
|
return false;
|
|
107
119
|
};
|
|
108
120
|
|
|
@@ -344,12 +356,14 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
344
356
|
|
|
345
357
|
if (index === -1) {
|
|
346
358
|
// Add item if not already selected
|
|
347
|
-
selectedItems.value =
|
|
359
|
+
selectedItems.value = disabledLocalSearch.value
|
|
360
|
+
? trackNewlySelectedItems(item, false)
|
|
361
|
+
: [...selectedItems.value, item];
|
|
348
362
|
} else {
|
|
349
363
|
// Remove item if already selected
|
|
350
364
|
const updatedItems = [...selectedItems.value];
|
|
351
365
|
updatedItems.splice(index, 1);
|
|
352
|
-
selectedItems.value = updatedItems;
|
|
366
|
+
selectedItems.value = disabledLocalSearch.value ? trackNewlySelectedItems(item, true) : updatedItems;
|
|
353
367
|
}
|
|
354
368
|
} else {
|
|
355
369
|
// For single-select, simply replace the selection
|
|
@@ -359,6 +373,25 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
359
373
|
};
|
|
360
374
|
// #endregion - Helper Methods
|
|
361
375
|
|
|
376
|
+
const trackNewlySelectedItems = (selectedItem: MenuListType, isDeselected: boolean) => {
|
|
377
|
+
const exists = apiSelectedList.value.some((existing) => {
|
|
378
|
+
return existing.value === selectedItem.value;
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
if (!exists) {
|
|
382
|
+
apiSelectedList.value.push(selectedItem);
|
|
383
|
+
} else {
|
|
384
|
+
const index = apiSelectedList.value.findIndex((existing) => {
|
|
385
|
+
return existing.value === selectedItem.value;
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
if (index !== -1 && isDeselected) {
|
|
389
|
+
apiSelectedList.value.splice(index, 1);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return apiSelectedList.value;
|
|
393
|
+
};
|
|
394
|
+
|
|
362
395
|
watch(
|
|
363
396
|
menuList,
|
|
364
397
|
() => {
|
|
@@ -371,6 +404,30 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
371
404
|
{ deep: true },
|
|
372
405
|
);
|
|
373
406
|
|
|
407
|
+
watch(searchText, () => {
|
|
408
|
+
if (disabledLocalSearch.value) {
|
|
409
|
+
searchString.value = searchText.value;
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
handleSearch();
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
watch(
|
|
416
|
+
apiSelectedList,
|
|
417
|
+
() => {
|
|
418
|
+
apiSelectedList.value.forEach((prevSelected) => {
|
|
419
|
+
const selected = selectedItems.value.some((item) => {
|
|
420
|
+
return item.value === prevSelected.value;
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
if (!selected) {
|
|
424
|
+
selectedItems.value.push(prevSelected);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
},
|
|
428
|
+
{ deep: true },
|
|
429
|
+
);
|
|
430
|
+
|
|
374
431
|
onMounted(() => {
|
|
375
432
|
setMenuList();
|
|
376
433
|
setPreSelectedItems();
|
|
@@ -381,10 +438,12 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
381
438
|
listClasses,
|
|
382
439
|
localizedMenuList,
|
|
383
440
|
groupedMenuList,
|
|
441
|
+
apiSelectedList,
|
|
384
442
|
isParentMenu,
|
|
385
443
|
isItemSelected,
|
|
386
444
|
getListItemClasses,
|
|
387
445
|
handleSearch,
|
|
388
446
|
handleSelectedItem,
|
|
447
|
+
trackNewlySelectedItems,
|
|
389
448
|
};
|
|
390
449
|
};
|
|
@@ -35,6 +35,10 @@ export const multiSelectPropTypes = {
|
|
|
35
35
|
type: Array as PropType<(string | number | Record<string, unknown>)[]>,
|
|
36
36
|
default: () => [],
|
|
37
37
|
},
|
|
38
|
+
searchValue: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: '',
|
|
41
|
+
},
|
|
38
42
|
options: {
|
|
39
43
|
type: Array as PropType<MenuListType[] | string[] | Record<string, unknown>[]>,
|
|
40
44
|
required: true,
|
|
@@ -121,11 +125,25 @@ export const multiSelectPropTypes = {
|
|
|
121
125
|
type: Boolean,
|
|
122
126
|
default: false,
|
|
123
127
|
},
|
|
128
|
+
searchable: {
|
|
129
|
+
type: Boolean,
|
|
130
|
+
default: false,
|
|
131
|
+
},
|
|
132
|
+
loading: {
|
|
133
|
+
type: Boolean,
|
|
134
|
+
default: false,
|
|
135
|
+
},
|
|
136
|
+
disabledLocalSearch: {
|
|
137
|
+
type: Boolean,
|
|
138
|
+
default: false,
|
|
139
|
+
},
|
|
124
140
|
};
|
|
125
141
|
|
|
126
142
|
export const multiSelectEmitTypes = {
|
|
127
143
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
128
144
|
'update:modelValue': (_value: unknown) => true,
|
|
145
|
+
'update:search': (value: string) => typeof value === 'string',
|
|
146
|
+
'infinite-scroll-trigger': Boolean,
|
|
129
147
|
};
|
|
130
148
|
|
|
131
149
|
export type MultiSelectPropTypes = ExtractPropTypes<typeof multiSelectPropTypes>;
|
|
@@ -119,22 +119,23 @@
|
|
|
119
119
|
</div>
|
|
120
120
|
|
|
121
121
|
<template #popper>
|
|
122
|
-
<div
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
122
|
+
<div
|
|
123
|
+
ref="multipleSelectPopperRef"
|
|
124
|
+
class="spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden spr-p-2"
|
|
125
|
+
>
|
|
126
|
+
<spr-list
|
|
127
|
+
v-model="multiSelectedListItems"
|
|
128
|
+
v-model:search-value="searchInput"
|
|
129
|
+
:searchable-menu="props.searchable"
|
|
130
|
+
searchable-menu-placeholder="Search"
|
|
131
|
+
:menu-list="multiSelectOptions"
|
|
132
|
+
:group-items-by="props.groupItemsBy"
|
|
133
|
+
:pre-selected-items="Array.isArray(multiSelectModel) ? multiSelectModel.flat() : [multiSelectModel]"
|
|
134
|
+
:loading="props.loading"
|
|
135
|
+
multi-select
|
|
136
|
+
:disabled-local-search="props.disabledLocalSearch"
|
|
137
|
+
@update:model-value="handleMultiSelectedItem"
|
|
138
|
+
/>
|
|
138
139
|
</div>
|
|
139
140
|
</template>
|
|
140
141
|
</Menu>
|
|
@@ -167,6 +168,8 @@ const {
|
|
|
167
168
|
multiSelectedListItems,
|
|
168
169
|
inputText,
|
|
169
170
|
isMultiSelectPopperDisabled,
|
|
171
|
+
searchInput,
|
|
172
|
+
multipleSelectPopperRef,
|
|
170
173
|
handleMultiSelectedItem,
|
|
171
174
|
handleChippedRemoveItem,
|
|
172
175
|
handleClear,
|