quasar-ui-danx 0.4.28 → 0.4.29
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.
- package/README.md +35 -35
- package/dist/danx.es.js +2927 -2814
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +48 -48
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/ActionTable/Form/Fields/EditableDiv.vue +47 -17
- package/src/components/ActionTable/Form/Fields/TextField.vue +2 -0
- package/src/components/ActionTable/Form/RenderedForm.vue +7 -20
- package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +1 -1
- package/src/components/ActionTable/Form/Utilities/SaveStateIndicator.vue +37 -0
- package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +3 -3
- package/src/components/ActionTable/{listControls.ts → controls.ts} +11 -7
- package/src/components/ActionTable/index.ts +1 -1
- package/src/components/DragAndDrop/ListItemDraggable.vue +8 -4
- package/src/components/DragAndDrop/dragAndDrop.ts +2 -1
- package/src/components/DragAndDrop/listDragAndDrop.ts +19 -6
- package/src/helpers/FileUpload.ts +4 -4
- package/src/helpers/files.ts +56 -43
- package/src/types/actions.d.ts +45 -27
- package/src/types/controls.d.ts +22 -41
- package/src/types/fields.d.ts +1 -0
- package/src/types/files.d.ts +2 -2
- package/src/types/index.d.ts +5 -0
- package/src/types/shared.d.ts +9 -0
- package/src/types/tables.d.ts +3 -3
package/package.json
CHANGED
@@ -1,30 +1,55 @@
|
|
1
1
|
<template>
|
2
|
-
<div
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
<div class="inline-block relative">
|
3
|
+
<div
|
4
|
+
contenteditable
|
5
|
+
class="relative inline-block transition duration-300 outline-none outline-offset-0 border-none focus:outline-4 hover:outline-4 rounded-sm z-10"
|
6
|
+
:style="{minWidth, minHeight}"
|
7
|
+
:class="contentClass"
|
8
|
+
@input="onInput"
|
9
|
+
>
|
10
|
+
{{ text }}
|
11
|
+
</div>
|
12
|
+
<div
|
13
|
+
v-if="!text && placeholder"
|
14
|
+
ref="placeholderDiv"
|
15
|
+
class="text-gray-600 absolute-top-left whitespace-nowrap z-1"
|
16
|
+
>
|
17
|
+
{{ placeholder }}
|
18
|
+
</div>
|
8
19
|
</div>
|
9
20
|
</template>
|
10
21
|
|
11
|
-
<script setup>
|
22
|
+
<script setup lang="ts">
|
12
23
|
import { useDebounceFn } from "@vueuse/core";
|
13
|
-
import { ref } from "vue";
|
24
|
+
import { computed, onMounted, ref } from "vue";
|
14
25
|
|
15
26
|
const emit = defineEmits(["update:model-value", "change"]);
|
16
|
-
const props = defineProps
|
17
|
-
modelValue:
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
27
|
+
const props = withDefaults(defineProps<{
|
28
|
+
modelValue: string;
|
29
|
+
color?: string;
|
30
|
+
debounceDelay?: number;
|
31
|
+
placeholder?: string;
|
32
|
+
}>(), {
|
33
|
+
// NOTE: You must safe-list required colors in tailwind.config.js
|
34
|
+
// Add text-blue-900, hover:bg-blue-200, hover:outline-blue-200, focus:outline-blue-200 and focus:bg-blue-200 for the following config
|
35
|
+
color: "blue-200",
|
36
|
+
textColor: "blue-900",
|
37
|
+
debounceDelay: 1000,
|
38
|
+
placeholder: "Enter Text..."
|
25
39
|
});
|
26
40
|
|
27
41
|
const text = ref(props.modelValue);
|
42
|
+
const placeholderDiv = ref(null);
|
43
|
+
const minWidth = ref(0);
|
44
|
+
const minHeight = ref(0);
|
45
|
+
|
46
|
+
onMounted(() => {
|
47
|
+
// Set the min-width to the width of the placeholder
|
48
|
+
if (placeholderDiv.value) {
|
49
|
+
minWidth.value = placeholderDiv.value.offsetWidth + "px";
|
50
|
+
minHeight.value = placeholderDiv.value.offsetHeight + "px";
|
51
|
+
}
|
52
|
+
});
|
28
53
|
|
29
54
|
const debouncedChange = useDebounceFn(() => {
|
30
55
|
emit("update:model-value", text.value);
|
@@ -36,4 +61,9 @@ function onInput(e) {
|
|
36
61
|
debouncedChange();
|
37
62
|
}
|
38
63
|
|
64
|
+
const contentClass = computed(() => [
|
65
|
+
`hover:bg-${props.color} focus:bg-${props.color}`,
|
66
|
+
`hover:text-${props.textColor} focus:text-${props.textColor}`,
|
67
|
+
`hover:outline-${props.color} focus:outline-${props.color}`
|
68
|
+
]);
|
39
69
|
</script>
|
@@ -23,6 +23,7 @@
|
|
23
23
|
:disable="disabled"
|
24
24
|
:label-slot="!noLabel"
|
25
25
|
:input-class="inputClass"
|
26
|
+
:rows="rows"
|
26
27
|
:class="{'dx-input-prepend-label': prependLabel}"
|
27
28
|
stack-label
|
28
29
|
:type="type"
|
@@ -71,6 +72,7 @@ withDefaults(defineProps<TextFieldProps>(), {
|
|
71
72
|
labelClass: "",
|
72
73
|
inputClass: "",
|
73
74
|
maxLength: null,
|
75
|
+
rows: 3,
|
74
76
|
debounce: 0,
|
75
77
|
placeholder: null
|
76
78
|
});
|
@@ -93,25 +93,11 @@
|
|
93
93
|
</div>
|
94
94
|
</template>
|
95
95
|
<slot />
|
96
|
-
<
|
97
|
-
|
98
|
-
:
|
99
|
-
class="
|
100
|
-
|
101
|
-
<slot
|
102
|
-
v-if="saving"
|
103
|
-
name="saving"
|
104
|
-
>
|
105
|
-
Saving...
|
106
|
-
<QSpinnerPie class="ml-2" />
|
107
|
-
</slot>
|
108
|
-
<slot
|
109
|
-
v-else
|
110
|
-
name="saved"
|
111
|
-
>
|
112
|
-
Saved at {{ fDateTime(savedAt, { format: "M/d/yy h:mm:ssa" }) }}
|
113
|
-
</slot>
|
114
|
-
</div>
|
96
|
+
<SaveStateIndicator
|
97
|
+
:saving="saving"
|
98
|
+
:saved-at="savedAt"
|
99
|
+
:saving-class="savingClass"
|
100
|
+
/>
|
115
101
|
<ConfirmDialog
|
116
102
|
v-if="variationToEdit !== false"
|
117
103
|
title="Change variation name"
|
@@ -139,7 +125,7 @@
|
|
139
125
|
<script setup lang="ts">
|
140
126
|
import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@heroicons/vue/solid";
|
141
127
|
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
142
|
-
import {
|
128
|
+
import { FlashMessages, incrementName, replace } from "../../../helpers";
|
143
129
|
import { TrashIcon as RemoveIcon } from "../../../svg";
|
144
130
|
import { AnyObject, FormFieldValue, RenderedFormProps } from "../../../types";
|
145
131
|
import { ConfirmDialog, RenderVnode } from "../../Utility";
|
@@ -154,6 +140,7 @@ import {
|
|
154
140
|
TextField,
|
155
141
|
WysiwygField
|
156
142
|
} from "./Fields";
|
143
|
+
import SaveStateIndicator from "./Utilities/SaveStateIndicator";
|
157
144
|
|
158
145
|
const props = withDefaults(defineProps<RenderedFormProps>(), {
|
159
146
|
values: null,
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
:class="savingClass"
|
4
|
+
class="dx-saving-indicator flex items-center flex-nowrap"
|
5
|
+
>
|
6
|
+
<slot
|
7
|
+
v-if="saving"
|
8
|
+
name="saving"
|
9
|
+
>
|
10
|
+
Saving...
|
11
|
+
<QSpinnerPie class="ml-2" />
|
12
|
+
</slot>
|
13
|
+
<slot
|
14
|
+
v-else
|
15
|
+
name="saved"
|
16
|
+
>
|
17
|
+
<template v-if="savedAt">
|
18
|
+
Saved at {{ fDateTime(savedAt, { format: "M/d/yy h:mm:ssa" }) }}
|
19
|
+
</template>
|
20
|
+
<template v-else>
|
21
|
+
Not saved
|
22
|
+
</template>
|
23
|
+
</slot>
|
24
|
+
</div>
|
25
|
+
</template>
|
26
|
+
<script setup lang="ts">
|
27
|
+
import { fDateTime } from "../../../../helpers";
|
28
|
+
|
29
|
+
withDefaults(defineProps<{
|
30
|
+
saving?: boolean;
|
31
|
+
savedAt?: string;
|
32
|
+
savingClass?: string;
|
33
|
+
}>(), {
|
34
|
+
savingClass: "text-sm text-slate-500",
|
35
|
+
savedAt: ""
|
36
|
+
});
|
37
|
+
</script>
|
@@ -25,7 +25,7 @@
|
|
25
25
|
v-if="activeFilter"
|
26
26
|
:name="controller.name"
|
27
27
|
:show-filters="showFilters"
|
28
|
-
:filters="controller.filters
|
28
|
+
:filters="controller.filters?.value"
|
29
29
|
:active-filter="activeFilter"
|
30
30
|
class="dx-action-table-filters"
|
31
31
|
@update:active-filter="controller.setActiveFilter"
|
@@ -73,7 +73,7 @@
|
|
73
73
|
</template>
|
74
74
|
<script setup lang="ts">
|
75
75
|
import { computed } from "vue";
|
76
|
-
import {
|
76
|
+
import { DanxController } from "../../../types";
|
77
77
|
import { PanelsDrawer } from "../../PanelsDrawer";
|
78
78
|
import { PreviousNextControls } from "../../Utility";
|
79
79
|
import ActionTable from "../ActionTable.vue";
|
@@ -81,7 +81,7 @@ import { CollapsableFiltersSidebar } from "../Filters";
|
|
81
81
|
import { ActionToolbar } from "../Toolbars";
|
82
82
|
|
83
83
|
export interface ActionTableLayoutProps {
|
84
|
-
controller:
|
84
|
+
controller: DanxController;
|
85
85
|
exporter?: () => Promise<void>;
|
86
86
|
hideToolbar?: boolean;
|
87
87
|
panelTitleField?: string;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { computed,
|
1
|
+
import { computed, ref, shallowRef, watch } from "vue";
|
2
2
|
import { RouteParams, Router } from "vue-router";
|
3
3
|
import { danxOptions } from "../../config";
|
4
4
|
import { getItem, latestCallOnly, setItem, storeObject, waitForRef } from "../../helpers";
|
@@ -10,11 +10,12 @@ import {
|
|
10
10
|
ListControlsFilter,
|
11
11
|
ListControlsOptions,
|
12
12
|
ListControlsPagination,
|
13
|
-
PagedItems
|
13
|
+
PagedItems,
|
14
|
+
Ref
|
14
15
|
} from "../../types";
|
15
16
|
import { getFilterFromUrl } from "./listHelpers";
|
16
17
|
|
17
|
-
export function
|
18
|
+
export function useControls(name: string, options: ListControlsOptions): ListController {
|
18
19
|
let isInitialized = false;
|
19
20
|
const PAGE_SETTINGS_KEY = `dx-${name}-pager`;
|
20
21
|
const pagedItems = shallowRef<PagedItems | null>(null);
|
@@ -72,12 +73,13 @@ export function useListControls(name: string, options: ListControlsOptions): Lis
|
|
72
73
|
|
73
74
|
async function loadList() {
|
74
75
|
if (!isInitialized) return;
|
75
|
-
isLoadingList.value = true;
|
76
|
+
// isLoadingList.value = true;
|
76
77
|
try {
|
77
78
|
setPagedItems(await options.routes.list(pager.value));
|
78
|
-
isLoadingList.value = false;
|
79
79
|
} catch (e) {
|
80
80
|
// Fail silently
|
81
|
+
} finally {
|
82
|
+
isLoadingList.value = false;
|
81
83
|
}
|
82
84
|
}
|
83
85
|
|
@@ -91,9 +93,10 @@ export function useListControls(name: string, options: ListControlsOptions): Lis
|
|
91
93
|
}
|
92
94
|
try {
|
93
95
|
summary.value = await options.routes.summary(summaryFilter);
|
94
|
-
isLoadingSummary.value = false;
|
95
96
|
} catch (e) {
|
96
97
|
// Fail silently
|
98
|
+
} finally {
|
99
|
+
isLoadingSummary.value = false;
|
97
100
|
}
|
98
101
|
}
|
99
102
|
|
@@ -116,9 +119,10 @@ export function useListControls(name: string, options: ListControlsOptions): Lis
|
|
116
119
|
isLoadingFilters.value = true;
|
117
120
|
try {
|
118
121
|
fieldOptions.value = await options.routes.fieldOptions(activeFilter.value) || {};
|
119
|
-
isLoadingFilters.value = false;
|
120
122
|
} catch (e) {
|
121
123
|
// Fail silently
|
124
|
+
} finally {
|
125
|
+
isLoadingFilters.value = false;
|
122
126
|
}
|
123
127
|
}
|
124
128
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
export * from "./Columns";
|
2
|
+
export * from "./controls";
|
2
3
|
export * from "./Filters";
|
3
4
|
export * from "./Form";
|
4
5
|
export * from "./Layouts";
|
5
|
-
export * from "./listControls";
|
6
6
|
export * from "./listHelpers";
|
7
7
|
export * from "./tableColumns";
|
8
8
|
export * from "./Toolbars";
|
@@ -1,13 +1,14 @@
|
|
1
1
|
<template>
|
2
2
|
<div
|
3
|
-
class="cursor-move"
|
3
|
+
:class="{'cursor-move': !showHandle}"
|
4
4
|
draggable="true"
|
5
|
-
@dragstart="dragAndDrop.dragStart"
|
5
|
+
@dragstart.stop="dragAndDrop.dragStart"
|
6
6
|
@dragend="dragAndDrop.dragEnd"
|
7
7
|
>
|
8
|
-
<div class="
|
8
|
+
<div :class="contentClass">
|
9
9
|
<div
|
10
10
|
v-if="showHandle"
|
11
|
+
class="cursor-move"
|
11
12
|
:class="handleClass"
|
12
13
|
>
|
13
14
|
<SvgImg
|
@@ -33,17 +34,20 @@ const props = withDefaults(defineProps<{
|
|
33
34
|
dropZone: string | (() => string);
|
34
35
|
direction?: "vertical" | "horizontal";
|
35
36
|
showHandle?: boolean;
|
37
|
+
changeDropZone?: boolean;
|
38
|
+
contentClass?: string | object;
|
36
39
|
handleClass?: string | object;
|
37
40
|
listItems?: any[];
|
38
41
|
}>(), {
|
39
42
|
direction: "vertical",
|
40
43
|
handleClass: "",
|
44
|
+
contentClass: "flex flex-nowrap items-center",
|
41
45
|
listItems: () => []
|
42
46
|
});
|
43
47
|
|
44
48
|
const dragAndDrop = new ListDragAndDrop()
|
45
49
|
.setDropZone(props.dropZone)
|
46
|
-
.setOptions({ showPlaceholder: true, direction: props.direction })
|
50
|
+
.setOptions({ showPlaceholder: true, allowDropZoneChange: props.changeDropZone, direction: props.direction })
|
47
51
|
.onStart(() => dragging.value = true)
|
48
52
|
.onEnd(() => dragging.value = false)
|
49
53
|
.onDropZoneChange((target, dropZone, newPosition, oldPosition, data) => {
|
@@ -6,6 +6,7 @@ export class DragAndDrop {
|
|
6
6
|
direction?: string,
|
7
7
|
hideDragImage?: boolean,
|
8
8
|
showPlaceholder?: boolean,
|
9
|
+
allowDropZoneChange?: boolean,
|
9
10
|
} = { direction: "vertical", hideDragImage: false };
|
10
11
|
|
11
12
|
// State
|
@@ -138,7 +139,7 @@ export class DragAndDrop {
|
|
138
139
|
*/
|
139
140
|
dragEnd(e) {
|
140
141
|
this.currentDropZone = null;
|
141
|
-
this.abortController
|
142
|
+
this.abortController?.abort();
|
142
143
|
this.draggableData = null;
|
143
144
|
this.onEndCb && this.onEndCb(e);
|
144
145
|
}
|
@@ -19,6 +19,7 @@ export class ListDragAndDrop extends DragAndDrop {
|
|
19
19
|
constructor(options = {}) {
|
20
20
|
super({
|
21
21
|
showPlaceholder: true,
|
22
|
+
allowDropZoneChange: true,
|
22
23
|
...options
|
23
24
|
});
|
24
25
|
}
|
@@ -156,7 +157,7 @@ export class ListDragAndDrop extends DragAndDrop {
|
|
156
157
|
*/
|
157
158
|
getChildren() {
|
158
159
|
return [...(this.currentDropZone?.children || [])].filter(
|
159
|
-
(c) => c.className.match(/drag-placeholder/) === null
|
160
|
+
(c) => c.className.match(/dx-drag-placeholder/) === null
|
160
161
|
);
|
161
162
|
}
|
162
163
|
|
@@ -167,6 +168,13 @@ export class ListDragAndDrop extends DragAndDrop {
|
|
167
168
|
return target.closest(`[data-drop-zone]`);
|
168
169
|
}
|
169
170
|
|
171
|
+
/**
|
172
|
+
* Check if the current drop zone is the same as the initial drop zone
|
173
|
+
*/
|
174
|
+
isSameDropZone() {
|
175
|
+
return this.currentDropZone === this.initialDropZone;
|
176
|
+
}
|
177
|
+
|
170
178
|
/**
|
171
179
|
* Find the element at the current cursor position in the given drop zone
|
172
180
|
* @param point
|
@@ -236,19 +244,24 @@ export class ListDragAndDrop extends DragAndDrop {
|
|
236
244
|
* Render a placeholder element at the given position (in between the elements)
|
237
245
|
*/
|
238
246
|
renderPlaceholder() {
|
247
|
+
// If we're not allowed to change drop zones and we're not in the same drop zone, don't render the placeholder
|
248
|
+
if (!this.options.allowDropZoneChange && !this.isSameDropZone()) {
|
249
|
+
return;
|
250
|
+
}
|
251
|
+
|
239
252
|
if (!this.placeholder) {
|
240
253
|
this.placeholder = document.createElement("div");
|
241
|
-
this.placeholder.classList.add("drag-placeholder");
|
254
|
+
this.placeholder.classList.add("dx-drag-placeholder");
|
242
255
|
}
|
243
256
|
|
244
257
|
// Make sure the placeholder is oriented correctly
|
245
258
|
if (this.isVertical()) {
|
246
|
-
this.placeholder.classList.add("direction-vertical");
|
247
|
-
this.placeholder.classList.remove("direction-horizontal");
|
259
|
+
this.placeholder.classList.add("dx-direction-vertical");
|
260
|
+
this.placeholder.classList.remove("dx-direction-horizontal");
|
248
261
|
this.placeholder.style.height = undefined;
|
249
262
|
} else {
|
250
|
-
this.placeholder.classList.add("direction-horizontal");
|
251
|
-
this.placeholder.classList.remove("direction-vertical");
|
263
|
+
this.placeholder.classList.add("dx-direction-horizontal");
|
264
|
+
this.placeholder.classList.remove("dx-direction-vertical");
|
252
265
|
this.placeholder.style.height =
|
253
266
|
this.currentDropZone.getBoundingClientRect().height + "px";
|
254
267
|
}
|
@@ -243,11 +243,11 @@ export class FileUpload {
|
|
243
243
|
/**
|
244
244
|
* Mark the presigned upload as completed and return the file resource from the platform server
|
245
245
|
*/
|
246
|
-
async completePresignedUpload(
|
246
|
+
async completePresignedUpload(xhrFile: XHRFileUpload) {
|
247
247
|
// Show 95% as the last 5% will be to complete the presigned upload
|
248
|
-
this.fireProgressCallback(
|
248
|
+
this.fireProgressCallback(xhrFile, .95);
|
249
249
|
|
250
|
-
if (!
|
250
|
+
if (!xhrFile.file.resource_id) {
|
251
251
|
throw new Error("File resource ID is required to complete presigned upload");
|
252
252
|
}
|
253
253
|
|
@@ -256,7 +256,7 @@ export class FileUpload {
|
|
256
256
|
}
|
257
257
|
|
258
258
|
// Let the platform know the presigned upload is complete
|
259
|
-
return await this.options.completePresignedUpload(
|
259
|
+
return await this.options.completePresignedUpload(xhrFile.file.resource_id, xhrFile);
|
260
260
|
}
|
261
261
|
|
262
262
|
/**
|
package/src/helpers/files.ts
CHANGED
@@ -1,53 +1,66 @@
|
|
1
1
|
import ExifReader from "exifreader";
|
2
|
+
import { UploadedFile } from "src/types";
|
2
3
|
import { useCompatibility } from "./compatibility";
|
3
4
|
import { FlashMessages } from "./FlashMessages";
|
4
5
|
|
5
|
-
export async function resolveFileLocation(file, waitMessage = null) {
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
export async function resolveFileLocation(file: UploadedFile, waitMessage: string | null = null) {
|
7
|
+
if (file.location) {
|
8
|
+
return file.location;
|
9
|
+
}
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
try {
|
12
|
+
const tags = await ExifReader.load(file.blobUrl || file.url || "", {
|
13
|
+
expanded: true
|
14
|
+
});
|
15
|
+
if (tags.gps) {
|
16
|
+
return {
|
17
|
+
latitude: tags.gps.Latitude,
|
18
|
+
longitude: tags.gps.Longitude
|
19
|
+
};
|
20
|
+
}
|
21
|
+
} catch (error) {
|
22
|
+
console.error("Failed to load EXIF data from file:", error);
|
23
|
+
}
|
20
24
|
|
21
|
-
|
25
|
+
try {
|
26
|
+
const { waitForLocation, location } = useCompatibility();
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
// Show a waiting for location message if we have not returned within 1 second
|
29
|
+
if (waitMessage) {
|
30
|
+
setTimeout(() => {
|
31
|
+
if (!location.value && waitMessage) {
|
32
|
+
FlashMessages.warning(waitMessage);
|
33
|
+
}
|
34
|
+
}, 1000);
|
35
|
+
}
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
// Wait for the browser to return the location (https only as http will not return a location)
|
38
|
+
if (window.location.protocol === "https:") {
|
39
|
+
await waitForLocation();
|
40
|
+
} else if (window.location.href.match("localhost")) {
|
41
|
+
// XXX: Special case for local development so we can test without https
|
42
|
+
return {
|
43
|
+
latitude: 37.7749,
|
44
|
+
longitude: -122.4194,
|
45
|
+
accuracy: 1,
|
46
|
+
altitude: 0,
|
47
|
+
altitudeAccuracy: 0
|
48
|
+
};
|
49
|
+
}
|
41
50
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
if (!location.value) {
|
52
|
+
return null;
|
53
|
+
}
|
54
|
+
|
55
|
+
return {
|
56
|
+
latitude: location.value.latitude,
|
57
|
+
longitude: location.value.longitude,
|
58
|
+
accuracy: location.value.accuracy,
|
59
|
+
altitude: location.value.altitude,
|
60
|
+
altitudeAccuracy: location.value.altitudeAccuracy
|
61
|
+
};
|
62
|
+
} catch (error) {
|
63
|
+
console.error(error);
|
64
|
+
return null;
|
65
|
+
}
|
53
66
|
}
|
package/src/types/actions.d.ts
CHANGED
@@ -1,25 +1,27 @@
|
|
1
|
-
import { ListController, ListControlsRoutes } from "src/types/controls";
|
1
|
+
import { FilterGroup, ListController, ListControlsRoutes } from "src/types/controls";
|
2
|
+
import { FormField } from "src/types/forms";
|
3
|
+
import { TableColumn } from "src/types/tables";
|
2
4
|
import { VNode } from "vue";
|
3
|
-
import { AnyObject, TypedObject } from "./shared";
|
4
|
-
|
5
|
-
export interface ActionPanel {
|
6
|
-
name: string | number;
|
7
|
-
label: string;
|
8
|
-
category?: string;
|
9
|
-
class?: string | object;
|
10
|
-
enabled?: boolean | ((target: ActionTargetItem) => boolean);
|
11
|
-
tabVnode?: (target: ActionTargetItem | null | undefined, activePanel: string | number) => VNode | any;
|
12
|
-
vnode: (target: ActionTargetItem | null | undefined) => VNode | any;
|
13
|
-
}
|
5
|
+
import { AnyObject, ComputedRef, TypedObject } from "./shared";
|
14
6
|
|
15
7
|
export interface ActionTargetItem extends TypedObject {
|
16
8
|
isSaving?: boolean;
|
17
9
|
updated_at?: string;
|
18
10
|
}
|
19
11
|
|
20
|
-
export type ActionTarget = ActionTargetItem[] |
|
12
|
+
export type ActionTarget<T = ActionTargetItem> = T[] | T | null;
|
21
13
|
|
22
|
-
export interface
|
14
|
+
export interface ActionPanel<T = ActionTargetItem> {
|
15
|
+
name: string | number;
|
16
|
+
label: string;
|
17
|
+
category?: string;
|
18
|
+
class?: string | object;
|
19
|
+
enabled?: boolean | ((target: T) => boolean);
|
20
|
+
tabVnode?: (target: T | null | undefined, activePanel: string | number) => VNode | any;
|
21
|
+
vnode: (target: T | null | undefined) => VNode | any;
|
22
|
+
}
|
23
|
+
|
24
|
+
export interface ActionOptions<T = ActionTargetItem> {
|
23
25
|
name: string;
|
24
26
|
alias?: string;
|
25
27
|
label?: string;
|
@@ -30,17 +32,17 @@ export interface ActionOptions {
|
|
30
32
|
category?: string;
|
31
33
|
class?: string;
|
32
34
|
debounce?: number;
|
33
|
-
optimistic?: boolean | ((action: ActionOptions
|
34
|
-
vnode?: (
|
35
|
-
enabled?: (target:
|
36
|
-
batchEnabled?: (targets:
|
37
|
-
onAction?: (action: string | ResourceAction | ActionOptions
|
38
|
-
onBatchAction?: (action: string | ResourceAction | ActionOptions
|
39
|
-
onStart?: (action: ActionOptions | null, targets: ActionTarget
|
40
|
-
onSuccess?: (result: any, targets: ActionTarget
|
41
|
-
onBatchSuccess?: (result: any, targets:
|
42
|
-
onError?: (result: any, targets: ActionTarget
|
43
|
-
onFinish?: (result: any, targets: ActionTarget
|
35
|
+
optimistic?: boolean | ((action: ActionOptions<T>, target: T | null, input: any) => void);
|
36
|
+
vnode?: (target: ActionTarget<T>, data: any) => VNode | any;
|
37
|
+
enabled?: (target: ActionTarget<T>) => boolean;
|
38
|
+
batchEnabled?: (targets: T[]) => boolean;
|
39
|
+
onAction?: (action: string | ResourceAction<T> | ActionOptions<T>, target: T | null, input?: AnyObject | any) => Promise<AnyObject | any> | void;
|
40
|
+
onBatchAction?: (action: string | ResourceAction<T> | ActionOptions<T>, targets: T[], input: any) => Promise<AnyObject | any> | void;
|
41
|
+
onStart?: (action: ActionOptions<T> | null, targets: ActionTarget<T>, input: any) => boolean;
|
42
|
+
onSuccess?: (result: any, targets: ActionTarget<T>, input: any) => any;
|
43
|
+
onBatchSuccess?: (result: any, targets: T[], input: any) => any;
|
44
|
+
onError?: (result: any, targets: ActionTarget<T>, input: any) => any;
|
45
|
+
onFinish?: (result: any, targets: ActionTarget<T>, input: any) => any;
|
44
46
|
}
|
45
47
|
|
46
48
|
export interface ActionGlobalOptions extends Partial<ActionOptions> {
|
@@ -48,8 +50,24 @@ export interface ActionGlobalOptions extends Partial<ActionOptions> {
|
|
48
50
|
controls?: ListController;
|
49
51
|
}
|
50
52
|
|
51
|
-
export interface ResourceAction extends ActionOptions {
|
53
|
+
export interface ResourceAction<T = ActionTargetItem> extends ActionOptions<T> {
|
52
54
|
isApplying: boolean;
|
53
|
-
trigger: (target?: ActionTarget
|
55
|
+
trigger: (target?: ActionTarget<T>, input?: any) => Promise<any>;
|
54
56
|
__type: string;
|
55
57
|
}
|
58
|
+
|
59
|
+
export interface ActionController<T = ActionTargetItem> {
|
60
|
+
// Actions
|
61
|
+
action?: (actionName: string, target?: T | null, input?: any) => Promise<any | void>;
|
62
|
+
getAction?: (actionName: string, actionOptions?: Partial<ActionOptions>) => ResourceAction;
|
63
|
+
getActions?: (names?: string[]) => ResourceAction[];
|
64
|
+
extendAction?: (actionName: string, extendedId: string | number, actionOptions: Partial<ActionOptions>) => ResourceAction;
|
65
|
+
modifyAction?: (actionName: string, actionOptions: Partial<ActionOptions>) => ResourceAction;
|
66
|
+
batchActions?: ResourceAction[];
|
67
|
+
menuActions?: ResourceAction[];
|
68
|
+
columns?: TableColumn[];
|
69
|
+
filters?: ComputedRef<FilterGroup[]>;
|
70
|
+
fields?: FormField[];
|
71
|
+
panels?: ActionPanel[];
|
72
|
+
routes?: ListControlsRoutes;
|
73
|
+
}
|