Skip to content

Commit

Permalink
feat: refactored toolbar logic
Browse files Browse the repository at this point in the history
  • Loading branch information
fluid-design-io committed Oct 18, 2023
1 parent 8dd6043 commit 133f736
Show file tree
Hide file tree
Showing 11 changed files with 1,493 additions and 1,453 deletions.
55 changes: 30 additions & 25 deletions apps/web/app/code/generate-button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"use client";

import { CSSType, generateCssVariables } from "@/lib/generateCssVariables";
import {
CodeButtonTitle,
CodeGenerateType,
generateCssVariables,
} from "@/lib/generateVariables";
import { Button } from "@ui/components/ui/button";
import {
Dialog,
Expand All @@ -17,11 +21,31 @@ import { ScrollArea } from "@ui/components/scroll-area";
import { Copy, CopyCheck } from "lucide-react";
import { useToast } from "@ui/components/ui/use-toast";

const copyDescription = (title: CodeButtonTitle) => {
switch (title) {
case CodeButtonTitle.RAW:
return "Copy and paste this code in your CSS file";
case CodeButtonTitle.TAILWINDCSS:
return "Copy and paste this code in your tailwind.config.js file";
case CodeButtonTitle.SHADCN:
return "Copy and paste this code in your global.css file";
case CodeButtonTitle.REACT_NATIVE_PAPER:
return "Copy and paste this code in your theme.js file";
case CodeButtonTitle.WEBFLOW:
return "Copy and paste this code in your Webflow project";
case CodeButtonTitle.FIGMA:
return "Copy and paste this url in Figma plugin, the link is valid for 7 days";
default:
return "Copy and paste this code in your CSS file";
}
};
function CodeGenerateButton({
title,
type,
available,
}: {
type: CSSType;
title: CodeButtonTitle;
type: CodeGenerateType;
available: boolean;
}) {
const [isCopied, setIsCopied] = useState(false);
Expand All @@ -40,31 +64,11 @@ function CodeGenerateButton({
setIsCopied(false);
}, 2000);
};
const copyDescription = () => {
switch (type) {
case CSSType.RAW:
return "Copy and paste this code in your CSS file";
case CSSType.TAILWINDCSS:
return "Copy and paste this code in your tailwind.config.js file";
case CSSType.SHADCN:
return "Copy and paste this code in your global.css file";
case CSSType.REACT_NATIVE_PAPER:
return "Copy and paste this code in your theme.js file";
case CSSType.WEBFLOW:
return "Copy and paste this code in your Webflow project";
case CSSType.FIGMA:
return "Copy and paste this url in Figma plugin, the link is valid for 7 days";
default:
return "Copy and paste this code in your CSS file";
}
};

const generate = async () => {
const c = await generateCssVariables({ type, colorPalettes, baseColors });
const c = await generateCssVariables({ title, colorPalettes, baseColors });
setCode(c);
};
useEffect(() => {
generate();
}, [colorPalettes, type]);
return (
<Dialog>
<DialogTrigger asChild>
Expand All @@ -73,6 +77,7 @@ function CodeGenerateButton({
variant="outline"
size="sm"
className="relative z-10 backdrop-blur-sm"
onClick={generate}
>
Generate
</Button>
Expand All @@ -90,7 +95,7 @@ function CodeGenerateButton({
<DialogContent className="block max-h-[min(60rem,calc(100dvh-2rem))] overflow-y-auto overflow-x-hidden sm:max-w-2xl">
<DialogHeader>
<DialogTitle>{type}</DialogTitle>
<DialogDescription>{copyDescription()}</DialogDescription>
<DialogDescription>{copyDescription(title)}</DialogDescription>
</DialogHeader>
<div className="relative mb-4 py-4">
<Button
Expand Down
3 changes: 2 additions & 1 deletion apps/web/components/palette/base-color-palettes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ function BaseColorPalettes() {
"mt-1.5 flex flex-col items-start justify-start text-xs tabular-nums",
"@xs/section-secondary:flex-row @xs/section-secondary:items-center @xs/section-secondary:justify-between",
"@md/section-secondary:flex-col @md/section-secondary:items-start @md/section-secondary:justify-start",
"@md/section-secondary:min-h-[2.0625rem]",
)}
>
<div className="font-comfortaa font-bold text-foreground/80">
Expand All @@ -78,7 +79,7 @@ function BaseColorPalettes() {
"hover:-mx-1 hover:-my-0.5 hover:rounded hover:px-1 hover:py-0.5",
"hover:text-muted-foreground hover:ring-1 hover:ring-inset hover:ring-border",
"contrast-more:font-medium contrast-more:text-foreground/80 contrast-more:hover:text-foreground",
"hover:absolute hover:left-0 hover:top-0 hover:z-10 hover:shadow-sm",
"@md/section-secondary:hover:absolute @md/section-secondary:hover:left-0 @md/section-secondary:hover:top-0 @md/section-secondary:hover:z-10 @md/section-secondary:hover:shadow-sm",
)}
>
<ColorString
Expand Down
35 changes: 35 additions & 0 deletions apps/web/components/toolbar/download-base-palette.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@ui/components/ui/dialog";
import DesktopPreviewToolbarIcon from "../ui/desktop-primary-toolbar-button";
import primaryToolbarMenu from "../ui/primary-toolbar-menu";

function ToolbarDownloadBasePalette() {
const menuItem = primaryToolbarMenu.Download;
return (
<Dialog>
<DialogTrigger>
<DesktopPreviewToolbarIcon {...menuItem} />
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
);
}

export default ToolbarDownloadBasePalette;

ToolbarDownloadBasePalette.displayName = "ToolbarDownloadBasePalette";
15 changes: 15 additions & 0 deletions apps/web/components/toolbar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ToolbarUploadImage from "./upload-image";
import ToolbarDownloadBasePalette from "./download-base-palette";
import ToolbarShareableLink from "./shareable-link";

// export like <Toolbar.UploadImage />

const ToolbarPrimative = ({ children }) => children;

ToolbarPrimative.displayName = "Toolbar";

export const Toolbar = Object.assign(ToolbarPrimative, {
UploadImage: ToolbarUploadImage,
DownloadBasePalette: ToolbarDownloadBasePalette,
ShareableLink: ToolbarShareableLink,
});
54 changes: 54 additions & 0 deletions apps/web/components/toolbar/shareable-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use client";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@ui/components/ui/popover";

import DesktopPreviewToolbarIcon from "../ui/desktop-primary-toolbar-button";
import primaryToolbarMenu from "../ui/primary-toolbar-menu";
import { useEffect, useState } from "react";
import { Input } from "ui/components/ui/input";
import { Button } from "ui/components/ui/button";
import { Copy } from "lucide-react";
import { useColorStore } from "@/store/store";
import { colorHelper } from "@/lib/colorHelper";

function ToolbarShareableLink() {
const menuItem = primaryToolbarMenu.Share;
const { baseColors } = useColorStore();
const [open, setOpen] = useState(false);
const [src, setSrc] = useState("");
const handleCopy = () => {
navigator.clipboard.writeText(src);
setOpen(false);
};
useEffect(() => {
if (!baseColors) return;
let search = Object.values(baseColors)
.map((color) => colorHelper.toHex(color))
.join(",");
search = encodeURIComponent(search);
const url = `${process.env.NEXT_PUBLIC_URL}/?colors=${search}`;
setSrc(url);
}, [!!open]);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger>
<DesktopPreviewToolbarIcon {...menuItem} />
</PopoverTrigger>
<PopoverContent className="w-[max(24rem,80%)]" align="end">
<div className="flex space-x-2">
<Input className="h-8" value={src} readOnly />
<Button className="h-8" size="icon" onClick={handleCopy}>
<Copy className="h-4 w-4" />
</Button>
</div>
</PopoverContent>
</Popover>
);
}

export default ToolbarShareableLink;

ToolbarShareableLink.displayName = "ToolbarShareableLink";
35 changes: 35 additions & 0 deletions apps/web/components/toolbar/upload-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@ui/components/ui/dialog";
import DesktopPreviewToolbarIcon from "../ui/desktop-primary-toolbar-button";
import primaryToolbarMenu from "../ui/primary-toolbar-menu";

function ToolbarUploadImage() {
const menuItem = primaryToolbarMenu["Upload Image"];
return (
<Dialog>
<DialogTrigger>
<DesktopPreviewToolbarIcon {...menuItem} />
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
);
}

export default ToolbarUploadImage;

ToolbarUploadImage.displayName = "ToolbarUploadImage";
49 changes: 49 additions & 0 deletions apps/web/components/ui/desktop-primary-toolbar-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use client";

import React from "react";
import { Button } from "@ui/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@ui/components/tooltip";

const DesktopPreviewToolbarIcon = ({
title,
description,
icon: Icon,
}: {
title: string;
description: string;
icon: React.FC<React.SVGProps<SVGSVGElement>>;
}) => {
return (
<TooltipProvider
key={`desktop-primary-toolbar-${title}`}
delayDuration={250}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
aria-label={title}
size="icon"
className="flex h-8 w-8 select-none items-center rounded-sm px-1 py-1 text-sm font-medium outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
asChild
>
<div>
<div className="sr-only">{title}</div>
<Icon className="h-5 w-5" />
</div>
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p className="max-w-[10rem]">{description}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};

export default DesktopPreviewToolbarIcon;
49 changes: 7 additions & 42 deletions apps/web/components/ui/desktop-primary-toolbar-buttons.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,19 @@
"use client";

import React, { Fragment } from "react";
import primaryToolbarMenu from "./primary-toolbar-menu";
import { Button } from "@ui/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@ui/components/tooltip";
import { usePathname } from "next/navigation";
import { cn } from "@ui/lib/utils";
import { Toolbar } from "../toolbar";
import { cn } from "ui/lib/utils";

function DesktopPreviewToolbarButtons() {
const pathname = usePathname();
const acitvePath = pathname.split("/")[1];
return (
<div
className={cn(
"hidden lg:flex",
"h-10 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm",
)}
>
{primaryToolbarMenu.map(({ title, description, icon: Icon }) => {
// hide "Download" if not in root path
if (title === "Download" && acitvePath !== "") return null;
return (
<TooltipProvider
key={`desktop-primary-toolbar-${title}`}
delayDuration={250}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
aria-label={title}
size="icon"
className="flex h-8 w-8 select-none items-center rounded-sm px-1 py-1 text-sm font-medium outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
>
<div className="sr-only">{title}</div>
<Icon className="h-5 w-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p className="max-w-[10rem]">{description}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
})}
<Toolbar>
<Toolbar.UploadImage />
<Toolbar.DownloadBasePalette />
<Toolbar.ShareableLink />
</Toolbar>
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/components/ui/mobile-primary-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ function MobilePrimaryMenu({ disabled }: { disabled: boolean }) {
<DropdownMenuContent align="end" className="min-w-[9.7rem]">
<DropdownMenuLabel>Tools</DropdownMenuLabel>
<DropdownMenuSeparator />
{primaryToolbarMenu.map(({ title, description, icon: Icon }) => (
{/* {primaryToolbarMenu.map(({ title, description, icon: Icon }) => (
<DropdownMenuItem key={`mobile-primary-menu-${title}`}>
<MenuItem title={title} description={description} icon={Icon} />
</DropdownMenuItem>
))}
))} */}
</DropdownMenuContent>
</DropdownMenu>
);
Expand Down
Loading

0 comments on commit 133f736

Please sign in to comment.