design-system-next 2.15.7 → 2.16.2
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 +7641 -7254
- package/dist/design-system-next.es.js.gz +0 -0
- package/dist/design-system-next.umd.js +13 -13
- 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 +1 -1
- package/package.json +1 -1
- package/src/assets/styles/tailwind.css +27 -6
- package/src/components/attribute-filter/attribute-filter.ts +134 -0
- package/src/components/attribute-filter/attribute-filter.vue +141 -0
- package/src/components/attribute-filter/use-attribute-filter.ts +125 -0
- package/src/components/card/card.ts +12 -0
- package/src/components/card/card.vue +1 -1
- package/src/components/card/use-card.ts +33 -12
- package/src/components/dropdown/dropdown.ts +1 -6
- package/src/components/dropdown/dropdown.vue +51 -41
- package/src/components/list/list.ts +0 -4
- package/src/components/select/select-ladderized/select-ladderized.vue +10 -7
- package/src/components/select/select-ladderized/use-select-ladderized.ts +2 -0
- package/src/components/select/select-multiple/select-multiple.vue +20 -10
- package/src/components/select/select-multiple/use-select-multiple.ts +3 -1
- package/src/components/select/select.vue +10 -7
- package/src/components/select/use-select.ts +4 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { PropType, ExtractPropTypes } from 'vue';
|
|
2
|
+
import type { MenuListType } from '../list/list';
|
|
3
|
+
|
|
4
|
+
export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
5
|
+
const TRIGGER_EVENTS = ['click', 'hover', 'focus', 'touch'] as const;
|
|
6
|
+
const POPPER_STRATEGY_TYPES = ['fixed', 'absolute'] as const;
|
|
7
|
+
const PLACEMENTS_TYPES = [
|
|
8
|
+
'auto',
|
|
9
|
+
'auto-start',
|
|
10
|
+
'auto-end',
|
|
11
|
+
'top',
|
|
12
|
+
'top-start',
|
|
13
|
+
'top-end',
|
|
14
|
+
'right',
|
|
15
|
+
'right-start',
|
|
16
|
+
'right-end',
|
|
17
|
+
'bottom',
|
|
18
|
+
'bottom-start',
|
|
19
|
+
'bottom-end',
|
|
20
|
+
'left',
|
|
21
|
+
'left-start',
|
|
22
|
+
'left-end',
|
|
23
|
+
] as const;
|
|
24
|
+
|
|
25
|
+
export const attributeFilterPropTypes = {
|
|
26
|
+
id: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: 'attribute_filter',
|
|
29
|
+
},
|
|
30
|
+
filterLabel: {
|
|
31
|
+
type: String,
|
|
32
|
+
default: 'Filter',
|
|
33
|
+
},
|
|
34
|
+
headerLabel: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: 'Add Filter',
|
|
37
|
+
},
|
|
38
|
+
triggers: {
|
|
39
|
+
type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
|
|
40
|
+
validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
|
|
41
|
+
return value.every((val) => TRIGGER_EVENTS.includes(val));
|
|
42
|
+
},
|
|
43
|
+
default: () => [],
|
|
44
|
+
},
|
|
45
|
+
popperTriggers: {
|
|
46
|
+
type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
|
|
47
|
+
validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
|
|
48
|
+
return value.every((val) => TRIGGER_EVENTS.includes(val));
|
|
49
|
+
},
|
|
50
|
+
default: () => [],
|
|
51
|
+
},
|
|
52
|
+
autoHide: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: true,
|
|
55
|
+
},
|
|
56
|
+
popperStrategy: {
|
|
57
|
+
type: String,
|
|
58
|
+
validator: (value: 'fixed' | 'absolute') => POPPER_STRATEGY_TYPES.includes(value),
|
|
59
|
+
default: 'absolute',
|
|
60
|
+
},
|
|
61
|
+
distance: {
|
|
62
|
+
type: Number,
|
|
63
|
+
default: 6,
|
|
64
|
+
},
|
|
65
|
+
placement: {
|
|
66
|
+
type: String as PropType<(typeof PLACEMENTS_TYPES)[number]>,
|
|
67
|
+
validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
|
|
68
|
+
default: 'bottom',
|
|
69
|
+
},
|
|
70
|
+
disabled: {
|
|
71
|
+
type: Boolean,
|
|
72
|
+
default: false,
|
|
73
|
+
},
|
|
74
|
+
wrapperPosition: {
|
|
75
|
+
type: String,
|
|
76
|
+
default: 'relative',
|
|
77
|
+
},
|
|
78
|
+
width: {
|
|
79
|
+
type: String,
|
|
80
|
+
default: '100%',
|
|
81
|
+
},
|
|
82
|
+
popperWidth: {
|
|
83
|
+
type: String,
|
|
84
|
+
default: '100%',
|
|
85
|
+
},
|
|
86
|
+
popperInnerWidth: {
|
|
87
|
+
type: String,
|
|
88
|
+
default: 'unset',
|
|
89
|
+
},
|
|
90
|
+
searchable: {
|
|
91
|
+
type: Boolean,
|
|
92
|
+
default: false,
|
|
93
|
+
},
|
|
94
|
+
multiselect: {
|
|
95
|
+
type: Boolean,
|
|
96
|
+
default: false,
|
|
97
|
+
},
|
|
98
|
+
filterMenuList: {
|
|
99
|
+
type: Array as PropType<MenuListType[] | string[]>,
|
|
100
|
+
default: [],
|
|
101
|
+
},
|
|
102
|
+
disableLocalSearch: {
|
|
103
|
+
type: Boolean,
|
|
104
|
+
default: false,
|
|
105
|
+
},
|
|
106
|
+
showBadge: {
|
|
107
|
+
type: Boolean,
|
|
108
|
+
default: true,
|
|
109
|
+
},
|
|
110
|
+
badgeVariant: {
|
|
111
|
+
type: String,
|
|
112
|
+
default: 'danger',
|
|
113
|
+
},
|
|
114
|
+
noList: {
|
|
115
|
+
type: Boolean,
|
|
116
|
+
default: false,
|
|
117
|
+
},
|
|
118
|
+
clearable: {
|
|
119
|
+
type: Boolean,
|
|
120
|
+
default: true,
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export const attributeFilterEmitTypes = {
|
|
125
|
+
onSaveFilter: (savedFilters: MenuListType[]) => Array.isArray(savedFilters),
|
|
126
|
+
onCloseFilter: () => true,
|
|
127
|
+
onOpenFilter: () => true,
|
|
128
|
+
onSelectFilter: (selectedFilters: MenuListType[]) => Array.isArray(selectedFilters),
|
|
129
|
+
infiniteScrollTrigger: () => true,
|
|
130
|
+
onClearFilter: () => true,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type AttributeFilterPropTypes = ExtractPropTypes<typeof attributeFilterPropTypes>;
|
|
134
|
+
export type AttributeFilterEmitTypes = typeof attributeFilterEmitTypes;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Menu
|
|
3
|
+
:shown="isFilterActive"
|
|
4
|
+
aria-id="attribute_filter_wrapper"
|
|
5
|
+
:distance="props.distance"
|
|
6
|
+
:placement="props.placement"
|
|
7
|
+
:triggers="props.triggers"
|
|
8
|
+
:popper-triggers="props.popperTriggers"
|
|
9
|
+
:auto-hide="props.autoHide"
|
|
10
|
+
:disabled="props.disabled"
|
|
11
|
+
:container="`#${props.id}`"
|
|
12
|
+
:strategy="
|
|
13
|
+
props.popperStrategy === 'fixed' || props.popperStrategy === 'absolute' ? props.popperStrategy : 'absolute'
|
|
14
|
+
"
|
|
15
|
+
:delay="0"
|
|
16
|
+
:style="{
|
|
17
|
+
position: props.wrapperPosition,
|
|
18
|
+
width: props.width,
|
|
19
|
+
}"
|
|
20
|
+
@hide="handleClosePopper"
|
|
21
|
+
@show="handleShowPopper"
|
|
22
|
+
>
|
|
23
|
+
<div @click="handleFilterTrigger">
|
|
24
|
+
<slot>
|
|
25
|
+
<spr-chips
|
|
26
|
+
:label="props.filterLabel"
|
|
27
|
+
:active="isFilterActive"
|
|
28
|
+
icon="ph:funnel-simple"
|
|
29
|
+
:badge="props.showBadge && savedFilters.length > 0"
|
|
30
|
+
:badge-text="savedFilters.length.toString()"
|
|
31
|
+
:badge-variant="props.badgeVariant"
|
|
32
|
+
:closable="props.clearable && savedFilters.length > 0"
|
|
33
|
+
:disabled="props.disabled"
|
|
34
|
+
@close="handleClear"
|
|
35
|
+
/>
|
|
36
|
+
</slot>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
:id="props.id"
|
|
41
|
+
:style="{
|
|
42
|
+
width: props.popperWidth,
|
|
43
|
+
}"
|
|
44
|
+
></div>
|
|
45
|
+
<template #popper="{ hide }">
|
|
46
|
+
<div
|
|
47
|
+
id="attribute_filter_popper"
|
|
48
|
+
:style="{
|
|
49
|
+
width: props.popperInnerWidth,
|
|
50
|
+
}"
|
|
51
|
+
>
|
|
52
|
+
<slot name="header">
|
|
53
|
+
<div
|
|
54
|
+
id="attribute_filter_header"
|
|
55
|
+
class="spr-border-color-weak spr-flex spr-items-center spr-justify-between spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid spr-px-size-spacing-xs spr-py-size-spacing-2xs"
|
|
56
|
+
>
|
|
57
|
+
<span class="spr-text-color-strong spr-text-300 spr-font-medium"> {{ props.headerLabel }} </span>
|
|
58
|
+
<Icon
|
|
59
|
+
icon="ph:x"
|
|
60
|
+
width="20px"
|
|
61
|
+
height="20px"
|
|
62
|
+
class="spr-text-color-weak spr-cursor-pointer"
|
|
63
|
+
@click="hide()"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
</slot>
|
|
67
|
+
<div
|
|
68
|
+
v-if="props.searchable"
|
|
69
|
+
id="attribute_filter_subheader"
|
|
70
|
+
class="spr-border-color-weak spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid spr-px-size-spacing-xs spr-py-size-spacing-2xs"
|
|
71
|
+
>
|
|
72
|
+
<spr-input-search
|
|
73
|
+
v-model="searchModel"
|
|
74
|
+
label=""
|
|
75
|
+
placeholder="Search..."
|
|
76
|
+
class="!spr-py-0"
|
|
77
|
+
@focus="isSearchFocused = true"
|
|
78
|
+
@blur="isSearchFocused = false"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
<slot name="actions"> </slot>
|
|
82
|
+
<slot name="body">
|
|
83
|
+
<div
|
|
84
|
+
v-if="!noList"
|
|
85
|
+
ref="filterDropdownRef"
|
|
86
|
+
id="attribute_filter_body"
|
|
87
|
+
class="spr-max-h-[250px] spr-overflow-y-auto"
|
|
88
|
+
>
|
|
89
|
+
<spr-list
|
|
90
|
+
v-model="selectedFilters"
|
|
91
|
+
:menu-list="attributeFilterList"
|
|
92
|
+
:multi-select="props.multiselect"
|
|
93
|
+
@update:model-value="handleOnSelect"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
</slot>
|
|
97
|
+
<slot name="footer">
|
|
98
|
+
<div
|
|
99
|
+
id="attribute_filter_footer"
|
|
100
|
+
class="spr-border-color-weak spr-flex spr-items-center spr-justify-end spr-gap-size-spacing-3xs spr-border-x-0 spr-border-b-0 spr-border-t spr-border-solid spr-px-size-spacing-xs spr-py-size-spacing-2xs"
|
|
101
|
+
>
|
|
102
|
+
<spr-button variant="secondary" size="medium" @click="isFilterActive = false"> Cancel </spr-button>
|
|
103
|
+
<spr-button variant="primary" size="medium" tone="success" @click="handleSave"> Save </spr-button>
|
|
104
|
+
</div>
|
|
105
|
+
</slot>
|
|
106
|
+
</div>
|
|
107
|
+
</template>
|
|
108
|
+
</Menu>
|
|
109
|
+
</template>
|
|
110
|
+
<script lang="ts" setup>
|
|
111
|
+
import { Menu } from 'floating-vue';
|
|
112
|
+
import { attributeFilterEmitTypes, attributeFilterPropTypes } from './attribute-filter';
|
|
113
|
+
import { useAttributeFilter } from './use-attribute-filter';
|
|
114
|
+
import SprChips from '../chips/chips.vue';
|
|
115
|
+
import { Icon } from '@iconify/vue';
|
|
116
|
+
import SprInputSearch from '../input/input-search/input-search.vue';
|
|
117
|
+
import SprList from '../list/list.vue';
|
|
118
|
+
import SprButton from '../button/button.vue';
|
|
119
|
+
|
|
120
|
+
import 'floating-vue/dist/style.css';
|
|
121
|
+
|
|
122
|
+
const props = defineProps(attributeFilterPropTypes);
|
|
123
|
+
const emit = defineEmits(attributeFilterEmitTypes);
|
|
124
|
+
const searchModel = defineModel<string>('search', {
|
|
125
|
+
default: '',
|
|
126
|
+
});
|
|
127
|
+
const {
|
|
128
|
+
isFilterActive,
|
|
129
|
+
isSearchFocused,
|
|
130
|
+
attributeFilterList,
|
|
131
|
+
handleFilterTrigger,
|
|
132
|
+
selectedFilters,
|
|
133
|
+
savedFilters,
|
|
134
|
+
handleClosePopper,
|
|
135
|
+
handleShowPopper,
|
|
136
|
+
handleSave,
|
|
137
|
+
handleOnSelect,
|
|
138
|
+
filterDropdownRef,
|
|
139
|
+
handleClear,
|
|
140
|
+
} = useAttributeFilter(props, emit, searchModel);
|
|
141
|
+
</script>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { MenuListType } from '../list/list';
|
|
2
|
+
import type { AttributeFilterPropTypes, AttributeFilterEmitTypes } from './attribute-filter';
|
|
3
|
+
import { onMounted, ref, toRefs, watch, type SetupContext, type Ref } from 'vue';
|
|
4
|
+
import { useInfiniteScroll } from '@vueuse/core';
|
|
5
|
+
export const useAttributeFilter = (
|
|
6
|
+
props: AttributeFilterPropTypes,
|
|
7
|
+
emit: SetupContext<AttributeFilterEmitTypes>['emit'],
|
|
8
|
+
searchModel: Ref<string>,
|
|
9
|
+
) => {
|
|
10
|
+
const isFilterActive = ref(false);
|
|
11
|
+
const isSearchFocused = ref(false);
|
|
12
|
+
const selectedFilters = ref<MenuListType[]>([]);
|
|
13
|
+
const savedFilters = ref<MenuListType[]>([]);
|
|
14
|
+
const isSaving = ref(false);
|
|
15
|
+
const filterDropdownRef = ref<HTMLDivElement | null>(null);
|
|
16
|
+
|
|
17
|
+
const { filterMenuList, disableLocalSearch, noList } = toRefs(props);
|
|
18
|
+
const attributeFilterList = ref<MenuListType[]>([]);
|
|
19
|
+
|
|
20
|
+
const handleFilterTrigger = () => {
|
|
21
|
+
isFilterActive.value = !isFilterActive.value;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const getFilterList = () => {
|
|
25
|
+
if (Array.isArray(filterMenuList.value)) {
|
|
26
|
+
attributeFilterList.value = filterMenuList.value as MenuListType[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof filterMenuList.value[0] === 'string' || typeof attributeFilterList.value[0] === 'number') {
|
|
30
|
+
attributeFilterList.value = (filterMenuList.value as string[] | number[]).map((item) => ({
|
|
31
|
+
text: item.toString(),
|
|
32
|
+
value: item,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handleClosePopper = () => {
|
|
38
|
+
if (isSaving.value) {
|
|
39
|
+
isSaving.value = false;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
isFilterActive.value = false;
|
|
44
|
+
emit('onCloseFilter');
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleShowPopper = () => {
|
|
48
|
+
emit('onOpenFilter');
|
|
49
|
+
if (noList.value) return;
|
|
50
|
+
|
|
51
|
+
selectedFilters.value = [...savedFilters.value];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleSave = () => {
|
|
55
|
+
isSaving.value = true;
|
|
56
|
+
savedFilters.value = [...selectedFilters.value];
|
|
57
|
+
isFilterActive.value = false;
|
|
58
|
+
|
|
59
|
+
emit('onSaveFilter', savedFilters.value);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getFilteredMenuList = (list: MenuListType[]) => {
|
|
63
|
+
return list.filter((item: MenuListType) => {
|
|
64
|
+
const searchTerm = searchModel.value.toLowerCase().trim();
|
|
65
|
+
return item.text.toLowerCase().trim().includes(searchTerm);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleSearch = () => {
|
|
70
|
+
if (disableLocalSearch.value || noList.value) return;
|
|
71
|
+
getFilterList();
|
|
72
|
+
attributeFilterList.value = getFilteredMenuList(attributeFilterList.value);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleOnSelect = (selectedFilters: MenuListType[]) => {
|
|
76
|
+
emit('onSelectFilter', selectedFilters);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleClear = () => {
|
|
80
|
+
selectedFilters.value = [];
|
|
81
|
+
savedFilters.value = [];
|
|
82
|
+
emit('onClearFilter');
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
useInfiniteScroll(
|
|
86
|
+
filterDropdownRef,
|
|
87
|
+
() => {
|
|
88
|
+
if (!filterDropdownRef.value) return;
|
|
89
|
+
const element = filterDropdownRef.value;
|
|
90
|
+
const hasVerticalScrollbar = element.scrollHeight > element.clientHeight;
|
|
91
|
+
|
|
92
|
+
if (hasVerticalScrollbar) {
|
|
93
|
+
emit('infiniteScrollTrigger');
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{ distance: 10 },
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
onMounted(() => {
|
|
100
|
+
if (noList.value) return;
|
|
101
|
+
getFilterList();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
watch(filterMenuList, () => {
|
|
105
|
+
if (noList.value) return;
|
|
106
|
+
getFilterList();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
watch(searchModel, () => handleSearch());
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
isFilterActive,
|
|
113
|
+
isSearchFocused,
|
|
114
|
+
attributeFilterList,
|
|
115
|
+
handleFilterTrigger,
|
|
116
|
+
selectedFilters,
|
|
117
|
+
handleClosePopper,
|
|
118
|
+
handleShowPopper,
|
|
119
|
+
handleSave,
|
|
120
|
+
savedFilters,
|
|
121
|
+
handleOnSelect,
|
|
122
|
+
filterDropdownRef,
|
|
123
|
+
handleClear,
|
|
124
|
+
};
|
|
125
|
+
};
|
|
@@ -2,9 +2,17 @@ import type { PropType, ExtractPropTypes } from 'vue';
|
|
|
2
2
|
|
|
3
3
|
export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
4
4
|
|
|
5
|
+
const TONES = ['plain', 'neutral', 'success', 'information', 'pending', 'caution', 'accent', 'danger'] as const;
|
|
5
6
|
const BORDER_RADIUS_SIZE = ['xl', 'lg', 'md', 'sm', 'xs', '2xs'] as const;
|
|
6
7
|
|
|
7
8
|
export const cardPropTypes = {
|
|
9
|
+
id: {
|
|
10
|
+
type: String,
|
|
11
|
+
},
|
|
12
|
+
tone: {
|
|
13
|
+
type: String as PropType<(typeof TONES)[number] | undefined>,
|
|
14
|
+
validator: (val: string | undefined) => !val || TONES.includes(val as (typeof TONES)[number]),
|
|
15
|
+
},
|
|
8
16
|
title: {
|
|
9
17
|
type: String,
|
|
10
18
|
default: '',
|
|
@@ -30,6 +38,10 @@ export const cardPropTypes = {
|
|
|
30
38
|
validator: (value: (typeof BORDER_RADIUS_SIZE)[number]) => BORDER_RADIUS_SIZE.includes(value),
|
|
31
39
|
default: 'xl',
|
|
32
40
|
},
|
|
41
|
+
borderWidth: {
|
|
42
|
+
type: String,
|
|
43
|
+
default: '1px',
|
|
44
|
+
},
|
|
33
45
|
hasCollapsible: {
|
|
34
46
|
type: Boolean,
|
|
35
47
|
default: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="cardClasses.baseClasses">
|
|
2
|
+
<div :class="cardClasses.baseClasses" :style="{ borderWidth: props.borderWidth }">
|
|
3
3
|
<div v-if="props.showHeader && ($slots.header || props.title)" :class="cardClasses.headerClasses">
|
|
4
4
|
<div v-if="props.title" class="spr-flex spr-items-center">
|
|
5
5
|
<Icon
|
|
@@ -12,21 +12,42 @@ interface CardClasses {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export const useCard = (props: CardPropTypes, slots: Slots) => {
|
|
15
|
-
const { title, headerIcon, borderRadiusSize, hasCollapsible, isCollapsibleOpen, hasContentPadding, flexbox } =
|
|
15
|
+
const { tone, title, headerIcon, borderRadiusSize, hasCollapsible, isCollapsibleOpen, hasContentPadding, flexbox } =
|
|
16
16
|
toRefs(props);
|
|
17
17
|
|
|
18
18
|
const cardClasses: ComputedRef<CardClasses> = computed(() => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
const toneValue = tone?.value;
|
|
20
|
+
|
|
21
|
+
const baseClasses = classNames(`spr-border-solid`, {
|
|
22
|
+
// Tones
|
|
23
|
+
'spr-background-color-base': !toneValue,
|
|
24
|
+
'spr-bg-white': toneValue === 'plain',
|
|
25
|
+
'spr-background-color-surface': toneValue === 'neutral',
|
|
26
|
+
'spr-background-color-success-weak': toneValue === 'success',
|
|
27
|
+
'spr-background-color-information-weak': toneValue === 'information',
|
|
28
|
+
'spr-background-color-pending-weak': toneValue === 'pending',
|
|
29
|
+
'spr-background-color-caution-weak': toneValue === 'caution',
|
|
30
|
+
'spr-background-color-accent-weak': toneValue === 'accent',
|
|
31
|
+
'spr-background-color-danger-weak': toneValue === 'danger',
|
|
32
|
+
|
|
33
|
+
// Borders
|
|
34
|
+
'spr-border-color-weak': !toneValue || toneValue === 'plain',
|
|
35
|
+
'spr-border-color-base': toneValue === 'neutral',
|
|
36
|
+
'spr-border-color-success-base': toneValue === 'success',
|
|
37
|
+
'spr-border-color-information-base': toneValue === 'information',
|
|
38
|
+
'spr-border-color-pending-base': toneValue === 'pending',
|
|
39
|
+
'spr-border-color-caution-base': toneValue === 'caution',
|
|
40
|
+
'spr-border-color-accent-base': toneValue === 'accent',
|
|
41
|
+
'spr-border-color-danger-base': toneValue === 'danger',
|
|
42
|
+
|
|
43
|
+
// Border radius
|
|
44
|
+
'spr-rounded-border-radius-2xs': borderRadiusSize.value === 'xs',
|
|
45
|
+
'spr-rounded-border-radius-xs': borderRadiusSize.value === 'sm',
|
|
46
|
+
'spr-rounded-border-radius-sm': borderRadiusSize.value === 'md',
|
|
47
|
+
'spr-rounded-border-radius-md': borderRadiusSize.value === 'xs',
|
|
48
|
+
'spr-rounded-border-radius-lg': borderRadiusSize.value === 'lg',
|
|
49
|
+
'spr-rounded-border-radius-xl': borderRadiusSize.value === 'xl' || !borderRadiusSize.value,
|
|
50
|
+
});
|
|
30
51
|
|
|
31
52
|
const headerClasses = classNames(`spr-flex spr-items-center transition-all duration-300 ease-in-out`, {
|
|
32
53
|
'spr-min-h-[18px]': slots.header,
|
|
@@ -39,8 +39,7 @@ export const dropdownPropTypes = {
|
|
|
39
39
|
},
|
|
40
40
|
menuList: {
|
|
41
41
|
type: Array as PropType<MenuListType[] | string[] | Record<string, unknown>[]>,
|
|
42
|
-
|
|
43
|
-
default: [],
|
|
42
|
+
default: () => [],
|
|
44
43
|
},
|
|
45
44
|
searchableMenu: {
|
|
46
45
|
type: Boolean,
|
|
@@ -116,10 +115,6 @@ export const dropdownPropTypes = {
|
|
|
116
115
|
type: Boolean,
|
|
117
116
|
default: false,
|
|
118
117
|
},
|
|
119
|
-
dropdown: {
|
|
120
|
-
type: Boolean,
|
|
121
|
-
default: false,
|
|
122
|
-
},
|
|
123
118
|
lozenge: {
|
|
124
119
|
type: Boolean,
|
|
125
120
|
default: false,
|
|
@@ -30,47 +30,57 @@
|
|
|
30
30
|
></div>
|
|
31
31
|
|
|
32
32
|
<template #popper>
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
33
|
+
<template v-if="$slots.popper">
|
|
34
|
+
<div
|
|
35
|
+
class="spr-overflow-y-auto spr-overflow-x-hidden spr-p-2"
|
|
36
|
+
:style="{
|
|
37
|
+
width: props.popperInnerWidth,
|
|
38
|
+
}"
|
|
39
|
+
>
|
|
40
|
+
<slot name="popper" />
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
<template v-else>
|
|
44
|
+
<div
|
|
45
|
+
ref="dropdownRef"
|
|
46
|
+
:class="[
|
|
47
|
+
'spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden',
|
|
48
|
+
!props.ladderized || isLadderizedSearch,
|
|
49
|
+
]"
|
|
50
|
+
:style="{
|
|
51
|
+
width: props.popperInnerWidth,
|
|
52
|
+
}"
|
|
53
|
+
>
|
|
54
|
+
<template v-if="dropdownMenuList.length > 0">
|
|
55
|
+
<spr-list
|
|
56
|
+
v-if="!props.ladderized || isLadderizedSearch"
|
|
57
|
+
v-model="selectedListItems"
|
|
58
|
+
:menu-list="dropdownMenuList"
|
|
59
|
+
:searchable-menu="props.searchableMenu"
|
|
60
|
+
:group-items-by="props.groupItemsBy"
|
|
61
|
+
:multi-select="props.multiSelect"
|
|
62
|
+
:pre-selected-items="dropdownValue"
|
|
63
|
+
:no-check="props.noCheckInList"
|
|
64
|
+
:lozenge="props.lozenge"
|
|
65
|
+
@update:model-value="handleSelectedItem"
|
|
66
|
+
/>
|
|
67
|
+
<spr-ladderized-list
|
|
68
|
+
v-else
|
|
69
|
+
v-model="dropdownValue"
|
|
70
|
+
:ladderized="props.ladderized"
|
|
71
|
+
:menu-list="dropdownMenuList"
|
|
72
|
+
:searchable-menu="props.searchableMenu"
|
|
73
|
+
:remove-current-level-in-back-label="removeCurrentLevelInBackLabel"
|
|
74
|
+
@update:model-value="handleSelectedLadderizedItem"
|
|
75
|
+
/>
|
|
76
|
+
</template>
|
|
77
|
+
<template v-else>
|
|
78
|
+
<div class="spr-flex spr-items-center spr-justify-center spr-p-2 spr-text-center">
|
|
79
|
+
<span class="spr-body-sm-regular spr-m-0">No results found</span>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
74
84
|
</template>
|
|
75
85
|
</Menu>
|
|
76
86
|
</template>
|
|
@@ -42,13 +42,16 @@
|
|
|
42
42
|
:error="props.error"
|
|
43
43
|
>
|
|
44
44
|
<template #icon>
|
|
45
|
-
<div
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
<div
|
|
46
|
+
:class="[
|
|
47
|
+
'spr-flex spr-items-center spr-gap-1',
|
|
48
|
+
{
|
|
49
|
+
'spr-cursor-pointer': !props.disabled,
|
|
50
|
+
'spr-cursor-not-allowed': props.disabled,
|
|
51
|
+
},
|
|
52
|
+
]"
|
|
53
|
+
>
|
|
54
|
+
<Icon v-if="props.clearable && inputText" icon="ph:x" @click.stop="handleClear" />
|
|
52
55
|
<Icon icon="ph:caret-down" />
|
|
53
56
|
</div>
|
|
54
57
|
</template>
|