Skip to content

Commit 591d59f

Browse files
committed
fix(theme): improve app config types for ui object
Resolves #3579
1 parent caa3bf9 commit 591d59f

File tree

4 files changed

+77
-73
lines changed

4 files changed

+77
-73
lines changed

src/runtime/types/tv.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { ClassValue, TVVariants, TVCompoundVariants, TVDefaultVariants } from 'tailwind-variants'
2+
3+
/**
4+
* Defines the AppConfig object based on the tailwind-variants configuration.
5+
*/
6+
export type TVConfig<T extends Record<string, any>> = {
7+
[P in keyof T]?: {
8+
[K in keyof T[P]as K extends 'base' | 'slots' | 'variants' | 'compoundVariants' | 'defaultVariants' ? K : never]?: K extends 'base' ? ClassValue
9+
: K extends 'slots' ? {
10+
[S in keyof T[P]['slots']]?: ClassValue
11+
}
12+
: K extends 'variants' ? TVVariants<T[P]['slots'], ClassValue, T[P]['variants']>
13+
: K extends 'compoundVariants' ? TVCompoundVariants<T[P]['variants'], T[P]['slots'], ClassValue, object, undefined>
14+
: K extends 'defaultVariants' ? TVDefaultVariants<T[P]['variants'], T[P]['slots'], object, undefined>
15+
: never
16+
}
17+
}
18+
19+
/**
20+
* Utility type to flatten intersection types for better IDE hover information.
21+
* @template T The type to flatten.
22+
*/
23+
type Id<T> = {} & { [P in keyof T]: T[P] }
24+
25+
type ComponentVariants<T extends { variants?: Record<string, Record<string, any>> }> = {
26+
[K in keyof T['variants']]: keyof T['variants'][K]
27+
}
28+
29+
type ComponentSlots<T extends { slots?: Record<string, any> }> = Id<{
30+
[K in keyof T['slots']]?: ClassValue
31+
}>
32+
33+
type GetComponentAppConfig<A, U extends string, K extends string> =
34+
A extends Record<U, Record<K, any>> ? A[U][K] : {}
35+
36+
type ComponentAppConfig<
37+
T,
38+
A extends Record<string, any>,
39+
K extends string,
40+
U extends string = 'ui' | 'uiPro' | 'uiPro.prose'
41+
> = A & (
42+
U extends 'uiPro.prose'
43+
? { uiPro?: { prose?: { [k in K]?: Partial<T> } } }
44+
: { [key in Exclude<U, 'uiPro.prose'>]?: { [k in K]?: Partial<T> } }
45+
)
46+
47+
/**
48+
* Defines the configuration shape expected for a component.
49+
* @template T The component's theme imported from `#build/ui/*`.
50+
* @template A The base AppConfig type from `@nuxt/schema`.
51+
* @template K The key identifying the component (e.g., 'badge').
52+
* @template U The top-level key in AppConfig ('ui' or 'uiPro').
53+
*/
54+
export type ComponentConfig<
55+
T extends Record<string, any>,
56+
A extends Record<string, any>,
57+
K extends string,
58+
U extends 'ui' | 'uiPro' | 'uiPro.prose' = 'ui'
59+
> = {
60+
AppConfig: ComponentAppConfig<T, A, K, U>
61+
variants: ComponentVariants<T & GetComponentAppConfig<A, U, K>>
62+
slots: ComponentSlots<T>
63+
}

src/runtime/types/utils.ts

Lines changed: 9 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
1-
import type { AcceptableValue as _AcceptableValue } from 'reka-ui'
2-
import type { ClassValue } from 'tailwind-variants'
31
import type { VNode } from 'vue'
4-
5-
export interface TightMap<O = any> {
6-
[key: string]: TightMap | O
7-
}
8-
9-
export type DeepPartial<T, O = any> = {
10-
[P in keyof T]?: T[P] extends Array<string>
11-
? string
12-
: T[P] extends object
13-
? DeepPartial<T[P], O>
14-
: T[P];
15-
} & {
16-
[key: string]: O | TightMap<O>
17-
}
2+
import type { AcceptableValue as _AcceptableValue } from 'reka-ui'
183

194
export type DynamicSlotsKeys<Name extends string | undefined, Suffix extends string | undefined = undefined> = (
205
Name extends string
@@ -56,13 +41,13 @@ export type MergeTypes<T extends object> = {
5641
export type GetItemKeys<I> = keyof Extract<NestedItem<I>, object>
5742

5843
export type GetItemValue<I, VK extends GetItemKeys<I> | undefined, T extends NestedItem<I> = NestedItem<I>> =
59-
T extends object
60-
? VK extends undefined
61-
? T
62-
: VK extends keyof T
63-
? T[VK]
64-
: never
65-
: T
44+
T extends object
45+
? VK extends undefined
46+
? T
47+
: VK extends keyof T
48+
? T[VK]
49+
: never
50+
: T
6651

6752
export type GetModelValue<
6853
T,
@@ -92,48 +77,4 @@ export type EmitsToProps<T> = {
9277
: never
9378
}
9479

95-
/**
96-
* Utility type to flatten intersection types for better IDE hover information.
97-
* @template T The type to flatten.
98-
*/
99-
type Id<T> = {} & { [P in keyof T]: T[P] }
100-
101-
type ComponentVariants<T extends { variants?: Record<string, Record<string, any>> }> = {
102-
[K in keyof T['variants']]: keyof T['variants'][K]
103-
}
104-
105-
type ComponentSlots<T extends { slots?: Record<string, any> }> = Id<{
106-
[K in keyof T['slots']]?: ClassValue
107-
}>
108-
109-
type GetComponentAppConfig<A, U extends string, K extends string> =
110-
A extends Record<U, Record<K, any>> ? A[U][K] : {}
111-
112-
type ComponentAppConfig<
113-
T,
114-
A extends Record<string, any>,
115-
K extends string,
116-
U extends string = 'ui' | 'uiPro' | 'uiPro.prose'
117-
> = A & (
118-
U extends 'uiPro.prose'
119-
? { uiPro?: { prose?: { [k in K]?: Partial<T> } } }
120-
: { [key in Exclude<U, 'uiPro.prose'>]?: { [k in K]?: Partial<T> } }
121-
)
122-
123-
/**
124-
* Defines the configuration shape expected for a component.
125-
* @template T The component's theme imported from `#build/ui/*`.
126-
* @template A The base AppConfig type from `@nuxt/schema`.
127-
* @template K The key identifying the component (e.g., 'badge').
128-
* @template U The top-level key in AppConfig ('ui' or 'uiPro').
129-
*/
130-
export type ComponentConfig<
131-
T extends Record<string, any>,
132-
A extends Record<string, any>,
133-
K extends string,
134-
U extends 'ui' | 'uiPro' | 'uiPro.prose' = 'ui'
135-
> = {
136-
AppConfig: ComponentAppConfig<T, A, K, U>
137-
variants: ComponentVariants<T & GetComponentAppConfig<A, U, K>>
138-
slots: ComponentSlots<T>
139-
}
80+
export * from './tv'

src/templates.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export function getTemplates(options: ModuleOptions, uiConfig: Record<string, an
149149
templates.push({
150150
filename: 'types/ui.d.ts',
151151
getContents: () => `import * as ui from '#build/ui'
152-
import type { DeepPartial } from '@nuxt/ui'
152+
import type { TVConfig } from '@nuxt/ui'
153153
import type { defaultConfig } from 'tailwind-variants'
154154
import colors from 'tailwindcss/colors'
155155
@@ -165,7 +165,7 @@ type AppConfigUI = {
165165
}
166166
icons?: Partial<typeof icons>
167167
tv?: typeof defaultConfig
168-
} & DeepPartial<typeof ui>
168+
} & TVConfig<typeof ui>
169169
170170
declare module '@nuxt/schema' {
171171
interface AppConfigInput {

src/unplugin.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ import PluginsPlugin from './plugins/plugins'
2020
import AppConfigPlugin from './plugins/app-config'
2121
import ComponentImportPlugin from './plugins/components'
2222
import NuxtEnvironmentPlugin from './plugins/nuxt-environment'
23-
24-
import type { DeepPartial } from './runtime/types/utils'
2523
import AutoImportPlugin from './plugins/auto-import'
2624

25+
import type { TVConfig } from './runtime/types/tv'
26+
2727
type NeutralColor = 'slate' | 'gray' | 'zinc' | 'neutral' | 'stone'
2828
type Color = Exclude<keyof typeof colors, 'inherit' | 'current' | 'transparent' | 'black' | 'white' | NeutralColor> | (string & {})
2929

3030
type AppConfigUI = {
3131
// TODO: add type hinting for colors from `options.theme.colors`
3232
colors?: Record<string, Color> & { neutral?: NeutralColor }
3333
icons?: Partial<typeof icons>
34-
} & DeepPartial<typeof ui>
34+
} & TVConfig<typeof ui>
3535

3636
export interface NuxtUIOptions extends Omit<ModuleOptions, 'fonts' | 'colorMode'> {
3737
/** Whether to generate declaration files for auto-imported components. */

0 commit comments

Comments
 (0)