sprintify-ui 0.7.11 → 0.8.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.
Files changed (44) hide show
  1. package/dist/sprintify-ui.es.js +10482 -10348
  2. package/dist/style.css +1 -1
  3. package/dist/types/components/BaseDataIterator.vue.d.ts +50 -4
  4. package/dist/types/components/BaseDataIteratorSectionColumns.vue.d.ts +156 -68
  5. package/dist/types/components/BaseDataTable.vue.d.ts +86 -6
  6. package/dist/types/components/BaseDataTableTemplate.vue.d.ts +44 -28
  7. package/dist/types/components/BaseTable.vue.d.ts +30 -7
  8. package/dist/types/components/BaseTableCell.vue.d.ts +19 -1
  9. package/dist/types/components/BaseTableColumn.vue.d.ts +50 -3
  10. package/dist/types/components/BaseTableHead.vue.d.ts +6 -5
  11. package/dist/types/components/BaseTableHeader.vue.d.ts +12 -2
  12. package/dist/types/components/BaseTableRow.vue.d.ts +5 -0
  13. package/dist/types/composables/isFirstColumn.d.ts +4 -0
  14. package/dist/types/composables/isLastColumn.d.ts +4 -0
  15. package/dist/types/composables/paginatedData.d.ts +5 -5
  16. package/dist/types/services/table/types.d.ts +6 -5
  17. package/dist/types/types/index.d.ts +7 -2
  18. package/dist/types/utils/getApiData.d.ts +1 -1
  19. package/package.json +2 -1
  20. package/src/assets/main.css +0 -1
  21. package/src/components/BaseAvatar.vue +1 -0
  22. package/src/components/BaseDataIterator.stories.js +96 -1
  23. package/src/components/BaseDataIterator.vue +135 -11
  24. package/src/components/BaseDataIteratorSectionColumns.vue +2 -2
  25. package/src/components/BaseDataTable.stories.js +140 -50
  26. package/src/components/BaseDataTable.vue +82 -48
  27. package/src/components/BaseDataTableTemplate.vue +208 -372
  28. package/src/components/BaseTable.stories.js +57 -15
  29. package/src/components/BaseTable.vue +71 -9
  30. package/src/components/BaseTableBody.vue +1 -1
  31. package/src/components/BaseTableCell.vue +94 -36
  32. package/src/components/BaseTableColumn.vue +25 -2
  33. package/src/components/BaseTableHead.vue +17 -5
  34. package/src/components/BaseTableHeader.vue +39 -10
  35. package/src/components/BaseTableRow.vue +27 -6
  36. package/src/composables/isFirstColumn.ts +31 -0
  37. package/src/composables/isLastColumn.ts +31 -0
  38. package/src/composables/paginatedData.ts +22 -10
  39. package/src/services/table/classes.ts +13 -9
  40. package/src/services/table/types.ts +6 -5
  41. package/src/stories/List.stories.js +5 -1
  42. package/src/types/index.ts +7 -2
  43. package/src/utils/getApiData.ts +1 -1
  44. package/src/assets/base-table.css +0 -17
@@ -6,12 +6,14 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
6
6
  target?: "_blank" | "_self" | "_parent" | "_top" | undefined;
7
7
  title?: string | undefined;
8
8
  class?: ClassNameValue;
9
+ onClick?: ((e: MouseEvent) => void) | undefined;
9
10
  }>, {
10
11
  href: undefined;
11
12
  to: undefined;
12
13
  target: undefined;
13
14
  title: undefined;
14
15
  class: undefined;
16
+ onClick: undefined;
15
17
  }>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
18
  click: (...args: any[]) => void;
17
19
  mouseenter: (...args: any[]) => void;
@@ -22,18 +24,21 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
22
24
  target?: "_blank" | "_self" | "_parent" | "_top" | undefined;
23
25
  title?: string | undefined;
24
26
  class?: ClassNameValue;
27
+ onClick?: ((e: MouseEvent) => void) | undefined;
25
28
  }>, {
26
29
  href: undefined;
27
30
  to: undefined;
28
31
  target: undefined;
29
32
  title: undefined;
30
33
  class: undefined;
34
+ onClick: undefined;
31
35
  }>>> & {
32
36
  onClick?: ((...args: any[]) => any) | undefined;
33
37
  onMouseenter?: ((...args: any[]) => any) | undefined;
34
38
  onMouseleave?: ((...args: any[]) => any) | undefined;
35
39
  }, {
36
40
  class: string | false | 0 | ClassNameValue[] | null;
41
+ onClick: (e: MouseEvent) => void;
37
42
  title: string;
38
43
  to: RouteLocationRaw;
39
44
  href: string;
@@ -0,0 +1,4 @@
1
+ import { MaybeRef } from "vue";
2
+ export declare function useIsFirstColumn(tdOrTh: MaybeRef<HTMLTableCellElement | null>): {
3
+ isFirstColumn: import("vue").ComputedRef<boolean>;
4
+ };
@@ -0,0 +1,4 @@
1
+ import { MaybeRef } from "vue";
2
+ export declare function useIsLastColumn(tdOrTh: MaybeRef<HTMLTableCellElement | null>): {
3
+ isLastColumn: import("vue").ComputedRef<boolean>;
4
+ };
@@ -1,7 +1,7 @@
1
- import { Ref } from 'vue';
1
+ import { ComputedRef, Ref } from 'vue';
2
2
  import { Collection, PaginatedCollection, ResourceCollection, PaginationMetadata } from '@/types';
3
- export declare function useHasPaginatedData(data: Ref<Collection | PaginatedCollection | ResourceCollection | null>): {
4
- items: import("vue").ComputedRef<Collection>;
5
- paginationMetadata: import("vue").ComputedRef<PaginationMetadata | null>;
6
- lastPage: import("vue").ComputedRef<number>;
3
+ export declare function useHasPaginatedData(data: Ref<Collection | PaginatedCollection | ResourceCollection | null | undefined>, page?: ComputedRef<number>, perPage?: ComputedRef<number>): {
4
+ items: ComputedRef<Collection>;
5
+ paginationMetadata: ComputedRef<PaginationMetadata | null>;
6
+ lastPage: ComputedRef<number>;
7
7
  };
@@ -1,15 +1,16 @@
1
1
  import { RouteLocationRaw } from "vue-router";
2
- export type CellSpacing = 'none' | 'sm' | 'md' | 'lg';
2
+ export type CellSize = 'none' | 'xs' | 'sm' | 'md' | 'lg';
3
3
  export interface TableProps {
4
- spacing?: CellSpacing;
4
+ size?: CellSize;
5
5
  flush?: boolean;
6
+ fixedHeader?: boolean;
7
+ fixedColumn?: boolean;
6
8
  }
7
- export type CellConfig = {
8
- flush?: boolean;
9
- } & TableProps;
9
+ export type CellConfig = TableProps;
10
10
  export interface CellProps {
11
11
  title?: string;
12
12
  href?: string;
13
13
  to?: RouteLocationRaw;
14
14
  target?: string;
15
+ onClick?: (e: MouseEvent) => void;
15
16
  }
@@ -76,15 +76,20 @@ export interface BaseTableColumnData {
76
76
  label: string;
77
77
  field: string;
78
78
  meta: undefined | Record<string, any>;
79
- newKey: number;
79
+ newKey: string;
80
80
  numeric: boolean;
81
81
  position: 'left' | 'right';
82
82
  searchable: boolean;
83
83
  sortable: boolean;
84
- clickable: boolean;
84
+ ignoreRowInteractions: boolean;
85
85
  toggle: boolean;
86
86
  toggleDefault: boolean;
87
87
  width: number;
88
+ class?: string | string[];
89
+ to?: (row: Row) => RouteLocationRaw;
90
+ href?: (row: Row) => string;
91
+ target?: '_blank' | '_self' | '_parent' | '_top';
92
+ onClick?: (row: Row, index: number, column: BaseTableColumnData, colIndex: number, event: MouseEvent) => void;
88
93
  style: {
89
94
  width: undefined | number;
90
95
  };
@@ -1,2 +1,2 @@
1
1
  import { Collection, PaginatedCollection, ResourceCollection } from "@/types";
2
- export declare function getItems(data: Collection | PaginatedCollection | ResourceCollection | null): Collection;
2
+ export declare function getItems(data: Collection | PaginatedCollection | ResourceCollection | null | undefined): Collection;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.7.11",
3
+ "version": "0.8.1",
4
4
  "scripts": {
5
5
  "build": "rimraf dist && vue-tsc && vite build",
6
6
  "build-fast": "rimraf dist && vite build",
@@ -56,6 +56,7 @@
56
56
  "@commitlint/config-conventional": "^17.8.1",
57
57
  "@iconify/vue": "^4.1.1",
58
58
  "@storybook/addon-actions": "^7.2.1",
59
+ "@storybook/addon-controls": "^7.6.17",
59
60
  "@storybook/addon-essentials": "^7.6.17",
60
61
  "@storybook/addon-interactions": "^7.6.17",
61
62
  "@storybook/addon-links": "^7.6.17",
@@ -3,7 +3,6 @@
3
3
  @import "./base-date-picker.css";
4
4
  @import "./base-time-picker.css";
5
5
  @import "./base-rich-text.css";
6
- @import "./base-table.css";
7
6
  @import "./base-tabs.css";
8
7
  @import "./base-spinner.css";
9
8
 
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <BaseTooltip
3
+ v-if="user"
3
4
  :visible="tooltip"
4
5
  :text="tooltipText"
5
6
  :class="classInternal"
@@ -3,7 +3,8 @@ import BaseSelect from './BaseSelect.vue';
3
3
  import BaseCard from './BaseCard.vue';
4
4
  import BaseCardRow from './BaseCardRow.vue';
5
5
  import BaseLoadingCover from './BaseLoadingCover.vue';
6
- import { sizes } from '../../.storybook/utils';
6
+ import { options, sizes } from '../../.storybook/utils';
7
+ import { computed } from 'vue';
7
8
 
8
9
  export default {
9
10
  title: 'Data/BaseDataIterator',
@@ -205,3 +206,97 @@ Simple.args = {
205
206
  searchable: false,
206
207
  actions: [],
207
208
  };
209
+
210
+ const LocalDataTemplate = (args) => ({
211
+ components: {
212
+ BaseDataIterator,
213
+ BaseCard,
214
+ BaseCardRow,
215
+ BaseLoadingCover,
216
+ BaseSelect,
217
+ },
218
+ setup() {
219
+
220
+ const baseDataIteratorRef = ref(null);
221
+
222
+ const filteredItems = computed(() => {
223
+ return options
224
+ .filter((item) => {
225
+
226
+ const type = baseDataIteratorRef.value?.query?.filter?.type ?? null;
227
+
228
+ if (type) {
229
+ return item.type === type;
230
+ }
231
+
232
+ return true;
233
+ });
234
+ });
235
+
236
+ return { args, filteredItems, baseDataIteratorRef };
237
+ },
238
+ template: `
239
+ <BaseDataIterator ref="baseDataIteratorRef" v-bind="args" :items="filteredItems">
240
+ <template #default="{ items, loading }">
241
+
242
+ <div class="relative">
243
+ <div class="space-y-1.5">
244
+ <div
245
+ v-for="item in items"
246
+ :key="item.id"
247
+ class="block group"
248
+ >
249
+ <BaseCard class="group-hover:bg-slate-100">
250
+ <BaseCardRow size="xs">
251
+ <div class="font-medium text-slate-900">
252
+ {{ item.label }}
253
+ </div>
254
+ <p class="text-xs leading-tight text-slate-500">
255
+ {{ item.type }}
256
+ </p>
257
+ </BaseCardRow>
258
+ </BaseCard>
259
+ </div>
260
+ </div>
261
+
262
+ <BaseLoadingCover
263
+ v-show="loading"
264
+ size="lg"
265
+ backdropClass="bg-white bg-opacity-50"
266
+ >
267
+ </BaseLoadingCover>
268
+ </div>
269
+ </template>
270
+
271
+ <template #filters="{ query, updateQueryValue }">
272
+ <div class="space-y-3">
273
+ <div>
274
+ <p class="mb-1 text-sm">
275
+ Type
276
+ </p>
277
+ <BaseSelect
278
+ :model-value="query.filter.type ?? null"
279
+ class="w-full rounded border-slate-300"
280
+ placeholder="-"
281
+ @update:model-value="updateQueryValue('filter.type', $event)"
282
+ >
283
+ <option value="jedi">
284
+ Jedi
285
+ </option>
286
+ <option value="sith">
287
+ Sith
288
+ </option>
289
+ </BaseSelect>
290
+ </div>
291
+ </div>
292
+ </template>
293
+ </BaseDataIterator>
294
+ `,
295
+ });
296
+
297
+ export const Local = LocalDataTemplate.bind({});
298
+ Local.args = {
299
+ url: null,
300
+ actions: [],
301
+ perPage: 4,
302
+ };
@@ -84,10 +84,8 @@
84
84
  />
85
85
  </BaseDataIteratorSectionBox>
86
86
  </div>
87
-
88
-
89
87
  <slot
90
- :items="items"
88
+ :items="itemsInternal"
91
89
  :loading="loading"
92
90
  :error="error"
93
91
  :first-load="firstLoad"
@@ -197,7 +195,7 @@ const DEFAULT_QUERY = {
197
195
  </script>
198
196
 
199
197
  <script lang="ts" setup>
200
- import { cloneDeep, debounce, merge, set } from 'lodash';
198
+ import { cloneDeep, debounce, merge, set, sortBy } from 'lodash';
201
199
  import hash from 'object-hash';
202
200
  import { PropType } from 'vue';
203
201
  import {
@@ -220,12 +218,21 @@ import BaseDataIteratorSectionModal from './BaseDataIteratorSectionModal.vue';
220
218
  import BaseDataIteratorSectionBox from './BaseDataIteratorSectionBox.vue';
221
219
 
222
220
  const props = defineProps({
221
+
222
+ /**
223
+ * Data to display
224
+ */
225
+ items: {
226
+ default: undefined,
227
+ type: Array as PropType<Collection | undefined>,
228
+ },
229
+
223
230
  /**
224
231
  * Base URL from which to make requests
225
232
  */
226
233
  url: {
227
- required: true,
228
- type: String,
234
+ default: undefined,
235
+ type: String as PropType<string | undefined>,
229
236
  },
230
237
 
231
238
  /**
@@ -311,9 +318,28 @@ const props = defineProps({
311
318
  filtersPosition: {
312
319
  default: 'section',
313
320
  type: String as PropType<'section' | 'top'>,
321
+ },
322
+
323
+ /**
324
+ * Per page (only when using local data)
325
+ */
326
+ perPage: {
327
+ default: 16,
328
+ type: Number,
329
+ },
330
+
331
+ /**
332
+ * Function to search local data
333
+ */
334
+ search: {
335
+ default: undefined,
336
+ type: Function as PropType<(items: Collection, search: string | null) => Collection>,
314
337
  }
315
338
  });
316
339
 
340
+ const LOCAL = 'local';
341
+ const REMOTE = 'remote';
342
+
317
343
  const http = config.http;
318
344
 
319
345
  const emit = defineEmits([
@@ -327,6 +353,14 @@ const emit = defineEmits([
327
353
  'fetch',
328
354
  ]);
329
355
 
356
+ const dataMode = computed(() => {
357
+ if (props.url) {
358
+ return REMOTE;
359
+ }
360
+
361
+ return LOCAL;
362
+ });
363
+
330
364
  const dataIteratorNode = ref<null | HTMLElement>(null);
331
365
  const searchInputFocus = ref(false);
332
366
 
@@ -350,7 +384,7 @@ useResizeObserver(dataIteratorNode, () => {
350
384
  /** Data table state */
351
385
 
352
386
  const firstLoad = ref(false);
353
- const loading = ref(true);
387
+ const loading = ref(false);
354
388
  const error = ref(false);
355
389
  const searchQuery = ref('');
356
390
 
@@ -476,7 +510,7 @@ function queryHash(query: DataTableQuery): string {
476
510
  |--------------------------------------------------------------------------
477
511
  */
478
512
 
479
- const url = computed<string>(() => {
513
+ const url = computed<string | undefined>(() => {
480
514
  return props.url;
481
515
  });
482
516
 
@@ -518,7 +552,7 @@ const onSearch = debounce((event: any) => {
518
552
  newQuery.search = searchQuery.value;
519
553
 
520
554
  updateQuery(newQuery);
521
- }, 350);
555
+ }, dataMode.value == REMOTE ? 350 : 0);
522
556
 
523
557
  /*
524
558
  |--------------------------------------------------------------------------
@@ -608,6 +642,11 @@ function fetch(force = false, showLoading = true) {
608
642
  return;
609
643
  }
610
644
 
645
+ if (url.value == null) {
646
+ firstLoad.value = true;
647
+ return;
648
+ }
649
+
611
650
  const urlSplit = url.value.split(/[?#]/);
612
651
 
613
652
  const baseUrl = urlSplit[0];
@@ -664,15 +703,100 @@ const data = ref<null | ResourceCollection | PaginatedCollection | Collection>(
664
703
  null
665
704
  );
666
705
 
667
- const { items, paginationMetadata, lastPage } = useHasPaginatedData(data);
668
-
669
706
  const page = computed((): number => {
670
707
  if (query.value.page && parseInt(query.value.page + '')) {
671
708
  return parseInt(query.value.page + '');
672
709
  }
710
+
673
711
  return 1;
674
712
  });
675
713
 
714
+ const dataInternal = computed<ResourceCollection | PaginatedCollection | Collection | null | undefined>(() => {
715
+ if (dataMode.value == REMOTE) {
716
+ return data.value;
717
+ }
718
+
719
+ return searchItems(props.items);
720
+ });
721
+
722
+ const { items, paginationMetadata, lastPage } = useHasPaginatedData(
723
+ dataInternal,
724
+ page,
725
+ computed(() => props.perPage),
726
+ );
727
+
728
+ const itemsInternal = computed(() => {
729
+ if (dataMode.value == REMOTE) {
730
+ return items.value;
731
+ }
732
+
733
+ return paginateItems(sortItems(items.value));
734
+ });
735
+
736
+ /*
737
+ |--------------------------------------------------------------------------
738
+ | Local data filtering, sorting and pagination
739
+ |--------------------------------------------------------------------------
740
+ */
741
+
742
+ function searchItems(items: Collection | undefined) {
743
+
744
+ if (!items) {
745
+ return [];
746
+ }
747
+
748
+ const searchWords = searchKeywords.value.toLowerCase();
749
+
750
+ if (props.search) {
751
+ return props.search(items, searchKeywords.value);
752
+ }
753
+
754
+ return items?.filter((item) => {
755
+ return Object.values(item).some((value) => {
756
+ if (typeof value == 'string') {
757
+ return value.toLowerCase().includes(searchWords);
758
+ }
759
+ return false;
760
+ });
761
+ });
762
+ }
763
+
764
+ function sortItems(items: Collection | undefined) {
765
+ if (!items) {
766
+ return [];
767
+ }
768
+
769
+ if (!sortField.value) {
770
+ return items;
771
+ }
772
+
773
+ if (!sortDirection.value) {
774
+ return items;
775
+ }
776
+
777
+ if (sortDirection.value == 'asc') {
778
+ return sortBy(items, (item) => {
779
+ return item[sortField.value];
780
+ });
781
+ }
782
+
783
+ return sortBy(items, (item) => {
784
+ return item[sortField.value];
785
+ })
786
+ .reverse();
787
+ }
788
+
789
+ function paginateItems(items: Collection | undefined) {
790
+ if (!items) {
791
+ return [];
792
+ }
793
+
794
+ return items.slice(
795
+ (page.value - 1) * props.perPage,
796
+ page.value * props.perPage
797
+ );
798
+ }
799
+
676
800
  const sortField = computed((): string => {
677
801
  return query.value.sort?.trim().replace(/^(-)/, '') ?? '';
678
802
  });
@@ -32,7 +32,7 @@ const props = defineProps({
32
32
  },
33
33
  visibleColumns: {
34
34
  required: true,
35
- type: Array as PropType<number[]>,
35
+ type: Array as PropType<string[]>,
36
36
  },
37
37
  });
38
38
 
@@ -52,7 +52,7 @@ const toggleableColumns = computed(() => {
52
52
  return tableVue.newColumns.filter((c) => c.toggle);
53
53
  });
54
54
 
55
- function onVisibleColumnChange(event: any, newKey: number) {
55
+ function onVisibleColumnChange(event: any, newKey: string) {
56
56
  const checked = event.target.checked as boolean;
57
57
 
58
58
  let newVisibleColumns = cloneDeep(props.visibleColumns);