@falcondev-oss/nuxt-layers-base 0.40.3 → 0.41.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.
Files changed (61) hide show
  1. package/app/app.tsx +14 -0
  2. package/app/assets/css/base.css +6 -0
  3. package/app/components/layout/LayoutAuth.tsx +22 -0
  4. package/app/components/layout/LayoutNavbar.tsx +193 -0
  5. package/app/components/layout/LayoutPage.tsx +183 -0
  6. package/app/components/layout/LayoutSidebar.tsx +167 -0
  7. package/app/components/modals/ConfirmModal.tsx +80 -0
  8. package/app/components/modals/EntityCrudModal.tsx +124 -0
  9. package/app/components/overlay/modal/ModalActions.tsx +36 -0
  10. package/app/components/overlay/modal/ModalConfirm.tsx +46 -0
  11. package/app/components/overlay/modal/ModalRadioGroup.tsx +62 -0
  12. package/app/components/u/UActions.tsx +30 -0
  13. package/app/components/u/UAuthForm.tsx +30 -0
  14. package/app/components/u/UCustomApp.tsx +33 -0
  15. package/app/components/u/UField.tsx +211 -0
  16. package/app/components/u/UForm.tsx +133 -0
  17. package/app/components/u/UImageWithFallback.tsx +47 -0
  18. package/app/components/u/UInputDatePicker.tsx +94 -0
  19. package/app/components/u/UInputDateRangePicker.tsx +143 -0
  20. package/app/components/u/UInputDurationMinutes.tsx +106 -0
  21. package/app/components/u/UInputFile.tsx +132 -0
  22. package/app/components/u/URibbonCard.tsx +54 -0
  23. package/app/components/u/URibbonSection.tsx +36 -0
  24. package/app/components/u/USwitchCard.tsx +64 -0
  25. package/app/components/util/Define.tsx +21 -0
  26. package/app/components/util/SlotRef.tsx +21 -0
  27. package/app/components/util/Sync.tsx +37 -0
  28. package/app/components/util/TemplateText.tsx +31 -0
  29. package/app/composables/confirm.ts +1 -1
  30. package/app/error.tsx +24 -0
  31. package/app/utils/define-setup-component.ts +39 -5
  32. package/nuxt.config.ts +8 -0
  33. package/package.json +4 -3
  34. package/app/app.vue +0 -3
  35. package/app/components/layout/LayoutAuth.vue +0 -13
  36. package/app/components/layout/LayoutNavbar.vue +0 -140
  37. package/app/components/layout/LayoutPage.vue +0 -174
  38. package/app/components/layout/LayoutSidebar.vue +0 -117
  39. package/app/components/modals/ConfirmModal.vue +0 -65
  40. package/app/components/modals/EntityCrudModal.vue +0 -109
  41. package/app/components/overlay/modal/ModalActions.vue +0 -25
  42. package/app/components/overlay/modal/ModalConfirm.vue +0 -25
  43. package/app/components/overlay/modal/ModalRadioGroup.vue +0 -37
  44. package/app/components/u/UActions.vue +0 -23
  45. package/app/components/u/UAuthForm.vue +0 -36
  46. package/app/components/u/UCustomApp.vue +0 -23
  47. package/app/components/u/UField.vue +0 -191
  48. package/app/components/u/UForm.vue +0 -96
  49. package/app/components/u/UImageWithFallback.vue +0 -35
  50. package/app/components/u/UInputDatePicker.vue +0 -79
  51. package/app/components/u/UInputDateRangePicker.vue +0 -100
  52. package/app/components/u/UInputDurationMinutes.vue +0 -90
  53. package/app/components/u/UInputFile.vue +0 -127
  54. package/app/components/u/URibbonCard.vue +0 -43
  55. package/app/components/u/URibbonSection.vue +0 -29
  56. package/app/components/u/USwitchCard.vue +0 -51
  57. package/app/components/util/Define.vue +0 -13
  58. package/app/components/util/SlotRef.vue +0 -17
  59. package/app/components/util/Sync.vue +0 -24
  60. package/app/components/util/TemplateText.vue +0 -20
  61. package/app/error.vue +0 -15
@@ -1,174 +0,0 @@
1
- <script setup lang="ts">
2
- import type {
3
- ButtonProps,
4
- FooterProps,
5
- FooterSlots,
6
- HeaderProps,
7
- HeaderSlots,
8
- NavigationMenuItem,
9
- NavigationMenuProps,
10
- } from '@nuxt/ui'
11
- import type { AddPropertyPrefix } from '../../types/helpers'
12
- import { keys, omit, pickBy, pipe, pullObject } from 'remeda'
13
-
14
- defineProps<{
15
- header?: {
16
- logo?: {
17
- src?: string
18
- iconSrc?: string
19
- }
20
- navigation?: NavigationMenuProps
21
- actions?: ButtonProps[]
22
- ui?: HeaderProps['ui']
23
- mobileActions?: ButtonProps[]
24
- }
25
- footer?: {
26
- items?: NavigationMenuItem[]
27
- actions?: ButtonProps[]
28
- ui?: FooterProps['ui']
29
- }
30
- }>()
31
-
32
- const slots = defineSlots<
33
- {
34
- default: any
35
- } & AddPropertyPrefix<HeaderSlots, 'header'> &
36
- AddPropertyPrefix<FooterSlots, 'footer'>
37
- >()
38
-
39
- const omitHeaderSlots = [
40
- 'header-title',
41
- 'header-right',
42
- 'header-default',
43
- 'header-body',
44
- ] satisfies (keyof typeof slots)[]
45
-
46
- const headerSlots = computed(() =>
47
- pipe(
48
- slots,
49
- pickBy((_, key) => key.startsWith('header-')),
50
- omit(omitHeaderSlots),
51
- keys(),
52
- pullObject(
53
- (key) => key.replace('header-', ''),
54
- (key) => key,
55
- ),
56
- ),
57
- )
58
-
59
- const omitFooterSlots = [
60
- 'footer-left',
61
- 'footer-right',
62
- 'footer-default',
63
- ] satisfies (keyof typeof slots)[]
64
-
65
- const footerSlots = computed(() =>
66
- pipe(
67
- slots,
68
- pickBy((_, key) => key.startsWith('footer-')),
69
- omit(omitFooterSlots),
70
- keys(),
71
- pullObject(
72
- (key) => key.replace('footer-', ''),
73
- (key) => key,
74
- ),
75
- ),
76
- )
77
- </script>
78
-
79
- <!-- eslint-disable vue/require-explicit-slots -->
80
- <template>
81
- <div>
82
- <UHeader v-if="header" :ui="header.ui">
83
- <template v-if="header.logo || slots['header-title']" #title>
84
- <slot name="header-title">
85
- <template v-if="header.logo">
86
- <img v-if="header.logo.src" class="h-5 w-auto shrink-0" :src="header.logo.src" />
87
- <img
88
- v-if="!header.logo.src && header.logo.iconSrc"
89
- class="size-5"
90
- :src="header.logo.iconSrc"
91
- />
92
- </template>
93
- </slot>
94
- </template>
95
-
96
- <UNavigationMenu v-if="header.navigation" v-bind="header.navigation" />
97
-
98
- <template v-if="header.actions || slots['header-right']" #right>
99
- <slot name="header-right">
100
- <UActions
101
- v-if="header.mobileActions"
102
- class="md:hidden"
103
- :actions="header.mobileActions"
104
- :defaults="{
105
- variant: 'subtle',
106
- }"
107
- />
108
- <UActions
109
- v-if="header.actions"
110
- class="max-sm:hidden"
111
- :actions="header.actions"
112
- :defaults="{
113
- variant: 'subtle',
114
- }"
115
- />
116
- </slot>
117
- </template>
118
-
119
- <template #body>
120
- <slot name="header-body">
121
- <div class="flex flex-col gap-4">
122
- <UNavigationMenu
123
- v-if="header.navigation"
124
- v-bind="header.navigation"
125
- orientation="vertical"
126
- />
127
- <USeparator v-if="header.navigation && header.actions" />
128
- <UActions
129
- v-if="header.actions"
130
- class="flex-col"
131
- :actions="header.actions"
132
- :defaults="{
133
- variant: 'subtle',
134
- }"
135
- />
136
- </div>
137
- </slot>
138
- </template>
139
-
140
- <template v-for="(originalName, slotName) in headerSlots" #[slotName]="slotData">
141
- <!-- @vue-ignore -->
142
- <slot :name="originalName" v-bind="slotData || {}" />
143
- </template>
144
- </UHeader>
145
- <UMain>
146
- <slot />
147
- </UMain>
148
- <UFooter v-if="footer" :ui="footer.ui">
149
- <template #left>
150
- <slot name="footer-left">
151
- <p class="text-muted text-sm">Copyright © {{ new Date().getFullYear() }}</p>
152
- </slot>
153
- </template>
154
-
155
- <UNavigationMenu v-if="footer.items" :items="footer.items" variant="link" />
156
-
157
- <template v-if="footer.actions || slots['footer-right']" #right>
158
- <slot name="footer-right">
159
- <UActions
160
- :actions="footer.actions"
161
- :defaults="{
162
- variant: 'ghost',
163
- }"
164
- />
165
- </slot>
166
- </template>
167
-
168
- <template v-for="(originalName, slotName) in footerSlots" #[slotName]="slotData">
169
- <!-- @vue-ignore -->
170
- <slot :name="originalName" v-bind="slotData || {}" />
171
- </template>
172
- </UFooter>
173
- </div>
174
- </template>
@@ -1,117 +0,0 @@
1
- <script setup lang="ts">
2
- import type {
3
- ArrayOrNested,
4
- AvatarProps,
5
- DashboardSearchProps,
6
- DashboardSidebarProps,
7
- DropdownMenuItem,
8
- NavigationMenuItem,
9
- NavigationMenuProps,
10
- } from '@nuxt/ui'
11
- import { mergeSlotClass } from '../../utils/ui'
12
-
13
- defineProps<{
14
- sidebar?: DashboardSidebarProps
15
- logo?: {
16
- src?: string
17
- iconSrc?: string
18
- }
19
- search?: DashboardSearchProps
20
- items?: NavigationMenuItem[]
21
- bottomItems?: NavigationMenuItem[]
22
- userMenu?: {
23
- name: string
24
- avatar?: AvatarProps
25
- items: ArrayOrNested<DropdownMenuItem>
26
- }
27
- itemsUi?: NavigationMenuProps['ui']
28
- bottomItemsUi?: NavigationMenuProps['ui']
29
- }>()
30
-
31
- const slots = defineSlots<{
32
- 'default': any
33
- 'logo'?: any
34
- 'icon'?: any
35
- }>()
36
-
37
- const config = useRuntimeConfig()
38
- </script>
39
-
40
- <template>
41
- <UDashboardGroup storage="local" :storage-key="`${config.public.projectId}-dashboard`" unit="rem">
42
- <UDashboardSidebar
43
- v-bind="sidebar"
44
- class="bg-white"
45
- mode="drawer"
46
- collapsible
47
- :ui="{
48
- ...sidebar?.ui,
49
- header: mergeSlotClass(sidebar?.ui?.header, 'border-b border-default'),
50
- footer: mergeSlotClass(sidebar?.ui?.footer, 'border-t border-default'),
51
- }"
52
- >
53
- <template
54
- v-if="logo?.src || logo?.iconSrc || slots.logo || slots.icon"
55
- #header="{ collapsed }"
56
- >
57
- <slot v-if="!collapsed" name="logo">
58
- <img v-if="logo?.src" class="h-5 w-auto shrink-0" :src="logo.src" />
59
- </slot>
60
- <div v-if="collapsed || (!logo?.src && !slots.logo)" :class="{ 'mx-auto': collapsed }">
61
- <slot name="icon">
62
- <img v-if="logo?.iconSrc" class="size-5" :src="logo.iconSrc" />
63
- </slot>
64
- </div>
65
- </template>
66
- <template #default="{ collapsed }">
67
- <!-- height and negative margins line the border up with the `LayoutNavbar` toolbar's, cancelling the sidebar body's padding -->
68
- <div
69
- v-if="search"
70
- class="border-default -mx-4 -mt-2 -mb-2 flex h-[calc(--spacing(12)+1px)] shrink-0 items-center border-b px-4 sm:max-lg:-mx-6 sm:max-lg:px-6"
71
- >
72
- <UDashboardSearchButton :collapsed="collapsed" variant="outline" tooltip block />
73
- </div>
74
-
75
- <UNavigationMenu
76
- v-if="items"
77
- :collapsed="collapsed"
78
- :items="items"
79
- orientation="vertical"
80
- :ui="itemsUi"
81
- />
82
-
83
- <UNavigationMenu
84
- v-if="bottomItems"
85
- :collapsed="collapsed"
86
- :items="bottomItems"
87
- orientation="vertical"
88
- class="mt-auto"
89
- :ui="bottomItemsUi"
90
- />
91
- </template>
92
- <template v-if="userMenu" #footer="{ collapsed }">
93
- <UDropdownMenu :items="userMenu.items">
94
- <UButton
95
- :label="collapsed ? undefined : userMenu.name"
96
- :trailing-icon="collapsed ? undefined : 'ph:caret-up-down'"
97
- :icon="userMenu.avatar ? undefined : 'lucide:user-round'"
98
- :avatar="userMenu.avatar"
99
- color="neutral"
100
- variant="ghost"
101
- block
102
- :square="collapsed"
103
- class="data-[state=open]:bg-elevated"
104
- :ui="{
105
- leadingIcon: 'text-accented',
106
- trailingIcon: 'text-dimmed',
107
- }"
108
- />
109
- </UDropdownMenu>
110
- </template>
111
- </UDashboardSidebar>
112
-
113
- <UDashboardSearch v-if="search" :color-mode="false" v-bind="search" />
114
-
115
- <slot />
116
- </UDashboardGroup>
117
- </template>
@@ -1,65 +0,0 @@
1
- <script setup lang="ts" generic="const O extends RadioGroupItem[], R extends ConfirmModalResult<O>">
2
- import type { ButtonProps, RadioGroupItem, RadioGroupProps } from '@nuxt/ui'
3
- import type { ComponentPublicInstance } from 'vue'
4
- import type { ConfirmModalResult } from '../../composables/confirm'
5
- import type { templateParts } from '../../utils/display'
6
-
7
- export type ConfirmModalProps<O extends RadioGroupItem[]> = {
8
- title: string
9
- confirmLabel: string
10
- confirmDisabled?: () => boolean
11
- text?: string | ReturnType<typeof templateParts>
12
- slotRef?: ComponentPublicInstance
13
- options?: O
14
- color?: ButtonProps['color']
15
- }
16
-
17
- defineProps<ConfirmModalProps<O>>()
18
-
19
- const emit = defineEmits<{
20
- close: [result: R]
21
- }>()
22
-
23
- const selectedOption = ref<RadioGroupProps<O>['modelValue']>()
24
- </script>
25
-
26
- <template>
27
- <UModal
28
- :title
29
- :close="{ onClick: () => emit('close', false as R) }"
30
- :dismissible="false"
31
- :ui="{ header: 'border-b-0!', body: 'pt-0!' }"
32
- >
33
- <template #body>
34
- <div class="flex flex-col gap-4">
35
- <p v-if="text && typeof text === 'string'">{{ text }}</p>
36
- <TemplateText v-else-if="text && typeof text === 'object'" :text />
37
-
38
- <SlotRef :slot-ref />
39
-
40
- <URadioGroup
41
- v-model="selectedOption"
42
- :items="options"
43
- variant="table"
44
- :color="color ?? 'error'"
45
- />
46
- </div>
47
- </template>
48
- <template #footer>
49
- <div class="flex w-full justify-end gap-2">
50
- <UButton
51
- color="neutral"
52
- variant="soft"
53
- label="Abbrechen"
54
- @click="() => emit('close', false as R)"
55
- />
56
- <UButton
57
- :color="color ?? 'error'"
58
- :label="confirmLabel"
59
- :disabled="(!!options?.length && selectedOption === undefined) || confirmDisabled?.()"
60
- @click="() => emit('close', (selectedOption ?? true) as R)"
61
- />
62
- </div>
63
- </template>
64
- </UModal>
65
- </template>
@@ -1,109 +0,0 @@
1
- <script
2
- setup
3
- lang="ts"
4
- generic="
5
- Schema extends FormSchema,
6
- QueryData extends NonNullable<FormSourceValues<Schema>>,
7
- Select extends QueryData = QueryData,
8
- Id extends string | null = string | null
9
- "
10
- >
11
- import type { FormSchema, FormSourceValues, FormSubmitValues } from '@falcondev-oss/form-core'
12
- import type { DefaultError, UseQueryOptions } from '@tanstack/vue-query'
13
- import type { If, IsNever } from 'type-fest'
14
- import { useForm } from '@falcondev-oss/form-vue'
15
- import { useQuery } from '@tanstack/vue-query'
16
- import modalTheme from '#build/ui/modal'
17
-
18
- const props = defineProps<{
19
- id: Id
20
- name: string
21
- schema: Schema
22
- getQueryOptions: (
23
- id: Id,
24
- ) => UseQueryOptions<If<IsNever<Select>, QueryData, any>, DefaultError, Select>
25
- newValues: NonNullable<FormSourceValues<Schema>>
26
- mutate: (values: FormSubmitValues<Schema> & { id: Id }) => Promise<unknown>
27
- }>()
28
-
29
- const emit = defineEmits<{
30
- close: []
31
- }>()
32
-
33
- defineSlots<{
34
- default: (props: { form: ReturnType<typeof useForm<Schema>>; data: typeof data.value }) => any
35
- }>()
36
-
37
- const toast = useToast()
38
- const appConfig = useAppConfig()
39
-
40
- const { data, isLoading } = useQuery(computed(() => props.getQueryOptions(props.id)))
41
-
42
- const form = useForm({
43
- schema: props.schema,
44
- sourceValues: () => {
45
- if (isLoading.value) return
46
-
47
- return data.value ?? props.newValues
48
- },
49
- async submit({ values }) {
50
- await props.mutate({
51
- ...values,
52
- id: props.id,
53
- })
54
-
55
- emit('close')
56
- toast.add({
57
- title: `${props.name} ${props.id ? 'aktualisiert' : 'erstellt'}`,
58
- color: 'success',
59
- icon: 'lucide:check',
60
- })
61
- },
62
- })
63
-
64
- const formActionsTeleportId = useId()
65
- const closeHandle = ref<(() => void) | null>(null)
66
-
67
- // const { tryLeave, onLeave } = usePreventLeave()
68
- // onLeave(() => {
69
- // console.warn('leave')
70
- // closeHandle.value?.()
71
- // })
72
- </script>
73
-
74
- <template>
75
- <UModal :close="false" :title="`${name} ${id ? 'bearbeiten' : 'erstellen'}`" :dismissible="false">
76
- <template #actions>
77
- <UButton
78
- :class="modalTheme.slots.close"
79
- :icon="appConfig.ui.icons.close"
80
- color="neutral"
81
- variant="ghost"
82
- aria-label="Schließen"
83
- loading-auto
84
- @click="
85
- async () => {
86
- // if (!(await tryLeave())) return
87
- closeHandle?.()
88
- }
89
- "
90
- />
91
- </template>
92
- <template #body="{ close }">
93
- <Sync v-model:output="closeHandle" :input="close" />
94
- <UForm
95
- class="flex flex-col gap-2"
96
- :form
97
- :submit-label="id ? 'Speichern' : 'Erstellen'"
98
- :actions-teleport-to="`#${formActionsTeleportId}`"
99
- >
100
- <slot :form :data />
101
- </UForm>
102
- </template>
103
- <template #footer>
104
- <div :id="formActionsTeleportId" class="flex w-full justify-end gap-2">
105
- <UButton color="neutral" variant="soft" label="Abbrechen" @click="() => emit('close')" />
106
- </div>
107
- </template>
108
- </UModal>
109
- </template>
@@ -1,25 +0,0 @@
1
- <script setup lang="ts">
2
- import type { ButtonProps } from '@nuxt/ui'
3
- import { UModal } from '#components'
4
-
5
- defineProps<{
6
- actions: ButtonProps[]
7
- title: string
8
- description?: string
9
- }>()
10
- </script>
11
-
12
- <template>
13
- <UModal :title :description :ui="{ content: 'max-w-fit', footer: 'flex flex-col gap-4' }">
14
- <template #footer>
15
- <UButton
16
- v-for="action of actions"
17
- :key="action.label"
18
- variant="subtle"
19
- class="w-full justify-center"
20
- size="xl"
21
- v-bind="action"
22
- />
23
- </template>
24
- </UModal>
25
- </template>
@@ -1,25 +0,0 @@
1
- <script setup lang="ts">
2
- defineProps<{
3
- title: string
4
- description: string
5
- submitLabel: string
6
- }>()
7
-
8
- const emit = defineEmits<{ close: [boolean] }>()
9
- </script>
10
-
11
- <template>
12
- <UModal :close="{ onClick: () => emit('close', false) }" :title :description class="max-w-sm">
13
- <template #footer>
14
- <div class="flex w-full justify-end gap-2">
15
- <UButton
16
- variant="ghost"
17
- color="neutral"
18
- label="Abbrechen"
19
- @click="() => emit('close', false)"
20
- />
21
- <UButton :label="submitLabel" color="error" @click="() => emit('close', true)" />
22
- </div>
23
- </template>
24
- </UModal>
25
- </template>
@@ -1,37 +0,0 @@
1
- <script setup lang="ts" generic="T extends Extract<RadioGroupItem, object>">
2
- import type { GetItemValue, NestedItem, RadioGroupItem } from '#ui/types'
3
- import { UModal } from '#components'
4
-
5
- const props = defineProps<{
6
- items: T[]
7
- defaultValue: GetItemValue<T[], 'value', NestedItem<T>>
8
- title: string
9
- submitButtonLabel: string
10
- description?: string
11
- }>()
12
-
13
- const emit = defineEmits<{
14
- close: [value: T['value'] | undefined]
15
- }>()
16
-
17
- const selectedValue = ref(props.defaultValue)
18
- </script>
19
-
20
- <template>
21
- <UModal :title :description>
22
- <template #body>
23
- <div class="flex flex-col gap-4">
24
- <URadioGroup v-model="selectedValue" variant="table" :items />
25
- <div class="flex w-full justify-end gap-4">
26
- <UButton
27
- label="Abbrechen"
28
- variant="ghost"
29
- color="neutral"
30
- @click="() => emit('close', undefined)"
31
- />
32
- <UButton :label="submitButtonLabel" @click="() => emit('close', selectedValue)" />
33
- </div>
34
- </div>
35
- </template>
36
- </UModal>
37
- </template>
@@ -1,23 +0,0 @@
1
- <script setup lang="ts">
2
- import type { ButtonProps } from '@nuxt/ui'
3
-
4
- defineProps<{
5
- actions?: ButtonProps[]
6
- defaults?: Partial<ButtonProps>
7
- }>()
8
-
9
- function getActionKey(action: ButtonProps) {
10
- return action.label || action.icon
11
- }
12
- </script>
13
-
14
- <template>
15
- <div v-if="actions" class="flex gap-4">
16
- <UButton
17
- v-for="action of actions"
18
- :key="getActionKey(action)"
19
- v-bind="{ ...defaults, ...action }"
20
- />
21
- </div>
22
- <div v-else />
23
- </template>
@@ -1,36 +0,0 @@
1
- <script
2
- setup
3
- lang="ts"
4
- generic="T extends FormSchema = FormSchema<object>, F extends AuthFormField = AuthFormField"
5
- >
6
- import type {
7
- AuthFormField,
8
- AuthFormProps,
9
- AuthFormSlots,
10
- FormSchema,
11
- FormSubmitEvent,
12
- InferInput,
13
- InferOutput,
14
- } from '@nuxt/ui'
15
- import type { Reactive } from 'vue'
16
- import NuxtUIAuthForm from '@nuxt/ui/components/AuthForm.vue'
17
- import { useForwardProps } from 'reka-ui'
18
-
19
- const props = defineProps<
20
- Omit<AuthFormProps<T, F>, 'onSubmit'> & {
21
- onSubmit?: (event: FormSubmitEvent<InferOutput<T>>) => void | Promise<void>
22
- }
23
- >()
24
- const slots = defineSlots<AuthFormSlots<Reactive<InferInput<T>>, F>>()
25
-
26
- const forwarded = useForwardProps(props)
27
- </script>
28
-
29
- <template>
30
- <NuxtUIAuthForm v-bind="forwarded">
31
- <template v-for="(_, name) in slots" #[name]="slotData">
32
- <!-- @vue-ignore -->
33
- <slot :name="name" v-bind="slotData || {}" />
34
- </template>
35
- </NuxtUIAuthForm>
36
- </template>
@@ -1,23 +0,0 @@
1
- <script setup lang="ts">
2
- import type { AppProps } from '@nuxt/ui'
3
- import { useForwardProps } from 'reka-ui'
4
-
5
- const props = defineProps<{
6
- app?: AppProps
7
- }>()
8
-
9
- const forwarded = useForwardProps(props)
10
-
11
- const config = useRuntimeConfig()
12
- </script>
13
-
14
- <template>
15
- <UApp v-bind="forwarded.app" :nonce="config.public.projectId">
16
- <NuxtLoadingIndicator color="var(--color-brand-secondary, var(--color-brand-primary))" />
17
- <Suspense>
18
- <NuxtLayout>
19
- <NuxtPage />
20
- </NuxtLayout>
21
- </Suspense>
22
- </UApp>
23
- </template>