adminforth 1.5.11-next.1 → 1.5.11
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.
|
@@ -116,8 +116,12 @@ const options = computed(() => {
|
|
|
116
116
|
|
|
117
117
|
// for each of ...props.options merge on lowest level. so if { chart: {height : 2} }, it should not replace chart level, only height level
|
|
118
118
|
function mergeOptions(options: any, newOptions: any) {
|
|
119
|
+
if (!newOptions) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
119
122
|
for (const key in newOptions) {
|
|
120
|
-
|
|
123
|
+
// and is not array
|
|
124
|
+
if (typeof newOptions[key] === 'object' && !Array.isArray(newOptions[key])) {
|
|
121
125
|
if (!options[key]) {
|
|
122
126
|
options[key] = {};
|
|
123
127
|
}
|
|
@@ -128,8 +128,12 @@ const options = computed(() => {
|
|
|
128
128
|
|
|
129
129
|
// for each of ...props.options merge on lowest level. so if { chart: {height : 2} }, it should not replace chart level, only height level
|
|
130
130
|
function mergeOptions(options: any, newOptions: any) {
|
|
131
|
+
if (!newOptions) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
131
134
|
for (const key in newOptions) {
|
|
132
|
-
|
|
135
|
+
// and is not array
|
|
136
|
+
if (typeof newOptions[key] === 'object' && !Array.isArray(newOptions[key])) {
|
|
133
137
|
if (!options[key]) {
|
|
134
138
|
options[key] = {};
|
|
135
139
|
}
|
|
@@ -42,7 +42,7 @@ const optionsBase = {
|
|
|
42
42
|
chart: {
|
|
43
43
|
height: 400,
|
|
44
44
|
width: "100%",
|
|
45
|
-
type: "
|
|
45
|
+
type: "pie",
|
|
46
46
|
},
|
|
47
47
|
stroke: {
|
|
48
48
|
colors: ["transparent"],
|
|
@@ -137,7 +137,8 @@ const options = computed(() => {
|
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
139
|
for (const key in newOptions) {
|
|
140
|
-
|
|
140
|
+
// and is not array
|
|
141
|
+
if (typeof newOptions[key] === 'object' && !Array.isArray(newOptions[key])) {
|
|
141
142
|
if (!options[key]) {
|
|
142
143
|
options[key] = {};
|
|
143
144
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative inline-block afcl-select">
|
|
2
|
+
<div class="relative inline-block afcl-select" ref="internalSelect">
|
|
3
3
|
<div class="relative">
|
|
4
4
|
<input
|
|
5
5
|
ref="inputEl"
|
|
@@ -89,7 +89,6 @@ import { ref, computed, onMounted, onUnmounted, watch, type Ref } from 'vue';
|
|
|
89
89
|
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
|
|
90
90
|
import { useElementSize } from '@vueuse/core'
|
|
91
91
|
|
|
92
|
-
|
|
93
92
|
const props = defineProps({
|
|
94
93
|
options: Array,
|
|
95
94
|
modelValue: {
|
|
@@ -123,6 +122,7 @@ const dropdownStyle = ref<{ top?: string; }>({
|
|
|
123
122
|
});
|
|
124
123
|
|
|
125
124
|
const selectedItems: Ref<any[]> = ref([]);
|
|
125
|
+
const internalSelect = ref<HTMLElement | null>(null);
|
|
126
126
|
|
|
127
127
|
function inputInput() {
|
|
128
128
|
if (!props.multiple && selectedItems.value.length) {
|
|
@@ -147,15 +147,12 @@ function updateFromProps() {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
function inputClick() {
|
|
150
|
-
if (props.isReadonly)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (!search.value) {
|
|
157
|
-
showDropdown.value = false;
|
|
158
|
-
}
|
|
150
|
+
if (props.isReadonly) return;
|
|
151
|
+
// Toggle local dropdown
|
|
152
|
+
showDropdown.value = !showDropdown.value;
|
|
153
|
+
// If the dropdown is about to close, reset the search
|
|
154
|
+
if (!showDropdown.value && !search.value) {
|
|
155
|
+
search.value = '';
|
|
159
156
|
}
|
|
160
157
|
}
|
|
161
158
|
|
|
@@ -198,10 +195,12 @@ const filteredItems = computed(() => {
|
|
|
198
195
|
);
|
|
199
196
|
});
|
|
200
197
|
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
|
|
199
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
200
|
+
const targetEl = event.target as HTMLElement | null;
|
|
201
|
+
const closestSelect = targetEl?.closest('.afcl-select');
|
|
202
|
+
if (closestSelect !== internalSelect.value)
|
|
203
203
|
showDropdown.value = false;
|
|
204
|
-
}
|
|
205
204
|
};
|
|
206
205
|
|
|
207
206
|
const addClickListener = () => {
|
|
@@ -228,7 +227,7 @@ const toogleItem = (item) => {
|
|
|
228
227
|
if (!props.multiple && search.value) {
|
|
229
228
|
search.value = '';
|
|
230
229
|
}
|
|
231
|
-
|
|
230
|
+
|
|
232
231
|
const list = selectedItems.value.map(item => item.value);
|
|
233
232
|
const updValue = list.length ? list : null;
|
|
234
233
|
let emitValue;
|
|
@@ -238,13 +237,10 @@ const toogleItem = (item) => {
|
|
|
238
237
|
emitValue = updValue;
|
|
239
238
|
}
|
|
240
239
|
emit('update:modelValue', emitValue);
|
|
241
|
-
|
|
242
240
|
};
|
|
243
241
|
|
|
244
|
-
|
|
245
242
|
onUnmounted(() => {
|
|
246
243
|
removeClickListener();
|
|
247
244
|
});
|
|
248
245
|
|
|
249
|
-
|
|
250
246
|
</script>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
|
|
3
|
+
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
|
|
4
|
+
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
5
|
+
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
6
|
+
<tr>
|
|
7
|
+
<th scope="col" class="px-6 py-3"
|
|
8
|
+
v-for="column in columns"
|
|
9
|
+
>
|
|
10
|
+
<slot v-if="$slots[`header:${column.fieldName}`]" :name="`header:${column.fieldName}`" :column="column" />
|
|
11
|
+
|
|
12
|
+
<span v-else>
|
|
13
|
+
{{ column.label }}
|
|
14
|
+
</span>
|
|
15
|
+
</th>
|
|
16
|
+
</tr>
|
|
17
|
+
</thead>
|
|
18
|
+
<tbody>
|
|
19
|
+
<tr
|
|
20
|
+
v-for="(item, index) in dataPage"
|
|
21
|
+
:class="{
|
|
22
|
+
'odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800': evenHighlights,
|
|
23
|
+
'border-b dark:border-gray-700': index !== dataPage.length - 1 || totalPages > 1,
|
|
24
|
+
}"
|
|
25
|
+
>
|
|
26
|
+
<td class="px-6 py-4"
|
|
27
|
+
v-for="column in props.columns"
|
|
28
|
+
>
|
|
29
|
+
<slot v-if="$slots[`cell:${column.fieldName}`]"
|
|
30
|
+
:name="`cell:${column.fieldName}`"
|
|
31
|
+
:item="item" :column="column"
|
|
32
|
+
>
|
|
33
|
+
</slot>
|
|
34
|
+
<span v-else>
|
|
35
|
+
{{ item[column.fieldName] }}
|
|
36
|
+
</span>
|
|
37
|
+
</td>
|
|
38
|
+
</tr>
|
|
39
|
+
</tbody>
|
|
40
|
+
</table>
|
|
41
|
+
<nav class="flex items-center flex-column flex-wrap md:flex-row justify-between p-4"
|
|
42
|
+
v-if="totalPages > 1"
|
|
43
|
+
:aria-label="$t('Table navigation')">
|
|
44
|
+
<i18n-t
|
|
45
|
+
keypath="Showing {from} to {to} of {total}" tag="span" class="text-sm font-normal text-gray-500 dark:text-gray-400 mb-4 md:mb-0 block w-full md:inline md:w-auto"
|
|
46
|
+
>
|
|
47
|
+
<template #from><span class="font-semibold text-gray-900 dark:text-white">{{ Math.min((currentPage - 1) * props.pageSize + 1, props.data.length) }}</span></template>
|
|
48
|
+
<template #to><span class="font-semibold text-gray-900 dark:text-white">{{ Math.min(currentPage * props.pageSize, props.data.length) }}</span></template>
|
|
49
|
+
<template #total><span class="font-semibold text-gray-900 dark:text-white">{{ props.data.length }}</span></template>
|
|
50
|
+
</i18n-t>
|
|
51
|
+
|
|
52
|
+
<ul class="inline-flex -space-x-px rtl:space-x-reverse text-sm h-8">
|
|
53
|
+
<li v-for="page in totalPages" :key="page">
|
|
54
|
+
<a href="#"
|
|
55
|
+
@click.prevent="switchPage(page)"
|
|
56
|
+
:aria-current="page === page ? 'page' : undefined"
|
|
57
|
+
:class='{
|
|
58
|
+
"text-blue-600 bg-lightPrimary text-lightPrimaryContrast dark:bg-darkPrimary dark:text-darkPrimaryContrast hover:opacity-90": page === currentPage,
|
|
59
|
+
"text-gray-500 border bg-white border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white": page !== currentPage,
|
|
60
|
+
"rounded-s-lg ms-0": page === 1,
|
|
61
|
+
"rounded-e-lg": page === totalPages,
|
|
62
|
+
}'
|
|
63
|
+
class="flex items-center justify-center px-3 h-8 leading-tight ">
|
|
64
|
+
{{ page }}
|
|
65
|
+
</a>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</nav>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<script setup lang="ts">
|
|
76
|
+
import { ref, type Ref, computed } from 'vue';
|
|
77
|
+
|
|
78
|
+
const props = withDefaults(
|
|
79
|
+
defineProps<{
|
|
80
|
+
columns: {
|
|
81
|
+
label: string,
|
|
82
|
+
fieldName: string,
|
|
83
|
+
}[],
|
|
84
|
+
data: {
|
|
85
|
+
[key: string]: any,
|
|
86
|
+
}[],
|
|
87
|
+
evenHighlights?: boolean,
|
|
88
|
+
pageSize?: number,
|
|
89
|
+
}>(), {
|
|
90
|
+
evenHighlights: true,
|
|
91
|
+
pageSize: 10,
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const currentPage = ref(1);
|
|
96
|
+
|
|
97
|
+
const totalPages = computed(() => {
|
|
98
|
+
return Math.ceil(props.data.length / props.pageSize);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const dataPage = computed(() => {
|
|
102
|
+
const start = (currentPage.value - 1) * props.pageSize;
|
|
103
|
+
const end = start + props.pageSize;
|
|
104
|
+
return props.data.slice(start, end);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
function switchPage(p: number) {
|
|
108
|
+
currentPage.value = p;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const emites = defineEmits([
|
|
112
|
+
'update:activeTab',
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
</script>
|
|
@@ -12,4 +12,7 @@ export { default as Dropzone } from './Dropzone.vue';
|
|
|
12
12
|
export { default as AreaChart } from './AreaChart.vue';
|
|
13
13
|
export { default as BarChart } from './BarChart.vue';
|
|
14
14
|
export { default as PieChart } from './PieChart.vue';
|
|
15
|
+
export { default as Table } from './Table.vue';
|
|
16
|
+
|
|
17
|
+
|
|
15
18
|
|