@static-ui/core 0.1.0-beta.1
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.d.ts +47 -0
- package/dist/index.js +255 -0
- package/package.json +59 -0
- package/src/generators/index.ts +4 -0
- package/src/generators/react.ts +54 -0
- package/src/generators/solid.ts +59 -0
- package/src/generators/svelte.ts +48 -0
- package/src/generators/vue.ts +70 -0
- package/src/index.ts +3 -0
- package/src/schemas/button.ts +46 -0
- package/src/types.ts +48 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type FrameworkId = "react" | "nextjs" | "vue" | "nuxt" | "solid" | "svelte" | "astro" | "remix";
|
|
2
|
+
interface ComponentSchema {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
category: string;
|
|
6
|
+
frameworks: FrameworkId[];
|
|
7
|
+
props: ComponentProp[];
|
|
8
|
+
dependencies: Record<FrameworkId, string[]>;
|
|
9
|
+
tailwindClasses: string;
|
|
10
|
+
slots: ComponentSlot[];
|
|
11
|
+
variants: Record<string, ComponentVariantDefinition>;
|
|
12
|
+
}
|
|
13
|
+
interface ComponentProp {
|
|
14
|
+
name: string;
|
|
15
|
+
type: string;
|
|
16
|
+
default?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface ComponentSlot {
|
|
21
|
+
name: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
required?: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface ComponentVariantDefinition {
|
|
26
|
+
values: Record<string, string>;
|
|
27
|
+
default: string;
|
|
28
|
+
}
|
|
29
|
+
interface GeneratedComponent {
|
|
30
|
+
framework: FrameworkId;
|
|
31
|
+
name: string;
|
|
32
|
+
content: string;
|
|
33
|
+
extension: string;
|
|
34
|
+
dependencies: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare const buttonSchema: ComponentSchema;
|
|
38
|
+
|
|
39
|
+
declare function generateReactComponent(schema: ComponentSchema): GeneratedComponent;
|
|
40
|
+
|
|
41
|
+
declare function generateVueComponent(schema: ComponentSchema): GeneratedComponent;
|
|
42
|
+
|
|
43
|
+
declare function generateSolidComponent(schema: ComponentSchema): GeneratedComponent;
|
|
44
|
+
|
|
45
|
+
declare function generateSvelteComponent(schema: ComponentSchema): GeneratedComponent;
|
|
46
|
+
|
|
47
|
+
export { type ComponentProp, type ComponentSchema, type ComponentSlot, type ComponentVariantDefinition, type FrameworkId, type GeneratedComponent, buttonSchema, generateReactComponent, generateSolidComponent, generateSvelteComponent, generateVueComponent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// src/schemas/button.ts
|
|
2
|
+
var buttonSchema = {
|
|
3
|
+
name: "button",
|
|
4
|
+
description: "A clickable element that triggers an action",
|
|
5
|
+
category: "actions",
|
|
6
|
+
frameworks: ["react", "nextjs", "vue", "nuxt", "solid", "svelte"],
|
|
7
|
+
props: [
|
|
8
|
+
{ name: "variant", type: "'default' | 'outline' | 'destructive'", default: "default", description: "Button visual style" },
|
|
9
|
+
{ name: "size", type: "'default' | 'sm' | 'lg'", default: "default", description: "Button size" },
|
|
10
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Whether the button is disabled" },
|
|
11
|
+
{ name: "children", type: "React.ReactNode", description: "Button content", required: true }
|
|
12
|
+
],
|
|
13
|
+
dependencies: {
|
|
14
|
+
react: ["@base-ui/react", "class-variance-authority", "clsx", "tailwind-merge"],
|
|
15
|
+
nextjs: ["@base-ui/react", "class-variance-authority", "clsx", "tailwind-merge"],
|
|
16
|
+
vue: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
17
|
+
nuxt: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
18
|
+
solid: ["clsx", "tailwind-merge"],
|
|
19
|
+
svelte: ["clsx", "tailwind-merge"],
|
|
20
|
+
astro: [],
|
|
21
|
+
remix: ["@base-ui/react", "class-variance-authority", "clsx", "tailwind-merge"]
|
|
22
|
+
},
|
|
23
|
+
tailwindClasses: "inline-flex items-center justify-center font-medium rounded-md text-sm transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[focus-visible]:ring-2 data-[focus-visible]:ring-ring data-[focus-visible]:ring-offset-2",
|
|
24
|
+
slots: [
|
|
25
|
+
{ name: "root", description: "The button element itself" }
|
|
26
|
+
],
|
|
27
|
+
variants: {
|
|
28
|
+
variant: {
|
|
29
|
+
values: {
|
|
30
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
31
|
+
outline: "border border-border bg-background text-foreground hover:bg-muted",
|
|
32
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
33
|
+
},
|
|
34
|
+
default: "default"
|
|
35
|
+
},
|
|
36
|
+
size: {
|
|
37
|
+
values: {
|
|
38
|
+
default: "h-10 px-4 py-2",
|
|
39
|
+
sm: "h-9 px-3",
|
|
40
|
+
lg: "h-11 px-8"
|
|
41
|
+
},
|
|
42
|
+
default: "default"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/generators/react.ts
|
|
48
|
+
function generateReactComponent(schema) {
|
|
49
|
+
const variantsCode = schema.variants ? Object.entries(schema.variants).reduce((acc, [name, variant]) => {
|
|
50
|
+
const entries = Object.entries(variant.values).map(([k, v]) => ` ${k}: "${v}",`).join("\n");
|
|
51
|
+
return `${acc}
|
|
52
|
+
${name}: {
|
|
53
|
+
${entries}
|
|
54
|
+
},
|
|
55
|
+
defaultVariants: {
|
|
56
|
+
${name}: "${variant.default}",
|
|
57
|
+
},`;
|
|
58
|
+
}, `const ${schema.name}Variants = cva("${schema.tailwindClasses}", {`) + "\n });" : "";
|
|
59
|
+
const content = `import * as React from "react"
|
|
60
|
+
import { Button as BaseButton } from "@base-ui/react/button"
|
|
61
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
62
|
+
import { cn } from "@/lib/utils"
|
|
63
|
+
|
|
64
|
+
${variantsCode}
|
|
65
|
+
|
|
66
|
+
export interface ${capitalize(schema.name)}Props
|
|
67
|
+
extends React.ComponentPropsWithoutRef<typeof BaseButton>,
|
|
68
|
+
VariantProps<typeof ${schema.name}Variants> {}
|
|
69
|
+
|
|
70
|
+
const ${capitalize(schema.name)} = React.forwardRef<HTMLButtonElement, ${capitalize(schema.name)}Props>(
|
|
71
|
+
({ className, variant, size, ...props }, ref) => {
|
|
72
|
+
return (
|
|
73
|
+
<BaseButton
|
|
74
|
+
ref={ref}
|
|
75
|
+
className={cn(${schema.name}Variants({ variant, size, className }))}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
${capitalize(schema.name)}.displayName = "${capitalize(schema.name)}"
|
|
83
|
+
|
|
84
|
+
export { ${capitalize(schema.name)}, ${schema.name}Variants }
|
|
85
|
+
`;
|
|
86
|
+
return {
|
|
87
|
+
framework: "react",
|
|
88
|
+
name: schema.name,
|
|
89
|
+
content,
|
|
90
|
+
extension: ".tsx",
|
|
91
|
+
dependencies: schema.dependencies.react
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function capitalize(s) {
|
|
95
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/generators/vue.ts
|
|
99
|
+
function generateVueComponent(schema) {
|
|
100
|
+
const variantProps = schema.variants ? Object.entries(schema.variants).map(([name, v]) => ` ${name}: {
|
|
101
|
+
type: String,
|
|
102
|
+
default: "${v.default}",
|
|
103
|
+
validator: (value: string) => ["${Object.keys(v.values).join('", "')}"].includes(value),
|
|
104
|
+
},`).join("\n") : "";
|
|
105
|
+
const variantClasses = schema.variants ? Object.entries(schema.variants).map(([name, v]) => {
|
|
106
|
+
const entries = Object.entries(v.values).map(([k, cls]) => ` case "${k}": classes.push("${cls}"); break;`).join("\n");
|
|
107
|
+
return ` const ${name}Classes = computed(() => {
|
|
108
|
+
const classes: string[] = [];
|
|
109
|
+
switch (props.${name}) {
|
|
110
|
+
${entries}
|
|
111
|
+
}
|
|
112
|
+
return classes;
|
|
113
|
+
});`;
|
|
114
|
+
}).join("\n\n") : "";
|
|
115
|
+
const mergedClasses = schema.variants ? `const mergedClasses = computed(() => {
|
|
116
|
+
return cn("${schema.tailwindClasses}", ...${Object.keys(schema.variants).map((v) => `${v}Classes.value`).join(", ")});
|
|
117
|
+
});` : `const mergedClasses = "${schema.tailwindClasses}";`;
|
|
118
|
+
const content = `<template>
|
|
119
|
+
<button
|
|
120
|
+
:class="mergedClasses"
|
|
121
|
+
:disabled="disabled"
|
|
122
|
+
v-bind="$attrs"
|
|
123
|
+
>
|
|
124
|
+
<slot />
|
|
125
|
+
</button>
|
|
126
|
+
</template>
|
|
127
|
+
|
|
128
|
+
<script setup lang="ts">
|
|
129
|
+
import { computed } from "vue"
|
|
130
|
+
import { cn } from "@/lib/utils"
|
|
131
|
+
|
|
132
|
+
interface Props {
|
|
133
|
+
variant?: "${Object.keys(schema.variants?.variant?.values || {}).join('" | "')}"
|
|
134
|
+
size?: "${Object.keys(schema.variants?.size?.values || {}).join('" | "')}"
|
|
135
|
+
disabled?: boolean
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
139
|
+
variant: "${schema.variants?.variant?.default || "default"}",
|
|
140
|
+
size: "${schema.variants?.size?.default || "default"}",
|
|
141
|
+
disabled: false,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
${variantClasses}
|
|
145
|
+
|
|
146
|
+
${mergedClasses}
|
|
147
|
+
</script>
|
|
148
|
+
`;
|
|
149
|
+
return {
|
|
150
|
+
framework: "vue",
|
|
151
|
+
name: schema.name,
|
|
152
|
+
content,
|
|
153
|
+
extension: ".vue",
|
|
154
|
+
dependencies: schema.dependencies.vue
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/generators/solid.ts
|
|
159
|
+
function generateSolidComponent(schema) {
|
|
160
|
+
const variantEntries = schema.variants ? Object.entries(schema.variants).map(([name, v]) => {
|
|
161
|
+
const entries = Object.entries(v.values).map(([k, cls]) => ` ${k}: "${cls}"`).join(",\n");
|
|
162
|
+
return `const ${schema.name}${capitalize2(name)} = {
|
|
163
|
+
${entries},
|
|
164
|
+
} as const;`;
|
|
165
|
+
}).join("\n\n") : "";
|
|
166
|
+
const variantFn = schema.variants ? `function get${capitalize2(schema.name)}Classes(props: ${capitalize2(schema.name)}Props) {
|
|
167
|
+
const classes = ["${schema.tailwindClasses}"];
|
|
168
|
+
${Object.entries(schema.variants).map(([name, v]) => {
|
|
169
|
+
return `if (props.${name}) classes.push(${schema.name}${capitalize2(name)}[props.${name}]);`;
|
|
170
|
+
}).join("\n ")}
|
|
171
|
+
return cn(...classes);
|
|
172
|
+
}` : "";
|
|
173
|
+
const content = `import type { ComponentProps } from "solid-js"
|
|
174
|
+
import { cn } from "@/lib/utils"
|
|
175
|
+
|
|
176
|
+
${variantEntries}
|
|
177
|
+
|
|
178
|
+
export interface ${capitalize2(schema.name)}Props {
|
|
179
|
+
variant?: "${Object.keys(schema.variants?.variant?.values || {}).join('" | "')}"
|
|
180
|
+
size?: "${Object.keys(schema.variants?.size?.values || {}).join('" | "')}"
|
|
181
|
+
disabled?: boolean
|
|
182
|
+
class?: string
|
|
183
|
+
children?: any
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
${variantFn}
|
|
187
|
+
|
|
188
|
+
export function ${capitalize2(schema.name)}(props: ${capitalize2(schema.name)}Props) {
|
|
189
|
+
return (
|
|
190
|
+
<button
|
|
191
|
+
class={get${capitalize2(schema.name)}Classes(props)}
|
|
192
|
+
disabled={props.disabled}
|
|
193
|
+
>
|
|
194
|
+
{props.children}
|
|
195
|
+
</button>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
`;
|
|
199
|
+
return {
|
|
200
|
+
framework: "solid",
|
|
201
|
+
name: schema.name,
|
|
202
|
+
content,
|
|
203
|
+
extension: ".tsx",
|
|
204
|
+
dependencies: schema.dependencies.solid
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function capitalize2(s) {
|
|
208
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/generators/svelte.ts
|
|
212
|
+
function generateSvelteComponent(schema) {
|
|
213
|
+
const variantProps = schema.variants ? Object.entries(schema.variants).map(([name, v]) => {
|
|
214
|
+
return ` let ${name}: "${Object.keys(v.values).join('" | "')}" = "${v.default}";`;
|
|
215
|
+
}).join("\n") : "";
|
|
216
|
+
const variantClassMap = schema.variants ? Object.entries(schema.variants).map(([name, v]) => {
|
|
217
|
+
const mapEntries = Object.entries(v.values).map(([k, cls]) => ` "${k}": "${cls}"`).join(",\n");
|
|
218
|
+
return ` const ${name}Classes: Record<string, string> = {
|
|
219
|
+
${mapEntries},
|
|
220
|
+
};`;
|
|
221
|
+
}).join("\n\n") : "";
|
|
222
|
+
const scriptContent = `<script lang="ts">
|
|
223
|
+
import { cn } from "$lib/utils"
|
|
224
|
+
|
|
225
|
+
export let disabled = false;
|
|
226
|
+
${variantProps ? "\n" + variantProps : ""}
|
|
227
|
+
${variantClassMap ? "\n" + variantClassMap : ""}
|
|
228
|
+
|
|
229
|
+
$: classes = cn(
|
|
230
|
+
"${schema.tailwindClasses}",
|
|
231
|
+
${schema.variants ? Object.entries(schema.variants).map(([name]) => `${name}Classes[${name}]`).join(",\n ") : ""}
|
|
232
|
+
);
|
|
233
|
+
</script>
|
|
234
|
+
`;
|
|
235
|
+
const templateContent = `
|
|
236
|
+
<button class={classes} {disabled} on:click>
|
|
237
|
+
<slot />
|
|
238
|
+
</button>
|
|
239
|
+
`;
|
|
240
|
+
const content = scriptContent + templateContent;
|
|
241
|
+
return {
|
|
242
|
+
framework: "svelte",
|
|
243
|
+
name: schema.name,
|
|
244
|
+
content,
|
|
245
|
+
extension: ".svelte",
|
|
246
|
+
dependencies: schema.dependencies.svelte
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export {
|
|
250
|
+
buttonSchema,
|
|
251
|
+
generateReactComponent,
|
|
252
|
+
generateSolidComponent,
|
|
253
|
+
generateSvelteComponent,
|
|
254
|
+
generateVueComponent
|
|
255
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@static-ui/core",
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Static UI core - component schemas and multi-framework code generators. Source of truth for component definitions.",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
27
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
28
|
+
"typecheck": "tsc --noEmit"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"static-ui",
|
|
32
|
+
"core",
|
|
33
|
+
"schemas",
|
|
34
|
+
"codegen",
|
|
35
|
+
"components",
|
|
36
|
+
"multi-framework"
|
|
37
|
+
],
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/Anshul563/static-ui.git",
|
|
41
|
+
"directory": "packages/core"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://get-staticui.vercel.app",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/Anshul563/static-ui/issues"
|
|
46
|
+
},
|
|
47
|
+
"author": {
|
|
48
|
+
"name": "Anshul Shakya",
|
|
49
|
+
"url": "https://github.com/Anshul563"
|
|
50
|
+
},
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"typescript": "^5.3.3"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.11.0",
|
|
57
|
+
"tsup": "^8.0.1"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ComponentSchema, GeneratedComponent } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export function generateReactComponent(schema: ComponentSchema): GeneratedComponent {
|
|
4
|
+
const variantsCode = schema.variants ? Object.entries(schema.variants).reduce((acc, [name, variant]) => {
|
|
5
|
+
const entries = Object.entries(variant.values).map(([k, v]) => ` ${k}: "${v}",`).join("\n");
|
|
6
|
+
return `${acc}
|
|
7
|
+
${name}: {
|
|
8
|
+
${entries}
|
|
9
|
+
},
|
|
10
|
+
defaultVariants: {
|
|
11
|
+
${name}: "${variant.default}",
|
|
12
|
+
},`;
|
|
13
|
+
}, `const ${schema.name}Variants = cva("${schema.tailwindClasses}", {`) + "\n });" : "";
|
|
14
|
+
|
|
15
|
+
const content = `import * as React from "react"
|
|
16
|
+
import { Button as BaseButton } from "@base-ui/react/button"
|
|
17
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
18
|
+
import { cn } from "@/lib/utils"
|
|
19
|
+
|
|
20
|
+
${variantsCode}
|
|
21
|
+
|
|
22
|
+
export interface ${capitalize(schema.name)}Props
|
|
23
|
+
extends React.ComponentPropsWithoutRef<typeof BaseButton>,
|
|
24
|
+
VariantProps<typeof ${schema.name}Variants> {}
|
|
25
|
+
|
|
26
|
+
const ${capitalize(schema.name)} = React.forwardRef<HTMLButtonElement, ${capitalize(schema.name)}Props>(
|
|
27
|
+
({ className, variant, size, ...props }, ref) => {
|
|
28
|
+
return (
|
|
29
|
+
<BaseButton
|
|
30
|
+
ref={ref}
|
|
31
|
+
className={cn(${schema.name}Variants({ variant, size, className }))}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
${capitalize(schema.name)}.displayName = "${capitalize(schema.name)}"
|
|
39
|
+
|
|
40
|
+
export { ${capitalize(schema.name)}, ${schema.name}Variants }
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
framework: "react",
|
|
45
|
+
name: schema.name,
|
|
46
|
+
content,
|
|
47
|
+
extension: ".tsx",
|
|
48
|
+
dependencies: schema.dependencies.react,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function capitalize(s: string): string {
|
|
53
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
54
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ComponentSchema, GeneratedComponent } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export function generateSolidComponent(schema: ComponentSchema): GeneratedComponent {
|
|
4
|
+
const variantEntries = schema.variants
|
|
5
|
+
? Object.entries(schema.variants).map(([name, v]) => {
|
|
6
|
+
const entries = Object.entries(v.values).map(([k, cls]) => ` ${k}: "${cls}"`).join(",\n");
|
|
7
|
+
return `const ${schema.name}${capitalize(name)} = {\n${entries},\n } as const;`;
|
|
8
|
+
}).join("\n\n")
|
|
9
|
+
: "";
|
|
10
|
+
|
|
11
|
+
const variantFn = schema.variants
|
|
12
|
+
? `function get${capitalize(schema.name)}Classes(props: ${capitalize(schema.name)}Props) {
|
|
13
|
+
const classes = ["${schema.tailwindClasses}"];
|
|
14
|
+
${Object.entries(schema.variants).map(([name, v]) => {
|
|
15
|
+
return `if (props.${name}) classes.push(${schema.name}${capitalize(name)}[props.${name}]);`;
|
|
16
|
+
}).join("\n ")}
|
|
17
|
+
return cn(...classes);
|
|
18
|
+
}`
|
|
19
|
+
: "";
|
|
20
|
+
|
|
21
|
+
const content = `import type { ComponentProps } from "solid-js"
|
|
22
|
+
import { cn } from "@/lib/utils"
|
|
23
|
+
|
|
24
|
+
${variantEntries}
|
|
25
|
+
|
|
26
|
+
export interface ${capitalize(schema.name)}Props {
|
|
27
|
+
variant?: "${Object.keys(schema.variants?.variant?.values || {}).join('" | "')}"
|
|
28
|
+
size?: "${Object.keys(schema.variants?.size?.values || {}).join('" | "')}"
|
|
29
|
+
disabled?: boolean
|
|
30
|
+
class?: string
|
|
31
|
+
children?: any
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
${variantFn}
|
|
35
|
+
|
|
36
|
+
export function ${capitalize(schema.name)}(props: ${capitalize(schema.name)}Props) {
|
|
37
|
+
return (
|
|
38
|
+
<button
|
|
39
|
+
class={get${capitalize(schema.name)}Classes(props)}
|
|
40
|
+
disabled={props.disabled}
|
|
41
|
+
>
|
|
42
|
+
{props.children}
|
|
43
|
+
</button>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
framework: "solid",
|
|
50
|
+
name: schema.name,
|
|
51
|
+
content,
|
|
52
|
+
extension: ".tsx",
|
|
53
|
+
dependencies: schema.dependencies.solid,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function capitalize(s: string): string {
|
|
58
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
59
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ComponentSchema, GeneratedComponent } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export function generateSvelteComponent(schema: ComponentSchema): GeneratedComponent {
|
|
4
|
+
const variantProps = schema.variants
|
|
5
|
+
? Object.entries(schema.variants).map(([name, v]) => {
|
|
6
|
+
return ` let ${name}: "${Object.keys(v.values).join('" | "')}" = "${v.default}";`;
|
|
7
|
+
}).join("\n")
|
|
8
|
+
: "";
|
|
9
|
+
|
|
10
|
+
const variantClassMap = schema.variants
|
|
11
|
+
? Object.entries(schema.variants).map(([name, v]) => {
|
|
12
|
+
const mapEntries = Object.entries(v.values).map(([k, cls]) => ` "${k}": "${cls}"`).join(",\n");
|
|
13
|
+
return ` const ${name}Classes: Record<string, string> = {\n${mapEntries},\n };`;
|
|
14
|
+
}).join("\n\n")
|
|
15
|
+
: "";
|
|
16
|
+
|
|
17
|
+
const scriptContent = `<script lang="ts">
|
|
18
|
+
import { cn } from "$lib/utils"
|
|
19
|
+
|
|
20
|
+
export let disabled = false;
|
|
21
|
+
${variantProps ? "\n" + variantProps : ""}
|
|
22
|
+
${variantClassMap ? "\n" + variantClassMap : ""}
|
|
23
|
+
|
|
24
|
+
$: classes = cn(
|
|
25
|
+
"${schema.tailwindClasses}",
|
|
26
|
+
${schema.variants
|
|
27
|
+
? Object.entries(schema.variants).map(([name]) => `${name}Classes[${name}]`).join(",\n ")
|
|
28
|
+
: ""}
|
|
29
|
+
);
|
|
30
|
+
</script>
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const templateContent = `
|
|
34
|
+
<button class={classes} {disabled} on:click>
|
|
35
|
+
<slot />
|
|
36
|
+
</button>
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const content = scriptContent + templateContent;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
framework: "svelte",
|
|
43
|
+
name: schema.name,
|
|
44
|
+
content,
|
|
45
|
+
extension: ".svelte",
|
|
46
|
+
dependencies: schema.dependencies.svelte,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ComponentSchema, GeneratedComponent } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export function generateVueComponent(schema: ComponentSchema): GeneratedComponent {
|
|
4
|
+
const variantProps = schema.variants
|
|
5
|
+
? Object.entries(schema.variants).map(([name, v]) => ` ${name}: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: "${v.default}",
|
|
8
|
+
validator: (value: string) => ["${Object.keys(v.values).join('", "')}"].includes(value),
|
|
9
|
+
},`).join("\n")
|
|
10
|
+
: "";
|
|
11
|
+
|
|
12
|
+
const variantClasses = schema.variants
|
|
13
|
+
? Object.entries(schema.variants).map(([name, v]) => {
|
|
14
|
+
const entries = Object.entries(v.values).map(([k, cls]) => ` case "${k}": classes.push("${cls}"); break;`).join("\n");
|
|
15
|
+
return ` const ${name}Classes = computed(() => {
|
|
16
|
+
const classes: string[] = [];
|
|
17
|
+
switch (props.${name}) {
|
|
18
|
+
${entries}
|
|
19
|
+
}
|
|
20
|
+
return classes;
|
|
21
|
+
});`;
|
|
22
|
+
}).join("\n\n")
|
|
23
|
+
: "";
|
|
24
|
+
|
|
25
|
+
const mergedClasses = schema.variants
|
|
26
|
+
? `const mergedClasses = computed(() => {
|
|
27
|
+
return cn("${schema.tailwindClasses}", ...${Object.keys(schema.variants).map(v => `${v}Classes.value`).join(", ")});
|
|
28
|
+
});`
|
|
29
|
+
: `const mergedClasses = "${schema.tailwindClasses}";`;
|
|
30
|
+
|
|
31
|
+
const content = `<template>
|
|
32
|
+
<button
|
|
33
|
+
:class="mergedClasses"
|
|
34
|
+
:disabled="disabled"
|
|
35
|
+
v-bind="$attrs"
|
|
36
|
+
>
|
|
37
|
+
<slot />
|
|
38
|
+
</button>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
import { computed } from "vue"
|
|
43
|
+
import { cn } from "@/lib/utils"
|
|
44
|
+
|
|
45
|
+
interface Props {
|
|
46
|
+
variant?: "${Object.keys(schema.variants?.variant?.values || {}).join('" | "')}"
|
|
47
|
+
size?: "${Object.keys(schema.variants?.size?.values || {}).join('" | "')}"
|
|
48
|
+
disabled?: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
52
|
+
variant: "${schema.variants?.variant?.default || "default"}",
|
|
53
|
+
size: "${schema.variants?.size?.default || "default"}",
|
|
54
|
+
disabled: false,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
${variantClasses}
|
|
58
|
+
|
|
59
|
+
${mergedClasses}
|
|
60
|
+
</script>
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
framework: "vue",
|
|
65
|
+
name: schema.name,
|
|
66
|
+
content,
|
|
67
|
+
extension: ".vue",
|
|
68
|
+
dependencies: schema.dependencies.vue,
|
|
69
|
+
};
|
|
70
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type { ComponentSchema, GeneratedComponent, ComponentProp, ComponentSlot, ComponentVariantDefinition, FrameworkId } from "./types.js";
|
|
2
|
+
export { buttonSchema } from "./schemas/button.js";
|
|
3
|
+
export { generateReactComponent, generateVueComponent, generateSolidComponent, generateSvelteComponent } from "./generators/index.js";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ComponentSchema } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const buttonSchema: ComponentSchema = {
|
|
4
|
+
name: "button",
|
|
5
|
+
description: "A clickable element that triggers an action",
|
|
6
|
+
category: "actions",
|
|
7
|
+
frameworks: ["react", "nextjs", "vue", "nuxt", "solid", "svelte"],
|
|
8
|
+
props: [
|
|
9
|
+
{ name: "variant", type: "'default' | 'outline' | 'destructive'", default: "default", description: "Button visual style" },
|
|
10
|
+
{ name: "size", type: "'default' | 'sm' | 'lg'", default: "default", description: "Button size" },
|
|
11
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Whether the button is disabled" },
|
|
12
|
+
{ name: "children", type: "React.ReactNode", description: "Button content", required: true },
|
|
13
|
+
],
|
|
14
|
+
dependencies: {
|
|
15
|
+
react: ["@base-ui/react", "class-variance-authority", "clsx", "tailwind-merge"],
|
|
16
|
+
nextjs: ["@base-ui/react", "class-variance-authority", "clsx", "tailwind-merge"],
|
|
17
|
+
vue: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
18
|
+
nuxt: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
19
|
+
solid: ["clsx", "tailwind-merge"],
|
|
20
|
+
svelte: ["clsx", "tailwind-merge"],
|
|
21
|
+
astro: [],
|
|
22
|
+
remix: ["@base-ui/react", "class-variance-authority", "clsx", "tailwind-merge"],
|
|
23
|
+
},
|
|
24
|
+
tailwindClasses: "inline-flex items-center justify-center font-medium rounded-md text-sm transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[focus-visible]:ring-2 data-[focus-visible]:ring-ring data-[focus-visible]:ring-offset-2",
|
|
25
|
+
slots: [
|
|
26
|
+
{ name: "root", description: "The button element itself" },
|
|
27
|
+
],
|
|
28
|
+
variants: {
|
|
29
|
+
variant: {
|
|
30
|
+
values: {
|
|
31
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
32
|
+
outline: "border border-border bg-background text-foreground hover:bg-muted",
|
|
33
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
34
|
+
},
|
|
35
|
+
default: "default",
|
|
36
|
+
},
|
|
37
|
+
size: {
|
|
38
|
+
values: {
|
|
39
|
+
default: "h-10 px-4 py-2",
|
|
40
|
+
sm: "h-9 px-3",
|
|
41
|
+
lg: "h-11 px-8",
|
|
42
|
+
},
|
|
43
|
+
default: "default",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type FrameworkId =
|
|
2
|
+
| "react"
|
|
3
|
+
| "nextjs"
|
|
4
|
+
| "vue"
|
|
5
|
+
| "nuxt"
|
|
6
|
+
| "solid"
|
|
7
|
+
| "svelte"
|
|
8
|
+
| "astro"
|
|
9
|
+
| "remix";
|
|
10
|
+
|
|
11
|
+
export interface ComponentSchema {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
category: string;
|
|
15
|
+
frameworks: FrameworkId[];
|
|
16
|
+
props: ComponentProp[];
|
|
17
|
+
dependencies: Record<FrameworkId, string[]>;
|
|
18
|
+
tailwindClasses: string;
|
|
19
|
+
slots: ComponentSlot[];
|
|
20
|
+
variants: Record<string, ComponentVariantDefinition>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ComponentProp {
|
|
24
|
+
name: string;
|
|
25
|
+
type: string;
|
|
26
|
+
default?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
required?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ComponentSlot {
|
|
32
|
+
name: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
required?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ComponentVariantDefinition {
|
|
38
|
+
values: Record<string, string>;
|
|
39
|
+
default: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface GeneratedComponent {
|
|
43
|
+
framework: FrameworkId;
|
|
44
|
+
name: string;
|
|
45
|
+
content: string;
|
|
46
|
+
extension: string;
|
|
47
|
+
dependencies: string[];
|
|
48
|
+
}
|