quasar-ui-danx 0.4.1 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. package/dist/danx.es.js +7234 -6741
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +11 -5
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +3 -1
  7. package/src/components/ActionTable/ActionTable.vue +31 -43
  8. package/src/components/ActionTable/Columns/ActionTableColumn.vue +19 -18
  9. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +15 -14
  10. package/src/components/ActionTable/Filters/{FilterFieldList.vue → FilterList.vue} +26 -26
  11. package/src/components/ActionTable/Filters/FilterableField.vue +28 -31
  12. package/src/components/ActionTable/Filters/index.ts +2 -2
  13. package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +71 -0
  14. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +8 -13
  15. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +34 -33
  16. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +48 -44
  17. package/src/components/ActionTable/Form/Fields/NumberField.vue +60 -59
  18. package/src/components/ActionTable/Form/Fields/SelectField.vue +124 -138
  19. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +28 -33
  20. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +15 -15
  21. package/src/components/ActionTable/Form/Fields/SliderNumberField.vue +45 -0
  22. package/src/components/ActionTable/Form/Fields/TextField.vue +47 -66
  23. package/src/components/ActionTable/Form/Fields/index.ts +2 -0
  24. package/src/components/ActionTable/Form/RenderedForm.vue +50 -13
  25. package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +17 -0
  26. package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
  27. package/src/components/ActionTable/Form/index.ts +1 -0
  28. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +22 -16
  29. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +11 -11
  30. package/src/components/ActionTable/listControls.ts +104 -166
  31. package/src/components/ActionTable/listHelpers.ts +2 -3
  32. package/src/components/ActionTable/tableColumns.ts +53 -77
  33. package/src/components/AuditHistory/AuditHistoryItemValue.vue +26 -26
  34. package/src/components/PanelsDrawer/PanelsDrawer.vue +17 -4
  35. package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +6 -11
  36. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +20 -20
  37. package/src/components/Utility/Dialogs/ConfirmActionDialog.vue +39 -0
  38. package/src/components/Utility/Dialogs/ConfirmDialog.vue +57 -117
  39. package/src/components/Utility/Dialogs/DialogLayout.vue +77 -0
  40. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +42 -36
  41. package/src/components/Utility/Dialogs/InfoDialog.vue +40 -80
  42. package/src/components/Utility/Dialogs/index.ts +1 -0
  43. package/src/components/Utility/Files/FilePreview.vue +76 -73
  44. package/src/components/Utility/Layouts/ContentDrawer.vue +24 -31
  45. package/src/components/Utility/Tools/ActionVnode.vue +3 -3
  46. package/src/components/Utility/Tools/RenderVnode.vue +20 -11
  47. package/src/components/Utility/Transitions/MaxHeightTransition.vue +26 -0
  48. package/src/components/Utility/Transitions/index.ts +1 -0
  49. package/src/config/index.ts +36 -31
  50. package/src/helpers/FileUpload.ts +295 -297
  51. package/src/helpers/FlashMessages.ts +80 -71
  52. package/src/helpers/actions.ts +102 -82
  53. package/src/helpers/download.ts +189 -189
  54. package/src/helpers/downloadPdf.ts +55 -52
  55. package/src/helpers/formats.ts +151 -109
  56. package/src/helpers/index.ts +2 -0
  57. package/src/helpers/multiFileUpload.ts +72 -58
  58. package/src/helpers/objectStore.ts +52 -0
  59. package/src/helpers/request.ts +70 -51
  60. package/src/helpers/routes.ts +29 -0
  61. package/src/helpers/storage.ts +7 -3
  62. package/src/helpers/utils.ts +47 -29
  63. package/src/styles/quasar-reset.scss +94 -68
  64. package/src/styles/themes/danx/dialogs.scss +47 -0
  65. package/src/styles/themes/danx/forms.scss +18 -0
  66. package/src/styles/themes/danx/index.scss +4 -0
  67. package/src/types/actions.d.ts +43 -0
  68. package/src/types/config.d.ts +15 -0
  69. package/src/types/controls.d.ts +99 -0
  70. package/src/types/dialogs.d.ts +32 -0
  71. package/src/types/fields.d.ts +20 -0
  72. package/src/types/files.d.ts +54 -0
  73. package/src/types/formats.d.ts +4 -0
  74. package/src/{components/ActionTable/Form/form.d.ts → types/forms.d.ts} +6 -0
  75. package/src/types/index.d.ts +12 -0
  76. package/src/types/requests.d.ts +13 -0
  77. package/src/types/shared.d.ts +15 -0
  78. package/src/types/tables.d.ts +27 -0
  79. package/types/index.d.ts +1 -1
  80. /package/src/components/ActionTable/Filters/{FilterFieldItem.vue → FilterItem.vue} +0 -0
@@ -1,135 +1,40 @@
1
- import { computed, ComputedRef, Ref, ref, ShallowRef, shallowRef, VNode, watch } from "vue";
2
- import { ActionTargetItem, AnyObject, getItem, setItem, waitForRef } from "../../helpers";
1
+ import { computed, Ref, ref, shallowRef, watch } from "vue";
2
+ import { RouteLocationNormalizedLoaded, RouteParams, Router, useRoute, useRouter } from "vue-router";
3
+ import { getItem, setItem, storeObject, waitForRef } from "../../helpers";
4
+ import {
5
+ ActionController,
6
+ ActionTargetItem,
7
+ AnyObject,
8
+ FilterGroup,
9
+ ListControlsFilter,
10
+ ListControlsOptions,
11
+ ListControlsPagination,
12
+ PagedItems
13
+ } from "../../types";
3
14
  import { getFilterFromUrl } from "./listHelpers";
4
15
 
5
- export interface ActionController {
6
- name: string;
7
- label: string;
8
- pagedItems: Ref<PagedItems | null>;
9
- activeFilter: Ref<ListControlsFilter>;
10
- globalFilter: Ref<ListControlsFilter>;
11
- filterActiveCount: ComputedRef<number>;
12
- showFilters: Ref<boolean>;
13
- summary: ShallowRef<object | null>;
14
- filterFieldOptions: Ref<AnyObject>;
15
- selectedRows: ShallowRef<ActionTargetItem[]>;
16
- isLoadingList: Ref<boolean>;
17
- isLoadingFilters: Ref<boolean>;
18
- isLoadingSummary: Ref<boolean>;
19
- pager: ComputedRef<{
20
- perPage: number;
21
- page: number;
22
- filter: ListControlsFilter;
23
- sort: object[] | undefined;
24
- }>;
25
- pagination: ShallowRef<ListControlsPagination>;
26
- activeItem: ShallowRef<ActionTargetItem | null>;
27
- activePanel: ShallowRef<string | null>;
28
-
29
- // Actions
30
- initialize: () => void;
31
- loadSummary: () => Promise<void>;
32
- resetPaging: () => void;
33
- setPagination: (updated: ListControlsPagination) => void;
34
- setSelectedRows: (selection: ActionTargetItem[]) => void;
35
- loadList: () => Promise<void>;
36
- loadMore: (index: number, perPage?: number) => Promise<boolean>;
37
- refreshAll: () => Promise<void[]>;
38
- exportList: () => Promise<void>;
39
- setActiveItem: (item: ActionTargetItem | null) => void;
40
- getNextItem: (offset: number) => Promise<void>;
41
- activatePanel: (item: ActionTargetItem | null, panel: string | null) => void;
42
- setActiveFilter: (filter: ListControlsFilter) => void;
43
- applyFilterFromUrl: (url: string, filterFields: Ref<FilterGroup[]> | null) => void;
44
- setItemInList: (updatedItem: ActionTargetItem) => void;
45
- }
46
-
47
- export interface LabelValueItem {
48
- label: string;
49
- value: string | number | boolean;
50
- }
51
-
52
- export interface FilterField {
53
- name: string;
54
- label: string;
55
- type: string;
56
- options?: string[] | number[] | LabelValueItem[];
57
- inline?: boolean;
58
- }
59
-
60
- export interface FilterGroup {
61
- name?: string;
62
- flat?: boolean;
63
- fields: FilterField[];
64
- }
65
-
66
- export interface ActionPanel {
67
- name: string;
68
- label: string;
69
- category?: string;
70
- enabled: boolean | (() => boolean);
71
- tabVnode: () => VNode;
72
- vnode: () => VNode;
73
- }
74
-
75
- export interface ListControlsFilter {
76
- [key: string]: object | object[] | null | undefined | string | number | boolean;
77
- }
78
-
79
- export interface ListControlsRoutes {
80
- list: (pager: object) => Promise<ActionTargetItem[]>;
81
- details?: (item: object) => Promise<ActionTargetItem> | null;
82
- summary?: (filter: object | null) => Promise<object> | null;
83
- filterFieldOptions?: (filter: object | null) => Promise<object> | null;
84
- more?: (pager: object) => Promise<ActionTargetItem[]> | null;
85
- export: (filter: object) => Promise<void>;
86
- }
87
-
88
- export interface ListControlsOptions {
89
- label?: string,
90
- routes: ListControlsRoutes;
91
- urlPattern?: RegExp | null;
92
- filterDefaults?: Record<string, object>;
93
- refreshFilters?: boolean;
94
- }
95
-
96
- export interface ListControlsPagination {
97
- __sort: object[] | null;
98
- sortBy: string | null;
99
- descending: boolean;
100
- page: number;
101
- rowsNumber: number;
102
- rowsPerPage: number;
103
- }
104
-
105
- export interface PagedItems {
106
- data: ActionTargetItem[] | undefined;
107
- meta: {
108
- total: number;
109
- last_page?: number;
110
- } | undefined;
111
- }
112
-
113
- export function useListControls(name: string, options: ListControlsOptions) {
16
+ export function useListControls(name: string, options: ListControlsOptions): ActionController {
114
17
  let isInitialized = false;
18
+ let vueRoute: RouteLocationNormalizedLoaded | null = null;
19
+ let vueRouter: Router | null = null;
115
20
  const PAGE_SETTINGS_KEY = `dx-${name}-pager`;
116
- const pagedItems: Ref<PagedItems | null> = shallowRef(null);
117
- const activeFilter: Ref<ListControlsFilter> = ref({});
21
+ const pagedItems = shallowRef<PagedItems | null>(null);
22
+ const activeFilter = ref<ListControlsFilter>({});
118
23
  const globalFilter = ref({});
119
24
  const showFilters = ref(false);
120
- const selectedRows: ShallowRef<ActionTargetItem[]> = shallowRef([]);
25
+ const selectedRows = shallowRef<ActionTargetItem[]>([]);
121
26
  const isLoadingList = ref(false);
122
27
  const isLoadingSummary = ref(false);
123
- const summary: ShallowRef<object | null> = shallowRef(null);
28
+ const summary = shallowRef<AnyObject | null>(null);
124
29
 
125
30
  // The active ad for viewing / editing
126
- const activeItem: ShallowRef<ActionTargetItem | null> = shallowRef(null);
31
+ const activeItem = shallowRef<ActionTargetItem | null>(null);
127
32
  // Controls the active panel (ie: tab) if rendering a panels drawer or similar
128
- const activePanel: ShallowRef<string> = shallowRef("");
33
+ const activePanel = shallowRef<string>("");
129
34
 
130
- // Filter fields are the field values available for the currently applied filter on Creative Groups
35
+ // Field options are the lists of field values available given the applied filter on the list query. These are used for drop-downs / options in forms, filters, etc.
131
36
  // (ie: all states available under the current filter)
132
- const filterFieldOptions: Ref<AnyObject> = ref({});
37
+ const fieldOptions = ref<AnyObject>({});
133
38
  const isLoadingFilters = ref(false);
134
39
 
135
40
  const filterActiveCount = computed(() => Object.keys(activeFilter.value).filter(key => activeFilter.value[key] !== undefined).length);
@@ -163,7 +68,7 @@ export function useListControls(name: string, options: ListControlsOptions) {
163
68
  watch(selectedRows, loadSummary);
164
69
 
165
70
  if (options.refreshFilters) {
166
- watch(activeFilter, loadFilterFieldOptions);
71
+ watch(activeFilter, loadFieldOptions);
167
72
  }
168
73
 
169
74
  async function loadList() {
@@ -185,11 +90,15 @@ export function useListControls(name: string, options: ListControlsOptions) {
185
90
  isLoadingSummary.value = false;
186
91
  }
187
92
 
93
+ async function loadListAndSummary() {
94
+ await Promise.all([loadList(), loadSummary()]);
95
+ }
96
+
188
97
  /**
189
98
  * Gets the field options for the given field name.
190
99
  */
191
- function getFieldOptions(field: string) {
192
- return filterFieldOptions.value[field] || [];
100
+ function getFieldOptions(field: string): any[] {
101
+ return fieldOptions.value[field] || [];
193
102
  }
194
103
 
195
104
  /**
@@ -197,20 +106,20 @@ export function useListControls(name: string, options: ListControlsOptions) {
197
106
  *
198
107
  * @returns {Promise<void>}
199
108
  */
200
- async function loadFilterFieldOptions() {
201
- if (!options.routes.filterFieldOptions || !isInitialized) return;
109
+ async function loadFieldOptions() {
110
+ if (!options.routes.fieldOptions || !isInitialized) return;
202
111
  isLoadingFilters.value = true;
203
- filterFieldOptions.value = await options.routes.filterFieldOptions(activeFilter.value) || {};
112
+ fieldOptions.value = await options.routes.fieldOptions(activeFilter.value) || {};
204
113
  isLoadingFilters.value = false;
205
114
  }
206
115
 
207
116
  /**
208
117
  * Watches for a filter URL parameter and applies the filter if it is set.
209
118
  */
210
- function applyFilterFromUrl(url: string, filterFields: Ref<FilterGroup[]> | null = null) {
119
+ function applyFilterFromUrl(url: string, filterGroups: Ref<FilterGroup[]> | null = null) {
211
120
  if (options.urlPattern && url.match(options.urlPattern)) {
212
121
  // A flat list of valid filterable field names
213
- const validFilterKeys = filterFields?.value?.map(group => group.fields.map(field => field.name)).flat();
122
+ const validFilterKeys = filterGroups?.value?.map(group => group.fields.map(field => field.name)).flat();
214
123
 
215
124
  const urlFilter = getFilterFromUrl(url, validFilterKeys);
216
125
 
@@ -231,7 +140,6 @@ export function useListControls(name: string, options: ListControlsOptions) {
231
140
  if (Array.isArray(items)) {
232
141
  data = items;
233
142
  meta = { total: items.length };
234
-
235
143
  } else if (items.data) {
236
144
  data = items.data;
237
145
  meta = items.meta;
@@ -243,11 +151,11 @@ export function useListControls(name: string, options: ListControlsOptions) {
243
151
  }
244
152
 
245
153
  // Add a reactive isSaving property to each item (for performance reasons in checking saving state)
246
- data = data.map((item) => {
154
+ data = data.map((item: ActionTargetItem) => {
155
+ item.isSaving = item.isSaving === undefined ? false : item.isSaving;
247
156
  // We want to keep the isSaving state if it is already set, as optimizations prevent reloading the
248
157
  // components, and therefore reactivity is not responding to the new isSaving state
249
- const oldItem = pagedItems.value?.data?.find(i => i.id === item.id);
250
- return { ...item, isSaving: oldItem?.isSaving || ref(false) };
158
+ return storeObject(item);
251
159
  });
252
160
 
253
161
  pagedItems.value = { data, meta };
@@ -276,30 +184,17 @@ export function useListControls(name: string, options: ListControlsOptions) {
276
184
  }
277
185
 
278
186
  /**
279
- * Updates a row in the paged items list with the new item data. Uses the item's id to find the row.
280
- *
281
- * @param updatedItem
187
+ * Clears the selected rows in the list.
282
188
  */
283
- function setItemInList(updatedItem: ActionTargetItem) {
284
- if (updatedItem && updatedItem.id) {
285
- const data = pagedItems.value?.data?.map(item => (item.id === updatedItem.id && (item.updated_at === null || item.updated_at <= updatedItem.updated_at)) ? updatedItem : item);
286
- setPagedItems({
287
- data,
288
- meta: { total: pagedItems.value?.meta?.total || 0 }
289
- });
290
-
291
- // Update the active item as well if it is set
292
- if (activeItem.value?.id === updatedItem.id) {
293
- activeItem.value = { ...activeItem.value, ...updatedItem };
294
- }
295
- }
189
+ function clearSelectedRows() {
190
+ selectedRows.value = [];
296
191
  }
297
192
 
298
193
  /**
299
194
  * Loads more items into the list.
300
195
  */
301
- async function loadMore(index: number, perPage = undefined) {
302
- if (!options.routes.more) return;
196
+ async function loadMore(index: number, perPage: number | undefined = undefined) {
197
+ if (!options.routes.more) return false;
303
198
 
304
199
  const newItems = await options.routes.more({
305
200
  page: index + 1,
@@ -322,7 +217,7 @@ export function useListControls(name: string, options: ListControlsOptions) {
322
217
  * Refreshes the list, summary, and filter field options.
323
218
  */
324
219
  async function refreshAll() {
325
- return Promise.all([loadList(), loadSummary(), loadFilterFieldOptions(), getActiveItemDetails()]);
220
+ return Promise.all([loadList(), loadSummary(), loadFieldOptions(), getActiveItemDetails()]);
326
221
  }
327
222
 
328
223
  /**
@@ -362,7 +257,7 @@ export function useListControls(name: string, options: ListControlsOptions) {
362
257
  }
363
258
 
364
259
  if (!isLoadingFilters.value) {
365
- loadFilterFieldOptions();
260
+ loadFieldOptions();
366
261
  }
367
262
  }, 1);
368
263
  }
@@ -389,13 +284,12 @@ export function useListControls(name: string, options: ListControlsOptions) {
389
284
 
390
285
  const result = await options.routes.details(activeItem.value);
391
286
 
392
- // Only set the ad details if we are the response for the currently loaded item
393
- // NOTE: race conditions might allow the finished loading item to be different to the currently
394
- // requested item
395
- if (result?.id === activeItem.value?.id) {
396
- const loadedItem = pagedItems.value?.data?.find((i: ActionTargetItem) => i.id === result.id);
397
- activeItem.value = { ...result, isSaving: loadedItem?.isSaving || ref(false) };
287
+ if (!result || !result.__type || !result.id) {
288
+ return console.error("Invalid response from details route: All responses must include a __type and id field. result =", result);
398
289
  }
290
+
291
+ // Reassign the active item to the store object to ensure reactivity
292
+ activeItem.value = storeObject(result);
399
293
  }
400
294
 
401
295
  // Whenever the active item changes, fill the additional item details
@@ -411,16 +305,32 @@ export function useListControls(name: string, options: ListControlsOptions) {
411
305
  /**
412
306
  * Opens the item's form with the given item and tab
413
307
  */
414
- function activatePanel(item: ActionTargetItem | null, panel: string) {
415
- activeItem.value = item;
308
+ function activatePanel(item: ActionTargetItem | null, panel: string = "") {
309
+ // If we're already on the correct item and panel, don't do anything
310
+ if (item?.id === activeItem.value?.id && panel === activePanel.value) return;
311
+
312
+ setActiveItem(item);
416
313
  activePanel.value = panel;
314
+
315
+ // Push vue router change /:id/:panel
316
+ if (vueRoute && vueRouter && item?.id) {
317
+ vueRouter.push({
318
+ name: Array.isArray(vueRoute.name) ? vueRoute.name[0] : vueRoute.name,
319
+ params: { id: item.id, panel },
320
+ replace: true
321
+ });
322
+ }
417
323
  }
418
324
 
419
325
  /**
420
326
  * Sets the currently active item in the list.
421
327
  */
422
328
  function setActiveItem(item: ActionTargetItem | null) {
423
- activeItem.value = item;
329
+ activeItem.value = item ? storeObject(item) : item;
330
+
331
+ if (!item?.id) {
332
+ vueRouter?.push({ name: vueRoute?.name || "home" });
333
+ }
424
334
  }
425
335
 
426
336
  /**
@@ -465,11 +375,11 @@ export function useListControls(name: string, options: ListControlsOptions) {
465
375
  /**
466
376
  * Sets the active filter to the given filter.
467
377
  */
468
- function setActiveFilter(filter: ListControlsFilter) {
469
- activeFilter.value = filter;
378
+ function setActiveFilter(filter?: ListControlsFilter) {
379
+ activeFilter.value = filter || {};
470
380
  }
471
381
 
472
- async function exportList(filter: object) {
382
+ async function exportList(filter?: ListControlsFilter) {
473
383
  return options.routes.export(filter);
474
384
  }
475
385
 
@@ -477,6 +387,33 @@ export function useListControls(name: string, options: ListControlsOptions) {
477
387
  function initialize() {
478
388
  isInitialized = true;
479
389
  loadSettings();
390
+
391
+ // Setup Vue Router handling
392
+ vueRoute = useRoute();
393
+ vueRouter = useRouter();
394
+ /**
395
+ * Watch the id params in the route and set the active item to the item with the given id.
396
+ */
397
+ if (options.routes.details) {
398
+ const { params, meta } = vueRoute;
399
+
400
+ const controlRouteName = vueRoute.name;
401
+ vueRouter.afterEach((to) => {
402
+ if (to.name === controlRouteName) {
403
+ setPanelFromRoute(to.params, to.meta);
404
+ }
405
+ });
406
+
407
+ setPanelFromRoute(params, meta);
408
+ }
409
+ }
410
+
411
+ function setPanelFromRoute(params: RouteParams, meta: AnyObject) {
412
+ const id = Array.isArray(params?.id) ? params.id[0] : params?.id;
413
+ if (id && meta.type) {
414
+ const panel = Array.isArray(params?.panel) ? params.panel[0] : params?.panel;
415
+ activatePanel({ id, __type: "" + meta.type }, panel || activePanel.value || "");
416
+ }
480
417
  }
481
418
 
482
419
  return {
@@ -489,7 +426,6 @@ export function useListControls(name: string, options: ListControlsOptions) {
489
426
  filterActiveCount,
490
427
  showFilters,
491
428
  summary,
492
- filterFieldOptions,
493
429
  selectedRows,
494
430
  isLoadingList,
495
431
  isLoadingFilters,
@@ -501,12 +437,15 @@ export function useListControls(name: string, options: ListControlsOptions) {
501
437
 
502
438
  // Actions
503
439
  initialize,
504
- loadSummary,
505
440
  resetPaging,
506
441
  setPagination,
507
442
  setSelectedRows,
443
+ clearSelectedRows,
508
444
  loadList,
445
+ loadSummary,
446
+ loadListAndSummary,
509
447
  loadMore,
448
+ getActiveItemDetails,
510
449
  refreshAll,
511
450
  exportList,
512
451
  setActiveItem,
@@ -514,7 +453,6 @@ export function useListControls(name: string, options: ListControlsOptions) {
514
453
  activatePanel,
515
454
  setActiveFilter,
516
455
  applyFilterFromUrl,
517
- setItemInList,
518
456
  getFieldOptions
519
457
  };
520
458
  }
@@ -1,7 +1,6 @@
1
- import { ListControlsPagination } from "src/components/ActionTable/listControls";
2
- import { TableColumn } from "src/components/ActionTable/tableColumns";
3
1
  import { onMounted, Ref } from "vue";
4
- import { AnyObject, getUrlParam } from "../../helpers";
2
+ import { getUrlParam } from "../../helpers";
3
+ import { AnyObject, ListControlsPagination, TableColumn } from "../../types";
5
4
 
6
5
  export function registerStickyScrolling(tableRef: Ref) {
7
6
  onMounted(() => {
@@ -1,82 +1,58 @@
1
1
  import { computed, ref, watch } from "vue";
2
2
  import { getItem, setItem } from "../../helpers";
3
-
4
- export interface TableColumn {
5
- actionMenu?: object,
6
- align?: string,
7
- category?: string,
8
- class?: string | object,
9
- field: string,
10
- format?: Function,
11
- innerClass?: string | object,
12
- style?: string | object,
13
- headerStyle?: string | object,
14
- isSavingRow?: boolean | Function,
15
- label: string,
16
- maxWidth?: number,
17
- minWidth?: number,
18
- name: string,
19
- onClick?: Function,
20
- required?: boolean,
21
- resizeable?: boolean,
22
- sortable?: boolean,
23
- sortBy?: string,
24
- sortByExpression?: string,
25
- titleColumns?: Function,
26
- vnode?: Function,
27
- }
3
+ import { TableColumn } from "../../types";
28
4
 
29
5
  export function useTableColumns(name: string, columns: TableColumn[]) {
30
- const COLUMN_ORDER_KEY = `${name}-column-order`;
31
- const VISIBLE_COLUMNS_KEY = `${name}-visible-columns`;
32
- const TITLE_COLUMNS_KEY = `${name}-title-columns`;
33
-
34
- // The list that defines the order the columns should appear in
35
- const columnOrder = ref(getItem(COLUMN_ORDER_KEY) || []);
36
-
37
- // Manages visible columns on the table
38
- const hiddenColumnNames = ref(getItem(VISIBLE_COLUMNS_KEY, []));
39
-
40
- // Title columns will have their name appear on the first column of the table as part of the records' title
41
- const titleColumnNames = ref(getItem(TITLE_COLUMNS_KEY, []));
42
-
43
- // Columns that should be locked to the left side of the table
44
- const lockedColumns = computed(() => orderedColumns.value.slice(0, 1));
45
-
46
- // The resolved list of columns in the order they should appear in
47
- const orderedColumns = computed(() => [...columns].sort((a, b) => {
48
- const aIndex = columnOrder.value.indexOf(a.name);
49
- const bIndex = columnOrder.value.indexOf(b.name);
50
- return aIndex === -1 ? 1 : bIndex === -1 ? -1 : aIndex - bIndex;
51
- }));
52
-
53
- // The ordered list of columns. The ordering of this list is editable and will be stored in localStorage
54
- const sortableColumns = computed({
55
- get() {
56
- return orderedColumns.value.slice(1);
57
- },
58
- set(newColumns) {
59
- columnOrder.value = [...lockedColumns.value.map(c => c.name), ...newColumns.map(c => c.name)];
60
- setItem(COLUMN_ORDER_KEY, columnOrder.value);
61
- }
62
- });
63
-
64
- // The list of columns that are visible. To edit the visible columns, edit the hiddenColumnNames list
65
- const visibleColumns = computed(() => orderedColumns.value.filter(c => !hiddenColumnNames.value.includes(c.name)));
66
-
67
- // The list of columns that should be included in the title of a row
68
- const orderedTitleColumns = computed(() => orderedColumns.value.filter(c => titleColumnNames.value.includes(c.name)));
69
-
70
- // Save changes to the list of hidden columns in localStorage
71
- watch(() => hiddenColumnNames.value, () => setItem(VISIBLE_COLUMNS_KEY, hiddenColumnNames.value));
72
- watch(() => titleColumnNames.value, () => setItem(TITLE_COLUMNS_KEY, titleColumnNames.value));
73
-
74
- return {
75
- sortableColumns,
76
- lockedColumns,
77
- visibleColumns,
78
- hiddenColumnNames,
79
- titleColumnNames,
80
- orderedTitleColumns
81
- };
6
+ const COLUMN_ORDER_KEY = `${name}-column-order`;
7
+ const VISIBLE_COLUMNS_KEY = `${name}-visible-columns`;
8
+ const TITLE_COLUMNS_KEY = `${name}-title-columns`;
9
+
10
+ // The list that defines the order the columns should appear in
11
+ const columnOrder = ref(getItem(COLUMN_ORDER_KEY) || []);
12
+
13
+ // Manages visible columns on the table
14
+ const hiddenColumnNames = ref(getItem(VISIBLE_COLUMNS_KEY, []));
15
+
16
+ // Title columns will have their name appear on the first column of the table as part of the records' title
17
+ const titleColumnNames = ref(getItem(TITLE_COLUMNS_KEY, []));
18
+
19
+ // Columns that should be locked to the left side of the table
20
+ const lockedColumns = computed(() => orderedColumns.value.slice(0, 1));
21
+
22
+ // The resolved list of columns in the order they should appear in
23
+ const orderedColumns = computed(() => [...columns].sort((a, b) => {
24
+ const aIndex = columnOrder.value.indexOf(a.name);
25
+ const bIndex = columnOrder.value.indexOf(b.name);
26
+ return aIndex === -1 ? 1 : bIndex === -1 ? -1 : aIndex - bIndex;
27
+ }));
28
+
29
+ // The ordered list of columns. The ordering of this list is editable and will be stored in localStorage
30
+ const sortableColumns = computed({
31
+ get() {
32
+ return orderedColumns.value.slice(1);
33
+ },
34
+ set(newColumns) {
35
+ columnOrder.value = [...lockedColumns.value.map(c => c.name), ...newColumns.map(c => c.name)];
36
+ setItem(COLUMN_ORDER_KEY, columnOrder.value);
37
+ }
38
+ });
39
+
40
+ // The list of columns that are visible. To edit the visible columns, edit the hiddenColumnNames list
41
+ const visibleColumns = computed(() => orderedColumns.value.filter(c => !hiddenColumnNames.value.includes(c.name)));
42
+
43
+ // The list of columns that should be included in the title of a row
44
+ const orderedTitleColumns = computed(() => orderedColumns.value.filter(c => titleColumnNames.value.includes(c.name)));
45
+
46
+ // Save changes to the list of hidden columns in localStorage
47
+ watch(() => hiddenColumnNames.value, () => setItem(VISIBLE_COLUMNS_KEY, hiddenColumnNames.value));
48
+ watch(() => titleColumnNames.value, () => setItem(TITLE_COLUMNS_KEY, titleColumnNames.value));
49
+
50
+ return {
51
+ sortableColumns,
52
+ lockedColumns,
53
+ visibleColumns,
54
+ hiddenColumnNames,
55
+ titleColumnNames,
56
+ orderedTitleColumns
57
+ };
82
58
  }
@@ -2,7 +2,7 @@
2
2
  <div class="flex space-x-2">
3
3
  <template v-if="type === 'SINGLE_FILE'">
4
4
  <FilePreview
5
- :image="value"
5
+ :file="value"
6
6
  class="w-24"
7
7
  />
8
8
  </template>
@@ -10,7 +10,7 @@
10
10
  <FilePreview
11
11
  v-for="file in value"
12
12
  :key="'file-' + file.id"
13
- :image="file"
13
+ :file="file"
14
14
  class="w-24 mb-2"
15
15
  />
16
16
  </template>
@@ -27,34 +27,34 @@ import { fCurrency, fDate, fLocalizedDateTime, fNumber } from "../../helpers";
27
27
  import { FilePreview } from "../Utility";
28
28
 
29
29
  const props = defineProps({
30
- type: {
31
- type: String,
32
- required: true
33
- },
34
- value: {
35
- type: [Number, String, Array, Object, Boolean],
36
- default: null
37
- }
30
+ type: {
31
+ type: String,
32
+ required: true
33
+ },
34
+ value: {
35
+ type: [Number, String, Array, Object, Boolean],
36
+ default: null
37
+ }
38
38
  });
39
39
 
40
40
  function format(value) {
41
- if (value === null || value === "" || value === undefined) {
42
- return "";
43
- }
41
+ if (value === null || value === "" || value === undefined) {
42
+ return "";
43
+ }
44
44
 
45
- switch (props.type) {
46
- case "NUMBER":
47
- return fNumber(value);
48
- case "CURRENCY":
49
- return fCurrency(value);
50
- case "DATE":
51
- return fDate(value);
52
- case "DATETIME":
53
- return fLocalizedDateTime(value);
54
- case "BOOLEAN":
55
- return value ? "Yes" : "No";
56
- }
45
+ switch (props.type) {
46
+ case "NUMBER":
47
+ return fNumber(value);
48
+ case "CURRENCY":
49
+ return fCurrency(value);
50
+ case "DATE":
51
+ return fDate(value);
52
+ case "DATETIME":
53
+ return fLocalizedDateTime(value);
54
+ case "BOOLEAN":
55
+ return value ? "Yes" : "No";
56
+ }
57
57
 
58
- return value;
58
+ return value;
59
59
  }
60
60
  </script>