design-system-next 2.9.7 → 2.9.10
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.js +6672 -6381
- package/dist/design-system-next.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 +1 -80
- package/src/assets/styles/tailwind.css +10 -0
- package/src/components/avatar/avatar.ts +4 -0
- package/src/components/avatar/avatar.vue +5 -1
- package/src/components/avatar/use-avatar.ts +2 -1
- package/src/components/calendar/calendar.ts +9 -0
- package/src/components/calendar/calendar.vue +69 -24
- package/src/components/calendar/use-calendar.ts +6 -4
- package/src/components/calendar-cell/calendar-cell.ts +4 -0
- package/src/components/calendar-cell/calendar-cell.vue +3 -1
- package/src/components/calendar-cell/use-calendar-cell.ts +3 -2
- package/src/components/card/card.ts +5 -0
- package/src/components/card/card.vue +1 -1
- package/src/components/card/use-card.ts +8 -1
- package/src/components/filter/filter.ts +12 -0
- package/src/components/filter/filter.vue +23 -13
- package/src/components/filter/use-filter.ts +49 -19
- package/src/components/lozenge/lozenge.ts +4 -0
- package/src/components/lozenge/lozenge.vue +3 -2
- package/src/components/lozenge/use-lozenge.ts +12 -5
- package/src/components/select/select-ladderized/use-select-ladderized.ts +164 -164
- package/src/components/sidenav/sidenav.ts +47 -7
- package/src/components/sidenav/sidenav.vue +6 -6
- package/src/components/sidenav/use-sidenav.ts +97 -3
|
@@ -13,13 +13,14 @@ interface AvatarClasses {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const useAvatar = (props: AvatarPropTypes) => {
|
|
16
|
-
const { size, color, variant, initial } = toRefs(props);
|
|
16
|
+
const { size, color, variant, initial, loading } = toRefs(props);
|
|
17
17
|
|
|
18
18
|
const avatarClasses: ComputedRef<AvatarClasses> = computed(() => {
|
|
19
19
|
const baseClasses = classNames('spr-relative spr-inline-block spr-rounded-full', {
|
|
20
20
|
'spr-background-color-surface': color.value === 'primary',
|
|
21
21
|
'spr-background-color': color.value === 'secondary',
|
|
22
22
|
'spr-background-color spr-border-color-success-base spr-border spr-border-solid': color.value === 'tertiary',
|
|
23
|
+
'spr-skeletal-loader spr-h-full spr-w-full': loading.value,
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
const imageContainerClasses = classNames(
|
|
@@ -71,6 +71,15 @@ export const calendarPropTypes = {
|
|
|
71
71
|
type: Boolean,
|
|
72
72
|
default: false,
|
|
73
73
|
},
|
|
74
|
+
|
|
75
|
+
infiniteLoading: {
|
|
76
|
+
type: Boolean,
|
|
77
|
+
default: false,
|
|
78
|
+
},
|
|
79
|
+
loadingTextCompleted: {
|
|
80
|
+
type: String,
|
|
81
|
+
default: '',
|
|
82
|
+
},
|
|
74
83
|
};
|
|
75
84
|
|
|
76
85
|
export const calendarEmitTypes = {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<SprCard :has-content-padding="false" class="spr-flex spr-h-full spr-flex-col spr-overflow-hidden">
|
|
2
|
+
<SprCard flexbox :has-content-padding="false" class="spr-flex spr-h-full spr-flex-col spr-overflow-hidden">
|
|
3
3
|
<template #content>
|
|
4
4
|
<div :class="getCalendarClasses.contentWrapper">
|
|
5
5
|
<div :class="[getCalendarClasses.headerWrapper]">
|
|
@@ -20,17 +20,19 @@
|
|
|
20
20
|
<!-- Filters -->
|
|
21
21
|
<slot name="filter" />
|
|
22
22
|
|
|
23
|
-
<div
|
|
24
|
-
<div class="spr-
|
|
25
|
-
<table
|
|
26
|
-
id="table-calendar"
|
|
27
|
-
aria-describedby="calendar"
|
|
28
|
-
:class="[getCalendarClasses.calendarTable, 'spr-relative']"
|
|
29
|
-
>
|
|
23
|
+
<div class="spr-table-wrapper spr-relative spr-flex spr-h-full spr-flex-col spr-overflow-hidden">
|
|
24
|
+
<div class="spr-h-full">
|
|
25
|
+
<table id="table-calendar" aria-describedby="calendar" :class="getCalendarClasses.calendarTable">
|
|
30
26
|
<!-- Calendar Header -->
|
|
31
|
-
<thead
|
|
27
|
+
<thead>
|
|
32
28
|
<tr>
|
|
33
|
-
<th
|
|
29
|
+
<th
|
|
30
|
+
:class="[
|
|
31
|
+
getCalendarClasses.tableHeaderEmployeeName,
|
|
32
|
+
'spr-sticky spr-left-0',
|
|
33
|
+
getCalendarClasses.borderClasses,
|
|
34
|
+
]"
|
|
35
|
+
>
|
|
34
36
|
<div :class="getCalendarClasses.headerContent">
|
|
35
37
|
<div>Employee Name</div>
|
|
36
38
|
<div
|
|
@@ -65,12 +67,12 @@
|
|
|
65
67
|
</th>
|
|
66
68
|
</tr>
|
|
67
69
|
</thead>
|
|
68
|
-
<tbody v-if="employees.length > 0 && !loading" class="spr-overflow-y-auto">
|
|
70
|
+
<tbody v-if="employees.length > 0 && !loading" ref="tableBodyRef" class="spr-h-full spr-overflow-y-auto">
|
|
69
71
|
<tr v-for="employee in employees" :key="employee.id">
|
|
70
72
|
<td
|
|
71
73
|
:class="[
|
|
72
74
|
getCalendarClasses.borderClasses,
|
|
73
|
-
'spr-bg-white spr-sticky spr-left-0 spr-z-10 spr-content-start spr-border-y spr-border-
|
|
75
|
+
'spr-bg-white spr-sticky spr-left-0 spr-z-10 spr-content-start spr-border-y spr-border-l-0 spr-border-r spr-border-t-0 spr-p-size-spacing-xs',
|
|
74
76
|
]"
|
|
75
77
|
>
|
|
76
78
|
<div class="spr-flex spr-flex-col spr-gap-size-spacing-3xs spr-overflow-hidden">
|
|
@@ -81,7 +83,9 @@
|
|
|
81
83
|
:variant="employee.avatar ? 'image' : 'initial'"
|
|
82
84
|
color="tertiary"
|
|
83
85
|
/>
|
|
84
|
-
<div class="spr-label-xs-regular">
|
|
86
|
+
<div class="spr-label-xs-regular">
|
|
87
|
+
{{ employee.name }}
|
|
88
|
+
</div>
|
|
85
89
|
<div class="spr-text-color-supporting spr-label-xs-regular spr-uppercase">
|
|
86
90
|
{{ employee.position }}
|
|
87
91
|
</div>
|
|
@@ -102,7 +106,7 @@
|
|
|
102
106
|
:key="index"
|
|
103
107
|
:class="[
|
|
104
108
|
getCalendarClasses.borderClasses,
|
|
105
|
-
'spr-min-w-[180px] spr-content-start spr-space-y-size-spacing-3xs spr-border-x spr-border-b
|
|
109
|
+
'spr-min-w-[180px] spr-content-start spr-space-y-size-spacing-3xs spr-border-x spr-border-b spr-border-t-0 spr-p-size-spacing-sm last:spr-mb-size-spacing-lg last:spr-border-r-0',
|
|
106
110
|
]"
|
|
107
111
|
@mouseover="handleHover(true, index, employee.id)"
|
|
108
112
|
@mouseleave="handleHover(false, index, employee.id)"
|
|
@@ -148,6 +152,7 @@
|
|
|
148
152
|
:title="`${schedule.startTime} - ${schedule.endTime}`"
|
|
149
153
|
:description="schedule.location"
|
|
150
154
|
:sub-description="schedule.type"
|
|
155
|
+
:type="schedule.color"
|
|
151
156
|
fullwidth
|
|
152
157
|
/>
|
|
153
158
|
</div>
|
|
@@ -172,8 +177,57 @@
|
|
|
172
177
|
</section>
|
|
173
178
|
</td>
|
|
174
179
|
</tr>
|
|
180
|
+
<tr v-if="props.infiniteLoading || props.loadingTextCompleted">
|
|
181
|
+
<td
|
|
182
|
+
:colspan="weekDates.length + 1"
|
|
183
|
+
class="spr-flex spr-h-full spr-items-center spr-justify-center spr-p-size-spacing-xs"
|
|
184
|
+
>
|
|
185
|
+
<div v-if="props.infiniteLoading">
|
|
186
|
+
<Icon icon="svg-spinners:ring-resize" class="spr-text-color-success-base spr-font-size-400" />
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div v-else>
|
|
190
|
+
{{ props.loadingTextCompleted }}
|
|
191
|
+
</div>
|
|
192
|
+
</td>
|
|
193
|
+
</tr>
|
|
175
194
|
</tbody>
|
|
176
|
-
<tbody v-
|
|
195
|
+
<tbody v-if="employees.length === 0 && loading">
|
|
196
|
+
<tr v-for="employeeKey in 10" :key="employeeKey">
|
|
197
|
+
<td
|
|
198
|
+
:class="[
|
|
199
|
+
getCalendarClasses.borderClasses,
|
|
200
|
+
'spr-bg-white spr-sticky spr-left-0 spr-z-10 spr-content-start spr-border-y spr-border-l-0 spr-border-r spr-border-t-0 spr-p-size-spacing-xs',
|
|
201
|
+
]"
|
|
202
|
+
>
|
|
203
|
+
<div class="spr-flex spr-flex-col spr-gap-size-spacing-3xs spr-overflow-hidden">
|
|
204
|
+
<spr-avatar size="md" variant="initial" color="tertiary" loading />
|
|
205
|
+
<div :class="[{ 'spr-skeletal-loader spr-h-6 spr-rounded-md': true }]" />
|
|
206
|
+
<div :class="[{ 'spr-skeletal-loader spr-h-6 spr-rounded-md': true }]" />
|
|
207
|
+
</div>
|
|
208
|
+
<div class="spr-mt-size-spacing-xs">
|
|
209
|
+
<spr-lozenge loading />
|
|
210
|
+
</div>
|
|
211
|
+
</td>
|
|
212
|
+
<td
|
|
213
|
+
v-for="weekDays in 7"
|
|
214
|
+
:key="weekDays"
|
|
215
|
+
:class="[
|
|
216
|
+
getCalendarClasses.borderClasses,
|
|
217
|
+
'spr-min-w-[180px] spr-content-start spr-space-y-size-spacing-3xs spr-border-x spr-border-b spr-border-t-0 spr-p-size-spacing-sm last:spr-mb-size-spacing-lg last:spr-border-r-0',
|
|
218
|
+
]"
|
|
219
|
+
>
|
|
220
|
+
<section class="spr-flex spr-flex-col spr-justify-start spr-gap-size-spacing-3xs">
|
|
221
|
+
<div v-for="weekDaysValue in 1" :key="weekDaysValue" class="spr-w-full">
|
|
222
|
+
<div class="spr-flex spr-flex-col spr-items-center spr-justify-start">
|
|
223
|
+
<spr-calendar-cell type="restday" fullwidth loading />
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</section>
|
|
227
|
+
</td>
|
|
228
|
+
</tr>
|
|
229
|
+
</tbody>
|
|
230
|
+
<tbody v-else class="spr-h-full">
|
|
177
231
|
<tr v-if="!loading" class="spr-h-full">
|
|
178
232
|
<td :colspan="weekDates.length + 1" class="spr-flex spr-h-full spr-items-center spr-justify-center">
|
|
179
233
|
<slot name="empty-state">
|
|
@@ -193,15 +247,6 @@
|
|
|
193
247
|
</slot>
|
|
194
248
|
</td>
|
|
195
249
|
</tr>
|
|
196
|
-
<tr v-else>
|
|
197
|
-
<td :colspan="weekDates.length + 1" class="spr-h-[360px] spr-overflow-hidden">
|
|
198
|
-
<slot name="loading">
|
|
199
|
-
<div class="spr-flex spr-items-center spr-justify-center">
|
|
200
|
-
<Icon size="48" icon="svg-spinners:ring-resize" class="spr-text-color-success-base" />
|
|
201
|
-
</div>
|
|
202
|
-
</slot>
|
|
203
|
-
</td>
|
|
204
|
-
</tr>
|
|
205
250
|
</tbody>
|
|
206
251
|
</table>
|
|
207
252
|
</div>
|
|
@@ -141,15 +141,17 @@ export const useCalendar = (props: CalendarPropTypes, emit: SetupContext<Calenda
|
|
|
141
141
|
'spr-bg-color-weak spr-flex spr-w-full spr-items-center spr-justify-between spr-p-size-spacing-sm',
|
|
142
142
|
);
|
|
143
143
|
|
|
144
|
-
const contentWrapper = classNames(
|
|
144
|
+
const contentWrapper = classNames(
|
|
145
|
+
' spr-flex spr-flex-col spr-h-full spr-divide-color-weak spr-divide-x-0 spr-divide-y spr-divide-solid',
|
|
146
|
+
);
|
|
145
147
|
const calendarTable = classNames(
|
|
146
|
-
'
|
|
148
|
+
'spr-h-full spr-table spr-w-full spr-table-fixed spr-border-collapse spr-border-spacing-0 spr-rounded-border',
|
|
147
149
|
);
|
|
148
150
|
const tableHeaderEmployeeName = classNames(
|
|
149
|
-
'spr-sticky spr-left-0 spr-z-20 spr-background-color spr-body-xs-regular-medium spr-p-size-spacing-xs spr-text-left spr-overflow-hidden spr-h-full',
|
|
151
|
+
'spr-sticky spr-left-0 spr-z-20 spr-background-color spr-body-xs-regular-medium spr-p-size-spacing-xs spr-text-left spr-overflow-hidden spr-h-full spr-border-x-0 spr-border-t-0 ',
|
|
150
152
|
);
|
|
151
153
|
const tableHeader = classNames(
|
|
152
|
-
'spr-background-color spr-border-x-0 spr-border-
|
|
154
|
+
'spr-background-color spr-border-x-0 spr-border-t-0 spr-border-l spr-p-size-spacing-sm spr-text-center',
|
|
153
155
|
);
|
|
154
156
|
const headerContent = classNames(
|
|
155
157
|
'spr-flex spr-flex-row spr-w-full spr-items-center spr-gap-size-spacing-3xs lg:spr-flex-col spr-overflow-hidden',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="getCalendarCellClassess.getMainClasses" @click="handleClick">
|
|
2
|
+
<div v-if="!props.loading" :class="getCalendarCellClassess.getMainClasses" @click="handleClick">
|
|
3
3
|
<slot name="prefix">
|
|
4
4
|
<Icon v-if="hasIconStatus" :icon="getCellIcon" />
|
|
5
5
|
</slot>
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
</div>
|
|
19
19
|
</slot>
|
|
20
20
|
</div>
|
|
21
|
+
|
|
22
|
+
<div v-else :class="getCalendarCellClassess.getMainClasses" />
|
|
21
23
|
</template>
|
|
22
24
|
|
|
23
25
|
<script setup lang="ts">
|
|
@@ -66,11 +66,12 @@ export const useCalendarCell = (props: CalendarCellPropTypes, emit: SetupContext
|
|
|
66
66
|
|
|
67
67
|
const getCalendarCellClassess = computed(() => {
|
|
68
68
|
const calendarCellWrapper = classNames(
|
|
69
|
-
'spr-flex spr-items-center
|
|
69
|
+
'spr-flex spr-items-center spr-p-size-spacing-3xs spr-gap-size-spacing-3xs spr-relative spr-rounded-lg spr-border-2 spr-transition-all sm:spr-flex-col spr-overflow-hidden',
|
|
70
70
|
{
|
|
71
71
|
'spr-w-full': props.fullwidth,
|
|
72
72
|
'spr-max-w-[217px]': !props.fullwidth,
|
|
73
|
-
'hover:spr-drop-shadow-sm
|
|
73
|
+
'hover:spr-drop-shadow-sm spr-cursor-pointer': !props.viewOnly,
|
|
74
|
+
'spr-h-[80px] spr-skeletal-loader': props.loading,
|
|
74
75
|
},
|
|
75
76
|
);
|
|
76
77
|
|
|
@@ -8,10 +8,12 @@ interface CardClasses {
|
|
|
8
8
|
headerClasses: string;
|
|
9
9
|
contentPaddingSizeClasses: string;
|
|
10
10
|
footerClasses: string;
|
|
11
|
+
hasFlexBox: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const useCard = (props: CardPropTypes, slots: Slots) => {
|
|
14
|
-
const { title, headerIcon, borderRadiusSize, hasCollapsible, isCollapsibleOpen, hasContentPadding } =
|
|
15
|
+
const { title, headerIcon, borderRadiusSize, hasCollapsible, isCollapsibleOpen, hasContentPadding, flexbox } =
|
|
16
|
+
toRefs(props);
|
|
15
17
|
|
|
16
18
|
const cardClasses: ComputedRef<CardClasses> = computed(() => {
|
|
17
19
|
const baseClasses = classNames(
|
|
@@ -56,11 +58,16 @@ export const useCard = (props: CardPropTypes, slots: Slots) => {
|
|
|
56
58
|
},
|
|
57
59
|
);
|
|
58
60
|
|
|
61
|
+
const hasFlexBox = classNames({
|
|
62
|
+
'spr-flex spr-flex-col spr-h-full': flexbox.value,
|
|
63
|
+
});
|
|
64
|
+
|
|
59
65
|
return {
|
|
60
66
|
baseClasses,
|
|
61
67
|
headerClasses,
|
|
62
68
|
contentPaddingSizeClasses,
|
|
63
69
|
footerClasses,
|
|
70
|
+
hasFlexBox,
|
|
64
71
|
};
|
|
65
72
|
});
|
|
66
73
|
|
|
@@ -73,6 +73,18 @@ export const filterPropTypes = {
|
|
|
73
73
|
type: Boolean,
|
|
74
74
|
default: false,
|
|
75
75
|
},
|
|
76
|
+
error: {
|
|
77
|
+
type: Boolean,
|
|
78
|
+
default: false,
|
|
79
|
+
},
|
|
80
|
+
helperText: {
|
|
81
|
+
type: String,
|
|
82
|
+
default: '',
|
|
83
|
+
},
|
|
84
|
+
hasAdvancedFilterApi: {
|
|
85
|
+
type: Boolean,
|
|
86
|
+
default: false,
|
|
87
|
+
},
|
|
76
88
|
};
|
|
77
89
|
|
|
78
90
|
export interface FilterPropsInterface {
|
|
@@ -6,22 +6,24 @@
|
|
|
6
6
|
placement="bottom"
|
|
7
7
|
:triggers="[]"
|
|
8
8
|
:popper-hide-triggers="[]"
|
|
9
|
-
:container="`#${
|
|
9
|
+
:container="`#${generateStableId}`"
|
|
10
10
|
:auto-hide="false"
|
|
11
11
|
:delay="0"
|
|
12
|
+
:popper-class="'filter-menu-popper'"
|
|
12
13
|
:style="{
|
|
13
14
|
width: props.width,
|
|
14
15
|
position: 'relative',
|
|
15
16
|
}"
|
|
16
17
|
>
|
|
17
|
-
<
|
|
18
|
-
:id="
|
|
18
|
+
<div
|
|
19
|
+
:id="generateStableId"
|
|
19
20
|
:style="{
|
|
20
21
|
width: props.width,
|
|
21
22
|
position: 'relative',
|
|
22
23
|
}"
|
|
23
24
|
@click="isFilterOpen = true"
|
|
24
25
|
>
|
|
26
|
+
<!-- Main Search Input -->
|
|
25
27
|
<slot>
|
|
26
28
|
<spr-input
|
|
27
29
|
:id="props.id"
|
|
@@ -30,13 +32,16 @@
|
|
|
30
32
|
:placeholder="placeholder"
|
|
31
33
|
:label="label"
|
|
32
34
|
:disabled="disabled"
|
|
35
|
+
:helper-text="props.helperText"
|
|
36
|
+
:display-helper="!!props.helperText"
|
|
37
|
+
:error="props.error"
|
|
33
38
|
>
|
|
34
39
|
<template #icon>
|
|
35
40
|
<Icon icon="ph:magnifying-glass" />
|
|
36
41
|
</template>
|
|
37
42
|
</spr-input>
|
|
38
43
|
</slot>
|
|
39
|
-
</
|
|
44
|
+
</div>
|
|
40
45
|
|
|
41
46
|
<template #popper>
|
|
42
47
|
<div :class="filterClass.MenuOptionClasses">
|
|
@@ -58,6 +63,8 @@
|
|
|
58
63
|
placement="right-start"
|
|
59
64
|
:triggers="['click']"
|
|
60
65
|
:auto-hide="false"
|
|
66
|
+
:delay="0"
|
|
67
|
+
popover-base="filter-menu-base"
|
|
61
68
|
>
|
|
62
69
|
<spr-button id="add-filter-button" has-icon variant="secondary" size="small">
|
|
63
70
|
<Icon icon="ph:faders-horizontal" />
|
|
@@ -79,7 +86,9 @@
|
|
|
79
86
|
aria-id="filter-menu-wrapper"
|
|
80
87
|
placement="right"
|
|
81
88
|
:triggers="['click']"
|
|
89
|
+
:delay="0"
|
|
82
90
|
:auto-hide="false"
|
|
91
|
+
popover-base="filter-menu-field"
|
|
83
92
|
>
|
|
84
93
|
<spr-chips
|
|
85
94
|
:active="mappedFilterMenuList[menu.field].isFilterVisible"
|
|
@@ -104,6 +113,7 @@
|
|
|
104
113
|
</div>
|
|
105
114
|
|
|
106
115
|
<div class="spr-p-size-spacing-2xs">
|
|
116
|
+
<!-- search for the filter Option -->
|
|
107
117
|
<spr-input
|
|
108
118
|
:id="`${props.id}-search`"
|
|
109
119
|
v-model="filterMenuSearchvalue"
|
|
@@ -130,26 +140,26 @@
|
|
|
130
140
|
/>
|
|
131
141
|
</div>
|
|
132
142
|
<div
|
|
133
|
-
v-if="
|
|
143
|
+
v-if="getFilteredMenuOption.length > 0"
|
|
134
144
|
:id="menu.field"
|
|
135
145
|
ref="filterMenuOptionList"
|
|
136
146
|
class="spr-h-[264px] spr-space-y-size-spacing-6xs spr-overflow-auto spr-p-size-spacing-2xs"
|
|
137
147
|
>
|
|
138
148
|
<div
|
|
139
|
-
v-for="(option, key) in
|
|
149
|
+
v-for="(option, key) in getFilteredMenuOption"
|
|
140
150
|
:key="option.value"
|
|
141
151
|
:class="[
|
|
142
152
|
filterClass.filterListClasses,
|
|
143
|
-
{ 'spr-background-color-multiple-active':
|
|
153
|
+
{ 'spr-background-color-multiple-active': getFilteredMenuOption[key].isSelected },
|
|
144
154
|
]"
|
|
145
155
|
>
|
|
146
156
|
<spr-checkbox
|
|
147
157
|
id="filter-menu-option"
|
|
148
|
-
v-model="
|
|
158
|
+
v-model="getFilteredMenuOption[key].isSelected"
|
|
149
159
|
class="spr-w-full"
|
|
150
|
-
:checked="
|
|
151
|
-
:label="
|
|
152
|
-
:description="
|
|
160
|
+
:checked="getFilteredMenuOption[key].isSelected"
|
|
161
|
+
:label="getFilteredMenuOption[key].text"
|
|
162
|
+
:description="getFilteredMenuOption[key].subtext"
|
|
153
163
|
/>
|
|
154
164
|
</div>
|
|
155
165
|
</div>
|
|
@@ -264,11 +274,11 @@ const {
|
|
|
264
274
|
isAddFilterVisible,
|
|
265
275
|
getFiltereredOption,
|
|
266
276
|
getSelectedFilterMenuOption,
|
|
267
|
-
|
|
277
|
+
getFilteredMenuOption,
|
|
268
278
|
filterMenuSearchvalue,
|
|
269
279
|
mappedFilterMenuList,
|
|
270
280
|
filterClass,
|
|
271
|
-
|
|
281
|
+
generateStableId,
|
|
272
282
|
filterOptionRef,
|
|
273
283
|
filterMenuOptionList,
|
|
274
284
|
|
|
@@ -7,7 +7,17 @@ import dayjs from 'dayjs';
|
|
|
7
7
|
import { useInfiniteScroll, onClickOutside } from '@vueuse/core';
|
|
8
8
|
|
|
9
9
|
export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitTypes>['emit']) => {
|
|
10
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
options,
|
|
12
|
+
filterMenu,
|
|
13
|
+
filterData,
|
|
14
|
+
loading,
|
|
15
|
+
filterable,
|
|
16
|
+
filling,
|
|
17
|
+
deselected,
|
|
18
|
+
hasSearchApi,
|
|
19
|
+
hasAdvancedFilterApi,
|
|
20
|
+
} = toRefs(props);
|
|
11
21
|
|
|
12
22
|
const selectedValue = useVModel(props, 'modelValue', emit);
|
|
13
23
|
const searchText = useVModel(props, 'search', emit);
|
|
@@ -20,14 +30,13 @@ export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitT
|
|
|
20
30
|
const isAdvanceFilterVisible = ref<boolean>(false);
|
|
21
31
|
const mappedMenuData = ref<Record<string, FilterPropsInterface['optionDetails']>>({});
|
|
22
32
|
const mappedFilterMenuList = ref<Record<string, FilterPropsInterface['filterDetails']>>({});
|
|
23
|
-
const uniqueId = ref<string>(`filter-${dayjs().valueOf()}-${Math.floor(Math.random() * 1000)}`);
|
|
24
33
|
const filterMenuList = ref<FilterPropsInterface['filterDetails'][]>(
|
|
25
34
|
filterMenu.value as FilterPropsInterface['filterDetails'][],
|
|
26
35
|
);
|
|
27
36
|
const selectedFilters = ref<FilterPropsInterface['optionDetails'][]>([]);
|
|
28
37
|
const filterOptionRef = ref<HTMLDivElement | null>(null);
|
|
29
38
|
const filterMenuOptionList = ref<(HTMLDivElement | null)[]>([]);
|
|
30
|
-
const selectedColumn = ref('');
|
|
39
|
+
const selectedColumn = ref<string>('');
|
|
31
40
|
|
|
32
41
|
// Main List
|
|
33
42
|
const getFiltereredOption = computed<FilterPropsInterface['optionDetails'][]>(() => {
|
|
@@ -47,19 +56,24 @@ export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitT
|
|
|
47
56
|
});
|
|
48
57
|
|
|
49
58
|
// Filter Menu Option List
|
|
50
|
-
const
|
|
59
|
+
const getFilteredMenuOption = computed<FilterPropsInterface['optionDetails'][]>(() => {
|
|
51
60
|
if (loading.value) return filterData.value;
|
|
52
61
|
|
|
62
|
+
const searchQuery = hasAdvancedFilterApi.value ? '' : filterMenuSearchvalue.value.toLowerCase();
|
|
63
|
+
|
|
53
64
|
const filter = filterData.value.filter((option, index) => {
|
|
54
65
|
const existing = selectedFilters.value.find(
|
|
55
|
-
(prevSelected) =>
|
|
66
|
+
(prevSelected) =>
|
|
67
|
+
prevSelected.value === option.value &&
|
|
68
|
+
prevSelected.isSelected &&
|
|
69
|
+
prevSelected.column === selectedColumn.value,
|
|
56
70
|
);
|
|
57
71
|
|
|
58
72
|
if (existing) {
|
|
59
73
|
filterData.value[index].isSelected = true;
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
return option.text.toLowerCase().includes(
|
|
76
|
+
return option.text.toLowerCase().includes(searchQuery.toLowerCase());
|
|
63
77
|
});
|
|
64
78
|
|
|
65
79
|
return filter;
|
|
@@ -70,12 +84,24 @@ export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitT
|
|
|
70
84
|
});
|
|
71
85
|
|
|
72
86
|
const getSelectedFilterMenuOption = computed(() => {
|
|
73
|
-
return
|
|
87
|
+
return selectedFilters.value.filter((item) => item.isSelected && item.column === selectedColumn.value);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const generateStableId = computed(() => {
|
|
91
|
+
if (props.id) {
|
|
92
|
+
return `filter-popover-${props.id}`;
|
|
93
|
+
}
|
|
94
|
+
return `filter-popover-${dayjs().valueOf().toString().slice(-8)}`;
|
|
74
95
|
});
|
|
75
96
|
|
|
76
97
|
const getMappedFilterData = (column: string) => {
|
|
77
|
-
|
|
78
|
-
|
|
98
|
+
mappedFilterMenuList.value[column].isFilterVisible = true;
|
|
99
|
+
|
|
100
|
+
if (selectedColumn.value !== column) {
|
|
101
|
+
filterMenuSearchvalue.value = '';
|
|
102
|
+
selectedColumn.value = column;
|
|
103
|
+
emit('getFilterData', column);
|
|
104
|
+
}
|
|
79
105
|
};
|
|
80
106
|
|
|
81
107
|
const setFilterVisible = (field: string) => {
|
|
@@ -112,20 +138,23 @@ export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitT
|
|
|
112
138
|
};
|
|
113
139
|
|
|
114
140
|
const saveSelectedFilter = (field: string) => {
|
|
115
|
-
const selectedValues =
|
|
116
|
-
|
|
141
|
+
const selectedValues = getFilteredMenuOption.value.filter((option) => option.isSelected && option.column === field);
|
|
142
|
+
|
|
143
|
+
const newValues = selectedValues.filter(
|
|
144
|
+
(newVal) =>
|
|
145
|
+
!selectedFilters.value.some((existing) => existing.value === newVal.value && existing.column === newVal.column),
|
|
117
146
|
);
|
|
118
|
-
selectedFilters.value = selectedFilters.value
|
|
119
|
-
.filter((prevSelected) => prevSelected.column !== field)
|
|
120
|
-
.concat(selectedValues);
|
|
121
147
|
|
|
122
|
-
|
|
148
|
+
selectedFilters.value = [...selectedFilters.value, ...newValues];
|
|
149
|
+
|
|
123
150
|
mappedFilterMenuList.value[field].count = selectedValues.length;
|
|
124
151
|
mappedFilterMenuList.value[field].isFilterVisible = false;
|
|
152
|
+
filterMenuSearchvalue.value = '';
|
|
153
|
+
emit('selectedFilter', selectedFilters.value);
|
|
125
154
|
};
|
|
126
155
|
|
|
127
156
|
const handleRemoveFilterValues = (filter: string) => {
|
|
128
|
-
|
|
157
|
+
getFilteredMenuOption.value.map((option) => {
|
|
129
158
|
if (option.value === filter) {
|
|
130
159
|
option.isSelected = false;
|
|
131
160
|
}
|
|
@@ -133,6 +162,7 @@ export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitT
|
|
|
133
162
|
});
|
|
134
163
|
|
|
135
164
|
selectedFilters.value = selectedFilters.value.filter((item) => item.value !== filter);
|
|
165
|
+
mappedFilterMenuList.value[selectedColumn.value].count = selectedFilters.value.length;
|
|
136
166
|
emit('selectedFilter', selectedFilters.value);
|
|
137
167
|
};
|
|
138
168
|
|
|
@@ -195,7 +225,7 @@ export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitT
|
|
|
195
225
|
});
|
|
196
226
|
|
|
197
227
|
watch(filterMenuSearchvalue, (value, oldValue) => {
|
|
198
|
-
if (value === oldValue) return; // Prevent unnecessary updates
|
|
228
|
+
if (value === oldValue || !hasAdvancedFilterApi.value) return; // Prevent unnecessary updates
|
|
199
229
|
searchFilterValue.value = value;
|
|
200
230
|
});
|
|
201
231
|
|
|
@@ -249,11 +279,11 @@ export const useFilter = (props: FilterPropTypes, emit: SetupContext<FilterEmitT
|
|
|
249
279
|
getFiltereredOption,
|
|
250
280
|
mappedMenuData,
|
|
251
281
|
getSelectedFilterMenuOption,
|
|
252
|
-
|
|
282
|
+
getFilteredMenuOption,
|
|
253
283
|
filterMenuSearchvalue,
|
|
254
284
|
mappedFilterMenuList,
|
|
255
285
|
filterClass,
|
|
256
|
-
|
|
286
|
+
generateStableId,
|
|
257
287
|
filterOptionRef,
|
|
258
288
|
filterMenuOptionList,
|
|
259
289
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<div v-if="visible" :class="lozengeClasses.baseClasses">
|
|
2
|
+
<div :class="lozengeClasses.wrapperClasses">
|
|
3
|
+
<div v-if="visible && !loading" :class="lozengeClasses.baseClasses">
|
|
4
4
|
<div :class="lozengeClasses.toneClasses">
|
|
5
5
|
<div v-if="$slots.icon" class="spr-flex spr-h-3 spr-w-3 spr-items-center spr-overflow-hidden">
|
|
6
6
|
<slot name="icon" />
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
<div>{{ label }}</div>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
|
+
<div v-if="visible && loading" :class="lozengeClasses.baseClasses" />
|
|
20
21
|
</div>
|
|
21
22
|
</template>
|
|
22
23
|
|