mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-21 13:40:31 -06:00
Compare commits
3 Commits
fix-broken
...
feat/butto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a12278d9d | ||
|
|
95408e577c | ||
|
|
cf01fdc93d |
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'],
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended', 'plugin:storybook/recommended'],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
|
||||
@@ -25,11 +25,15 @@
|
||||
"@storybook/blocks": "^7.6.4",
|
||||
"@storybook/react": "^7.6.4",
|
||||
"@storybook/react-vite": "^7.6.4",
|
||||
"@storybook/test": "^7.6.5",
|
||||
"@storybook/testing-library": "^0.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
"@typescript-eslint/parser": "^6.14.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"esbuild": "^0.19.9",
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"prop-types": "^15.8.1",
|
||||
"storybook": "^7.6.5",
|
||||
"tsup": "^8.0.1",
|
||||
"vite": "^5.0.8"
|
||||
}
|
||||
|
||||
6
apps/storybook/src/stories/button.stories.tsx
Normal file
6
apps/storybook/src/stories/button.stories.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import buttonMeta, { PrimaryStory } from "@formbricks/ui/v2/Button/stories";
|
||||
|
||||
const meta = buttonMeta;
|
||||
export default meta;
|
||||
|
||||
export const Primary = PrimaryStory;
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
UsersIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import clsx from "clsx";
|
||||
import { MenuIcon } from "lucide-react";
|
||||
import { ArrowLeftIcon, ArrowRightIcon, EditIcon, MenuIcon, PenIcon } from "lucide-react";
|
||||
import type { Session } from "next-auth";
|
||||
import { signOut } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
@@ -54,6 +54,7 @@ import {
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/Popover";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
|
||||
import { CustomersIcon, DashboardIcon, FilterIcon, FormIcon, SettingsIcon } from "@formbricks/ui/icons";
|
||||
import { ButtonV2 } from "@formbricks/ui/v2/Button";
|
||||
|
||||
import AddProductModal from "./AddProductModal";
|
||||
import UrlShortenerModal from "./UrlShortenerModal";
|
||||
@@ -245,6 +246,44 @@ export default function Navigation({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="m-16">
|
||||
<ButtonV2 size="sm" className="mr-4" StartIcon={ArrowLeftIcon} EndIcon={ArrowRightIcon}>
|
||||
Hello World
|
||||
</ButtonV2>
|
||||
|
||||
<ButtonV2 size="base" className="mr-4" StartIcon={ArrowLeftIcon} EndIcon={ArrowRightIcon}>
|
||||
Hello World
|
||||
</ButtonV2>
|
||||
|
||||
<ButtonV2 size="lg" StartIcon={ArrowLeftIcon} EndIcon={ArrowRightIcon} noShadow>
|
||||
Hello World
|
||||
</ButtonV2>
|
||||
</div>
|
||||
|
||||
<div className="m-16">
|
||||
<ButtonV2
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="mr-4"
|
||||
StartIcon={ArrowLeftIcon}
|
||||
EndIcon={ArrowRightIcon}>
|
||||
Hello World
|
||||
</ButtonV2>
|
||||
|
||||
<ButtonV2
|
||||
variant="secondary"
|
||||
size="base"
|
||||
className="mr-4"
|
||||
StartIcon={ArrowLeftIcon}
|
||||
EndIcon={ArrowRightIcon}>
|
||||
Hello World
|
||||
</ButtonV2>
|
||||
|
||||
<ButtonV2 variant="icon" label="Edit">
|
||||
<PenIcon />
|
||||
</ButtonV2>
|
||||
</div>
|
||||
|
||||
<div className="w-full px-4 sm:px-6">
|
||||
<div className="flex h-14 justify-between">
|
||||
<div className="flex space-x-4 py-2">
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
/* Label Colors */
|
||||
--formbricks-label-primary: #0f172a;
|
||||
--formbricks-label-secondary: #384258;
|
||||
--formbricks-label-tertiary: #f2f2f2;
|
||||
--formbricks-label-disabled: #bdbdbd;
|
||||
|
||||
/* Border Colors */
|
||||
@@ -28,7 +29,7 @@
|
||||
|
||||
.dark {
|
||||
/* Brand Colors */
|
||||
--formbricks-brand: #038178;
|
||||
--formbricks-brand: #038178;
|
||||
|
||||
/* Fill Colors */
|
||||
--formbricks-fill-primary: #0f172a;
|
||||
@@ -50,7 +51,6 @@
|
||||
--formbricks-error: #d13a3a;
|
||||
}
|
||||
|
||||
|
||||
@layer base {
|
||||
[data-nextjs-scroll-focus-boundary] {
|
||||
display: contents;
|
||||
|
||||
@@ -17,6 +17,12 @@ module.exports = {
|
||||
backgroundImage: {
|
||||
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||
},
|
||||
boxShadow: {
|
||||
"brand-shadow-sm": "3px 3px #0381782A",
|
||||
"brand-shadow-base": "4px 4px #0381782A",
|
||||
"brand-shadow-lg": "6px 6px #0381782A",
|
||||
"focus-shadow": "0px 0px 10px 0px red",
|
||||
},
|
||||
colors: {
|
||||
brand: {
|
||||
DEFAULT: "#00E6CA",
|
||||
@@ -35,6 +41,7 @@ module.exports = {
|
||||
labelColor: {
|
||||
primary: "var(--formbricks-label-primary, #0f172a)",
|
||||
secondary: "var(--formbricks-label-secondary, #384258)",
|
||||
tertiary: "var(--formbricks-label-tertiary, #f2f2f2)",
|
||||
disabled: "var(--formbricks-label-disabled, #bdbdbd)",
|
||||
error: "var(--formbricks-error, #d13a3a)",
|
||||
},
|
||||
|
||||
@@ -14,6 +14,8 @@ Tooltip.displayName = TooltipPrimitive.Tooltip.displayName;
|
||||
|
||||
const TooltipTrigger: React.ComponentType<TooltipPrimitive.TooltipTriggerProps> = TooltipPrimitive.Trigger;
|
||||
|
||||
const TooltipArrow: React.ComponentType<TooltipPrimitive.TooltipArrowProps> = TooltipPrimitive.Arrow;
|
||||
|
||||
const TooltipContent: React.ComponentType<TooltipPrimitive.TooltipContentProps> = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
@@ -30,4 +32,4 @@ const TooltipContent: React.ComponentType<TooltipPrimitive.TooltipContentProps>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipArrow };
|
||||
|
||||
@@ -4,10 +4,12 @@ import React, { AnchorHTMLAttributes, ButtonHTMLAttributes, forwardRef } from "r
|
||||
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
|
||||
import { Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger } from "../../Tooltip";
|
||||
|
||||
type SVGComponent = React.FunctionComponent<React.SVGProps<SVGSVGElement>> | LucideIcon;
|
||||
|
||||
export type ButtonBaseProps = {
|
||||
variant?: "highlight" | "primary" | "secondary" | "minimal" | "warn" | "alert" | "darkCTA";
|
||||
variant?: "highlight" | "primary" | "secondary" | "minimal" | "warn" | "alert" | "darkCTA" | "icon";
|
||||
size?: "base" | "sm" | "lg" | "fab" | "icon";
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
@@ -17,6 +19,8 @@ export type ButtonBaseProps = {
|
||||
EndIcon?: SVGComponent | React.ComponentType<React.ComponentProps<"svg">>;
|
||||
endIconClassName?: string;
|
||||
shallow?: boolean;
|
||||
noShadow?: boolean;
|
||||
label?: string;
|
||||
};
|
||||
type ButtonBasePropsWithTarget = ButtonBaseProps & { target?: string };
|
||||
|
||||
@@ -26,7 +30,7 @@ export type ButtonProps = ButtonBasePropsWithTarget &
|
||||
| (Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onClick" | "target"> & { href?: never })
|
||||
);
|
||||
|
||||
export const Button: React.ForwardRefExoticComponent<
|
||||
export const ButtonV2: React.ForwardRefExoticComponent<
|
||||
React.PropsWithoutRef<ButtonProps> & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>
|
||||
> = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonProps>(function Button(
|
||||
props: ButtonProps,
|
||||
@@ -41,6 +45,7 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
endIconClassName,
|
||||
EndIcon,
|
||||
shallow,
|
||||
noShadow,
|
||||
// attributes propagated from `HTMLAnchorProps` or `HTMLButtonProps`
|
||||
...passThroughProps
|
||||
} = props;
|
||||
@@ -51,7 +56,7 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
const isLink = typeof props.href !== "undefined";
|
||||
const elementType = isLink ? "span" : "button";
|
||||
|
||||
const element: any = React.createElement(
|
||||
const element = React.createElement(
|
||||
elementType,
|
||||
{
|
||||
...passThroughProps,
|
||||
@@ -61,11 +66,12 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
// base styles independent what type of button it is
|
||||
"inline-flex items-center appearance-none",
|
||||
// different styles depending on size
|
||||
size === "sm" && "px-3 py-2 text-sm leading-4 font-medium rounded-md",
|
||||
size === "base" && "px-6 py-3 text-sm font-medium rounded-md",
|
||||
size === "lg" && "px-4 py-2 text-base font-medium rounded-md",
|
||||
size === "sm" && "px-4 py-3 text-base leading-4 font-medium rounded-lg",
|
||||
size === "base" && "px-8 py-4 text-lg font-medium rounded-xl",
|
||||
size === "lg" && "px-12 py-6 text-xl font-medium rounded-xl",
|
||||
size === "icon" &&
|
||||
"w-10 h-10 justify-center group p-2 border rounded-lg border-transparent text-neutral-400 hover:border-slate-200 transition",
|
||||
"w-8 h-8 justify-center group p-2 border rounded-lg border-borderColor-primary transition",
|
||||
|
||||
// turn button into a floating action button (fab)
|
||||
size === "fab" ? "fixed" : "relative",
|
||||
size === "fab" && "justify-center bottom-20 right-8 rounded-full p-4 w-14 h-14",
|
||||
@@ -75,32 +81,46 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
(disabled
|
||||
? "border border-transparent bg-slate-400 text-white"
|
||||
: "text-white bg-brand-dark focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-slate-900 transition ease-in-out delay-50 hover:scale-105"),
|
||||
|
||||
variant === "primary" &&
|
||||
(disabled
|
||||
? "border border-transparent bg-slate-400 text-white"
|
||||
: "text-white bg-brand-dark hover:bg-brand focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-slate-900"),
|
||||
: cn(
|
||||
"text-white bg-brandnew hover:bg-gradient-to-b hover:from-black/20 hover:to-black/20 hover:shadow-none focus:outline-none focus:ring focus:ring-offset-4 focus:ring-focus active:bg-gradient-to-b active:from-white/20 active:to-white/20 active:shadow-none",
|
||||
size === "sm" && "shadow-brand-shadow-sm",
|
||||
size === "base" && "shadow-brand-shadow-base",
|
||||
size === "lg" && "shadow-brand-shadow-lg",
|
||||
noShadow && "shadow-none"
|
||||
)),
|
||||
|
||||
variant === "minimal" &&
|
||||
(disabled
|
||||
? "border border-slate-200 text-slate-400"
|
||||
: "hover:text-slate-600 text-slate-700 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-neutral-900 dark:text-slate-700 dark:hover:text-slate-500"),
|
||||
|
||||
variant === "alert" &&
|
||||
(disabled
|
||||
? "border border-transparent bg-slate-400 text-white"
|
||||
: "border border-transparent dark:text-darkmodebrandcontrast text-brandcontrast bg-red-600 dark:bg-darkmodebrand hover:bg-opacity-90 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-neutral-900"),
|
||||
|
||||
variant === "secondary" &&
|
||||
(disabled
|
||||
? "text-slate-400 dark:text-slate-500 bg-slate-200 dark:bg-slate-800"
|
||||
: "text-slate-600 hover:text-slate-500 bg-slate-200 hover:bg-slate-100 dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:bg-slate-300 focus:ring-neutral-500"),
|
||||
: "text-labelColor-primary focus:outline-none focus:ring focus:ring-offset-4 focus:ring-focus active:bg-gradient-to-b active:from-white/20 active:to-white/20 hover:text-labelColor-tertiary hover:bg-fill-secondary bg-fill-primary border border-borderColor-secondary dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600 focus:outline-none "),
|
||||
|
||||
variant === "warn" &&
|
||||
(disabled
|
||||
? "text-slate-400 bg-transparent"
|
||||
: "hover:bg-red-200 text-red-700 bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:bg-red-50 focus:ring-red-500"),
|
||||
|
||||
variant === "darkCTA" &&
|
||||
(disabled
|
||||
? "text-slate-400 dark:text-slate-500 bg-slate-200 dark:bg-slate-800"
|
||||
: "text-slate-100 hover:text-slate-50 bg-gradient-to-br from-slate-900 to-slate-800 hover:from-slate-800 hover:to-slate-700 dark:text-slate-300 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:bg-slate-700 focus:ring-neutral-500"),
|
||||
|
||||
variant === "icon" &&
|
||||
"h-8 w-8 p-2 text-labelColor-secondary rounded-lg bg-fill-primary border border-borderColor-primary",
|
||||
|
||||
// set not-allowed cursor if disabled
|
||||
loading ? "cursor-wait" : disabled ? "cursor-not-allowed" : "",
|
||||
props.className
|
||||
@@ -117,7 +137,10 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
<StartIcon
|
||||
className={cn(
|
||||
"flex",
|
||||
size === "icon" ? "h-4 w-4 " : "-ml-1 mr-1 h-3 w-3",
|
||||
// size === "icon" ? "h-4 w-4 " : "h-3 w-3",
|
||||
size === "sm" && "mr-1 h-4 w-4",
|
||||
size === "base" && "mr-3 h-6 w-6",
|
||||
size === "lg" && "mr-4 h-8 w-8",
|
||||
startIconClassName || ""
|
||||
)}
|
||||
/>
|
||||
@@ -142,9 +165,39 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
{EndIcon && <EndIcon className={cn("-mr-1 ml-2 inline h-5 w-5 rtl:mr-2", endIconClassName || "")} />}
|
||||
{EndIcon && (
|
||||
<EndIcon
|
||||
className={cn(
|
||||
// "inline h-5 w-5",
|
||||
size === "sm" && "ml-1 h-4 w-4",
|
||||
size === "base" && "ml-3 h-6 w-6",
|
||||
size === "lg" && "ml-4 h-8 w-8",
|
||||
endIconClassName || ""
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
if (props.variant === "icon" && props.label) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<TooltipContent
|
||||
style={{
|
||||
boxShadow: "0px 8px 16px 0px rgba(0, 0, 0, 0.08)",
|
||||
}}
|
||||
className="bg-fill-secondary rounded-[4px] px-2 py-1 text-[#fefefe]">
|
||||
{props.label} <TooltipArrow className="-mt-[2px]" />
|
||||
</TooltipContent>
|
||||
{element}
|
||||
</TooltipTrigger>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
return props.href ? (
|
||||
<Link passHref href={props.href} shallow={shallow && shallow} target={props.target || "_self"}>
|
||||
{element}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Button } from "./index";
|
||||
import { ButtonV2 } from "./index";
|
||||
|
||||
const meta = {
|
||||
const buttonMeta = {
|
||||
title: "Button",
|
||||
component: Button,
|
||||
component: ButtonV2,
|
||||
tags: ["autodocs"],
|
||||
argTypes: {
|
||||
variant: {
|
||||
@@ -14,15 +14,16 @@ const meta = {
|
||||
size: { control: "select", options: ["base", "sm", "lg", "fab", "icon"] },
|
||||
onClick: { action: "clicked", type: "function" },
|
||||
},
|
||||
} satisfies Meta<typeof Button>;
|
||||
} satisfies Meta<typeof ButtonV2>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
export default buttonMeta;
|
||||
type Story = StoryObj<typeof buttonMeta>;
|
||||
|
||||
export const Primary: Story = {
|
||||
export const PrimaryStory: Story = {
|
||||
name: "Primary",
|
||||
args: {
|
||||
className: "",
|
||||
children: "Button",
|
||||
variant: "secondary",
|
||||
variant: "primary",
|
||||
},
|
||||
};
|
||||
|
||||
1083
pnpm-lock.yaml
generated
1083
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user