@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.
- package/app/app.tsx +14 -0
- package/app/assets/css/base.css +6 -0
- package/app/components/layout/LayoutAuth.tsx +22 -0
- package/app/components/layout/LayoutNavbar.tsx +193 -0
- package/app/components/layout/LayoutPage.tsx +183 -0
- package/app/components/layout/LayoutSidebar.tsx +167 -0
- package/app/components/modals/ConfirmModal.tsx +80 -0
- package/app/components/modals/EntityCrudModal.tsx +124 -0
- package/app/components/overlay/modal/ModalActions.tsx +36 -0
- package/app/components/overlay/modal/ModalConfirm.tsx +46 -0
- package/app/components/overlay/modal/ModalRadioGroup.tsx +62 -0
- package/app/components/u/UActions.tsx +30 -0
- package/app/components/u/UAuthForm.tsx +30 -0
- package/app/components/u/UCustomApp.tsx +33 -0
- package/app/components/u/UField.tsx +211 -0
- package/app/components/u/UForm.tsx +133 -0
- package/app/components/u/UImageWithFallback.tsx +47 -0
- package/app/components/u/UInputDatePicker.tsx +94 -0
- package/app/components/u/UInputDateRangePicker.tsx +143 -0
- package/app/components/u/UInputDurationMinutes.tsx +106 -0
- package/app/components/u/UInputFile.tsx +132 -0
- package/app/components/u/URibbonCard.tsx +54 -0
- package/app/components/u/URibbonSection.tsx +36 -0
- package/app/components/u/USwitchCard.tsx +64 -0
- package/app/components/util/Define.tsx +21 -0
- package/app/components/util/SlotRef.tsx +21 -0
- package/app/components/util/Sync.tsx +37 -0
- package/app/components/util/TemplateText.tsx +31 -0
- package/app/composables/confirm.ts +1 -1
- package/app/error.tsx +24 -0
- package/app/utils/define-setup-component.ts +39 -5
- package/nuxt.config.ts +8 -0
- package/package.json +4 -3
- package/app/app.vue +0 -3
- package/app/components/layout/LayoutAuth.vue +0 -13
- package/app/components/layout/LayoutNavbar.vue +0 -140
- package/app/components/layout/LayoutPage.vue +0 -174
- package/app/components/layout/LayoutSidebar.vue +0 -117
- package/app/components/modals/ConfirmModal.vue +0 -65
- package/app/components/modals/EntityCrudModal.vue +0 -109
- package/app/components/overlay/modal/ModalActions.vue +0 -25
- package/app/components/overlay/modal/ModalConfirm.vue +0 -25
- package/app/components/overlay/modal/ModalRadioGroup.vue +0 -37
- package/app/components/u/UActions.vue +0 -23
- package/app/components/u/UAuthForm.vue +0 -36
- package/app/components/u/UCustomApp.vue +0 -23
- package/app/components/u/UField.vue +0 -191
- package/app/components/u/UForm.vue +0 -96
- package/app/components/u/UImageWithFallback.vue +0 -35
- package/app/components/u/UInputDatePicker.vue +0 -79
- package/app/components/u/UInputDateRangePicker.vue +0 -100
- package/app/components/u/UInputDurationMinutes.vue +0 -90
- package/app/components/u/UInputFile.vue +0 -127
- package/app/components/u/URibbonCard.vue +0 -43
- package/app/components/u/URibbonSection.vue +0 -29
- package/app/components/u/USwitchCard.vue +0 -51
- package/app/components/util/Define.vue +0 -13
- package/app/components/util/SlotRef.vue +0 -17
- package/app/components/util/Sync.vue +0 -24
- package/app/components/util/TemplateText.vue +0 -20
- 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>
|