quasar-ui-danx 0.4.2 → 0.4.4
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 +7127 -6615
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +11 -5
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -1
- package/src/components/ActionTable/ActionTable.vue +28 -41
- package/src/components/ActionTable/Columns/ActionTableColumn.vue +19 -18
- package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +6 -6
- package/src/components/ActionTable/Filters/{FilterFieldList.vue → FilterList.vue} +26 -26
- package/src/components/ActionTable/Filters/FilterableField.vue +28 -31
- package/src/components/ActionTable/Filters/index.ts +2 -2
- package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +71 -0
- package/src/components/ActionTable/Form/Fields/FieldLabel.vue +8 -13
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +48 -44
- package/src/components/ActionTable/Form/Fields/SelectField.vue +24 -38
- package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +28 -33
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +15 -15
- package/src/components/ActionTable/Form/Fields/SliderNumberField.vue +45 -0
- package/src/components/ActionTable/Form/Fields/TextField.vue +47 -66
- package/src/components/ActionTable/Form/Fields/index.ts +2 -0
- package/src/components/ActionTable/Form/RenderedForm.vue +50 -9
- package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +17 -0
- package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
- package/src/components/ActionTable/Form/index.ts +1 -0
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +16 -15
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +6 -6
- package/src/components/ActionTable/listControls.ts +106 -166
- package/src/components/ActionTable/listHelpers.ts +2 -3
- package/src/components/ActionTable/tableColumns.ts +3 -27
- package/src/components/AuditHistory/AuditHistoryItemValue.vue +26 -26
- package/src/components/PanelsDrawer/PanelsDrawer.vue +17 -4
- package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +6 -11
- package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +20 -20
- package/src/components/Utility/Dialogs/ConfirmActionDialog.vue +39 -0
- package/src/components/Utility/Dialogs/ConfirmDialog.vue +10 -24
- package/src/components/Utility/Dialogs/DialogLayout.vue +10 -28
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +42 -36
- package/src/components/Utility/Dialogs/index.ts +1 -0
- package/src/components/Utility/Files/FilePreview.vue +76 -73
- package/src/components/Utility/Layouts/ContentDrawer.vue +24 -31
- package/src/components/Utility/Tools/ActionVnode.vue +3 -3
- package/src/components/Utility/Tools/RenderVnode.vue +1 -1
- package/src/components/Utility/Transitions/MaxHeightTransition.vue +26 -0
- package/src/components/Utility/Transitions/index.ts +1 -0
- package/src/config/index.ts +36 -31
- package/src/helpers/FileUpload.ts +295 -297
- package/src/helpers/FlashMessages.ts +80 -71
- package/src/helpers/actions.ts +102 -82
- package/src/helpers/download.ts +189 -189
- package/src/helpers/downloadPdf.ts +55 -52
- package/src/helpers/formats.ts +151 -109
- package/src/helpers/index.ts +2 -0
- package/src/helpers/multiFileUpload.ts +72 -58
- package/src/helpers/objectStore.ts +52 -0
- package/src/helpers/request.ts +70 -51
- package/src/helpers/routes.ts +29 -0
- package/src/helpers/storage.ts +7 -3
- package/src/helpers/utils.ts +47 -29
- package/src/styles/quasar-reset.scss +16 -1
- package/src/styles/themes/danx/dialogs.scss +4 -0
- package/src/types/actions.d.ts +43 -0
- package/src/types/config.d.ts +15 -0
- package/src/types/controls.d.ts +99 -0
- package/src/types/dialogs.d.ts +32 -0
- package/src/types/fields.d.ts +20 -0
- package/src/types/files.d.ts +54 -0
- package/src/types/formats.d.ts +4 -0
- package/src/{components/ActionTable/Form/form.d.ts → types/forms.d.ts} +6 -0
- package/src/types/index.d.ts +12 -0
- package/src/types/requests.d.ts +13 -0
- package/src/types/shared.d.ts +15 -0
- package/src/types/tables.d.ts +27 -0
- package/types/index.d.ts +1 -1
- /package/src/components/ActionTable/Filters/{FilterFieldItem.vue → FilterItem.vue} +0 -0
@@ -64,47 +64,33 @@
|
|
64
64
|
</QSelect>
|
65
65
|
</div>
|
66
66
|
</template>
|
67
|
-
<script setup>
|
67
|
+
<script setup lang="ts">
|
68
68
|
import { ChevronDownIcon as DropDownIcon } from "@heroicons/vue/outline";
|
69
|
-
import { QSelect } from "quasar";
|
69
|
+
import { QSelect, QSelectProps } from "quasar";
|
70
70
|
import { computed, isRef, nextTick, ref } from "vue";
|
71
71
|
|
72
|
+
export interface Props extends QSelectProps {
|
73
|
+
modelValue?: any;
|
74
|
+
placeholder?: string;
|
75
|
+
selectionLabel?: string | ((option) => string);
|
76
|
+
chipLimit?: number;
|
77
|
+
inputClass?: string;
|
78
|
+
selectionClass?: string;
|
79
|
+
options?: unknown[];
|
80
|
+
filterable?: boolean;
|
81
|
+
filterFn?: (val: string) => void;
|
82
|
+
}
|
83
|
+
|
72
84
|
const emit = defineEmits(["update:model-value", "search", "update"]);
|
73
|
-
const props = defineProps({
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
},
|
83
|
-
selectionLabel: {
|
84
|
-
type: String,
|
85
|
-
default: null
|
86
|
-
},
|
87
|
-
chipLimit: {
|
88
|
-
type: Number,
|
89
|
-
default: 3
|
90
|
-
},
|
91
|
-
inputClass: {
|
92
|
-
type: String,
|
93
|
-
default: ""
|
94
|
-
},
|
95
|
-
selectionClass: {
|
96
|
-
type: String,
|
97
|
-
default: ""
|
98
|
-
},
|
99
|
-
options: {
|
100
|
-
type: Array,
|
101
|
-
default: () => []
|
102
|
-
},
|
103
|
-
filterable: Boolean,
|
104
|
-
filterFn: {
|
105
|
-
type: Function,
|
106
|
-
default: null
|
107
|
-
}
|
85
|
+
const props = withDefaults(defineProps<Props>(), {
|
86
|
+
modelValue: undefined,
|
87
|
+
placeholder: "",
|
88
|
+
selectionLabel: null,
|
89
|
+
chipLimit: 3,
|
90
|
+
inputClass: "",
|
91
|
+
selectionClass: "",
|
92
|
+
options: () => [],
|
93
|
+
filterFn: null
|
108
94
|
});
|
109
95
|
|
110
96
|
const selectField = ref(null);
|
@@ -136,7 +122,7 @@ const computedOptions = computed(() => {
|
|
136
122
|
|
137
123
|
const filteredOptions = computed(() => {
|
138
124
|
if (filter.value && !props.filterFn) {
|
139
|
-
return computedOptions.value.filter(o => o.label.toLocaleLowerCase().indexOf(filter.value
|
125
|
+
return computedOptions.value.filter(o => o.label.toLocaleLowerCase().indexOf(filter.value?.toLowerCase()) > -1);
|
140
126
|
} else {
|
141
127
|
return computedOptions.value;
|
142
128
|
}
|
@@ -31,55 +31,50 @@
|
|
31
31
|
</div>
|
32
32
|
</div>
|
33
33
|
</template>
|
34
|
-
<script setup>
|
34
|
+
<script setup lang="ts">
|
35
35
|
import { ref, watch } from "vue";
|
36
36
|
import { remove } from "../../../../helpers";
|
37
37
|
import SelectField from "./SelectField";
|
38
38
|
|
39
|
+
export interface SelectWithChildrenFieldProps {
|
40
|
+
modelValue?: string[];
|
41
|
+
label?: string;
|
42
|
+
placeholder?: string;
|
43
|
+
options?: any[];
|
44
|
+
loading?: boolean;
|
45
|
+
}
|
46
|
+
|
39
47
|
const emit = defineEmits(["update:model-value"]);
|
40
|
-
const props = defineProps({
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
label: {
|
46
|
-
type: String,
|
47
|
-
default: "Selection"
|
48
|
-
},
|
49
|
-
placeholder: {
|
50
|
-
type: String,
|
51
|
-
default: "Select an option"
|
52
|
-
},
|
53
|
-
options: {
|
54
|
-
type: Array,
|
55
|
-
default: () => []
|
56
|
-
},
|
57
|
-
loading: Boolean
|
48
|
+
const props = withDefaults(defineProps<SelectWithChildrenFieldProps>(), {
|
49
|
+
modelValue: () => [],
|
50
|
+
label: "Selection",
|
51
|
+
placeholder: "Select an option",
|
52
|
+
options: () => []
|
58
53
|
});
|
59
54
|
|
60
55
|
function resolveSelectedOption() {
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
if (props.modelValue?.length > 0) {
|
57
|
+
return props.options.find((option) => option.children.find(child => props.modelValue.includes(child.id)));
|
58
|
+
}
|
64
59
|
|
65
|
-
|
60
|
+
return null;
|
66
61
|
}
|
67
62
|
const selectedOption = ref(resolveSelectedOption());
|
68
63
|
const selectedChildren = ref(props.modelValue || []);
|
69
64
|
function onSelectChild(child) {
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
65
|
+
if (selectedChildren.value.includes(child.id)) {
|
66
|
+
selectedChildren.value = remove(selectedChildren.value, child.id);
|
67
|
+
} else {
|
68
|
+
selectedChildren.value.push(child.id);
|
69
|
+
}
|
70
|
+
emit("update:model-value", selectedChildren.value.length > 0 ? selectedChildren.value : undefined);
|
76
71
|
}
|
77
72
|
function onSelectOption() {
|
78
|
-
|
79
|
-
|
73
|
+
selectedChildren.value = [];
|
74
|
+
emit("update:model-value", undefined);
|
80
75
|
}
|
81
76
|
watch(() => props.modelValue, (value) => {
|
82
|
-
|
83
|
-
|
77
|
+
selectedOption.value = resolveSelectedOption();
|
78
|
+
selectedChildren.value = value || [];
|
84
79
|
});
|
85
80
|
</script>
|
@@ -35,7 +35,7 @@
|
|
35
35
|
v-if="!readonly || uploadedFile"
|
36
36
|
class="w-32 cursor-pointer mt-2"
|
37
37
|
:class="{'border border-dashed border-blue-600': !uploadedFile, 'mx-auto': !readonly}"
|
38
|
-
:
|
38
|
+
:file="uploadedFile"
|
39
39
|
downloadable
|
40
40
|
@click="!disable && $refs.file.click()"
|
41
41
|
/>
|
@@ -56,24 +56,24 @@ import FieldLabel from "./FieldLabel";
|
|
56
56
|
|
57
57
|
const emit = defineEmits(["update:model-value"]);
|
58
58
|
const props = defineProps({
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
59
|
+
modelValue: {
|
60
|
+
type: [Object, String],
|
61
|
+
default: null
|
62
|
+
},
|
63
|
+
field: {
|
64
|
+
type: Object,
|
65
|
+
required: true
|
66
|
+
},
|
67
|
+
showName: Boolean,
|
68
|
+
disable: Boolean,
|
69
|
+
readonly: Boolean
|
70
70
|
});
|
71
71
|
const { onComplete, onDrop, onFileSelected, uploadedFile, clearUploadedFile } = useSingleFileUpload();
|
72
72
|
onComplete(() => emit("update:model-value", uploadedFile.value));
|
73
73
|
|
74
74
|
onMounted(() => {
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
if (props.modelValue) {
|
76
|
+
uploadedFile.value = props.modelValue;
|
77
|
+
}
|
78
78
|
});
|
79
79
|
</script>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="dx-slider-number-field">
|
3
|
+
<FieldLabel
|
4
|
+
:label="label"
|
5
|
+
:field="field"
|
6
|
+
/>
|
7
|
+
<QSlider
|
8
|
+
v-bind="sliderProps"
|
9
|
+
:min="min"
|
10
|
+
:max="max"
|
11
|
+
label
|
12
|
+
label-always
|
13
|
+
switch-label-side
|
14
|
+
:price-label="priceLabel"
|
15
|
+
/>
|
16
|
+
</div>
|
17
|
+
</template>
|
18
|
+
|
19
|
+
<script setup lang="ts">
|
20
|
+
import { QSliderProps } from "quasar";
|
21
|
+
import { computed } from "vue";
|
22
|
+
import { FormField } from "../../../../types";
|
23
|
+
import FieldLabel from "./FieldLabel";
|
24
|
+
|
25
|
+
export interface SliderFieldProps extends QSliderProps {
|
26
|
+
modelValue?: number | null;
|
27
|
+
field?: FormField;
|
28
|
+
label?: string;
|
29
|
+
currency?: boolean;
|
30
|
+
percent?: boolean;
|
31
|
+
}
|
32
|
+
|
33
|
+
defineEmits(["update:model-value"]);
|
34
|
+
const props = defineProps<SliderFieldProps>();
|
35
|
+
const sliderProps = computed(() => ({ ...props, field: undefined, label: !!props.label }));
|
36
|
+
const priceLabel = computed(() => {
|
37
|
+
if (props.currency) {
|
38
|
+
return "$" + props.modelValue;
|
39
|
+
} else if (props.percent) {
|
40
|
+
return props.modelValue + "%";
|
41
|
+
} else {
|
42
|
+
return props.modelValue;
|
43
|
+
}
|
44
|
+
});
|
45
|
+
</script>
|
@@ -1,82 +1,63 @@
|
|
1
1
|
<template>
|
2
2
|
<div>
|
3
|
-
<QInput
|
4
|
-
v-if="!readonly"
|
5
|
-
:data-dusk="'text-field-' + field?.id"
|
6
|
-
:data-testid="'text-field-' + field?.id"
|
7
|
-
:placeholder="field?.placeholder"
|
8
|
-
outlined
|
9
|
-
dense
|
10
|
-
:disable="disabled"
|
11
|
-
:label-slot="!noLabel"
|
12
|
-
:input-class="inputClass"
|
13
|
-
:class="parentClass"
|
14
|
-
stack-label
|
15
|
-
:type="type"
|
16
|
-
:model-value="modelValue"
|
17
|
-
:debounce="debounce"
|
18
|
-
@keydown.enter="$emit('submit')"
|
19
|
-
@update:model-value="$emit('update:model-value', $event)"
|
20
|
-
>
|
21
|
-
<template #label>
|
22
|
-
<FieldLabel
|
23
|
-
:field="field"
|
24
|
-
:label="label"
|
25
|
-
:show-name="showName"
|
26
|
-
:class="labelClass"
|
27
|
-
/>
|
28
|
-
</template>
|
29
|
-
</QInput>
|
30
3
|
<div v-if="readonly">
|
31
4
|
<LabelValueBlock
|
32
|
-
:label="label || field
|
5
|
+
:label="label || field?.label || 'Text'"
|
33
6
|
:value="modelValue"
|
34
7
|
/>
|
35
8
|
</div>
|
9
|
+
<template v-else>
|
10
|
+
<QInput
|
11
|
+
:placeholder="field?.placeholder"
|
12
|
+
outlined
|
13
|
+
dense
|
14
|
+
:readonly="readonly"
|
15
|
+
:autogrow="autogrow"
|
16
|
+
:disable="disabled"
|
17
|
+
:label-slot="!noLabel"
|
18
|
+
:input-class="inputClass"
|
19
|
+
:class="parentClass"
|
20
|
+
stack-label
|
21
|
+
:type="type"
|
22
|
+
:model-value="modelValue"
|
23
|
+
:maxlength="allowOverMax ? undefined : field?.maxLength"
|
24
|
+
:debounce="debounce"
|
25
|
+
@keydown.enter="$emit('submit')"
|
26
|
+
@update:model-value="$emit('update:model-value', $event)"
|
27
|
+
>
|
28
|
+
<template #label>
|
29
|
+
<FieldLabel
|
30
|
+
:field="field"
|
31
|
+
:label="label"
|
32
|
+
:show-name="showName"
|
33
|
+
:class="labelClass"
|
34
|
+
/>
|
35
|
+
</template>
|
36
|
+
</QInput>
|
37
|
+
<MaxLengthCounter
|
38
|
+
:length="modelValue?.length || 0"
|
39
|
+
:max-length="field?.maxLength"
|
40
|
+
/>
|
41
|
+
</template>
|
36
42
|
</div>
|
37
43
|
</template>
|
38
44
|
|
39
|
-
<script setup>
|
45
|
+
<script setup lang="ts">
|
46
|
+
import { TextFieldProps } from "../../../../types";
|
47
|
+
import MaxLengthCounter from "../Utilities/MaxLengthCounter";
|
40
48
|
import FieldLabel from "./FieldLabel";
|
41
49
|
import LabelValueBlock from "./LabelValueBlock";
|
42
50
|
|
43
51
|
defineEmits(["update:model-value", "submit"]);
|
44
|
-
defineProps({
|
45
|
-
modelValue:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
type: String,
|
55
|
-
default: "text"
|
56
|
-
},
|
57
|
-
label: {
|
58
|
-
type: String,
|
59
|
-
default: null
|
60
|
-
},
|
61
|
-
labelClass: {
|
62
|
-
type: String,
|
63
|
-
default: ""
|
64
|
-
},
|
65
|
-
parentClass: {
|
66
|
-
type: String,
|
67
|
-
default: ""
|
68
|
-
},
|
69
|
-
inputClass: {
|
70
|
-
type: String,
|
71
|
-
default: ""
|
72
|
-
},
|
73
|
-
noLabel: Boolean,
|
74
|
-
showName: Boolean,
|
75
|
-
disabled: Boolean,
|
76
|
-
readonly: Boolean,
|
77
|
-
debounce: {
|
78
|
-
type: [String, Number],
|
79
|
-
default: 0
|
80
|
-
}
|
52
|
+
withDefaults(defineProps<TextFieldProps>(), {
|
53
|
+
modelValue: "",
|
54
|
+
field: null,
|
55
|
+
type: "text",
|
56
|
+
label: "",
|
57
|
+
labelClass: "",
|
58
|
+
parentClass: "",
|
59
|
+
inputClass: "",
|
60
|
+
maxLength: null,
|
61
|
+
debounce: 0
|
81
62
|
});
|
82
63
|
</script>
|
@@ -5,6 +5,7 @@ export { default as DateRangeField } from "./DateRangeField.vue";
|
|
5
5
|
export { default as DateTimeField } from "./DateTimeField.vue";
|
6
6
|
export { default as DateTimePicker } from "./DateTimePicker.vue";
|
7
7
|
export { default as EditableDiv } from "./EditableDiv.vue";
|
8
|
+
export { default as EditOnClickTextField } from "./EditOnClickTextField.vue";
|
8
9
|
export { default as FieldLabel } from "./FieldLabel.vue";
|
9
10
|
export { default as FileUploadButton } from "./FileUploadButton.vue";
|
10
11
|
export { default as InlineDateTimeField } from "./InlineDateTimeField.vue";
|
@@ -20,5 +21,6 @@ export { default as SelectDrawer } from "./SelectDrawer.vue";
|
|
20
21
|
export { default as SelectField } from "./SelectField.vue";
|
21
22
|
export { default as SelectWithChildrenField } from "./SelectWithChildrenField.vue";
|
22
23
|
export { default as SingleFileField } from "./SingleFileField.vue";
|
24
|
+
export { default as SliderNumberField } from "./SliderNumberField.vue";
|
23
25
|
export { default as TextField } from "./TextField.vue";
|
24
26
|
export { default as WysiwygField } from "./WysiwygField.vue";
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<template>
|
2
|
-
<div class="rendered-form">
|
2
|
+
<div class="dx-rendered-form">
|
3
3
|
<div
|
4
4
|
v-if="form.variations > 1"
|
5
5
|
class="mb-4"
|
@@ -65,12 +65,13 @@
|
|
65
65
|
<div
|
66
66
|
v-for="(field, index) in mappedFields"
|
67
67
|
:key="field.id"
|
68
|
-
:class="{ 'mt-4': index > 0 }"
|
68
|
+
:class="{ 'mt-4': index > 0, [fieldClass]: true }"
|
69
69
|
>
|
70
70
|
<RenderVnode
|
71
71
|
v-if="field.vnode"
|
72
72
|
:vnode="field.vnode"
|
73
|
-
:
|
73
|
+
:field="field"
|
74
|
+
:props="getVnodeProps(field)"
|
74
75
|
@update:model-value="onInput(field.name, $event)"
|
75
76
|
/>
|
76
77
|
<Component
|
@@ -81,11 +82,31 @@
|
|
81
82
|
:label="field.label || undefined"
|
82
83
|
:no-label="noLabel"
|
83
84
|
:show-name="showName"
|
85
|
+
:clearable="field.clearable || clearable"
|
84
86
|
:disable="disable"
|
85
87
|
:readonly="readonly"
|
86
88
|
@update:model-value="onInput(field.name, $event)"
|
87
89
|
/>
|
88
90
|
</div>
|
91
|
+
<div
|
92
|
+
v-if="savedAt"
|
93
|
+
:class="savingClass"
|
94
|
+
class="dx-saving-indicator flex items-center flex-nowrap"
|
95
|
+
>
|
96
|
+
<slot
|
97
|
+
v-if="saving"
|
98
|
+
name="saving"
|
99
|
+
>
|
100
|
+
Saving...
|
101
|
+
<QSpinnerPie class="ml-2" />
|
102
|
+
</slot>
|
103
|
+
<slot
|
104
|
+
v-else
|
105
|
+
name="saved"
|
106
|
+
>
|
107
|
+
Saved at {{ fDateTime(savedAt, { format: "M/d/yy h:mm:ssa" }) }}
|
108
|
+
</slot>
|
109
|
+
</div>
|
89
110
|
<ConfirmDialog
|
90
111
|
v-if="variationToEdit !== false"
|
91
112
|
title="Change variation name"
|
@@ -113,8 +134,9 @@
|
|
113
134
|
<script setup lang="ts">
|
114
135
|
import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@heroicons/vue/solid";
|
115
136
|
import { computed, ref } from "vue";
|
116
|
-
import { FlashMessages, incrementName, replace } from "../../../helpers";
|
137
|
+
import { fDateTime, FlashMessages, incrementName, replace } from "../../../helpers";
|
117
138
|
import { TrashIcon as RemoveIcon } from "../../../svg";
|
139
|
+
import { Form, FormFieldValue } from "../../../types";
|
118
140
|
import { ConfirmDialog, RenderVnode } from "../../Utility";
|
119
141
|
import {
|
120
142
|
BooleanField,
|
@@ -127,9 +149,8 @@ import {
|
|
127
149
|
TextField,
|
128
150
|
WysiwygField
|
129
151
|
} from "./Fields";
|
130
|
-
import { Form, FormFieldValue } from "./form.d.ts";
|
131
152
|
|
132
|
-
export interface
|
153
|
+
export interface RenderedFormProps {
|
133
154
|
values?: FormFieldValue[] | object;
|
134
155
|
form: Form;
|
135
156
|
noLabel?: boolean;
|
@@ -137,13 +158,20 @@ export interface Props {
|
|
137
158
|
disable?: boolean;
|
138
159
|
readonly?: boolean;
|
139
160
|
saving?: boolean;
|
161
|
+
clearable?: boolean;
|
140
162
|
emptyValue?: string | number | boolean;
|
141
163
|
canModifyVariations?: boolean;
|
164
|
+
fieldClass?: string;
|
165
|
+
savingClass?: string;
|
166
|
+
savedAt?: string;
|
142
167
|
}
|
143
168
|
|
144
|
-
const props = withDefaults(defineProps<
|
169
|
+
const props = withDefaults(defineProps<RenderedFormProps>(), {
|
145
170
|
values: null,
|
146
|
-
emptyValue: undefined
|
171
|
+
emptyValue: undefined,
|
172
|
+
fieldClass: "",
|
173
|
+
savingClass: "text-sm text-slate-500 justify-end mt-4",
|
174
|
+
savedAt: null
|
147
175
|
});
|
148
176
|
|
149
177
|
const emit = defineEmits(["update:values"]);
|
@@ -172,6 +200,19 @@ const fieldResponses = computed(() => {
|
|
172
200
|
if (Array.isArray(props.values)) return props.values;
|
173
201
|
return Object.entries(props.values).map(([name, value]) => ({ name, value, variation: "" }));
|
174
202
|
});
|
203
|
+
|
204
|
+
function getVnodeProps(field) {
|
205
|
+
return {
|
206
|
+
modelValue: getFieldValue(field.name),
|
207
|
+
label: field.label,
|
208
|
+
clearable: field.clearable || props.clearable,
|
209
|
+
readonly: props.readonly,
|
210
|
+
disable: props.disable,
|
211
|
+
showName: props.showName,
|
212
|
+
noLabel: props.noLabel
|
213
|
+
};
|
214
|
+
}
|
215
|
+
|
175
216
|
const variationNames = computed(() => {
|
176
217
|
const names = [...new Set(fieldResponses.value.map(v => v.variation))].sort();
|
177
218
|
// Always guarantee that we show the default variation
|
@@ -183,7 +224,7 @@ const variationNames = computed(() => {
|
|
183
224
|
|
184
225
|
const currentVariation = ref(variationNames.value[0] || "");
|
185
226
|
const newVariationName = ref("");
|
186
|
-
const variationToEdit = ref(false);
|
227
|
+
const variationToEdit = ref<boolean | string>(false);
|
187
228
|
const variationToDelete = ref("");
|
188
229
|
const canAddVariation = computed(() => props.canModifyVariations && !props.readonly && !props.disable && variationNames.value.length < props.form.variations);
|
189
230
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
v-if="maxLength"
|
4
|
+
class="danx-input-chars mt-1"
|
5
|
+
:class="{'danx-input-chars--error': length > maxLength}"
|
6
|
+
>
|
7
|
+
{{ fNumber(length) }} / {{ fNumber(maxLength) }} characters
|
8
|
+
</div>
|
9
|
+
</template>
|
10
|
+
<script setup lang="ts">
|
11
|
+
import { fNumber } from "../../../../helpers";
|
12
|
+
|
13
|
+
defineProps<{
|
14
|
+
length: number;
|
15
|
+
maxLength?: number;
|
16
|
+
}>();
|
17
|
+
</script>
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as MaxLengthCounter } from "./MaxLengthCounter.vue";
|
@@ -3,6 +3,7 @@
|
|
3
3
|
<slot name="top" />
|
4
4
|
<slot name="toolbar">
|
5
5
|
<ActionToolbar
|
6
|
+
:title="title"
|
6
7
|
:refresh-button="refreshButton"
|
7
8
|
:actions="actions?.filter(a => a.batch)"
|
8
9
|
:action-target="controller.selectedRows.value"
|
@@ -15,7 +16,7 @@
|
|
15
16
|
</ActionToolbar>
|
16
17
|
</slot>
|
17
18
|
<div class="flex flex-nowrap flex-grow overflow-hidden w-full">
|
18
|
-
<slot name="
|
19
|
+
<slot name="filters">
|
19
20
|
<CollapsableFiltersSidebar
|
20
21
|
v-if="activeFilter"
|
21
22
|
:name="controller.name"
|
@@ -48,6 +49,7 @@
|
|
48
49
|
v-if="activeItem && panels"
|
49
50
|
:title="panelTitle"
|
50
51
|
:model-value="activePanel"
|
52
|
+
:active-item="activeItem"
|
51
53
|
:panels="panels"
|
52
54
|
@update:model-value="panel => controller.activatePanel(activeItem, panel)"
|
53
55
|
@close="controller.setActiveItem(null)"
|
@@ -65,26 +67,25 @@
|
|
65
67
|
</template>
|
66
68
|
<script setup lang="ts">
|
67
69
|
import { computed } from "vue";
|
68
|
-
import { ActionOptions } from "../../../
|
70
|
+
import { ActionController, ActionOptions, ActionPanel, FilterGroup, TableColumn } from "../../../types";
|
69
71
|
import { PanelsDrawer } from "../../PanelsDrawer";
|
70
72
|
import { PreviousNextControls } from "../../Utility";
|
71
73
|
import ActionTable from "../ActionTable";
|
72
74
|
import { CollapsableFiltersSidebar } from "../Filters";
|
73
|
-
import { ActionController, ActionPanel, FilterGroup } from "../listControls";
|
74
|
-
import { TableColumn } from "../tableColumns";
|
75
75
|
import { ActionToolbar } from "../Toolbars";
|
76
76
|
|
77
77
|
const props = defineProps<{
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
78
|
+
title?: string;
|
79
|
+
refreshButton: boolean;
|
80
|
+
showFilters: boolean;
|
81
|
+
controller: ActionController;
|
82
|
+
columns: TableColumn[];
|
83
|
+
filters?: FilterGroup[];
|
84
|
+
panels?: ActionPanel[];
|
85
|
+
actions?: ActionOptions[];
|
86
|
+
exporter?: () => Promise<void>;
|
87
|
+
panelTitleField?: string;
|
88
|
+
tableClass?: string;
|
88
89
|
}>();
|
89
90
|
|
90
91
|
const activeFilter = computed(() => props.controller.activeFilter.value);
|
@@ -92,7 +93,7 @@ const activeItem = computed(() => props.controller.activeItem.value);
|
|
92
93
|
const activePanel = computed(() => props.controller.activePanel.value || "");
|
93
94
|
const panelTitle = computed(() => {
|
94
95
|
if (activeItem.value) {
|
95
|
-
return activeItem.value[props.
|
96
|
+
return activeItem.value[props.panelTitleField || "title"] || activeItem.value.label || activeItem.value.name || (props.title + " " + activeItem.value.id);
|
96
97
|
}
|
97
98
|
return null;
|
98
99
|
});
|
@@ -2,9 +2,9 @@
|
|
2
2
|
<div class="dx-action-toolbar flex items-center">
|
3
3
|
<div class="flex-grow px-6">
|
4
4
|
<slot name="title">
|
5
|
-
<
|
5
|
+
<h4 v-if="title">
|
6
6
|
{{ title }}
|
7
|
-
</
|
7
|
+
</h4>
|
8
8
|
</slot>
|
9
9
|
</div>
|
10
10
|
<div class="py-3 px-6 flex items-center flex-nowrap">
|
@@ -20,7 +20,7 @@
|
|
20
20
|
class="ml-4"
|
21
21
|
/>
|
22
22
|
<ActionMenu
|
23
|
-
v-if="actions.length > 0"
|
23
|
+
v-if="actions && actions.length > 0"
|
24
24
|
class="ml-4 dx-batch-actions"
|
25
25
|
:target="actionTarget"
|
26
26
|
:actions="actions"
|
@@ -30,17 +30,17 @@
|
|
30
30
|
</div>
|
31
31
|
</template>
|
32
32
|
<script setup lang="ts">
|
33
|
-
import { ActionOptions, ActionTargetItem } from "../../../
|
33
|
+
import { ActionOptions, ActionTargetItem, AnyObject } from "../../../types";
|
34
34
|
import { ExportButton, RefreshButton } from "../../Utility";
|
35
35
|
import ActionMenu from "../ActionMenu";
|
36
36
|
|
37
37
|
defineEmits(["refresh"]);
|
38
38
|
defineProps<{
|
39
39
|
title?: string,
|
40
|
-
actions
|
40
|
+
actions?: ActionOptions[],
|
41
41
|
actionTarget?: ActionTargetItem[],
|
42
42
|
refreshButton?: boolean,
|
43
43
|
loading?: boolean,
|
44
|
-
exporter?: () => void
|
44
|
+
exporter?: (filter?: AnyObject) => void | Promise<void>
|
45
45
|
}>();
|
46
46
|
</script>
|