@falcondev-oss/nuxt-layers-base 0.33.2 → 0.34.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.
@@ -0,0 +1,100 @@
1
+ import type { VNode } from 'vue'
2
+ import { UCard, UDropdownMenu, UField, UForm, UInput } from '#components'
3
+ import { Component } from 'vue'
4
+ import { z } from 'zod'
5
+
6
+ // https://github.com/vuejs/language-tools/blob/master/packages/component-type-helpers/index.ts
7
+ type ComponentSlots<T> = T extends new (...args: any) => { $slots: infer S }
8
+ ? NonNullable<S>
9
+ : T extends (props: any, ctx: { slots: infer S; attrs: any; emit: any }, ...args: any) => any
10
+ ? NonNullable<S>
11
+ : {}
12
+
13
+ type ComponentProps<T> = T extends new (...args: any) => { $props: infer P }
14
+ ? NonNullable<P>
15
+ : T extends (props: infer P, ...args: any) => any
16
+ ? P
17
+ : {}
18
+
19
+ /* eslint-disable ts/no-empty-object-type */
20
+ export default defineSetupComponent((_: { props: {}; emits: {}; slots: {} }) => ({
21
+ props: [],
22
+ emits: [],
23
+ setup: () => {
24
+ const form = useForm({
25
+ schema: z.object({
26
+ duration: z.number().meta({ title: 'Duration' }),
27
+ dateIso: z.string().meta({ title: 'Datum' }),
28
+ text: z
29
+ .string()
30
+ .length(8)
31
+ // .max(8)
32
+ .meta({
33
+ title: 'Text',
34
+ description: 'Beschreibung',
35
+ default: 'Default Wert',
36
+ examples: ['Hier könnte ein Beispieltext stehen', '123'],
37
+ }),
38
+ }),
39
+ sourceValues: () => ({
40
+ dateIso: null,
41
+ duration: null,
42
+ text: '',
43
+ }),
44
+ async submit({ values }) {
45
+ await new Promise((resolve) => setTimeout(resolve, 2000))
46
+ console.log(values)
47
+ },
48
+ })
49
+
50
+ return () => (
51
+ <UCard
52
+ class="max-w-sm"
53
+ ui={{
54
+ body: 'flex flex-col gap-4 items-start ',
55
+ }}
56
+ >
57
+ <UForm form={form} successToast={{ title: 'Success' }} class="flex flex-col gap-4">
58
+ {form.data}
59
+
60
+ {/* <UField
61
+ field={form.fields.text.$use()}
62
+ error-inline
63
+ vSlots={vSlots(UField, {
64
+ default({ bind }) {
65
+ return <UInput class="w-full" {...bind} />
66
+ },
67
+ })}
68
+ ></UField> */}
69
+ </UForm>
70
+ {/* <UForm
71
+ :form
72
+ :success-toast="{
73
+ title: 'test',
74
+ description: 'wow',
75
+ }"
76
+ class="flex flex-col gap-4"
77
+ >
78
+ {{ form.data }}
79
+ <UField v-slot="{ bind, field }" :field="form.fields.text.$use()" error-inline>
80
+ {{ field.schema }}
81
+ <UInput class="w-full" v-bind="bind" />
82
+ </UField>
83
+ <UField
84
+ v-slot="{ bind }"
85
+ :field="
86
+ form.fields.dateIso.$use({
87
+ translate: dateValueIsoTranslator(),
88
+ })
89
+ "
90
+ >
91
+ <UInputDatePicker class="w-full" v-bind="bind" />
92
+ </UField>
93
+ <UField v-slot="{ bind }" :field="form.fields.duration.$use()">
94
+ <UInputDurationMinutes class="w-full" v-bind="bind" />
95
+ </UField>
96
+ </UForm> */}
97
+ </UCard>
98
+ )
99
+ },
100
+ }))
@@ -0,0 +1,57 @@
1
+ import { UButton, UCard } from '#components'
2
+
3
+ export type ListItems = { label: string; value: string }[]
4
+
5
+ export default defineSetupComponent(
6
+ <T extends ListItems>(_: {
7
+ props: {
8
+ items: T
9
+ }
10
+ emits: {
11
+ choose: (value: T[number]) => void
12
+ }
13
+ slots: {
14
+ selected: (props: { item: T[number] }) => any
15
+ }
16
+ }) => ({
17
+ props: props(_, ['items']),
18
+ // props: ['items'],
19
+ emits: emits(_, ['choose']),
20
+ // emits: ['choose'],
21
+ setup: generic(_, (props, { emit, slots }) => {
22
+ const selected = ref<T[number]>()
23
+
24
+ return () => (
25
+ <UCard
26
+ class="w-fit"
27
+ vSlots={vSlots(UCard, {
28
+ header: () => <h1>Select an item</h1>,
29
+ })}
30
+ >
31
+ <div class="flex flex-col gap-2">
32
+ {props.items.map((item) => (
33
+ <UButton
34
+ variant="subtle"
35
+ key={item.value}
36
+ class="rounded bg-gray-200 px-4 py-2 hover:bg-gray-300"
37
+ onClick={() => {
38
+ selected.value = item
39
+ emit('choose', item)
40
+ }}
41
+ vSlots={vSlots(UButton, {
42
+ leading: () => `[${selected.value?.value === item.value ? 'x' : ' '}] `,
43
+ })}
44
+ >
45
+ {item.label}
46
+ </UButton>
47
+ ))}
48
+
49
+ {slots.selected && selected.value ? (
50
+ <div class="mt-4">{slots.selected({ item: selected.value })}</div>
51
+ ) : null}
52
+ </div>
53
+ </UCard>
54
+ )
55
+ }),
56
+ }),
57
+ )
@@ -278,6 +278,20 @@ const columns = useTableColumns<typeof data>(
278
278
  </UField>
279
279
  </UForm>
280
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>
281
295
  </LayoutNavbar>
282
296
  </LayoutSidebar>
283
297
  </template>
@@ -20,6 +20,7 @@ defineProps<{
20
20
  navigation?: NavigationMenuProps
21
21
  actions?: ButtonProps[]
22
22
  ui?: HeaderProps['ui']
23
+ mobileActions?: ButtonProps[]
23
24
  }
24
25
  footer?: {
25
26
  items?: NavigationMenuItem[]
@@ -96,6 +97,14 @@ const footerSlots = computed(() =>
96
97
 
97
98
  <template v-if="header.actions || slots['header-right']" #right>
98
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
+ />
99
108
  <UActions
100
109
  v-if="header.actions"
101
110
  class="max-sm:hidden"
@@ -0,0 +1,55 @@
1
+ /* eslint-disable ts/no-empty-object-type */
2
+ import type { AllUnionFields, UnionToTuple } from 'type-fest'
3
+ import type {
4
+ ComponentOptionsMixin,
5
+ CreateComponentPublicInstanceWithMixins,
6
+ EmitsOptions,
7
+ EmitsToProps,
8
+ PublicProps,
9
+ RenderFunction,
10
+ SetupContext,
11
+ SlotsType,
12
+ } from 'vue'
13
+
14
+ export function defineSetupComponentOld<
15
+ const Opts extends {
16
+ props: Record<string, any>
17
+ emits: Record<string, any>
18
+ slots: Record<string, any>
19
+ },
20
+ Setup extends (
21
+ props: Props,
22
+ ctx: SetupContext<Opts['emits'], SlotsType<Partial<Opts['slots']>>>,
23
+ ) => RenderFunction | Promise<RenderFunction>,
24
+ const PropsRuntime extends Readonly<UnionToTuple<keyof AllUnionFields<Opts['props']>>>,
25
+ E extends EmitsOptions = Opts['emits'],
26
+ Props extends Record<string, any> = Opts['props'] & EmitsToProps<E>,
27
+ PP = PublicProps & { vSlots?: Opts['slots'] },
28
+ S extends SlotsType<Partial<Opts['slots']>> = SlotsType<Partial<Opts['slots']>>,
29
+ >(
30
+ define: (opts: Opts) => { props: PropsRuntime },
31
+ setup: Setup,
32
+ ): new (
33
+ props: Opts['props'],
34
+ ) => CreateComponentPublicInstanceWithMixins<
35
+ Props,
36
+ {},
37
+ {},
38
+ {},
39
+ {},
40
+ ComponentOptionsMixin,
41
+ ComponentOptionsMixin,
42
+ E,
43
+ PP,
44
+ {},
45
+ false,
46
+ {},
47
+ S
48
+ > {
49
+ // eslint-disable-next-line ts/no-unsafe-argument
50
+ const opts = define({} as any)
51
+ // eslint-disable-next-line ts/no-unsafe-return
52
+ return defineComponent(setup, {
53
+ props: opts.props as unknown as string[],
54
+ }) as any
55
+ }
@@ -0,0 +1,112 @@
1
+ /* eslint-disable ts/no-empty-object-type */
2
+ import type { AllUnionFields, Schema, UnionToTuple } from 'type-fest'
3
+ import type {
4
+ ComponentOptionsMixin,
5
+ CreateComponentPublicInstanceWithMixins,
6
+ EmitsToProps,
7
+ ObjectEmitsOptions,
8
+ PublicProps,
9
+ RenderFunction,
10
+ SetupContext,
11
+ Slots as SlotOptions,
12
+ SlotsType,
13
+ } from 'vue'
14
+
15
+ declare module 'vue' {
16
+ interface ComponentCustomProps {
17
+ vSlots?: SlotOptions
18
+ }
19
+ }
20
+
21
+ export function defineSetupComponent<
22
+ const Opts extends {
23
+ props: Record<string, any>
24
+ emits: ObjectEmitsOptions
25
+ slots: Record<string, any>
26
+ },
27
+ Slots extends SlotsType<Partial<Opts['slots']>>,
28
+ Props extends Opts['props'] & EmitsToProps<Opts['emits']>,
29
+ Setup extends (
30
+ props: Props,
31
+ ctx: SetupContext<Opts['emits'], Slots>,
32
+ ) => RenderFunction | Promise<RenderFunction>,
33
+ const RuntimeProps extends Readonly<UnionToTuple<keyof AllUnionFields<Opts['props']>>>,
34
+ const RuntimeEmits extends Readonly<UnionToTuple<keyof AllUnionFields<Opts['emits']>>>,
35
+ >(
36
+ define: (opts: Opts) => {
37
+ props: NoInfer<RuntimeProps>
38
+ emits: NoInfer<RuntimeEmits>
39
+ setup: NoInfer<Setup>
40
+ },
41
+ ): new (
42
+ props: Opts['props'],
43
+ ) => CreateComponentPublicInstanceWithMixins<
44
+ Props,
45
+ {},
46
+ {},
47
+ {},
48
+ {},
49
+ ComponentOptionsMixin,
50
+ ComponentOptionsMixin,
51
+ Opts['emits'],
52
+ PublicProps & { vSlots?: Opts['slots'] },
53
+ {},
54
+ false,
55
+ {},
56
+ Slots
57
+ > {
58
+ // eslint-disable-next-line ts/no-unsafe-argument
59
+ const opts = define({} as any)
60
+ // eslint-disable-next-line ts/no-unsafe-return, ts/no-unsafe-argument
61
+ return defineComponent(opts.setup as any, {
62
+ props: opts.props as unknown as string[],
63
+ emits: opts.emits as unknown as string[],
64
+ }) as any
65
+ }
66
+
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 generic<
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
+ >(_opts: Opts, setup: Setup): NoInfer<Setup> {
100
+ return setup
101
+ }
102
+
103
+ // https://github.com/vuejs/language-tools/blob/master/packages/component-type-helpers/index.ts
104
+ type ComponentSlots<T> = T extends new (...args: any) => { $slots: infer S }
105
+ ? NonNullable<S>
106
+ : T extends (props: any, ctx: { slots: infer S; attrs: any; emit: any }, ...args: any) => any
107
+ ? NonNullable<S>
108
+ : {}
109
+
110
+ export function vSlots<C>(component: C, slots: ComponentSlots<C>) {
111
+ return slots
112
+ }
package/eslint.config.js CHANGED
@@ -13,10 +13,6 @@ export default eslintConfig({
13
13
  '.output/',
14
14
  '.temp/',
15
15
  '.data/',
16
- 'drizzle/',
17
- 'prisma/generated/',
18
- 'convex/_generated/',
19
16
  'pnpm-lock.yaml',
20
- '.playground/',
21
17
  ],
22
18
  })
package/nuxt.config.ts CHANGED
@@ -38,6 +38,7 @@ export default defineNuxtConfig({
38
38
  noImplicitOverride: true,
39
39
  noUncheckedIndexedAccess: true,
40
40
  },
41
+ exclude: ['.playground/'],
41
42
  },
42
43
  strict: true,
43
44
  },
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.33.2",
4
+ "version": "0.34.0",
5
5
  "description": "Nuxt layer with lots of useful helpers and @nuxt/ui components",
6
6
  "license": "MIT",
7
7
  "repository": "github:falcondev-oss/nuxt-layers",
@@ -11,7 +11,7 @@
11
11
  "main": "./nuxt.config.ts",
12
12
  "engines": {
13
13
  "node": "24",
14
- "pnpm": "10"
14
+ "pnpm": "11"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "@falcondev-oss/form-core": ">=0.22.4",
@@ -21,11 +21,11 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@falcondev-oss/trpc-typed-form-data": "^0.4.3",
24
- "@falcondev-oss/trpc-vue-query": "^0.5.3",
25
- "@iconify-json/lucide": "^1.2.101",
24
+ "@falcondev-oss/trpc-vue-query": "^0.5.4",
25
+ "@iconify-json/lucide": "^1.2.103",
26
26
  "@nuxt/icon": "^2.2.1",
27
27
  "@nuxtjs/color-mode": "^4.0.0",
28
- "@tanstack/vue-query": "^5.97.0",
28
+ "@tanstack/vue-query": "^5.100.1",
29
29
  "@trpc/client": "^11.16.0",
30
30
  "@trpc/server": "^11.16.0",
31
31
  "@vue/devtools-api": "^8.1.1",
@@ -36,19 +36,19 @@
36
36
  "consola": "^3.4.2",
37
37
  "defu": "^6.1.7",
38
38
  "maska": "^3.2.0",
39
- "reka-ui": "^2.9.5",
39
+ "reka-ui": "^2.9.6",
40
40
  "remeda": "^2.33.7",
41
41
  "superjson": "^2.2.6",
42
- "tailwindcss": "^4.2.2",
42
+ "tailwindcss": "^4.2.4",
43
43
  "trpc-nuxt": "^2.0.2",
44
- "type-fest": "^5.5.0",
45
- "vue": "^3.5.32",
46
- "vue-router": "^5.0.4"
44
+ "type-fest": "^5.6.0",
45
+ "vue": "^3.5.33",
46
+ "vue-router": "^5.0.6"
47
47
  },
48
48
  "devDependencies": {
49
49
  "nuxt": "^4.4.2",
50
50
  "typescript": "^5.9.3",
51
- "vue-tsc": "^3.2.6",
51
+ "vue-tsc": "^3.2.7",
52
52
  "zod": "^4.3.6"
53
53
  },
54
54
  "scripts": {