diff --git a/app/page.tsx b/app/page.tsx index 277852d..da2f0ca 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,9 +1,14 @@ +import { Typography } from "@/components/elements" + const Home: React.FC = () => { return (
-

- Start prompting -

+
) } diff --git a/components/elements/Button.config.tsx b/components/elements/Button.config.tsx new file mode 100644 index 0000000..6c15289 --- /dev/null +++ b/components/elements/Button.config.tsx @@ -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 diff --git a/components/elements/Button.tsx b/components/elements/Button.tsx new file mode 100644 index 0000000..65dc458 --- /dev/null +++ b/components/elements/Button.tsx @@ -0,0 +1,26 @@ +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["variant"] +> +export type ButtonSize = NonNullable< + VariantProps["size"] +> + +export interface ButtonProps + extends React.ComponentProps { + variant?: ButtonVariant + size?: ButtonSize +} + +function Button({ variant = "default", size = "default", ...props }: ButtonProps) { + return +} + +export { Button } diff --git a/components/elements/Card.config.tsx b/components/elements/Card.config.tsx new file mode 100644 index 0000000..30917a7 --- /dev/null +++ b/components/elements/Card.config.tsx @@ -0,0 +1,33 @@ +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: "array", + label: "Tags", + arrayFields: { + tag: { + type: "text", + label: "Tag", + }, + }, + }, + }, + }, +} + +export default CardConfig diff --git a/components/elements/Card.tsx b/components/elements/Card.tsx new file mode 100644 index 0000000..ac9c65d --- /dev/null +++ b/components/elements/Card.tsx @@ -0,0 +1,73 @@ +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 CardTag { + tag: string +} + +export interface CardProps extends React.ComponentProps { + image?: string + title?: string + subtitle?: string + tags?: CardTag[] +} + +function Card({ + image, + title, + subtitle, + tags, + className, + ...props +}: CardProps) { + return ( + + {image ? ( + {title + ) : null} + + + {title ? : null} + {subtitle ? ( + + ) : null} + + + {tags && tags.length > 0 ? ( + + {tags.map((item, index) => ( + + {item.tag} + + ))} + + ) : null} + + ) +} + +export { Card } diff --git a/components/elements/Icon.config.tsx b/components/elements/Icon.config.tsx new file mode 100644 index 0000000..dbc1218 --- /dev/null +++ b/components/elements/Icon.config.tsx @@ -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 diff --git a/components/elements/Icon.tsx b/components/elements/Icon.tsx new file mode 100644 index 0000000..7ec2741 --- /dev/null +++ b/components/elements/Icon.tsx @@ -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, "name"> { + name: IconName + size?: number + className?: string +} + +function Icon({ name, size = 24, className, ...props }: IconProps) { + return ( + + ) +} + +export { Icon } +export type { IconName } diff --git a/components/elements/Image.config.tsx b/components/elements/Image.config.tsx new file mode 100644 index 0000000..f362ccf --- /dev/null +++ b/components/elements/Image.config.tsx @@ -0,0 +1,42 @@ +import type { ElementConfig } from "./types" + +export const ImageConfig: ElementConfig = { + Image: { + label: "Image", + fields: { + src: { + type: "text", + 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: "text", + label: "Width", + }, + height: { + type: "text", + label: "Height", + }, + }, + }, +} + +export default ImageConfig diff --git a/components/elements/Image.tsx b/components/elements/Image.tsx new file mode 100644 index 0000000..1a24cc6 --- /dev/null +++ b/components/elements/Image.tsx @@ -0,0 +1,56 @@ +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 = { + 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 ( + + ) +} + +export { Image } diff --git a/components/elements/Typography.config.tsx b/components/elements/Typography.config.tsx new file mode 100644 index 0000000..ce14044 --- /dev/null +++ b/components/elements/Typography.config.tsx @@ -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 diff --git a/components/elements/Typography.tsx b/components/elements/Typography.tsx new file mode 100644 index 0000000..ad0e3ac --- /dev/null +++ b/components/elements/Typography.tsx @@ -0,0 +1,120 @@ +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 = { + 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 = { + h1: "h1", + h2: "h2", + h3: "h3", + h4: "h4", + h5: "h5", + h6: "h6", + body1: "p", + body2: "p", + caption: "span", +} + +const weightStyles: Record = { + 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 = { + left: "text-left", + center: "text-center", + right: "text-right", + justify: "text-justify", +} + +const defaultWeight: Record = { + h1: "bold", + h2: "bold", + h3: "bold", + h4: "bold", + h5: "bold", + h6: "bold", + body1: "normal", + body2: "normal", + caption: "normal", +} + +export interface TypographyProps + extends Omit, "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 ( + + {text} + + ) +} + +export { Typography } diff --git a/components/elements/index.ts b/components/elements/index.ts new file mode 100644 index 0000000..8cb1550 --- /dev/null +++ b/components/elements/index.ts @@ -0,0 +1,26 @@ +export * from "./types" + +export { Typography } from "./Typography" +export type { + TypographyProps, + TypographyVariant, + TypographyAlign, + TypographyWeight, +} from "./Typography" +export { TypographyConfig } from "./Typography.config" + +export { Button } from "./Button" +export type { ButtonProps, ButtonVariant, ButtonSize } from "./Button" +export { ButtonConfig } from "./Button.config" + +export { Image } from "./Image" +export type { ImageProps, ObjectFit } from "./Image" +export { ImageConfig } from "./Image.config" + +export { Icon } from "./Icon" +export type { IconProps, IconName } from "./Icon" +export { IconConfig } from "./Icon.config" + +export { Card } from "./Card" +export type { CardProps, CardTag } from "./Card" +export { CardConfig } from "./Card.config" diff --git a/components/elements/types.ts b/components/elements/types.ts new file mode 100644 index 0000000..a4648b3 --- /dev/null +++ b/components/elements/types.ts @@ -0,0 +1,29 @@ +export type FieldType = + | "text" + | "textarea" + | "color" + | "icon" + | "array" + | "object" + | "boolean" + | "select" + +export interface FieldOption { + label: string + value: string +} + +export interface FieldConfig { + type: FieldType + label?: string + options?: FieldOption[] + arrayFields?: Record + objectFields?: Record +} + +export interface ComponentConfig { + label: string + fields: Record +} + +export type ElementConfig = Record