adminforth 2.4.0-next.130 → 2.4.0-next.132

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,10 +116,56 @@
116
116
  </template>
117
117
 
118
118
  <script setup lang="ts">
119
- import { ref, type Ref, computed, useTemplateRef, watch, nextTick } from 'vue';
120
- import { asyncComputed } from '@vueuse/core';
119
+ import { ref, computed, useTemplateRef, watch, shallowRef, toRef } from 'vue';
121
120
  import SkeleteLoader from '@/components/SkeleteLoader.vue';
122
121
 
122
+ type Row = Record<string, unknown>
123
+ type LoadFn = (page: number, pageSize: number) => Promise<{ data: Row[]; total: number }>
124
+
125
+ const isFunc = (v: unknown): v is LoadFn => typeof v === 'function'
126
+
127
+ function usePagedData(props: {
128
+ data: Row[] | LoadFn
129
+ pageSize: number
130
+ currentPage: number
131
+ }) {
132
+ const page = ref(props.currentPage)
133
+ const pageSize = toRef(props, 'pageSize')
134
+
135
+ const isLoading = ref(false)
136
+ const error = shallowRef<unknown>(null)
137
+ const result = shallowRef<{ data: Row[]; total: number }>({ data: [], total: 0 })
138
+
139
+ let requestId = 0
140
+
141
+ async function fetchData() {
142
+ const id = ++requestId
143
+ isLoading.value = true
144
+ error.value = null
145
+ try {
146
+ if (isFunc(props.data)) {
147
+ const res = await props.data(page.value, pageSize.value)
148
+ if (id !== requestId) return
149
+ result.value = res
150
+ } else {
151
+ const start = (page.value - 1) * pageSize.value
152
+ const end = start + pageSize.value
153
+ result.value = { data: props.data.slice(start, end), total: props.data.length }
154
+ }
155
+ } catch (e) {
156
+ if (id !== requestId) return
157
+ error.value = e
158
+ result.value = { data: [], total: 0 }
159
+ } finally {
160
+ if (id === requestId) isLoading.value = false
161
+ }
162
+ }
163
+
164
+ watch([page, pageSize, () => props.data], fetchData, { immediate: true })
165
+
166
+ return { page, pageSize, isLoading, error, result, refresh: fetchData }
167
+ }
168
+
123
169
  const props = withDefaults(
124
170
  defineProps<{
125
171
  columns: {
@@ -137,43 +183,34 @@
137
183
  }
138
184
  );
139
185
 
140
- const currentPage = ref(1);
141
- const isLoading = ref(false);
186
+ const { result: dataResult, isLoading, error, page: currentPage, pageSize, refresh } = usePagedData({
187
+ data: props.data,
188
+ pageSize: props.pageSize,
189
+ currentPage: 1
190
+ });
191
+
142
192
  const pageInput = ref('1');
143
193
  const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
144
194
  const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
145
195
  const rowHeights = ref<number[]>([]);
146
196
  const columnWidths = ref<number[]>([]);
147
197
 
148
- const dataResult = asyncComputed( async() => {
149
- if (typeof props.data === 'function') {
150
- isLoading.value = true;
151
- const result = await props.data(currentPage.value, props.pageSize);
152
- isLoading.value = false;
153
- return result;
154
- }
155
- const start = (currentPage.value - 1) * props.pageSize;
156
- const end = start + props.pageSize;
157
- return { data: props.data.slice(start, end), total: props.data.length };
158
- });
159
-
160
198
  watch(() => currentPage.value, () => {
161
- // rows are set to null when new records are loading
162
199
  rowHeights.value = !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
163
200
  columnWidths.value = !headerRefs.value ? [] : headerRefs.value.map((el: HTMLElement) => el.offsetWidth);
164
201
  });
165
202
 
166
-
167
203
  const totalPages = computed(() => {
168
204
  return dataResult.value?.total ? Math.ceil(dataResult.value.total / props.pageSize) : 1;
169
205
  });
170
206
 
171
- const dataPage = asyncComputed( async() => {
207
+ const dataPage = computed(() => {
172
208
  return dataResult.value.data;
173
209
  });
174
210
 
175
211
  function switchPage(p: number) {
176
212
  currentPage.value = p;
213
+ pageInput.value = p.toString();
177
214
  }
178
215
 
179
216
  const emites = defineEmits([
@@ -191,6 +228,10 @@
191
228
  pageInput.value = validPage.toString();
192
229
  }
193
230
 
231
+ watch(() => currentPage.value, (newPage) => {
232
+ pageInput.value = newPage.toString();
233
+ });
234
+
194
235
  async function onPageKeydown(event: any) {
195
236
  // page input should accept only numbers, arrow keys and backspace
196
237
  if (['Enter', 'Space'].includes(event.code) ||
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <div v-if="error" class="af-login-modal-error flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
3
+ <svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
4
+ <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
5
+ </svg>
6
+ <span class="sr-only">{{ $t('Info') }}</span>
7
+ <div>
8
+ {{ error }}
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script setup>
14
+
15
+ defineProps({
16
+ error: {
17
+ type: String,
18
+ default: null
19
+ }
20
+ });
21
+ </script>
@@ -92,15 +92,7 @@
92
92
  :meta="c.meta"
93
93
  />
94
94
 
95
- <div v-if="error" class="af-login-modal-error flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
96
- <svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
97
- <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
98
- </svg>
99
- <span class="sr-only">{{ $t('Info') }}</span>
100
- <div>
101
- {{ error }}
102
- </div>
103
- </div>
95
+ <ErrorMessage :error="error" />
104
96
 
105
97
  <div v-if="loginPromptHTML"
106
98
  class="flex items-center p-4 mb-4 text-sm text-lightLoginViewPromptText rounded-lg bg-lightLoginViewPromptBackground dark:bg-darkLoginViewPromptBackground dark:text-darkLoginViewPromptText" role="alert"
@@ -136,6 +128,7 @@ import { callAdminForthApi, loadFile } from '@/utils';
136
128
  import { useRoute, useRouter } from 'vue-router';
137
129
  import { Button, Checkbox, Input } from '@/afcl';
138
130
  import { useI18n } from 'vue-i18n';
131
+ import ErrorMessage from '@/components/ErrorMessage.vue';
139
132
 
140
133
  const { t } = useI18n();
141
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "2.4.0-next.130",
3
+ "version": "2.4.0-next.132",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",