@weni/unnnic-system 3.0.4 → 3.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weni/unnnic-system",
3
- "version": "3.0.4",
3
+ "version": "3.1.0",
4
4
  "type": "commonjs",
5
5
  "files": [
6
6
  "dist",
@@ -0,0 +1,466 @@
1
+ <template>
2
+ <table
3
+ class="unnnic-data-table"
4
+ :style="props.fixedHeaders ? {} : heightStyles"
5
+ >
6
+ <thead
7
+ v-if="!shouldHideHeaders"
8
+ class="unnnic-data-table__header"
9
+ >
10
+ <tr class="unnnic-data-table__header-row">
11
+ <th
12
+ v-for="header in headers"
13
+ :key="header.itemKey"
14
+ :class="[
15
+ 'unnnic-data-table__header-cell',
16
+ {
17
+ 'unnnic-data-table__header-cell--clickable': header.isSortable,
18
+ 'unnnic-data-table__header-cell--sorting':
19
+ sort.header === header.title && sort.order !== '',
20
+ },
21
+ ]"
22
+ @click.stop="handleClickHeader(header)"
23
+ >
24
+ <slot
25
+ v-if="slots[`header-${header.itemKey}`]"
26
+ :name="`header-${header.itemKey}`"
27
+ :header="header"
28
+ />
29
+ <template v-else>
30
+ {{ header.title }}
31
+ </template>
32
+ <template v-if="header.isSortable">
33
+ <IconArrowsDefault
34
+ v-if="sort.header !== header.title"
35
+ class="order-default-icon"
36
+ data-testid="arrow-default-icon"
37
+ />
38
+ <Icon
39
+ v-else-if="sort.order === 'asc'"
40
+ clickable
41
+ size="ant"
42
+ :icon="'switch_left'"
43
+ style="transform: rotate(-90deg)"
44
+ data-testid="arrow-asc-icon"
45
+ />
46
+ <Icon
47
+ v-else-if="sort.order === 'desc'"
48
+ clickable
49
+ size="ant"
50
+ :icon="'switch_left'"
51
+ style="transform: rotate(90deg)"
52
+ data-testid="arrow-desc-icon"
53
+ />
54
+ </template>
55
+ </th>
56
+ </tr>
57
+ </thead>
58
+ <tbody
59
+ :class="[
60
+ 'unnnic-data-table__body',
61
+ { 'unnnic-data-table__body--hide-headers': props.hideHeaders },
62
+ ]"
63
+ :style="props.fixedHeaders ? heightStyles : {}"
64
+ >
65
+ <tr
66
+ v-if="isLoading"
67
+ :class="[
68
+ 'unnnic-data-table__body-row',
69
+ 'unnnic-data-table__body-row--loading',
70
+ ]"
71
+ >
72
+ <img
73
+ class="unnnic-data-table__body-cell--loading"
74
+ data-testid="body-row-loading"
75
+ src="../../assets/icons/weni-loading.svg"
76
+ height="40"
77
+ />
78
+ </tr>
79
+ <template v-else-if="props.items.length">
80
+ <tr
81
+ v-for="(item, index) in props.items"
82
+ :key="index"
83
+ :class="[
84
+ 'unnnic-data-table__body-row',
85
+ { 'unnnic-data-table__body-row--clickable': props.clickable },
86
+ ]"
87
+ @click="handleClickRow(item)"
88
+ >
89
+ <template
90
+ v-for="key in headersItemsKeys"
91
+ :key="key"
92
+ >
93
+ <td
94
+ v-if="slots[`body-${key}`]"
95
+ :class="[
96
+ 'unnnic-data-table__body-cell',
97
+ `unnnic-data-table__body-cell--${size}`,
98
+ ]"
99
+ >
100
+ <slot
101
+ :name="`body-${key}`"
102
+ :item="item"
103
+ />
104
+ </td>
105
+ <td
106
+ v-else
107
+ :class="[
108
+ 'unnnic-data-table__body-cell',
109
+ `unnnic-data-table__body-cell--${size}`,
110
+ ]"
111
+ >
112
+ {{ item[key] }}
113
+ </td>
114
+ </template>
115
+ </tr>
116
+ </template>
117
+ <tr
118
+ v-else
119
+ class="unnnic-data-table__body-row"
120
+ >
121
+ <td
122
+ v-if="slots['without-results']"
123
+ :class="[
124
+ 'unnnic-data-table__body-cell',
125
+ `unnnic-data-table__body-cell--${size}`,
126
+ ]"
127
+ >
128
+ <slot name="without-results" />
129
+ </td>
130
+ <td
131
+ v-else
132
+ :class="[
133
+ 'unnnic-data-table__body-cell',
134
+ `unnnic-data-table__body-cell--${size}`,
135
+ ]"
136
+ data-testid="body-cell"
137
+ >
138
+ <p
139
+ class="unnnic-data-table__body-cell-text"
140
+ data-testid="body-cell-text"
141
+ >
142
+ {{ defaultTranslations.without_results[props.locale || 'en'] }}
143
+ </p>
144
+ </td>
145
+ </tr>
146
+ </tbody>
147
+ <TablePagination
148
+ v-if="!props.hidePagination"
149
+ :modelValue="props.page"
150
+ :total="props.pageTotal"
151
+ :interval="props.pageInterval"
152
+ @update:model-value="$emit('update:page', $event)"
153
+ />
154
+ </table>
155
+ </template>
156
+
157
+ <script setup lang="ts">
158
+ import { computed, ComputedRef, ref, useSlots } from 'vue';
159
+
160
+ import Icon from '../Icon.vue';
161
+ import IconArrowsDefault from '../icons/iconArrowsDefault.vue';
162
+ import TablePagination from '../TableNext/TablePagination.vue';
163
+
164
+ type DataTableHeader = {
165
+ title: string;
166
+ isSortable?: boolean;
167
+ itemKey: string;
168
+ align?: 'start' | 'center' | 'end';
169
+ size?: number | string;
170
+ };
171
+
172
+ type DataTableItem = {
173
+ [key: string]: any;
174
+ };
175
+
176
+ interface Props {
177
+ headers: DataTableHeader[];
178
+ items: DataTableItem[];
179
+ isLoading?: boolean;
180
+ size?: 'sm' | 'md';
181
+ height?: string;
182
+ maxHeight?: string;
183
+ clickable?: boolean;
184
+ fixedHeaders?: boolean;
185
+ hideHeaders?: boolean;
186
+ hidePagination?: boolean;
187
+ page?: number;
188
+ pageTotal?: number;
189
+ pageInterval?: number;
190
+ locale?: string;
191
+ }
192
+
193
+ defineOptions({
194
+ name: 'UnnnicDataTable',
195
+ });
196
+
197
+ const slots = useSlots();
198
+
199
+ const emit = defineEmits<{
200
+ 'update:sort': [sort: { header: string; order: string }];
201
+ itemClick: [item: DataTableItem];
202
+ 'update:page': [page: number];
203
+ }>();
204
+
205
+
206
+
207
+ const props = withDefaults(defineProps<Props>(), {
208
+ isLoading: false,
209
+ size: 'md',
210
+ height: '',
211
+ maxHeight: '',
212
+ clickable: false,
213
+ fixedHeaders: false,
214
+ hideHeaders: false,
215
+ hidePagination: false,
216
+ page: 1,
217
+ pageTotal: 0,
218
+ pageInterval: 5,
219
+ locale: 'en',
220
+ });
221
+
222
+ const defaultTranslations = {
223
+ without_results: {
224
+ 'pt-br': 'Nenhum resultado correspondente',
225
+ en: 'No matching results',
226
+ es: 'No hay resultados coincidentes',
227
+ },
228
+ };
229
+
230
+ const heightStyles = computed(() => {
231
+ return {
232
+ height: props.height || 'unset',
233
+ 'max-height': props.maxHeight || 'unset',
234
+ overflow: props.height || props.maxHeight ? 'auto' : 'unset',
235
+ };
236
+ });
237
+
238
+ const shouldHideHeaders = computed(() => {
239
+ return props.hideHeaders || !props.headers.length;
240
+ });
241
+
242
+ const headersItemsKeys: ComputedRef<string[]> = computed(() => {
243
+ return props.headers.map((header) => header.itemKey);
244
+ });
245
+
246
+ const sort = ref({
247
+ header: '',
248
+ order: '',
249
+ });
250
+
251
+ const getHeaderColumnSize = (header: DataTableHeader): string => {
252
+ return typeof header.size === 'number'
253
+ ? `${header.size || 1}fr`
254
+ : header.size || '1fr';
255
+ };
256
+
257
+ const gridTemplateColumns: ComputedRef<string> = computed(() => {
258
+ const columnSizes = props.headers.length
259
+ ? props.headers.map(getHeaderColumnSize)
260
+ : props.items[0].content.map(() => '1fr');
261
+
262
+ return columnSizes.join(' ');
263
+ });
264
+
265
+ const handleSort = (key: string, order: string) => {
266
+ sort.value = { header: key, order };
267
+ emit('update:sort', sort.value);
268
+ };
269
+
270
+ const handleClickHeader = (header: DataTableHeader) => {
271
+ if (!header.isSortable) return;
272
+
273
+ const nextSortOrderMapper = {
274
+ asc: 'desc',
275
+ desc: 'asc',
276
+ '': 'asc',
277
+ };
278
+
279
+ const nextSort =
280
+ header.title !== sort.value.header
281
+ ? 'asc'
282
+ : nextSortOrderMapper[sort.value.order];
283
+
284
+ handleSort(nextSort === '' ? '' : header.title, nextSort);
285
+ };
286
+
287
+ const handleClickRow = (item: DataTableItem) => {
288
+ if (!props.clickable) return;
289
+
290
+ emit('itemClick', item);
291
+ };
292
+ </script>
293
+
294
+ <style scoped lang="scss">
295
+ @use '@/assets/scss/unnnic' as *;
296
+
297
+ $tableBorder: $unnnic-border-width-thinner solid $unnnic-color-neutral-soft;
298
+
299
+ .unnnic-data-table {
300
+ border-spacing: 0;
301
+
302
+ overflow: hidden;
303
+
304
+ width: 100%;
305
+
306
+ display: flex;
307
+ flex-direction: column;
308
+
309
+ &::-webkit-scrollbar {
310
+ width: $unnnic-spacing-inline-nano;
311
+ }
312
+
313
+ &::-webkit-scrollbar-thumb {
314
+ background: $unnnic-color-neutral-cleanest;
315
+ border-radius: $unnnic-border-radius-pill;
316
+ }
317
+
318
+ &::-webkit-scrollbar-track {
319
+ background: $unnnic-color-neutral-soft;
320
+ border-radius: $unnnic-border-radius-pill;
321
+ }
322
+
323
+ &__header {
324
+ &-row {
325
+ @extend %base-row;
326
+
327
+ grid-template-columns: v-bind(gridTemplateColumns);
328
+ }
329
+
330
+ &-cell {
331
+ @extend %base-cell;
332
+
333
+ height: 100%;
334
+
335
+ box-sizing: border-box;
336
+ border: $tableBorder;
337
+ background-color: $unnnic-color-neutral-light;
338
+
339
+ font-weight: $unnnic-font-weight-bold;
340
+
341
+ display: flex;
342
+
343
+ &:first-of-type {
344
+ border-radius: $unnnic-border-radius-sm 0 0 0;
345
+ }
346
+
347
+ &:not(:first-of-type) {
348
+ border-left: none;
349
+ }
350
+
351
+ &:last-of-type {
352
+ border-radius: 0 $unnnic-border-radius-sm 0 0;
353
+ }
354
+
355
+ &--sorting {
356
+ background-color: $unnnic-color-neutral-soft;
357
+ }
358
+
359
+ &--clickable {
360
+ &:hover {
361
+ cursor: pointer;
362
+ background-color: $unnnic-color-neutral-soft;
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ &__body {
369
+ &::-webkit-scrollbar {
370
+ width: $unnnic-spacing-inline-nano;
371
+ }
372
+
373
+ &::-webkit-scrollbar-thumb {
374
+ background: $unnnic-color-neutral-cleanest;
375
+ border-radius: $unnnic-border-radius-pill;
376
+ }
377
+
378
+ &::-webkit-scrollbar-track {
379
+ background: $unnnic-color-neutral-soft;
380
+ border-radius: $unnnic-border-radius-pill;
381
+ }
382
+
383
+ &--hide-headers {
384
+ .unnnic-data-table__body-row:first-of-type {
385
+ border-radius: $unnnic-border-radius-sm $unnnic-border-radius-sm 0 0;
386
+ border-top: $tableBorder;
387
+ }
388
+ }
389
+
390
+ &-row {
391
+ @extend %base-row;
392
+
393
+ overflow: hidden;
394
+
395
+ border: $tableBorder;
396
+ border-collapse: collapse;
397
+ border-top: none;
398
+
399
+ grid-template-columns: v-bind(gridTemplateColumns);
400
+
401
+ &--loading {
402
+ grid-template-columns: 1fr;
403
+ }
404
+
405
+ &--clickable {
406
+ text-decoration: none;
407
+
408
+ &:hover {
409
+ cursor: pointer;
410
+ background-color: $unnnic-color-neutral-lightest;
411
+ }
412
+ }
413
+
414
+ &:last-of-type {
415
+ border-radius: 0 0 $unnnic-border-radius-sm $unnnic-border-radius-sm;
416
+ }
417
+ }
418
+
419
+ &-cell {
420
+ @extend %base-cell;
421
+
422
+ &-text {
423
+ margin: 0;
424
+
425
+ overflow: hidden;
426
+ white-space: nowrap;
427
+ text-overflow: ellipsis;
428
+ }
429
+ }
430
+
431
+ td.unnnic-data-table__body-cell--sm {
432
+ padding: $unnnic-spacing-ant $unnnic-spacing-sm;
433
+ }
434
+
435
+ &-cell--loading {
436
+ margin: $unnnic-spacing-xl 0;
437
+ padding: 0;
438
+
439
+ width: 100%;
440
+
441
+ pointer-events: none;
442
+ }
443
+ }
444
+
445
+ %base-cell {
446
+ border-collapse: collapse;
447
+
448
+ padding: $unnnic-spacing-sm $unnnic-spacing-sm;
449
+
450
+ font-family: $unnnic-font-family-secondary;
451
+ font-size: $unnnic-font-size-body-gt;
452
+ line-height: $unnnic-line-height-small * 5.5;
453
+ text-align: left;
454
+ color: $unnnic-color-neutral-dark;
455
+
456
+ overflow: hidden;
457
+ white-space: nowrap;
458
+ text-overflow: ellipsis;
459
+ }
460
+
461
+ %base-row {
462
+ display: grid;
463
+ align-items: center;
464
+ }
465
+ }
466
+ </style>
@@ -88,6 +88,7 @@ import ModalDialog from "./ModalDialog/ModalDialog.vue";
88
88
  import Tour from "./Tour/Tour.vue";
89
89
  import Navigator from "./Navigator/index.vue";
90
90
  import SelectTime from "./SelectTime/index.vue";
91
+ import DataTable from "./DataTable/index.vue";
91
92
 
92
93
  type VueComponent = Component;
93
94
 
@@ -186,6 +187,7 @@ export const components: ComponentsMap = {
186
187
  unnnicTour: Tour,
187
188
  unnnicNavigator: Navigator,
188
189
  unnnicSelectTime: SelectTime,
190
+ unnnicDataTable: DataTable,
189
191
  };
190
192
 
191
193
  export const unnnicFontSize = fontSize;
@@ -278,4 +280,5 @@ export const unnnicDrawer = Drawer;
278
280
  export const unnnicTableNext = TableNext;
279
281
  export const unnnicTour = Tour;
280
282
  export const unnnicNavigator = Navigator;
283
+ export const unnnicDataTable = DataTable as VueComponent;
281
284
  export const unnnicSelectTime = SelectTime as VueComponent;