quasar-ui-danx 0.2.31 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@
6
6
  <QTabPanel v-for="panel in panels" :key="panel.name" :name="panel.name">
7
7
  <RenderVnode
8
8
  v-if="panel.vnode"
9
- :vnode="panel.vnode()"
9
+ :vnode="panel.vnode"
10
10
  />
11
11
  </QTabPanel>
12
12
  </QTabPanels>
@@ -1,5 +1,19 @@
1
1
  <script>
2
- const RenderVnode = (props) => props.vnode;
3
- RenderVnode.props = { vnode: { type: Object, required: true } };
2
+ const RenderVnode = (props) => {
3
+ if (props.vnode.__v_isVNode) {
4
+ return props.vnode;
5
+ }
6
+
7
+ if (props.vnode.__v_isRef) {
8
+ return props.vnode.value;
9
+ }
10
+
11
+ if (typeof props.vnode === "function") {
12
+ return props.vnode();
13
+ }
14
+
15
+ return null;
16
+ };
17
+ RenderVnode.props = { vnode: { type: [Function, Object], required: true } };
4
18
  export default RenderVnode;
5
19
  </script>
@@ -1,17 +1,23 @@
1
- import { ref, shallowRef, VNode } from "vue";
1
+ import { useDebounceFn } from "@vueuse/core";
2
+ import { Ref, shallowRef, VNode } from "vue";
2
3
  import { FlashMessages } from "./FlashMessages";
3
4
 
4
- type ActionTarget = object[] | object;
5
+ export type ActionTargetItem = {
6
+ id: number | string;
7
+ isSaving: Ref<boolean>;
8
+ [key: string]: any;
9
+ };
10
+ export type ActionTarget = ActionTargetItem[] | ActionTargetItem;
5
11
 
6
- interface ActionOptions {
12
+ export interface ActionOptions {
7
13
  name?: string;
8
14
  label?: string;
9
15
  menu?: boolean;
10
16
  batch?: boolean;
11
17
  category?: string;
12
18
  class?: string;
19
+ debounce?: number;
13
20
  trigger?: (target: ActionTarget, input: any) => Promise<any>;
14
- activeTarget?: any;
15
21
  vnode?: (target: ActionTarget) => VNode;
16
22
  enabled?: (target: object) => boolean;
17
23
  batchEnabled?: (targets: object[]) => boolean;
@@ -24,7 +30,7 @@ interface ActionOptions {
24
30
  onFinish?: (result: any, targets: ActionTarget, input: any) => any;
25
31
  }
26
32
 
27
- export const activeActionVnode: object = shallowRef(null);
33
+ export const activeActionVnode: Ref = shallowRef(null);
28
34
 
29
35
  /**
30
36
  * Hook to perform an action on a set of targets
@@ -36,31 +42,25 @@ export const activeActionVnode: object = shallowRef(null);
36
42
  export function useActions(actions: ActionOptions[], globalOptions: ActionOptions | null = null) {
37
43
  const mappedActions = actions.map(action => {
38
44
  const mappedAction: ActionOptions = { ...globalOptions, ...action };
39
- if (!mappedAction.trigger) {
40
- mappedAction.trigger = (target, input) => performAction(mappedAction, target, input);
41
- mappedAction.activeTarget = ref(null);
45
+ if (mappedAction.debounce) {
46
+ mappedAction.trigger = useDebounceFn((target, input) => performAction(mappedAction, target, input, true), mappedAction.debounce);
47
+ } else if (!mappedAction.trigger) {
48
+ mappedAction.trigger = (target, input) => performAction(mappedAction, target, input, true);
42
49
  }
43
50
  return mappedAction;
44
51
  });
45
52
 
46
53
  /**
47
- * Check if the provided target is currently being saved by any of the actions
54
+ * Set the reactive saving state of a target
48
55
  */
49
- function isSavingTarget(target: any): boolean {
50
- if (!target) return false;
51
-
52
- for (const action of mappedActions) {
53
- const activeTargets = (Array.isArray(action.activeTarget.value) ? action.activeTarget.value : [action.activeTarget.value]).filter(t => t);
54
- if (activeTargets.length === 0) continue;
55
-
56
- for (const activeTarget of activeTargets) {
57
- if (activeTarget === target || (activeTarget.id && activeTarget.id === target.id)) {
58
- return true;
59
- }
56
+ function setTargetSavingState(target: ActionTarget, saving: boolean) {
57
+ if (Array.isArray(target)) {
58
+ for (const t of target) {
59
+ t.isSaving.value = saving;
60
60
  }
61
+ } else {
62
+ target.isSaving.value = saving;
61
63
  }
62
-
63
- return false;
64
64
  }
65
65
 
66
66
  /**
@@ -69,22 +69,23 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
69
69
  * @param {string} name - can either be a string or an action object
70
70
  * @param {object[]|object} target - an array of targets or a single target object
71
71
  * @param {any} input - The input data to pass to the action handler
72
+ * @param isTriggered - Whether the action was triggered by a trigger function
72
73
  */
73
- async function performAction(name: string | object, target: ActionTarget, input: any = null) {
74
- const action: ActionOptions = typeof name === "string" ? mappedActions.find(a => a.name === name) : name;
74
+ async function performAction(name: string | object, target: ActionTarget, input: any = null, isTriggered = false) {
75
+ const action: ActionOptions | null | undefined = typeof name === "string" ? mappedActions.find(a => a.name === name) : name;
75
76
  if (!action) {
76
77
  throw new Error(`Unknown action: ${name}`);
77
78
  }
78
79
 
79
- if (!action.activeTarget) {
80
- throw new Error(`Action ${action.name} does not have an activeTarget ref. Please use useActions() or manually set the activeTarget ref`);
80
+ // We always want to call the trigger function if it exists, unless it's already been triggered
81
+ // This provides behavior like debounce and custom action resolution
82
+ if (action.trigger && !isTriggered) {
83
+ return action.trigger(target, input);
81
84
  }
82
85
 
83
86
  const vnode = action.vnode && action.vnode(target);
84
87
  let result: any;
85
88
 
86
- action.activeTarget.value = target;
87
-
88
89
  // Run the onStart handler if it exists and quit the operation if it returns false
89
90
  if (action.onStart) {
90
91
  if (!action.onStart(action, target, input)) {
@@ -92,6 +93,8 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
92
93
  }
93
94
  }
94
95
 
96
+ setTargetSavingState(target, true);
97
+
95
98
  // If additional input is required, first render the vnode and wait for the confirm or cancel action
96
99
  if (vnode) {
97
100
  // If the action requires an input, we set the activeActionVnode to the input component.
@@ -119,7 +122,8 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
119
122
  result = await onConfirmAction(action, target, input);
120
123
  }
121
124
 
122
- action.activeTarget.value = null;
125
+ setTargetSavingState(target, false);
126
+
123
127
  return result;
124
128
  }
125
129
 
@@ -135,14 +139,13 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
135
139
 
136
140
  for (const filter of Object.keys(filters)) {
137
141
  const filterValue = filters[filter];
138
- filteredActions = filteredActions.filter(a => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
142
+ filteredActions = filteredActions.filter((a: object) => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
139
143
  }
140
144
  return filteredActions;
141
145
  }
142
146
 
143
147
  return {
144
148
  actions: mappedActions,
145
- isSavingTarget,
146
149
  filterActions,
147
150
  performAction
148
151
  };
@@ -1,17 +1,12 @@
1
1
  /**
2
- *
3
- * @param array
4
- * @param item
5
- * @param newItem
6
- * @returns {*[]}
2
+ * Replace an item in an array with a new item
7
3
  */
8
- export function replace(array, item, newItem = undefined) {
9
- const index =
10
- typeof item === "function" ? array.findIndex(item) : array.indexOf(item);
11
- if (index === false) {
12
- console.error("Item not found in array", item, array);
13
- throw new Error("Item not found in array");
4
+ export function replace(array: any[], item: any, newItem = undefined, appendIfMissing = false) {
5
+ const index: any = typeof item === "function" ? array.findIndex(item) : array.indexOf(item);
6
+ if (index === false || index === -1) {
7
+ return appendIfMissing ? [...array, newItem] : array;
14
8
  }
9
+
15
10
  const newArray = [...array];
16
11
  newItem !== undefined
17
12
  ? newArray.splice(index, 1, newItem)
@@ -19,17 +14,17 @@ export function replace(array, item, newItem = undefined) {
19
14
  return newArray;
20
15
  }
21
16
 
22
- export function remove(array, item) {
17
+ /**
18
+ * Remove an item from an array
19
+ */
20
+ export function remove(array: any[], item: any) {
23
21
  return replace(array, item);
24
22
  }
25
23
 
26
24
  /**
27
25
  * Remove duplicate items from an array using a callback to compare 2 elements
28
- * @param array
29
- * @param cb
30
- * @returns {*}
31
26
  */
32
- export function uniqueBy(array, cb) {
27
+ export function uniqueBy(array: any[], cb: Function) {
33
28
  return array.filter((a, index, self) => {
34
29
  // Check if the current element 'a' is the first occurrence in the array
35
30
  return index === self.findIndex((b) => cb(a, b));
@@ -1,25 +1,18 @@
1
- import { watch } from "vue";
1
+ import { Ref, watch } from "vue";
2
2
 
3
3
  /**
4
- * Sleep function to be used in conjuction with async await:
4
+ * Sleep function to be used in conjunction with async await:
5
5
  *
6
6
  * eg: await sleep(5000);
7
- *
8
- * @param delay
9
- * @returns {Promise<any>}
10
7
  */
11
- export function sleep(delay) {
8
+ export function sleep(delay: number) {
12
9
  return new Promise((resolve) => setTimeout(resolve, delay));
13
10
  }
14
11
 
15
12
  /**
16
13
  * Wait for a ref to have a value and then resolve the promise
17
- *
18
- * @param ref
19
- * @param value
20
- * @returns {Promise<void>}
21
14
  */
22
- export function waitForRef(ref, value) {
15
+ export function waitForRef(ref: Ref, value: any) {
23
16
  return new Promise<void>((resolve) => {
24
17
  watch(ref, (newValue) => {
25
18
  if (newValue === value) {
@@ -31,39 +24,29 @@ export function waitForRef(ref, value) {
31
24
 
32
25
  /**
33
26
  * Returns a number that is constrained to the given range.
34
- * @param min
35
- * @param max
36
- * @param value
37
- * @returns {number}
38
27
  */
39
- export function minmax(min, max, value) {
28
+ export function minmax(min: number, max: number, value: number) {
40
29
  return Math.max(min, Math.min(max, value));
41
30
  }
42
31
 
43
32
  /**
44
33
  * Convert meters to miles
45
- * @param meters
46
- * @returns {number}
47
34
  */
48
- export function metersToMiles(meters) {
35
+ export function metersToMiles(meters: number) {
49
36
  return meters * 0.000621371;
50
37
  }
51
38
 
52
39
  /**
53
40
  * Convert miles to meters
54
- * @param miles
55
- * @returns {number}
56
41
  */
57
- export function milesToMeters(miles) {
42
+ export function milesToMeters(miles: number) {
58
43
  return miles / 0.000621371;
59
44
  }
60
45
 
61
46
  /**
62
47
  * Parses a string for Lat and Long coords
63
- * @param location
64
- * @returns {null|{lng: number, lat: number}}
65
48
  */
66
- export function parseCoords(location) {
49
+ export function parseCoords(location: string) {
67
50
  const latLong = location.split(",");
68
51
 
69
52
  if (latLong.length === 2) {
@@ -80,3 +63,15 @@ export function parseCoords(location) {
80
63
 
81
64
  return null;
82
65
  }
66
+
67
+ /**
68
+ * Increment a name by adding a number to the end of it or incrementing the number if it already exists
69
+ */
70
+ export function incrementName(name: string) {
71
+ name = (name || "New Item").trim();
72
+ const match = name.match(/(\d+)$/);
73
+ if (match) {
74
+ return name.replace(/\d+$/, (match: string) => "" + (parseInt(match) + 1));
75
+ }
76
+ return `${name} 1`;
77
+ }
@@ -1,3 +1,7 @@
1
+ .q-tab {
2
+ text-transform: capitalize
3
+ }
4
+
1
5
  .q-tab-panels {
2
6
  overflow: visible;
3
7