@falcondev-oss/nuxt-layers-base 0.35.0 → 0.35.4
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/components/layout/LayoutSidebar.vue +1 -1
- package/app/components/u/UField.vue +1 -1
- package/app/components/u/UInputDurationMinutes.vue +5 -2
- package/app/utils/define-setup-component.ts +88 -63
- package/package.json +12 -5
- package/.playground/app/assets/test.css +0 -4
- package/.playground/app/components/FormCard.tsx +0 -89
- package/.playground/app/components/Select.tsx +0 -58
- package/.playground/app/pages/index.vue +0 -297
- package/.playground/app/pages/login.vue +0 -29
- package/.playground/app/pages/page.vue +0 -31
- package/.playground/app/plugins/trpc.ts +0 -5
- package/.playground/app/plugins/vue-query.ts +0 -1
- package/.playground/app.vue +0 -15
- package/.playground/nuxt.config.ts +0 -10
- package/.playground/tsconfig.json +0 -17
- package/eslint.config.js +0 -18
|
@@ -4,7 +4,7 @@ import type { FormFieldProps, FormFieldSlots } from '@nuxt/ui'
|
|
|
4
4
|
import { createReusableTemplate } from '@vueuse/core'
|
|
5
5
|
import { useForwardProps } from 'reka-ui'
|
|
6
6
|
import * as R from 'remeda'
|
|
7
|
-
import { mergeSlotClass } from '
|
|
7
|
+
import { mergeSlotClass } from '../../utils/ui'
|
|
8
8
|
|
|
9
9
|
type InputProps<T> = {
|
|
10
10
|
'modelValue': T
|
|
@@ -5,7 +5,10 @@ import { regex } from 'arkregex'
|
|
|
5
5
|
import { vMaska } from 'maska/vue'
|
|
6
6
|
import { useForwardPropsEmits } from 'reka-ui'
|
|
7
7
|
|
|
8
|
-
type Props = Omit<InputProps<string>, 'modelValue' | 'defaultValue' | 'modelModifiers'>
|
|
8
|
+
type Props = Omit<InputProps<string>, 'modelValue' | 'defaultValue' | 'modelModifiers'> & {
|
|
9
|
+
showZeroMinutes?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
9
12
|
const props = defineProps<Props>()
|
|
10
13
|
const emit = defineEmits<Omit<InputEmits<string>, 'update:modelValue'>>()
|
|
11
14
|
const forwardedProps = useForwardPropsEmits(props as Props, emit)
|
|
@@ -27,7 +30,7 @@ const duration = computed({
|
|
|
27
30
|
const sign = model.value < 0 ? '-' : ''
|
|
28
31
|
|
|
29
32
|
// don't always add :00 if minutes is 0
|
|
30
|
-
if (minutes > 0)
|
|
33
|
+
if (minutes > 0 || props.showZeroMinutes)
|
|
31
34
|
return `${sign}${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`
|
|
32
35
|
|
|
33
36
|
return `${sign}${hours.toString()}`
|
|
@@ -18,45 +18,107 @@ declare module 'vue' {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
21
|
+
interface ComponentTypes {
|
|
22
|
+
props?: Record<string, any>
|
|
23
|
+
emits?: ObjectEmitsOptions
|
|
24
|
+
slots?: Record<string, any>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Reads field `K` from the declared types `T`, defaulting to `{}` when the field
|
|
29
|
+
* is omitted. Omitting e.g. `emits` leaves `'emits'` out of `keyof T`, so the
|
|
30
|
+
* declaration can drop empty `emits: {}` / `slots: {}` entries entirely instead of
|
|
31
|
+
* resolving them to `undefined` and poisoning downstream inference.
|
|
32
|
+
*/
|
|
33
|
+
type Field<T extends ComponentTypes, K extends keyof ComponentTypes> = K extends keyof T
|
|
34
|
+
? NonNullable<T[K]>
|
|
35
|
+
: {}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Keys of `T` as a readonly tuple, or `readonly []` when `T` has no keys.
|
|
39
|
+
*
|
|
40
|
+
* `keyof {}` is `never` and `UnionToTuple<never>` resolves to `never` (not `[]`),
|
|
41
|
+
* so the empty case is guarded explicitly. Without it an empty `props`/`emits`
|
|
42
|
+
* declaration (e.g. `emits: {}`) poisons inference of the surrounding options object.
|
|
43
|
+
*/
|
|
44
|
+
type KeysTuple<T> = [keyof T] extends [never]
|
|
45
|
+
? readonly []
|
|
46
|
+
: Readonly<UnionToTuple<keyof AllUnionFields<T>>>
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Vue's own `emit` type (`SetupContext<E>['emit']`) for declared emits, but an empty
|
|
50
|
+
* emits declaration produces an *uncallable* `emit` (`event: never`) rather than Vue's
|
|
51
|
+
* permissive `(event: string, ...args: any[])` fallback. So when `emits` is omitted,
|
|
52
|
+
* `emit('click')` is a type error instead of silently allowed.
|
|
53
|
+
*/
|
|
54
|
+
type StrictEmitFn<E extends ObjectEmitsOptions> = [keyof E] extends [never]
|
|
55
|
+
? (event: never, ...args: never) => void
|
|
56
|
+
: SetupContext<E>['emit']
|
|
57
|
+
|
|
58
|
+
/** Runtime config for a component, derived from its declared types `T`. */
|
|
59
|
+
interface SetupConfig<T extends ComponentTypes> {
|
|
60
|
+
props: KeysTuple<Field<T, 'props'>>
|
|
61
|
+
emits: KeysTuple<Field<T, 'emits'>>
|
|
62
|
+
setup: (
|
|
63
|
+
props: Field<T, 'props'> & EmitsToProps<Field<T, 'emits'>>,
|
|
64
|
+
ctx: Omit<SetupContext<Field<T, 'emits'>, SlotsType<Partial<Field<T, 'slots'>>>>, 'emit'> & {
|
|
65
|
+
emit: StrictEmitFn<Field<T, 'emits'>>
|
|
66
|
+
},
|
|
67
|
+
) => RenderFunction | Promise<RenderFunction>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Brand applied by `options()` and required by `defineSetupComponent`, so the config
|
|
71
|
+
// must go through `options()` (where props/emits are validated against `T`) rather than
|
|
72
|
+
// be returned as a raw object literal. The key is a readable string so bypassing it
|
|
73
|
+
// reports `Property '"use the options() helper"' is missing`.
|
|
74
|
+
type OptionsBrand = { readonly ['use the options() helper']: true }
|
|
75
|
+
type ViaOptions<T extends ComponentTypes> = SetupConfig<T> & OptionsBrand
|
|
76
|
+
|
|
77
|
+
// `defineSetupComponent`'s callback must return an `options()` result. This shape is
|
|
78
|
+
// intentionally independent of `T`: `options()` already validated everything and typed
|
|
79
|
+
// `setup`, so referencing `T` here again would only re-collapse `T` inference (and yield
|
|
80
|
+
// a confusing error) if `options()` is bypassed.
|
|
81
|
+
type LooseSetupConfig = {
|
|
82
|
+
props: readonly string[]
|
|
83
|
+
emits: readonly string[]
|
|
84
|
+
setup: (...args: any) => any
|
|
85
|
+
} & OptionsBrand
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Validates the runtime `config` against the declared types `_` and types `setup`.
|
|
89
|
+
*
|
|
90
|
+
* `_` binds the declared types `T`, so `config` is checked as a plain argument (not as
|
|
91
|
+
* an inferred callback return) — which means key mismatches are reported locally, right
|
|
92
|
+
* on the offending `props`/`emits`/`setup` property.
|
|
93
|
+
*/
|
|
94
|
+
export function options<const T extends ComponentTypes>(
|
|
95
|
+
_: T,
|
|
96
|
+
config: SetupConfig<T>,
|
|
97
|
+
): ViaOptions<T> {
|
|
98
|
+
return config as ViaOptions<T>
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function defineSetupComponent<const T extends ComponentTypes>(
|
|
102
|
+
options_: (opts: T) => LooseSetupConfig,
|
|
41
103
|
): new (
|
|
42
|
-
props:
|
|
104
|
+
props: Field<T, 'props'>,
|
|
43
105
|
) => CreateComponentPublicInstanceWithMixins<
|
|
44
|
-
|
|
106
|
+
Field<T, 'props'> & EmitsToProps<Field<T, 'emits'>>,
|
|
45
107
|
{},
|
|
46
108
|
{},
|
|
47
109
|
{},
|
|
48
110
|
{},
|
|
49
111
|
ComponentOptionsMixin,
|
|
50
112
|
ComponentOptionsMixin,
|
|
51
|
-
|
|
52
|
-
PublicProps & { vSlots?:
|
|
113
|
+
Field<T, 'emits'>,
|
|
114
|
+
PublicProps & { vSlots?: Field<T, 'slots'> },
|
|
53
115
|
{},
|
|
54
116
|
false,
|
|
55
117
|
{},
|
|
56
|
-
|
|
118
|
+
SlotsType<Partial<Field<T, 'slots'>>>
|
|
57
119
|
> {
|
|
58
120
|
// eslint-disable-next-line ts/no-unsafe-argument
|
|
59
|
-
const opts =
|
|
121
|
+
const opts = options_({} as any)
|
|
60
122
|
// eslint-disable-next-line ts/no-unsafe-return, ts/no-unsafe-argument
|
|
61
123
|
return defineComponent(opts.setup as any, {
|
|
62
124
|
props: opts.props as unknown as string[],
|
|
@@ -64,43 +126,6 @@ export function defineSetupComponent<
|
|
|
64
126
|
}) as any
|
|
65
127
|
}
|
|
66
128
|
|
|
67
|
-
export function props<
|
|
68
|
-
const Opts extends {
|
|
69
|
-
props: Record<string, any>
|
|
70
|
-
},
|
|
71
|
-
const RuntimeProps extends UnionToTuple<keyof AllUnionFields<Opts['props']>>,
|
|
72
|
-
// eslint-disable-next-line no-shadow
|
|
73
|
-
>(_opts: Opts, props: NoInfer<RuntimeProps>): NoInfer<RuntimeProps> {
|
|
74
|
-
return props
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function emits<
|
|
78
|
-
const Opts extends {
|
|
79
|
-
emits: ObjectEmitsOptions
|
|
80
|
-
},
|
|
81
|
-
const RuntimeEmits extends UnionToTuple<keyof AllUnionFields<Opts['emits']>>,
|
|
82
|
-
// eslint-disable-next-line no-shadow
|
|
83
|
-
>(_opts: Opts, emits: NoInfer<RuntimeEmits>): NoInfer<RuntimeEmits> {
|
|
84
|
-
return emits
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function setup<
|
|
88
|
-
const Opts extends {
|
|
89
|
-
props: Record<string, any>
|
|
90
|
-
emits: ObjectEmitsOptions
|
|
91
|
-
slots: Record<string, any>
|
|
92
|
-
},
|
|
93
|
-
Slots extends SlotsType<Partial<Opts['slots']>>,
|
|
94
|
-
Props extends Opts['props'] & EmitsToProps<Opts['emits']>,
|
|
95
|
-
Setup extends (
|
|
96
|
-
props: Props,
|
|
97
|
-
ctx: SetupContext<Opts['emits'], Slots>,
|
|
98
|
-
) => RenderFunction | Promise<RenderFunction>,
|
|
99
|
-
// eslint-disable-next-line no-shadow
|
|
100
|
-
>(_opts: Opts, setup: Setup): NoInfer<Setup> {
|
|
101
|
-
return setup
|
|
102
|
-
}
|
|
103
|
-
|
|
104
129
|
// https://github.com/vuejs/language-tools/blob/master/packages/component-type-helpers/index.ts
|
|
105
130
|
type ComponentSlots<T> = T extends new (...args: any) => { $slots: infer S }
|
|
106
131
|
? NonNullable<S>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@falcondev-oss/nuxt-layers-base",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.35.
|
|
4
|
+
"version": "0.35.4",
|
|
5
5
|
"description": "Nuxt layer with lots of useful helpers and @nuxt/ui components",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -12,16 +12,22 @@
|
|
|
12
12
|
"url": "https://github.com/falcondev-oss/nuxt-layers/issues"
|
|
13
13
|
},
|
|
14
14
|
"main": "./nuxt.config.ts",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
15
|
+
"devEngines": {
|
|
16
|
+
"runtime": {
|
|
17
|
+
"name": "node",
|
|
18
|
+
"version": "24"
|
|
19
|
+
},
|
|
20
|
+
"packageManager": {
|
|
21
|
+
"name": "pnpm",
|
|
22
|
+
"version": "11",
|
|
23
|
+
"onFail": "warn"
|
|
24
|
+
}
|
|
18
25
|
},
|
|
19
26
|
"peerDependencies": {
|
|
20
27
|
"@falcondev-oss/form-core": ">=0.23.5",
|
|
21
28
|
"@falcondev-oss/form-vue": ">=0.23.5",
|
|
22
29
|
"@internationalized/date": ">=3.12.1",
|
|
23
30
|
"@nuxt/ui": ">=4.9.0",
|
|
24
|
-
"reka-ui": ">=2.9.10",
|
|
25
31
|
"tailwind-variants": ">=3.2.2"
|
|
26
32
|
},
|
|
27
33
|
"dependencies": {
|
|
@@ -41,6 +47,7 @@
|
|
|
41
47
|
"consola": "^3.4.2",
|
|
42
48
|
"defu": "^6.1.7",
|
|
43
49
|
"maska": "^3.2.0",
|
|
50
|
+
"reka-ui": "~2.9.10",
|
|
44
51
|
"remeda": "^2.39.0",
|
|
45
52
|
"superjson": "^2.2.6",
|
|
46
53
|
"tailwindcss": "^4.3.1",
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// import type { VNode } from 'vue'
|
|
2
|
-
// import { UCard, UDropdownMenu, UField, UForm, UInput } from '#components'
|
|
3
|
-
// import { z } from 'zod'
|
|
4
|
-
|
|
5
|
-
/* eslint-disable ts/no-empty-object-type */
|
|
6
|
-
// @ts-expect-error FIXME: empty emits causes type error
|
|
7
|
-
export default defineSetupComponent((_: { props: { name: string }; emits: {}; slots: {} }) => ({
|
|
8
|
-
props: props(_, ['name']),
|
|
9
|
-
emits: emits(_, []),
|
|
10
|
-
setup: (props) => {
|
|
11
|
-
console.log(props)
|
|
12
|
-
// const form = useForm({
|
|
13
|
-
// schema: z.object({
|
|
14
|
-
// duration: z.number().meta({ title: 'Duration' }),
|
|
15
|
-
// dateIso: z.string().meta({ title: 'Datum' }),
|
|
16
|
-
// text: z
|
|
17
|
-
// .string()
|
|
18
|
-
// .length(8)
|
|
19
|
-
// // .max(8)
|
|
20
|
-
// .meta({
|
|
21
|
-
// title: 'Text',
|
|
22
|
-
// description: 'Beschreibung',
|
|
23
|
-
// default: 'Default Wert',
|
|
24
|
-
// examples: ['Hier könnte ein Beispieltext stehen', '123'],
|
|
25
|
-
// }),
|
|
26
|
-
// }),
|
|
27
|
-
// sourceValues: () => ({
|
|
28
|
-
// dateIso: null,
|
|
29
|
-
// duration: null,
|
|
30
|
-
// text: '',
|
|
31
|
-
// }),
|
|
32
|
-
// async submit({ values }) {
|
|
33
|
-
// await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
34
|
-
// console.log(values)
|
|
35
|
-
// },
|
|
36
|
-
// })
|
|
37
|
-
|
|
38
|
-
return () => (
|
|
39
|
-
// <UCard
|
|
40
|
-
// class="max-w-sm"
|
|
41
|
-
// ui={{
|
|
42
|
-
// body: 'flex flex-col gap-4 items-start ',
|
|
43
|
-
// }}
|
|
44
|
-
// >
|
|
45
|
-
// <UForm form={form} successToast={{ title: 'Success' }} class="flex flex-col gap-4">
|
|
46
|
-
// {form.data}
|
|
47
|
-
|
|
48
|
-
// {/* <UField
|
|
49
|
-
// field={form.fields.text.$use()}
|
|
50
|
-
// error-inline
|
|
51
|
-
// vSlots={vSlots(UField, {
|
|
52
|
-
// default({ bind }) {
|
|
53
|
-
// return <UInput class="w-full" {...bind} />
|
|
54
|
-
// },
|
|
55
|
-
// })}
|
|
56
|
-
// ></UField> */}
|
|
57
|
-
// </UForm>
|
|
58
|
-
// {/* <UForm
|
|
59
|
-
// :form
|
|
60
|
-
// :success-toast="{
|
|
61
|
-
// title: 'test',
|
|
62
|
-
// description: 'wow',
|
|
63
|
-
// }"
|
|
64
|
-
// class="flex flex-col gap-4"
|
|
65
|
-
// >
|
|
66
|
-
// {{ form.data }}
|
|
67
|
-
// <UField v-slot="{ bind, field }" :field="form.fields.text.$use()" error-inline>
|
|
68
|
-
// {{ field.schema }}
|
|
69
|
-
// <UInput class="w-full" v-bind="bind" />
|
|
70
|
-
// </UField>
|
|
71
|
-
// <UField
|
|
72
|
-
// v-slot="{ bind }"
|
|
73
|
-
// :field="
|
|
74
|
-
// form.fields.dateIso.$use({
|
|
75
|
-
// translate: dateValueIsoTranslator(),
|
|
76
|
-
// })
|
|
77
|
-
// "
|
|
78
|
-
// >
|
|
79
|
-
// <UInputDatePicker class="w-full" v-bind="bind" />
|
|
80
|
-
// </UField>
|
|
81
|
-
// <UField v-slot="{ bind }" :field="form.fields.duration.$use()">
|
|
82
|
-
// <UInputDurationMinutes class="w-full" v-bind="bind" />
|
|
83
|
-
// </UField>
|
|
84
|
-
// </UForm> */}
|
|
85
|
-
// </UCard>
|
|
86
|
-
<></>
|
|
87
|
-
)
|
|
88
|
-
},
|
|
89
|
-
}))
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { UButton, UCard } from '#components'
|
|
2
|
-
import { setup } from '../../../app/utils/define-setup-component'
|
|
3
|
-
|
|
4
|
-
export type ListItems = { label: string; value: string }[]
|
|
5
|
-
|
|
6
|
-
export default defineSetupComponent(
|
|
7
|
-
<T extends ListItems>(_: {
|
|
8
|
-
props: {
|
|
9
|
-
items: T
|
|
10
|
-
}
|
|
11
|
-
emits: {
|
|
12
|
-
choose: (value: T[number]) => void
|
|
13
|
-
}
|
|
14
|
-
slots: {
|
|
15
|
-
selected: (props: { item: T[number] }) => any
|
|
16
|
-
}
|
|
17
|
-
}) => ({
|
|
18
|
-
props: props(_, ['items']),
|
|
19
|
-
// props: ['items'],
|
|
20
|
-
emits: emits(_, ['choose']),
|
|
21
|
-
// emits: ['choose'],
|
|
22
|
-
setup: setup(_, (props, { emit, slots }) => {
|
|
23
|
-
const selected = ref<T[number]>()
|
|
24
|
-
|
|
25
|
-
return () => (
|
|
26
|
-
<UCard
|
|
27
|
-
class="w-fit"
|
|
28
|
-
vSlots={vSlots(UCard, {
|
|
29
|
-
header: () => [<h1>Select an item</h1>],
|
|
30
|
-
})}
|
|
31
|
-
>
|
|
32
|
-
<div class="flex flex-col gap-2">
|
|
33
|
-
{props.items.map((item) => (
|
|
34
|
-
<UButton
|
|
35
|
-
variant="subtle"
|
|
36
|
-
key={item.value}
|
|
37
|
-
class="rounded bg-gray-200 px-4 py-2 hover:bg-gray-300"
|
|
38
|
-
onClick={() => {
|
|
39
|
-
selected.value = item
|
|
40
|
-
emit('choose', item)
|
|
41
|
-
}}
|
|
42
|
-
vSlots={vSlots(UButton, {
|
|
43
|
-
leading: () => [<>{`[${selected.value?.value === item.value ? 'x' : ' '}] `}</>],
|
|
44
|
-
})}
|
|
45
|
-
>
|
|
46
|
-
{item.label}
|
|
47
|
-
</UButton>
|
|
48
|
-
))}
|
|
49
|
-
|
|
50
|
-
{slots.selected && selected.value ? (
|
|
51
|
-
<div class="mt-4">{slots.selected({ item: selected.value })}</div>
|
|
52
|
-
) : null}
|
|
53
|
-
</div>
|
|
54
|
-
</UCard>
|
|
55
|
-
)
|
|
56
|
-
}),
|
|
57
|
-
}),
|
|
58
|
-
)
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { LazyOverlayModalActions } from '#components'
|
|
3
|
-
import z from 'zod'
|
|
4
|
-
|
|
5
|
-
const confirm = useConfirm()
|
|
6
|
-
const overlay = useOverlay()
|
|
7
|
-
|
|
8
|
-
const form = useForm({
|
|
9
|
-
schema: z.object({
|
|
10
|
-
duration: z.number().meta({ title: 'Duration' }),
|
|
11
|
-
dateIso: z.string().meta({ title: 'Datum' }),
|
|
12
|
-
text: z
|
|
13
|
-
.string()
|
|
14
|
-
.length(8)
|
|
15
|
-
// .max(8)
|
|
16
|
-
.meta({
|
|
17
|
-
title: 'Text',
|
|
18
|
-
description: 'Beschreibung',
|
|
19
|
-
default: 'Default Wert',
|
|
20
|
-
examples: ['Hier könnte ein Beispieltext stehen', '123'],
|
|
21
|
-
}),
|
|
22
|
-
}),
|
|
23
|
-
sourceValues: () => ({
|
|
24
|
-
dateIso: null,
|
|
25
|
-
duration: null,
|
|
26
|
-
text: '',
|
|
27
|
-
}),
|
|
28
|
-
async submit({ values }) {
|
|
29
|
-
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
30
|
-
console.log(values)
|
|
31
|
-
},
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
const data = ref([
|
|
35
|
-
{
|
|
36
|
-
hey: '',
|
|
37
|
-
ho: 1,
|
|
38
|
-
},
|
|
39
|
-
])
|
|
40
|
-
|
|
41
|
-
const columns = useTableColumns<typeof data>(
|
|
42
|
-
() => [
|
|
43
|
-
{
|
|
44
|
-
accessorKey: 'hey',
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
accessorKey: 'ho',
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
{
|
|
51
|
-
headerActions: [
|
|
52
|
-
{
|
|
53
|
-
label: 'Add Row',
|
|
54
|
-
onClick: () => {
|
|
55
|
-
data.value.push({ hey: 'new', ho: data.value.length + 1 })
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
label: 'Add Row 2',
|
|
60
|
-
onClick: () => {
|
|
61
|
-
data.value.push({ hey: 'new', ho: data.value.length + 1 })
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
onDelete(row) {
|
|
66
|
-
data.value = data.value.filter((_, i) => i !== row.index)
|
|
67
|
-
},
|
|
68
|
-
rowActions: [
|
|
69
|
-
{
|
|
70
|
-
icon: 'lucide:pencil',
|
|
71
|
-
onClick: () => {
|
|
72
|
-
console.log('Edit row')
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
},
|
|
77
|
-
)
|
|
78
|
-
</script>
|
|
79
|
-
|
|
80
|
-
<template>
|
|
81
|
-
<LayoutSidebar
|
|
82
|
-
:items="[
|
|
83
|
-
{
|
|
84
|
-
label: 'Home',
|
|
85
|
-
icon: 'i-lucide-house',
|
|
86
|
-
active: true,
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
label: 'Inbox',
|
|
90
|
-
icon: 'i-lucide-inbox',
|
|
91
|
-
badge: '4',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
label: 'Contacts',
|
|
95
|
-
icon: 'i-lucide-users',
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
label: 'Settings',
|
|
99
|
-
icon: 'i-lucide-settings',
|
|
100
|
-
defaultOpen: true,
|
|
101
|
-
children: [
|
|
102
|
-
{
|
|
103
|
-
label: 'General',
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
label: 'Members',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
label: 'Notifications',
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
},
|
|
113
|
-
]"
|
|
114
|
-
:bottom-items="[
|
|
115
|
-
{
|
|
116
|
-
label: 'Home',
|
|
117
|
-
icon: 'i-lucide-house',
|
|
118
|
-
active: true,
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
label: 'Inbox',
|
|
122
|
-
icon: 'i-lucide-inbox',
|
|
123
|
-
badge: '4',
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
label: 'Contacts',
|
|
127
|
-
icon: 'i-lucide-users',
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
label: 'Settings',
|
|
131
|
-
icon: 'i-lucide-settings',
|
|
132
|
-
defaultOpen: true,
|
|
133
|
-
children: [
|
|
134
|
-
{
|
|
135
|
-
label: 'General',
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
label: 'Members',
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
label: 'Notifications',
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
},
|
|
145
|
-
]"
|
|
146
|
-
:user-menu="{
|
|
147
|
-
name: 'Benjamin Canac',
|
|
148
|
-
// avatar: { src: 'https://github.com/benjamincanac.png' },
|
|
149
|
-
items: [
|
|
150
|
-
{
|
|
151
|
-
icon: 'lucide:log-out',
|
|
152
|
-
label: 'Logout',
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
}"
|
|
156
|
-
>
|
|
157
|
-
<LayoutNavbar
|
|
158
|
-
:navbar="{
|
|
159
|
-
title: 'Dashboard',
|
|
160
|
-
ui: {
|
|
161
|
-
root: 'relative',
|
|
162
|
-
title: 'flex-1 absolute inset-0 w-full',
|
|
163
|
-
},
|
|
164
|
-
}"
|
|
165
|
-
:toolbar="{
|
|
166
|
-
items: [
|
|
167
|
-
{
|
|
168
|
-
label: 'General',
|
|
169
|
-
icon: 'i-lucide-user',
|
|
170
|
-
active: true,
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
label: 'Members',
|
|
174
|
-
icon: 'i-lucide-users',
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
label: 'Notifications',
|
|
178
|
-
icon: 'i-lucide-bell',
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
itemsEnd: [
|
|
182
|
-
{
|
|
183
|
-
label: 'General',
|
|
184
|
-
icon: 'i-lucide-user',
|
|
185
|
-
active: true,
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
label: 'Members',
|
|
189
|
-
icon: 'i-lucide-users',
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
label: 'Notifications',
|
|
193
|
-
icon: 'i-lucide-bell',
|
|
194
|
-
},
|
|
195
|
-
],
|
|
196
|
-
}"
|
|
197
|
-
>
|
|
198
|
-
<template #navbar-title>
|
|
199
|
-
<div class="w-full text-center">title</div>
|
|
200
|
-
</template>
|
|
201
|
-
|
|
202
|
-
<UTableCard>
|
|
203
|
-
<UTable :data :columns @select="() => {}" />
|
|
204
|
-
</UTableCard>
|
|
205
|
-
<UCard
|
|
206
|
-
:ui="{
|
|
207
|
-
body: 'flex flex-col gap-4 items-start',
|
|
208
|
-
}"
|
|
209
|
-
>
|
|
210
|
-
<UButton
|
|
211
|
-
label="Confirm"
|
|
212
|
-
variant="subtle"
|
|
213
|
-
@click="
|
|
214
|
-
() => {
|
|
215
|
-
confirm.confirmDestructive({
|
|
216
|
-
title: 'Are you sure?',
|
|
217
|
-
description: 'This action cannot be undone.',
|
|
218
|
-
submitLabel: 'Yes, delete it',
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
"
|
|
222
|
-
/>
|
|
223
|
-
<UButton
|
|
224
|
-
label="Actions"
|
|
225
|
-
variant="subtle"
|
|
226
|
-
@click="
|
|
227
|
-
() => {
|
|
228
|
-
overlay.create(LazyOverlayModalActions, {
|
|
229
|
-
defaultOpen: true,
|
|
230
|
-
props: {
|
|
231
|
-
title: 'Actions',
|
|
232
|
-
description: 'Choose an action to perform',
|
|
233
|
-
actions: [
|
|
234
|
-
{
|
|
235
|
-
label: 'Action 1',
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
label: 'Action 2',
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
},
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
"
|
|
245
|
-
/>
|
|
246
|
-
</UCard>
|
|
247
|
-
<UCard
|
|
248
|
-
class="max-w-sm"
|
|
249
|
-
:ui="{
|
|
250
|
-
body: 'flex flex-col gap-4 items-start ',
|
|
251
|
-
}"
|
|
252
|
-
>
|
|
253
|
-
<UForm
|
|
254
|
-
:form
|
|
255
|
-
:success-toast="{
|
|
256
|
-
title: 'test',
|
|
257
|
-
description: 'wow',
|
|
258
|
-
}"
|
|
259
|
-
class="flex flex-col gap-4"
|
|
260
|
-
>
|
|
261
|
-
{{ form.data }}
|
|
262
|
-
<UField v-slot="{ bind, field }" :field="form.fields.text.$use()" error-inline>
|
|
263
|
-
{{ field.schema }}
|
|
264
|
-
<UInput class="w-full" v-bind="bind" />
|
|
265
|
-
</UField>
|
|
266
|
-
<UField
|
|
267
|
-
v-slot="{ bind }"
|
|
268
|
-
:field="
|
|
269
|
-
form.fields.dateIso.$use({
|
|
270
|
-
translate: dateValueIsoTranslator(),
|
|
271
|
-
})
|
|
272
|
-
"
|
|
273
|
-
>
|
|
274
|
-
<UInputDatePicker class="w-full" v-bind="bind" />
|
|
275
|
-
</UField>
|
|
276
|
-
<UField v-slot="{ bind }" :field="form.fields.duration.$use()">
|
|
277
|
-
<UInputDurationMinutes class="w-full" v-bind="bind" />
|
|
278
|
-
</UField>
|
|
279
|
-
</UForm>
|
|
280
|
-
</UCard>
|
|
281
|
-
<Select
|
|
282
|
-
:items="[
|
|
283
|
-
{ label: 'One', value: '1' },
|
|
284
|
-
{ label: 'Two', value: '2' },
|
|
285
|
-
]"
|
|
286
|
-
@choose="console.warn"
|
|
287
|
-
>
|
|
288
|
-
<template #selected="{ item }">
|
|
289
|
-
<div class="flex items-center gap-2">
|
|
290
|
-
<span>Selected:</span>
|
|
291
|
-
<span>{{ item.label }}</span>
|
|
292
|
-
</div>
|
|
293
|
-
</template>
|
|
294
|
-
</Select>
|
|
295
|
-
</LayoutNavbar>
|
|
296
|
-
</LayoutSidebar>
|
|
297
|
-
</template>
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { AuthFormField } from '@nuxt/ui'
|
|
3
|
-
|
|
4
|
-
const fields: AuthFormField[] = [
|
|
5
|
-
{
|
|
6
|
-
name: 'email',
|
|
7
|
-
type: 'email',
|
|
8
|
-
label: 'Email',
|
|
9
|
-
placeholder: 'Enter your email',
|
|
10
|
-
required: true,
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
name: 'password',
|
|
14
|
-
label: 'Password',
|
|
15
|
-
type: 'password',
|
|
16
|
-
placeholder: 'Enter your password',
|
|
17
|
-
required: true,
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: 'remember',
|
|
21
|
-
label: 'Remember me',
|
|
22
|
-
type: 'checkbox',
|
|
23
|
-
},
|
|
24
|
-
]
|
|
25
|
-
</script>
|
|
26
|
-
|
|
27
|
-
<template>
|
|
28
|
-
<UAuthForm :fields />
|
|
29
|
-
</template>
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<LayoutPage
|
|
3
|
-
:header="{
|
|
4
|
-
navigation: {
|
|
5
|
-
variant: 'pill',
|
|
6
|
-
items: [
|
|
7
|
-
{
|
|
8
|
-
label: 'test',
|
|
9
|
-
to: '/',
|
|
10
|
-
},
|
|
11
|
-
],
|
|
12
|
-
},
|
|
13
|
-
}"
|
|
14
|
-
:footer="{
|
|
15
|
-
items: [
|
|
16
|
-
{
|
|
17
|
-
label: 'hallo',
|
|
18
|
-
to: '/',
|
|
19
|
-
},
|
|
20
|
-
],
|
|
21
|
-
ui: {
|
|
22
|
-
root: 'bg-primary-200',
|
|
23
|
-
},
|
|
24
|
-
}"
|
|
25
|
-
>
|
|
26
|
-
<UContainer>test</UContainer>
|
|
27
|
-
|
|
28
|
-
<template #footer-left>left</template>
|
|
29
|
-
<template #footer-bottom>bottom</template>
|
|
30
|
-
</LayoutPage>
|
|
31
|
-
</template>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default defineNuxtPlugin(vueQueryPlugin())
|
package/.playground/app.vue
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { de } from '@nuxt/ui/locale'
|
|
3
|
-
import { Settings } from 'luxon'
|
|
4
|
-
|
|
5
|
-
Settings.throwOnInvalid = true
|
|
6
|
-
Settings.defaultLocale = 'de'
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
|
-
<template>
|
|
10
|
-
<UCustomApp
|
|
11
|
-
:app="{
|
|
12
|
-
locale: de,
|
|
13
|
-
}"
|
|
14
|
-
/>
|
|
15
|
-
</template>
|
package/eslint.config.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
import eslintConfig from '@falcondev-oss/configs/eslint'
|
|
3
|
-
|
|
4
|
-
export default eslintConfig({
|
|
5
|
-
tsconfigPath: './tsconfig.json',
|
|
6
|
-
nuxt: true,
|
|
7
|
-
}).append({
|
|
8
|
-
ignores: [
|
|
9
|
-
'node_modules/',
|
|
10
|
-
'dist/',
|
|
11
|
-
'.nuxt/',
|
|
12
|
-
'.nitro/',
|
|
13
|
-
'.output/',
|
|
14
|
-
'.temp/',
|
|
15
|
-
'.data/',
|
|
16
|
-
'pnpm-lock.yaml',
|
|
17
|
-
],
|
|
18
|
-
})
|