design-system-next 2.1.0 → 2.2.0

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.
@@ -22,6 +22,11 @@
22
22
  <table aria-describedby="describe" class="spr-w-full spr-table-fixed" cellspacing="0" cellpadding="0">
23
23
  <thead>
24
24
  <tr v-if="!(props.removeHeaderOnEmpty && sortedData.length <= 0)">
25
+ <th v-if="props.isMultiSelect" :class="[getTableClasses.multiselectClass, getTableClasses.headerClasses]">
26
+ <div class="spr-flex spr-justify-center spr-items-center">
27
+ <spr-checkbox label="" :checked="isAllSelected" @update:model-value="handleSelectAll"/>
28
+ </div>
29
+ </th>
25
30
  <th v-for="(header, keyHeader) in headers" :key="keyHeader" :class="[getTableClasses.headerClasses]">
26
31
  <div :class="getTableClasses.headerNameClass">
27
32
  <span :class="[{ 'spr-cursor-pointer': header.sort }]" @click="header.sort && sortData(header.field)">
@@ -56,61 +61,69 @@
56
61
  @mouseover="$emit('onHover', { active: true, data: item })"
57
62
  @mouseleave="$emit('onHover', { active: false, data: item })"
58
63
  >
64
+ <td v-if="props.isMultiSelect" :class="[getTableClasses.multiselectClass, getTableClasses.multiselectRowClass]">
65
+ <div class="spr-flex spr-justify-center spr-items-center">
66
+ <spr-checkbox label="" :checked="isRowSelected(item)" @update:modelValue="handleSelect(item)"/>
67
+ </div>
68
+ </td>
59
69
  <td v-for="(column, headerKey) in headers" :key="headerKey" :class="getTableClasses.tableDataClasses">
60
- <div v-if="sortedData[keyIndex][column.field]" class="spr-flex spr-flex-row spr-items-center spr-gap-2">
61
- <spr-avatar
62
- v-if="column.hasAvatar"
63
- size="lg"
64
- :src="sortedData[keyIndex][column.field].image"
65
- alt="User Avatar"
66
- :variant="column.avatarVariant ? column.avatarVariant : 'initial'"
67
- :initial="sortedData[keyIndex][column.field].title as string"
68
- />
69
- <div
70
- v-if="column.hasIcon"
71
- class="spr-flex spr-items-center spr-rounded-full spr-bg-mushroom-200 spr-p-1"
72
- >
73
- <Icon :icon="sortedData[keyIndex][column.field].icon || ''" />
74
- </div>
75
- <div>
76
- <!-- Array Title -->
70
+ <slot v-if="slots[column.field]" :name="column.field" :row="item[column.field]" />
71
+ <template v-else>
72
+ <div v-if="sortedData[keyIndex][column.field]" class="spr-flex spr-flex-row spr-items-center spr-gap-2">
73
+ <spr-avatar
74
+ v-if="column.hasAvatar"
75
+ size="lg"
76
+ :src="sortedData[keyIndex][column.field].image"
77
+ alt="User Avatar"
78
+ :variant="column.avatarVariant ? column.avatarVariant : 'initial'"
79
+ :initial="sortedData[keyIndex][column.field].title as string"
80
+ />
77
81
  <div
78
- v-if="Array.isArray(sortedData[keyIndex][column.field].title)"
79
- class="spr-flex spr-flex-wrap spr-gap-2"
82
+ v-if="column.hasIcon"
83
+ class="spr-flex spr-items-center spr-rounded-full spr-bg-mushroom-200 spr-p-1"
80
84
  >
81
- <div v-for="(cell, index) in sortedData[keyIndex][column.field].title" :key="index">
85
+ <Icon :icon="sortedData[keyIndex][column.field].icon || ''" />
86
+ </div>
87
+ <div>
88
+ <!-- Array Title -->
89
+ <div
90
+ v-if="Array.isArray(sortedData[keyIndex][column.field].title)"
91
+ class="spr-flex spr-flex-wrap spr-gap-2"
92
+ >
93
+ <div v-for="(cell, index) in sortedData[keyIndex][column.field].title" :key="index">
94
+ <div v-if="column.hasLozengeTitle" class="spr-mt-1">
95
+ <spr-table-lozenge-title :cell="cell as LozengeTitle" />
96
+ </div>
97
+ <div v-else-if="column.hasChipTitle" class="spr-mt-1">
98
+ <spr-table-chips-title :cell="cell as ChipTitle" />
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ <!-- Single Title Handling -->
104
+ <div v-else>
82
105
  <div v-if="column.hasLozengeTitle" class="spr-mt-1">
83
- <spr-table-lozenge-title :cell="cell as LozengeTitle" />
106
+ <!-- Defining lozenge title in the title so it wont confuse the consumer; hence the title.title-->
107
+ <!-- Also this structure allows multiple instances -->
108
+ <spr-table-lozenge-title :cell="sortedData[keyIndex][column.field].title as LozengeTitle" />
84
109
  </div>
110
+ <!-- Defining the chip title so it wont confuse the consumer; hence the title.title -->
111
+ <!-- Also this structure allows multiple instances -->
85
112
  <div v-else-if="column.hasChipTitle" class="spr-mt-1">
86
- <spr-table-chips-title :cell="cell as ChipTitle" />
113
+ <spr-table-chips-title :cell="sortedData[keyIndex][column.field].title as ChipTitle" />
114
+ </div>
115
+ <div v-else class="spr-text-color-strong spr-font-size-200 spr-font-normal">
116
+ {{ sortedData[keyIndex][column.field].title }}
87
117
  </div>
88
118
  </div>
89
- </div>
90
119
 
91
- <!-- Single Title Handling -->
92
- <div v-else>
93
- <div v-if="column.hasLozengeTitle" class="spr-mt-1">
94
- <!-- Defining lozenge title in the title so it wont confuse the consumer; hence the title.title-->
95
- <!-- Also this structure allows multiple instances -->
96
- <spr-table-lozenge-title :cell="sortedData[keyIndex][column.field].title as LozengeTitle" />
97
- </div>
98
- <!-- Defining the chip title so it wont confuse the consumer; hence the title.title -->
99
- <!-- Also this structure allows multiple instances -->
100
- <div v-else-if="column.hasChipTitle" class="spr-mt-1">
101
- <spr-table-chips-title :cell="sortedData[keyIndex][column.field].title as ChipTitle" />
120
+ <!-- Subtitle -->
121
+ <div v-if="column.hasSubtext" class="spr-text-color-base spr-text-xs spr-font-normal">
122
+ {{ sortedData[keyIndex][column.field].subtext }}
102
123
  </div>
103
- <div v-else class="spr-text-color-strong spr-font-size-200 spr-font-normal">
104
- {{ sortedData[keyIndex][column.field].title }}
105
- </div>
106
- </div>
107
-
108
- <!-- Subtitle -->
109
- <div v-if="column.hasSubtext" class="spr-text-color-base spr-text-xs spr-font-normal">
110
- {{ sortedData[keyIndex][column.field].subtext }}
111
124
  </div>
112
125
  </div>
113
- </div>
126
+ </template>
114
127
  </td>
115
128
  <td v-if="action" :class="getTableClasses.tableRowActionClasses">
116
129
  <div class="spr-flex spr-items-center">
@@ -137,7 +150,7 @@
137
150
  </tbody>
138
151
  </table>
139
152
  </div>
140
- <div v-if="$slots.footer" :class="getTableClasses.tableFooterClasses">
153
+ <div v-if="$slots.footer" :class="getTableClasses.tableFooterClasses">
141
154
  <slot name="footer" />
142
155
  </div>
143
156
  </div>
@@ -152,6 +165,7 @@ import SprBadge from '@/components/badge/badge.vue';
152
165
  import SprTableActions from '@/components/table/table-actions/table-actions.vue';
153
166
  import SprTableLozengeTitle from '@/components/table/table-lozenge-title/table-lozenge-title.vue';
154
167
  import SprTableChipsTitle from '@/components/table/table-chips-title/table-chips-title.vue';
168
+ import SprCheckbox from '@/components/checkbox/checkbox.vue';
155
169
 
156
170
  import { tablePropTypes, tableEmitTypes } from './table';
157
171
  import type { ChipTitle } from '@/components/table/table-chips-title/table-chips-title';
@@ -169,9 +183,13 @@ const {
169
183
  searchField,
170
184
  getTableClasses,
171
185
  getEmptyStateSize,
172
-
186
+ isAllSelected,
187
+
188
+ isRowSelected,
173
189
  sortData,
174
190
  updateSearchField,
175
191
  handleRowClick,
192
+ handleSelect,
193
+ handleSelectAll
176
194
  } = useTable(props, emit, slots);
177
195
  </script>
@@ -1,4 +1,4 @@
1
- import { ref, computed, toRefs, Slots } from 'vue';
1
+ import { ref, computed, toRefs, Slots, watch } from 'vue';
2
2
 
3
3
  import type { TablePropTypes, TableEmitTypes, TABLE_SORT, TableData } from './table';
4
4
  import type { SetupContext } from 'vue';
@@ -6,10 +6,18 @@ import type { SetupContext } from 'vue';
6
6
  import classNames from 'classnames';
7
7
 
8
8
  export const useTable = (props: TablePropTypes, emit: SetupContext<TableEmitTypes>['emit'], slots: Slots) => {
9
- const { dataTable, action, headers, sortOrder, fullHeight } = toRefs(props);
9
+ const { dataTable, action, headers, sortOrder, fullHeight, selectedKeyId, returnCompleteSelectedProperties } =
10
+ toRefs(props);
10
11
  const sortField = ref('');
11
12
  const searchField = ref(props.searchModel);
12
13
  const tableSortOrder = ref<TABLE_SORT>(sortOrder.value || 'asc');
14
+ const selectAll = ref(false);
15
+ const selectedData = ref<TableData[]>([]);
16
+
17
+ const isAllSelected = computed(() => {
18
+ if (selectedData.value.length === 0) return false;
19
+ return selectedData.value.length === sortedData.value.length;
20
+ });
13
21
 
14
22
  const sortedData = computed(() => {
15
23
  if (!sortField.value || sortOrder.value) return dataTable.value;
@@ -121,6 +129,12 @@ export const useTable = (props: TablePropTypes, emit: SetupContext<TableEmitType
121
129
  'spr-h-[360px]': !fullHeight.value && !slots.footer,
122
130
  });
123
131
 
132
+ const multiselectRowClass = classNames(
133
+ 'spr-border-color-weak spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid',
134
+ );
135
+
136
+ const multiselectClass = classNames('spr-px-size-spacing-2xs spr-py-size-spacing-3xs spr-w-[44px] ');
137
+
124
138
  return {
125
139
  headerClasses,
126
140
  tableWrapperClasses,
@@ -128,6 +142,8 @@ export const useTable = (props: TablePropTypes, emit: SetupContext<TableEmitType
128
142
  tableHeaderActionsClasses,
129
143
  headerNameClass,
130
144
  tableCellSlotClasses,
145
+ multiselectRowClass,
146
+ multiselectClass,
131
147
 
132
148
  tableRowClasses,
133
149
  tableDataClasses,
@@ -139,15 +155,60 @@ export const useTable = (props: TablePropTypes, emit: SetupContext<TableEmitType
139
155
  };
140
156
  });
141
157
 
158
+ const handleSelect = (item: TableData) => {
159
+ if (!selectedKeyId.value || !(selectedKeyId.value in item)) return;
160
+
161
+ const selectedIndex = selectedData.value.findIndex(
162
+ (data) => data[selectedKeyId.value].title === item[selectedKeyId.value].title,
163
+ );
164
+
165
+ if (selectedIndex !== -1) {
166
+ selectedData.value.splice(selectedIndex, 1);
167
+ } else {
168
+ selectedData.value.push(item);
169
+ }
170
+ };
171
+
172
+ const handleSelectAll = () => {
173
+ if (isAllSelected.value) {
174
+ selectedData.value = [];
175
+ } else {
176
+ selectedData.value = sortedData.value;
177
+ }
178
+ };
179
+
180
+ const isRowSelected = (item: TableData) => {
181
+ if (!selectedKeyId.value || !(selectedKeyId.value in item)) return false;
182
+ return selectedData.value.some((data) => data[selectedKeyId.value].title === item[selectedKeyId.value].title);
183
+ };
184
+
185
+ watch(
186
+ () => selectedData.value.length,
187
+ () => {
188
+ if (returnCompleteSelectedProperties.value) {
189
+ emit('update:selectedData', selectedData.value);
190
+ } else {
191
+ const mappedData = selectedData.value.map((item) => ({ ...item[selectedKeyId.value] }));
192
+ emit('update:selectedData', mappedData);
193
+ }
194
+ },
195
+ );
196
+
142
197
  return {
143
198
  sortData,
144
199
  updateSearchField,
145
200
  handleRowClick,
201
+ handleSelect,
202
+ handleSelectAll,
146
203
  sortedData,
147
204
  getHeaderCount,
148
205
  hasTableActions,
149
206
  searchField,
150
207
  getTableClasses,
151
208
  getEmptyStateSize,
209
+ selectAll,
210
+ selectedData,
211
+ isAllSelected,
212
+ isRowSelected,
152
213
  };
153
214
  };
@@ -37,6 +37,10 @@ export const tooltipPropTypes = {
37
37
  type: Boolean,
38
38
  default: true,
39
39
  },
40
+ fitContent: {
41
+ type: Boolean,
42
+ default: true
43
+ }
40
44
  };
41
45
 
42
46
  export type TooltipPropTypes = ExtractPropTypes<typeof tooltipPropTypes>;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <template v-if="props.text || slots['popper-content']">
3
3
  <Tooltip
4
- class="spr-w-fit"
4
+ :class="[ props.fitContent ? 'spr-w-fit' : 'spr-w-full' ]"
5
5
  :aria-id="props.hasMaxWidth ? 'tooltip-full-width-wrapper' : 'tooltip-wrapper'"
6
6
  :placement="placement"
7
7
  >