@weni/unnnic-system 3.0.0 → 3.0.1-alpha.1

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.
@@ -0,0 +1,493 @@
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 lang="ts">
158
+ export default {
159
+ name: 'UnnnicDataTable',
160
+ };
161
+ </script>
162
+
163
+ <script setup lang="ts">
164
+ import { computed, ref, useSlots } from 'vue';
165
+
166
+ import Icon from '../Icon.vue';
167
+ import IconArrowsDefault from '../icons/iconArrowsDefault.vue';
168
+ import TablePagination from '../TableNext/TablePagination.vue';
169
+
170
+ type DataTableHeader = {
171
+ title: string;
172
+ isSortable?: boolean;
173
+ itemKey: string;
174
+ align?: 'start' | 'center' | 'end';
175
+ size?: number | string;
176
+ };
177
+
178
+ type DataTableItem = {
179
+ [key: string]: any;
180
+ };
181
+
182
+ const slots = useSlots();
183
+
184
+ const emit = defineEmits<{
185
+ 'update:sort': [sort: { header: string; order: string }];
186
+ itemClick: [item: DataTableItem];
187
+ 'update:page': [page: number];
188
+ }>();
189
+
190
+ const props = defineProps<{
191
+ headers: {
192
+ type: DataTableHeader[];
193
+ required: true;
194
+ };
195
+ items: {
196
+ type: DataTableItem[];
197
+ required: true;
198
+ };
199
+ isLoading: {
200
+ type: Boolean;
201
+ default: false;
202
+ };
203
+ size: {
204
+ type: 'sm' | 'md';
205
+ default: 'md';
206
+ };
207
+ height: {
208
+ type: String;
209
+ default: '';
210
+ };
211
+ maxHeight: {
212
+ type: String;
213
+ default: '';
214
+ };
215
+ clickable: {
216
+ type: Boolean;
217
+ default: false;
218
+ };
219
+ fixedHeaders: {
220
+ type: Boolean;
221
+ default: false;
222
+ };
223
+ hideHeaders: {
224
+ type: Boolean;
225
+ default: false;
226
+ };
227
+ hidePagination: {
228
+ type: Boolean;
229
+ default: false;
230
+ };
231
+ page: {
232
+ type: Number;
233
+ default: 1;
234
+ };
235
+ pageTotal: {
236
+ type: Number;
237
+ default: 0;
238
+ };
239
+ pageInterval: {
240
+ type: Number;
241
+ default: 5;
242
+ };
243
+ locale: {
244
+ type: string;
245
+ default: 'en';
246
+ };
247
+ }>();
248
+
249
+ const defaultTranslations = {
250
+ without_results: {
251
+ 'pt-br': 'Nenhum resultado correspondente',
252
+ en: 'No matching results',
253
+ es: 'No hay resultados coincidentes',
254
+ },
255
+ };
256
+
257
+ const heightStyles = computed(() => {
258
+ return {
259
+ height: props.height || 'unset',
260
+ 'max-height': props.maxHeight || 'unset',
261
+ overflow: props.height || props.maxHeight ? 'auto' : 'unset',
262
+ };
263
+ });
264
+
265
+ const shouldHideHeaders = computed(() => {
266
+ return props.hideHeaders || !props.headers.length;
267
+ });
268
+
269
+ const headersItemsKeys: string[] = computed(() => {
270
+ return props.headers.map((header) => header.itemKey);
271
+ });
272
+
273
+ const sort = ref({
274
+ header: '',
275
+ order: '',
276
+ });
277
+
278
+ const getHeaderColumnSize = (header: DataTableHeader): string => {
279
+ return typeof header.size === 'number'
280
+ ? `${header.size || 1}fr`
281
+ : header.size || '1fr';
282
+ };
283
+
284
+ const gridTemplateColumns: string = computed(() => {
285
+ const columnSizes = props.headers.length
286
+ ? props.headers.map(getHeaderColumnSize)
287
+ : props.items[0].content.map(() => '1fr');
288
+
289
+ return columnSizes.join(' ');
290
+ });
291
+
292
+ const handleSort = (key: string, order: string) => {
293
+ sort.value = { header: key, order };
294
+ emit('update:sort', sort.value);
295
+ };
296
+
297
+ const handleClickHeader = (header: DataTableHeader) => {
298
+ if (!header.isSortable) return;
299
+
300
+ const nextSortOrderMapper = {
301
+ asc: 'desc',
302
+ desc: 'asc',
303
+ '': 'asc',
304
+ };
305
+
306
+ const nextSort =
307
+ header.title !== sort.value.header
308
+ ? 'asc'
309
+ : nextSortOrderMapper[sort.value.order];
310
+
311
+ handleSort(nextSort === '' ? '' : header.title, nextSort);
312
+ };
313
+
314
+ const handleClickRow = (item: DataTableItem) => {
315
+ if (!props.clickable) return;
316
+
317
+ emit('itemClick', item);
318
+ };
319
+ </script>
320
+
321
+ <style scoped lang="scss">
322
+ @use '@/assets/scss/unnnic' as *;
323
+
324
+ $tableBorder: $unnnic-border-width-thinner solid $unnnic-color-neutral-soft;
325
+
326
+ .unnnic-data-table {
327
+ border-spacing: 0;
328
+
329
+ overflow: hidden;
330
+
331
+ width: 100%;
332
+
333
+ display: flex;
334
+ flex-direction: column;
335
+
336
+ &::-webkit-scrollbar {
337
+ width: $unnnic-spacing-inline-nano;
338
+ }
339
+
340
+ &::-webkit-scrollbar-thumb {
341
+ background: $unnnic-color-neutral-cleanest;
342
+ border-radius: $unnnic-border-radius-pill;
343
+ }
344
+
345
+ &::-webkit-scrollbar-track {
346
+ background: $unnnic-color-neutral-soft;
347
+ border-radius: $unnnic-border-radius-pill;
348
+ }
349
+
350
+ &__header {
351
+ &-row {
352
+ @extend %base-row;
353
+
354
+ grid-template-columns: v-bind(gridTemplateColumns);
355
+ }
356
+
357
+ &-cell {
358
+ @extend %base-cell;
359
+
360
+ height: 100%;
361
+
362
+ box-sizing: border-box;
363
+ border: $tableBorder;
364
+ background-color: $unnnic-color-neutral-light;
365
+
366
+ font-weight: $unnnic-font-weight-bold;
367
+
368
+ display: flex;
369
+
370
+ &:first-of-type {
371
+ border-radius: $unnnic-border-radius-sm 0 0 0;
372
+ }
373
+
374
+ &:not(:first-of-type) {
375
+ border-left: none;
376
+ }
377
+
378
+ &:last-of-type {
379
+ border-radius: 0 $unnnic-border-radius-sm 0 0;
380
+ }
381
+
382
+ &--sorting {
383
+ background-color: $unnnic-color-neutral-soft;
384
+ }
385
+
386
+ &--clickable {
387
+ &:hover {
388
+ cursor: pointer;
389
+ background-color: $unnnic-color-neutral-soft;
390
+ }
391
+ }
392
+ }
393
+ }
394
+
395
+ &__body {
396
+ &::-webkit-scrollbar {
397
+ width: $unnnic-spacing-inline-nano;
398
+ }
399
+
400
+ &::-webkit-scrollbar-thumb {
401
+ background: $unnnic-color-neutral-cleanest;
402
+ border-radius: $unnnic-border-radius-pill;
403
+ }
404
+
405
+ &::-webkit-scrollbar-track {
406
+ background: $unnnic-color-neutral-soft;
407
+ border-radius: $unnnic-border-radius-pill;
408
+ }
409
+
410
+ &--hide-headers {
411
+ .unnnic-data-table__body-row:first-of-type {
412
+ border-radius: $unnnic-border-radius-sm $unnnic-border-radius-sm 0 0;
413
+ border-top: $tableBorder;
414
+ }
415
+ }
416
+
417
+ &-row {
418
+ @extend %base-row;
419
+
420
+ overflow: hidden;
421
+
422
+ border: $tableBorder;
423
+ border-collapse: collapse;
424
+ border-top: none;
425
+
426
+ grid-template-columns: v-bind(gridTemplateColumns);
427
+
428
+ &--loading {
429
+ grid-template-columns: 1fr;
430
+ }
431
+
432
+ &--clickable {
433
+ text-decoration: none;
434
+
435
+ &:hover {
436
+ cursor: pointer;
437
+ background-color: $unnnic-color-neutral-lightest;
438
+ }
439
+ }
440
+
441
+ &:last-of-type {
442
+ border-radius: 0 0 $unnnic-border-radius-sm $unnnic-border-radius-sm;
443
+ }
444
+ }
445
+
446
+ &-cell {
447
+ @extend %base-cell;
448
+
449
+ &-text {
450
+ margin: 0;
451
+
452
+ overflow: hidden;
453
+ white-space: nowrap;
454
+ text-overflow: ellipsis;
455
+ }
456
+ }
457
+
458
+ td.unnnic-data-table__body-cell--sm {
459
+ padding: $unnnic-spacing-ant $unnnic-spacing-sm;
460
+ }
461
+
462
+ &-cell--loading {
463
+ margin: $unnnic-spacing-xl 0;
464
+ padding: 0;
465
+
466
+ width: 100%;
467
+
468
+ pointer-events: none;
469
+ }
470
+ }
471
+
472
+ %base-cell {
473
+ border-collapse: collapse;
474
+
475
+ padding: $unnnic-spacing-sm $unnnic-spacing-sm;
476
+
477
+ font-family: $unnnic-font-family-secondary;
478
+ font-size: $unnnic-font-size-body-gt;
479
+ line-height: $unnnic-line-height-small * 5.5;
480
+ text-align: left;
481
+ color: $unnnic-color-neutral-dark;
482
+
483
+ overflow: hidden;
484
+ white-space: nowrap;
485
+ text-overflow: ellipsis;
486
+ }
487
+
488
+ %base-row {
489
+ display: grid;
490
+ align-items: center;
491
+ }
492
+ }
493
+ </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;
281
- export const unnnicSelectTime = SelectTime as VueComponent;
283
+ export const unnnicDataTable = DataTable;
284
+ export const unnnicSelectTime = SelectTime as VueComponent;