adminforth 2.4.0-next.124 → 2.4.0-next.126
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/spa/src/adminforth.ts +5 -6
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Checkbox.vue +1 -1
- 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 +9 -8
- package/dist/spa/src/afcl/Table.vue +18 -9
- package/dist/spa/src/afcl/Toggle.vue +1 -1
- package/dist/spa/src/components/ResourceListTable.vue +42 -42
- package/dist/spa/src/components/ResourceListTableVirtual.vue +41 -41
- package/dist/spa/src/components/SkeleteLoader.vue +2 -2
- package/dist/spa/src/components/ThreeDotsMenu.vue +15 -14
- package/dist/spa/src/components/ValueRenderer.vue +39 -12
- package/dist/spa/src/spa_types/core.ts +1 -1
- package/dist/spa/src/stores/modal.ts +6 -1
- package/dist/spa/src/types/Common.ts +17 -21
- package/dist/spa/src/views/CreateView.vue +1 -5
- package/dist/spa/src/views/EditView.vue +1 -5
- package/dist/spa/src/views/ListView.vue +67 -38
- package/dist/spa/src/views/ShowView.vue +36 -37
- package/dist/spa/vite.config.ts +3 -3
- package/dist/types/Common.d.ts +12 -20
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/package.json +1 -1
|
@@ -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';
|
|
@@ -85,7 +84,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
confirm(params: ConfirmParams): Promise<
|
|
87
|
+
confirm(params: ConfirmParams): Promise<boolean> {
|
|
89
88
|
return new Promise((resolve, reject) => {
|
|
90
89
|
this.modalStore.setModalContent({
|
|
91
90
|
content: params.message,
|
|
@@ -112,7 +111,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
112
111
|
throw new Error(`Cannot use ${this.setListFilter.name} filter on a list page`)
|
|
113
112
|
} else {
|
|
114
113
|
console.log(this.coreStore.resourceColumnsWithFilters,'core store')
|
|
115
|
-
const filterField = this.coreStore.resourceColumnsWithFilters.find((col:
|
|
114
|
+
const filterField = this.coreStore.resourceColumnsWithFilters.find((col: AdminForthResourceColumnCommon) => col.name === filter.field)
|
|
116
115
|
if(!filterField){
|
|
117
116
|
throw new Error(`Field ${filter.field} is not available for filtering`)
|
|
118
117
|
}
|
|
@@ -123,7 +122,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
123
122
|
|
|
124
123
|
setListFilter(filter: FilterParams): void {
|
|
125
124
|
if(this.listFilterValidation(filter)){
|
|
126
|
-
if(this.filtersStore.filters.some((f) => {return f.field === filter.field && f.operator === filter.operator})){
|
|
125
|
+
if(this.filtersStore.filters.some((f: any) => {return f.field === filter.field && f.operator === filter.operator})){
|
|
127
126
|
throw new Error(`Filter ${filter.field} with operator ${filter.operator} already exists`)
|
|
128
127
|
} else {
|
|
129
128
|
this.filtersStore.setFilter(filter)
|
|
@@ -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,7 +6,7 @@
|
|
|
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"
|
|
@@ -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', type === 'number' ? Number($event.target?.value) : $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
|
},
|
|
@@ -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">
|
|
@@ -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,
|
|
@@ -178,14 +179,14 @@ function inputInput() {
|
|
|
178
179
|
function updateFromProps() {
|
|
179
180
|
if (props.modelValue !== undefined) {
|
|
180
181
|
if (!props.multiple) {
|
|
181
|
-
const el = props.options
|
|
182
|
+
const el = props.options?.find((item: any) => item.value === props.modelValue);
|
|
182
183
|
if (el) {
|
|
183
184
|
selectedItems.value = [el];
|
|
184
185
|
} else {
|
|
185
186
|
selectedItems.value = [];
|
|
186
187
|
}
|
|
187
188
|
} else {
|
|
188
|
-
selectedItems.value = props.options
|
|
189
|
+
selectedItems.value = props.options?.filter((item: any) => props.modelValue?.includes(item.value)) || [];
|
|
189
190
|
}
|
|
190
191
|
}
|
|
191
192
|
}
|
|
@@ -268,7 +269,7 @@ onMounted(() => {
|
|
|
268
269
|
}
|
|
269
270
|
});
|
|
270
271
|
|
|
271
|
-
const filteredItems = computed(() => {
|
|
272
|
+
const filteredItems: Ref<any[]> = computed(() => {
|
|
272
273
|
|
|
273
274
|
if (props.searchDisabled) {
|
|
274
275
|
return props.options || [];
|
|
@@ -295,7 +296,7 @@ const removeClickListener = () => {
|
|
|
295
296
|
document.removeEventListener('click', handleClickOutside);
|
|
296
297
|
};
|
|
297
298
|
|
|
298
|
-
const toogleItem = (item) => {
|
|
299
|
+
const toogleItem = (item: any) => {
|
|
299
300
|
if (selectedItems.value.includes(item)) {
|
|
300
301
|
selectedItems.value = selectedItems.value.filter(i => i.value !== item.value);
|
|
301
302
|
} else {
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
<i18n-t
|
|
45
45
|
keypath="Showing {from} to {to} of {total}" tag="span" class="afcl-table-pagination-text text-sm font-normal text-lightTablePaginationText dark:text-darkTablePaginationText mb-4 md:mb-0 block w-full md:inline md:w-auto"
|
|
46
46
|
>
|
|
47
|
-
<template #from><span class="font-semibold text-lightTablePaginationNumeration dark:text-darkTablePaginationNumeration">{{ Math.min((currentPage - 1) * props.pageSize + 1,
|
|
48
|
-
<template #to><span class="font-semibold text-lightTablePaginationNumeration dark:text-darkTablePaginationNumeration">{{ Math.min(currentPage * props.pageSize,
|
|
49
|
-
<template #total><span class="font-semibold text-lightTablePaginationNumeration dark:text-darkTablePaginationNumeration">{{
|
|
47
|
+
<template #from><span class="font-semibold text-lightTablePaginationNumeration dark:text-darkTablePaginationNumeration">{{ Math.min((currentPage - 1) * props.pageSize + 1, dataResult.total) }}</span></template>
|
|
48
|
+
<template #to><span class="font-semibold text-lightTablePaginationNumeration dark:text-darkTablePaginationNumeration">{{ Math.min(currentPage * props.pageSize, dataResult.total) }}</span></template>
|
|
49
|
+
<template #total><span class="font-semibold text-lightTablePaginationNumeration dark:text-darkTablePaginationNumeration">{{ dataResult.total }}</span></template>
|
|
50
50
|
</i18n-t>
|
|
51
51
|
|
|
52
52
|
<ul class="afcl-table-pagination-list inline-flex -space-x-px rtl:space-x-reverse text-sm h-8">
|
|
@@ -74,6 +74,8 @@
|
|
|
74
74
|
|
|
75
75
|
<script setup lang="ts">
|
|
76
76
|
import { ref, type Ref, computed } from 'vue';
|
|
77
|
+
import { asyncComputed } from '@vueuse/core';
|
|
78
|
+
import { IconArrowRightOutline, IconArrowLeftOutline } from '@iconify-prerendered/vue-flowbite';
|
|
77
79
|
|
|
78
80
|
const props = withDefaults(
|
|
79
81
|
defineProps<{
|
|
@@ -83,7 +85,7 @@
|
|
|
83
85
|
}[],
|
|
84
86
|
data: {
|
|
85
87
|
[key: string]: any,
|
|
86
|
-
}[],
|
|
88
|
+
}[] | ((offset: number, limit: number) => Promise<{data: {[key: string]: any}[], total: number}>),
|
|
87
89
|
evenHighlights?: boolean,
|
|
88
90
|
pageSize?: number,
|
|
89
91
|
}>(), {
|
|
@@ -94,14 +96,21 @@
|
|
|
94
96
|
|
|
95
97
|
const currentPage = ref(1);
|
|
96
98
|
|
|
99
|
+
const dataResult = asyncComputed( async() => {
|
|
100
|
+
if (typeof props.data === 'function') {
|
|
101
|
+
return await props.data(currentPage.value, props.pageSize);
|
|
102
|
+
}
|
|
103
|
+
const start = (currentPage.value - 1) * props.pageSize;
|
|
104
|
+
const end = start + props.pageSize;
|
|
105
|
+
return { data: props.data.slice(start, end), total: props.data.length };
|
|
106
|
+
});
|
|
107
|
+
|
|
97
108
|
const totalPages = computed(() => {
|
|
98
|
-
return Math.ceil(
|
|
109
|
+
return dataResult.value?.total ? Math.ceil(dataResult.value.total / props.pageSize) : 1;
|
|
99
110
|
});
|
|
100
111
|
|
|
101
|
-
const dataPage =
|
|
102
|
-
|
|
103
|
-
const end = start + props.pageSize;
|
|
104
|
-
return props.data.slice(start, end);
|
|
112
|
+
const dataPage = asyncComputed( async() => {
|
|
113
|
+
return dataResult.value.data;
|
|
105
114
|
});
|
|
106
115
|
|
|
107
116
|
function switchPage(p: number) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
value="" class="sr-only peer"
|
|
6
6
|
:disabled="props.disabled"
|
|
7
7
|
:checked="props.modelValue"
|
|
8
|
-
@change="$emit('update:modelValue', $event.target.checked)"
|
|
8
|
+
@change="$emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
|
|
9
9
|
>
|
|
10
10
|
<div class="afcl-toggle border border-lightToggleBorderUnactive relative min-w-11 min-h-6 bg-lightToggleBgUnactive peer-focus:outline-none peer-focus:ring-4
|
|
11
11
|
peer-focus:ring-lightToggleRing dark:peer-focus:ring-darkToggleRing rounded-full peer dark:bg-darkToggleBgUnactive
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
</div>
|
|
48
48
|
<span
|
|
49
49
|
class="bg-red-100 text-red-800 text-xs font-medium me-1 px-1 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
|
|
50
|
-
v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
|
|
51
|
-
{{ sort.findIndex((s) => s.field === c.name) + 1 }}
|
|
50
|
+
v-if="sort.findIndex((s: any) => s.field === c.name) !== -1 && sort?.length > 1">
|
|
51
|
+
{{ sort.findIndex((s: any) => s.field === c.name) + 1 }}
|
|
52
52
|
</span>
|
|
53
53
|
|
|
54
54
|
</div>
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
<!-- table header end -->
|
|
65
65
|
<SkeleteLoader
|
|
66
66
|
v-if="!rows"
|
|
67
|
-
:columns="resource?.columns.filter(c => c.showIn
|
|
67
|
+
:columns="resource?.columns.filter((c: AdminForthResourceColumnInputCommon) => c.showIn?.list).length + 2"
|
|
68
68
|
:rows="rowHeights.length || 3"
|
|
69
69
|
:row-heights="rowHeights"
|
|
70
70
|
:column-widths="columnWidths"
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
<td class="w-4 p-4 cursor-default" @click="(e)=>e.stopPropagation()">
|
|
94
94
|
<Checkbox
|
|
95
95
|
:model-value="checkboxesInternal.includes(row._primaryKeyValue)"
|
|
96
|
-
@change="(e)=>{addToCheckedValues(row._primaryKeyValue)}"
|
|
97
|
-
@click="(e)=>e.stopPropagation()"
|
|
96
|
+
@change="(e: any)=>{addToCheckedValues(row._primaryKeyValue)}"
|
|
97
|
+
@click="(e: any)=>e.stopPropagation()"
|
|
98
98
|
>
|
|
99
99
|
<span class="sr-only">{{ $t('checkbox') }}</span>
|
|
100
100
|
</Checkbox>
|
|
@@ -103,8 +103,8 @@
|
|
|
103
103
|
<td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4">
|
|
104
104
|
<!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
|
|
105
105
|
<component
|
|
106
|
-
:is="c?.components?.list ? getCustomComponent(c.components.list) : ValueRenderer"
|
|
107
|
-
:meta="c?.components?.list
|
|
106
|
+
:is="c?.components?.list ? getCustomComponent(typeof c.components.list === 'string' ? { file: c.components.list } : c.components.list) : ValueRenderer"
|
|
107
|
+
:meta="typeof c?.components?.list === 'object' ? c.components.list.meta : undefined"
|
|
108
108
|
:column="c"
|
|
109
109
|
:record="row"
|
|
110
110
|
:adminUser="coreStore.adminUser"
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
<div class="flex text-lightPrimary dark:text-darkPrimary items-center">
|
|
116
116
|
<Tooltip>
|
|
117
117
|
<RouterLink
|
|
118
|
-
v-if="resource.options?.allowedActions
|
|
118
|
+
v-if="resource.options?.allowedActions?.show"
|
|
119
119
|
:to="{
|
|
120
120
|
name: 'resource-show',
|
|
121
121
|
params: {
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
|
|
136
136
|
<Tooltip>
|
|
137
137
|
<RouterLink
|
|
138
|
-
v-if="resource.options?.allowedActions
|
|
138
|
+
v-if="resource.options?.allowedActions?.edit"
|
|
139
139
|
:to="{
|
|
140
140
|
name: 'resource-edit',
|
|
141
141
|
params: {
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
|
|
154
154
|
<Tooltip>
|
|
155
155
|
<button
|
|
156
|
-
v-if="resource.options?.allowedActions
|
|
156
|
+
v-if="resource.options?.allowedActions?.delete"
|
|
157
157
|
@click="deleteRecord(row)"
|
|
158
158
|
>
|
|
159
159
|
<IconTrashBinSolid class="af-delete-icon w-5 h-5 me-2"/>
|
|
@@ -308,7 +308,7 @@ import {
|
|
|
308
308
|
} from '@iconify-prerendered/vue-flowbite';
|
|
309
309
|
import router from '@/router';
|
|
310
310
|
import { Tooltip } from '@/afcl';
|
|
311
|
-
import type { AdminForthResourceCommon } from '@/types/Common';
|
|
311
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon, AdminForthResourceColumnCommon } from '@/types/Common';
|
|
312
312
|
import adminforth from '@/adminforth';
|
|
313
313
|
import Checkbox from '@/afcl/Checkbox.vue';
|
|
314
314
|
|
|
@@ -339,7 +339,7 @@ const emits = defineEmits([
|
|
|
339
339
|
const checkboxesInternal: Ref<any[]> = ref([]);
|
|
340
340
|
const pageInput = ref('1');
|
|
341
341
|
const page = ref(1);
|
|
342
|
-
const sort = ref([]);
|
|
342
|
+
const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
|
|
343
343
|
|
|
344
344
|
|
|
345
345
|
const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
|
|
@@ -348,11 +348,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
|
|
|
348
348
|
watch(() => page.value, (newPage) => {
|
|
349
349
|
emits('update:page', newPage);
|
|
350
350
|
});
|
|
351
|
-
async function onPageKeydown(event) {
|
|
351
|
+
async function onPageKeydown(event: any) {
|
|
352
352
|
// page input should accept only numbers, arrow keys and backspace
|
|
353
353
|
if (['Enter', 'Space'].includes(event.code) ||
|
|
354
354
|
(!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
|
|
355
|
-
&& isNaN(String.fromCharCode(event.keyCode)))) {
|
|
355
|
+
&& isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
|
|
356
356
|
event.preventDefault();
|
|
357
357
|
if (event.code === 'Enter') {
|
|
358
358
|
validatePageInput();
|
|
@@ -373,7 +373,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
|
|
|
373
373
|
checkboxesInternal.value = newCheckboxes;
|
|
374
374
|
});
|
|
375
375
|
|
|
376
|
-
watch(() => props.sort, (newSort) => {
|
|
376
|
+
watch(() => props.sort, (newSort: any) => {
|
|
377
377
|
sort.value = newSort;
|
|
378
378
|
});
|
|
379
379
|
|
|
@@ -384,17 +384,17 @@ watch(() => props.page, (newPage) => {
|
|
|
384
384
|
page.value = newPage;
|
|
385
385
|
});
|
|
386
386
|
|
|
387
|
-
const rowRefs = useTemplateRef('rowRefs');
|
|
388
|
-
const headerRefs = useTemplateRef('headerRefs');
|
|
389
|
-
const rowHeights = ref([]);
|
|
390
|
-
const columnWidths = ref([]);
|
|
387
|
+
const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
|
|
388
|
+
const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
|
|
389
|
+
const rowHeights = ref<number[]>([]);
|
|
390
|
+
const columnWidths = ref<number[]>([]);
|
|
391
391
|
watch(() => props.rows, (newRows) => {
|
|
392
392
|
// rows are set to null when new records are loading
|
|
393
|
-
rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el) => el.offsetHeight);
|
|
394
|
-
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el) => el.offsetWidth)];
|
|
393
|
+
rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
|
|
394
|
+
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
|
|
395
395
|
});
|
|
396
396
|
|
|
397
|
-
function addToCheckedValues(id) {
|
|
397
|
+
function addToCheckedValues(id: string) {
|
|
398
398
|
if (checkboxesInternal.value.includes(id)) {
|
|
399
399
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
|
|
400
400
|
} else {
|
|
@@ -403,17 +403,17 @@ function addToCheckedValues(id) {
|
|
|
403
403
|
checkboxesInternal.value = [ ...checkboxesInternal.value ]
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn
|
|
406
|
+
const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
|
|
407
407
|
|
|
408
|
-
async function selectAll(
|
|
408
|
+
async function selectAll() {
|
|
409
409
|
if (!allFromThisPageChecked.value) {
|
|
410
|
-
props.rows
|
|
410
|
+
props.rows?.forEach((r) => {
|
|
411
411
|
if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
|
|
412
412
|
checkboxesInternal.value.push(r._primaryKeyValue)
|
|
413
413
|
}
|
|
414
414
|
});
|
|
415
415
|
} else {
|
|
416
|
-
props.rows
|
|
416
|
+
props.rows?.forEach((r) => {
|
|
417
417
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
|
|
418
418
|
});
|
|
419
419
|
}
|
|
@@ -426,15 +426,15 @@ const allFromThisPageChecked = computed(() => {
|
|
|
426
426
|
if (!props.rows || !props.rows.length) return false;
|
|
427
427
|
return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
|
|
428
428
|
});
|
|
429
|
-
const ascArr = computed(() => sort.value.filter((s) => s.direction === 'asc').map((s) => s.field));
|
|
430
|
-
const descArr = computed(() => sort.value.filter((s) => s.direction === 'desc').map((s) => s.field));
|
|
429
|
+
const ascArr = computed(() => sort.value.filter((s:any) => s.direction === 'asc').map((s: any) => s.field));
|
|
430
|
+
const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
|
|
431
431
|
|
|
432
432
|
|
|
433
|
-
function onSortButtonClick(event, field) {
|
|
433
|
+
function onSortButtonClick(event: any, field: string) {
|
|
434
434
|
// if ctrl key is pressed, add to sort otherwise sort by this field
|
|
435
435
|
// in any case if field is already in sort, toggle direction
|
|
436
436
|
|
|
437
|
-
const sortIndex = sort.value.findIndex((s) => s.field === field);
|
|
437
|
+
const sortIndex = sort.value.findIndex((s: any) => s.field === field);
|
|
438
438
|
if (sortIndex === -1) {
|
|
439
439
|
// field is not in sort, add it
|
|
440
440
|
if (event.ctrlKey) {
|
|
@@ -445,9 +445,9 @@ function onSortButtonClick(event, field) {
|
|
|
445
445
|
} else {
|
|
446
446
|
const sortField = sort.value[sortIndex];
|
|
447
447
|
if (sortField.direction === 'asc') {
|
|
448
|
-
sort.value = sort.value.map((s) => s.field === field ? {field, direction: 'desc'} : s);
|
|
448
|
+
sort.value = sort.value.map((s: any) => s.field === field ? {field, direction: 'desc'} : s);
|
|
449
449
|
} else {
|
|
450
|
-
sort.value = sort.value.filter((s) => s.field !== field);
|
|
450
|
+
sort.value = sort.value.filter((s: any) => s.field !== field);
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
}
|
|
@@ -455,11 +455,11 @@ function onSortButtonClick(event, field) {
|
|
|
455
455
|
|
|
456
456
|
const clickTarget = ref(null);
|
|
457
457
|
|
|
458
|
-
async function onClick(e,row) {
|
|
458
|
+
async function onClick(e: any, row: any) {
|
|
459
459
|
if(clickTarget.value === e.target) return;
|
|
460
460
|
clickTarget.value = e.target;
|
|
461
461
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
462
|
-
if (window.getSelection()
|
|
462
|
+
if (window.getSelection()?.toString()) return;
|
|
463
463
|
else {
|
|
464
464
|
if (row._clickUrl === null) {
|
|
465
465
|
// user asked to nothing on click
|
|
@@ -474,7 +474,7 @@ async function onClick(e,row) {
|
|
|
474
474
|
router.resolve({
|
|
475
475
|
name: 'resource-show',
|
|
476
476
|
params: {
|
|
477
|
-
resourceId: props.resource
|
|
477
|
+
resourceId: props.resource?.resourceId,
|
|
478
478
|
primaryKey: row._primaryKeyValue,
|
|
479
479
|
},
|
|
480
480
|
}).href,
|
|
@@ -492,7 +492,7 @@ async function onClick(e,row) {
|
|
|
492
492
|
router.push({
|
|
493
493
|
name: 'resource-show',
|
|
494
494
|
params: {
|
|
495
|
-
resourceId: props.resource
|
|
495
|
+
resourceId: props.resource?.resourceId,
|
|
496
496
|
primaryKey: row._primaryKeyValue,
|
|
497
497
|
},
|
|
498
498
|
});
|
|
@@ -501,7 +501,7 @@ async function onClick(e,row) {
|
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
-
async function deleteRecord(row) {
|
|
504
|
+
async function deleteRecord(row: any) {
|
|
505
505
|
const data = await adminforth.confirm({
|
|
506
506
|
message: t('Are you sure you want to delete this item?'),
|
|
507
507
|
yes: t('Delete'),
|
|
@@ -513,7 +513,7 @@ async function deleteRecord(row) {
|
|
|
513
513
|
path: '/delete_record',
|
|
514
514
|
method: 'POST',
|
|
515
515
|
body: {
|
|
516
|
-
resourceId: props.resource
|
|
516
|
+
resourceId: props.resource?.resourceId,
|
|
517
517
|
primaryKey: row._primaryKeyValue,
|
|
518
518
|
}
|
|
519
519
|
});
|
|
@@ -531,16 +531,16 @@ async function deleteRecord(row) {
|
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
533
|
|
|
534
|
-
const actionLoadingStates = ref({});
|
|
534
|
+
const actionLoadingStates = ref<Record<string | number, boolean>>({});
|
|
535
535
|
|
|
536
|
-
async function startCustomAction(actionId, row) {
|
|
536
|
+
async function startCustomAction(actionId: string, row: any) {
|
|
537
537
|
actionLoadingStates.value[actionId] = true;
|
|
538
538
|
|
|
539
539
|
const data = await callAdminForthApi({
|
|
540
540
|
path: '/start_custom_action',
|
|
541
541
|
method: 'POST',
|
|
542
542
|
body: {
|
|
543
|
-
resourceId: props.resource
|
|
543
|
+
resourceId: props.resource?.resourceId,
|
|
544
544
|
actionId: actionId,
|
|
545
545
|
recordId: row._primaryKeyValue
|
|
546
546
|
}
|
|
@@ -578,7 +578,7 @@ async function startCustomAction(actionId, row) {
|
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
-
function onPageInput(event) {
|
|
581
|
+
function onPageInput(event: any) {
|
|
582
582
|
pageInput.value = event.target.innerText;
|
|
583
583
|
}
|
|
584
584
|
|