From 550f37be0a8c61d522fc26a4195b5e1dbb176196 Mon Sep 17 00:00:00 2001 From: alstjr7375 Date: Fri, 22 Nov 2024 22:50:43 +0900 Subject: [PATCH 1/2] Feat: Rules - Props type #131 --- packages/css/src/rules/types.ts | 129 +++++++++++++++++- packages/transform-to-vanilla/src/index.ts | 1 + .../src/types/style-rule.ts | 3 +- 3 files changed, 127 insertions(+), 6 deletions(-) diff --git a/packages/css/src/rules/types.ts b/packages/css/src/rules/types.ts index 2b11673..6439d73 100644 --- a/packages/css/src/rules/types.ts +++ b/packages/css/src/rules/types.ts @@ -1,4 +1,9 @@ -import type { ComplexCSSRule, CSSRule } from "@mincho-js/transform-to-vanilla"; +import type { + ComplexCSSRule, + CSSRule, + ResolvedProperties, + NonNullableString +} from "@mincho-js/transform-to-vanilla"; type Resolve = { [Key in keyof T]: T[Key]; @@ -10,6 +15,12 @@ export type RemoveUndefinedFromIntersection = { [K in keyof T]: RemoveUndefined; }[keyof T]; +type UnionToIntersection = ( + U extends unknown ? (k: U) => void : never +) extends (k: infer I) => void + ? I + : never; + type Primitive = string | number | boolean | null | undefined; export type Serializable = | { @@ -51,6 +62,70 @@ export type VariantsClassNames = { }; }; +export type PropTarget = keyof ResolvedProperties; + +export type ComplexPropDefinition = + | PropDefinition + | Array>; +export type PropDefinition = + | PropDefinitionWithPropTarget + | PropDefinitionWithString; + +type PropDefinitionWithPropTarget = { + [Key in Exclude]?: { + base: ResolvedProperties[Key]; + }; +}; +type PropDefinitionWithString = { + [Key in NonNullableString]?: + | { + base?: ResolvedProperties[Exclude]; + targets: PropKeys[]; + } + | PropKeys[]; +}; + +export type PropDefinitionOutput< + T extends ComplexPropDefinition +> = UnionToIntersection< + T extends unknown[] + ? PropDefinitionOutputElement + : PropDefinitionOutputElement +>; +type PropDefinitionOutputElement = + DefinitionElement extends string + ? HandlePropTarget + : DefinitionElement extends { [key: string]: unknown } + ? HandlePropDefinition + : never; + +type HandlePropTarget = PropKeys extends PropTarget + ? { [Key in PropKeys]: ResolvedProperties[Key] } + : never; +type HandlePropDefinition = + UnionToIntersection< + { + [Key in keyof PropObject & string]: HandlePropDefinitionEntry< + Key, + PropObject[Key] + >; + }[keyof PropObject & string] + >; +type HandlePropDefinitionEntry< + Key extends string, + PropValue +> = PropValue extends { + targets: infer T; +} + ? T extends unknown[] + ? { [P in Key]: ResolvedProperties[Extract] } + : never + : PropValue extends unknown[] + ? { [P in Key]: ResolvedProperties[Extract] } + : Key extends PropTarget + ? { [P in Key]: ResolvedProperties[Key] } + : never; + export type PatternResult = { defaultClassName: string; variantClassNames: VariantsClassNames; @@ -76,9 +151,11 @@ export type ConditionalVariants< export type PatternOptions< Variants extends VariantGroups | undefined, - ToggleVariants extends VariantDefinitions | undefined + ToggleVariants extends VariantDefinitions | undefined, + Props extends ComplexPropDefinition | undefined > = CSSRule & { base?: RecipeStyleRule; + props?: Props; toggles?: ToggleVariants; variants?: Variants; defaultVariants?: VariantSelection< @@ -295,9 +372,10 @@ if (import.meta.vitest) { describe.concurrent("Types related to Rules", () => { function assertValidOptions< Variants extends VariantGroups | undefined = undefined, - ToggleVariants extends VariantDefinitions | undefined = undefined - >(options: PatternOptions) { - assertType>(options); + ToggleVariants extends VariantDefinitions | undefined = undefined, + Props extends ComplexPropDefinition | undefined = undefined + >(options: PatternOptions) { + assertType>(options); return options; } @@ -537,6 +615,47 @@ if (import.meta.vitest) { }); }); + it("Props PatternOptions", () => { + // Property Array + assertValidOptions({ + props: ["color", "background"] + }); + + // Property with base value + assertValidOptions({ + props: { + background: { base: "red" } + } + }); + + // Property with aliased + assertValidOptions({ + props: { + size: ["padding", "margin"] + } + }); + assertValidOptions({ + props: { + size: { base: "3px", targets: ["padding", "margin"] } + } + }); + + // Complex Props + assertValidOptions({ + props: ["color", "background", { size: ["padding", "margin"] }] + }); + assertValidOptions({ + props: [ + "color", + { + background: { base: "red" }, + contour: ["border", "outline"], + size: { base: "3px", targets: ["padding", "margin"] } + } + ] + }); + }); + it("Complex Style PatternOptions", () => { assertValidOptions({ backgroundColor: "gray", diff --git a/packages/transform-to-vanilla/src/index.ts b/packages/transform-to-vanilla/src/index.ts index 7f6d6f7..a69fd80 100644 --- a/packages/transform-to-vanilla/src/index.ts +++ b/packages/transform-to-vanilla/src/index.ts @@ -5,3 +5,4 @@ export { } from "@/transform-object"; export { replaceVariantReference } from "@/transform-object/variant-reference"; export type * from "./types/style-rule"; +export type { NonNullableString } from "./types/string"; diff --git a/packages/transform-to-vanilla/src/types/style-rule.ts b/packages/transform-to-vanilla/src/types/style-rule.ts index 1349545..ea09f22 100644 --- a/packages/transform-to-vanilla/src/types/style-rule.ts +++ b/packages/transform-to-vanilla/src/types/style-rule.ts @@ -244,7 +244,8 @@ export interface AnonymousProperty { } export type AnonymousPropertyKey = keyof AnonymousProperty; -interface ResolvedProperties extends Properties {} +export interface ResolvedProperties + extends Properties {} type CSSKeyframeFromTo = | "from" From d0991f539e651abf074aea91ca2dcd3078a0cd08 Mon Sep 17 00:00:00 2001 From: alstjr7375 Date: Sat, 23 Nov 2024 22:51:35 +0900 Subject: [PATCH 2/2] Fix: Rules - Prop expression type limitation #131 --- packages/css/src/rules/index.ts | 7 +++-- packages/css/src/rules/types.ts | 54 ++++++++------------------------- 2 files changed, 18 insertions(+), 43 deletions(-) diff --git a/packages/css/src/rules/index.ts b/packages/css/src/rules/index.ts index 6d62d19..5af1e4b 100644 --- a/packages/css/src/rules/index.ts +++ b/packages/css/src/rules/index.ts @@ -14,6 +14,8 @@ import type { VariantDefinitions, VariantSelection, VariantObjectSelection, + ComplexPropDefinitions, + PropTarget, ConditionalVariants, Serializable } from "./types"; @@ -27,9 +29,10 @@ const mergeObject = deepmerge(); export function rules< Variants extends VariantGroups | undefined = undefined, - ToggleVariants extends VariantDefinitions | undefined = undefined + ToggleVariants extends VariantDefinitions | undefined = undefined, + Props extends ComplexPropDefinitions | undefined = undefined >( - options: PatternOptions, + options: PatternOptions, debugId?: string ): RuntimeFn> { const { diff --git a/packages/css/src/rules/types.ts b/packages/css/src/rules/types.ts index 6439d73..dc604b0 100644 --- a/packages/css/src/rules/types.ts +++ b/packages/css/src/rules/types.ts @@ -64,29 +64,18 @@ export type VariantsClassNames = { export type PropTarget = keyof ResolvedProperties; -export type ComplexPropDefinition = +export type ComplexPropDefinitions = | PropDefinition | Array>; -export type PropDefinition = - | PropDefinitionWithPropTarget - | PropDefinitionWithString; - -type PropDefinitionWithPropTarget = { - [Key in Exclude]?: { - base: ResolvedProperties[Key]; +type PropDefinition = { + [Key in NonNullableString | PropTarget]?: { + base?: ResolvedProperties[Exclude]; + targets: PropKeys[]; }; }; -type PropDefinitionWithString = { - [Key in NonNullableString]?: - | { - base?: ResolvedProperties[Exclude]; - targets: PropKeys[]; - } - | PropKeys[]; -}; export type PropDefinitionOutput< - T extends ComplexPropDefinition + T extends ComplexPropDefinitions > = UnionToIntersection< T extends unknown[] ? PropDefinitionOutputElement @@ -120,11 +109,7 @@ type HandlePropDefinitionEntry< ? T extends unknown[] ? { [P in Key]: ResolvedProperties[Extract] } : never - : PropValue extends unknown[] - ? { [P in Key]: ResolvedProperties[Extract] } - : Key extends PropTarget - ? { [P in Key]: ResolvedProperties[Key] } - : never; + : never; export type PatternResult = { defaultClassName: string; @@ -152,7 +137,7 @@ export type ConditionalVariants< export type PatternOptions< Variants extends VariantGroups | undefined, ToggleVariants extends VariantDefinitions | undefined, - Props extends ComplexPropDefinition | undefined + Props extends ComplexPropDefinitions | undefined > = CSSRule & { base?: RecipeStyleRule; props?: Props; @@ -373,7 +358,7 @@ if (import.meta.vitest) { function assertValidOptions< Variants extends VariantGroups | undefined = undefined, ToggleVariants extends VariantDefinitions | undefined = undefined, - Props extends ComplexPropDefinition | undefined = undefined + Props extends ComplexPropDefinitions | undefined = undefined >(options: PatternOptions) { assertType>(options); return options; @@ -621,17 +606,10 @@ if (import.meta.vitest) { props: ["color", "background"] }); - // Property with base value - assertValidOptions({ - props: { - background: { base: "red" } - } - }); - - // Property with aliased + // Property object assertValidOptions({ props: { - size: ["padding", "margin"] + size: { targets: ["padding", "margin"] } } }); assertValidOptions({ @@ -641,17 +619,11 @@ if (import.meta.vitest) { }); // Complex Props - assertValidOptions({ - props: ["color", "background", { size: ["padding", "margin"] }] - }); assertValidOptions({ props: [ "color", - { - background: { base: "red" }, - contour: ["border", "outline"], - size: { base: "3px", targets: ["padding", "margin"] } - } + "background", + { size: { targets: ["padding", "margin"] } } ] }); });