quasar-ui-danx 0.4.2 → 0.4.4

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 +106 -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
@@ -3,86 +3,95 @@ import { watch } from "vue";
3
3
  import { danxOptions } from "../config";
4
4
 
5
5
  export class FlashMessages {
6
- static notify: (options: QNotifyCreateOptions) => void;
6
+ static notify: (options: QNotifyCreateOptions) => void;
7
7
 
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
- };
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
- 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
- }
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
- static send(message?: string, options: QNotifyCreateOptions = {}) {
33
- if (message) {
34
- FlashMessages.notify({
35
- message,
36
- timeout: 10000,
37
- classes: "bg-gray-500 text-white",
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, "&nbsp;&nbsp;&nbsp;&nbsp;");
35
+ // Replace <SomeComponent ... > with <b>SomeComponent ... </b>
36
+ return message.replace(/<(\w+)([^>]*)>/g, "<b>$1$2</b>");
37
+ }
45
38
 
46
- static success(message?: string, options: QNotifyCreateOptions = {}) {
47
- FlashMessages.send(message, {
48
- classes: "bg-green-300 !text-green-900",
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
- static error(message?: string, options: QNotifyCreateOptions = {}) {
56
- FlashMessages.send(message, {
57
- classes: "bg-red-300 !text-red-900",
58
- icon: "error",
59
- ...options,
60
- ...danxOptions.value.flashMessages.error
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
- static warning(message?: string, options: QNotifyCreateOptions = {}) {
65
- FlashMessages.send(message, {
66
- classes: "bg-yellow-300 !text-yellow-900",
67
- icon: "warning",
68
- ...options,
69
- ...danxOptions.value.flashMessages.warning
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
- static combine(type: string, messages: string[] | { message: string, Message: string }[], options = {}) {
74
- // @ts-expect-error - type is a string
75
- const messageType = FlashMessages[type];
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
- if (typeof messageType !== "function") {
78
- throw new Error(`FlashMessages.${type} is not a function`);
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
- messageType(messages.map(m => typeof m === "string" ? m : (m.message || m.Message)).join("<br/>"), {
82
- ...options,
83
- html: true
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();
@@ -1,36 +1,9 @@
1
1
  import { useDebounceFn } from "@vueuse/core";
2
- import { Ref, shallowRef, VNode } from "vue";
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 | null;
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: ActionTargetItem | null, input: any) => void;
27
- onAction?: (action: string | null | undefined, target: ActionTargetItem | null, input: any) => Promise<any>;
28
- onBatchAction?: (action: string | null | undefined, targets: ActionTargetItem[], 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 mappedActions = actions.map(action => {
46
- const mappedAction: ActionOptions = { ...globalOptions, ...action };
47
- if (mappedAction.debounce) {
48
- mappedAction.trigger = useDebounceFn((target, input) => performAction(mappedAction, target, input, true), mappedAction.debounce);
49
- } else if (!mappedAction.trigger) {
50
- mappedAction.trigger = (target, input) => performAction(mappedAction, target, input, true);
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 };
51
26
  }
52
- return mappedAction;
53
- });
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
+ }
40
+ }
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
- * Set the reactive saving state of a target
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 setTargetSavingState(target: ActionTarget, saving: boolean) {
59
- if (Array.isArray(target)) {
60
- for (const t of target) {
61
- t.isSaving.value = saving;
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 if (target) {
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} name - can either be a string or an action object
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(name: string | object, target: ActionTarget = null, input: any = null, isTriggered = false) {
77
- const action: ActionOptions | null | undefined = typeof name === "string" ? mappedActions.find(a => a.name === name) : name;
78
- if (!action) {
79
- throw new Error(`Unknown action: ${name}`);
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
- return result;
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
- for (const filter of Object.keys(filters)) {
143
- const filterValue = filters[filter];
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
- return filteredActions;
134
+
135
+ return result;
147
136
  }
148
137
 
149
138
  return {
150
- actions: mappedActions,
151
- filterActions,
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 (Array.isArray(target)) {
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) {