sprintify-ui 0.0.178 → 0.0.180

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.
Files changed (35) hide show
  1. package/dist/sprintify-ui.es.js +8880 -8644
  2. package/dist/style.css +1 -1
  3. package/dist/types/src/components/BaseDataIterator.vue.d.ts +52 -13
  4. package/dist/types/src/components/BaseDataIteratorSectionBox.vue.d.ts +23 -0
  5. package/dist/types/src/components/BaseDataIteratorSectionButton.vue.d.ts +20 -0
  6. package/dist/types/src/components/{BaseDataTableToggleColumns.vue.d.ts → BaseDataIteratorSectionColumns.vue.d.ts} +6 -40
  7. package/dist/types/src/components/BaseDataIteratorSectionModal.vue.d.ts +29 -0
  8. package/dist/types/src/components/BaseDataTable.vue.d.ts +71 -17
  9. package/dist/types/src/components/BaseDataTableRowAction.vue.d.ts +18 -0
  10. package/dist/types/src/components/BaseMenu.vue.d.ts +9 -0
  11. package/dist/types/src/components/BaseMenuItem.vue.d.ts +9 -0
  12. package/dist/types/src/components/BaseTable.vue.d.ts +2 -17
  13. package/dist/types/src/components/BaseTableColumn.vue.d.ts +10 -0
  14. package/dist/types/src/index.d.ts +12 -0
  15. package/dist/types/src/types/index.d.ts +15 -0
  16. package/package.json +9 -9
  17. package/src/assets/main.css +23 -0
  18. package/src/components/BaseActionItem.vue +3 -1
  19. package/src/components/BaseDataIterator.vue +126 -87
  20. package/src/components/BaseDataIteratorSectionBox.vue +33 -0
  21. package/src/components/BaseDataIteratorSectionButton.vue +34 -0
  22. package/src/components/{BaseDataTableToggleColumns.vue → BaseDataIteratorSectionColumns.vue} +3 -3
  23. package/src/components/BaseDataIteratorSectionModal.vue +41 -0
  24. package/src/components/BaseDataTable.stories.js +73 -17
  25. package/src/components/BaseDataTable.vue +239 -79
  26. package/src/components/BaseDataTableRowAction.vue +28 -0
  27. package/src/components/BaseFieldI18n.stories.js +1 -1
  28. package/src/components/BaseMediaLibrary.stories.js +1 -1
  29. package/src/components/BaseMenu.vue +7 -0
  30. package/src/components/BaseMenuItem.vue +17 -1
  31. package/src/components/BaseTable.vue +165 -112
  32. package/src/components/BaseTableColumn.vue +5 -0
  33. package/src/lang/en.json +3 -0
  34. package/src/lang/fr.json +3 -0
  35. package/src/types/index.ts +17 -0
@@ -1,11 +1,15 @@
1
1
  <template>
2
- <div class="relative w-full overflow-hidden">
2
+ <div
3
+ class="relative w-full overflow-hidden"
4
+ :class="maxHeight ? 'base-table--has-max-height' : ''"
5
+ >
3
6
  <div ref="slot" style="display: none">
4
7
  <slot />
5
8
  </div>
6
9
 
7
10
  <div class="flex flex-col">
8
11
  <div
12
+ ref="scrollable"
9
13
  class="overflow-x-auto overflow-y-auto"
10
14
  data-scroll-lock-scrollable
11
15
  :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"
@@ -15,11 +19,17 @@
15
19
  <table class="min-w-full border-separate border-spacing-0">
16
20
  <thead v-if="newColumns.length" ref="thead">
17
21
  <tr>
18
- <th v-if="showDetailRowIcon" class="th" />
22
+ <th
23
+ v-if="showDetailRowIcon"
24
+ class="th"
25
+ :style="detailsStyles(true)"
26
+ />
19
27
  <th
20
28
  v-if="checkable && checkboxPosition === 'left'"
21
- class="th py-0 pl-3"
29
+ class="th group cursor-pointer py-0 pl-3"
22
30
  align="left"
31
+ :style="checkStyles(true)"
32
+ @click="checkAll"
23
33
  >
24
34
  <div class="flex items-center">
25
35
  <input
@@ -28,7 +38,6 @@
28
38
  :checked="isAllChecked"
29
39
  :disabled="isAllUncheckable"
30
40
  :class="checkboxStyle"
31
- @change="checkAll"
32
41
  />
33
42
  </div>
34
43
  </th>
@@ -36,39 +45,63 @@
36
45
  v-for="(column, index) in visibleColumns"
37
46
  :key="column.newKey + ':' + index + 'header'"
38
47
  v-bind="column.thAttrs && column.thAttrs(column)"
39
- :style="column.style"
40
- class="th py-2 pl-3 pr-3 text-left"
48
+ :style="[
49
+ column.style,
50
+ index == 0 ? firstColStyles(true) : {},
51
+ ]"
52
+ class="th group py-2 pl-3 pr-3 text-left"
53
+ :class="{
54
+ 'cursor-pointer': column.sortable,
55
+ }"
41
56
  @click.stop="sort(column, undefined, $event as any)"
42
57
  >
43
58
  <button
44
59
  type="button"
45
60
  class="flex w-full items-center bg-transparent text-left text-sm font-medium leading-tight text-slate-900"
46
61
  :class="{
47
- 'cursor-default': !column.sortable,
48
62
  'text-blue-600':
49
63
  column.sortable && currentSortColumn === column,
50
64
  }"
51
65
  >
52
- <span class="mr-1 whitespace-nowrap">{{
53
- column.label
54
- }}</span>
55
- <span
56
- v-show="column.sortable && currentSortColumn === column"
57
- >
58
- <BaseIcon
59
- icon="mdi:chevron-down"
60
- class="h-5 w-5 duration-300"
61
- :class="{
62
- 'rotate-180': isAsc,
63
- }"
64
- />
66
+ <span class="whitespace-nowrap text-xs text-slate-500">
67
+ {{ column.label }}
65
68
  </span>
69
+ <div
70
+ v-if="column.sortable"
71
+ class="w-3"
72
+ :class="[
73
+ currentSortColumn === column
74
+ ? ''
75
+ : 'opacity-0 duration-200 group-hover:opacity-100',
76
+ ]"
77
+ >
78
+ <svg
79
+ viewBox="0 0 24 24"
80
+ class="absolute top-1/2 h-5 w-5 -translate-y-1/2"
81
+ >
82
+ <g transform="translate(0 -3)">
83
+ <path
84
+ :opacity="!isAsc ? '0.5' : '1'"
85
+ fill="currentColor"
86
+ d="M8.71 12.29L11.3 9.7a.996.996 0 0 1 1.41 0l2.59 2.59c.63.63.18 1.71-.71 1.71H9.41c-.89 0-1.33-1.08-.7-1.71z"
87
+ ></path>
88
+ </g>
89
+ <g transform="translate(0 3)">
90
+ <path
91
+ :opacity="isAsc ? '0.5' : '1'"
92
+ fill="currentColor"
93
+ d="m8.71 11.71l2.59 2.59c.39.39 1.02.39 1.41 0l2.59-2.59c.63-.63.18-1.71-.71-1.71H9.41c-.89 0-1.33 1.08-.7 1.71z"
94
+ ></path>
95
+ </g>
96
+ </svg>
97
+ </div>
66
98
  </button>
67
99
  </th>
68
100
  <th
69
101
  v-if="checkable && checkboxPosition === 'right'"
70
- class="th pr-3"
102
+ class="th group cursor-pointer pr-3"
71
103
  align="right"
104
+ @click="checkAll"
72
105
  >
73
106
  <input
74
107
  autocomplete="off"
@@ -76,101 +109,45 @@
76
109
  :checked="isAllChecked"
77
110
  :disabled="isAllUncheckable"
78
111
  :class="checkboxStyle"
79
- @change="checkAll"
80
112
  />
81
113
  </th>
82
114
  </tr>
83
115
  </thead>
84
116
 
85
117
  <tbody class="bg-white">
86
- <tr v-if="newCheckedRows.length">
87
- <td
88
- :colspan="columnCount"
89
- class="sticky z-[1] p-0"
90
- :style="{
91
- top: theadHeight + 'px',
92
- }"
93
- :class="[borderClasses]"
94
- >
95
- <slot
96
- name="checkedHeader"
97
- :uncheck-all="uncheckAll"
98
- :count="newCheckedRows.length"
99
- :check-rows="newCheckedRows"
100
- >
101
- <div
102
- class="flex items-center justify-between bg-slate-100 py-2 pl-3 pr-2 text-sm"
103
- >
104
- <div>
105
- <span class="mr-3 text-slate-500"
106
- >{{
107
- $t('sui.x_rows_selected', {
108
- count: newCheckedRows.length,
109
- })
110
- }}.</span
111
- >
112
- <button
113
- type="button"
114
- class="mr-3 inline text-slate-700 underline"
115
- @click="uncheckAll()"
116
- >
117
- {{ $t('sui.deselect_all') }}
118
- </button>
119
- </div>
120
- <BaseMenu
121
- v-if="checkableActions?.length"
122
- menu-class="w-52"
123
- :items="checkableActions"
124
- >
125
- <template #button="{ open }">
126
- <div
127
- class="flex h-10 w-10 items-center justify-center rounded-full border border-slate-300 bg-white duration-150 hover:bg-slate-50"
128
- :class="[
129
- open
130
- ? 'ring-2 ring-primary-500 ring-offset-2'
131
- : false,
132
- ]"
133
- >
134
- <BaseIcon icon="heroicons-solid:dots-vertical" />
135
- </div>
136
- </template>
137
- </BaseMenu>
138
- </div>
139
- </slot>
140
- </td>
141
- </tr>
142
-
143
118
  <template
144
119
  v-for="(row, index) in data"
145
120
  :key="getRowIndex(row, index)"
146
121
  >
147
- <tr>
122
+ <tr class="item-row">
148
123
  <td
149
124
  v-if="showDetailRowIcon"
150
- class="pl-3"
125
+ class="group cursor-pointer bg-white pl-3"
151
126
  :class="borderBottomClasses(index, row)"
152
- style="width: 36px"
127
+ :style="detailsStyles(false)"
128
+ @click.stop="toggleDetails(row)"
153
129
  >
154
130
  <button
155
131
  type="button"
156
- class="mr-0 flex h-8 w-8 appearance-none items-center justify-center rounded-full border-0 bg-white text-slate-400 duration-300 hover:bg-slate-100 hover:text-slate-700"
157
- :class="{
158
- 'rotate-180': isVisibleDetailRow(row),
159
- }"
160
- @click.stop="toggleDetails(row)"
132
+ class="mr-0 flex h-5 w-5 appearance-none items-center justify-center rounded-full border border-slate-300 bg-white text-slate-400 shadow duration-100 group-hover:text-slate-600 group-hover:shadow-md"
161
133
  >
162
134
  <BaseIcon
163
135
  v-if="hasDetailedVisible(row)"
164
136
  icon="mdi:chevron-down"
165
- class="h-5 w-5"
137
+ class="h-5 w-5 duration-300"
138
+ :class="{
139
+ 'rotate-180': isVisibleDetailRow(row),
140
+ }"
166
141
  />
167
142
  </button>
168
143
  </td>
169
144
 
170
145
  <td
171
146
  v-if="checkable && checkboxPosition === 'left'"
172
- class="pl-3"
147
+ class="group z-[1] cursor-pointer bg-white pl-3"
148
+ :style="checkStyles(false)"
173
149
  :class="borderBottomClasses(index, row)"
150
+ @click="checkRow(row, index, $event as MouseEvent)"
174
151
  >
175
152
  <div class="flex items-center">
176
153
  <input
@@ -179,7 +156,6 @@
179
156
  :disabled="!isRowCheckable(row)"
180
157
  :checked="isRowChecked(row)"
181
158
  :class="checkboxStyle"
182
- @click="checkRow(row, index, $event as MouseEvent)"
183
159
  />
184
160
  </div>
185
161
  </td>
@@ -192,8 +168,15 @@
192
168
  scoped
193
169
  name="default"
194
170
  tag="td"
195
- class="py-3 pl-3 pr-3 text-sm"
196
- :class="borderBottomClasses(index, row)"
171
+ class="bg-white py-3 pl-3 pr-3 text-sm"
172
+ :style="[
173
+ column.style,
174
+ colindex === 0 ? firstColStyles(false) : {},
175
+ ]"
176
+ :class="[
177
+ borderBottomClasses(index, row),
178
+ column.clickable ? 'cursor-pointer' : '',
179
+ ]"
197
180
  :data-label="column.label"
198
181
  :props="{ row, column, index, colindex, toggleDetails }"
199
182
  @click="
@@ -203,9 +186,10 @@
203
186
 
204
187
  <td
205
188
  v-if="checkable && checkboxPosition === 'right'"
206
- class="pr-3"
189
+ class="group cursor-pointer pr-3"
207
190
  :class="borderBottomClasses(index, row)"
208
191
  align="right"
192
+ @click="checkRow(row, index, $event as MouseEvent)"
209
193
  >
210
194
  <input
211
195
  type="checkbox"
@@ -213,7 +197,6 @@
213
197
  :disabled="!isRowCheckable(row)"
214
198
  :checked="isRowChecked(row)"
215
199
  :class="checkboxStyle"
216
- @click="checkRow(row, index, $event as MouseEvent)"
217
200
  />
218
201
  </td>
219
202
  </tr>
@@ -253,7 +236,7 @@
253
236
  >
254
237
  <div
255
238
  v-if="loading"
256
- class="absolute inset-0 flex h-full w-full items-start justify-center"
239
+ class="absolute inset-0 z-[1] flex h-full w-full items-start justify-center"
257
240
  >
258
241
  <div class="absolute h-full w-full bg-white bg-opacity-60" />
259
242
 
@@ -279,15 +262,16 @@ export default {
279
262
 
280
263
  <script lang="ts" setup>
281
264
  import { PropType, ref } from 'vue';
282
- import { BaseTableColumn, MenuItemInterface, Row } from '@/types';
265
+ import { BaseTableColumn, Row } from '@/types';
283
266
  import SlotComponent from './SlotComponent';
284
- import { useResizeObserver } from '@vueuse/core';
267
+ import { useResizeObserver, useScroll } from '@vueuse/core';
285
268
  import { debounce, isArray } from 'lodash';
286
- import BaseMenu from './BaseMenu.vue';
287
269
  import BaseSpinnerLarge from '../svg/BaseSpinnerLarge.vue';
288
270
 
289
271
  const checkboxStyle =
290
- 'disabled:bg-slate-100 disabled:border-slate-300 disabled:cursor-not-allowed border-slate-400 rounded';
272
+ 'disabled:bg-slate-100 group-hover:shadow-md disabled:border-slate-300 disabled:cursor-not-allowed duration-300 cursor-pointer focus:ring-blue-300 border border-slate-300 shadow h-[18px] w-[18px] rounded';
273
+ const DETAIL_ROW_WIDTH = 36;
274
+ const CHECK_ROW_WIDTH = 36;
291
275
 
292
276
  provide('table', getCurrentInstance());
293
277
 
@@ -316,11 +300,6 @@ const props = defineProps({
316
300
  default: false,
317
301
  type: Boolean,
318
302
  },
319
- /** Define checkable actions */
320
- checkableActions: {
321
- default: undefined,
322
- type: Array as PropType<MenuItemInterface[]>,
323
- },
324
303
  /**
325
304
  * Position of the checkbox (if checkable is true)
326
305
  * @values left, right
@@ -550,13 +529,17 @@ const isAllChecked = computed(() => {
550
529
  return !missingChecked;
551
530
  });
552
531
 
532
+ function getCheckedRowIndex(row: Row) {
533
+ return newCheckedRows.value.findIndex(
534
+ (r) => r[props.rowKey] == row[props.rowKey]
535
+ );
536
+ }
537
+
553
538
  /**
554
539
  * Remove a checked row from the array.
555
540
  */
556
541
  function removeCheckedRow(row: Row) {
557
- const index = newCheckedRows.value.findIndex(
558
- (r) => r[props.rowKey] == row[props.rowKey]
559
- );
542
+ const index = getCheckedRowIndex(row);
560
543
  if (index >= 0) {
561
544
  newCheckedRows.value.splice(index, 1);
562
545
  }
@@ -571,7 +554,7 @@ function checkAll() {
571
554
  newCheckedRows.value = [];
572
555
  } else {
573
556
  props.data.forEach((currentRow) => {
574
- if (props.isRowCheckable(currentRow)) {
557
+ if (props.isRowCheckable(currentRow) && !isRowChecked(currentRow)) {
575
558
  newCheckedRows.value.push(currentRow);
576
559
  }
577
560
  });
@@ -749,7 +732,7 @@ function removeColumn(column: BaseTableColumn) {
749
732
  );
750
733
  }
751
734
 
752
- const borderClasses = 'border-b border-slate-300';
735
+ const borderClasses = 'border-b border-slate-200';
753
736
 
754
737
  function borderBottomClasses(index: number, row: Record<string, any>): string {
755
738
  if (index < props.data.length - 1) {
@@ -800,22 +783,92 @@ function getRowIndex(row: Row, index: number): string {
800
783
  return index + '';
801
784
  }
802
785
 
786
+ // Sticky styles
787
+
788
+ const horizontalScrolling = ref(false);
789
+ const scrollable = ref<null | HTMLElement>(null);
790
+
791
+ const { x } = useScroll(scrollable);
792
+ watch(x, (value) => {
793
+ horizontalScrolling.value = value > 0;
794
+ });
795
+
796
+ function zIndex(th: boolean) {
797
+ if (th) {
798
+ return props.maxHeight ? 3 : 2;
799
+ }
800
+ return 1;
801
+ }
802
+
803
+ function detailsStyles(th: boolean): any {
804
+ if (props.detailed) {
805
+ return {
806
+ zIndex: zIndex(th),
807
+ position: 'sticky',
808
+ left: 0,
809
+ width: DETAIL_ROW_WIDTH + 'px',
810
+ minWidth: DETAIL_ROW_WIDTH + 'px',
811
+ maxWidth: DETAIL_ROW_WIDTH + 'px',
812
+ };
813
+ }
814
+ return {};
815
+ }
816
+
817
+ function checkStyles(th: boolean): any {
818
+ if (props.checkable) {
819
+ return {
820
+ zIndex: zIndex(th),
821
+ position: 'sticky',
822
+ left: props.detailed ? DETAIL_ROW_WIDTH + 'px' : 0,
823
+ width: CHECK_ROW_WIDTH + 'px',
824
+ minWidth: CHECK_ROW_WIDTH + 'px',
825
+ maxWidth: CHECK_ROW_WIDTH + 'px',
826
+ };
827
+ }
828
+ return {};
829
+ }
830
+
831
+ function firstColStyles(th: boolean): any {
832
+ let left = 0;
833
+ if (props.checkable) {
834
+ left += CHECK_ROW_WIDTH;
835
+ }
836
+ if (props.detailed) {
837
+ left += DETAIL_ROW_WIDTH;
838
+ }
839
+ return {
840
+ zIndex: zIndex(th),
841
+ position: 'sticky',
842
+ left: left + 'px',
843
+ borderRight: horizontalScrolling.value ? '1px solid #e2e8f0' : 'none',
844
+ };
845
+ }
846
+
803
847
  provide('addColumn', addColumn);
804
848
  provide('removeColumn', removeColumn);
805
849
  provide('nextSequence', nextSequence);
806
850
 
807
851
  defineExpose({
808
852
  newColumns,
853
+ uncheckAll,
809
854
  });
810
855
  </script>
811
856
 
812
- <style scoped>
857
+ <style lang="postcss" scoped>
813
858
  .th {
859
+ @apply relative;
814
860
  @apply bg-slate-50;
861
+ @apply border-b border-slate-300;
862
+ }
863
+
864
+ .base-table--has-max-height .th {
815
865
  @apply sticky;
816
866
  @apply top-0;
817
- @apply z-[1];
818
- @apply border-b border-slate-300;
867
+ @apply z-[2];
819
868
  @apply bg-opacity-75 backdrop-blur backdrop-filter;
820
869
  }
870
+
871
+ tbody tr.item-row:hover td {
872
+ @apply bg-slate-50;
873
+ }
821
874
  </style>
@@ -24,6 +24,10 @@ export default defineComponent({
24
24
  default: undefined,
25
25
  type: Number,
26
26
  },
27
+ padding: {
28
+ default: undefined,
29
+ type: String,
30
+ },
27
31
  numeric: {
28
32
  default: false,
29
33
  type: Boolean,
@@ -95,6 +99,7 @@ export default defineComponent({
95
99
  style() {
96
100
  return {
97
101
  width: this.width ? this.width + 'px' : undefined,
102
+ padding: this.padding ? this.padding : '0.5rem 0.75rem',
98
103
  };
99
104
  },
100
105
  },
package/src/lang/en.json CHANGED
@@ -4,6 +4,7 @@
4
4
  "address_1_placeholder": "Postal address",
5
5
  "address_2_description": "Apartment, suite, unit, building",
6
6
  "and": "and",
7
+ "apply": "Apply",
7
8
  "apply_filters": "Apply filters",
8
9
  "autocomplete_placeholder": "Type to start your search",
9
10
  "cancel": "Cancel",
@@ -17,10 +18,12 @@
17
18
  "country": "Country",
18
19
  "create_new": "Create new",
19
20
  "day": "Day",
21
+ "delete": "Delete",
20
22
  "delete_record": "Delete the record",
21
23
  "delete_record_description": "Are you sure to delete this record? This action is irreversible.",
22
24
  "deselect_all": "Deselect all",
23
25
  "drop_or_click_to_upload": "Drop or click to upload",
26
+ "edit": "Edit",
24
27
  "error": "Error",
25
28
  "file_must_be_of_type": "The file must be of type",
26
29
  "filters": "Filters",
package/src/lang/fr.json CHANGED
@@ -4,6 +4,7 @@
4
4
  "address_1_placeholder": "Adresse postale",
5
5
  "address_2_description": "Appartement, suite, unité, immeuble",
6
6
  "and": "et",
7
+ "apply": "Appliquer",
7
8
  "apply_filters": "Appliquer les filtres",
8
9
  "autocomplete_placeholder": "Tapez pour lancer votre recherche",
9
10
  "cancel": "Annuler",
@@ -17,10 +18,12 @@
17
18
  "country": "Pays",
18
19
  "create_new": "Créer un nouveau",
19
20
  "day": "Jour",
21
+ "delete": "Supprimer",
20
22
  "delete_record": "Supprimer l'item",
21
23
  "delete_record_description": "Voulez-vous vraiment supprimer cet item ? \nCette action est irréversible.",
22
24
  "deselect_all": "Tout déselectionner",
23
25
  "drop_or_click_to_upload": "Déposez ou cliquez pour télécharger",
26
+ "edit": "Modifier",
24
27
  "error": "Erreur",
25
28
  "file_must_be_of_type": "Le fichier doit être de type",
26
29
  "filters": "Filtres",
@@ -84,6 +84,7 @@ export interface ActionItem {
84
84
  action?: () => Promise<void> | void;
85
85
  icon?: string;
86
86
  count?: number;
87
+ disabled?: boolean;
87
88
  meta?: Record<string, any>;
88
89
  actions?: ActionItem[];
89
90
  }
@@ -209,3 +210,19 @@ export interface SelectConfiguration {
209
210
  options: SelectConfigurationOption[];
210
211
  onChange?: (option: SelectConfigurationOption | null) => void;
211
212
  }
213
+
214
+ export interface DataIteratorSection {
215
+ name: string;
216
+ title: string;
217
+ closeText: string;
218
+ icon: string;
219
+ opened?: boolean;
220
+ }
221
+
222
+ export interface RowAction {
223
+ label: string;
224
+ icon: string;
225
+ action?: (row: CollectionItem) => Promise<void> | void;
226
+ to?: (row: CollectionItem) => RouteLocationRaw;
227
+ disabled?: (row: CollectionItem) => boolean;
228
+ }