adminforth 2.7.18 → 2.8.0
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/createApp/templates/index.ts.hbs +2 -1
- package/commands/createCustomComponent/main.js +1 -0
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +33 -15
- 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 +30 -1
- 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 +11 -0
- 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/modules/codeInjector.d.ts +1 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +4 -0
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +6 -2
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +2 -0
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +8 -1
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +9 -2
- package/dist/modules/styles.js.map +1 -1
- package/dist/spa/src/App.vue +12 -4
- package/dist/spa/src/adminforth.ts +31 -11
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Checkbox.vue +2 -2
- package/dist/spa/src/afcl/Dialog.vue +38 -21
- package/dist/spa/src/afcl/Dropzone.vue +2 -2
- package/dist/spa/src/afcl/Input.vue +1 -1
- package/dist/spa/src/afcl/LinkButton.vue +1 -1
- package/dist/spa/src/afcl/PieChart.vue +5 -5
- package/dist/spa/src/afcl/Select.vue +17 -12
- package/dist/spa/src/afcl/Table.vue +202 -72
- package/dist/spa/src/afcl/Textarea.vue +31 -0
- package/dist/spa/src/afcl/Toggle.vue +2 -2
- package/dist/spa/src/afcl/Tooltip.vue +0 -1
- package/dist/spa/src/afcl/index.ts +1 -1
- package/dist/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/src/components/ColumnValueInput.vue +11 -11
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +2 -2
- package/dist/spa/src/components/CustomRangePicker.vue +5 -5
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +7 -6
- package/dist/spa/src/components/GroupsTable.vue +2 -1
- package/dist/spa/src/components/MenuLink.vue +3 -3
- package/dist/spa/src/components/ResourceForm.vue +35 -27
- package/dist/spa/src/components/ResourceListTable.vue +42 -42
- package/dist/spa/src/components/ResourceListTableVirtual.vue +41 -41
- package/dist/spa/src/components/ShowTable.vue +3 -3
- package/dist/spa/src/components/SkeleteLoader.vue +2 -2
- package/dist/spa/src/components/ThreeDotsMenu.vue +69 -10
- package/dist/spa/src/components/Toast.vue +25 -2
- package/dist/spa/src/components/ValueRenderer.vue +39 -12
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/shims-vue.d.ts +5 -0
- package/dist/spa/src/spa_types/core.ts +1 -1
- 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 +31 -6
- package/dist/spa/src/types/Common.ts +33 -24
- package/dist/spa/src/types/FrontendAPI.ts +21 -5
- package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -4
- package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
- package/dist/spa/src/types/adapters/index.ts +1 -0
- package/dist/spa/src/utils.ts +8 -7
- package/dist/spa/src/views/CreateView.vue +15 -16
- package/dist/spa/src/views/EditView.vue +23 -17
- package/dist/spa/src/views/ListView.vue +116 -66
- package/dist/spa/src/views/LoginView.vue +2 -9
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +59 -39
- 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 +16 -1
- 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 +27 -22
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +21 -3
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/dist/types/adapters/EmailAdapter.d.ts +2 -3
- package/dist/types/adapters/EmailAdapter.d.ts.map +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/index.d.ts +1 -0
- package/dist/types/adapters/index.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/spa/src/App.vue
CHANGED
|
@@ -78,13 +78,21 @@
|
|
|
78
78
|
>
|
|
79
79
|
<div class="h-full px-3 pb-4 overflow-y-auto bg-lightSidebar dark:bg-darkSidebar border-r border-lightSidebarBorder dark:border-darkSidebarBorder">
|
|
80
80
|
<div class="af-logo-title-wrapper flex ms-2 m-4">
|
|
81
|
-
<img :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="af-logo h-8 me-3" />
|
|
81
|
+
<img v-if="coreStore.config?.showBrandLogoInSidebar !== false" :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="af-logo h-8 me-3" />
|
|
82
82
|
<span
|
|
83
83
|
v-if="coreStore.config?.showBrandNameInSidebar"
|
|
84
84
|
class="af-title self-center text-lightNavbarText-size font-semibold sm:text-lightNavbarText-size whitespace-nowrap dark:text-darkSidebarText text-lightSidebarText"
|
|
85
85
|
>
|
|
86
86
|
{{ coreStore.config?.brandName }}
|
|
87
87
|
</span>
|
|
88
|
+
<div class="flex items-center gap-2 w-auto" :class="{'w-full justify-end': coreStore.config?.showBrandLogoInSidebar === false}">
|
|
89
|
+
<component
|
|
90
|
+
v-for="c in coreStore?.config?.globalInjections?.sidebarTop || []"
|
|
91
|
+
:is="getCustomComponent(c)"
|
|
92
|
+
:meta="c.meta"
|
|
93
|
+
:adminUser="coreStore.adminUser"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
88
96
|
</div>
|
|
89
97
|
|
|
90
98
|
<ul class="af-sidebar-container space-y-2 font-medium">
|
|
@@ -195,7 +203,7 @@
|
|
|
195
203
|
</div>
|
|
196
204
|
</div>
|
|
197
205
|
<AcceptModal />
|
|
198
|
-
<div v-if="toastStore.toasts.length>0" class="fixed bottom-5 right-5 flex gap-1 flex-col-reverse z-
|
|
206
|
+
<div v-if="toastStore.toasts.length>0" class="fixed bottom-5 right-5 flex gap-1 flex-col-reverse z-[100]">
|
|
199
207
|
<transition-group
|
|
200
208
|
name="fade"
|
|
201
209
|
tag="div"
|
|
@@ -274,7 +282,7 @@ const defaultLayout = ref(true);
|
|
|
274
282
|
const route = useRoute();
|
|
275
283
|
const router = useRouter();
|
|
276
284
|
//create a ref to store the opened menu items with ts type;
|
|
277
|
-
const opened = ref<string[]>([]);
|
|
285
|
+
const opened = ref<(string|number)[]>([]);
|
|
278
286
|
const publicConfigLoaded = ref(false);
|
|
279
287
|
const dropdownUserButton = ref(null);
|
|
280
288
|
|
|
@@ -296,7 +304,7 @@ function toggleTheme() {
|
|
|
296
304
|
coreStore.toggleTheme();
|
|
297
305
|
}
|
|
298
306
|
|
|
299
|
-
function clickOnMenuItem(label: string) {
|
|
307
|
+
function clickOnMenuItem(label: string | number) {
|
|
300
308
|
if (opened.value.includes(label)) {
|
|
301
309
|
opened.value = opened.value.filter((item) => item !== label);
|
|
302
310
|
} else {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { FilterParams, FrontendAPIInterface } from
|
|
2
|
-
import type {
|
|
3
|
-
import type { AdminForthFilterOperators, AdminForthResourceColumn } from '@/types/Common';
|
|
1
|
+
import type { FilterParams, FrontendAPIInterface, ConfirmParams, AlertParams, } from '@/types/FrontendAPI';
|
|
2
|
+
import type { AdminForthFilterOperators, AdminForthResourceColumnCommon } from '@/types/Common';
|
|
4
3
|
import { useToastStore } from '@/stores/toast';
|
|
5
4
|
import { useModalStore } from '@/stores/modal';
|
|
6
5
|
import { useCoreStore } from '@/stores/core';
|
|
@@ -36,6 +35,10 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
36
35
|
refreshMenuBadges: () => void;
|
|
37
36
|
}
|
|
38
37
|
|
|
38
|
+
public show: {
|
|
39
|
+
refresh(): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
39
42
|
closeUserMenuDropdown(): void {
|
|
40
43
|
console.log('closeUserMenuDropdown')
|
|
41
44
|
}
|
|
@@ -73,9 +76,15 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
73
76
|
updateFilter: this.updateListFilter.bind(this),
|
|
74
77
|
clearFilters: this.clearListFilters.bind(this),
|
|
75
78
|
}
|
|
79
|
+
|
|
80
|
+
this.show = {
|
|
81
|
+
refresh: () => {
|
|
82
|
+
console.log('show.refresh')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
76
85
|
}
|
|
77
86
|
|
|
78
|
-
confirm(params: ConfirmParams): Promise<
|
|
87
|
+
confirm(params: ConfirmParams): Promise<boolean> {
|
|
79
88
|
return new Promise((resolve, reject) => {
|
|
80
89
|
this.modalStore.setModalContent({
|
|
81
90
|
content: params.message,
|
|
@@ -88,13 +97,24 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
88
97
|
})
|
|
89
98
|
}
|
|
90
99
|
|
|
91
|
-
alert(params: AlertParams): void {
|
|
92
|
-
|
|
100
|
+
alert(params: AlertParams): void | Promise<string> | string {
|
|
101
|
+
const toats = {
|
|
93
102
|
message: params.message,
|
|
94
103
|
messageHtml: params.messageHtml,
|
|
95
104
|
variant: params.variant,
|
|
96
|
-
timeout: params.timeout
|
|
97
|
-
|
|
105
|
+
timeout: params.timeout,
|
|
106
|
+
buttons: params.buttons,
|
|
107
|
+
}
|
|
108
|
+
if (params.buttons && params.buttons.length > 0) {
|
|
109
|
+
return new Promise<string>((resolve) => {
|
|
110
|
+
this.toastStore.addToast({
|
|
111
|
+
...toats,
|
|
112
|
+
onResolve: (value?: any) => resolve(String(value ?? '')),
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
} else {
|
|
116
|
+
this.toastStore.addToast({...toats})
|
|
117
|
+
}
|
|
98
118
|
}
|
|
99
119
|
|
|
100
120
|
listFilterValidation(filter: FilterParams): boolean {
|
|
@@ -102,7 +122,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
102
122
|
throw new Error(`Cannot use ${this.setListFilter.name} filter on a list page`)
|
|
103
123
|
} else {
|
|
104
124
|
console.log(this.coreStore.resourceColumnsWithFilters,'core store')
|
|
105
|
-
const filterField = this.coreStore.resourceColumnsWithFilters.find((col:
|
|
125
|
+
const filterField = this.coreStore.resourceColumnsWithFilters.find((col: AdminForthResourceColumnCommon) => col.name === filter.field)
|
|
106
126
|
if(!filterField){
|
|
107
127
|
throw new Error(`Field ${filter.field} is not available for filtering`)
|
|
108
128
|
}
|
|
@@ -113,10 +133,10 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
113
133
|
|
|
114
134
|
setListFilter(filter: FilterParams): void {
|
|
115
135
|
if(this.listFilterValidation(filter)){
|
|
116
|
-
if(this.filtersStore.filters.some((f) => {return f.field === filter.field && f.operator === filter.operator})){
|
|
136
|
+
if(this.filtersStore.filters.some((f: any) => {return f.field === filter.field && f.operator === filter.operator})){
|
|
117
137
|
throw new Error(`Filter ${filter.field} with operator ${filter.operator} already exists`)
|
|
118
138
|
} else {
|
|
119
|
-
|
|
139
|
+
this.filtersStore.setFilter(filter)
|
|
120
140
|
}
|
|
121
141
|
}
|
|
122
142
|
}
|
|
@@ -60,7 +60,7 @@ const optionsBase = {
|
|
|
60
60
|
tooltip: {
|
|
61
61
|
shared: true,
|
|
62
62
|
intersect: false,
|
|
63
|
-
formatter: function (value) {
|
|
63
|
+
formatter: function (value: any) {
|
|
64
64
|
return value
|
|
65
65
|
},
|
|
66
66
|
},
|
|
@@ -71,7 +71,7 @@ const optionsBase = {
|
|
|
71
71
|
fontFamily: "Inter, sans-serif",
|
|
72
72
|
cssClass: 'text-xs font-normal fill-gray-500 dark:fill-gray-400'
|
|
73
73
|
},
|
|
74
|
-
formatter: function (value) {
|
|
74
|
+
formatter: function (value: any) {
|
|
75
75
|
return value
|
|
76
76
|
}
|
|
77
77
|
},
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
type="checkbox"
|
|
7
7
|
:checked="props.modelValue"
|
|
8
8
|
:disabled="props.disabled"
|
|
9
|
-
@change="$emit('update:modelValue', $event.target.checked)"
|
|
9
|
+
@change="$emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
|
|
10
10
|
class="peer appearance-none min-w-4 min-h-4 bg-lightCheckboxBgUnchecked border border-lightCheckboxBorderColor rounded-sm checked:bg-lightCheckboxBgChecked
|
|
11
11
|
focus:ring-lightFocusRing dark:focus:ring-darkFocusRing dark:focus:ring-darkFocusRing
|
|
12
12
|
focus:ring-2 dark:bg-darkCheckboxBgUnchecked dark:border-darkCheckboxBorderColor dark:checked:bg-darkCheckboxBgChecked cursor-pointer"
|
|
13
13
|
>
|
|
14
14
|
<div class="pointer-events-none absolute text-lightCheckboxIconColor dark:text-darkCheckboxIconColor leading-none peer-checked:block hidden">
|
|
15
|
-
<IconCheckOutline
|
|
15
|
+
<IconCheckOutline class="w-[1.1rem] h-[1.1rem]" />
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
18
18
|
<label :for="id" class="ms-2 text-sm font-medium text-lightTextLabel dark:text-darkTextLabel">
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<slot name="trigger"></slot>
|
|
7
7
|
</div>
|
|
8
8
|
<Teleport to="body">
|
|
9
|
-
<div ref="modalEl" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-full max-h-full">
|
|
10
|
-
<div v-bind="$attrs" class="relative p-4 max-w-2xl max-h-full" :class="$attrs.class?.includes('w-') ? '' : 'w-full'">
|
|
9
|
+
<div ref="modalEl" tabindex="-1" aria-hidden="true" class="[scrollbar-gutter:stable] hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-full max-h-full">
|
|
10
|
+
<div v-bind="$attrs" class="relative p-4 max-w-2xl max-h-full" :class="($attrs.class as string)?.includes('w-') ? '' : 'w-full'">
|
|
11
11
|
<!-- Modal content -->
|
|
12
12
|
<div class="relative bg-lightDialogBackgorund rounded-lg shadow-sm dark:bg-darkDialogBackgorund">
|
|
13
13
|
<!-- Modal header -->
|
|
@@ -63,24 +63,31 @@ import { Modal } from 'flowbite';
|
|
|
63
63
|
const modalEl = ref(null);
|
|
64
64
|
const modal: Ref<Modal|null> = ref(null);
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
buttons
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
66
|
+
interface DialogButton {
|
|
67
|
+
label: string
|
|
68
|
+
onclick: (dialog: any) => void
|
|
69
|
+
options?: Record<string, any>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface DialogProps {
|
|
73
|
+
header?: string
|
|
74
|
+
headerCloseButton?: boolean
|
|
75
|
+
buttons?: DialogButton[]
|
|
76
|
+
clickToCloseOutside?: boolean
|
|
77
|
+
beforeCloseFunction?: (() => void | Promise<void>) | null
|
|
78
|
+
beforeOpenFunction?: (() => void | Promise<void>) | null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const props = withDefaults(defineProps<DialogProps>(), {
|
|
82
|
+
header: '',
|
|
83
|
+
headerCloseButton: true,
|
|
84
|
+
buttons: () => [
|
|
85
|
+
{ label: 'Close', onclick: (dialog: any) => dialog.hide(), type: '' },
|
|
86
|
+
],
|
|
87
|
+
clickToCloseOutside: true,
|
|
88
|
+
beforeCloseFunction: null,
|
|
89
|
+
beforeOpenFunction: null,
|
|
90
|
+
})
|
|
84
91
|
|
|
85
92
|
onMounted(async () => {
|
|
86
93
|
//await one tick when all is mounted
|
|
@@ -89,7 +96,17 @@ onMounted(async () => {
|
|
|
89
96
|
modalEl.value,
|
|
90
97
|
{
|
|
91
98
|
backdrop: props.clickToCloseOutside ? 'dynamic' : 'static',
|
|
92
|
-
|
|
99
|
+
onHide: async () => {
|
|
100
|
+
if (props.beforeCloseFunction) {
|
|
101
|
+
await props.beforeCloseFunction();
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
onShow: async () => {
|
|
105
|
+
if (props.beforeOpenFunction) {
|
|
106
|
+
await props.beforeOpenFunction();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
}
|
|
93
110
|
);
|
|
94
111
|
})
|
|
95
112
|
|
|
@@ -3,7 +3,7 @@
|
|
|
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
8
|
<label :id="id" class="flex flex-col items-center justify-center w-full border-2 border-dashed rounded-lg cursor-pointer
|
|
9
9
|
hover:bg-lightDropzoneBackgroundHover hover:border-lightDropzoneBorderHover dark:hover:border-darkDropzoneBorderHover dark:hover:bg-darkDropzoneBackgroundHover"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
</div>
|
|
43
43
|
<input :id="id" type="file" class="hidden"
|
|
44
44
|
:accept="props.extensions.join(', ')"
|
|
45
|
-
@change="doEmit($event.target.files)"
|
|
45
|
+
@change="$event.target && doEmit(($event.target as HTMLInputElement).files!)"
|
|
46
46
|
:multiple="props.multiple || false"
|
|
47
47
|
/>
|
|
48
48
|
</label>
|
|
@@ -12,7 +12,7 @@
|
|
|
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
18
|
class="afcl-input inline-flex bg-lightInputBackground border border-lightInputBorder text-lightInputText text-sm rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
|
|
@@ -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
|
},
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
/>
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
|
-
<teleport to="body" v-if="teleportToBody && showDropdown">
|
|
40
|
-
<div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop}"
|
|
41
|
-
class="fixed
|
|
39
|
+
<teleport to="body" v-if="(teleportToBody || teleportToTop) && showDropdown">
|
|
40
|
+
<div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop, 'z-[5]': teleportToBody, 'z-[1000]': teleportToTop}"
|
|
41
|
+
class="fixed w-full bg-lightDropdownOptionsBackground shadow-lg dark:shadow-black dark:bg-darkDropdownOptionsBackground
|
|
42
42
|
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"
|
|
43
43
|
@scroll="handleDropdownScroll">
|
|
44
44
|
<div
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
<label v-if="!$slots.item" :for="item.value">{{ item.label }}</label>
|
|
53
53
|
</div>
|
|
54
54
|
<div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText dark:text-darkDropdownOptionsText">
|
|
55
|
-
{{ options
|
|
55
|
+
{{ options?.length ? $t('No results found') : $t('No items here') }}
|
|
56
56
|
</div>
|
|
57
57
|
|
|
58
58
|
<div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-gray-400">
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
</div>
|
|
62
62
|
</teleport>
|
|
63
63
|
|
|
64
|
-
<div v-if="!teleportToBody && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
|
|
64
|
+
<div v-if="!teleportToBody && !teleportToTop && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
|
|
65
65
|
class="absolute z-10 mt-1 w-full bg-lightDropdownOptionsBackground shadow-lg text-lightDropdownButtonsText dark:shadow-black dark:bg-darkDropdownOptionsBackground
|
|
66
66
|
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"
|
|
67
67
|
@scroll="handleDropdownScroll">
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
<label v-if="!$slots.item" :for="item.value">{{ item.label }}</label>
|
|
77
77
|
</div>
|
|
78
78
|
<div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText dark:text-darkDropdownOptionsText">
|
|
79
|
-
{{ options
|
|
79
|
+
{{ options?.length ? $t('No results found') : $t('No items here') }}
|
|
80
80
|
</div>
|
|
81
81
|
<div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-darkDropdownOptionsText">
|
|
82
82
|
<slot name="extra-item"></slot>
|
|
@@ -114,14 +114,15 @@
|
|
|
114
114
|
</template>
|
|
115
115
|
|
|
116
116
|
<script setup lang="ts">
|
|
117
|
-
import { ref, computed, onMounted, onUnmounted, watch, nextTick, type Ref } from 'vue';
|
|
117
|
+
import { ref, computed, onMounted, onUnmounted, watch, nextTick,type PropType, type Ref } from 'vue';
|
|
118
118
|
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
|
|
119
119
|
import { useElementSize } from '@vueuse/core'
|
|
120
120
|
|
|
121
121
|
const props = defineProps({
|
|
122
122
|
options: Array,
|
|
123
123
|
modelValue: {
|
|
124
|
-
|
|
124
|
+
type: Array as PropType<(string | number)[]>,
|
|
125
|
+
default: () => [],
|
|
125
126
|
},
|
|
126
127
|
multiple: {
|
|
127
128
|
type: Boolean,
|
|
@@ -143,6 +144,10 @@ const props = defineProps({
|
|
|
143
144
|
type: Boolean,
|
|
144
145
|
default: false,
|
|
145
146
|
},
|
|
147
|
+
teleportToTop: {
|
|
148
|
+
type: Boolean,
|
|
149
|
+
default: false,
|
|
150
|
+
},
|
|
146
151
|
});
|
|
147
152
|
|
|
148
153
|
const emit = defineEmits(['update:modelValue', 'scroll-near-end', 'search']);
|
|
@@ -174,14 +179,14 @@ function inputInput() {
|
|
|
174
179
|
function updateFromProps() {
|
|
175
180
|
if (props.modelValue !== undefined) {
|
|
176
181
|
if (!props.multiple) {
|
|
177
|
-
const el = props.options
|
|
182
|
+
const el = props.options?.find((item: any) => item.value === props.modelValue);
|
|
178
183
|
if (el) {
|
|
179
184
|
selectedItems.value = [el];
|
|
180
185
|
} else {
|
|
181
186
|
selectedItems.value = [];
|
|
182
187
|
}
|
|
183
188
|
} else {
|
|
184
|
-
selectedItems.value = props.options
|
|
189
|
+
selectedItems.value = props.options?.filter((item: any) => props.modelValue?.includes(item.value)) || [];
|
|
185
190
|
}
|
|
186
191
|
}
|
|
187
192
|
}
|
|
@@ -264,7 +269,7 @@ onMounted(() => {
|
|
|
264
269
|
}
|
|
265
270
|
});
|
|
266
271
|
|
|
267
|
-
const filteredItems = computed(() => {
|
|
272
|
+
const filteredItems: Ref<any[]> = computed(() => {
|
|
268
273
|
|
|
269
274
|
if (props.searchDisabled) {
|
|
270
275
|
return props.options || [];
|
|
@@ -291,7 +296,7 @@ const removeClickListener = () => {
|
|
|
291
296
|
document.removeEventListener('click', handleClickOutside);
|
|
292
297
|
};
|
|
293
298
|
|
|
294
|
-
const toogleItem = (item) => {
|
|
299
|
+
const toogleItem = (item: any) => {
|
|
295
300
|
if (selectedItems.value.includes(item)) {
|
|
296
301
|
selectedItems.value = selectedItems.value.filter(i => i.value !== item.value);
|
|
297
302
|
} else {
|