quasar-ui-danx 0.4.9 → 0.4.12
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/danx.es.js +6509 -6230
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +7 -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 +8 -3
- 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/FieldLabel.vue +18 -15
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +1 -0
- package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +44 -15
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +1 -1
- package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +12 -13
- package/src/components/ActionTable/Form/Fields/NumberField.vue +40 -55
- package/src/components/ActionTable/Form/Fields/SelectField.vue +4 -3
- package/src/components/ActionTable/Form/Fields/TextField.vue +31 -12
- package/src/components/ActionTable/Form/RenderedForm.vue +11 -10
- 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/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/config/index.ts +2 -1
- package/src/helpers/FileUpload.ts +59 -8
- package/src/helpers/actions.ts +27 -27
- package/src/helpers/download.ts +8 -2
- package/src/helpers/formats.ts +79 -9
- 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 +11 -0
- package/src/index.ts +1 -0
- package/src/styles/danx.scss +5 -0
- package/src/styles/index.scss +1 -0
- package/src/styles/quasar-reset.scss +2 -0
- package/src/styles/themes/danx/action-table.scss +24 -13
- package/src/styles/themes/danx/forms.scss +1 -19
- package/src/types/actions.d.ts +13 -4
- package/src/types/controls.d.ts +4 -4
- package/src/types/fields.d.ts +10 -9
- package/src/types/files.d.ts +10 -5
- 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.12",
|
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",
|
@@ -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;
|
@@ -1,27 +1,30 @@
|
|
1
1
|
<template>
|
2
|
-
<
|
2
|
+
<div class="dx-field-label">
|
3
3
|
<slot>
|
4
|
-
|
5
|
-
|
4
|
+
<div class="dx-field-label-text">
|
5
|
+
<div class="dx-field-label-label">
|
6
|
+
{{ label }}
|
7
|
+
</div>
|
8
|
+
<div
|
9
|
+
v-if="name"
|
10
|
+
class="dx-field-label-name"
|
11
|
+
>
|
12
|
+
({{ name }})
|
13
|
+
</div>
|
14
|
+
</div>
|
6
15
|
</slot>
|
7
16
|
<span
|
8
|
-
v-if="
|
17
|
+
v-if="required"
|
9
18
|
class="dx-field-required"
|
10
|
-
>{{ requiredLabel }}</span>
|
11
|
-
</
|
19
|
+
>{{ requiredLabel || "*" }}</span>
|
20
|
+
</div>
|
12
21
|
</template>
|
13
22
|
|
14
23
|
<script setup lang="ts">
|
15
|
-
|
16
|
-
import { FormField } from "../../../../types";
|
17
|
-
|
18
|
-
const props = defineProps<{
|
19
|
-
field?: FormField;
|
24
|
+
defineProps<{
|
20
25
|
label?: string;
|
21
|
-
|
26
|
+
name?: string;
|
22
27
|
required?: boolean;
|
28
|
+
requiredLabel?: string;
|
23
29
|
}>();
|
24
|
-
|
25
|
-
const labelText = computed(() => props.label || props.field?.label);
|
26
|
-
const requiredLabel = computed(() => props.field?.required_group || (props.required || props.field?.required ? "*" : ""));
|
27
30
|
</script>
|
@@ -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);
|
@@ -1,22 +1,51 @@
|
|
1
1
|
<template>
|
2
2
|
<div>
|
3
|
-
<div class="text-xs font-bold">
|
4
|
-
|
5
|
-
|
3
|
+
<div class="text-xs font-bold">
|
4
|
+
{{ label }}
|
5
|
+
</div>
|
6
|
+
<div :class="valueClass">
|
7
|
+
<a
|
8
|
+
v-if="url"
|
9
|
+
target="_blank"
|
10
|
+
:href="url"
|
11
|
+
:class="valueClass"
|
12
|
+
>
|
13
|
+
<slot>{{ formattedValue }}</slot>
|
14
|
+
</a>
|
15
|
+
<template v-else>
|
16
|
+
<slot>{{ formattedValue }}</slot>
|
17
|
+
</template>
|
6
18
|
</div>
|
7
19
|
</div>
|
8
20
|
</template>
|
9
|
-
<script setup>
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
<script setup lang="ts">
|
22
|
+
import { computed } from "vue";
|
23
|
+
import { fBoolean, fNumber } from "../../../../helpers";
|
24
|
+
|
25
|
+
export interface LabelValueBlockProps {
|
26
|
+
label: string;
|
27
|
+
value: string | number | boolean;
|
28
|
+
url?: string;
|
29
|
+
dense?: boolean;
|
30
|
+
nowrap?: boolean;
|
31
|
+
}
|
32
|
+
|
33
|
+
const props = withDefaults(defineProps<LabelValueBlockProps>(), {
|
34
|
+
value: "",
|
35
|
+
url: ""
|
36
|
+
});
|
37
|
+
|
38
|
+
const valueClass = computed(() => ({ "mt-2": !props.dense, "mt-1": props.dense, "text-no-wrap": props.nowrap }));
|
39
|
+
const formattedValue = computed(() => {
|
40
|
+
switch (typeof props.value) {
|
41
|
+
case "boolean":
|
42
|
+
return fBoolean(props.value);
|
43
|
+
|
44
|
+
case "number":
|
45
|
+
return fNumber(props.value);
|
46
|
+
|
47
|
+
default:
|
48
|
+
return props.value || "-";
|
49
|
+
}
|
21
50
|
});
|
22
51
|
</script>
|
@@ -12,7 +12,6 @@
|
|
12
12
|
:field="field"
|
13
13
|
:no-label="!field.label"
|
14
14
|
label-class="text-xs font-bold text-zinc-800"
|
15
|
-
parent-class="tight-label"
|
16
15
|
input-class="!py-0"
|
17
16
|
dense
|
18
17
|
type="textarea"
|
@@ -29,29 +28,29 @@ import TextField from "./TextField";
|
|
29
28
|
|
30
29
|
const emit = defineEmits(["update:model-value"]);
|
31
30
|
const props = defineProps({
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
31
|
+
modelValue: {
|
32
|
+
type: [String, Number, Object],
|
33
|
+
default: ""
|
34
|
+
},
|
35
|
+
field: {
|
36
|
+
type: Object,
|
37
|
+
default: null
|
38
|
+
}
|
40
39
|
});
|
41
40
|
|
42
41
|
const selectedFieldName = ref(props.field.defaultOption);
|
43
42
|
const searchList = computed(() => props.modelValue && props.modelValue[selectedFieldName.value]);
|
44
43
|
const textInput = ref(formatModelValue());
|
45
44
|
function onChange() {
|
46
|
-
|
47
|
-
|
45
|
+
textInput.value = textInput.value?.replace(/\n/g, ",").replace(/,{2,}/g, ",") || "";
|
46
|
+
emit("update:model-value", textInput.value ? { [selectedFieldName.value]: textInput.value.split(",") } : undefined);
|
48
47
|
}
|
49
48
|
|
50
49
|
function formatModelValue() {
|
51
|
-
|
50
|
+
return Array.isArray(searchList.value) ? searchList.value?.join(",") : "";
|
52
51
|
}
|
53
52
|
|
54
53
|
watch(() => props.modelValue, () => {
|
55
|
-
|
54
|
+
textInput.value = formatModelValue();
|
56
55
|
});
|
57
56
|
</script>
|