@polymarbot/nuxt-layer-shadcn-ui 0.7.6 → 0.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.
@@ -1,3 +1,4 @@
1
1
  {
2
- "actions": "Actions"
2
+ "actions": "Actions",
3
+ "loadFailed": "Failed to load"
3
4
  }
@@ -197,3 +197,45 @@ export const WithCustomToolbar: Story = {
197
197
  `,
198
198
  }),
199
199
  }
200
+
201
+ /** First page loads, but switching to any later page rejects — to inspect how the table behaves when only a follow-up fetch fails. */
202
+ export const Errored: Story = {
203
+ parameters: {
204
+ ...noControls,
205
+ docs: {
206
+ source: {
207
+ code: `
208
+ <template>
209
+ <AsyncDataTable
210
+ :columns="columns"
211
+ :fetchMethod="failingAfterFirst"
212
+ />
213
+ </template>
214
+
215
+ <script setup>
216
+ function failingAfterFirst (params) {
217
+ if (params.offset === 0) return mockFetch(params)
218
+ return Promise.reject(new Error('mocked failure'))
219
+ }
220
+ </script>
221
+ `.trim(),
222
+ },
223
+ },
224
+ },
225
+ render: () => ({
226
+ components: { AsyncDataTable },
227
+ setup () {
228
+ const failingAfterFirst = (params: AsyncDataTableFetchParams): Promise<AsyncDataTableFetchResult<User>> => {
229
+ if (params.offset === 0) return mockFetch(params)
230
+ return new Promise((_, reject) => setTimeout(() => reject(new Error('mocked failure')), 500))
231
+ }
232
+ return { columns, failingAfterFirst }
233
+ },
234
+ template: `
235
+ <AsyncDataTable
236
+ :columns="columns"
237
+ :fetchMethod="failingAfterFirst"
238
+ />
239
+ `,
240
+ }),
241
+ }
@@ -32,6 +32,7 @@ const emit = defineEmits<{
32
32
  'rowClick': [row: TData, index: number, event: MouseEvent]
33
33
  }>()
34
34
 
35
+ const { t } = useI18n()
35
36
  const T = useTranslations('components.ui.AsyncDataTable')
36
37
  const { isMobile } = useDevice()
37
38
 
@@ -47,6 +48,7 @@ function onSelectionChange (value: TData | TData[] | null) {
47
48
 
48
49
  const loading = ref(false)
49
50
  const internalData = ref<TData[]>([]) as Ref<TData[]>
51
+ const errored = ref(false)
50
52
  const requestVersion = ref(0)
51
53
 
52
54
  const pagination = ref<AsyncDataTablePagination>({
@@ -105,6 +107,7 @@ async function fetchData (page?: number, forceRefresh = false) {
105
107
  pagination.value.page = page
106
108
  }
107
109
 
110
+ errored.value = false
108
111
  loading.value = true
109
112
  try {
110
113
  const result = await props.fetchMethod(buildFetchParams())
@@ -115,6 +118,10 @@ async function fetchData (page?: number, forceRefresh = false) {
115
118
  } catch (error) {
116
119
  if (currentVersion !== requestVersion.value) return
117
120
  console.error('AsyncDataTable fetchData failed:', error)
121
+ // Surface the failure via the empty-state slot rather than leaving the
122
+ // previous page's rows on screen while the pagination already moved on.
123
+ errored.value = true
124
+ internalData.value = []
118
125
  } finally {
119
126
  if (currentVersion === requestVersion.value) {
120
127
  loading.value = false
@@ -267,7 +274,7 @@ onMounted(() => {
267
274
  @rowClick="(row: TData, index: number, event: MouseEvent) => emit('rowClick', row, index, event)"
268
275
  >
269
276
  <template
270
- v-for="name in Object.keys($slots).filter(n => n !== 'toolbar')"
277
+ v-for="name in Object.keys($slots).filter(n => n !== 'toolbar' && n !== 'empty')"
271
278
  :key="name"
272
279
  #[name]="slotData"
273
280
  >
@@ -276,6 +283,34 @@ onMounted(() => {
276
283
  v-bind="slotData ?? {}"
277
284
  />
278
285
  </template>
286
+
287
+ <template
288
+ v-if="errored"
289
+ #empty
290
+ >
291
+ <div class="gap-2 text-muted-foreground flex flex-col items-center">
292
+ <Icon
293
+ name="circle-alert"
294
+ class="size-8"
295
+ />
296
+ <span class="text-sm">
297
+ {{ T('loadFailed') }}
298
+ </span>
299
+ <Button
300
+ variant="outline"
301
+ size="sm"
302
+ @click="() => fetchData(undefined, true)"
303
+ >
304
+ {{ t('common.actions.retry') }}
305
+ </Button>
306
+ </div>
307
+ </template>
308
+ <template
309
+ v-else-if="$slots.empty"
310
+ #empty
311
+ >
312
+ <slot name="empty" />
313
+ </template>
279
314
  </DataTable>
280
315
 
281
316
  <!-- Bottom toolbar -->
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "allLoaded": "— all loaded —",
3
3
  "count": "{loaded} of {total} loaded",
4
- "refresh": "Refresh",
4
+ "loadFailed": "Failed to load",
5
5
  "scrollToTop": "Scroll to top"
6
6
  }
@@ -163,3 +163,47 @@ export const PageScroll: Story = {
163
163
  parameters: noControls,
164
164
  args: { height: undefined },
165
165
  }
166
+
167
+ /** First page loads, but every subsequent `loadMore` call rejects — the bottom loader is replaced by a retry prompt. */
168
+ export const Errored: Story = {
169
+ parameters: {
170
+ ...noControls,
171
+ docs: {
172
+ source: {
173
+ code: `
174
+ <template>
175
+ <InfiniteDataTable
176
+ :columns="columns"
177
+ :fetchMethod="failingAfterFirst"
178
+ height="360px"
179
+ />
180
+ </template>
181
+
182
+ <script setup>
183
+ function failingAfterFirst (params) {
184
+ if (!params.cursor) return mockFetch(params)
185
+ return Promise.reject(new Error('mocked failure'))
186
+ }
187
+ </script>
188
+ `.trim(),
189
+ },
190
+ },
191
+ },
192
+ render: () => ({
193
+ components: { InfiniteDataTable: InfiniteDataTable as any },
194
+ setup () {
195
+ const failingAfterFirst = (params: InfiniteDataTableFetchParams): Promise<InfiniteDataTableFetchResult<User>> => {
196
+ if (!params.cursor) return mockFetch(params)
197
+ return new Promise((_, reject) => setTimeout(() => reject(new Error('mocked failure')), 400))
198
+ }
199
+ return { columns, failingAfterFirst }
200
+ },
201
+ template: `
202
+ <InfiniteDataTable
203
+ :columns="columns"
204
+ :fetchMethod="failingAfterFirst"
205
+ height="360px"
206
+ />
207
+ `,
208
+ }),
209
+ }
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts" generic="TData extends Record<string, any>">
2
2
  import type { InfiniteDataTableFetchParams, InfiniteDataTableProps } from './types'
3
+ import { useEventListener } from '@vueuse/core'
3
4
 
4
5
  const props = withDefaults(defineProps<InfiniteDataTableProps<TData>>(), {
5
6
  columns: () => [],
@@ -16,6 +17,7 @@ const emit = defineEmits<{
16
17
  'rowClick': [row: TData, index: number, event: MouseEvent]
17
18
  }>()
18
19
 
20
+ const { t } = useI18n()
19
21
  const T = useTranslations('components.ui.InfiniteDataTable')
20
22
 
21
23
  // -- Internal state --
@@ -24,6 +26,7 @@ const loading = ref(false)
24
26
  const internalData = ref<TData[]>([]) as Ref<TData[]>
25
27
  const cursor = ref<string | undefined>(undefined)
26
28
  const hasMore = ref(true)
29
+ const errored = ref(false)
27
30
  const total = ref<number | undefined>(undefined)
28
31
  const requestVersion = ref(0)
29
32
 
@@ -37,12 +40,28 @@ const isInitialLoad = computed(() => loading.value && internalData.value.length
37
40
  // -- IntersectionObserver root: only when internal scroll is active --
38
41
 
39
42
  const dataTableRef = ref<{ scrollEl?: HTMLElement } | null>(null)
43
+ const scrollEl = computed<HTMLElement | undefined>(() => dataTableRef.value?.scrollEl)
40
44
  const intersectionOptions = computed<IntersectionObserverInit | undefined>(() => { // eslint-disable-line no-undef
41
45
  if (!props.height) return undefined
42
- const root = dataTableRef.value?.scrollEl
46
+ const root = scrollEl.value
43
47
  return root ? { root } : undefined
44
48
  })
45
49
 
50
+ // -- Scroll-to-top availability --
51
+
52
+ const { isOverflowing, atStart } = useScrollState(scrollEl)
53
+ const isWindowAboveTable = ref(false)
54
+
55
+ function updateWindowPosition () {
56
+ const el = scrollEl.value
57
+ isWindowAboveTable.value = !!el && el.getBoundingClientRect().top < 0
58
+ }
59
+
60
+ useEventListener(window, 'scroll', updateWindowPosition, { passive: true })
61
+ onMounted(() => nextTick(updateWindowPosition))
62
+
63
+ const isAtTop = computed(() => isOverflowing.value ? atStart.value : !isWindowAboveTable.value)
64
+
46
65
  // -- Helpers --
47
66
 
48
67
  function getFilters (): Record<string, any> {
@@ -65,7 +84,11 @@ function resetState () {
65
84
  internalData.value = []
66
85
  cursor.value = undefined
67
86
  hasMore.value = true
87
+ errored.value = false
68
88
  total.value = undefined
89
+ // Release the loading guard so a fresh loadMore can start even when one is
90
+ // in flight; the in-flight request bails out via the requestVersion check.
91
+ loading.value = false
69
92
  }
70
93
 
71
94
  // -- Loading --
@@ -74,6 +97,10 @@ async function loadMore () {
74
97
  if (!props.fetchMethod) return
75
98
  if (loading.value || !hasMore.value) return
76
99
 
100
+ // Calling loadMore is the retry path; the IntersectionChecker is hidden
101
+ // while errored, so it can't trigger this branch on its own.
102
+ errored.value = false
103
+
77
104
  const currentVersion = ++requestVersion.value
78
105
  loading.value = true
79
106
  try {
@@ -87,6 +114,7 @@ async function loadMore () {
87
114
  } catch (error) {
88
115
  if (currentVersion !== requestVersion.value) return
89
116
  console.error('InfiniteDataTable loadMore failed:', error)
117
+ errored.value = true
90
118
  } finally {
91
119
  if (currentVersion === requestVersion.value) loading.value = false
92
120
  }
@@ -99,9 +127,9 @@ async function refresh () {
99
127
  }
100
128
 
101
129
  function scrollToTop () {
102
- const el = dataTableRef.value?.scrollEl
130
+ const el = scrollEl.value
103
131
  if (!el) return
104
- if (el.scrollHeight > el.clientHeight) {
132
+ if (isOverflowing.value) {
105
133
  el.scrollTo({ top: 0, behavior: 'smooth' })
106
134
  } else {
107
135
  el.scrollIntoView({ behavior: 'smooth', block: 'start' })
@@ -186,20 +214,35 @@ onMounted(() => {
186
214
  >
187
215
  <div
188
216
  v-if="!hasMore"
189
- class="py-2 text-xs text-muted-foreground text-center"
217
+ class="text-sm text-muted-foreground text-center"
190
218
  >
191
219
  {{ T('allLoaded') }}
192
220
  </div>
221
+ <div
222
+ v-else-if="errored"
223
+ class="gap-2 text-sm flex items-center justify-center"
224
+ >
225
+ <span class="text-muted-foreground">
226
+ {{ T('loadFailed') }}
227
+ </span>
228
+ <Button
229
+ variant="ghost"
230
+ size="sm"
231
+ @click="loadMore"
232
+ >
233
+ {{ t('common.actions.retry') }}
234
+ </Button>
235
+ </div>
193
236
  <EffectIntersectionChecker
194
237
  v-else-if="!isInitialLoad"
195
238
  :disabled="loading"
196
239
  :options="intersectionOptions"
197
- class="py-2 flex items-center justify-center"
240
+ class="flex items-center justify-center"
198
241
  @show="loadMore"
199
242
  >
200
243
  <Icon
201
244
  name="loader-circle"
202
- class="size-4 animate-spin text-muted-foreground"
245
+ class="size-6 animate-spin text-muted-foreground"
203
246
  />
204
247
  </EffectIntersectionChecker>
205
248
  </template>
@@ -221,11 +264,11 @@ onMounted(() => {
221
264
  variant="ghost"
222
265
  size="icon-sm"
223
266
  icon="arrow-up-to-line"
224
- :disabled="loading || internalData.length === 0"
267
+ :disabled="loading || internalData.length === 0 || isAtTop"
225
268
  @click="scrollToTop"
226
269
  />
227
270
  </Tooltip>
228
- <Tooltip :text="T('refresh')">
271
+ <Tooltip :text="t('common.actions.refresh')">
229
272
  <Button
230
273
  variant="ghost"
231
274
  size="icon-sm"
package/app/en.json CHANGED
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Cancel",
5
5
  "close": "Close",
6
- "confirm": "Confirm"
6
+ "confirm": "Confirm",
7
+ "refresh": "Refresh",
8
+ "retry": "Retry"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "إلغاء",
5
5
  "close": "إغلاق",
6
- "confirm": "تأكيد"
6
+ "confirm": "تأكيد",
7
+ "refresh": "تحديث",
8
+ "retry": "إعادة محاولة"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "الإجراءات"
31
+ "actions": "الإجراءات",
32
+ "loadFailed": "فشل التحميل"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "تم النسخ",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— تم تحميل الكل —",
56
59
  "count": "{loaded} من {total} تم تحميله",
57
- "refresh": "تحديث",
60
+ "loadFailed": "فشل التحميل",
58
61
  "scrollToTop": "الرجوع إلى الأعلى"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Abbrechen",
5
5
  "close": "Schließen",
6
- "confirm": "Bestätigen"
6
+ "confirm": "Bestätigen",
7
+ "refresh": "Aktualisieren",
8
+ "retry": "Erneut versuchen"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Aktionen"
31
+ "actions": "Aktionen",
32
+ "loadFailed": "Fehler beim Laden"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Kopiert",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— alle geladen —",
56
59
  "count": "{loaded} von {total} geladen",
57
- "refresh": "Aktualisieren",
60
+ "loadFailed": "Fehler beim Laden",
58
61
  "scrollToTop": "Nach oben scrollen"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Cancel",
5
5
  "close": "Close",
6
- "confirm": "Confirm"
6
+ "confirm": "Confirm",
7
+ "refresh": "Refresh",
8
+ "retry": "Retry"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Actions"
31
+ "actions": "Actions",
32
+ "loadFailed": "Failed to load"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Copied",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— all loaded —",
56
59
  "count": "{loaded} of {total} loaded",
57
- "refresh": "Refresh",
60
+ "loadFailed": "Failed to load",
58
61
  "scrollToTop": "Scroll to top"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Cancelar",
5
5
  "close": "Cerrar",
6
- "confirm": "Confirmar"
6
+ "confirm": "Confirmar",
7
+ "refresh": "Actualizar",
8
+ "retry": "Reintentar"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Acciones"
31
+ "actions": "Acciones",
32
+ "loadFailed": "No se pudo cargar"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Copiado",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— todos cargados —",
56
59
  "count": "{loaded} de {total} cargados",
57
- "refresh": "Actualizar",
60
+ "loadFailed": "No se pudo cargar",
58
61
  "scrollToTop": "Desplazarse hacia arriba"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Annuler",
5
5
  "close": "Fermer",
6
- "confirm": "Confirmer"
6
+ "confirm": "Confirmer",
7
+ "refresh": "Rafraîchir",
8
+ "retry": "Réessayer"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Actions"
31
+ "actions": "Actions",
32
+ "loadFailed": "Échec du chargement"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Copié",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— tout chargé —",
56
59
  "count": "{loaded} sur {total} chargés",
57
- "refresh": "Actualiser",
60
+ "loadFailed": "Échec du chargement",
58
61
  "scrollToTop": "Retour au haut"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "रद्द करें",
5
5
  "close": "बंद करें",
6
- "confirm": "पुष्टि करें"
6
+ "confirm": "पुष्टि करें",
7
+ "refresh": "ताज़ा करें",
8
+ "retry": "पुनः प्रयास करें"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "क्रियाएं"
31
+ "actions": "क्रियाएं",
32
+ "loadFailed": "लोड करने में विफल"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "कॉपी किया गया",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— सभी लोड हो गए —",
56
59
  "count": "{loaded} of {total} लोड हो गया",
57
- "refresh": "ताज़ा करें",
60
+ "loadFailed": "लोड करने में विफल",
58
61
  "scrollToTop": "शीर्ष पर जाएं"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Batal",
5
5
  "close": "Tutup",
6
- "confirm": "Konfirmasi"
6
+ "confirm": "Konfirmasi",
7
+ "refresh": "Segarkan",
8
+ "retry": "Coba Lagi"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Tindakan"
31
+ "actions": "Tindakan",
32
+ "loadFailed": "Gagal memuat"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Disalin",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— semua dimuat —",
56
59
  "count": "{loaded} dari {total} dimuat",
57
- "refresh": "Segarkan",
60
+ "loadFailed": "Gagal memuat",
58
61
  "scrollToTop": "Gulir ke atas"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Annulla",
5
5
  "close": "Chiudi",
6
- "confirm": "Conferma"
6
+ "confirm": "Conferma",
7
+ "refresh": "Aggiorna",
8
+ "retry": "Riprova"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Azioni"
31
+ "actions": "Azioni",
32
+ "loadFailed": "Caricamento non riuscito"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Copiato",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— completamente caricato —",
56
59
  "count": "{loaded} di {total} caricati",
57
- "refresh": "Aggiorna",
60
+ "loadFailed": "Caricamento non riuscito",
58
61
  "scrollToTop": "Scorri verso l'alto"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "キャンセル",
5
5
  "close": "閉じる",
6
- "confirm": "確認"
6
+ "confirm": "確認",
7
+ "refresh": "更新",
8
+ "retry": "再試行"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY年MM月DD日",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "アクション"
31
+ "actions": "アクション",
32
+ "loadFailed": "読み込みに失敗しました"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "コピーしました",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "すべて読み込み完了",
56
59
  "count": "{loaded}/{total} 読み込み済み",
57
- "refresh": "更新",
60
+ "loadFailed": "読み込みに失敗しました",
58
61
  "scrollToTop": "トップへスクロール"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "취소",
5
5
  "close": "닫기",
6
- "confirm": "확인"
6
+ "confirm": "확인",
7
+ "refresh": "새로고침",
8
+ "retry": "재시도"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "작업"
31
+ "actions": "작업",
32
+ "loadFailed": "로드 실패"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "복사됨",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— 모두 로드됨 —",
56
59
  "count": "{loaded}개/{total}개 로드됨",
57
- "refresh": "새로고침",
60
+ "loadFailed": "로드 실패",
58
61
  "scrollToTop": "상단으로 이동"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Annuleren",
5
5
  "close": "Sluiten",
6
- "confirm": "Bevestigen"
6
+ "confirm": "Bevestigen",
7
+ "refresh": "Vernieuwen",
8
+ "retry": "Opnieuw proberen"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Acties"
31
+ "actions": "Acties",
32
+ "loadFailed": "Laden mislukt"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Gekopieerd",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— alles geladen —",
56
59
  "count": "{loaded} van {total} geladen",
57
- "refresh": "Vernieuwen",
60
+ "loadFailed": "Laden mislukt",
58
61
  "scrollToTop": "Naar boven schuiven"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Anuluj",
5
5
  "close": "Zamknij",
6
- "confirm": "Potwierdź"
6
+ "confirm": "Potwierdź",
7
+ "refresh": "Odśwież",
8
+ "retry": "Ponów"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Akcje"
31
+ "actions": "Akcje",
32
+ "loadFailed": "Nie udało się załadować"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Skopiowano",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— wszystko załadowane —",
56
59
  "count": "{loaded} z {total} załadowane",
57
- "refresh": "Odśwież",
60
+ "loadFailed": "Nie udało się załadować",
58
61
  "scrollToTop": "Przewiń do góry"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Cancelar",
5
5
  "close": "Fechar",
6
- "confirm": "Confirmar"
6
+ "confirm": "Confirmar",
7
+ "refresh": "Atualizar",
8
+ "retry": "Tentar Novamente"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Ações"
31
+ "actions": "Ações",
32
+ "loadFailed": "Falha ao carregar"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Copiado",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— Tudo carregado —",
56
59
  "count": "{loaded} de {total} carregado(s)",
57
- "refresh": "Atualizar",
60
+ "loadFailed": "Falha ao carregar",
58
61
  "scrollToTop": "Voltar ao topo"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Отменить",
5
5
  "close": "Закрыть",
6
- "confirm": "Подтвердить"
6
+ "confirm": "Подтвердить",
7
+ "refresh": "Обновить",
8
+ "retry": "Повторить"
7
9
  },
8
10
  "formats": {
9
11
  "date": "ГГГГ-ММ-ДД",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Действия"
31
+ "actions": "Действия",
32
+ "loadFailed": "Ошибка загрузки"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Скопировано",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— все загружено —",
56
59
  "count": "{loaded} из {total} загружено",
57
- "refresh": "Обновить",
60
+ "loadFailed": "Ошибка загрузки",
58
61
  "scrollToTop": "Прокрутить вверх"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "ยกเลิก",
5
5
  "close": "ปิด",
6
- "confirm": "ยืนยัน"
6
+ "confirm": "ยืนยัน",
7
+ "refresh": "รีเฟรช",
8
+ "retry": "ลองใหม่"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "การกระทำ"
31
+ "actions": "การกระทำ",
32
+ "loadFailed": "ไม่สามารถโหลดได้"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "คัดลอกแล้ว",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— โหลดเสร็จแล้วทั้งหมด —",
56
59
  "count": "{loaded} จาก {total} โหลดแล้ว",
57
- "refresh": "รีเฟรช",
60
+ "loadFailed": "ไม่สามารถโหลดได้",
58
61
  "scrollToTop": "เลื่อนขึ้นด้านบน"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "İptal",
5
5
  "close": "Kapat",
6
- "confirm": "Onayla"
6
+ "confirm": "Onayla",
7
+ "refresh": "Yenile",
8
+ "retry": "Yeniden Dene"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "İşlemler"
31
+ "actions": "İşlemler",
32
+ "loadFailed": "Yüklenemedi"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Kopyalandı",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— tümü yüklendi —",
56
59
  "count": "{loaded} / {total} yüklendi",
57
- "refresh": "Yenile",
60
+ "loadFailed": "Yüklenemedi",
58
61
  "scrollToTop": "Başa dön"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "Hủy",
5
5
  "close": "Đóng",
6
- "confirm": "Xác nhận"
6
+ "confirm": "Xác nhận",
7
+ "refresh": "Làm mới",
8
+ "retry": "Thử lại"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "Hành động"
31
+ "actions": "Hành động",
32
+ "loadFailed": "Không thể tải"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "Đã sao chép",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— tất cả đã tải —",
56
59
  "count": "{loaded} của {total} đã tải",
57
- "refresh": "Làm mới",
60
+ "loadFailed": "Không thể tải",
58
61
  "scrollToTop": "Cuộn lên đầu"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "取消",
5
5
  "close": "关闭",
6
- "confirm": "确认"
6
+ "confirm": "确认",
7
+ "refresh": "刷新",
8
+ "retry": "重试"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "操作"
31
+ "actions": "操作",
32
+ "loadFailed": "加载失败"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "已复制",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "全部已加载",
56
59
  "count": "{loaded} / {total} 已加载",
57
- "refresh": "刷新",
60
+ "loadFailed": "加载失败",
58
61
  "scrollToTop": "回到顶部"
59
62
  },
60
63
  "InputRange": {
@@ -3,7 +3,9 @@
3
3
  "actions": {
4
4
  "cancel": "取消",
5
5
  "close": "關閉",
6
- "confirm": "確認"
6
+ "confirm": "確認",
7
+ "refresh": "重新整理",
8
+ "retry": "重試"
7
9
  },
8
10
  "formats": {
9
11
  "date": "YYYY-MM-DD",
@@ -26,7 +28,8 @@
26
28
  "components": {
27
29
  "ui": {
28
30
  "AsyncDataTable": {
29
- "actions": "操作"
31
+ "actions": "操作",
32
+ "loadFailed": "載入失敗"
30
33
  },
31
34
  "CopyButton": {
32
35
  "copied": "已複製",
@@ -54,7 +57,7 @@
54
57
  "InfiniteDataTable": {
55
58
  "allLoaded": "— 全部已載入 —",
56
59
  "count": "{loaded} 的 {total} 已載入",
57
- "refresh": "重新整理",
60
+ "loadFailed": "載入失敗",
58
61
  "scrollToTop": "回到頂部"
59
62
  },
60
63
  "InputRange": {
package/nuxt.config.ts CHANGED
@@ -13,7 +13,7 @@ export default defineNuxtConfig({
13
13
  },
14
14
 
15
15
  // Package is declared as peerDependency so consumers own the version.
16
- extends: [ '@polymarbot/nuxt-layer-effect' ],
16
+ extends: [ '@polymarbot/nuxt-layer-basic' ],
17
17
  modules: [ '@nuxtjs/i18n' ],
18
18
 
19
19
  // Lazy-loaded messages. @nuxtjs/i18n v9+ lazy-loads every locale file by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polymarbot/nuxt-layer-shadcn-ui",
3
- "version": "0.7.6",
3
+ "version": "0.8.0",
4
4
  "description": "Nuxt layer providing shadcn-vue based UI components",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -17,8 +17,8 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@polymarbot/nuxt-layer-effect": "^0.1",
21
- "@polymarbot/uitls-shared": "^0.3",
20
+ "@polymarbot/nuxt-layer-basic": "^0.1",
21
+ "@polymarbot/uitls-shared": "^0.5",
22
22
  "@tanstack/vue-table": "^8.21.3",
23
23
  "@types/lodash-es": "^4.17.12",
24
24
  "@types/qrcode": "^1.5.6",
@@ -42,5 +42,5 @@
42
42
  "vue-i18n": "^11",
43
43
  "vue-router": "^4 || ^5"
44
44
  },
45
- "gitHead": "9303e0b6a75098f94382a178c8ef2bc4dc5e8f88"
45
+ "gitHead": "dbce6d8341e369cdd6ae0c7008b31325c339d200"
46
46
  }