quasar-ui-danx 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. package/dist/danx.es.js +7127 -6615
  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 +28 -41
  8. package/src/components/ActionTable/Columns/ActionTableColumn.vue +19 -18
  9. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +6 -6
  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/MultiFileField.vue +48 -44
  16. package/src/components/ActionTable/Form/Fields/SelectField.vue +24 -38
  17. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +28 -33
  18. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +15 -15
  19. package/src/components/ActionTable/Form/Fields/SliderNumberField.vue +45 -0
  20. package/src/components/ActionTable/Form/Fields/TextField.vue +47 -66
  21. package/src/components/ActionTable/Form/Fields/index.ts +2 -0
  22. package/src/components/ActionTable/Form/RenderedForm.vue +50 -9
  23. package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +17 -0
  24. package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
  25. package/src/components/ActionTable/Form/index.ts +1 -0
  26. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +16 -15
  27. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +6 -6
  28. package/src/components/ActionTable/listControls.ts +104 -166
  29. package/src/components/ActionTable/listHelpers.ts +2 -3
  30. package/src/components/ActionTable/tableColumns.ts +3 -27
  31. package/src/components/AuditHistory/AuditHistoryItemValue.vue +26 -26
  32. package/src/components/PanelsDrawer/PanelsDrawer.vue +17 -4
  33. package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +6 -11
  34. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +20 -20
  35. package/src/components/Utility/Dialogs/ConfirmActionDialog.vue +39 -0
  36. package/src/components/Utility/Dialogs/ConfirmDialog.vue +10 -24
  37. package/src/components/Utility/Dialogs/DialogLayout.vue +10 -28
  38. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +42 -36
  39. package/src/components/Utility/Dialogs/index.ts +1 -0
  40. package/src/components/Utility/Files/FilePreview.vue +76 -73
  41. package/src/components/Utility/Layouts/ContentDrawer.vue +24 -31
  42. package/src/components/Utility/Tools/ActionVnode.vue +3 -3
  43. package/src/components/Utility/Tools/RenderVnode.vue +1 -1
  44. package/src/components/Utility/Transitions/MaxHeightTransition.vue +26 -0
  45. package/src/components/Utility/Transitions/index.ts +1 -0
  46. package/src/config/index.ts +36 -31
  47. package/src/helpers/FileUpload.ts +295 -297
  48. package/src/helpers/FlashMessages.ts +80 -71
  49. package/src/helpers/actions.ts +102 -82
  50. package/src/helpers/download.ts +189 -189
  51. package/src/helpers/downloadPdf.ts +55 -52
  52. package/src/helpers/formats.ts +151 -109
  53. package/src/helpers/index.ts +2 -0
  54. package/src/helpers/multiFileUpload.ts +72 -58
  55. package/src/helpers/objectStore.ts +52 -0
  56. package/src/helpers/request.ts +70 -51
  57. package/src/helpers/routes.ts +29 -0
  58. package/src/helpers/storage.ts +7 -3
  59. package/src/helpers/utils.ts +47 -29
  60. package/src/styles/quasar-reset.scss +16 -1
  61. package/src/styles/themes/danx/dialogs.scss +4 -0
  62. package/src/types/actions.d.ts +43 -0
  63. package/src/types/config.d.ts +15 -0
  64. package/src/types/controls.d.ts +99 -0
  65. package/src/types/dialogs.d.ts +32 -0
  66. package/src/types/fields.d.ts +20 -0
  67. package/src/types/files.d.ts +54 -0
  68. package/src/types/formats.d.ts +4 -0
  69. package/src/{components/ActionTable/Form/form.d.ts → types/forms.d.ts} +6 -0
  70. package/src/types/index.d.ts +12 -0
  71. package/src/types/requests.d.ts +13 -0
  72. package/src/types/shared.d.ts +15 -0
  73. package/src/types/tables.d.ts +27 -0
  74. package/types/index.d.ts +1 -1
  75. /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,30 +1,6 @@
1
- import { computed, ref, VNode, watch } from "vue";
2
- import { ActionOptions, getItem, setItem } from "../../helpers";
3
-
4
- export interface TableColumn {
5
- actionMenu?: ActionOptions[],
6
- align?: string,
7
- category?: string,
8
- class?: string | object,
9
- field: string,
10
- format?: (value: any, options: any) => any,
11
- innerClass?: string | object,
12
- style?: string | object,
13
- headerStyle?: string | object,
14
- isSavingRow?: boolean | (() => boolean),
15
- label: string,
16
- maxWidth?: number,
17
- minWidth?: number,
18
- name: string,
19
- onClick?: (target: any) => void,
20
- required?: boolean,
21
- resizeable?: boolean,
22
- sortable?: boolean,
23
- sortBy?: string,
24
- sortByExpression?: string,
25
- titleColumns?: () => string[],
26
- vnode?: () => VNode,
27
- }
1
+ import { computed, ref, watch } from "vue";
2
+ import { getItem, setItem } from "../../helpers";
3
+ import { TableColumn } from "../../types";
28
4
 
29
5
  export function useTableColumns(name: string, columns: TableColumn[]) {
30
6
  const COLUMN_ORDER_KEY = `${name}-column-order`;
@@ -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>
@@ -6,6 +6,7 @@
6
6
  content-class="h-full"
7
7
  class="dx-panels-drawer"
8
8
  title=""
9
+ no-route-dismiss
9
10
  @update:show="$emit('close')"
10
11
  >
11
12
  <div class="flex flex-col flex-nowrap h-full">
@@ -33,15 +34,17 @@
33
34
  <div class="dx-panels-drawer-body flex-grow overflow-hidden h-full">
34
35
  <div class="flex items-stretch flex-nowrap h-full">
35
36
  <PanelsDrawerTabs
37
+ :key="'pd-tabs:' + activeItem.id"
36
38
  v-model="activePanel"
37
39
  :class="tabsClass"
38
40
  :panels="panels"
39
41
  @update:model-value="$emit('update:model-value', $event)"
40
42
  />
41
43
  <PanelsDrawerPanels
44
+ :key="'pd-panels:' + activeItem.id"
42
45
  :panels="panels"
43
46
  :active-panel="activePanel"
44
- :class="panelsClass"
47
+ :class="activePanelOptions?.class || panelsClass"
45
48
  />
46
49
  <div
47
50
  v-if="$slots['right-sidebar']"
@@ -55,16 +58,17 @@
55
58
  </ContentDrawer>
56
59
  </template>
57
60
  <script setup lang="ts">
58
- import { ref, watch } from "vue";
61
+ import { computed, onMounted, ref, watch } from "vue";
59
62
  import { XIcon as CloseIcon } from "../../svg";
60
- import { ActionPanel } from "../ActionTable";
63
+ import { ActionPanel, ActionTargetItem } from "../../types";
61
64
  import { ContentDrawer } from "../Utility";
62
65
  import PanelsDrawerPanels from "./PanelsDrawerPanels";
63
66
  import PanelsDrawerTabs from "./PanelsDrawerTabs";
64
67
 
65
68
  export interface Props {
66
69
  title?: string,
67
- modelValue?: string,
70
+ modelValue?: string | number,
71
+ activeItem?: ActionTargetItem;
68
72
  tabsClass?: string | object,
69
73
  panelsClass?: string | object,
70
74
  panels: ActionPanel[]
@@ -74,10 +78,19 @@ defineEmits(["update:model-value", "close"]);
74
78
  const props = withDefaults(defineProps<Props>(), {
75
79
  title: "",
76
80
  modelValue: null,
81
+ activeItem: null,
77
82
  tabsClass: "w-[13.5rem]",
78
83
  panelsClass: "w-[35.5rem]"
79
84
  });
80
85
 
81
86
  const activePanel = ref(props.modelValue);
87
+ const activePanelOptions = computed(() => props.panels.find((panel) => panel.name === activePanel.value));
82
88
  watch(() => props.modelValue, (value) => activePanel.value = value);
89
+
90
+ onMounted(() => {
91
+ // Resolve the default panel if a panel has not been selected
92
+ if (!activePanel.value && props.panels.length) {
93
+ activePanel.value = props.panels[0].name;
94
+ }
95
+ });
83
96
  </script>
@@ -16,17 +16,12 @@
16
16
  </QTabPanels>
17
17
  </template>
18
18
 
19
- <script setup>
19
+ <script setup lang="ts">
20
+ import { ActionPanel } from "../../types";
20
21
  import { RenderVnode } from "../Utility";
21
22
 
22
- defineProps({
23
- activePanel: {
24
- type: String,
25
- default: null
26
- },
27
- panels: {
28
- type: Array,
29
- required: true
30
- }
31
- });
23
+ defineProps<{
24
+ activePanel?: string | number,
25
+ panels: ActionPanel[]
26
+ }>();
32
27
  </script>