clava 0.1.19 → 0.2.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.
package/src/index.ts CHANGED
@@ -14,8 +14,8 @@ import type {
14
14
  MergeVariants,
15
15
  ModalComponent,
16
16
  SplitPropsFunction,
17
+ StyleClassProps,
17
18
  StyleClassValue,
18
- StyleProps,
19
19
  StyleValue,
20
20
  VariantValues,
21
21
  Variants,
@@ -77,6 +77,7 @@ function assign<T extends object>(target: T, source: T): void {
77
77
  export type {
78
78
  ClassValue,
79
79
  StyleValue,
80
+ StyleClassProps,
80
81
  StyleClassValue,
81
82
  JSXProps,
82
83
  HTMLProps,
@@ -112,8 +113,7 @@ export interface CVConfig<
112
113
  computed?: Computed<MergeVariants<V, CV, E>>;
113
114
  }
114
115
 
115
- interface CreateParams<M extends Mode> {
116
- defaultMode?: M;
116
+ interface CreateParams {
117
117
  transformClass?: (className: string) => string;
118
118
  }
119
119
 
@@ -839,10 +839,9 @@ function createResolveDefaults(
839
839
  /**
840
840
  * Creates the cv and cx functions.
841
841
  */
842
- export function create<M extends Mode = "jsx">({
843
- defaultMode = "jsx" as M,
842
+ export function create({
844
843
  transformClass = (className) => className,
845
- }: CreateParams<M> = {}) {
844
+ }: CreateParams = {}) {
846
845
  const cx = (...classes: ClsxClassValue[]) => transformClass(clsx(...classes));
847
846
 
848
847
  const cv = <
@@ -851,12 +850,13 @@ export function create<M extends Mode = "jsx">({
851
850
  const E extends AnyComponent[] = [],
852
851
  >(
853
852
  config: CVConfig<V, CV, E> = {},
854
- ): CVComponent<V, CV, E, StyleProps[M]> => {
853
+ ): CVComponent<V, CV, E> => {
855
854
  type MergedVariants = MergeVariants<V, CV, E>;
856
855
 
857
856
  const variantKeys = collectVariantKeys(config);
858
857
  const disabledVariantKeys = collectDisabledVariantKeys(config);
859
858
  const disabledVariantValues = collectDisabledVariantValues(config);
859
+ const inputPropsKeys = ["class", "className", "style", ...variantKeys];
860
860
 
861
861
  const getPropsKeys = (mode: Mode) => [
862
862
  getClassPropertyName(mode),
@@ -970,6 +970,75 @@ export function create<M extends Mode = "jsx">({
970
970
  };
971
971
  };
972
972
 
973
+ const getVariants = (variants?: VariantValues<MergedVariants>) => {
974
+ const variantProps = variants ?? {};
975
+ const resolvedVariants = resolveVariants(config, variantProps);
976
+ // Run computed function to get variants set via setVariants and
977
+ // setDefaultVariants
978
+ const { updatedVariants } = runComputedFunction(
979
+ config,
980
+ resolvedVariants,
981
+ variantProps,
982
+ );
983
+ return updatedVariants as VariantValues<MergedVariants>;
984
+ };
985
+
986
+ // Compute base class (without variants) - includes extended base classes
987
+ const extendedBaseClasses: ClassValue[] = [];
988
+ if (config.extend) {
989
+ for (const ext of config.extend) {
990
+ const meta = getComponentMeta(ext);
991
+ extendedBaseClasses.push(meta?.baseClass ?? "");
992
+ }
993
+ }
994
+ const baseClass = cx(
995
+ ...(extendedBaseClasses as ClsxClassValue[]),
996
+ config.class as ClsxClassValue,
997
+ );
998
+
999
+ // Compute static defaults once at creation time (without triggering
1000
+ // computed functions)
1001
+ const staticDefaults = collectStaticDefaults(config);
1002
+
1003
+ const initializeComponent = <
1004
+ R extends ComponentResult,
1005
+ T extends ModalComponent<MergedVariants, R>,
1006
+ >(
1007
+ component: T,
1008
+ propsKeys: string[],
1009
+ ): T => {
1010
+ component.class = (props: ComponentProps<MergedVariants> = {}) => {
1011
+ return computeResult(props).className;
1012
+ };
1013
+
1014
+ component.getVariants = getVariants;
1015
+ component.keys = propsKeys;
1016
+ component.variantKeys = variantKeys;
1017
+ component.propKeys = propsKeys;
1018
+
1019
+ // Store internal metadata hidden from public types
1020
+ setComponentMeta(component, {
1021
+ baseClass,
1022
+ staticDefaults,
1023
+ resolveDefaults: createResolveDefaults(config),
1024
+ });
1025
+
1026
+ return component;
1027
+ };
1028
+
1029
+ const createDefaultComponent = (): CVComponent<V, CV, E> => {
1030
+ const component = ((props: ComponentProps<MergedVariants> = {}) => {
1031
+ const { className, style } = computeResult(props);
1032
+ return { class: className, style };
1033
+ }) as CVComponent<V, CV, E>;
1034
+
1035
+ component.style = (props: ComponentProps<MergedVariants> = {}) => {
1036
+ return computeResult(props).style;
1037
+ };
1038
+
1039
+ return initializeComponent(component, inputPropsKeys);
1040
+ };
1041
+
973
1042
  const createModalComponent = <R extends ComponentResult>(
974
1043
  mode: Mode,
975
1044
  ): ModalComponent<MergedVariants, R> => {
@@ -998,53 +1067,10 @@ export function create<M extends Mode = "jsx">({
998
1067
  if (mode === "html") return styleValueToHTMLStyle(style);
999
1068
  return styleValueToHTMLObjStyle(style);
1000
1069
  };
1001
-
1002
- component.getVariants = (variants?: VariantValues<MergedVariants>) => {
1003
- const variantProps = variants ?? {};
1004
- const resolvedVariants = resolveVariants(config, variantProps);
1005
- // Run computed function to get variants set via setVariants and
1006
- // setDefaultVariants
1007
- const { updatedVariants } = runComputedFunction(
1008
- config,
1009
- resolvedVariants,
1010
- variantProps,
1011
- );
1012
- return updatedVariants as VariantValues<MergedVariants>;
1013
- };
1014
-
1015
- component.keys = propsKeys;
1016
- component.variantKeys = variantKeys;
1017
- component.propKeys = propsKeys;
1018
-
1019
- // Compute base class (without variants) - includes extended base classes
1020
- const extendedBaseClasses: ClassValue[] = [];
1021
- if (config.extend) {
1022
- for (const ext of config.extend) {
1023
- const meta = getComponentMeta(ext);
1024
- extendedBaseClasses.push(meta?.baseClass ?? "");
1025
- }
1026
- }
1027
- const baseClass = cx(
1028
- ...(extendedBaseClasses as ClsxClassValue[]),
1029
- config.class as ClsxClassValue,
1030
- );
1031
-
1032
- // Compute static defaults once at creation time (without triggering
1033
- // computed functions)
1034
- const staticDefaults = collectStaticDefaults(config);
1035
-
1036
- // Store internal metadata hidden from public types
1037
- setComponentMeta(component, {
1038
- baseClass,
1039
- staticDefaults,
1040
- resolveDefaults: createResolveDefaults(config),
1041
- });
1042
-
1043
- return component;
1070
+ return initializeComponent(component, propsKeys);
1044
1071
  };
1045
1072
 
1046
- // Create the default modal component
1047
- const defaultComponent = createModalComponent<StyleProps[M]>(defaultMode);
1073
+ const defaultComponent = createDefaultComponent();
1048
1074
 
1049
1075
  // Create all modal variants
1050
1076
  const jsxComponent = createModalComponent<JSXProps>("jsx");
@@ -1052,7 +1078,7 @@ export function create<M extends Mode = "jsx">({
1052
1078
  const htmlObjComponent = createModalComponent<HTMLObjProps>("htmlObj");
1053
1079
 
1054
1080
  // Build the final component
1055
- const component = defaultComponent as CVComponent<V, CV, E, StyleProps[M]>;
1081
+ const component = defaultComponent;
1056
1082
  component.jsx = jsxComponent;
1057
1083
  component.html = htmlComponent;
1058
1084
  component.htmlObj = htmlObjComponent;
package/src/types.ts CHANGED
@@ -32,13 +32,22 @@ export interface HTMLObjProps {
32
32
  style: HTMLCSSProperties;
33
33
  }
34
34
 
35
+ export interface StyleClassProps {
36
+ class: string;
37
+ style: StyleValue;
38
+ }
39
+
35
40
  export interface StyleProps {
36
41
  jsx: JSXProps;
37
42
  html: HTMLProps;
38
43
  htmlObj: HTMLObjProps;
39
44
  }
40
45
 
41
- export type ComponentResult = JSXProps | HTMLProps | HTMLObjProps;
46
+ export type ComponentResult =
47
+ | JSXProps
48
+ | HTMLProps
49
+ | HTMLObjProps
50
+ | StyleClassProps;
42
51
 
43
52
  type AllComponentResultKeys =
44
53
  | keyof JSXProps
@@ -160,16 +169,16 @@ export interface ModalComponent<V, R extends ComponentResult> {
160
169
  class: (props?: ComponentProps<V>) => string;
161
170
  style: (props?: ComponentProps<V>) => R["style"];
162
171
  getVariants: GetVariants<V>;
163
- keys: (keyof V | keyof R)[];
172
+ keys: (keyof V | keyof NullableComponentResult)[];
164
173
  variantKeys: (keyof V)[];
165
- propKeys: (keyof V | keyof R)[];
174
+ propKeys: (keyof V | keyof NullableComponentResult)[];
166
175
  }
167
176
 
168
177
  export interface CVComponent<
169
178
  V extends Variants = {},
170
179
  CV extends ComputedVariants = {},
171
180
  E extends AnyComponent[] = [],
172
- R extends ComponentResult = ComponentResult,
181
+ R extends ComponentResult = StyleClassProps,
173
182
  > extends ModalComponent<MergeVariants<V, CV, E>, R> {
174
183
  jsx: ModalComponent<MergeVariants<V, CV, E>, JSXProps>;
175
184
  html: ModalComponent<MergeVariants<V, CV, E>, HTMLProps>;
@@ -0,0 +1,176 @@
1
+ import { expect } from "vitest";
2
+ import { type VariantProps, create, cv as cvBase } from "../src/index.ts";
3
+ import type {
4
+ AnyComponent,
5
+ CVComponent,
6
+ ComponentResult,
7
+ ComputedVariants,
8
+ HTMLObjProps,
9
+ HTMLProps,
10
+ JSXProps,
11
+ StyleClassProps,
12
+ StyleProperty,
13
+ Variants,
14
+ } from "../src/types.ts";
15
+ import {
16
+ htmlObjStyleToStyleValue,
17
+ htmlStyleToStyleValue,
18
+ isHTMLObjStyle,
19
+ jsxStyleToStyleValue,
20
+ } from "../src/utils.ts";
21
+
22
+ const MODES = ["jsx", "html", "htmlObj"] as const;
23
+ type Mode = (typeof MODES)[number] | null;
24
+
25
+ export type HTMLProperties<T extends AnyComponent> = VariantProps<T> & {
26
+ id?: string;
27
+ class?: string;
28
+ className?: string;
29
+ style?: StyleProperty;
30
+ };
31
+
32
+ type ConfigParams = NonNullable<Parameters<typeof create>[0]>;
33
+
34
+ interface Config {
35
+ mode: Mode;
36
+ transformClass?: ConfigParams["transformClass"];
37
+ }
38
+
39
+ const transformClass = {
40
+ uppercase: (className: string) => className.toUpperCase(),
41
+ } satisfies Record<string, Config["transformClass"]>;
42
+
43
+ export const CONFIGS = {
44
+ default: { mode: null },
45
+ jsx: { mode: "jsx" },
46
+ html: { mode: "html" },
47
+ htmlObj: { mode: "htmlObj" },
48
+ uppercase: {
49
+ mode: null,
50
+ transformClass: transformClass.uppercase,
51
+ },
52
+ } satisfies Record<string, Config>;
53
+
54
+ export function getConfigMode<T extends Config>(config: T): T["mode"] {
55
+ if (!("mode" in config)) return null;
56
+ return config.mode;
57
+ }
58
+
59
+ export function getConfigTransformClass(config: Config) {
60
+ if (!("transformClass" in config) || !config.transformClass) {
61
+ return (className: string) => className;
62
+ }
63
+ return config.transformClass;
64
+ }
65
+
66
+ export function getConfigDescription(config: Config) {
67
+ for (const [name, currentConfig] of Object.entries(CONFIGS)) {
68
+ if (currentConfig !== config) continue;
69
+ return name;
70
+ }
71
+ return "custom";
72
+ }
73
+
74
+ export function createCVFromConfig<T extends Config>(
75
+ config: T,
76
+ ): ReturnType<typeof create>["cv"] {
77
+ const transformClass = getConfigTransformClass(config);
78
+ const hasTransform = "transformClass" in config && config.transformClass;
79
+ if (!hasTransform) {
80
+ return cvBase;
81
+ }
82
+ const { cv } = create({ transformClass });
83
+ return cv;
84
+ }
85
+
86
+ export function getModeComponent<
87
+ M extends Mode,
88
+ V extends Variants = {},
89
+ CV extends ComputedVariants = {},
90
+ const E extends AnyComponent[] = [],
91
+ >(mode: M, component: CVComponent<V, CV, E>) {
92
+ if (!mode) return component;
93
+ return component[mode];
94
+ }
95
+
96
+ function getClass(props: ComponentResult) {
97
+ if ("class" in props) return props.class;
98
+ return props.className;
99
+ }
100
+
101
+ export function getClassPropertyName(config: Config) {
102
+ const mode = config.mode;
103
+ if (mode === "jsx" || mode === null) return "className";
104
+ return "class";
105
+ }
106
+
107
+ export function getExpectedPropsKeys(config: Config, ...variantKeys: string[]) {
108
+ if (config.mode === null) {
109
+ return ["class", "className", "style", ...variantKeys];
110
+ }
111
+ return [getClassPropertyName(config), "style", ...variantKeys];
112
+ }
113
+
114
+ export function assertDefaultProps(
115
+ props: ComponentResult,
116
+ ): asserts props is StyleClassProps {
117
+ if (!("class" in props)) {
118
+ expect.fail("Expected default props to have class");
119
+ }
120
+ if (!("style" in props) || typeof props.style !== "object") {
121
+ expect.fail("Expected default props to have style");
122
+ }
123
+ }
124
+
125
+ export function assertJSXProps(
126
+ props: ComponentResult,
127
+ ): asserts props is JSXProps {
128
+ if (!("className" in props)) {
129
+ expect.fail("Expected jsx props to have className");
130
+ }
131
+ if (!("style" in props) || typeof props.style !== "object") {
132
+ expect.fail("Expected jsx props to have style");
133
+ }
134
+ }
135
+
136
+ export function assertHTMLProps(
137
+ props: ComponentResult,
138
+ ): asserts props is HTMLProps {
139
+ if (!("class" in props)) {
140
+ expect.fail("Expected html props to have class");
141
+ }
142
+ if (!("style" in props) || typeof props.style !== "string") {
143
+ expect.fail("Expected html props to have style");
144
+ }
145
+ }
146
+
147
+ export function assertHTMLObjProps(
148
+ props: ComponentResult,
149
+ ): asserts props is HTMLObjProps {
150
+ if (!("class" in props)) {
151
+ expect.fail("Expected htmlObj props to have class");
152
+ }
153
+ if (!("style" in props) || typeof props.style !== "object") {
154
+ expect.fail("Expected htmlObj props to have style");
155
+ }
156
+ }
157
+
158
+ export function getStyle(props: Pick<ComponentResult, "style">) {
159
+ if (typeof props.style === "string") {
160
+ return htmlStyleToStyleValue(props.style);
161
+ }
162
+ if (typeof props.style === "object") {
163
+ if (isHTMLObjStyle(props.style)) {
164
+ return htmlObjStyleToStyleValue(props.style);
165
+ }
166
+ return jsxStyleToStyleValue(props.style);
167
+ }
168
+ return {};
169
+ }
170
+
171
+ export function getStyleClass(props: ComponentResult): Record<string, unknown> {
172
+ return {
173
+ ...getStyle(props),
174
+ class: getClass(props),
175
+ };
176
+ }