adminforth 2.4.0-next.31 → 2.4.0-next.310
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/commands/callTsProxy.js +14 -4
- package/commands/createApp/templates/api.ts.hbs +10 -0
- package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
- package/commands/createApp/templates/index.ts.hbs +12 -1
- package/commands/createApp/templates/package.json.hbs +1 -1
- package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
- package/commands/createApp/templates/schema.prisma.hbs +0 -1
- package/commands/createApp/utils.js +10 -0
- package/commands/createCustomComponent/configLoader.js +17 -4
- package/commands/createCustomComponent/main.js +13 -7
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
- package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
- package/commands/createPlugin/templates/package.json.hbs +1 -1
- package/commands/generateModels.js +30 -22
- package/dist/auth.d.ts +9 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +21 -2
- package/dist/auth.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +69 -17
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +15 -0
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +50 -15
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +11 -0
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +43 -14
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +11 -0
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -22
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +2 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +62 -6
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts +6 -0
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +202 -25
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +172 -31
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +499 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +555 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +7 -15
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +45 -68
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts +5 -0
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +40 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/package-lock.json +1208 -708
- package/dist/spa/package.json +34 -34
- package/dist/spa/src/App.vue +59 -174
- package/dist/spa/src/adminforth.ts +42 -18
- package/dist/spa/src/afcl/AreaChart.vue +0 -1
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Button.vue +6 -6
- package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
- package/dist/spa/src/afcl/Card.vue +25 -0
- package/dist/spa/src/afcl/Checkbox.vue +21 -13
- package/dist/spa/src/afcl/CountryFlag.vue +4 -1
- package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
- package/dist/spa/src/afcl/Dialog.vue +47 -27
- package/dist/spa/src/afcl/Dropzone.vue +127 -48
- package/dist/spa/src/afcl/Input.vue +14 -6
- package/dist/spa/src/afcl/JsonViewer.vue +25 -0
- package/dist/spa/src/afcl/LinkButton.vue +3 -3
- package/dist/spa/src/afcl/PieChart.vue +5 -5
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +82 -34
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +315 -73
- package/dist/spa/src/afcl/Textarea.vue +31 -0
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +28 -18
- package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
- package/dist/spa/src/afcl/index.ts +6 -3
- package/dist/spa/src/components/AcceptModal.vue +48 -14
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/CallActionWrapper.vue +15 -0
- package/dist/spa/src/components/ColumnValueInput.vue +38 -18
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +37 -21
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +195 -132
- package/dist/spa/src/components/GroupsTable.vue +9 -8
- package/dist/spa/src/components/MenuLink.vue +90 -23
- package/dist/spa/src/components/ResourceForm.vue +94 -51
- package/dist/spa/src/components/ResourceListTable.vue +115 -85
- package/dist/spa/src/components/ResourceListTableVirtual.vue +114 -80
- package/dist/spa/src/components/ShowTable.vue +21 -15
- package/dist/spa/src/components/Sidebar.vue +470 -0
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +3 -3
- package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
- package/dist/spa/src/components/Toast.vue +40 -29
- package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
- package/dist/spa/src/components/ValueRenderer.vue +44 -17
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/i18n.ts +5 -3
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/renderers/CompactField.vue +1 -1
- package/dist/spa/src/renderers/CompactUUID.vue +1 -1
- package/dist/spa/src/router/index.ts +8 -0
- package/dist/spa/src/shims-vue.d.ts +5 -0
- package/dist/spa/src/spa_types/core.ts +13 -1
- package/dist/spa/src/stores/core.ts +13 -1
- package/dist/spa/src/stores/filters.ts +33 -2
- package/dist/spa/src/stores/modal.ts +6 -1
- package/dist/spa/src/stores/toast.ts +22 -3
- package/dist/spa/src/types/Back.ts +163 -23
- package/dist/spa/src/types/Common.ts +91 -32
- package/dist/spa/src/types/FrontendAPI.ts +31 -5
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
- package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
- package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
- package/dist/spa/src/types/adapters/index.ts +8 -0
- package/dist/spa/src/utils.ts +291 -11
- package/dist/spa/src/views/CreateView.vue +63 -21
- package/dist/spa/src/views/EditView.vue +55 -22
- package/dist/spa/src/views/ListView.vue +144 -87
- package/dist/spa/src/views/LoginView.vue +26 -35
- package/dist/spa/src/views/ResourceParent.vue +2 -2
- package/dist/spa/src/views/SettingsView.vue +121 -0
- package/dist/spa/src/views/ShowView.vue +83 -53
- package/dist/spa/src/websocket.ts +6 -1
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/spa/vite.config.ts +45 -2
- package/dist/types/Back.d.ts +146 -14
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js +15 -0
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +106 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +31 -3
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
- package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CaptchaAdapter.js +5 -0
- package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
- package/dist/types/adapters/EmailAdapter.d.ts +1 -1
- package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.js +2 -0
- package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.js +2 -0
- package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
- package/dist/types/adapters/index.d.ts +9 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/index.js +2 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/package.json +4 -2
- package/dist/spa/src/types/adapters/index.js +0 -5
|
@@ -1,57 +1,98 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<!-- tag form used to reset the input (method .reset() in
|
|
2
|
+
<!-- tag form used to reset the input (method .reset() in clear() function) -->
|
|
3
3
|
<form class="flex items-center justify-center w-full"
|
|
4
4
|
@dragover.prevent="dragging = true"
|
|
5
5
|
@dragleave.prevent="dragging = false"
|
|
6
|
-
@drop.prevent="dragging = false; doEmit($event.dataTransfer.files)"
|
|
6
|
+
@drop.prevent="dragging = false; doEmit(($event.dataTransfer as DataTransfer).files)"
|
|
7
7
|
>
|
|
8
|
-
<label :id="id" class="flex flex-col items-center justify-center w-full border-2 border-dashed rounded-lg cursor-pointer
|
|
9
|
-
hover:bg-
|
|
8
|
+
<label :id="id" class="flex flex-col items-center justify-center w-full border-2 border-dashed rounded-lg cursor-pointer
|
|
9
|
+
hover:bg-lightDropzoneBackgroundHover hover:border-lightDropzoneBorderHover dark:hover:border-darkDropzoneBorderHover dark:hover:bg-darkDropzoneBackgroundHover"
|
|
10
10
|
:class="{
|
|
11
|
-
'border-
|
|
12
|
-
'border-
|
|
13
|
-
'bg-
|
|
14
|
-
'bg-
|
|
11
|
+
'border-lightDropzoneBorderDragging dark:border-darkDropzoneBorderDragging': dragging,
|
|
12
|
+
'border-lightDropzoneBorder dark:border-darkDropzoneBorder': !dragging,
|
|
13
|
+
'bg-lightDropzoneBackgroundDragging dark:bg-darkDropzoneBackgroundDragging': dragging,
|
|
14
|
+
'bg-lightDropzoneBackground dark:bg-darkDropzoneBackground': !dragging,
|
|
15
15
|
'min-h-32 h-full': props.multiple,
|
|
16
16
|
'h-32': !props.multiple,
|
|
17
17
|
}"
|
|
18
18
|
>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
<input
|
|
20
|
+
:id="id"
|
|
21
|
+
type="file"
|
|
22
|
+
class="hidden"
|
|
23
|
+
:accept="normalizedExtensions.join(',')"
|
|
24
|
+
@change="$event.target && doEmit(($event.target as HTMLInputElement).files!)"
|
|
25
|
+
:multiple="props.multiple || false"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<div class="flex flex-col items-center justify-center pt-5 pb-6">
|
|
29
|
+
<svg
|
|
30
|
+
v-if="!selectedFiles.length"
|
|
31
|
+
class="w-8 h-8 mb-4 text-lightDropzoneIcon dark:text-darkDropzoneIcon"
|
|
32
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
33
|
+
fill="none"
|
|
34
|
+
viewBox="0 0 20 16"
|
|
35
|
+
>
|
|
36
|
+
<path
|
|
37
|
+
stroke="currentColor"
|
|
38
|
+
stroke-linecap="round"
|
|
39
|
+
stroke-linejoin="round"
|
|
40
|
+
stroke-width="2"
|
|
41
|
+
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5
|
|
42
|
+
5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0
|
|
43
|
+
0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
|
|
44
|
+
/>
|
|
45
|
+
</svg>
|
|
46
|
+
|
|
47
|
+
<div
|
|
48
|
+
v-else
|
|
49
|
+
class="flex items-center justify-center py-1 flex-wrap gap-2 w-full gap-2 mt-1 mb-4 px-4"
|
|
50
|
+
>
|
|
51
|
+
<template v-for="(file, index) in selectedFiles" :key="index">
|
|
52
|
+
<div
|
|
53
|
+
class="text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon bg-lightDropzoneBackgroundHover dark:bg-darkDropzoneBackgroundHover rounded-md
|
|
54
|
+
flex items-center gap-1 px-2 py-1 group"
|
|
55
|
+
>
|
|
56
|
+
<IconFileSolid class="w-4 h-4 flex-shrink-0" />
|
|
57
|
+
<span class="truncate max-w-[200px]">{{ file.name }}</span>
|
|
58
|
+
<span class="text-xs">({{ humanifySize(file.size) }})</span>
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
@click.prevent.stop="removeFile(index)"
|
|
62
|
+
class="text-lightDropzoneIcon dark:text-darkDropzoneIcon hover:text-red-600 dark:hover:text-red-400
|
|
63
|
+
opacity-70 hover:opacity-100 transition-all"
|
|
64
|
+
:title="$t('Remove file')"
|
|
65
|
+
>
|
|
66
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
67
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
68
|
+
</svg>
|
|
69
|
+
</button>
|
|
33
70
|
</div>
|
|
34
|
-
|
|
35
|
-
<p v-if="!selectedFiles.length" class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">{{ $t('Click to upload') }}</span> {{ $t('or drag and drop') }}</p>
|
|
36
|
-
<p class="text-xs text-gray-500 dark:text-gray-400">
|
|
37
|
-
{{ props.extensions.join(', ').toUpperCase().replace(/\./g, '') }}
|
|
38
|
-
<template v-if="props.maxSizeBytes">
|
|
39
|
-
(Max size: {{ humanifySize(props.maxSizeBytes) }})
|
|
40
|
-
</template>
|
|
41
|
-
</p>
|
|
71
|
+
</template>
|
|
42
72
|
</div>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
73
|
+
|
|
74
|
+
<p
|
|
75
|
+
v-if="!selectedFiles.length"
|
|
76
|
+
class="mb-2 text-sm text-lightDropzoneText dark:text-darkDropzoneText"
|
|
77
|
+
>
|
|
78
|
+
<span class="font-semibold">{{ $t('Click to upload') }}</span>
|
|
79
|
+
{{ $t('or drag and drop') }}
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
<p class="text-xs text-lightDropzoneText dark:text-darkDropzoneText">
|
|
83
|
+
{{ normalizedExtensions.join(', ').toUpperCase().replace(/\./g, '') }}
|
|
84
|
+
<template v-if="props.maxSizeBytes">
|
|
85
|
+
(Max size: {{ humanifySize(props.maxSizeBytes) }})
|
|
86
|
+
</template>
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
48
89
|
</label>
|
|
49
|
-
</form>
|
|
90
|
+
</form>
|
|
50
91
|
</template>
|
|
51
92
|
|
|
52
93
|
<script setup lang="ts">
|
|
53
94
|
import { humanifySize } from '@/utils';
|
|
54
|
-
import { ref, type Ref } from 'vue';
|
|
95
|
+
import { ref, type Ref, computed } from 'vue';
|
|
55
96
|
import { IconFileSolid } from '@iconify-prerendered/vue-flowbite';
|
|
56
97
|
import { watch } from 'vue';
|
|
57
98
|
import adminforth from '@/adminforth';
|
|
@@ -67,25 +108,40 @@ const emit = defineEmits(['update:modelValue']);
|
|
|
67
108
|
|
|
68
109
|
const id = `afcl-dropzone-${Math.random().toString(36).substring(7)}`;
|
|
69
110
|
|
|
111
|
+
const normalizedExtensions = computed(() => {
|
|
112
|
+
return props.extensions.map(ext => {
|
|
113
|
+
const trimmed = ext.trim().toLowerCase();
|
|
114
|
+
return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
70
118
|
const selectedFiles: Ref<{
|
|
71
119
|
name: string,
|
|
72
120
|
size: number,
|
|
73
121
|
mime: string,
|
|
74
122
|
}[]> = ref([]);
|
|
75
123
|
|
|
124
|
+
const storedFiles: Ref<File[]> = ref([]);
|
|
125
|
+
|
|
76
126
|
watch(() => props.modelValue, (files) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
});
|
|
127
|
+
if (files && files.length > 0) {
|
|
128
|
+
selectedFiles.value = Array.from(files).map(file => ({
|
|
129
|
+
name: file.name,
|
|
130
|
+
size: file.size,
|
|
131
|
+
mime: file.type,
|
|
132
|
+
}));
|
|
133
|
+
storedFiles.value = Array.from(files);
|
|
134
|
+
} else {
|
|
135
|
+
selectedFiles.value = [];
|
|
136
|
+
storedFiles.value = [];
|
|
137
|
+
}
|
|
138
|
+
}, { immediate: true });
|
|
83
139
|
|
|
84
140
|
function doEmit(filesIn: FileList) {
|
|
85
141
|
|
|
86
142
|
const multiple = props.multiple || false;
|
|
87
143
|
const files = Array.from(filesIn);
|
|
88
|
-
const allowedExtensions =
|
|
144
|
+
const allowedExtensions = normalizedExtensions.value;
|
|
89
145
|
const maxSizeBytes = props.maxSizeBytes;
|
|
90
146
|
|
|
91
147
|
if (!files.length) return;
|
|
@@ -96,6 +152,18 @@ function doEmit(filesIn: FileList) {
|
|
|
96
152
|
const extension = file.name.split('.').pop()?.toLowerCase() || '';
|
|
97
153
|
const size = file.size;
|
|
98
154
|
|
|
155
|
+
const isDuplicate = storedFiles.value.some(
|
|
156
|
+
existingFile => existingFile.name === file.name && existingFile.size === file.size
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (isDuplicate) {
|
|
160
|
+
adminforth.alert({
|
|
161
|
+
message: `The file "${file.name}" is already selected.`,
|
|
162
|
+
variant: 'warning',
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
99
167
|
if (!allowedExtensions.includes(`.${extension}`)) {
|
|
100
168
|
adminforth.alert({
|
|
101
169
|
message: `Sorry, the file type .${extension} is not allowed. Please upload a file with one of the following extensions: ${allowedExtensions.join(', ')}`,
|
|
@@ -110,26 +178,37 @@ function doEmit(filesIn: FileList) {
|
|
|
110
178
|
});
|
|
111
179
|
return;
|
|
112
180
|
}
|
|
113
|
-
|
|
181
|
+
|
|
114
182
|
validFiles.push(file);
|
|
115
183
|
});
|
|
116
184
|
|
|
117
185
|
if (!multiple) {
|
|
118
|
-
validFiles.
|
|
186
|
+
storedFiles.value = validFiles.slice(0, 1);
|
|
187
|
+
} else {
|
|
188
|
+
storedFiles.value = [...storedFiles.value, ...validFiles];
|
|
119
189
|
}
|
|
120
|
-
|
|
190
|
+
|
|
191
|
+
selectedFiles.value = storedFiles.value.map(file => ({
|
|
121
192
|
name: file.name,
|
|
122
193
|
size: file.size,
|
|
123
194
|
mime: file.type,
|
|
124
195
|
}));
|
|
125
196
|
|
|
126
|
-
emit('update:modelValue',
|
|
197
|
+
emit('update:modelValue', storedFiles.value);
|
|
198
|
+
|
|
127
199
|
}
|
|
128
200
|
|
|
129
201
|
const dragging = ref(false);
|
|
130
202
|
|
|
203
|
+
function removeFile(index: number) {
|
|
204
|
+
storedFiles.value = storedFiles.value.filter((_, i) => i !== index);
|
|
205
|
+
selectedFiles.value = selectedFiles.value.filter((_, i) => i !== index);
|
|
206
|
+
emit('update:modelValue', storedFiles.value);
|
|
207
|
+
}
|
|
208
|
+
|
|
131
209
|
function clear() {
|
|
132
210
|
selectedFiles.value = [];
|
|
211
|
+
storedFiles.value = [];
|
|
133
212
|
emit('update:modelValue', []);
|
|
134
213
|
const form = document.getElementById(id)?.closest('form');
|
|
135
214
|
form?.reset();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<div class="afcl-input-wrapper flex z-0 relative" :class="{'opacity-50' : readonly}">
|
|
4
4
|
<span
|
|
5
5
|
v-if="$slots.prefix || prefix"
|
|
6
|
-
class="inline-flex items-center px-3 text-sm text-
|
|
6
|
+
class="inline-flex items-center px-3 text-sm text-lightInputText bg-lightInputBackground border border-s-0 border-lightInputBorder rounded-s-md dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder">
|
|
7
7
|
<slot name="prefix">{{ prefix }}</slot>
|
|
8
8
|
</span>
|
|
9
9
|
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
ref="input"
|
|
13
13
|
v-bind="$attrs"
|
|
14
14
|
:type="type"
|
|
15
|
-
@input="$emit('update:modelValue', $event.target?.value)"
|
|
15
|
+
@input="$emit('update:modelValue', type === 'number' ? Number(($event.target as HTMLInputElement)?.value) : ($event.target as HTMLInputElement)?.value)"
|
|
16
16
|
:value="modelValue"
|
|
17
17
|
aria-describedby="helper-text-explanation"
|
|
18
|
-
class="afcl-input
|
|
19
|
-
blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-
|
|
20
|
-
:class="{'rounded-l-md': !$slots.prefix && !prefix, 'rounded-r-md': !$slots.suffix && !suffix, 'w-full': fullWidth}"
|
|
18
|
+
class="afcl-input inline-flex bg-lightInputBackground border border-lightInputBorder rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
|
|
19
|
+
blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkInputBackground dark:border-darkInputBorder placeholder-lightInputPlaceholderText dark:placeholder-darkInputPlaceholderText dark:text-darkInputText translate-y-0"
|
|
20
|
+
:class="{'rounded-l-md': !$slots.prefix && !prefix, 'rounded-r-md': !$slots.suffix && !suffix, 'w-full': fullWidth, 'text-base': isIOSDevice, 'text-sm': !isIOSDevice }"
|
|
21
21
|
:disabled="readonly"
|
|
22
22
|
>
|
|
23
23
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
<span
|
|
28
28
|
v-if="$slots.suffix || suffix"
|
|
29
|
-
class="inline-flex items-center px-3 text-sm text-
|
|
29
|
+
class="inline-flex items-center px-3 text-sm text-lightInputText bg-lightInputBackground border border-s-0 border-lightInputBorder rounded-e-md dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder ">
|
|
30
30
|
<slot name="suffix">{{ suffix }}</slot>
|
|
31
31
|
</span>
|
|
32
32
|
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
<script setup lang="ts">
|
|
37
37
|
|
|
38
38
|
import { ref } from 'vue';
|
|
39
|
+
const isIOSDevice = isIOS();
|
|
39
40
|
|
|
40
41
|
const props = defineProps<{
|
|
41
42
|
type: string,
|
|
@@ -52,5 +53,12 @@ defineExpose({
|
|
|
52
53
|
focus: () => input.value?.focus(),
|
|
53
54
|
});
|
|
54
55
|
|
|
56
|
+
function isIOS() {
|
|
57
|
+
return (
|
|
58
|
+
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
59
|
+
(navigator.userAgent.includes('Mac') && 'ontouchend' in document)
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
</script>
|
|
56
64
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<JsonViewer
|
|
3
|
+
class="afcl-json-viewer min-w-[6rem]"
|
|
4
|
+
:value="value"
|
|
5
|
+
:expandDepth="expandDepth"
|
|
6
|
+
copyable
|
|
7
|
+
sort
|
|
8
|
+
:theme="currentTheme"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import { computed } from 'vue'
|
|
14
|
+
import { JsonViewer } from 'vue3-json-viewer'
|
|
15
|
+
import { useCoreStore } from '@/stores/core'
|
|
16
|
+
|
|
17
|
+
defineProps<{
|
|
18
|
+
value: any
|
|
19
|
+
expandDepth?: number
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const coreStore = useCoreStore()
|
|
23
|
+
|
|
24
|
+
const currentTheme = computed(() => (coreStore.theme === 'dark' ? 'dark' : 'light'))
|
|
25
|
+
</script>
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
v-bind="$attrs"
|
|
4
4
|
:to="props.to"
|
|
5
5
|
type="submit"
|
|
6
|
-
class="afcl-link-button flex items-center justify-center gap-1 text-
|
|
7
|
-
focus:ring-4 focus:outline-none focus:ring-
|
|
6
|
+
class="afcl-link-button flex items-center justify-center gap-1 text-lightButtonsText bg-lightButtonsBackground border border-lightButtonsBorder dark:bg-darkButtonsBackground hover:bg-lightButtonsHover hover:border-lightButtonsBorderHover
|
|
7
|
+
focus:ring-4 focus:outline-none focus:ring-lightButtonFocusRing focus:ring-opacity-50 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-darkButtonFocusRing dark:text-darkButtonsText dark:border-darkButtonsBorder dark:hover:bg-darkButtonsHover dark:hover:border-darkButtonsBorderHover"
|
|
8
8
|
:class="{
|
|
9
9
|
'cursor-default': props.disabled,
|
|
10
10
|
'opacity-50': props.disabled,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script setup lang="ts">
|
|
19
19
|
|
|
20
20
|
const props = defineProps<{
|
|
21
|
-
disabled
|
|
21
|
+
disabled?: boolean,
|
|
22
22
|
to: string,
|
|
23
23
|
}>();
|
|
24
24
|
|
|
@@ -63,8 +63,8 @@ const optionsBase = {
|
|
|
63
63
|
show: false,
|
|
64
64
|
fontFamily: "Inter, sans-serif",
|
|
65
65
|
label: "",
|
|
66
|
-
formatter: function (w) {
|
|
67
|
-
const sum = w.globals.seriesTotals.reduce((a, b) => {
|
|
66
|
+
formatter: function (w: any) {
|
|
67
|
+
const sum = w.globals.seriesTotals.reduce((a: any, b: any) => {
|
|
68
68
|
return a + b
|
|
69
69
|
}, 0)
|
|
70
70
|
return sum
|
|
@@ -74,7 +74,7 @@ const optionsBase = {
|
|
|
74
74
|
show: true,
|
|
75
75
|
fontFamily: "Inter, sans-serif",
|
|
76
76
|
offsetY: -20,
|
|
77
|
-
formatter: function (value) {
|
|
77
|
+
formatter: function (value: any) {
|
|
78
78
|
return value + "k"
|
|
79
79
|
},
|
|
80
80
|
},
|
|
@@ -100,14 +100,14 @@ const optionsBase = {
|
|
|
100
100
|
},
|
|
101
101
|
yaxis: {
|
|
102
102
|
labels: {
|
|
103
|
-
formatter: function (value) {
|
|
103
|
+
formatter: function (value: any) {
|
|
104
104
|
return value;
|
|
105
105
|
},
|
|
106
106
|
},
|
|
107
107
|
},
|
|
108
108
|
xaxis: {
|
|
109
109
|
labels: {
|
|
110
|
-
formatter: function (value) {
|
|
110
|
+
formatter: function (value: any) {
|
|
111
111
|
return value;
|
|
112
112
|
},
|
|
113
113
|
},
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative mt-4 lg:mt-10 w-full max-w-[700px] bg-
|
|
3
|
-
<span class="absolute -top-6 left-0 text-sm text-
|
|
4
|
-
<span class="absolute -top-6 right-0 text-sm text-
|
|
2
|
+
<div class="relative mt-4 lg:mt-10 w-full max-w-[700px] bg-lightProgressBarUnfilledColor rounded-full h-2.5 dark:bg-darkProgressBarUnfilledColor">
|
|
3
|
+
<span class="absolute -top-6 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ leftLabel }}</span>
|
|
4
|
+
<span class="absolute -top-6 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ rightLabel }}</span>
|
|
5
5
|
<div
|
|
6
|
-
class="bg-
|
|
6
|
+
class="bg-lightProgressBarFilledColor dark:bg-darkProgressBarFilledColor h-2.5 rounded-full transition-all duration-300 ease-in-out"
|
|
7
7
|
:style="{ width: `${percentage}%` }"
|
|
8
8
|
></div>
|
|
9
|
-
<span v-if="showValues" class="absolute top-4 left-0 text-sm text-
|
|
10
|
-
<span v-if="showProgress" class="absolute top-4 right-1/2 translate-x-1/2 text-sm text-
|
|
11
|
-
<span v-if="showValues" class="absolute top-4 right-0 text-sm text-
|
|
9
|
+
<span v-if="showValues" class="absolute top-4 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(minValue) }}</span>
|
|
10
|
+
<span v-if="showProgress" class="absolute top-4 right-1/2 translate-x-1/2 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ progressText }}</span>
|
|
11
|
+
<span v-if="showValues" class="absolute top-4 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(maxValue) }}</span>
|
|
12
12
|
</div>
|
|
13
13
|
</template>
|
|
14
14
|
|