quasar-ui-danx 0.0.11 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +6 -1
- package/src/components/ActionTable/ActionTable.vue +49 -41
- package/src/components/ActionTable/BatchActionMenu.vue +20 -20
- package/src/components/ActionTable/EmptyTableState.vue +5 -5
- package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +11 -11
- package/src/components/ActionTable/Filters/FilterGroupItem.vue +7 -7
- package/src/components/ActionTable/Filters/FilterGroupList.vue +29 -29
- package/src/components/ActionTable/Filters/FilterListToggle.vue +15 -15
- package/src/components/ActionTable/Filters/FilterableField.vue +82 -80
- package/src/components/ActionTable/Filters/index.ts +5 -0
- package/src/components/ActionTable/Form/Fields/BooleanField.vue +13 -13
- package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +11 -11
- package/src/components/ActionTable/Form/Fields/DateField.vue +13 -13
- package/src/components/ActionTable/Form/Fields/DateRangeField.vue +25 -25
- package/src/components/ActionTable/Form/Fields/DateTimeField.vue +21 -21
- package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +23 -23
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +31 -31
- package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +19 -19
- package/src/components/ActionTable/Form/Fields/IntegerField.vue +7 -7
- package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +22 -0
- package/src/components/ActionTable/Form/Fields/LabeledInput.vue +19 -19
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +40 -40
- package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +23 -23
- package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +10 -10
- package/src/components/ActionTable/Form/Fields/NumberField.vue +29 -29
- package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +33 -33
- package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +36 -36
- package/src/components/ActionTable/Form/Fields/SelectField.vue +66 -66
- package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +23 -23
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +32 -32
- package/src/components/ActionTable/Form/Fields/TextField.vue +36 -36
- package/src/components/ActionTable/Form/Fields/WysiwygField.vue +16 -16
- package/src/components/ActionTable/Form/Fields/index.ts +23 -23
- package/src/components/ActionTable/Form/RenderedForm.vue +27 -25
- package/src/components/ActionTable/Form/index.ts +2 -0
- package/src/components/ActionTable/TableSummaryRow.vue +33 -33
- package/src/components/ActionTable/index.ts +8 -13
- package/src/components/ActionTable/listActions.ts +340 -339
- package/src/components/ActionTable/listHelpers.ts +74 -0
- package/src/components/ActionTable/tableColumns.ts +56 -56
- package/src/components/DragAndDrop/HandleDraggable.vue +29 -29
- package/src/components/DragAndDrop/ListItemDraggable.vue +10 -10
- package/src/components/DragAndDrop/index.ts +0 -1
- package/src/components/DragAndDrop/listDragAndDrop.ts +1 -1
- package/src/components/Utility/CollapsableSidebar.vue +35 -35
- package/src/components/Utility/ContentDrawer.vue +20 -20
- package/src/components/Utility/Dialogs/ConfirmDialog.vue +55 -55
- package/src/components/Utility/Dialogs/FullScreenDialog.vue +18 -18
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +105 -0
- package/src/components/Utility/Dialogs/InfoDialog.vue +10 -10
- package/src/components/Utility/Dialogs/InputDialog.vue +13 -13
- package/src/components/Utility/ImagePreview.vue +192 -0
- package/src/components/Utility/Popover/PopoverMenu.vue +64 -0
- package/src/components/Utility/Transitions/StaggeredListTransition.vue +15 -15
- package/src/components/Utility/index.ts +11 -9
- package/src/components/index.ts +1 -1
- package/src/helpers/FileUpload.ts +274 -273
- package/src/helpers/compatibility.ts +45 -45
- package/src/helpers/date.ts +2 -2
- package/src/helpers/download.ts +166 -158
- package/src/helpers/downloadPdf.ts +48 -48
- package/src/helpers/files.ts +42 -42
- package/src/helpers/index.ts +2 -0
- package/src/helpers/multiFileUpload.ts +56 -56
- package/src/helpers/singleFileUpload.ts +49 -49
- package/src/index.esm.js +3 -4
- package/src/svg/FilterIcon.svg +7 -0
- package/src/svg/ImageIcon.svg +30 -0
- package/src/svg/PdfIcon.svg +21 -0
- package/src/svg/PercentIcon.svg +13 -0
- package/src/svg/TrashIcon.svg +15 -0
- package/src/svg/XIcon.svg +18 -0
- package/src/svg/index.ts +8 -0
- package/src/vendor/tinymce-config.ts +1 -0
- package/src/vue-plugin.js +7 -4
- package/tsconfig.json +14 -13
- package/src/components/ActionTable/tableHelpers.ts +0 -83
- package/src/components/DragAndDrop/Icons/index.ts +0 -2
- /package/src/{components/DragAndDrop/Icons → svg}/DragHandleDotsIcon.svg +0 -0
- /package/src/{components/DragAndDrop/Icons → svg}/DragHandleIcon.svg +0 -0
@@ -1,44 +1,44 @@
|
|
1
1
|
<template>
|
2
|
-
<
|
2
|
+
<q-btn v-bind="$props" @click="$refs.fileUpload.click()">
|
3
3
|
<slot>
|
4
4
|
<PlusIcon class="w-5 mr-2" />
|
5
5
|
{{ text }}
|
6
6
|
</slot>
|
7
7
|
|
8
8
|
<input
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
ref="fileUpload"
|
10
|
+
data-testid="file-upload"
|
11
|
+
type="file"
|
12
|
+
:accept="geolocation ? 'image/*;capture=camera' : undefined"
|
13
|
+
:capture="geolocation ? 'environment' : undefined"
|
14
|
+
class="hidden"
|
15
|
+
multiple
|
16
|
+
@change="onAttachFiles"
|
17
17
|
/>
|
18
|
-
|
18
|
+
</q-btn>
|
19
19
|
</template>
|
20
20
|
<script setup>
|
21
|
-
import { PlusIcon } from
|
22
|
-
import { FileUpload } from
|
23
|
-
import { QBtn } from
|
24
|
-
import { ref } from
|
21
|
+
import { PlusIcon } from '@heroicons/vue/outline';
|
22
|
+
import { FileUpload } from '@ui/helpers';
|
23
|
+
import { QBtn } from 'quasar';
|
24
|
+
import { ref } from 'vue';
|
25
25
|
|
26
26
|
defineExpose({ upload });
|
27
27
|
const emit = defineEmits([
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
'uploading',
|
29
|
+
'file-progress',
|
30
|
+
'file-complete',
|
31
|
+
'complete'
|
32
32
|
]);
|
33
33
|
const props = defineProps({
|
34
34
|
...QBtn.props,
|
35
35
|
text: {
|
36
36
|
type: String,
|
37
|
-
default:
|
37
|
+
default: 'Add File'
|
38
38
|
},
|
39
39
|
locationWaitMessage: {
|
40
40
|
type: String,
|
41
|
-
default:
|
41
|
+
default: 'Waiting for location...'
|
42
42
|
},
|
43
43
|
geolocation: Boolean
|
44
44
|
});
|
@@ -56,18 +56,18 @@ function upload() {
|
|
56
56
|
* @returns {Promise<void>}
|
57
57
|
*/
|
58
58
|
async function onAttachFiles({ target: { files } }) {
|
59
|
-
emit(
|
59
|
+
emit('uploading', files);
|
60
60
|
let fileUpload = new FileUpload(files)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
61
|
+
.onProgress(({ file, progress }) => {
|
62
|
+
file.progress = progress;
|
63
|
+
emit('file-progress', file);
|
64
|
+
})
|
65
|
+
.onComplete(({ file, uploadedFile }) => {
|
66
|
+
emit('file-complete', { file, uploadedFile });
|
67
|
+
})
|
68
|
+
.onAllComplete(() => {
|
69
|
+
emit('complete', fileUpload.files);
|
70
|
+
});
|
71
71
|
|
72
72
|
if (props.geolocation) {
|
73
73
|
await fileUpload.resolveLocation(props.locationWaitMessage);
|
@@ -1,33 +1,33 @@
|
|
1
1
|
<template>
|
2
2
|
<div class="inline-block">
|
3
3
|
<div
|
4
|
-
|
4
|
+
class="cursor-pointer py-2 hover:bg-blue-light flex items-center justify-end"
|
5
5
|
>
|
6
|
-
{{ fLocalizedDateTime(modelValue, { empty:
|
6
|
+
{{ fLocalizedDateTime(modelValue, { empty: 'Never' }) }}
|
7
7
|
<EditIcon class="w-4 font-bold ml-2 text-gray-base" />
|
8
|
-
<
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
<q-popup-edit
|
9
|
+
v-slot="scope"
|
10
|
+
:model-value="modelValue"
|
11
|
+
touch-position
|
12
|
+
:offset="[0, 20]"
|
13
|
+
class="bg-blue-base text-white !min-w-0"
|
14
14
|
>
|
15
15
|
<DateTimePicker
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
v-model="scope.value"
|
17
|
+
:nullable="nullable"
|
18
|
+
@save="onSave(scope)"
|
19
|
+
@cancel="scope.cancel"
|
20
20
|
/>
|
21
|
-
|
21
|
+
</q-popup-edit>
|
22
22
|
</div>
|
23
23
|
</div>
|
24
24
|
</template>
|
25
25
|
<script setup>
|
26
|
-
import { PencilIcon as EditIcon } from
|
27
|
-
import
|
28
|
-
import
|
26
|
+
import { PencilIcon as EditIcon } from '@heroicons/vue/solid';
|
27
|
+
import { fLocalizedDateTime } from '@ui/helpers/formats';
|
28
|
+
import DateTimePicker from './DateTimePicker';
|
29
29
|
|
30
|
-
const emit = defineEmits([
|
30
|
+
const emit = defineEmits(['close', 'save', 'update:model-value']);
|
31
31
|
defineProps({
|
32
32
|
modelValue: {
|
33
33
|
type: String,
|
@@ -37,8 +37,8 @@ defineProps({
|
|
37
37
|
});
|
38
38
|
|
39
39
|
function onSave(scope) {
|
40
|
-
emit(
|
41
|
-
emit(
|
40
|
+
emit('update:model-value', scope.value);
|
41
|
+
emit('save', scope.value);
|
42
42
|
scope.set();
|
43
43
|
}
|
44
44
|
</script>
|
@@ -1,17 +1,17 @@
|
|
1
1
|
<template>
|
2
2
|
<NumberField
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
:field="field"
|
4
|
+
:precision="0"
|
5
|
+
:model-value="modelValue"
|
6
|
+
:show-name="showName"
|
7
|
+
@update:model-value="$emit('update:model-value', $event)"
|
8
8
|
/>
|
9
9
|
</template>
|
10
10
|
|
11
11
|
<script setup>
|
12
|
-
import NumberField from
|
12
|
+
import NumberField from './NumberField';
|
13
13
|
|
14
|
-
defineEmits([
|
14
|
+
defineEmits(['update:model-value']);
|
15
15
|
defineProps({
|
16
16
|
modelValue: {
|
17
17
|
type: [String, Number],
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<template>
|
2
|
+
<div>
|
3
|
+
<div class="text-xs font-bold">{{ label }}</div>
|
4
|
+
<div :class="{'mt-2': !dense, 'mt-1': dense, 'text-no-wrap': nowrap}">
|
5
|
+
<slot>{{ value || "-" }}</slot>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
</template>
|
9
|
+
<script setup>
|
10
|
+
defineProps({
|
11
|
+
label: {
|
12
|
+
type: String,
|
13
|
+
required: true
|
14
|
+
},
|
15
|
+
value: {
|
16
|
+
type: [String, Number],
|
17
|
+
default: "-"
|
18
|
+
},
|
19
|
+
dense: Boolean,
|
20
|
+
nowrap: Boolean
|
21
|
+
});
|
22
|
+
</script>
|
@@ -4,34 +4,34 @@
|
|
4
4
|
<slot name="label">{{ label }}</slot>
|
5
5
|
</label>
|
6
6
|
<div class="mt-1">
|
7
|
-
<
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
7
|
+
<q-input
|
8
|
+
:model-value="modelValue"
|
9
|
+
:error-message="error"
|
10
|
+
:error="!!error"
|
11
|
+
no-error-icon
|
12
|
+
outlined
|
13
|
+
dense
|
14
|
+
:disable="disabled"
|
15
|
+
:readonly="disabled"
|
16
|
+
:type="type"
|
17
|
+
:name="name"
|
18
|
+
:placeholder="placeholder || label"
|
19
|
+
:required="required"
|
20
|
+
:rules="rules"
|
21
|
+
:lazy-rules="!!rules"
|
22
|
+
class="w-full"
|
23
|
+
@update:model-value="$emit('update:model-value', $event)"
|
24
24
|
/>
|
25
25
|
</div>
|
26
26
|
</div>
|
27
27
|
</template>
|
28
28
|
|
29
29
|
<script setup>
|
30
|
-
defineEmits([
|
30
|
+
defineEmits(['update:model-value']);
|
31
31
|
defineProps({
|
32
32
|
type: {
|
33
33
|
type: String,
|
34
|
-
default:
|
34
|
+
default: 'text'
|
35
35
|
},
|
36
36
|
name: {
|
37
37
|
type: String,
|
@@ -1,58 +1,58 @@
|
|
1
1
|
<template>
|
2
2
|
<div
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
class="max-w-full relative overflow-auto"
|
4
|
+
:class="{'p-4 border rounded border-gray-medium': !readonly}"
|
5
|
+
@dragover.prevent
|
6
|
+
@drop.prevent="onDrop"
|
7
7
|
>
|
8
8
|
<FieldLabel
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
:field="field"
|
10
|
+
:show-name="showName"
|
11
|
+
class="text-sm font-semibold"
|
12
12
|
/>
|
13
13
|
<div
|
14
|
-
|
15
|
-
|
14
|
+
v-if="!disable && !readonly"
|
15
|
+
class="text-sm my-2"
|
16
16
|
>
|
17
17
|
<a
|
18
|
-
|
19
|
-
|
18
|
+
class="text-blue-base"
|
19
|
+
@click="$refs.file.click()"
|
20
20
|
>Upload</a>
|
21
21
|
<a
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
v-if="uploadedFiles.length > 0"
|
23
|
+
class="ml-3 text-red-dark"
|
24
|
+
@click="onClear"
|
25
25
|
>Clear</a>
|
26
26
|
<input
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
ref="file"
|
28
|
+
class="hidden"
|
29
|
+
type="file"
|
30
|
+
multiple
|
31
|
+
@change="onFilesSelected"
|
32
32
|
/>
|
33
33
|
</div>
|
34
34
|
|
35
35
|
<div class="max-w-[50em] flex items-stretch justify-start">
|
36
36
|
<ImagePreview
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
37
|
+
v-for="file in uploadedFiles"
|
38
|
+
:key="'file-upload-' + file.id"
|
39
|
+
class="w-32 m-2 cursor-pointer bg-neutral-plus-5"
|
40
|
+
:class="{'border border-dashed border-blue-base': !uploadedFiles.length}"
|
41
|
+
:image="file"
|
42
|
+
:related-files="uploadedFiles"
|
43
|
+
downloadable
|
44
|
+
:removable="!readonly && !disable"
|
45
|
+
@remove="onRemove(file)"
|
46
46
|
/>
|
47
47
|
<ImagePreview
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
v-if="!disable && !readonly"
|
49
|
+
class="w-32 m-2 cursor-pointer border border-dashed border-blue-base"
|
50
|
+
disabled
|
51
|
+
@click="$refs.file.click()"
|
52
52
|
/>
|
53
53
|
<div
|
54
|
-
|
55
|
-
|
54
|
+
v-if="readonly && uploadedFiles.length === 0"
|
55
|
+
class="p-1"
|
56
56
|
>
|
57
57
|
--
|
58
58
|
</div>
|
@@ -61,12 +61,12 @@
|
|
61
61
|
</template>
|
62
62
|
|
63
63
|
<script setup>
|
64
|
-
import ImagePreview from
|
65
|
-
import
|
66
|
-
import {
|
67
|
-
import
|
64
|
+
import { ImagePreview } from '@ui/components';
|
65
|
+
import { useMultiFileUpload } from '@ui/helpers';
|
66
|
+
import { onMounted } from 'vue';
|
67
|
+
import FieldLabel from './FieldLabel';
|
68
68
|
|
69
|
-
const emit = defineEmits([
|
69
|
+
const emit = defineEmits(['update:model-value']);
|
70
70
|
const props = defineProps({
|
71
71
|
modelValue: {
|
72
72
|
type: [Object, String],
|
@@ -87,5 +87,5 @@ onMounted(() => {
|
|
87
87
|
uploadedFiles.value = props.modelValue;
|
88
88
|
}
|
89
89
|
});
|
90
|
-
onComplete(() => emit(
|
90
|
+
onComplete(() => emit('update:model-value', uploadedFiles.value));
|
91
91
|
</script>
|
@@ -1,37 +1,37 @@
|
|
1
1
|
<template>
|
2
2
|
<div>
|
3
3
|
<SelectField
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
v-model="selectedFieldName"
|
5
|
+
:label="undefined"
|
6
|
+
:options="field.options"
|
7
|
+
class="mb-2"
|
8
|
+
@update:model-value="onChange"
|
9
9
|
/>
|
10
10
|
<TextField
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
v-model="textInput"
|
12
|
+
:field="field"
|
13
|
+
:no-label="!field.label"
|
14
|
+
label-class="text-xs font-bold text-gray-dark"
|
15
|
+
parent-class="tight-label"
|
16
|
+
input-class="!py-0"
|
17
|
+
dense
|
18
|
+
type="textarea"
|
19
|
+
:debounce="500"
|
20
|
+
@update:model-value="onChange"
|
21
21
|
/>
|
22
22
|
</div>
|
23
23
|
</template>
|
24
24
|
|
25
25
|
<script setup>
|
26
|
-
import
|
27
|
-
import
|
28
|
-
import
|
26
|
+
import { computed, ref, watch } from 'vue';
|
27
|
+
import SelectField from './SelectField';
|
28
|
+
import TextField from './TextField';
|
29
29
|
|
30
|
-
const emit = defineEmits([
|
30
|
+
const emit = defineEmits(['update:model-value']);
|
31
31
|
const props = defineProps({
|
32
32
|
modelValue: {
|
33
33
|
type: [String, Number, Object],
|
34
|
-
default:
|
34
|
+
default: ''
|
35
35
|
},
|
36
36
|
field: {
|
37
37
|
type: Object,
|
@@ -43,12 +43,12 @@ const selectedFieldName = ref(props.field.defaultOption);
|
|
43
43
|
const searchList = computed(() => props.modelValue && props.modelValue[selectedFieldName.value]);
|
44
44
|
const textInput = ref(formatModelValue());
|
45
45
|
function onChange() {
|
46
|
-
textInput.value = textInput.value?.replace(/\n/g,
|
47
|
-
emit(
|
46
|
+
textInput.value = textInput.value?.replace(/\n/g, ',').replace(/,{2,}/g, ',') || '';
|
47
|
+
emit('update:model-value', textInput.value ? { [selectedFieldName.value]: textInput.value.split(',') } : undefined);
|
48
48
|
}
|
49
49
|
|
50
50
|
function formatModelValue() {
|
51
|
-
return Array.isArray(searchList.value) ? searchList.value?.join(
|
51
|
+
return Array.isArray(searchList.value) ? searchList.value?.join(',') : '';
|
52
52
|
}
|
53
53
|
|
54
54
|
watch(() => props.modelValue, () => {
|
@@ -1,28 +1,28 @@
|
|
1
1
|
<template>
|
2
2
|
<LabeledInput
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
type="password"
|
4
|
+
v-bind="props"
|
5
|
+
:rules="rules"
|
6
|
+
@update:model-value="$emit('update:model-value', $event)"
|
7
7
|
/>
|
8
8
|
</template>
|
9
9
|
|
10
10
|
<script setup>
|
11
|
-
import LabeledInput from
|
11
|
+
import LabeledInput from './LabeledInput';
|
12
12
|
|
13
|
-
defineEmits([
|
13
|
+
defineEmits(['update:model-value']);
|
14
14
|
const props = defineProps({
|
15
15
|
name: {
|
16
16
|
type: String,
|
17
|
-
default:
|
17
|
+
default: 'password'
|
18
18
|
},
|
19
19
|
label: {
|
20
20
|
type: String,
|
21
|
-
default:
|
21
|
+
default: 'Password'
|
22
22
|
},
|
23
23
|
placeholder: {
|
24
24
|
type: String,
|
25
|
-
default:
|
25
|
+
default: 'Enter Password'
|
26
26
|
},
|
27
27
|
modelValue: {
|
28
28
|
type: [String, Number],
|
@@ -35,5 +35,5 @@ const props = defineProps({
|
|
35
35
|
disabled: Boolean
|
36
36
|
});
|
37
37
|
|
38
|
-
const rules = [(val) => val.length >= 8 ||
|
38
|
+
const rules = [(val) => val.length >= 8 || 'Please use at least 8 characters'];
|
39
39
|
</script>
|
@@ -1,34 +1,34 @@
|
|
1
1
|
<template>
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
<q-input
|
3
|
+
:model-value="numberVal"
|
4
|
+
:data-testid="'number-field-' + fieldOptions.id"
|
5
|
+
:placeholder="fieldOptions.placeholder"
|
6
|
+
outlined
|
7
|
+
dense
|
8
|
+
inputmode="numeric"
|
9
|
+
:input-class="{[inputClass]: true, 'text-right bg-white': !hidePrependLabel, 'text-right !text-xs text-black font-normal': hidePrependLabel}"
|
10
|
+
:class="{'no-prepend-icon w-32': hidePrependLabel, 'prepend-label': !hidePrependLabel}"
|
11
|
+
@update:model-value="onInput"
|
12
12
|
>
|
13
13
|
<template #prepend>
|
14
14
|
<FieldLabel
|
15
|
-
|
16
|
-
|
15
|
+
:field="fieldOptions"
|
16
|
+
:show-name="showName"
|
17
17
|
/>
|
18
18
|
</template>
|
19
|
-
|
19
|
+
</q-input>
|
20
20
|
</template>
|
21
21
|
|
22
22
|
<script setup>
|
23
|
-
import
|
24
|
-
import {
|
25
|
-
import
|
23
|
+
import { fNumber } from '@ui/helpers/formats';
|
24
|
+
import { computed, nextTick, ref, watch } from 'vue';
|
25
|
+
import FieldLabel from './FieldLabel';
|
26
26
|
|
27
|
-
const emit = defineEmits([
|
27
|
+
const emit = defineEmits(['update:model-value']);
|
28
28
|
const props = defineProps({
|
29
29
|
modelValue: {
|
30
30
|
type: [String, Number],
|
31
|
-
default:
|
31
|
+
default: ''
|
32
32
|
},
|
33
33
|
precision: {
|
34
34
|
type: Number,
|
@@ -44,7 +44,7 @@ const props = defineProps({
|
|
44
44
|
},
|
45
45
|
inputClass: {
|
46
46
|
type: String,
|
47
|
-
default:
|
47
|
+
default: ''
|
48
48
|
},
|
49
49
|
hidePrependLabel: Boolean,
|
50
50
|
currency: Boolean,
|
@@ -54,41 +54,41 @@ const props = defineProps({
|
|
54
54
|
const numberVal = ref(format(props.modelValue));
|
55
55
|
watch(() => props.modelValue, () => numberVal.value = format(props.modelValue));
|
56
56
|
|
57
|
-
const fieldOptions = computed(() => props.field || { label: props.label ||
|
57
|
+
const fieldOptions = computed(() => props.field || { label: props.label || '', placeholder: '', id: '' });
|
58
58
|
|
59
59
|
function format(number) {
|
60
|
-
if (!number && number !== 0 && number !==
|
60
|
+
if (!number && number !== 0 && number !== '0') return number;
|
61
61
|
|
62
|
-
const minimumFractionDigits = Math.min(props.precision, (
|
62
|
+
const minimumFractionDigits = Math.min(props.precision, ('' + number).split('.')[1]?.length || 0);
|
63
63
|
let options = {
|
64
64
|
minimumFractionDigits
|
65
65
|
};
|
66
66
|
|
67
67
|
if (props.currency) {
|
68
68
|
options = {
|
69
|
-
style:
|
70
|
-
currency:
|
69
|
+
style: 'currency',
|
70
|
+
currency: 'USD',
|
71
71
|
minimumFractionDigits
|
72
72
|
};
|
73
73
|
}
|
74
74
|
return fNumber(number, options);
|
75
75
|
}
|
76
76
|
function onInput(value) {
|
77
|
-
let number =
|
77
|
+
let number = '';
|
78
78
|
|
79
79
|
// Prevent invalid characters
|
80
80
|
if (value.match(/[^\d.,$]/)) {
|
81
81
|
const oldVal = numberVal.value;
|
82
82
|
// XXX: To get QInput to show only the value we want
|
83
|
-
numberVal.value +=
|
83
|
+
numberVal.value += ' ';
|
84
84
|
return nextTick(() => numberVal.value = oldVal);
|
85
85
|
}
|
86
86
|
|
87
|
-
if (value !==
|
88
|
-
value = value.replace(/[^\d.]/g,
|
87
|
+
if (value !== '') {
|
88
|
+
value = value.replace(/[^\d.]/g, '');
|
89
89
|
number = Number(value);
|
90
90
|
numberVal.value = format(number);
|
91
91
|
}
|
92
|
-
emit(
|
92
|
+
emit('update:model-value', number === '' ? undefined : number);
|
93
93
|
}
|
94
94
|
</script>
|