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/CHANGELOG.md +31 -0
- package/dist/index.d.ts +13 -20
- package/dist/index.js +49 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +79 -53
- package/src/types.ts +13 -4
- package/tests/_utils.ts +176 -0
- package/tests/class-style-test.ts +341 -0
- package/tests/component-api-test.ts +214 -0
- package/tests/computed-test.ts +800 -0
- package/tests/computed-variants-test.ts +298 -0
- package/tests/extend-test.ts +253 -0
- package/{src/test-language-service.ts → tests/language-service-test.ts} +3 -2
- package/{src/test-react.ts → tests/react-test.ts} +3 -3
- package/{src/test-solid.ts → tests/solid-test.ts} +3 -5
- package/tests/split-props-test.ts +590 -0
- package/tests/variants-test.ts +380 -0
- package/tsconfig.json +1 -1
- package/src/test.ts +0 -2873
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
|
|
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
|
|
843
|
-
defaultMode = "jsx" as M,
|
|
842
|
+
export function create({
|
|
844
843
|
transformClass = (className) => className,
|
|
845
|
-
}: CreateParams
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
172
|
+
keys: (keyof V | keyof NullableComponentResult)[];
|
|
164
173
|
variantKeys: (keyof V)[];
|
|
165
|
-
propKeys: (keyof V | keyof
|
|
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 =
|
|
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>;
|
package/tests/_utils.ts
ADDED
|
@@ -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
|
+
}
|