diff --git a/.changeset/afraid-steaks-give.md b/.changeset/afraid-steaks-give.md new file mode 100644 index 0000000..e1a1ed1 --- /dev/null +++ b/.changeset/afraid-steaks-give.md @@ -0,0 +1,5 @@ +--- +"@zenml-io/react-component-library": minor +--- + +add initial version of the button component to the library diff --git a/package.json b/package.json index fb94405..706c01f 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "changeset": "changeset && pnpm install", "changeset:publish": "changeset publish", "changeset:version": "changeset version && pnpm install --lockfile-only", + "dev": "tsup --watch", "format": "prettier -w src", "prepare": "husky install", "prepublishOnly": "pnpm build", @@ -58,6 +59,8 @@ "*.{css,js,md,ts,jsx,tsx,ts}": "prettier --write" }, "dependencies": { + "@radix-ui/react-slot": "^1.0.2", + "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "tailwind-merge": "^1.14.0", "tailwindcss": "^3.3.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 605cb6c..0053322 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,12 +5,18 @@ settings: excludeLinksFromLockfile: false dependencies: + '@radix-ui/react-slot': + specifier: ^1.0.2 + version: 1.0.2(react@18.2.0) '@types/react': specifier: ^18.0.0 version: 18.2.20 '@types/react-dom': specifier: ^18.0.0 version: 18.2.7 + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 clsx: specifier: ^2.0.0 version: 2.0.0 @@ -1372,7 +1378,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 - dev: true /@babel/template@7.22.5: resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} @@ -2113,7 +2118,6 @@ packages: dependencies: '@babel/runtime': 7.22.10 react: 18.2.0 - dev: true /@radix-ui/react-context@1.0.1(react@18.2.0): resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} @@ -2375,7 +2379,6 @@ packages: '@babel/runtime': 7.22.10 '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) react: 18.2.0 - dev: true /@radix-ui/react-toggle-group@1.0.4(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==} @@ -4625,6 +4628,12 @@ packages: engines: {node: '>=8'} dev: true + /class-variance-authority@0.7.0: + resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + dependencies: + clsx: 2.0.0 + dev: false + /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -8174,7 +8183,6 @@ packages: /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - dev: true /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} diff --git a/src/components/button/Button.stories.tsx b/src/components/button/Button.stories.tsx new file mode 100644 index 0000000..422cf6a --- /dev/null +++ b/src/components/button/Button.stories.tsx @@ -0,0 +1,123 @@ +import { Meta } from "@storybook/react"; +import { Button } from "./Button"; +import { StoryObj } from "@storybook/react"; + +const meta = { + title: "Elements/Button", + component: Button, + parameters: { + layout: "centered" + }, + argTypes: { + onClick: { action: "clicked" }, + size: { + description: "defining the size of the button", + control: "select", + defaultValue: "sm", + options: ["sm", "lg", "xl"] + }, + intent: { + description: "defining the intent of the button", + defaultValue: "primary", + control: "select", + options: ["primary", "secondary", "danger"] + }, + asChild: { + description: "if true, the props of the button are getting merged to the first child", + defaultValue: false, + control: "boolean" + }, + emphasis: { + description: "emphasis of the button", + defaultValue: "bold", + control: "select", + options: ["bold", "subtle", "minimal"] + } + }, + tags: ["autodocs"] +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Primary: Story = { + args: { + children: "Hello World", + size: "sm", + intent: "primary", + emphasis: "bold" + } +}; + +export const Secondary: Story = { + args: { + children: "Hello World", + size: "sm", + intent: "secondary", + emphasis: "bold" + } +}; + +export const Danger: Story = { + args: { + children: "Hello World", + size: "sm", + intent: "danger", + emphasis: "bold" + } +}; + +export const Small: Story = { + args: { + children: "Hello World", + size: "sm", + intent: "primary", + emphasis: "bold" + } +}; + +export const Large: Story = { + args: { + children: "Hello World", + size: "lg", + intent: "primary", + emphasis: "bold" + } +}; + +export const ExtraLarge: Story = { + args: { + children: "Hello World", + size: "xl", + intent: "primary", + emphasis: "bold" + } +}; + +export const Bold: Story = { + args: { + children: "Hello World", + emphasis: "bold", + size: "xl", + intent: "primary" + } +}; + +export const Subtle: Story = { + args: { + children: "Hello World", + size: "xl", + emphasis: "subtle", + intent: "primary" + } +}; + +export const Minimal: Story = { + args: { + children: "Hello World", + size: "xl", + intent: "primary", + emphasis: "minimal" + } +}; diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx new file mode 100644 index 0000000..75b5225 --- /dev/null +++ b/src/components/button/Button.tsx @@ -0,0 +1,102 @@ +import React, { ButtonHTMLAttributes } from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { type VariantProps, cva } from "class-variance-authority"; +import { cn } from "../../utilities/index"; + +const buttonVariants = cva( + "transition-all rounded-md duration-200 flex gap-1 items-center font-semibold disabled:pointer-events-none", + { + variants: { + emphasis: { + bold: "", + subtle: "", + minimal: "" + }, + intent: { + primary: "", + secondary: "", + danger: "" + }, + size: { + sm: "py-1 px-2", + lg: "py-2 px-3", + xl: "py-3 px-5" + } + }, + compoundVariants: [ + // Primary + { + emphasis: "bold", + intent: "primary", + class: + "bg-primary-500 text-white hover:bg-primary-400 active:ring-[#E4D8FD] active:bg-primary-500 focus-visible:outline-none active:ring-4 disabled:bg-primary-50" + }, + { + emphasis: "subtle", + intent: "primary", + class: + "border border-primary-400 text-primary-600 hover:bg-primary-50 active:bg-primary-100 active:ring-[#E4D8FD] active:ring-4 disabled:border-primary-100 disabled:text-primary-100" + }, + { + emphasis: "minimal", + intent: "primary", + class: + "text-primary-600 hover:bg-primary-50 active:bg-primary-100 active:ring-[#E4D8FD] active:ring-4 disabled:text-primary-100" + }, + // Secondary + { + emphasis: "bold", + intent: "secondary", + class: + "bg-neutral-200 text-theme-text-primary hover:bg-neutral-300 focus-visible:outline-none active:bg-neutral-400 active:ring-4 active:ring-[#E5E7EB] disabled:text-neutral-300" + }, + { + emphasis: "subtle", + intent: "secondary", + class: + "border border-neutral-300 text-neutral-900 hover:bg-neutral-100 active:border-neutral-400 active:bg-neutral-300 active:ring-4 active:ring-neutral-100 disabled:border-neutral-300 disabled:text-neutral-300" + }, + { + emphasis: "minimal", + intent: "secondary", + class: + "text-primary-900 hover:bg-neutral-200 active:bg-neutral-300 active:ring-4 active:ring-neutral-100 disabled:text-neutral-300" + }, + // Danger + { + emphasis: "bold", + intent: "danger", + class: + "bg-error-600 text-white hover:bg-error-500 active:ring-error-50 active:bg-error-600 focus-visible:outline-none active:ring-4 disabled:bg-error-100" + }, + { + emphasis: "subtle", + intent: "danger", + class: + "border border-error-600 text-error-700 hover:bg-error-50 active:bg-error-100 active:ring-4 active:ring-error-50 disabled:border-error-100 disabled:text-error-100" + }, + { + emphasis: "minimal", + intent: "danger", + class: + "text-error-700 hover:bg-error-50 active:bg-error-100 active:ring-4 active:ring-error-50 disabled:text-error-100" + } + ], + defaultVariants: { + size: "sm", + emphasis: "bold", + intent: "primary" + } + } +); + +export interface ButtonProps + extends ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +export function Button({ intent, size, className, asChild, emphasis, ...rest }: ButtonProps) { + const Comp = asChild ? Slot : "button"; + return ; +} diff --git a/src/index.ts b/src/index.ts index 03e1285..9a29c7c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export { cn } from "./utilities/index"; export { zenmlPlugin, zenmlPreset } from "./tailwind/index"; +export { Button, ButtonProps } from "./components/button/Button"; diff --git a/src/stories/Button.stories.ts b/src/stories/Button.story.ts similarity index 100% rename from src/stories/Button.stories.ts rename to src/stories/Button.story.ts diff --git a/src/stories/Header.stories.ts b/src/stories/Header.story.ts similarity index 100% rename from src/stories/Header.stories.ts rename to src/stories/Header.story.ts diff --git a/src/stories/Page.stories.ts b/src/stories/Page.story.ts similarity index 100% rename from src/stories/Page.stories.ts rename to src/stories/Page.story.ts