design-system-next 2.15.6 → 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 +7804 -7415
- 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/App.vue +619 -604
- 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
- package/src/components/sidenav/use-sidenav.ts +6 -1
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
#sidenav-menu-wrapper,
|
|
65
65
|
#sidenav-submenu-l1-wrapper,
|
|
66
66
|
#user-menu-wrapper {
|
|
67
|
-
@apply spr-background-color spr-border-color-base spr-overflow-y-auto spr-overflow-x-hidden spr-rounded-
|
|
67
|
+
@apply spr-background-color spr-border-color-base spr-overflow-y-auto spr-overflow-x-hidden spr-rounded-xl spr-border spr-border-solid spr-drop-shadow-md;
|
|
68
68
|
|
|
69
69
|
.v-popper__wrapper {
|
|
70
70
|
.v-popper__inner {
|
|
71
|
-
@apply spr-overflow-hidden spr-rounded-none spr-border-none spr-bg-transparent spr-shadow-
|
|
71
|
+
@apply spr-overflow-hidden spr-rounded-none spr-border-none spr-bg-transparent spr-shadow-none;
|
|
72
72
|
|
|
73
73
|
.slide-fade-enter-active {
|
|
74
74
|
@apply spr-duration-300 spr-ease-out;
|
|
@@ -124,11 +124,11 @@
|
|
|
124
124
|
|
|
125
125
|
/* #region - Dropdown */
|
|
126
126
|
#dropdown-wrapper {
|
|
127
|
-
@apply spr-z-[1000] spr-w-[inherit] spr-overflow-hidden;
|
|
127
|
+
@apply spr-border-color-weak spr-z-[1000] spr-w-[inherit] spr-overflow-hidden spr-rounded-xl spr-border spr-border-solid spr-shadow-drop;
|
|
128
128
|
|
|
129
129
|
.v-popper__wrapper {
|
|
130
130
|
.v-popper__inner {
|
|
131
|
-
@apply spr-
|
|
131
|
+
@apply spr-w-full spr-rounded-none spr-border-none spr-font-main spr-shadow-none;
|
|
132
132
|
|
|
133
133
|
&::-webkit-scrollbar-track {
|
|
134
134
|
@apply spr-rounded-br-xl spr-rounded-tr-xl;
|
|
@@ -146,11 +146,11 @@
|
|
|
146
146
|
#select-wrapper,
|
|
147
147
|
#multi-select-wrapper,
|
|
148
148
|
#ladderized-select-wrapper {
|
|
149
|
-
@apply spr-z-[1000] spr-w-[inherit] spr-overflow-hidden;
|
|
149
|
+
@apply spr-border-color-weak spr-z-[1000] spr-w-[inherit] spr-overflow-hidden spr-rounded-xl spr-border spr-border-solid spr-shadow-drop;
|
|
150
150
|
|
|
151
151
|
.v-popper__wrapper {
|
|
152
152
|
.v-popper__inner {
|
|
153
|
-
@apply spr-
|
|
153
|
+
@apply spr-w-full spr-rounded-none spr-border-none spr-font-main spr-shadow-none;
|
|
154
154
|
|
|
155
155
|
&::-webkit-scrollbar-track {
|
|
156
156
|
@apply spr-rounded-br-xl spr-rounded-tr-xl;
|
|
@@ -183,6 +183,7 @@
|
|
|
183
183
|
thead {
|
|
184
184
|
@apply spr-pr-size-spacing-6xs;
|
|
185
185
|
}
|
|
186
|
+
|
|
186
187
|
tbody::-webkit-scrollbar {
|
|
187
188
|
@apply spr-h-0 spr-w-0; /* Hides the scrollbar in WebKit browsers */
|
|
188
189
|
}
|
|
@@ -418,6 +419,26 @@
|
|
|
418
419
|
|
|
419
420
|
/* #endregion - Labels */
|
|
420
421
|
|
|
422
|
+
/* #region - Attribute Filter */
|
|
423
|
+
#attribute_filter_wrapper {
|
|
424
|
+
@apply spr-z-[1000] spr-w-[inherit] spr-overflow-hidden;
|
|
425
|
+
|
|
426
|
+
.v-popper__wrapper {
|
|
427
|
+
.v-popper__inner {
|
|
428
|
+
@apply spr-border-color-weak spr-w-full spr-rounded-xl spr-border spr-border-solid spr-font-main spr-shadow-none;
|
|
429
|
+
|
|
430
|
+
&::-webkit-scrollbar-track {
|
|
431
|
+
@apply spr-rounded-br-xl spr-rounded-tr-xl;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.v-popper__arrow-container {
|
|
436
|
+
@apply spr-hidden;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/* #endregion - Attribute Filter */
|
|
441
|
+
|
|
421
442
|
/* #region - Font Size */
|
|
422
443
|
.spr-font-size-100 {
|
|
423
444
|
@apply spr-text-100;
|
|
@@ -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,
|