Compare commits
4 Commits
d7ebc09cbb
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98dd7741a0 | ||
|
|
1ebe68efeb | ||
|
|
791a257294 | ||
|
|
3ed7e027c2 |
11
app/page.tsx
11
app/page.tsx
@@ -1,9 +1,14 @@
|
|||||||
|
import { Typography } from "@/components/elements/Typography"
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center w-full h-screen">
|
<div className="flex items-center justify-center w-full h-screen">
|
||||||
<h1 className="text-6xl font-bold font-heading text-black text-center">
|
<Typography
|
||||||
Start prompting
|
variant="h1"
|
||||||
</h1>
|
textAlign="center"
|
||||||
|
text="Start prompting"
|
||||||
|
className="text-6xl font-bold font-heading text-black"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
38
components/elements/Button.config.tsx
Normal file
38
components/elements/Button.config.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import type { ElementConfig } from "./types"
|
||||||
|
|
||||||
|
export const ButtonConfig: ElementConfig = {
|
||||||
|
Button: {
|
||||||
|
label: "Button",
|
||||||
|
fields: {
|
||||||
|
children: {
|
||||||
|
type: "text",
|
||||||
|
label: "Label",
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: "select",
|
||||||
|
label: "Variant",
|
||||||
|
options: [
|
||||||
|
{ label: "Default", value: "default" },
|
||||||
|
{ label: "Destructive", value: "destructive" },
|
||||||
|
{ label: "Outline", value: "outline" },
|
||||||
|
{ label: "Secondary", value: "secondary" },
|
||||||
|
{ label: "Ghost", value: "ghost" },
|
||||||
|
{ label: "Link", value: "link" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: "select",
|
||||||
|
label: "Size",
|
||||||
|
options: [
|
||||||
|
{ label: "Default", value: "default" },
|
||||||
|
{ label: "Extra Small", value: "xs" },
|
||||||
|
{ label: "Small", value: "sm" },
|
||||||
|
{ label: "Large", value: "lg" },
|
||||||
|
{ label: "Icon", value: "icon" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ButtonConfig
|
||||||
30
components/elements/Button.tsx
Normal file
30
components/elements/Button.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button as ShadcnButton,
|
||||||
|
type buttonVariants,
|
||||||
|
} from "@/components/ui/button"
|
||||||
|
import type { VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
export type ButtonVariant = NonNullable<
|
||||||
|
VariantProps<typeof buttonVariants>["variant"]
|
||||||
|
>
|
||||||
|
export type ButtonSize = NonNullable<
|
||||||
|
VariantProps<typeof buttonVariants>["size"]
|
||||||
|
>
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ComponentProps<typeof ShadcnButton> {
|
||||||
|
variant?: ButtonVariant
|
||||||
|
size?: ButtonSize
|
||||||
|
}
|
||||||
|
|
||||||
|
function Button({ variant = "default", size = "default", ...props }: ButtonProps) {
|
||||||
|
return (
|
||||||
|
<span className="inline-block">
|
||||||
|
<ShadcnButton variant={variant} size={size} {...props} />
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Button }
|
||||||
27
components/elements/Card.config.tsx
Normal file
27
components/elements/Card.config.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import type { ElementConfig } from "./types"
|
||||||
|
|
||||||
|
export const CardConfig: ElementConfig = {
|
||||||
|
Card: {
|
||||||
|
label: "Card",
|
||||||
|
fields: {
|
||||||
|
image: {
|
||||||
|
type: "text",
|
||||||
|
label: "Image URL",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: "text",
|
||||||
|
label: "Title",
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
type: "text",
|
||||||
|
label: "Subtitle",
|
||||||
|
},
|
||||||
|
tags: {
|
||||||
|
type: "tags",
|
||||||
|
label: "Tags",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardConfig
|
||||||
68
components/elements/Card.tsx
Normal file
68
components/elements/Card.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Card as ShadcnCard,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
} from "@/components/ui/card"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Image } from "./Image"
|
||||||
|
import { Typography } from "./Typography"
|
||||||
|
|
||||||
|
export interface CardProps extends React.ComponentProps<typeof ShadcnCard> {
|
||||||
|
image?: string
|
||||||
|
title?: string
|
||||||
|
subtitle?: string
|
||||||
|
tags?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Card({
|
||||||
|
image,
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
tags,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: CardProps) {
|
||||||
|
return (
|
||||||
|
<ShadcnCard
|
||||||
|
className={cn("overflow-hidden pt-0", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{image ? (
|
||||||
|
<Image
|
||||||
|
src={image}
|
||||||
|
alt={title ?? ""}
|
||||||
|
objectFit="cover"
|
||||||
|
width={600}
|
||||||
|
height={300}
|
||||||
|
className="h-48 w-full rounded-none"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<CardHeader>
|
||||||
|
{title ? <Typography variant="h5" text={title} /> : null}
|
||||||
|
{subtitle ? (
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
text={subtitle}
|
||||||
|
className="text-muted-foreground"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
{tags && tags.length > 0 ? (
|
||||||
|
<CardContent className="flex flex-wrap gap-2">
|
||||||
|
{tags.map((tag, index) => (
|
||||||
|
<Badge key={`${tag}-${index}`} variant="secondary">
|
||||||
|
{tag}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
) : null}
|
||||||
|
</ShadcnCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Card }
|
||||||
23
components/elements/Icon.config.tsx
Normal file
23
components/elements/Icon.config.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import type { ElementConfig } from "./types"
|
||||||
|
|
||||||
|
export const IconConfig: ElementConfig = {
|
||||||
|
Icon: {
|
||||||
|
label: "Icon",
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
type: "icon",
|
||||||
|
label: "Icon",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: "text",
|
||||||
|
label: "Size",
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
type: "text",
|
||||||
|
label: "Class Name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IconConfig
|
||||||
25
components/elements/Icon.tsx
Normal file
25
components/elements/Icon.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { DynamicIcon, type IconName } from "lucide-react/dynamic"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export interface IconProps
|
||||||
|
extends Omit<React.ComponentProps<typeof DynamicIcon>, "name"> {
|
||||||
|
name: IconName
|
||||||
|
size?: number
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Icon({ name, size = 24, className, ...props }: IconProps) {
|
||||||
|
return (
|
||||||
|
<DynamicIcon
|
||||||
|
name={name}
|
||||||
|
size={size}
|
||||||
|
className={cn(className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Icon }
|
||||||
|
export type { IconName }
|
||||||
42
components/elements/Image.config.tsx
Normal file
42
components/elements/Image.config.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import type { ElementConfig } from "./types"
|
||||||
|
|
||||||
|
export const ImageConfig: ElementConfig = {
|
||||||
|
Image: {
|
||||||
|
label: "Image",
|
||||||
|
fields: {
|
||||||
|
src: {
|
||||||
|
type: "image",
|
||||||
|
label: "Source URL",
|
||||||
|
},
|
||||||
|
alt: {
|
||||||
|
type: "text",
|
||||||
|
label: "Alt Text",
|
||||||
|
},
|
||||||
|
objectFit: {
|
||||||
|
type: "select",
|
||||||
|
label: "Object Fit",
|
||||||
|
options: [
|
||||||
|
{ label: "Cover", value: "cover" },
|
||||||
|
{ label: "Contain", value: "contain" },
|
||||||
|
{ label: "Fill", value: "fill" },
|
||||||
|
{ label: "None", value: "none" },
|
||||||
|
{ label: "Scale Down", value: "scale-down" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
type: "boolean",
|
||||||
|
label: "Circle",
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: "number",
|
||||||
|
label: "Width",
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: "number",
|
||||||
|
label: "Height",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageConfig
|
||||||
57
components/elements/Image.tsx
Normal file
57
components/elements/Image.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import NextImage from "next/image"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export type ObjectFit =
|
||||||
|
| "contain"
|
||||||
|
| "cover"
|
||||||
|
| "fill"
|
||||||
|
| "none"
|
||||||
|
| "scale-down"
|
||||||
|
|
||||||
|
const objectFitStyles: Record<ObjectFit, string> = {
|
||||||
|
contain: "object-contain",
|
||||||
|
cover: "object-cover",
|
||||||
|
fill: "object-fill",
|
||||||
|
none: "object-none",
|
||||||
|
"scale-down": "object-scale-down",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageProps {
|
||||||
|
src: string
|
||||||
|
alt?: string
|
||||||
|
objectFit?: ObjectFit
|
||||||
|
circle?: boolean
|
||||||
|
height?: number
|
||||||
|
width?: number
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Image({
|
||||||
|
src,
|
||||||
|
alt = "",
|
||||||
|
objectFit = "cover",
|
||||||
|
circle = false,
|
||||||
|
height = 300,
|
||||||
|
width = 300,
|
||||||
|
className,
|
||||||
|
}: ImageProps) {
|
||||||
|
return (
|
||||||
|
<span className="inline-block">
|
||||||
|
<NextImage
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
height={height}
|
||||||
|
width={width}
|
||||||
|
className={cn(
|
||||||
|
objectFitStyles[objectFit],
|
||||||
|
circle ? "rounded-full" : "rounded-md",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Image }
|
||||||
59
components/elements/Typography.config.tsx
Normal file
59
components/elements/Typography.config.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import type { ElementConfig } from "./types"
|
||||||
|
|
||||||
|
export const TypographyConfig: ElementConfig = {
|
||||||
|
Typography: {
|
||||||
|
label: "Typography",
|
||||||
|
fields: {
|
||||||
|
text: {
|
||||||
|
type: "textarea",
|
||||||
|
label: "Text",
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: "select",
|
||||||
|
label: "Variant",
|
||||||
|
options: [
|
||||||
|
{ label: "Heading 1", value: "h1" },
|
||||||
|
{ label: "Heading 2", value: "h2" },
|
||||||
|
{ label: "Heading 3", value: "h3" },
|
||||||
|
{ label: "Heading 4", value: "h4" },
|
||||||
|
{ label: "Heading 5", value: "h5" },
|
||||||
|
{ label: "Heading 6", value: "h6" },
|
||||||
|
{ label: "Body 1", value: "body1" },
|
||||||
|
{ label: "Body 2", value: "body2" },
|
||||||
|
{ label: "Caption", value: "caption" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
textAlign: {
|
||||||
|
type: "select",
|
||||||
|
label: "Text Align",
|
||||||
|
options: [
|
||||||
|
{ label: "Left", value: "left" },
|
||||||
|
{ label: "Center", value: "center" },
|
||||||
|
{ label: "Right", value: "right" },
|
||||||
|
{ label: "Justify", value: "justify" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fontWeight: {
|
||||||
|
type: "select",
|
||||||
|
label: "Font Weight",
|
||||||
|
options: [
|
||||||
|
{ label: "Thin", value: "thin" },
|
||||||
|
{ label: "Extra Light", value: "extralight" },
|
||||||
|
{ label: "Light", value: "light" },
|
||||||
|
{ label: "Normal", value: "normal" },
|
||||||
|
{ label: "Medium", value: "medium" },
|
||||||
|
{ label: "Semibold", value: "semibold" },
|
||||||
|
{ label: "Bold", value: "bold" },
|
||||||
|
{ label: "Extra Bold", value: "extrabold" },
|
||||||
|
{ label: "Black", value: "black" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
type: "text",
|
||||||
|
label: "Class Name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TypographyConfig
|
||||||
119
components/elements/Typography.tsx
Normal file
119
components/elements/Typography.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export type TypographyVariant =
|
||||||
|
| "h1"
|
||||||
|
| "h2"
|
||||||
|
| "h3"
|
||||||
|
| "h4"
|
||||||
|
| "h5"
|
||||||
|
| "h6"
|
||||||
|
| "body1"
|
||||||
|
| "body2"
|
||||||
|
| "caption"
|
||||||
|
|
||||||
|
export type TypographyAlign = "left" | "center" | "right" | "justify"
|
||||||
|
|
||||||
|
export type TypographyWeight =
|
||||||
|
| "thin"
|
||||||
|
| "extralight"
|
||||||
|
| "light"
|
||||||
|
| "normal"
|
||||||
|
| "medium"
|
||||||
|
| "semibold"
|
||||||
|
| "bold"
|
||||||
|
| "extrabold"
|
||||||
|
| "black"
|
||||||
|
|
||||||
|
const variantStyles: Record<TypographyVariant, string> = {
|
||||||
|
h1: "font-heading scroll-m-20 text-4xl tracking-tight text-balance lg:text-5xl",
|
||||||
|
h2: "font-heading scroll-m-20 text-3xl tracking-tight",
|
||||||
|
h3: "font-heading scroll-m-20 text-2xl tracking-tight",
|
||||||
|
h4: "font-heading scroll-m-20 text-xl tracking-tight",
|
||||||
|
h5: "font-heading scroll-m-20 text-lg tracking-tight",
|
||||||
|
h6: "font-heading scroll-m-20 text-base tracking-tight",
|
||||||
|
body1: "leading-7 text-base",
|
||||||
|
body2: "leading-6 text-sm",
|
||||||
|
caption: "text-xs text-muted-foreground",
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantElement: Record<TypographyVariant, React.ElementType> = {
|
||||||
|
h1: "h1",
|
||||||
|
h2: "h2",
|
||||||
|
h3: "h3",
|
||||||
|
h4: "h4",
|
||||||
|
h5: "h5",
|
||||||
|
h6: "h6",
|
||||||
|
body1: "p",
|
||||||
|
body2: "p",
|
||||||
|
caption: "span",
|
||||||
|
}
|
||||||
|
|
||||||
|
const weightStyles: Record<TypographyWeight, string> = {
|
||||||
|
thin: "font-thin",
|
||||||
|
extralight: "font-extralight",
|
||||||
|
light: "font-light",
|
||||||
|
normal: "font-normal",
|
||||||
|
medium: "font-medium",
|
||||||
|
semibold: "font-semibold",
|
||||||
|
bold: "font-bold",
|
||||||
|
extrabold: "font-extrabold",
|
||||||
|
black: "font-black",
|
||||||
|
}
|
||||||
|
|
||||||
|
const alignStyles: Record<TypographyAlign, string> = {
|
||||||
|
left: "text-left",
|
||||||
|
center: "text-center",
|
||||||
|
right: "text-right",
|
||||||
|
justify: "text-justify",
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultWeight: Record<TypographyVariant, TypographyWeight> = {
|
||||||
|
h1: "bold",
|
||||||
|
h2: "bold",
|
||||||
|
h3: "bold",
|
||||||
|
h4: "bold",
|
||||||
|
h5: "bold",
|
||||||
|
h6: "bold",
|
||||||
|
body1: "normal",
|
||||||
|
body2: "normal",
|
||||||
|
caption: "normal",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TypographyProps
|
||||||
|
extends Omit<React.HTMLAttributes<HTMLElement>, "children"> {
|
||||||
|
text?: React.ReactNode
|
||||||
|
variant?: TypographyVariant
|
||||||
|
textAlign?: TypographyAlign
|
||||||
|
fontWeight?: TypographyWeight
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Typography({
|
||||||
|
text,
|
||||||
|
variant = "body1",
|
||||||
|
textAlign,
|
||||||
|
fontWeight,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: TypographyProps) {
|
||||||
|
const Comp = variantElement[variant]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-variant={variant}
|
||||||
|
className={cn(
|
||||||
|
variantStyles[variant],
|
||||||
|
weightStyles[fontWeight ?? defaultWeight[variant]],
|
||||||
|
textAlign && alignStyles[textAlign],
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Comp>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Typography }
|
||||||
32
components/elements/types.ts
Normal file
32
components/elements/types.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
export type FieldType =
|
||||||
|
| "text"
|
||||||
|
| "textarea"
|
||||||
|
| "color"
|
||||||
|
| "icon"
|
||||||
|
| "array"
|
||||||
|
| "object"
|
||||||
|
| "boolean"
|
||||||
|
| "select"
|
||||||
|
| "image"
|
||||||
|
| "number"
|
||||||
|
| "tags"
|
||||||
|
|
||||||
|
export interface FieldOption {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FieldConfig {
|
||||||
|
type: FieldType
|
||||||
|
label?: string
|
||||||
|
options?: FieldOption[]
|
||||||
|
arrayFields?: Record<string, FieldConfig>
|
||||||
|
objectFields?: Record<string, FieldConfig>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComponentConfig {
|
||||||
|
label: string
|
||||||
|
fields: Record<string, FieldConfig>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ElementConfig = Record<string, ComponentConfig>
|
||||||
Reference in New Issue
Block a user