@polymarbot/nuxt-layer-shadcn-ui 0.7.7 → 0.8.1

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.
@@ -8,6 +8,7 @@
8
8
  * Stacking order (low → high):
9
9
  * layout header z-100 (project convention)
10
10
  * dialog / sheet (Modal / Drawer) z-200 (cover the header)
11
+ * alert dialog (system interrupts) z-250 (always above other modals)
11
12
  * tooltip / popover / dropdown / select z-300 (above modal when nested)
12
13
  * ========================================================================= */
13
14
 
@@ -19,6 +20,17 @@
19
20
  z-index: 200;
20
21
  }
21
22
 
23
+ /* AlertDialog must always sit above regular Modals, regardless of teleport
24
+ * order. Vue's <Teleport> places content at a stable anchor based on the
25
+ * host component's mount time — so an AlertDialog declared in app.vue (early
26
+ * mount) ends up *behind* a page-level Modal opened later. The overlay is
27
+ * matched via :has() because reka-ui's DialogPortal renders it as the
28
+ * adjacent previous sibling of the content. */
29
+ [data-slot='dialog-content'][data-alert-dialog],
30
+ [data-slot='dialog-overlay']:has(+ [data-slot='dialog-content'][data-alert-dialog]) {
31
+ z-index: 250;
32
+ }
33
+
22
34
  [data-slot='tooltip-content'],
23
35
  [data-slot='popover-content'],
24
36
  [data-slot='dropdown-menu-content'],
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- const { current, isOpen, close, onClosed } = useDialogState()
2
+ const { current, isOpen, isActive, close, onClosed } = useDialogState()
3
3
 
4
4
  const isAlert = computed(() => current.value?.options.rejectLabel === '')
5
5
  const isDestructive = computed(() => {
@@ -10,6 +10,8 @@ const isDestructive = computed(() => {
10
10
 
11
11
  <template>
12
12
  <Modal
13
+ v-if="isActive"
14
+ data-alert-dialog
13
15
  :visible="isOpen"
14
16
  :title="current?.options.title"
15
17
  :type="current?.options.type"
@@ -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 -->
@@ -11,6 +11,8 @@ import {
11
11
  } from '../../shadcn/sheet'
12
12
  import type { DrawerProps } from './types'
13
13
 
14
+ defineOptions({ inheritAttrs: false })
15
+
14
16
  const props = withDefaults(defineProps<DrawerProps>(), {
15
17
  showClose: true,
16
18
  closeOnClickOutside: false,
@@ -92,6 +94,7 @@ const contentClass = computed(() =>
92
94
  </SheetTrigger>
93
95
 
94
96
  <SheetContent
97
+ v-bind="$attrs"
95
98
  :side="side"
96
99
  :class="contentClass"
97
100
  @pointerDownOutside="onPointerDownOutside"
@@ -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
+ }
@@ -17,6 +17,7 @@ const emit = defineEmits<{
17
17
  'rowClick': [row: TData, index: number, event: MouseEvent]
18
18
  }>()
19
19
 
20
+ const { t } = useI18n()
20
21
  const T = useTranslations('components.ui.InfiniteDataTable')
21
22
 
22
23
  // -- Internal state --
@@ -25,6 +26,7 @@ const loading = ref(false)
25
26
  const internalData = ref<TData[]>([]) as Ref<TData[]>
26
27
  const cursor = ref<string | undefined>(undefined)
27
28
  const hasMore = ref(true)
29
+ const errored = ref(false)
28
30
  const total = ref<number | undefined>(undefined)
29
31
  const requestVersion = ref(0)
30
32
 
@@ -82,7 +84,11 @@ function resetState () {
82
84
  internalData.value = []
83
85
  cursor.value = undefined
84
86
  hasMore.value = true
87
+ errored.value = false
85
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
86
92
  }
87
93
 
88
94
  // -- Loading --
@@ -91,6 +97,10 @@ async function loadMore () {
91
97
  if (!props.fetchMethod) return
92
98
  if (loading.value || !hasMore.value) return
93
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
+
94
104
  const currentVersion = ++requestVersion.value
95
105
  loading.value = true
96
106
  try {
@@ -104,6 +114,7 @@ async function loadMore () {
104
114
  } catch (error) {
105
115
  if (currentVersion !== requestVersion.value) return
106
116
  console.error('InfiniteDataTable loadMore failed:', error)
117
+ errored.value = true
107
118
  } finally {
108
119
  if (currentVersion === requestVersion.value) loading.value = false
109
120
  }
@@ -203,20 +214,35 @@ onMounted(() => {
203
214
  >
204
215
  <div
205
216
  v-if="!hasMore"
206
- class="py-2 text-xs text-muted-foreground text-center"
217
+ class="text-sm text-muted-foreground text-center"
207
218
  >
208
219
  {{ T('allLoaded') }}
209
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>
210
236
  <EffectIntersectionChecker
211
237
  v-else-if="!isInitialLoad"
212
238
  :disabled="loading"
213
239
  :options="intersectionOptions"
214
- class="py-2 flex items-center justify-center"
240
+ class="flex items-center justify-center"
215
241
  @show="loadMore"
216
242
  >
217
243
  <Icon
218
244
  name="loader-circle"
219
- class="size-4 animate-spin text-muted-foreground"
245
+ class="size-6 animate-spin text-muted-foreground"
220
246
  />
221
247
  </EffectIntersectionChecker>
222
248
  </template>
@@ -242,7 +268,7 @@ onMounted(() => {
242
268
  @click="scrollToTop"
243
269
  />
244
270
  </Tooltip>
245
- <Tooltip :text="T('refresh')">
271
+ <Tooltip :text="t('common.actions.refresh')">
246
272
  <Button
247
273
  variant="ghost"
248
274
  size="icon-sm"
@@ -11,6 +11,8 @@ import {
11
11
  } from '../../shadcn/dialog'
12
12
  import type { ModalProps } from './types'
13
13
 
14
+ defineOptions({ inheritAttrs: false })
15
+
14
16
  const props = withDefaults(defineProps<ModalProps>(), {
15
17
  showClose: true,
16
18
  closeOnClickOutside: false,
@@ -97,6 +99,7 @@ const contentClass = computed(() =>
97
99
  </DialogTrigger>
98
100
 
99
101
  <DialogContent
102
+ v-bind="$attrs"
100
103
  :class="contentClass"
101
104
  :showCloseButton="false"
102
105
  @pointerDownOutside="onPointerDownOutside"
@@ -18,6 +18,7 @@ interface DialogQueueItem {
18
18
 
19
19
  const dialogQueue = reactive<DialogQueueItem[]>([])
20
20
  const isOpen = ref(false)
21
+ const mountedInstances = ref<symbol[]>([])
21
22
 
22
23
  function showDialog (options: DialogOptions): Promise<boolean> {
23
24
  return new Promise<boolean>(resolve => {
@@ -52,8 +53,22 @@ export function useDialog () {
52
53
 
53
54
  /**
54
55
  * Internal composable for the AlertDialog component to access dialog queue.
56
+ *
57
+ * Multiple `<AlertDialog />` instances can be mounted simultaneously (e.g. in
58
+ * Storybook docs view), but only the first one renders the modal — the rest
59
+ * stay inert via `isActive`. This prevents stacked overlays and duplicate
60
+ * `onClosed` events from over-shifting the shared queue.
55
61
  */
56
62
  export function useDialogState () {
63
+ const id = Symbol('AlertDialog')
64
+ mountedInstances.value.push(id)
65
+
66
+ onScopeDispose(() => {
67
+ const idx = mountedInstances.value.indexOf(id)
68
+ if (idx >= 0) mountedInstances.value.splice(idx, 1)
69
+ })
70
+
71
+ const isActive = computed(() => mountedInstances.value[0] === id)
57
72
  const current = computed(() => dialogQueue[0] ?? null)
58
73
 
59
74
  /** Resolve current dialog and trigger close animation */
@@ -72,5 +87,5 @@ export function useDialogState () {
72
87
  }
73
88
  }
74
89
 
75
- return { current, isOpen, close, onClosed }
90
+ return { current, isOpen, isActive, close, onClosed }
76
91
  }
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.7",
3
+ "version": "0.8.1",
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": "e053ea4dd7358d5974b1118be8588d88be36e376"
45
+ "gitHead": "6e895437229b0f15e709d90785903ea846399c36"
46
46
  }