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
@@ -3,86 +3,95 @@ import { watch } from "vue";
|
|
3
3
|
import { danxOptions } from "../config";
|
4
4
|
|
5
5
|
export class FlashMessages {
|
6
|
-
|
6
|
+
static notify: (options: QNotifyCreateOptions) => void;
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
8
|
+
static PROP_DEFINITIONS = {
|
9
|
+
successMsg: {
|
10
|
+
type: String,
|
11
|
+
default: ""
|
12
|
+
},
|
13
|
+
errorMsg: {
|
14
|
+
type: String,
|
15
|
+
default: ""
|
16
|
+
},
|
17
|
+
warningMsg: {
|
18
|
+
type: String,
|
19
|
+
default: ""
|
20
|
+
}
|
21
|
+
};
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
static enable(msgProps: { successMsg?: string, errorMsg?: string, warningMsg?: string }) {
|
24
|
+
FlashMessages.success(msgProps.successMsg);
|
25
|
+
FlashMessages.error(msgProps.errorMsg);
|
26
|
+
FlashMessages.warning(msgProps.warningMsg);
|
27
|
+
watch(() => msgProps.successMsg, msg => FlashMessages.success(msg));
|
28
|
+
watch(() => msgProps.errorMsg, msg => FlashMessages.error(msg));
|
29
|
+
watch(() => msgProps.warningMsg, msg => FlashMessages.warning(msg));
|
30
|
+
}
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
position: "top",
|
39
|
-
closeBtn: "X",
|
40
|
-
...danxOptions.value.flashMessages.default,
|
41
|
-
...options
|
42
|
-
});
|
43
|
-
}
|
44
|
-
}
|
32
|
+
static formatMessage(message: string) {
|
33
|
+
message = message.replace(/\r\n|\r|\n/g, "<br/>");
|
34
|
+
message = message.replace(/\t/g, " ");
|
35
|
+
// Replace <SomeComponent ... > with <b>SomeComponent ... </b>
|
36
|
+
return message.replace(/<(\w+)([^>]*)>/g, "<b>$1$2</b>");
|
37
|
+
}
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
icon: "check",
|
50
|
-
...options,
|
51
|
-
...danxOptions.value.flashMessages.success
|
52
|
-
});
|
53
|
-
}
|
39
|
+
static send(message?: string, options: QNotifyCreateOptions = {}) {
|
40
|
+
if (message) {
|
41
|
+
message = FlashMessages.formatMessage(message);
|
54
42
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
43
|
+
FlashMessages.notify({
|
44
|
+
message,
|
45
|
+
timeout: 10000,
|
46
|
+
classes: "bg-gray-500 text-white",
|
47
|
+
position: "top",
|
48
|
+
closeBtn: "X",
|
49
|
+
...danxOptions.value.flashMessages?.default,
|
50
|
+
...options
|
51
|
+
});
|
52
|
+
}
|
53
|
+
}
|
63
54
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
55
|
+
static success(message?: string, options: QNotifyCreateOptions = {}) {
|
56
|
+
FlashMessages.send(message, {
|
57
|
+
classes: "bg-green-300 !text-green-900",
|
58
|
+
icon: "check",
|
59
|
+
...options,
|
60
|
+
...danxOptions.value.flashMessages?.success
|
61
|
+
});
|
62
|
+
}
|
72
63
|
|
73
|
-
|
74
|
-
|
75
|
-
|
64
|
+
static error(message?: string, options: QNotifyCreateOptions = {}) {
|
65
|
+
FlashMessages.send(message, {
|
66
|
+
classes: "bg-red-300 !text-red-900",
|
67
|
+
icon: "error",
|
68
|
+
...options,
|
69
|
+
...danxOptions.value.flashMessages?.error
|
70
|
+
});
|
71
|
+
}
|
76
72
|
|
77
|
-
|
78
|
-
|
79
|
-
|
73
|
+
static warning(message?: string, options: QNotifyCreateOptions = {}) {
|
74
|
+
FlashMessages.send(message, {
|
75
|
+
classes: "bg-yellow-300 !text-yellow-900",
|
76
|
+
icon: "warning",
|
77
|
+
...options,
|
78
|
+
...danxOptions.value.flashMessages?.warning
|
79
|
+
});
|
80
|
+
}
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
82
|
+
static combine(type: string, messages: string[] | { message: string, Message: string }[], options = {}) {
|
83
|
+
// @ts-expect-error - type is a string
|
84
|
+
const messageType = FlashMessages[type];
|
85
|
+
|
86
|
+
if (typeof messageType !== "function") {
|
87
|
+
throw new Error(`FlashMessages.${type} is not a function`);
|
88
|
+
}
|
89
|
+
|
90
|
+
messageType(messages.map(m => typeof m === "string" ? m : (m.message || m.Message)).join("<br/>"), {
|
91
|
+
...options,
|
92
|
+
html: true
|
93
|
+
});
|
94
|
+
}
|
86
95
|
}
|
87
96
|
|
88
97
|
export const notify = new FlashMessages();
|
package/src/helpers/actions.ts
CHANGED
@@ -1,36 +1,9 @@
|
|
1
1
|
import { useDebounceFn } from "@vueuse/core";
|
2
|
-
import {
|
2
|
+
import { uid } from "quasar";
|
3
|
+
import { isReactive, Ref, shallowRef } from "vue";
|
4
|
+
import { ActionOptions, ActionTarget, AnyObject } from "../types";
|
3
5
|
import { FlashMessages } from "./FlashMessages";
|
4
|
-
|
5
|
-
export type AnyObject = { [key: string]: any };
|
6
|
-
|
7
|
-
export type ActionTargetItem = {
|
8
|
-
id: number | string;
|
9
|
-
isSaving: Ref<boolean>;
|
10
|
-
[key: string]: any;
|
11
|
-
};
|
12
|
-
export type ActionTarget = ActionTargetItem[] | ActionTargetItem;
|
13
|
-
|
14
|
-
export interface ActionOptions {
|
15
|
-
name?: string;
|
16
|
-
label?: string;
|
17
|
-
menu?: boolean;
|
18
|
-
batch?: boolean;
|
19
|
-
category?: string;
|
20
|
-
class?: string;
|
21
|
-
debounce?: number;
|
22
|
-
trigger?: (target: ActionTarget, input: any) => Promise<any>;
|
23
|
-
vnode?: ((target: ActionTarget) => VNode) | undefined;
|
24
|
-
enabled?: (target: object) => boolean;
|
25
|
-
batchEnabled?: (targets: object[]) => boolean;
|
26
|
-
optimistic?: (action: ActionOptions, target: object, input: any) => void;
|
27
|
-
onAction?: (action: string | null | undefined, target: object, input: any) => Promise<any>;
|
28
|
-
onBatchAction?: (action: string | null | undefined, targets: object[], input: any) => Promise<any>;
|
29
|
-
onStart?: (action: ActionOptions | null, targets: ActionTarget, input: any) => boolean;
|
30
|
-
onSuccess?: (result: any, targets: ActionTarget, input: any) => any;
|
31
|
-
onError?: (result: any, targets: ActionTarget, input: any) => any;
|
32
|
-
onFinish?: (result: any, targets: ActionTarget, input: any) => any;
|
33
|
-
}
|
6
|
+
import { storeObject } from "./objectStore";
|
34
7
|
|
35
8
|
export const activeActionVnode: Ref = shallowRef(null);
|
36
9
|
|
@@ -42,48 +15,67 @@ export const activeActionVnode: Ref = shallowRef(null);
|
|
42
15
|
* @param {ActionOptions | null} globalOptions
|
43
16
|
*/
|
44
17
|
export function useActions(actions: ActionOptions[], globalOptions: ActionOptions | null = null) {
|
45
|
-
const
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
18
|
+
const namespace = uid();
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Resolve the action object based on the provided name (or return the object if the name is already an object)
|
22
|
+
*/
|
23
|
+
function getAction(action: string | ActionOptions): ActionOptions {
|
24
|
+
if (typeof action === "string") {
|
25
|
+
action = actions.find(a => a.name === action) || { name: action };
|
26
|
+
}
|
27
|
+
|
28
|
+
// If the action is already reactive, return it
|
29
|
+
if (isReactive(action) && action.__type) return action;
|
30
|
+
|
31
|
+
action = { ...globalOptions, ...action };
|
32
|
+
|
33
|
+
// Assign Trigger function if it doesn't exist
|
34
|
+
if (!action.trigger) {
|
35
|
+
if (action.debounce) {
|
36
|
+
action.trigger = useDebounceFn((target, input) => performAction(action, target, input), action.debounce);
|
37
|
+
} else {
|
38
|
+
action.trigger = (target, input) => performAction(action, target, input);
|
39
|
+
}
|
51
40
|
}
|
52
|
-
|
53
|
-
|
41
|
+
|
42
|
+
// Set the initial state for the action
|
43
|
+
action.isApplying = false;
|
44
|
+
|
45
|
+
return storeObject({ ...action, __type: "__Action:" + namespace });
|
46
|
+
}
|
54
47
|
|
55
48
|
/**
|
56
|
-
*
|
49
|
+
* Filter the list of actions based on the provided filters in key-value pairs
|
50
|
+
* You can filter on any ActionOptions property by matching the value exactly or by providing an array of values
|
51
|
+
*
|
52
|
+
* @param filters
|
53
|
+
* @returns {ActionOptions[]}
|
57
54
|
*/
|
58
|
-
function
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
function getActions(filters?: AnyObject): ActionOptions[] {
|
56
|
+
let filteredActions = [...actions];
|
57
|
+
|
58
|
+
if (filters) {
|
59
|
+
for (const filter of Object.keys(filters)) {
|
60
|
+
const filterValue = filters[filter];
|
61
|
+
filteredActions = filteredActions.filter((a: AnyObject) => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
|
62
62
|
}
|
63
|
-
} else {
|
64
|
-
target.isSaving.value = saving;
|
65
63
|
}
|
64
|
+
|
65
|
+
return filteredActions.map((a: AnyObject) => getAction(a));
|
66
66
|
}
|
67
67
|
|
68
68
|
/**
|
69
69
|
* Perform an action on a set of targets
|
70
70
|
*
|
71
|
-
* @param {string}
|
71
|
+
* @param {string} action - can either be a string or an action object
|
72
72
|
* @param {object[]|object} target - an array of targets or a single target object
|
73
73
|
* @param {any} input - The input data to pass to the action handler
|
74
|
-
* @param isTriggered - Whether the action was triggered by a trigger function
|
75
74
|
*/
|
76
|
-
async function performAction(
|
77
|
-
|
78
|
-
if
|
79
|
-
|
80
|
-
}
|
81
|
-
|
82
|
-
// We always want to call the trigger function if it exists, unless it's already been triggered
|
83
|
-
// This provides behavior like debounce and custom action resolution
|
84
|
-
if (action.trigger && !isTriggered) {
|
85
|
-
return action.trigger(target, input);
|
86
|
-
}
|
75
|
+
async function performAction(action: string | ActionOptions, target: ActionTarget = null, input: any = null) {
|
76
|
+
action = getAction(action);
|
77
|
+
// Resolve the original action, if the current action is an alias
|
78
|
+
const aliasedAction = action.alias ? getAction(action.alias) : null;
|
87
79
|
|
88
80
|
const vnode = action.vnode && action.vnode(target);
|
89
81
|
let result: any;
|
@@ -96,6 +88,11 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
|
|
96
88
|
}
|
97
89
|
|
98
90
|
setTargetSavingState(target, true);
|
91
|
+
action.isApplying = true;
|
92
|
+
|
93
|
+
if (aliasedAction) {
|
94
|
+
aliasedAction.isApplying = true;
|
95
|
+
}
|
99
96
|
|
100
97
|
// If additional input is required, first render the vnode and wait for the confirm or cancel action
|
101
98
|
if (vnode) {
|
@@ -124,56 +121,70 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
|
|
124
121
|
result = await onConfirmAction(action, target, input);
|
125
122
|
}
|
126
123
|
|
124
|
+
action.isApplying = false;
|
127
125
|
setTargetSavingState(target, false);
|
128
126
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
/**
|
133
|
-
* Filter the list of actions based on the provided filters in key-value pairs
|
134
|
-
* You can filter on any ActionOptions property by matching the value exactly or by providing an array of values
|
135
|
-
*
|
136
|
-
* @param filters
|
137
|
-
* @returns {ActionOptions[]}
|
138
|
-
*/
|
139
|
-
function filterActions(filters: AnyObject): ActionOptions[] {
|
140
|
-
let filteredActions = [...mappedActions];
|
127
|
+
if (aliasedAction) {
|
128
|
+
aliasedAction.isApplying = false;
|
129
|
+
}
|
141
130
|
|
142
|
-
|
143
|
-
|
144
|
-
filteredActions = filteredActions.filter((a: AnyObject) => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
|
131
|
+
if (result?.item) {
|
132
|
+
result.item = storeObject(result.item);
|
145
133
|
}
|
146
|
-
|
134
|
+
|
135
|
+
return result;
|
147
136
|
}
|
148
137
|
|
149
138
|
return {
|
150
|
-
|
151
|
-
|
152
|
-
performAction
|
139
|
+
getAction,
|
140
|
+
getActions
|
153
141
|
};
|
154
142
|
}
|
155
143
|
|
144
|
+
/**
|
145
|
+
* Set the reactive saving state of a target
|
146
|
+
*/
|
147
|
+
function setTargetSavingState(target: ActionTarget, saving: boolean) {
|
148
|
+
if (!target) return;
|
149
|
+
target = Array.isArray(target) ? target : [target];
|
150
|
+
for (const t of target) {
|
151
|
+
t.isSaving = saving;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
/**
|
156
|
+
* Execute the confirmed action on the target (ie: calling the server, or whatever the callback function does).
|
157
|
+
*
|
158
|
+
* 1. If the action has an optimistic callback, it will be called before the actual action to immediately update the UI (non batch actions only).
|
159
|
+
* 2. Call the onBatchAction or onAction callback of the action object, depending on if the target is an array or not.
|
160
|
+
* 3. Call the onSuccess or onError callback based on the result of the action.
|
161
|
+
* 4. Call the onFinish callback of the action object.
|
162
|
+
*/
|
156
163
|
async function onConfirmAction(action: ActionOptions, target: ActionTarget, input: any = null) {
|
157
164
|
if (!action.onAction) {
|
158
165
|
throw new Error("No onAction handler found for the selected action:" + action.name);
|
159
166
|
}
|
160
167
|
|
168
|
+
const isBatch = Array.isArray(target);
|
161
169
|
let result: any;
|
170
|
+
|
162
171
|
try {
|
163
|
-
if (
|
172
|
+
if (isBatch) {
|
164
173
|
if (action.onBatchAction) {
|
165
|
-
result = await action.onBatchAction(action.name, target, input);
|
174
|
+
result = await action.onBatchAction(action.alias || action.name, target, input);
|
166
175
|
} else {
|
167
176
|
result = { error: `Action ${action.name} does not support batch actions` };
|
168
177
|
}
|
169
178
|
} else {
|
170
179
|
// If the action has an optimistic callback, we call it before the actual action to immediately
|
171
180
|
// update the UI
|
172
|
-
if (action.optimistic) {
|
181
|
+
if (typeof action.optimistic === "function") {
|
173
182
|
action.optimistic(action, target, input);
|
183
|
+
} else if (action.optimistic) {
|
184
|
+
storeObject({ ...target, ...input });
|
174
185
|
}
|
175
186
|
|
176
|
-
result = await action.onAction(action.name, target, input);
|
187
|
+
result = await action.onAction(action.alias || action.name, target, input);
|
177
188
|
}
|
178
189
|
} catch (e) {
|
179
190
|
console.error(e);
|
@@ -182,6 +193,11 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
|
|
182
193
|
|
183
194
|
// If there is no return value or the result marks it as successful, we show a success message
|
184
195
|
if (result === undefined || result === true || result?.success) {
|
196
|
+
// Add the item to the object store if it has a type
|
197
|
+
if (result && result.item) {
|
198
|
+
result.item = storeObject(result.item);
|
199
|
+
}
|
200
|
+
|
185
201
|
if (result?.success && Array.isArray(target)) {
|
186
202
|
FlashMessages.success(`Successfully performed action ${action.label} on ${target.length} items`);
|
187
203
|
}
|
@@ -189,6 +205,10 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
|
|
189
205
|
if (action.onSuccess) {
|
190
206
|
action.onSuccess(result, target, input);
|
191
207
|
}
|
208
|
+
|
209
|
+
if (isBatch && action.onBatchSuccess) {
|
210
|
+
action.onBatchSuccess(result, target, input);
|
211
|
+
}
|
192
212
|
} else {
|
193
213
|
const errors = [];
|
194
214
|
if (result.errors) {
|