quasar-ui-danx 0.4.10 → 0.4.13
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/dist/danx.es.js +12389 -7677
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +137 -7
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/index.d.ts +7 -0
- package/index.ts +1 -0
- package/package.json +10 -4
- package/src/components/ActionTable/ActionMenu.vue +26 -31
- package/src/components/ActionTable/ActionTable.vue +4 -1
- package/src/components/ActionTable/Columns/ActionTableColumn.vue +14 -6
- package/src/components/ActionTable/Columns/ActionTableHeaderColumn.vue +63 -42
- package/src/components/ActionTable/Form/ActionForm.vue +55 -0
- package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +11 -5
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +1 -0
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +1 -1
- package/src/components/ActionTable/Form/Fields/NumberField.vue +0 -1
- package/src/components/ActionTable/Form/RenderedForm.vue +57 -50
- package/src/components/ActionTable/Form/index.ts +1 -0
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +3 -3
- package/src/components/ActionTable/TableSummaryRow.vue +48 -37
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +2 -2
- package/src/components/ActionTable/listControls.ts +3 -2
- package/src/components/PanelsDrawer/PanelsDrawer.vue +15 -5
- package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +3 -1
- package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +17 -4
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +30 -5
- package/src/components/Utility/Files/FilePreview.vue +72 -12
- package/src/components/Utility/Popovers/PopoverMenu.vue +34 -29
- package/src/components/Utility/Tools/RenderVnode.vue +5 -1
- package/src/config/index.ts +2 -1
- package/src/helpers/FileUpload.ts +59 -8
- package/src/helpers/actions.ts +27 -27
- package/src/helpers/date.ts +2 -2
- package/src/helpers/download.ts +8 -2
- package/src/helpers/formats.ts +52 -5
- package/src/helpers/multiFileUpload.ts +6 -4
- package/src/helpers/objectStore.ts +14 -17
- package/src/helpers/request.ts +12 -0
- package/src/helpers/singleFileUpload.ts +63 -55
- package/src/helpers/utils.ts +12 -3
- package/src/index.ts +1 -0
- package/src/styles/danx.scss +5 -0
- package/src/styles/index.scss +1 -0
- package/src/styles/themes/danx/action-table.scss +24 -13
- package/src/types/actions.d.ts +16 -7
- package/src/types/controls.d.ts +4 -4
- package/src/types/files.d.ts +10 -5
- package/src/types/forms.d.ts +19 -1
- package/src/types/index.d.ts +0 -1
- package/src/types/requests.d.ts +2 -0
- package/src/types/tables.d.ts +28 -22
- package/src/{vue-plugin.js → vue-plugin.ts} +5 -4
- package/tsconfig.json +1 -0
- package/types/index.d.ts +2 -0
package/index.d.ts
ADDED
package/index.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./src";
|
package/package.json
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
{
|
2
2
|
"name": "quasar-ui-danx",
|
3
|
-
"version": "0.4.
|
3
|
+
"version": "0.4.13",
|
4
4
|
"author": "Dan <dan@flytedesk.com>",
|
5
5
|
"description": "DanX Vue / Quasar component library",
|
6
6
|
"license": "MIT",
|
7
7
|
"type": "module",
|
8
|
-
"main": "dist/danx.
|
9
|
-
"module": "dist/danx.es.js",
|
8
|
+
"main": "./dist/danx.umd.js",
|
9
|
+
"module": "./dist/danx.es.js",
|
10
|
+
"types": "types/index.d.ts",
|
10
11
|
"scripts": {
|
11
12
|
"dev": "cd dev && quasar dev && cd ..",
|
12
13
|
"build": "vite build",
|
@@ -17,6 +18,10 @@
|
|
17
18
|
"type": "git",
|
18
19
|
"url": "https://github.com/flytedan/quasar-ui-danx"
|
19
20
|
},
|
21
|
+
"peerDependencies": {
|
22
|
+
"quasar": "^2.0.0",
|
23
|
+
"vue": "^3.4.21"
|
24
|
+
},
|
20
25
|
"devDependencies": {
|
21
26
|
"@quasar/extras": "^1.16.4",
|
22
27
|
"@types/luxon": "^3.4.2",
|
@@ -52,7 +57,8 @@
|
|
52
57
|
"danx-icon": "^1.0.2",
|
53
58
|
"exifreader": "^4.21.1",
|
54
59
|
"gsap": "^3.12.5",
|
55
|
-
"luxon": "^3.4.4"
|
60
|
+
"luxon": "^3.4.4",
|
61
|
+
"yaml": "^2.4.5"
|
56
62
|
},
|
57
63
|
"browserslist": [
|
58
64
|
"last 4 Chrome versions",
|
@@ -3,53 +3,48 @@
|
|
3
3
|
class="px-2 flex dx-action-button"
|
4
4
|
:items="activeActions"
|
5
5
|
:disabled="!hasTarget"
|
6
|
-
:tooltip="!hasTarget ? tooltip :
|
7
|
-
:loading="isSaving || loading"
|
6
|
+
:tooltip="!hasTarget ? tooltip : ''"
|
7
|
+
:loading="isSaving || !!loading"
|
8
8
|
:loading-component="loadingComponent"
|
9
9
|
@action-item="onAction"
|
10
10
|
/>
|
11
11
|
</template>
|
12
|
-
<script setup>
|
12
|
+
<script setup lang="ts">
|
13
13
|
import { computed, ref } from "vue";
|
14
|
+
import { ActionTarget, ResourceAction } from "../../types";
|
14
15
|
import { PopoverMenu } from "../Utility";
|
15
16
|
|
16
|
-
const
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
default: "First select records to perform a batch action"
|
28
|
-
},
|
29
|
-
loading: Boolean,
|
30
|
-
loadingComponent: {
|
31
|
-
type: [Function, Object],
|
32
|
-
default: undefined
|
33
|
-
}
|
17
|
+
const emit = defineEmits(["success"]);
|
18
|
+
const props = withDefaults(defineProps<{
|
19
|
+
actions: ResourceAction[],
|
20
|
+
target?: ActionTarget,
|
21
|
+
tooltip?: string,
|
22
|
+
loading?: boolean,
|
23
|
+
loadingComponent?: any
|
24
|
+
}>(), {
|
25
|
+
target: () => [],
|
26
|
+
tooltip: "First select records to perform a batch action",
|
27
|
+
loadingComponent: undefined
|
34
28
|
});
|
35
29
|
|
36
30
|
const hasTarget = computed(() => Array.isArray(props.target) ? props.target.length > 0 : !!props.target);
|
37
31
|
|
38
32
|
const activeActions = computed(() => props.actions.filter(action => {
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
if (Array.isArray(props.target)) {
|
34
|
+
return action.batchEnabled ? action.batchEnabled(props.target) : true;
|
35
|
+
}
|
42
36
|
|
43
|
-
|
37
|
+
return action.enabled ? action.enabled(props.target) : true;
|
44
38
|
}));
|
45
39
|
|
46
40
|
const isSaving = ref(false);
|
47
41
|
async function onAction(action) {
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
42
|
+
if (!action.trigger) {
|
43
|
+
throw new Error("Action must have a trigger function! Make sure you are using useActions() or implement your own trigger function.");
|
44
|
+
}
|
45
|
+
isSaving.value = true;
|
46
|
+
await action.trigger(props.target);
|
47
|
+
isSaving.value = false;
|
48
|
+
emit("success", action);
|
54
49
|
}
|
55
50
|
</script>
|
@@ -31,6 +31,7 @@
|
|
31
31
|
:label="label"
|
32
32
|
:item-count="summary?.count || 0"
|
33
33
|
:selected-count="selectedRows.length"
|
34
|
+
:sticky-colspan="summaryColSpan"
|
34
35
|
:loading="loadingSummary"
|
35
36
|
:summary="summary"
|
36
37
|
:columns="tableColumns"
|
@@ -87,6 +88,7 @@ export interface Props {
|
|
87
88
|
summary: any;
|
88
89
|
columns: TableColumn[];
|
89
90
|
rowsPerPageOptions?: number[];
|
91
|
+
summaryColSpan?: number;
|
90
92
|
}
|
91
93
|
|
92
94
|
const props = withDefaults(defineProps<Props>(), {
|
@@ -94,7 +96,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
94
96
|
pagedItems: null,
|
95
97
|
summary: null,
|
96
98
|
loadingSummary: false,
|
97
|
-
rowsPerPageOptions: () => [10, 25, 50, 100]
|
99
|
+
rowsPerPageOptions: () => [10, 25, 50, 100],
|
100
|
+
summaryColSpan: null
|
98
101
|
});
|
99
102
|
|
100
103
|
const actionTable = ref(null);
|
@@ -3,13 +3,18 @@
|
|
3
3
|
:key="rowProps.key"
|
4
4
|
:props="rowProps"
|
5
5
|
:style="columnStyle"
|
6
|
+
class="dx-column"
|
7
|
+
:class="column.columnClass"
|
6
8
|
>
|
7
9
|
<div :style="columnStyle">
|
8
10
|
<div
|
9
11
|
class="flex items-center flex-nowrap"
|
10
|
-
:class="
|
12
|
+
:class="wrapClass"
|
11
13
|
>
|
12
|
-
<div
|
14
|
+
<div
|
15
|
+
v-if="!column.hideContent"
|
16
|
+
class="flex-grow overflow-hidden"
|
17
|
+
>
|
13
18
|
<a
|
14
19
|
v-if="column.onClick"
|
15
20
|
:class="column.innerClass"
|
@@ -25,6 +30,7 @@
|
|
25
30
|
<div
|
26
31
|
v-else
|
27
32
|
:class="column.innerClass"
|
33
|
+
class="dx-column-text"
|
28
34
|
>
|
29
35
|
<RenderVnode
|
30
36
|
v-if="column.vnode"
|
@@ -42,7 +48,8 @@
|
|
42
48
|
</div>
|
43
49
|
<div
|
44
50
|
v-if="column.actionMenu"
|
45
|
-
class="flex flex-shrink-0
|
51
|
+
class="flex flex-shrink-0"
|
52
|
+
:class="{'ml-2': !column.hideContent}"
|
46
53
|
>
|
47
54
|
<ActionMenu
|
48
55
|
class="dx-column-action-menu"
|
@@ -55,12 +62,13 @@
|
|
55
62
|
</div>
|
56
63
|
</QTd>
|
57
64
|
</template>
|
58
|
-
<script setup>
|
65
|
+
<script setup lang="ts">
|
59
66
|
import { QTd } from "quasar";
|
60
67
|
import { computed } from "vue";
|
61
68
|
import { RenderVnode } from "../../Utility";
|
62
69
|
import ActionMenu from "../ActionMenu";
|
63
70
|
import { TitleColumnFormat } from "./";
|
71
|
+
import { TableColumn } from "./../../../types";
|
64
72
|
|
65
73
|
const props = defineProps({
|
66
74
|
rowProps: {
|
@@ -74,7 +82,7 @@ const props = defineProps({
|
|
74
82
|
});
|
75
83
|
|
76
84
|
const row = computed(() => props.rowProps.row);
|
77
|
-
const column = computed(() => props.rowProps.col);
|
85
|
+
const column = computed<TableColumn>(() => props.rowProps.col);
|
78
86
|
const value = computed(() => props.rowProps.value);
|
79
87
|
const isSaving = computed(() => row.value.isSaving?.value);
|
80
88
|
|
@@ -86,7 +94,7 @@ const columnStyle = computed(() => {
|
|
86
94
|
};
|
87
95
|
});
|
88
96
|
|
89
|
-
const
|
97
|
+
const wrapClass = computed(() => ({
|
90
98
|
[column.value.class || ""]: true,
|
91
99
|
"is-saving": isSaving.value,
|
92
100
|
"justify-end": column.value.align === "right",
|
@@ -3,7 +3,7 @@
|
|
3
3
|
:key="rowProps.key"
|
4
4
|
:props="rowProps"
|
5
5
|
:data-drop-zone="isResizeable && `resize-column-` + column.name"
|
6
|
-
:class="
|
6
|
+
:class="columnClass"
|
7
7
|
:style="columnStyle"
|
8
8
|
>
|
9
9
|
{{ column.label }}
|
@@ -17,67 +17,88 @@
|
|
17
17
|
</HandleDraggable>
|
18
18
|
</QTh>
|
19
19
|
</template>
|
20
|
-
<script setup>
|
20
|
+
<script setup lang="ts">
|
21
21
|
import { QTh } from "quasar";
|
22
|
-
import { computed } from "vue";
|
22
|
+
import { computed, useCssModule } from "vue";
|
23
23
|
import { DragHandleIcon as RowResizeIcon } from "../../../svg";
|
24
|
+
import { TableColumn } from "../../../types";
|
24
25
|
import { HandleDraggable } from "../../DragAndDrop";
|
25
26
|
|
26
27
|
const emit = defineEmits(["update:model-value"]);
|
27
28
|
const props = defineProps({
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
29
|
+
modelValue: {
|
30
|
+
type: Object,
|
31
|
+
required: true
|
32
|
+
},
|
33
|
+
name: {
|
34
|
+
type: String,
|
35
|
+
required: true
|
36
|
+
},
|
37
|
+
rowProps: {
|
38
|
+
type: Object,
|
39
|
+
required: true
|
40
|
+
}
|
40
41
|
});
|
41
42
|
|
42
|
-
const column = computed(() => props.rowProps.col);
|
43
|
+
const column = computed<TableColumn>(() => props.rowProps.col);
|
43
44
|
const isResizeable = computed(() => column.value.resizeable);
|
44
45
|
|
45
46
|
const columnStyle = computed(() => {
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
const width = props.settings?.width || column.value.width;
|
48
|
+
return {
|
49
|
+
width: width ? `${width}px` : undefined,
|
50
|
+
minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined,
|
51
|
+
...(column.value.headerStyle || {})
|
52
|
+
};
|
52
53
|
});
|
53
54
|
|
55
|
+
const clsModule = useCssModule("cls");
|
56
|
+
const columnClass = computed(() => {
|
57
|
+
const colCls = {
|
58
|
+
[clsModule["handle-drop-zone"]]: isResizeable.value,
|
59
|
+
"dx-column-shrink": column.value.shrink
|
60
|
+
};
|
61
|
+
|
62
|
+
const headerClass = column.value.headerClass;
|
63
|
+
if (headerClass) {
|
64
|
+
if (typeof headerClass === "string") {
|
65
|
+
colCls[headerClass] = true;
|
66
|
+
} else {
|
67
|
+
Object.keys(headerClass).forEach((key) => {
|
68
|
+
colCls[key] = headerClass[key];
|
69
|
+
});
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
return colCls;
|
74
|
+
});
|
54
75
|
|
55
76
|
function onResizeColumn(val) {
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
77
|
+
const settings = {
|
78
|
+
...props.modelValue,
|
79
|
+
[column.value.name]: {
|
80
|
+
width: Math.max(Math.min(val.distance + val.startDropZoneSize, column.value.maxWidth || 500), column.value.minWidth || 80)
|
81
|
+
}
|
82
|
+
};
|
83
|
+
emit("update:model-value", settings);
|
63
84
|
}
|
64
85
|
</script>
|
65
86
|
|
66
87
|
<style lang="scss" module="cls">
|
67
88
|
.handle-drop-zone {
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
89
|
+
.resize-handle {
|
90
|
+
position: absolute;
|
91
|
+
top: 0;
|
92
|
+
right: -.45em;
|
93
|
+
width: .9em;
|
94
|
+
opacity: 0;
|
95
|
+
transition: all .3s;
|
96
|
+
}
|
76
97
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
98
|
+
&:hover {
|
99
|
+
.resize-handle {
|
100
|
+
opacity: 1;
|
101
|
+
}
|
102
|
+
}
|
82
103
|
}
|
83
104
|
</style>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<template>
|
2
|
+
<div>
|
3
|
+
<RenderedForm
|
4
|
+
v-bind="renderedFormProps"
|
5
|
+
v-model:values="input"
|
6
|
+
empty-value=""
|
7
|
+
:saving="action.isApplying"
|
8
|
+
@update:values="action.trigger(target, input)"
|
9
|
+
/>
|
10
|
+
</div>
|
11
|
+
</template>
|
12
|
+
<script setup lang="ts">
|
13
|
+
import { ref, watch } from "vue";
|
14
|
+
import { ActionTargetItem, AnyObject, Form, ResourceAction } from "../../../types";
|
15
|
+
import RenderedForm from "./RenderedForm.vue";
|
16
|
+
|
17
|
+
interface ActionFormProps {
|
18
|
+
action: ResourceAction;
|
19
|
+
target: ActionTargetItem;
|
20
|
+
form: Form;
|
21
|
+
noLabel?: boolean;
|
22
|
+
showName?: boolean;
|
23
|
+
disable?: boolean;
|
24
|
+
readonly?: boolean;
|
25
|
+
clearable?: boolean;
|
26
|
+
fieldClass?: string;
|
27
|
+
savingClass?: string;
|
28
|
+
}
|
29
|
+
|
30
|
+
const props = defineProps<ActionFormProps>();
|
31
|
+
const renderedFormProps = {
|
32
|
+
form: props.form,
|
33
|
+
noLabel: props.noLabel,
|
34
|
+
showName: props.showName,
|
35
|
+
disable: props.disable,
|
36
|
+
readonly: props.readonly,
|
37
|
+
clearable: props.clearable,
|
38
|
+
fieldClass: props.fieldClass,
|
39
|
+
savingClass: props.savingClass
|
40
|
+
};
|
41
|
+
|
42
|
+
const input: AnyObject = ref({ ...props.target });
|
43
|
+
const fieldStatus: AnyObject = {};
|
44
|
+
|
45
|
+
// Only update field values from target changes when the field is not already being saved
|
46
|
+
watch(() => props.target, (target: ActionTargetItem) => {
|
47
|
+
if (!target) return;
|
48
|
+
|
49
|
+
for (let field of props.form.fields) {
|
50
|
+
if (!fieldStatus[field.name]?.isSaving) {
|
51
|
+
input.value[field.name] = target[field.name];
|
52
|
+
}
|
53
|
+
}
|
54
|
+
});
|
55
|
+
</script>
|
@@ -3,12 +3,13 @@
|
|
3
3
|
class="danx-edit-on-click-text-field flex flex-nowrap items-center rounded overflow-ellipsis"
|
4
4
|
:class="{[props.class]: true, 'is-readonly': readonly, 'cursor-pointer': !isEditing && !readonly}"
|
5
5
|
@click="onEdit"
|
6
|
+
@focusout="isEditing = false"
|
6
7
|
>
|
7
8
|
<div
|
8
9
|
ref="editableBox"
|
9
10
|
:contenteditable="!readonly && isEditing"
|
10
11
|
class="flex-grow p-2 rounded outline-none border-none"
|
11
|
-
:class="{[editingClass]: isEditing}"
|
12
|
+
:class="{[editingClass]: isEditing, [inputClass]: true}"
|
12
13
|
@input="text = $event.target.innerText"
|
13
14
|
>
|
14
15
|
{{ text }}
|
@@ -33,15 +34,20 @@
|
|
33
34
|
import { FaSolidCheck as DoneIcon, FaSolidPencil as EditIcon } from "danx-icon";
|
34
35
|
import { nextTick, ref } from "vue";
|
35
36
|
|
36
|
-
export interface
|
37
|
-
class?:
|
38
|
-
editingClass?:
|
37
|
+
export interface EditOnClickTextFieldProps {
|
38
|
+
class?: string;
|
39
|
+
editingClass?: string;
|
40
|
+
inputClass?: string;
|
39
41
|
readonly?: boolean;
|
40
42
|
}
|
41
43
|
|
42
44
|
const editableBox = ref<HTMLElement | null>(null);
|
43
45
|
const text = defineModel({ type: String });
|
44
|
-
const props = defineProps<
|
46
|
+
const props = withDefaults(defineProps<EditOnClickTextFieldProps>(), {
|
47
|
+
class: "hover:bg-slate-300",
|
48
|
+
editingClass: "bg-slate-500",
|
49
|
+
inputClass: "whitespace-normal"
|
50
|
+
});
|
45
51
|
const isEditing = ref(false);
|
46
52
|
function onEdit() {
|
47
53
|
if (props.readonly) return;
|
@@ -62,6 +62,7 @@ function upload() {
|
|
62
62
|
async function onAttachFiles({ target: { files } }) {
|
63
63
|
emit("uploading", files);
|
64
64
|
let fileUpload = new FileUpload(files)
|
65
|
+
.prepare()
|
65
66
|
.onProgress(({ file, progress }) => {
|
66
67
|
file.progress = progress;
|
67
68
|
emit("file-progress", file);
|
@@ -62,32 +62,36 @@
|
|
62
62
|
</QTab>
|
63
63
|
</QTabs>
|
64
64
|
</div>
|
65
|
-
<
|
65
|
+
<template
|
66
66
|
v-for="(field, index) in mappedFields"
|
67
67
|
:key="field.id"
|
68
|
-
:class="{ 'mt-4': index > 0, [fieldClass]: true }"
|
69
68
|
>
|
70
|
-
<
|
71
|
-
v-
|
72
|
-
:
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
69
|
+
<div
|
70
|
+
v-show="isFieldEnabled(field)"
|
71
|
+
:class="{ 'mt-4': index > 0, [fieldClass]: true }"
|
72
|
+
>
|
73
|
+
<RenderVnode
|
74
|
+
v-if="field.vnode"
|
75
|
+
:vnode="field.vnode"
|
76
|
+
:props="getVnodeProps(field)"
|
77
|
+
:params="fieldInputs"
|
78
|
+
@update:model-value="onInput(field.name, $event)"
|
79
|
+
/>
|
80
|
+
<Component
|
81
|
+
:is="field.component"
|
82
|
+
:key="field.name + '-' + currentVariation"
|
83
|
+
:model-value="getFieldValue(field.name)"
|
84
|
+
:field="field"
|
85
|
+
:label="field.label || undefined"
|
86
|
+
:no-label="noLabel"
|
87
|
+
:show-name="showName"
|
88
|
+
:clearable="field.clearable || clearable"
|
89
|
+
:disable="disable"
|
90
|
+
:readonly="readonly"
|
91
|
+
@update:model-value="onInput(field.name, $event)"
|
92
|
+
/>
|
93
|
+
</div>
|
94
|
+
</template>
|
91
95
|
<div
|
92
96
|
v-if="savedAt"
|
93
97
|
:class="savingClass"
|
@@ -136,7 +140,7 @@ import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@h
|
|
136
140
|
import { computed, ref } from "vue";
|
137
141
|
import { fDateTime, FlashMessages, incrementName, replace } from "../../../helpers";
|
138
142
|
import { TrashIcon as RemoveIcon } from "../../../svg";
|
139
|
-
import {
|
143
|
+
import { AnyObject, FormFieldValue, RenderedFormProps } from "../../../types";
|
140
144
|
import { ConfirmDialog, RenderVnode } from "../../Utility";
|
141
145
|
import {
|
142
146
|
BooleanField,
|
@@ -150,28 +154,12 @@ import {
|
|
150
154
|
WysiwygField
|
151
155
|
} from "./Fields";
|
152
156
|
|
153
|
-
export interface RenderedFormProps {
|
154
|
-
values?: FormFieldValue[] | object;
|
155
|
-
form: Form;
|
156
|
-
noLabel?: boolean;
|
157
|
-
showName?: boolean;
|
158
|
-
disable?: boolean;
|
159
|
-
readonly?: boolean;
|
160
|
-
saving?: boolean;
|
161
|
-
clearable?: boolean;
|
162
|
-
emptyValue?: string | number | boolean;
|
163
|
-
canModifyVariations?: boolean;
|
164
|
-
fieldClass?: string;
|
165
|
-
savingClass?: string;
|
166
|
-
savedAt?: string;
|
167
|
-
}
|
168
|
-
|
169
157
|
const props = withDefaults(defineProps<RenderedFormProps>(), {
|
170
158
|
values: null,
|
171
159
|
emptyValue: undefined,
|
172
160
|
fieldClass: "",
|
173
161
|
savingClass: "text-sm text-slate-500 justify-end mt-4",
|
174
|
-
savedAt:
|
162
|
+
savedAt: undefined
|
175
163
|
});
|
176
164
|
|
177
165
|
const emit = defineEmits(["update:values"]);
|
@@ -190,9 +178,9 @@ const FORM_FIELD_MAP = {
|
|
190
178
|
|
191
179
|
const mappedFields = props.form.fields.map((field) => ({
|
192
180
|
placeholder: `Enter ${field.label}`,
|
181
|
+
default: field.type === "BOOLEAN" ? false : "",
|
193
182
|
...field,
|
194
|
-
component: field.component || FORM_FIELD_MAP[field.type]
|
195
|
-
default: field.type === "BOOLEAN" ? false : ""
|
183
|
+
component: field.component || FORM_FIELD_MAP[field.type]
|
196
184
|
}));
|
197
185
|
|
198
186
|
const fieldResponses = computed(() => {
|
@@ -201,6 +189,24 @@ const fieldResponses = computed(() => {
|
|
201
189
|
return Object.entries(props.values).map(([name, value]) => ({ name, value, variation: "" }));
|
202
190
|
});
|
203
191
|
|
192
|
+
const fieldInputs = computed(() => {
|
193
|
+
const inputs: AnyObject = {};
|
194
|
+
for (const field of mappedFields) {
|
195
|
+
inputs[field.name] = getFieldValue(field.name);
|
196
|
+
}
|
197
|
+
return inputs;
|
198
|
+
});
|
199
|
+
|
200
|
+
function isFieldEnabled(field) {
|
201
|
+
if (field.enabled === undefined) return true;
|
202
|
+
|
203
|
+
if (typeof field.enabled === "function") {
|
204
|
+
return field.enabled(fieldInputs.value);
|
205
|
+
}
|
206
|
+
|
207
|
+
return field.enabled;
|
208
|
+
}
|
209
|
+
|
204
210
|
function getVnodeProps(field) {
|
205
211
|
return {
|
206
212
|
modelValue: getFieldValue(field.name),
|
@@ -226,16 +232,16 @@ const currentVariation = ref(variationNames.value[0] || "");
|
|
226
232
|
const newVariationName = ref("");
|
227
233
|
const variationToEdit = ref<boolean | string>(false);
|
228
234
|
const variationToDelete = ref("");
|
229
|
-
const canAddVariation = computed(() => props.canModifyVariations && !props.readonly && !props.disable && variationNames.value.length < props.form.variations);
|
235
|
+
const canAddVariation = computed(() => props.canModifyVariations && !props.readonly && !props.disable && variationNames.value.length < (props.form.variations || 0));
|
230
236
|
|
231
|
-
function getFieldResponse(name, variation
|
237
|
+
function getFieldResponse(name: string, variation?: string) {
|
232
238
|
if (!fieldResponses.value) return undefined;
|
233
239
|
return fieldResponses.value.find((fr: FormFieldValue) => fr.variation === (variation !== undefined ? variation : currentVariation.value) && fr.name === name);
|
234
240
|
}
|
235
|
-
function getFieldValue(name) {
|
241
|
+
function getFieldValue(name: string) {
|
236
242
|
return getFieldResponse(name)?.value;
|
237
243
|
}
|
238
|
-
function onInput(name, value) {
|
244
|
+
function onInput(name: string, value: any) {
|
239
245
|
const fieldResponse = getFieldResponse(name);
|
240
246
|
const newFieldResponse = {
|
241
247
|
name,
|
@@ -291,11 +297,12 @@ function updateValues(values: FormFieldValue[]) {
|
|
291
297
|
let updatedValues: FormFieldValue[] | object = values;
|
292
298
|
|
293
299
|
if (!Array.isArray(props.values)) {
|
294
|
-
updatedValues = values.reduce((acc, v) => {
|
300
|
+
updatedValues = values.reduce((acc: AnyObject, v) => {
|
295
301
|
acc[v.name] = v.value;
|
296
302
|
return acc;
|
297
303
|
}, {});
|
298
304
|
}
|
305
|
+
|
299
306
|
emit("update:values", updatedValues);
|
300
307
|
}
|
301
308
|
|
@@ -311,8 +318,8 @@ function onRemoveVariation(name: string) {
|
|
311
318
|
variationToDelete.value = "";
|
312
319
|
}
|
313
320
|
|
314
|
-
function isVariationFormComplete(variation) {
|
315
|
-
const requiredGroups = {};
|
321
|
+
function isVariationFormComplete(variation: string) {
|
322
|
+
const requiredGroups: AnyObject = {};
|
316
323
|
return props.form.fields.filter(r => r.required || r.required_group).every((field) => {
|
317
324
|
const fieldResponse = getFieldResponse(field.name, variation);
|
318
325
|
const hasValue = !!fieldResponse && fieldResponse.value !== null;
|