tailwind-styled-v4 5.1.32 → 5.1.34
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/dist/index.browser.mjs +4 -6
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +78 -40
- package/dist/index.d.ts +78 -40
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -12,9 +12,25 @@ type HtmlTagName = keyof HTMLElementTagNameMap;
|
|
|
12
12
|
type VariantLiterals = string | number | boolean;
|
|
13
13
|
/** Sizes sugar syntax — shorthand untuk variants.size */
|
|
14
14
|
type SizesConfig = Record<string, string>;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Shared literal-narrowing logic dipakai baik oleh `InferVariantProps` (top-level
|
|
17
|
+
* component variants) maupun `InferSubVariantsFromConfig` (sub-component variants) —
|
|
18
|
+
* supaya kedua tempat itu tidak duplikasi & selalu konsisten.
|
|
19
|
+
*/
|
|
20
|
+
type InferVariantPropsFromVariantsMap<V> = {
|
|
21
|
+
[K in keyof V as string extends K ? never : number extends K ? never : K]?: V[K] extends Record<infer Key, unknown> ? Key extends "true" | "false" ? boolean : Key extends number ? Key : Key extends `${infer N extends number}` ? N : Key : never;
|
|
17
22
|
};
|
|
23
|
+
type InferVariantProps<T extends ComponentConfig> = InferVariantPropsFromVariantsMap<T["variants"]>;
|
|
24
|
+
/**
|
|
25
|
+
* Infer allowed types for defaultVariants based on variant keys.
|
|
26
|
+
* Supports:
|
|
27
|
+
* - String keys: { variant: "primary" }
|
|
28
|
+
* - Number keys: { priority: 0 }
|
|
29
|
+
* - Boolean keys: { disabled: false } (translates true/false to "true"/"false" keys)
|
|
30
|
+
*
|
|
31
|
+
* This helper ensures type safety when defaultVariants contains non-string values.
|
|
32
|
+
*/
|
|
33
|
+
type InferDefaultVariantsType<T extends ComponentConfig> = InferVariantPropsFromVariantsMap<T["variants"]>;
|
|
18
34
|
type InferSizeProps<T extends ComponentConfig> = T["sizes"] extends Record<string, string> ? {
|
|
19
35
|
size?: keyof T["sizes"];
|
|
20
36
|
} : Record<never, never>;
|
|
@@ -27,7 +43,7 @@ type InferSizeProps<T extends ComponentConfig> = T["sizes"] extends Record<strin
|
|
|
27
43
|
* // → { loading?: boolean, fullWidth?: boolean }
|
|
28
44
|
*/
|
|
29
45
|
type InferStatesProps<T extends ComponentConfig> = {
|
|
30
|
-
[K in keyof T["states"]]?: boolean;
|
|
46
|
+
[K in keyof T["states"] as string extends K ? never : number extends K ? never : K]?: boolean;
|
|
31
47
|
};
|
|
32
48
|
/**
|
|
33
49
|
* Sub-component config bisa berupa:
|
|
@@ -52,22 +68,8 @@ interface SubComponentConfig {
|
|
|
52
68
|
class: string;
|
|
53
69
|
[key: string]: string;
|
|
54
70
|
}>;
|
|
55
|
-
/**
|
|
56
|
-
* Override HTML tag untuk direct sub-component (bare key + base/variants).
|
|
57
|
-
* Default: "div" kalau tidak diset (backward-compatible).
|
|
58
|
-
* @example canvas: { tag: "section", base: "...", variants: {...} }
|
|
59
|
-
*/
|
|
60
|
-
tag?: HtmlTagName;
|
|
61
71
|
}
|
|
62
72
|
type SubValue = string | SubComponentConfig | Record<string, string | SubComponentConfig>;
|
|
63
|
-
/**
|
|
64
|
-
* Mirror dari disambiguation logic runtime di createComponent.ts (registerSubComponents):
|
|
65
|
-
* `"base" in value || "variants" in value`
|
|
66
|
-
* Kalau bare-key punya property "base"/"variants" langsung, value itu adalah
|
|
67
|
-
* SubComponentConfig untuk key itu sendiri — bukan map { namaSubkomponen: classes }.
|
|
68
|
-
* Contoh: `canvas: { base: "...", variants: {...} }` → key "canvas" ITU sendiri nama sub-component.
|
|
69
|
-
*/
|
|
70
|
-
type IsDirectSubConfig<T> = T extends object ? ("base" extends keyof T ? true : "variants" extends keyof T ? true : false) : false;
|
|
71
73
|
/**
|
|
72
74
|
* Boolean props yang di-resolve via bitmask lookup table (pre-generated di build time).
|
|
73
75
|
* Berbeda dari `state` (CSS data-attribute driven) — ini adalah React props boolean.
|
|
@@ -153,16 +155,29 @@ interface ComponentConfig {
|
|
|
153
155
|
* "header" → "header" (tidak berubah)
|
|
154
156
|
*/
|
|
155
157
|
type ExtractSubName<K extends string> = K extends `${string}:${infer Name}` ? Name : K;
|
|
158
|
+
/**
|
|
159
|
+
* True kalau S[K] adalah SubComponentConfig LANGSUNG (satu sub-component dengan
|
|
160
|
+
* `base`/`variants` sendiri, mis. `canvas: { base: "...", variants: {...} }`) —
|
|
161
|
+
* bukan nested named-group (mis. `header: { topBar: "...", nav: "..." }`).
|
|
162
|
+
*
|
|
163
|
+
* HARUS identik dengan discriminant runtime di createComponent.ts:
|
|
164
|
+
* `"base" in value || "variants" in value`. Kedua bentuk sama-sama tampak sebagai
|
|
165
|
+
* `Record<string, ...>` secara struktural, jadi tidak bisa dibedakan cuma lewat
|
|
166
|
+
* `extends Record<string, string>` — perlu cek keberadaan key `base`/`variants`
|
|
167
|
+
* secara eksplisit seperti runtime-nya.
|
|
168
|
+
*/
|
|
169
|
+
type IsDirectSubConfig<V> = V extends string ? false : "variants" extends keyof V ? true : "base" extends keyof V ? true : false;
|
|
156
170
|
/**
|
|
157
171
|
* Infer semua sub-component names dari config.sub:
|
|
158
|
-
* - string value plain
|
|
159
|
-
* - string value tag:name
|
|
160
|
-
* -
|
|
172
|
+
* - string value plain → key langsung: { icon: "..." } → "icon"
|
|
173
|
+
* - string value tag:name → strip tag: { "div:action": "..." } → "action"
|
|
174
|
+
* - direct SubComponentConfig → key langsung (support "tag:name" juga): { canvas: {base, variants} } → "canvas"
|
|
175
|
+
* - nested named-group → nested keys: { h2: { title: "..." } } → "title"
|
|
161
176
|
*/
|
|
162
177
|
type InferSubFromConfig<C extends ComponentConfig> = C extends {
|
|
163
178
|
sub: infer S extends Record<string, SubValue>;
|
|
164
179
|
} ? {
|
|
165
|
-
[K in keyof S]: S[K] extends string ? K extends string ? ExtractSubName<K> : never : IsDirectSubConfig<S[K]> extends true ? K : S[K] extends Record<infer N extends string, string
|
|
180
|
+
[K in keyof S]: S[K] extends string ? K extends string ? ExtractSubName<K> : never : IsDirectSubConfig<S[K]> extends true ? K extends string ? ExtractSubName<K> : never : S[K] extends Record<infer N extends string, string> ? N : never;
|
|
166
181
|
}[keyof S] : never;
|
|
167
182
|
/**
|
|
168
183
|
* HTML tags yang otomatis dikenali sebagai tag dari bare sub-key (tanpa "tag:name" syntax).
|
|
@@ -197,15 +212,30 @@ type InferSubTagsFromConfig<C extends ComponentConfig> = C extends {
|
|
|
197
212
|
[K in keyof S]: K extends string ? S[K] extends string ? {
|
|
198
213
|
[N in ExtractSubName<K>]: ResolveSubTag<K>;
|
|
199
214
|
} : IsDirectSubConfig<S[K]> extends true ? {
|
|
200
|
-
[N in K]:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
} : S[K] extends Record<string, string | SubComponentConfig> ? {
|
|
204
|
-
[N in keyof S[K]]: S[K][N] extends {
|
|
205
|
-
tag: infer T extends HtmlTagName;
|
|
206
|
-
} ? T : K extends HtmlTagName ? K : "span";
|
|
215
|
+
[N in ExtractSubName<K>]: ResolveSubTag<K>;
|
|
216
|
+
} : S[K] extends Record<string, string> ? {
|
|
217
|
+
[N in keyof S[K]]: K extends HtmlTagName ? K : "span";
|
|
207
218
|
} : never : never;
|
|
208
219
|
}[keyof S]> : Record<string, never>;
|
|
220
|
+
/**
|
|
221
|
+
* Infer mapping { subComponentName: variantProps } dari config.sub — dipakai supaya
|
|
222
|
+
* sub-component yang punya `variants` sendiri (mis. `canvas: { base, variants: { layout: {...} } }`)
|
|
223
|
+
* ikut ke-expose propnya (`layout`) di accessor-nya (`PlaygroundWrap.canvas`), bukan cuma
|
|
224
|
+
* native HTML attributes dari tag-nya.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* sub: { canvas: { variants: { layout: { wrap: "...", column: "..." } } } }
|
|
228
|
+
* → { canvas: { layout?: "wrap" | "column" } }
|
|
229
|
+
*/
|
|
230
|
+
type InferSubVariantsFromConfig<C extends ComponentConfig> = C extends {
|
|
231
|
+
sub: infer S extends Record<string, SubValue>;
|
|
232
|
+
} ? UnionToIntersection<{
|
|
233
|
+
[K in keyof S]: K extends string ? IsDirectSubConfig<S[K]> extends true ? S[K] extends {
|
|
234
|
+
variants?: infer V;
|
|
235
|
+
} ? {
|
|
236
|
+
[N in ExtractSubName<K>]: InferVariantPropsFromVariantsMap<V>;
|
|
237
|
+
} : Record<never, never> : Record<never, never> : Record<never, never>;
|
|
238
|
+
}[keyof S]> : Record<never, never>;
|
|
209
239
|
interface ContainerConfig {
|
|
210
240
|
base?: string;
|
|
211
241
|
queries?: Record<string, string>;
|
|
@@ -217,7 +247,7 @@ interface StateConfig {
|
|
|
217
247
|
defaultStates?: Record<string, boolean>;
|
|
218
248
|
}
|
|
219
249
|
type CvFn<C extends ComponentConfig> = (props?: {
|
|
220
|
-
[K in keyof C["variants"]]?: keyof C["variants"][K];
|
|
250
|
+
[K in keyof C["variants"] as string extends K ? never : number extends K ? never : K]?: keyof C["variants"][K];
|
|
221
251
|
} & {
|
|
222
252
|
class?: string;
|
|
223
253
|
className?: string;
|
|
@@ -228,7 +258,7 @@ interface StyledComponentProps {
|
|
|
228
258
|
children?: React.ReactNode;
|
|
229
259
|
}
|
|
230
260
|
type SubComponentMap = Record<string, unknown>;
|
|
231
|
-
type TwSubComponentAccessor<Tag extends HtmlTagName = "span"> = React.FC<Omit<React.ComponentPropsWithoutRef<Tag>, "ref"> & {
|
|
261
|
+
type TwSubComponentAccessor<Tag extends HtmlTagName = "span", ExtraProps extends Record<string, unknown> = Record<never, never>> = React.FC<Omit<React.ComponentPropsWithoutRef<Tag>, "ref"> & ExtraProps & {
|
|
232
262
|
children?: React.ReactNode;
|
|
233
263
|
className?: string;
|
|
234
264
|
}>;
|
|
@@ -240,22 +270,28 @@ type TrimRight<S extends string> = S extends `${infer L} ` | `${infer L}
|
|
|
240
270
|
` ? TrimRight<L> : S;
|
|
241
271
|
type Trim<S extends string> = TrimLeft<TrimRight<S>>;
|
|
242
272
|
type ExtractSubNames<T extends string> = T extends `${string}[${infer Name}]${string}{${string}}${infer Rest}` ? Trim<Name> | ExtractSubNames<Rest> : T extends `${string}\n${infer Name}{${string}}${infer Rest}` ? (Trim<Name> extends "" ? never : Trim<Name>) | ExtractSubNames<Rest> : never;
|
|
243
|
-
type SubComponentKeys<S extends string, TagMap extends Record<string, string> = Record<string, never>> = {
|
|
244
|
-
[K in S]: TwSubComponentAccessor<K extends keyof TagMap ? (TagMap[K] extends HtmlTagName ? TagMap[K] : "span") : "span"
|
|
273
|
+
type SubComponentKeys<S extends string, TagMap extends Record<string, string> = Record<string, never>, SubVariantsMap extends Record<string, Record<string, unknown>> = Record<string, never>> = {
|
|
274
|
+
[K in S]: TwSubComponentAccessor<K extends keyof TagMap ? (TagMap[K] extends HtmlTagName ? TagMap[K] : "span") : "span", K extends keyof SubVariantsMap ? SubVariantsMap[K] : Record<never, never>>;
|
|
245
275
|
};
|
|
246
|
-
type TwStyledComponent<Config extends ComponentConfig = ComponentConfig, S extends string = string, TagMap extends Record<string, string> = Record<string, never>, Tag extends HtmlTagName = HtmlTagName
|
|
276
|
+
type TwStyledComponent<Config extends ComponentConfig = ComponentConfig, S extends string = string, TagMap extends Record<string, string> = Record<string, never>, Tag extends HtmlTagName = HtmlTagName, SubVariantsMap extends Record<string, Record<string, unknown>> = Record<string, never>> = {
|
|
247
277
|
(props: React.ComponentPropsWithoutRef<Tag> & StyledComponentProps & InferVariantProps<Config> & InferSizeProps<Config> & InferStatesProps<Config>): React.ReactElement | null;
|
|
248
278
|
displayName?: string;
|
|
249
279
|
extend: {
|
|
250
|
-
(strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent<Config, S, TagMap, Tag>;
|
|
251
|
-
|
|
280
|
+
(strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent<Config, S, TagMap, Tag, SubVariantsMap>;
|
|
281
|
+
<EC extends {
|
|
252
282
|
classes?: string;
|
|
253
283
|
variants?: ComponentConfig["variants"];
|
|
254
284
|
defaultVariants?: ComponentConfig["defaultVariants"];
|
|
255
285
|
compoundVariants?: ComponentConfig["compoundVariants"];
|
|
256
|
-
}
|
|
286
|
+
}>(config: EC & {
|
|
287
|
+
defaultVariants?: InferDefaultVariantsType<{
|
|
288
|
+
variants: EC["variants"];
|
|
289
|
+
}>;
|
|
290
|
+
}): TwStyledComponent<Config, S, TagMap, Tag, SubVariantsMap>;
|
|
257
291
|
};
|
|
258
|
-
withVariants: (config: Partial<
|
|
292
|
+
withVariants: (config: Partial<Omit<Config, "defaultVariants">> & {
|
|
293
|
+
defaultVariants?: InferDefaultVariantsType<Config>;
|
|
294
|
+
}) => TwStyledComponent<Config, S, TagMap, Tag, SubVariantsMap>;
|
|
259
295
|
/**
|
|
260
296
|
* Declare sub-component names secara eksplisit untuk autocomplete + type safety.
|
|
261
297
|
*
|
|
@@ -269,14 +305,16 @@ type TwStyledComponent<Config extends ComponentConfig = ComponentConfig, S exten
|
|
|
269
305
|
* Button.footer // ✅ autocomplete
|
|
270
306
|
* Button.xyz // ❌ TypeScript error
|
|
271
307
|
*/
|
|
272
|
-
withSub<NewS extends string>(): TwStyledComponent<Config, NewS, TagMap, Tag>;
|
|
273
|
-
} & SubComponentKeys<S, TagMap>;
|
|
308
|
+
withSub<NewS extends string>(): TwStyledComponent<Config, NewS, TagMap, Tag, SubVariantsMap>;
|
|
309
|
+
} & SubComponentKeys<S, TagMap, SubVariantsMap>;
|
|
274
310
|
interface TwSubComponent<P = unknown> {
|
|
275
311
|
(props: P): React.ReactElement | null;
|
|
276
312
|
displayName?: string;
|
|
277
313
|
}
|
|
278
314
|
interface TwTemplateFactory<Config extends ComponentConfig = ComponentConfig, Tag extends HtmlTagName = HtmlTagName> {
|
|
279
|
-
<C extends ComponentConfig>(config: C
|
|
315
|
+
<C extends ComponentConfig>(config: C & {
|
|
316
|
+
defaultVariants?: InferDefaultVariantsType<C>;
|
|
317
|
+
}): TwStyledComponent<C, InferSubFromConfig<C>, InferSubTagsFromConfig<C> extends Record<string, string> ? InferSubTagsFromConfig<C> : Record<string, never>, Tag, InferSubVariantsFromConfig<C> extends Record<string, Record<string, unknown>> ? InferSubVariantsFromConfig<C> : Record<string, never>>;
|
|
280
318
|
<const T extends string>(strings: readonly [T], ...exprs: []): TwStyledComponent<Config, ExtractSubNames<T>, Record<string, never>, Tag>;
|
|
281
319
|
(strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent<Config, string, Record<string, never>, Tag>;
|
|
282
320
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,9 +12,25 @@ type HtmlTagName = keyof HTMLElementTagNameMap;
|
|
|
12
12
|
type VariantLiterals = string | number | boolean;
|
|
13
13
|
/** Sizes sugar syntax — shorthand untuk variants.size */
|
|
14
14
|
type SizesConfig = Record<string, string>;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Shared literal-narrowing logic dipakai baik oleh `InferVariantProps` (top-level
|
|
17
|
+
* component variants) maupun `InferSubVariantsFromConfig` (sub-component variants) —
|
|
18
|
+
* supaya kedua tempat itu tidak duplikasi & selalu konsisten.
|
|
19
|
+
*/
|
|
20
|
+
type InferVariantPropsFromVariantsMap<V> = {
|
|
21
|
+
[K in keyof V as string extends K ? never : number extends K ? never : K]?: V[K] extends Record<infer Key, unknown> ? Key extends "true" | "false" ? boolean : Key extends number ? Key : Key extends `${infer N extends number}` ? N : Key : never;
|
|
17
22
|
};
|
|
23
|
+
type InferVariantProps<T extends ComponentConfig> = InferVariantPropsFromVariantsMap<T["variants"]>;
|
|
24
|
+
/**
|
|
25
|
+
* Infer allowed types for defaultVariants based on variant keys.
|
|
26
|
+
* Supports:
|
|
27
|
+
* - String keys: { variant: "primary" }
|
|
28
|
+
* - Number keys: { priority: 0 }
|
|
29
|
+
* - Boolean keys: { disabled: false } (translates true/false to "true"/"false" keys)
|
|
30
|
+
*
|
|
31
|
+
* This helper ensures type safety when defaultVariants contains non-string values.
|
|
32
|
+
*/
|
|
33
|
+
type InferDefaultVariantsType<T extends ComponentConfig> = InferVariantPropsFromVariantsMap<T["variants"]>;
|
|
18
34
|
type InferSizeProps<T extends ComponentConfig> = T["sizes"] extends Record<string, string> ? {
|
|
19
35
|
size?: keyof T["sizes"];
|
|
20
36
|
} : Record<never, never>;
|
|
@@ -27,7 +43,7 @@ type InferSizeProps<T extends ComponentConfig> = T["sizes"] extends Record<strin
|
|
|
27
43
|
* // → { loading?: boolean, fullWidth?: boolean }
|
|
28
44
|
*/
|
|
29
45
|
type InferStatesProps<T extends ComponentConfig> = {
|
|
30
|
-
[K in keyof T["states"]]?: boolean;
|
|
46
|
+
[K in keyof T["states"] as string extends K ? never : number extends K ? never : K]?: boolean;
|
|
31
47
|
};
|
|
32
48
|
/**
|
|
33
49
|
* Sub-component config bisa berupa:
|
|
@@ -52,22 +68,8 @@ interface SubComponentConfig {
|
|
|
52
68
|
class: string;
|
|
53
69
|
[key: string]: string;
|
|
54
70
|
}>;
|
|
55
|
-
/**
|
|
56
|
-
* Override HTML tag untuk direct sub-component (bare key + base/variants).
|
|
57
|
-
* Default: "div" kalau tidak diset (backward-compatible).
|
|
58
|
-
* @example canvas: { tag: "section", base: "...", variants: {...} }
|
|
59
|
-
*/
|
|
60
|
-
tag?: HtmlTagName;
|
|
61
71
|
}
|
|
62
72
|
type SubValue = string | SubComponentConfig | Record<string, string | SubComponentConfig>;
|
|
63
|
-
/**
|
|
64
|
-
* Mirror dari disambiguation logic runtime di createComponent.ts (registerSubComponents):
|
|
65
|
-
* `"base" in value || "variants" in value`
|
|
66
|
-
* Kalau bare-key punya property "base"/"variants" langsung, value itu adalah
|
|
67
|
-
* SubComponentConfig untuk key itu sendiri — bukan map { namaSubkomponen: classes }.
|
|
68
|
-
* Contoh: `canvas: { base: "...", variants: {...} }` → key "canvas" ITU sendiri nama sub-component.
|
|
69
|
-
*/
|
|
70
|
-
type IsDirectSubConfig<T> = T extends object ? ("base" extends keyof T ? true : "variants" extends keyof T ? true : false) : false;
|
|
71
73
|
/**
|
|
72
74
|
* Boolean props yang di-resolve via bitmask lookup table (pre-generated di build time).
|
|
73
75
|
* Berbeda dari `state` (CSS data-attribute driven) — ini adalah React props boolean.
|
|
@@ -153,16 +155,29 @@ interface ComponentConfig {
|
|
|
153
155
|
* "header" → "header" (tidak berubah)
|
|
154
156
|
*/
|
|
155
157
|
type ExtractSubName<K extends string> = K extends `${string}:${infer Name}` ? Name : K;
|
|
158
|
+
/**
|
|
159
|
+
* True kalau S[K] adalah SubComponentConfig LANGSUNG (satu sub-component dengan
|
|
160
|
+
* `base`/`variants` sendiri, mis. `canvas: { base: "...", variants: {...} }`) —
|
|
161
|
+
* bukan nested named-group (mis. `header: { topBar: "...", nav: "..." }`).
|
|
162
|
+
*
|
|
163
|
+
* HARUS identik dengan discriminant runtime di createComponent.ts:
|
|
164
|
+
* `"base" in value || "variants" in value`. Kedua bentuk sama-sama tampak sebagai
|
|
165
|
+
* `Record<string, ...>` secara struktural, jadi tidak bisa dibedakan cuma lewat
|
|
166
|
+
* `extends Record<string, string>` — perlu cek keberadaan key `base`/`variants`
|
|
167
|
+
* secara eksplisit seperti runtime-nya.
|
|
168
|
+
*/
|
|
169
|
+
type IsDirectSubConfig<V> = V extends string ? false : "variants" extends keyof V ? true : "base" extends keyof V ? true : false;
|
|
156
170
|
/**
|
|
157
171
|
* Infer semua sub-component names dari config.sub:
|
|
158
|
-
* - string value plain
|
|
159
|
-
* - string value tag:name
|
|
160
|
-
* -
|
|
172
|
+
* - string value plain → key langsung: { icon: "..." } → "icon"
|
|
173
|
+
* - string value tag:name → strip tag: { "div:action": "..." } → "action"
|
|
174
|
+
* - direct SubComponentConfig → key langsung (support "tag:name" juga): { canvas: {base, variants} } → "canvas"
|
|
175
|
+
* - nested named-group → nested keys: { h2: { title: "..." } } → "title"
|
|
161
176
|
*/
|
|
162
177
|
type InferSubFromConfig<C extends ComponentConfig> = C extends {
|
|
163
178
|
sub: infer S extends Record<string, SubValue>;
|
|
164
179
|
} ? {
|
|
165
|
-
[K in keyof S]: S[K] extends string ? K extends string ? ExtractSubName<K> : never : IsDirectSubConfig<S[K]> extends true ? K : S[K] extends Record<infer N extends string, string
|
|
180
|
+
[K in keyof S]: S[K] extends string ? K extends string ? ExtractSubName<K> : never : IsDirectSubConfig<S[K]> extends true ? K extends string ? ExtractSubName<K> : never : S[K] extends Record<infer N extends string, string> ? N : never;
|
|
166
181
|
}[keyof S] : never;
|
|
167
182
|
/**
|
|
168
183
|
* HTML tags yang otomatis dikenali sebagai tag dari bare sub-key (tanpa "tag:name" syntax).
|
|
@@ -197,15 +212,30 @@ type InferSubTagsFromConfig<C extends ComponentConfig> = C extends {
|
|
|
197
212
|
[K in keyof S]: K extends string ? S[K] extends string ? {
|
|
198
213
|
[N in ExtractSubName<K>]: ResolveSubTag<K>;
|
|
199
214
|
} : IsDirectSubConfig<S[K]> extends true ? {
|
|
200
|
-
[N in K]:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
} : S[K] extends Record<string, string | SubComponentConfig> ? {
|
|
204
|
-
[N in keyof S[K]]: S[K][N] extends {
|
|
205
|
-
tag: infer T extends HtmlTagName;
|
|
206
|
-
} ? T : K extends HtmlTagName ? K : "span";
|
|
215
|
+
[N in ExtractSubName<K>]: ResolveSubTag<K>;
|
|
216
|
+
} : S[K] extends Record<string, string> ? {
|
|
217
|
+
[N in keyof S[K]]: K extends HtmlTagName ? K : "span";
|
|
207
218
|
} : never : never;
|
|
208
219
|
}[keyof S]> : Record<string, never>;
|
|
220
|
+
/**
|
|
221
|
+
* Infer mapping { subComponentName: variantProps } dari config.sub — dipakai supaya
|
|
222
|
+
* sub-component yang punya `variants` sendiri (mis. `canvas: { base, variants: { layout: {...} } }`)
|
|
223
|
+
* ikut ke-expose propnya (`layout`) di accessor-nya (`PlaygroundWrap.canvas`), bukan cuma
|
|
224
|
+
* native HTML attributes dari tag-nya.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* sub: { canvas: { variants: { layout: { wrap: "...", column: "..." } } } }
|
|
228
|
+
* → { canvas: { layout?: "wrap" | "column" } }
|
|
229
|
+
*/
|
|
230
|
+
type InferSubVariantsFromConfig<C extends ComponentConfig> = C extends {
|
|
231
|
+
sub: infer S extends Record<string, SubValue>;
|
|
232
|
+
} ? UnionToIntersection<{
|
|
233
|
+
[K in keyof S]: K extends string ? IsDirectSubConfig<S[K]> extends true ? S[K] extends {
|
|
234
|
+
variants?: infer V;
|
|
235
|
+
} ? {
|
|
236
|
+
[N in ExtractSubName<K>]: InferVariantPropsFromVariantsMap<V>;
|
|
237
|
+
} : Record<never, never> : Record<never, never> : Record<never, never>;
|
|
238
|
+
}[keyof S]> : Record<never, never>;
|
|
209
239
|
interface ContainerConfig {
|
|
210
240
|
base?: string;
|
|
211
241
|
queries?: Record<string, string>;
|
|
@@ -217,7 +247,7 @@ interface StateConfig {
|
|
|
217
247
|
defaultStates?: Record<string, boolean>;
|
|
218
248
|
}
|
|
219
249
|
type CvFn<C extends ComponentConfig> = (props?: {
|
|
220
|
-
[K in keyof C["variants"]]?: keyof C["variants"][K];
|
|
250
|
+
[K in keyof C["variants"] as string extends K ? never : number extends K ? never : K]?: keyof C["variants"][K];
|
|
221
251
|
} & {
|
|
222
252
|
class?: string;
|
|
223
253
|
className?: string;
|
|
@@ -228,7 +258,7 @@ interface StyledComponentProps {
|
|
|
228
258
|
children?: React.ReactNode;
|
|
229
259
|
}
|
|
230
260
|
type SubComponentMap = Record<string, unknown>;
|
|
231
|
-
type TwSubComponentAccessor<Tag extends HtmlTagName = "span"> = React.FC<Omit<React.ComponentPropsWithoutRef<Tag>, "ref"> & {
|
|
261
|
+
type TwSubComponentAccessor<Tag extends HtmlTagName = "span", ExtraProps extends Record<string, unknown> = Record<never, never>> = React.FC<Omit<React.ComponentPropsWithoutRef<Tag>, "ref"> & ExtraProps & {
|
|
232
262
|
children?: React.ReactNode;
|
|
233
263
|
className?: string;
|
|
234
264
|
}>;
|
|
@@ -240,22 +270,28 @@ type TrimRight<S extends string> = S extends `${infer L} ` | `${infer L}
|
|
|
240
270
|
` ? TrimRight<L> : S;
|
|
241
271
|
type Trim<S extends string> = TrimLeft<TrimRight<S>>;
|
|
242
272
|
type ExtractSubNames<T extends string> = T extends `${string}[${infer Name}]${string}{${string}}${infer Rest}` ? Trim<Name> | ExtractSubNames<Rest> : T extends `${string}\n${infer Name}{${string}}${infer Rest}` ? (Trim<Name> extends "" ? never : Trim<Name>) | ExtractSubNames<Rest> : never;
|
|
243
|
-
type SubComponentKeys<S extends string, TagMap extends Record<string, string> = Record<string, never>> = {
|
|
244
|
-
[K in S]: TwSubComponentAccessor<K extends keyof TagMap ? (TagMap[K] extends HtmlTagName ? TagMap[K] : "span") : "span"
|
|
273
|
+
type SubComponentKeys<S extends string, TagMap extends Record<string, string> = Record<string, never>, SubVariantsMap extends Record<string, Record<string, unknown>> = Record<string, never>> = {
|
|
274
|
+
[K in S]: TwSubComponentAccessor<K extends keyof TagMap ? (TagMap[K] extends HtmlTagName ? TagMap[K] : "span") : "span", K extends keyof SubVariantsMap ? SubVariantsMap[K] : Record<never, never>>;
|
|
245
275
|
};
|
|
246
|
-
type TwStyledComponent<Config extends ComponentConfig = ComponentConfig, S extends string = string, TagMap extends Record<string, string> = Record<string, never>, Tag extends HtmlTagName = HtmlTagName
|
|
276
|
+
type TwStyledComponent<Config extends ComponentConfig = ComponentConfig, S extends string = string, TagMap extends Record<string, string> = Record<string, never>, Tag extends HtmlTagName = HtmlTagName, SubVariantsMap extends Record<string, Record<string, unknown>> = Record<string, never>> = {
|
|
247
277
|
(props: React.ComponentPropsWithoutRef<Tag> & StyledComponentProps & InferVariantProps<Config> & InferSizeProps<Config> & InferStatesProps<Config>): React.ReactElement | null;
|
|
248
278
|
displayName?: string;
|
|
249
279
|
extend: {
|
|
250
|
-
(strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent<Config, S, TagMap, Tag>;
|
|
251
|
-
|
|
280
|
+
(strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent<Config, S, TagMap, Tag, SubVariantsMap>;
|
|
281
|
+
<EC extends {
|
|
252
282
|
classes?: string;
|
|
253
283
|
variants?: ComponentConfig["variants"];
|
|
254
284
|
defaultVariants?: ComponentConfig["defaultVariants"];
|
|
255
285
|
compoundVariants?: ComponentConfig["compoundVariants"];
|
|
256
|
-
}
|
|
286
|
+
}>(config: EC & {
|
|
287
|
+
defaultVariants?: InferDefaultVariantsType<{
|
|
288
|
+
variants: EC["variants"];
|
|
289
|
+
}>;
|
|
290
|
+
}): TwStyledComponent<Config, S, TagMap, Tag, SubVariantsMap>;
|
|
257
291
|
};
|
|
258
|
-
withVariants: (config: Partial<
|
|
292
|
+
withVariants: (config: Partial<Omit<Config, "defaultVariants">> & {
|
|
293
|
+
defaultVariants?: InferDefaultVariantsType<Config>;
|
|
294
|
+
}) => TwStyledComponent<Config, S, TagMap, Tag, SubVariantsMap>;
|
|
259
295
|
/**
|
|
260
296
|
* Declare sub-component names secara eksplisit untuk autocomplete + type safety.
|
|
261
297
|
*
|
|
@@ -269,14 +305,16 @@ type TwStyledComponent<Config extends ComponentConfig = ComponentConfig, S exten
|
|
|
269
305
|
* Button.footer // ✅ autocomplete
|
|
270
306
|
* Button.xyz // ❌ TypeScript error
|
|
271
307
|
*/
|
|
272
|
-
withSub<NewS extends string>(): TwStyledComponent<Config, NewS, TagMap, Tag>;
|
|
273
|
-
} & SubComponentKeys<S, TagMap>;
|
|
308
|
+
withSub<NewS extends string>(): TwStyledComponent<Config, NewS, TagMap, Tag, SubVariantsMap>;
|
|
309
|
+
} & SubComponentKeys<S, TagMap, SubVariantsMap>;
|
|
274
310
|
interface TwSubComponent<P = unknown> {
|
|
275
311
|
(props: P): React.ReactElement | null;
|
|
276
312
|
displayName?: string;
|
|
277
313
|
}
|
|
278
314
|
interface TwTemplateFactory<Config extends ComponentConfig = ComponentConfig, Tag extends HtmlTagName = HtmlTagName> {
|
|
279
|
-
<C extends ComponentConfig>(config: C
|
|
315
|
+
<C extends ComponentConfig>(config: C & {
|
|
316
|
+
defaultVariants?: InferDefaultVariantsType<C>;
|
|
317
|
+
}): TwStyledComponent<C, InferSubFromConfig<C>, InferSubTagsFromConfig<C> extends Record<string, string> ? InferSubTagsFromConfig<C> : Record<string, never>, Tag, InferSubVariantsFromConfig<C> extends Record<string, Record<string, unknown>> ? InferSubVariantsFromConfig<C> : Record<string, never>>;
|
|
280
318
|
<const T extends string>(strings: readonly [T], ...exprs: []): TwStyledComponent<Config, ExtractSubNames<T>, Record<string, never>, Tag>;
|
|
281
319
|
(strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent<Config, string, Record<string, never>, Tag>;
|
|
282
320
|
}
|
package/dist/index.js
CHANGED
|
@@ -913,8 +913,7 @@ function registerSubComponents(component, template, configSub) {
|
|
|
913
913
|
tag
|
|
914
914
|
);
|
|
915
915
|
} else if ("base" in value || "variants" in value) {
|
|
916
|
-
|
|
917
|
-
map[key] = createComponent(explicitTag, value);
|
|
916
|
+
map[key] = createComponent("div", value);
|
|
918
917
|
} else {
|
|
919
918
|
const tag = key;
|
|
920
919
|
for (const [componentName, classesOrConfig] of Object.entries(value)) {
|
|
@@ -926,8 +925,7 @@ function registerSubComponents(component, template, configSub) {
|
|
|
926
925
|
tag
|
|
927
926
|
);
|
|
928
927
|
} else {
|
|
929
|
-
|
|
930
|
-
map[componentName] = createComponent(nestedTag, classesOrConfig);
|
|
928
|
+
map[componentName] = createComponent(tag, classesOrConfig);
|
|
931
929
|
}
|
|
932
930
|
}
|
|
933
931
|
}
|
|
@@ -1048,7 +1046,7 @@ function attachExtend(component, originalTag, base, config) {
|
|
|
1048
1046
|
return extended;
|
|
1049
1047
|
}
|
|
1050
1048
|
component.extend = extendWithClasses;
|
|
1051
|
-
component.withVariants = (newConfig) => {
|
|
1049
|
+
component.withVariants = ((newConfig) => {
|
|
1052
1050
|
const existing = typeof config === "object" ? config : {};
|
|
1053
1051
|
return createComponent(originalTag, {
|
|
1054
1052
|
...existing,
|
|
@@ -1063,7 +1061,7 @@ function attachExtend(component, originalTag, base, config) {
|
|
|
1063
1061
|
...newConfig.defaultVariants ?? {}
|
|
1064
1062
|
}
|
|
1065
1063
|
});
|
|
1066
|
-
};
|
|
1064
|
+
});
|
|
1067
1065
|
component.withSub = (() => component);
|
|
1068
1066
|
return component;
|
|
1069
1067
|
}
|