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.
@@ -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 ref="tableBodyRef" class="spr-table-wrapper spr-h-[calc(100vh-12rem)] spr-w-full spr-overflow-auto">
24
- <div class="spr-pb-size-spacing-lg">
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 class="spr-bg-white spr-sticky spr-top-0 spr-z-20">
27
+ <thead>
32
28
  <tr>
33
- <th :class="[getCalendarClasses.tableHeaderEmployeeName, 'spr-sticky spr-left-0']">
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-b-0 spr-border-l-0 spr-border-r spr-p-size-spacing-xs',
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">{{ employee.name }}</div>
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-0 spr-border-t spr-p-size-spacing-sm last:spr-mb-size-spacing-lg last:spr-border-r-0',
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-else>
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('spr-divide-color-weak spr-divide-x-0 spr-divide-y spr-divide-solid');
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
- ' spr-overflow-y-auto spr-h-full spr-table spr-w-full spr-table-fixed spr-border-collapse spr-border-spacing-0 spr-rounded-border',
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-y-0 spr-border-l spr-p-size-spacing-sm spr-text-center',
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',
@@ -45,6 +45,10 @@ export const calendarCellPropTypes = {
45
45
  type: String,
46
46
  default: '',
47
47
  },
48
+ loading: {
49
+ type: Boolean,
50
+ default: false,
51
+ },
48
52
  };
49
53
 
50
54
  export type CalendarCellPropTypes = ExtractPropTypes<typeof calendarCellPropTypes>;
@@ -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 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',
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 spr-cursor-pointer': !props.viewOnly,
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
 
@@ -42,6 +42,11 @@ export const cardPropTypes = {
42
42
  type: Boolean,
43
43
  default: true,
44
44
  },
45
+
46
+ flexbox: {
47
+ type: Boolean,
48
+ default: false,
49
+ },
45
50
  };
46
51
 
47
52
  export type CardPropTypes = ExtractPropTypes<typeof cardPropTypes>;
@@ -21,7 +21,7 @@
21
21
  </template>
22
22
  </div>
23
23
 
24
- <div v-if="$slots.content" :class="cardClasses.contentPaddingSizeClasses">
24
+ <div v-if="$slots.content" :class="[cardClasses.contentPaddingSizeClasses, cardClasses.hasFlexBox]">
25
25
  <slot name="content" />
26
26
  </div>
27
27
 
@@ -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 } = toRefs(props);
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="`#${uniqueId}`"
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
- <span
18
- :id="uniqueId"
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
- </span>
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="getFiltereredMenuOption.length > 0"
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 getFiltereredMenuOption"
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': getFiltereredMenuOption[key].isSelected },
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="getFiltereredMenuOption[key].isSelected"
158
+ v-model="getFilteredMenuOption[key].isSelected"
149
159
  class="spr-w-full"
150
- :checked="getFiltereredMenuOption[key].isSelected"
151
- :label="getFiltereredMenuOption[key].text"
152
- :description="getFiltereredMenuOption[key].subtext"
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
- getFiltereredMenuOption,
277
+ getFilteredMenuOption,
268
278
  filterMenuSearchvalue,
269
279
  mappedFilterMenuList,
270
280
  filterClass,
271
- uniqueId,
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 { options, filterMenu, filterData, loading, filterable, filling, deselected, hasSearchApi } = toRefs(props);
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 getFiltereredMenuOption = computed<FilterPropsInterface['optionDetails'][]>(() => {
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) => prevSelected.value === option.value && prevSelected.isSelected,
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(filterMenuSearchvalue.value.toLowerCase());
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 getFiltereredMenuOption.value.filter((item) => item.isSelected);
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
- emit('getFilterData', column);
78
- selectedColumn.value = column;
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 = Object.values(getFiltereredMenuOption.value).filter(
116
- (selected) => selected.isSelected && selected.column === field,
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
- emit('selectedFilter', selectedFilters.value);
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
- getFiltereredMenuOption.value.map((option) => {
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
- getFiltereredMenuOption,
282
+ getFilteredMenuOption,
253
283
  filterMenuSearchvalue,
254
284
  mappedFilterMenuList,
255
285
  filterClass,
256
- uniqueId,
286
+ generateStableId,
257
287
  filterOptionRef,
258
288
  filterMenuOptionList,
259
289
 
@@ -48,6 +48,10 @@ export const lozengePropTypes = {
48
48
  type: Boolean,
49
49
  default: true,
50
50
  },
51
+ loading: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
51
55
  };
52
56
 
53
57
  export type LozengePropTypes = ExtractPropTypes<typeof lozengePropTypes>;
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div class="spr-h-fit spr-w-fit">
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