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
@@ -8,7 +8,7 @@
8
8
  >
9
9
  <div
10
10
  class="min-w-0"
11
- :class="{ 'col-span-1': !mobileLayout, 'col-span-2': mobileLayout }"
11
+ :class="{ 'col-span-1': !compactLayout, 'col-span-2': compactLayout }"
12
12
  >
13
13
  <!-- Header -->
14
14
  <div class="mb-4 flex space-x-2 empty:mb-0">
@@ -52,19 +52,18 @@
52
52
  </div>
53
53
  </div>
54
54
 
55
- <!-- Filters (mobile) -->
56
- <button
57
- v-if="mobileLayout && hasFilters"
58
- class="btn flex h-11 items-center justify-center py-1 text-base shadow-sm"
59
- type="button"
60
- @click="showFilters = true"
61
- >
62
- <BaseIcon
63
- class="h-6 w-6 text-slate-500 xs:mr-2"
64
- icon="heroicons:adjustments-horizontal-solid"
65
- />
66
- <span class="hidden xs:block">{{ $t('sui.filters') }}</span>
67
- </button>
55
+ <template v-if="compactLayout">
56
+ <template
57
+ v-for="(section, i) in sectionsInternal"
58
+ :key="section.name"
59
+ >
60
+ <BaseDataIteratorSectionButton
61
+ :section="section"
62
+ @open="openSection(i)"
63
+ >
64
+ </BaseDataIteratorSectionButton>
65
+ </template>
66
+ </template>
68
67
 
69
68
  <!-- Menu -->
70
69
  <BaseMenu
@@ -99,8 +98,8 @@
99
98
 
100
99
  <!-- Pagination -->
101
100
 
102
- <div v-if="paginationMetadata" class="mt-5">
103
- <p class="text-center text-xs text-slate-400 sm:text-right">
101
+ <div v-if="paginationMetadata" class="mt-4">
102
+ <p class="text-center text-sm text-slate-400 sm:text-right">
104
103
  {{
105
104
  (paginationMetadata.current_page - 1) *
106
105
  paginationMetadata.per_page +
@@ -128,74 +127,46 @@
128
127
  </div>
129
128
  </div>
130
129
 
131
- <div v-if="!mobileLayout" ref="sidebar">
132
- <slot
133
- name="sidebarTop"
134
- :pagination-metadata="paginationMetadata"
135
- ></slot>
136
-
137
- <div v-if="hasFilters" class="mb-4">
138
- <BaseCard>
139
- <BaseCardRow size="sm">
140
- <button
141
- type="button"
142
- class="flex w-full items-center justify-between"
143
- @click="showFilterDesktop = !showFilterDesktop"
144
- >
145
- <h2 class="font-semibold">
146
- {{ $t('sui.filters') }}
147
- </h2>
148
-
149
- <BaseIcon
150
- :icon="
151
- showFilterDesktop
152
- ? 'heroicons:chevron-down'
153
- : 'heroicons:chevron-up'
154
- "
155
- ></BaseIcon>
156
- </button>
157
-
158
- <div v-show="showFilterDesktop" class="mt-4 space-y-3">
159
- <slot
160
- name="filters"
161
- :query="query"
162
- :update-query="updateFilterQuery"
163
- :update-query-value="updateFilterQueryValue"
164
- />
165
- </div>
166
- </BaseCardRow>
167
- </BaseCard>
168
- </div>
169
-
170
- <slot
171
- name="sidebarBottom"
172
- :pagination-metadata="paginationMetadata"
173
- ></slot>
174
- </div>
175
- </div>
176
-
177
- <BaseModalSide v-if="hasFilters" v-model="showFilters">
178
- <div class="px-4 py-5">
179
- <h2 class="mb-4 font-semibold">
180
- {{ $t('sui.filters') }}
181
- </h2>
182
-
130
+ <div v-if="!compactLayout" ref="sidebar">
183
131
  <div class="space-y-3">
184
132
  <slot
185
- name="filters"
186
- :query="query"
187
- :update-query="updateFilterQuery"
188
- :update-query-value="updateFilterQueryValue"
189
- />
190
- </div>
133
+ name="sidebarTop"
134
+ :pagination-metadata="paginationMetadata"
135
+ ></slot>
136
+
137
+ <template v-for="section in sectionsInternal" :key="section.name">
138
+ <BaseDataIteratorSectionBox :section="section">
139
+ <slot
140
+ :name="section.name"
141
+ :query="query"
142
+ :update-query="updateFilterQuery"
143
+ :update-query-value="updateFilterQueryValue"
144
+ />
145
+ </BaseDataIteratorSectionBox>
146
+ </template>
191
147
 
192
- <div>
193
- <button class="btn btn-primary mt-4" @click="showFilters = false">
194
- {{ $t('sui.apply_filters') }}
195
- </button>
148
+ <slot
149
+ name="sidebarBottom"
150
+ :pagination-metadata="paginationMetadata"
151
+ ></slot>
196
152
  </div>
197
153
  </div>
198
- </BaseModalSide>
154
+ </div>
155
+
156
+ <template v-for="(section, i) in sectionsInternal" :key="section.name">
157
+ <BaseDataIteratorSectionModal
158
+ :section="section"
159
+ :model-value="sectionModalActive == i"
160
+ @update:model-value="closeSection"
161
+ >
162
+ <slot
163
+ :name="section.name"
164
+ :query="query"
165
+ :update-query="updateFilterQuery"
166
+ :update-query-value="updateFilterQueryValue"
167
+ />
168
+ </BaseDataIteratorSectionModal>
169
+ </template>
199
170
  </div>
200
171
  </template>
201
172
 
@@ -218,6 +189,7 @@ import hash from 'object-hash';
218
189
  import { PropType } from 'vue';
219
190
  import {
220
191
  Collection,
192
+ DataIteratorSection,
221
193
  DataTableQuery,
222
194
  MenuItemInterface,
223
195
  PaginatedCollection,
@@ -225,14 +197,13 @@ import {
225
197
  } from '@/types';
226
198
 
227
199
  import BaseMenu from './BaseMenu.vue';
228
- import BaseCard from './BaseCard.vue';
229
- import BaseCardRow from './BaseCardRow.vue';
230
200
  import BasePagination from './BasePagination.vue';
231
- import BaseModalSide from './BaseModalSide.vue';
232
201
  import { config } from '@/index';
233
202
  import { useMutationObserver, useResizeObserver } from '@vueuse/core';
234
203
  import { useHasPaginatedData } from '@/composables/paginatedData';
235
- import { useNotificationsStore } from '@/index';
204
+ import BaseDataIteratorSectionButton from './BaseDataIteratorSectionButton.vue';
205
+ import BaseDataIteratorSectionModal from './BaseDataIteratorSectionModal.vue';
206
+ import BaseDataIteratorSectionBox from './BaseDataIteratorSectionBox.vue';
236
207
 
237
208
  const props = defineProps({
238
209
  /**
@@ -287,10 +258,26 @@ const props = defineProps({
287
258
  default: false,
288
259
  type: Boolean,
289
260
  },
261
+
262
+ /**
263
+ * Layout type
264
+ */
265
+ layout: {
266
+ default: 'default',
267
+ type: String as PropType<'default' | 'compact'>,
268
+ },
269
+
270
+ /**
271
+ * Sections
272
+ */
273
+ sections: {
274
+ default: undefined,
275
+ type: Array as PropType<DataIteratorSection[]>,
276
+ },
290
277
  });
291
278
 
279
+ const i18n = useI18n();
292
280
  const http = config.http;
293
- const notificationsStore = useNotificationsStore();
294
281
 
295
282
  defineEmits([
296
283
  'click',
@@ -324,7 +311,6 @@ useResizeObserver(dataIteratorNode, () => {
324
311
  const firstLoad = ref(false);
325
312
  const loading = ref(true);
326
313
  const error = ref(false);
327
- const showFilters = ref(false);
328
314
  const searchQuery = ref('');
329
315
 
330
316
  let lastUrl = '';
@@ -332,7 +318,10 @@ let lastQueryHash = '';
332
318
  const query = ref<DataTableQuery>(cloneDeep(props.defaultQuery));
333
319
  const slots = useSlots();
334
320
 
335
- const mobileLayout = computed(() => {
321
+ const compactLayout = computed(() => {
322
+ if (props.layout === 'compact') {
323
+ return true;
324
+ }
336
325
  return width.value < 1024;
337
326
  });
338
327
 
@@ -366,7 +355,7 @@ onMounted(() => {
366
355
  });
367
356
 
368
357
  watch(
369
- () => mobileLayout.value,
358
+ () => compactLayout.value,
370
359
  () => {
371
360
  // After the sidebar appears...
372
361
  nextTick(() => {
@@ -606,6 +595,9 @@ function fetch(force = false, showLoading = true) {
606
595
  data.value = null;
607
596
  loading.value = false;
608
597
  error.value = true;
598
+ })
599
+ .finally(() => {
600
+ loading.value = false;
609
601
  });
610
602
  }
611
603
 
@@ -684,6 +676,53 @@ onMounted(() => {
684
676
  updateSearchInput();
685
677
  });
686
678
 
679
+ /*
680
+ |--------------------------------------------------------------------------
681
+ | Sections
682
+ |--------------------------------------------------------------------------
683
+ */
684
+
685
+ const sectionModalActive = ref<null | number>(null);
686
+
687
+ const sectionsInternal = computed<DataIteratorSection[]>(() => {
688
+ const sections = props.sections ?? [];
689
+
690
+ if (hasFilters.value) {
691
+ return [
692
+ {
693
+ name: 'filters',
694
+ title: i18n.t('sui.filters'),
695
+ closeText: i18n.t('sui.apply_filters'),
696
+ icon: 'heroicons:adjustments-horizontal-solid',
697
+ opened: true,
698
+ },
699
+ ...sections,
700
+ ];
701
+ }
702
+
703
+ return sections;
704
+ });
705
+
706
+ function openSection(index: number) {
707
+ if (sectionsInternal.value[index]) {
708
+ sectionModalActive.value = index;
709
+ } else {
710
+ sectionModalActive.value = null;
711
+ }
712
+ }
713
+
714
+ function closeSection() {
715
+ sectionModalActive.value = null;
716
+ }
717
+
718
+ /*
719
+ |--------------------------------------------------------------------------
720
+ | Provide
721
+ |--------------------------------------------------------------------------
722
+ */
723
+
724
+ provide('dataIterator:width', width);
725
+
687
726
  /*
688
727
  |--------------------------------------------------------------------------
689
728
  | Exposed API
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <BaseCard>
3
+ <button
4
+ type="button"
5
+ class="flex w-full items-center justify-between px-4 py-3"
6
+ @click="active = !active"
7
+ >
8
+ <h2 class="text-sm font-semibold">
9
+ {{ section.title }}
10
+ </h2>
11
+
12
+ <BaseIcon
13
+ class="h-5 w-5 text-slate-500 duration-300"
14
+ :class="active ? 'rotate-90 transform' : ''"
15
+ icon="heroicons:chevron-right-20-solid"
16
+ ></BaseIcon>
17
+ </button>
18
+ <div v-show="active" class="border-t border-slate-300 py-5 px-4">
19
+ <slot />
20
+ </div>
21
+ </BaseCard>
22
+ </template>
23
+
24
+ <script lang="ts" setup>
25
+ import { DataIteratorSection } from '@/types';
26
+ import { BaseCard, BaseIcon } from '.';
27
+
28
+ const props = defineProps<{
29
+ section: DataIteratorSection;
30
+ }>();
31
+
32
+ const active = ref<boolean>(props.section.opened ?? false);
33
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <button
3
+ class="btn flex h-11 items-center justify-center py-1 text-base shadow-sm"
4
+ :class="[width > 600 ? 'px-4' : 'px-3.5']"
5
+ type="button"
6
+ @click="open()"
7
+ >
8
+ <BaseIcon class="h-5 w-5 text-slate-500" :icon="section.icon" />
9
+ <span
10
+ v-if="section.title && width > 600"
11
+ class="ml-2 whitespace-pre text-sm"
12
+ >{{ section.title }}</span
13
+ >
14
+ </button>
15
+ </template>
16
+
17
+ <script lang="ts" setup>
18
+ import { DataIteratorSection } from '@/types';
19
+ import { BaseIcon } from '.';
20
+
21
+ defineProps<{
22
+ section: DataIteratorSection;
23
+ }>();
24
+
25
+ const emit = defineEmits<{
26
+ (event: 'open'): void;
27
+ }>();
28
+
29
+ const width = inject('dataIterator:width', ref(0));
30
+
31
+ function open() {
32
+ emit('open');
33
+ }
34
+ </script>
@@ -1,15 +1,15 @@
1
1
  <template>
2
2
  <ul>
3
3
  <li v-for="col in toggleableColumns" :key="col.newKey">
4
- <label>
4
+ <label class="cursor-pointer">
5
5
  <input
6
6
  :checked="visibleColumns.includes(col.newKey)"
7
7
  type="checkbox"
8
- class="mr-1.5 h-3.5 w-3.5 rounded focus:ring-1 focus:ring-primary-300 focus:ring-offset-1"
8
+ class="mr-2 h-3.5 w-3.5 cursor-pointer rounded focus:ring-1 focus:ring-primary-300 focus:ring-offset-1"
9
9
  :value="col.newKey"
10
10
  @change="onVisibleColumnChange($event, col.newKey)"
11
11
  />
12
- <span class="text-xs text-slate-600">
12
+ <span class="text-xs text-slate-700">
13
13
  {{ col.label }}
14
14
  </span>
15
15
  </label>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <BaseModalSide
3
+ :model-value="modelValue"
4
+ @update:model-value="emit('update:modelValue', $event)"
5
+ >
6
+ <div>
7
+ <div class="border-b border-slate-300 p-4">
8
+ <h2 class="text-base font-semibold">
9
+ {{ section.title }}
10
+ </h2>
11
+ </div>
12
+
13
+ <div class="py-5 px-4">
14
+ <slot />
15
+ </div>
16
+
17
+ <div class="border-t border-slate-300 p-4">
18
+ <button
19
+ class="btn btn-primary w-full sm:w-auto"
20
+ @click="emit('update:modelValue', false)"
21
+ >
22
+ {{ section.closeText }}
23
+ </button>
24
+ </div>
25
+ </div>
26
+ </BaseModalSide>
27
+ </template>
28
+
29
+ <script lang="ts" setup>
30
+ import { DataIteratorSection } from '@/types';
31
+ import BaseModalSide from './BaseModalSide.vue';
32
+
33
+ defineProps<{
34
+ modelValue: boolean;
35
+ section: DataIteratorSection;
36
+ }>();
37
+
38
+ const emit = defineEmits<{
39
+ (event: 'update:modelValue', value: boolean): void;
40
+ }>();
41
+ </script>
@@ -9,11 +9,18 @@ import BaseAppDialogs from './BaseAppDialogs.vue';
9
9
  export default {
10
10
  title: 'Data/BaseDataTable',
11
11
  component: BaseDataTable,
12
- argTypes: {},
12
+ argTypes: {
13
+ layout: {
14
+ control: {
15
+ type: 'select',
16
+ options: ['default', 'compact'],
17
+ },
18
+ },
19
+ },
13
20
  args: {
14
21
  url: 'https://effettandem.com/api/content/articles',
15
22
  urlQuery: {
16
- per_page: 5,
23
+ per_page: 10,
17
24
  },
18
25
  historyMode: false,
19
26
  },
@@ -28,11 +35,11 @@ const template = `
28
35
  field="title->en"
29
36
  :toggle="false"
30
37
  >
31
- <div class="max-w-sm">
32
- <div class="font-medium text-slate-900">
38
+ <div class="max-w-[200px]">
39
+ <div class="font-medium text-slate-900 leading-tight truncate">
33
40
  {{ row.title }}
34
41
  </div>
35
- <p class="text-xs leading-tight text-slate-500">
42
+ <p v-if="false" class="text-xs leading-tight mt-1 text-slate-500 line-clamp-2">
36
43
  {{ row.subtitle }}
37
44
  </p>
38
45
  </div>
@@ -53,11 +60,33 @@ const template = `
53
60
  field="votes_avg_score"
54
61
  sortable
55
62
  >
56
- <div class="">
63
+ <div class="whitespace-pre">
57
64
  {{ Math.round(row.votes_avg_score) }} / 5
58
65
  </div>
59
66
  </BaseTableColumn>
60
67
 
68
+ <BaseTableColumn
69
+ v-slot="{ row }"
70
+ label="Vote"
71
+ field="votes_avg_score"
72
+ sortable
73
+ >
74
+ <BaseBadge>
75
+ <span class="whitespace-pre">{{ row.owner?.name }}</span>
76
+ </BaseBadge>
77
+ </BaseTableColumn>
78
+
79
+ <BaseTableColumn
80
+ v-slot="{ row }"
81
+ label="Select"
82
+ :clickable="false"
83
+ padding="0px"
84
+ >
85
+ <select class="select border border-slate-300 rounded text-xs">
86
+ <option>Option 1</option>
87
+ </select>
88
+ </BaseTableColumn>
89
+
61
90
  <BaseTableColumn
62
91
  v-slot="{ row }"
63
92
  label="Access level"
@@ -77,6 +106,12 @@ const template = `
77
106
  </div>
78
107
  </template>
79
108
 
109
+ <template #test>
110
+ <div>
111
+ Section Test
112
+ </div>
113
+ </template>
114
+
80
115
  <template #filters="{ query, updateQueryValue }">
81
116
  <div class="space-y-3">
82
117
  <div>
@@ -85,7 +120,7 @@ const template = `
85
120
  </p>
86
121
  <BaseSelect
87
122
  :model-value="query.type ?? null"
88
- class="w-full rounded border-slate-300"
123
+ class="w-full rounded border-slate-300 text-base sm:text-sm"
89
124
  placeholder="-"
90
125
  @update:model-value="updateQueryValue('type', $event)"
91
126
  >
@@ -103,7 +138,7 @@ const template = `
103
138
  </p>
104
139
  <BaseSelect
105
140
  :model-value="query.access_level ?? null"
106
- class="w-full rounded border-slate-300"
141
+ class="w-full rounded border-slate-300 text-base sm:text-sm"
107
142
  placeholder="-"
108
143
  @update:model-value="updateQueryValue('access_level', $event)"
109
144
  >
@@ -146,8 +181,21 @@ const Template = (args) => ({
146
181
  });
147
182
 
148
183
  export const Demo = Template.bind({});
184
+
149
185
  Demo.args = {
186
+ showUrl() {
187
+ return '/';
188
+ },
189
+ editUrl() {
190
+ return '/';
191
+ },
192
+ deleteUrl() {
193
+ return '/';
194
+ },
195
+ detailed: true,
150
196
  searchable: true,
197
+ maxHeight: 300,
198
+ checkable: true,
151
199
  actions: [
152
200
  {
153
201
  label: 'Open Google',
@@ -167,7 +215,6 @@ Demo.args = {
167
215
  color: 'danger',
168
216
  },
169
217
  ],
170
- checkable: true,
171
218
  checkableActions: [
172
219
  {
173
220
  label: 'Delete all',
@@ -176,14 +223,23 @@ Demo.args = {
176
223
  },
177
224
  },
178
225
  ],
179
- detailed: true,
180
- maxHeight: 400,
181
- editUrl() {
182
- return '/';
183
- },
184
- deleteUrl() {
185
- return '/';
186
- },
226
+ rowActions: [
227
+ {
228
+ label: 'Open Google',
229
+ icon: 'heroicons:link',
230
+ action() {
231
+ alert('hit');
232
+ },
233
+ },
234
+ ],
235
+ sections: [
236
+ {
237
+ name: 'test',
238
+ title: 'Section Test',
239
+ icon: 'heroicons:link',
240
+ closeText: 'Close',
241
+ },
242
+ ],
187
243
  };
188
244
 
189
245
  const SimpleTemplate = (args) => ({