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.
- package/dist/danx.es.js +7234 -6741
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +11 -5
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -1
- package/src/components/ActionTable/ActionTable.vue +31 -43
- package/src/components/ActionTable/Columns/ActionTableColumn.vue +19 -18
- package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +15 -14
- package/src/components/ActionTable/Filters/{FilterFieldList.vue → FilterList.vue} +26 -26
- package/src/components/ActionTable/Filters/FilterableField.vue +28 -31
- package/src/components/ActionTable/Filters/index.ts +2 -2
- package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +71 -0
- package/src/components/ActionTable/Form/Fields/FieldLabel.vue +8 -13
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +34 -33
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +48 -44
- package/src/components/ActionTable/Form/Fields/NumberField.vue +60 -59
- package/src/components/ActionTable/Form/Fields/SelectField.vue +124 -138
- package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +28 -33
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +15 -15
- package/src/components/ActionTable/Form/Fields/SliderNumberField.vue +45 -0
- package/src/components/ActionTable/Form/Fields/TextField.vue +47 -66
- package/src/components/ActionTable/Form/Fields/index.ts +2 -0
- package/src/components/ActionTable/Form/RenderedForm.vue +50 -13
- package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +17 -0
- package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
- package/src/components/ActionTable/Form/index.ts +1 -0
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +22 -16
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +11 -11
- package/src/components/ActionTable/listControls.ts +104 -166
- package/src/components/ActionTable/listHelpers.ts +2 -3
- package/src/components/ActionTable/tableColumns.ts +53 -77
- package/src/components/AuditHistory/AuditHistoryItemValue.vue +26 -26
- package/src/components/PanelsDrawer/PanelsDrawer.vue +17 -4
- package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +6 -11
- package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +20 -20
- package/src/components/Utility/Dialogs/ConfirmActionDialog.vue +39 -0
- package/src/components/Utility/Dialogs/ConfirmDialog.vue +57 -117
- package/src/components/Utility/Dialogs/DialogLayout.vue +77 -0
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +42 -36
- package/src/components/Utility/Dialogs/InfoDialog.vue +40 -80
- package/src/components/Utility/Dialogs/index.ts +1 -0
- package/src/components/Utility/Files/FilePreview.vue +76 -73
- package/src/components/Utility/Layouts/ContentDrawer.vue +24 -31
- package/src/components/Utility/Tools/ActionVnode.vue +3 -3
- package/src/components/Utility/Tools/RenderVnode.vue +20 -11
- package/src/components/Utility/Transitions/MaxHeightTransition.vue +26 -0
- package/src/components/Utility/Transitions/index.ts +1 -0
- package/src/config/index.ts +36 -31
- package/src/helpers/FileUpload.ts +295 -297
- package/src/helpers/FlashMessages.ts +80 -71
- package/src/helpers/actions.ts +102 -82
- package/src/helpers/download.ts +189 -189
- package/src/helpers/downloadPdf.ts +55 -52
- package/src/helpers/formats.ts +151 -109
- package/src/helpers/index.ts +2 -0
- package/src/helpers/multiFileUpload.ts +72 -58
- package/src/helpers/objectStore.ts +52 -0
- package/src/helpers/request.ts +70 -51
- package/src/helpers/routes.ts +29 -0
- package/src/helpers/storage.ts +7 -3
- package/src/helpers/utils.ts +47 -29
- package/src/styles/quasar-reset.scss +94 -68
- package/src/styles/themes/danx/dialogs.scss +47 -0
- package/src/styles/themes/danx/forms.scss +18 -0
- package/src/styles/themes/danx/index.scss +4 -0
- package/src/types/actions.d.ts +43 -0
- package/src/types/config.d.ts +15 -0
- package/src/types/controls.d.ts +99 -0
- package/src/types/dialogs.d.ts +32 -0
- package/src/types/fields.d.ts +20 -0
- package/src/types/files.d.ts +54 -0
- package/src/types/formats.d.ts +4 -0
- package/src/{components/ActionTable/Form/form.d.ts → types/forms.d.ts} +6 -0
- package/src/types/index.d.ts +12 -0
- package/src/types/requests.d.ts +13 -0
- package/src/types/shared.d.ts +15 -0
- package/src/types/tables.d.ts +27 -0
- package/types/index.d.ts +1 -1
- /package/src/components/ActionTable/Filters/{FilterFieldItem.vue → FilterItem.vue} +0 -0
@@ -1,135 +1,40 @@
|
|
1
|
-
import { computed,
|
2
|
-
import {
|
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
|
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
|
117
|
-
const activeFilter
|
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
|
25
|
+
const selectedRows = shallowRef<ActionTargetItem[]>([]);
|
121
26
|
const isLoadingList = ref(false);
|
122
27
|
const isLoadingSummary = ref(false);
|
123
|
-
const summary
|
28
|
+
const summary = shallowRef<AnyObject | null>(null);
|
124
29
|
|
125
30
|
// The active ad for viewing / editing
|
126
|
-
const activeItem
|
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
|
33
|
+
const activePanel = shallowRef<string>("");
|
129
34
|
|
130
|
-
//
|
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
|
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,
|
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
|
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
|
201
|
-
if (!options.routes.
|
109
|
+
async function loadFieldOptions() {
|
110
|
+
if (!options.routes.fieldOptions || !isInitialized) return;
|
202
111
|
isLoadingFilters.value = true;
|
203
|
-
|
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,
|
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 =
|
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
|
-
|
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
|
-
*
|
280
|
-
*
|
281
|
-
* @param updatedItem
|
187
|
+
* Clears the selected rows in the list.
|
282
188
|
*/
|
283
|
-
function
|
284
|
-
|
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(),
|
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
|
-
|
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
|
-
|
393
|
-
|
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
|
-
|
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
|
469
|
-
activeFilter.value = filter;
|
378
|
+
function setActiveFilter(filter?: ListControlsFilter) {
|
379
|
+
activeFilter.value = filter || {};
|
470
380
|
}
|
471
381
|
|
472
|
-
async function exportList(filter
|
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 {
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
:
|
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
|
-
:
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
41
|
+
if (value === null || value === "" || value === undefined) {
|
42
|
+
return "";
|
43
|
+
}
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
58
|
+
return value;
|
59
59
|
}
|
60
60
|
</script>
|