Skip to content

Commit

Permalink
feat: added react native paper codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
fluid-design-io committed Oct 18, 2023
1 parent d92a416 commit a02686c
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 12 deletions.
2 changes: 1 addition & 1 deletion apps/web/app/code/generate-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function CodeGenerateButton({
}}
>
{/* make it a span */}
{code.split("\n").map((line, index) => (
{code?.split("\n").map((line, index) => (
<span
className="inline-block min-h-[1rem] w-full px-4 py-[0.125rem] text-background dark:text-foreground"
key={index}
Expand Down
1 change: 0 additions & 1 deletion apps/web/app/code/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export default function CodeGenPage() {
{
title: CodeButtonTitle.REACT_NATIVE_PAPER,
icon: ReactNativePaperLogo,
available: false,
type: CodeGenerateType.CODEGEN,
},
{
Expand Down
6 changes: 4 additions & 2 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ export async function generateMetadata(
// optionally access and extend (rather than replace) parent metadata
const previousImages = (await parent).openGraph?.images || [];
return {
title: "Check out this palette",
description: `Generated with Fluid Colors: ${paletteColors}`,
title: colors ? "Check out this palette" : "Color Palette Generator",
description: colors
? `Generated with Fluid Colors: ${paletteColors}`
: "Unleash the Power of Dynamic, Variable-Based Color Palettes",
openGraph: {
images: [opengraphImage, ...previousImages],
},
Expand Down
38 changes: 32 additions & 6 deletions apps/web/components/toolbar/shareable-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import { Button } from "ui/components/ui/button";
import { Copy } from "lucide-react";
import { useColorStore } from "@/store/store";
import { colorHelper } from "@/lib/colorHelper";
import { Skeleton } from "@ui/components/ui/skeleton";
import { cn } from "ui/lib/utils";
import Image from "next/image";

function ToolbarShareableLink() {
const menuItem = primaryToolbarMenu.Share;
const { baseColors } = useColorStore();
const [open, setOpen] = useState(false);
const [src, setSrc] = useState("");
const [colors, setColors] = useState("");
const [loadingSocialPreview, setLoadingSocialPreview] = useState(true);
const handleCopy = () => {
navigator.clipboard.writeText(src);
navigator.clipboard.writeText(colors);
setOpen(false);
};
useEffect(() => {
Expand All @@ -29,17 +33,39 @@ function ToolbarShareableLink() {
.map((color) => colorHelper.toHex(color))
.join(",");
search = encodeURIComponent(search);
const url = `${process.env.NEXT_PUBLIC_URL}/?colors=${search}`;
setSrc(url);
setColors(search);
}, [!!open]);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger>
<DesktopPreviewToolbarIcon {...menuItem} />
</PopoverTrigger>
<PopoverContent className="w-[max(24rem,80%)]" align="end">
<PopoverContent className="w-[18rem] sm:w-[24rem]" align="end">
<div className="relative">
<div
className={cn(
"mb-4 overflow-hidden rounded-md",
"aspect-[120/63] w-[16rem] rounded-md sm:w-[22rem]",
)}
>
<Skeleton className="h-full w-full" />

<Image
src={`${process.env.NEXT_PUBLIC_URL}/api/og?colors=${colors}`}
className={cn("absolute inset-0 h-full w-full object-cover")}
alt="Social preview"
// onLoad={() => setLoadingSocialPreview(false)}
width={288}
height={151}
/>
</div>
</div>
<div className="flex space-x-2">
<Input className="h-8" value={src} readOnly />
<Input
className="h-8"
value={`${process.env.NEXT_PUBLIC_URL}/?colors=${colors}`}
readOnly
/>
<Button className="h-8" size="icon" onClick={handleCopy}>
<Copy className="h-4 w-4" />
</Button>
Expand Down
162 changes: 162 additions & 0 deletions apps/web/lib/code-gen/generate-rnp-tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { BaseColorTypes, ColorPalettes } from "@/types/app";
import { colorStepMap } from "../colorStepMap";
import { colorHelper } from "../colorHelper";

/*
{
"colors": {
"primary": "rgb(120, 69, 172)",
"onPrimary": "rgb(255, 255, 255)",
"primaryContainer": "rgb(240, 219, 255)",
"onPrimaryContainer": "rgb(44, 0, 81)",
"secondary": "rgb(102, 90, 111)",
"onSecondary": "rgb(255, 255, 255)",
"secondaryContainer": "rgb(237, 221, 246)",
"onSecondaryContainer": "rgb(33, 24, 42)",
"tertiary": "rgb(128, 81, 88)",
"onTertiary": "rgb(255, 255, 255)",
"tertiaryContainer": "rgb(255, 217, 221)",
"onTertiaryContainer": "rgb(50, 16, 23)",
"error": "rgb(186, 26, 26)",
"onError": "rgb(255, 255, 255)",
"errorContainer": "rgb(255, 218, 214)",
"onErrorContainer": "rgb(65, 0, 2)",
"background": "rgb(255, 251, 255)",
"onBackground": "rgb(29, 27, 30)",
"surface": "rgb(255, 251, 255)",
"onSurface": "rgb(29, 27, 30)",
"surfaceVariant": "rgb(233, 223, 235)",
"onSurfaceVariant": "rgb(74, 69, 78)",
"outline": "rgb(124, 117, 126)",
"outlineVariant": "rgb(204, 196, 206)",
"shadow": "rgb(0, 0, 0)",
"scrim": "rgb(0, 0, 0)",
"inverseSurface": "rgb(50, 47, 51)",
"inverseOnSurface": "rgb(245, 239, 244)",
"inversePrimary": "rgb(220, 184, 255)",
"elevation": {
"level0": "transparent",
"level1": "rgb(248, 242, 251)",
"level2": "rgb(244, 236, 248)",
"level3": "rgb(240, 231, 246)",
"level4": "rgb(239, 229, 245)",
"level5": "rgb(236, 226, 243)"
},
"surfaceDisabled": "rgba(29, 27, 30, 0.12)",
"onSurfaceDisabled": "rgba(29, 27, 30, 0.38)",
"backdrop": "rgba(51, 47, 55, 0.4)"
}
}
*/
export const getColorRgb = <T extends BaseColorTypes>(
palette: ColorPalettes[T],
step: number,
): string => {
const color = palette.find((color) => color.step === colorStepMap[step])
?.color;
const { r, g, b } = colorHelper.toRgb(color);
return `rgb(${r},${g},${b})`;
};

const generateRNPTokens = (colorPalettes: ColorPalettes) => {
/**
* Helper function to get a color from a palette
* @param color
* @param step 0-10
* @returns
*/
const g = (color: BaseColorTypes, step: number) =>
getColorRgb(colorPalettes[color], step);
const lightTheme: Record<string, any> = {
primary: g("primary", 6),
onPrimary: g("primary", 0),
primaryContainer: g("primary", 1),
onPrimaryContainer: g("primary", 9),
secondary: g("secondary", 6),
onSecondary: g("secondary", 0),
secondaryContainer: g("secondary", 1),
onSecondaryContainer: g("secondary", 9),
tertiary: g("accent", 6),
onTertiary: g("accent", 0),
tertiaryContainer: g("accent", 1),
onTertiaryContainer: g("accent", 9),
error: "rgb(186, 26, 26)",
onError: "rgb(255, 255, 255)",
errorContainer: "rgb(255, 218, 214)",
onErrorContainer: "rgb(65, 0, 2)",
background: g("gray", 0),
onBackground: g("gray", 10),
surface: g("gray", 0),
onSurface: g("gray", 10),
surfaceVariant: g("gray", 1),
onSurfaceVariant: g("gray", 9),
outline: g("gray", 2),
outlineVariant: g("accent", 3),
shadow: g("gray", 10),
scrim: g("gray", 10),
inverseSurface: g("gray", 9),
inverseOnSurface: g("gray", 1),
inversePrimary: g("primary", 6),
elevation: {
level0: "transparent",
level1: g("gray", 1),
level2: g("gray", 2),
level3: g("gray", 3),
level4: g("gray", 4),
level5: g("gray", 5),
},
surfaceDisabled: "rgba(29, 27, 30, 0.12)",
onSurfaceDisabled: "rgba(29, 27, 30, 0.38)",
backdrop: "rgba(51, 47, 55, 0.4)",
};
const darkTheme: Record<string, any> = {
primary: g("primary", 6),
onPrimary: g("primary", 0),
primaryContainer: g("primary", 1),
onPrimaryContainer: g("primary", 9),
secondary: g("secondary", 6),
onSecondary: g("secondary", 0),
secondaryContainer: g("secondary", 1),
onSecondaryContainer: g("secondary", 9),
tertiary: g("accent", 6),
onTertiary: g("accent", 0),
tertiaryContainer: g("accent", 1),
onTertiaryContainer: g("accent", 9),
error: "rgb(186, 26, 26)",
onError: "rgb(255, 255, 255)",
errorContainer: "rgb(255, 218, 214)",
onErrorContainer: "rgb(65, 0, 2)",
background: g("gray", 10),
onBackground: g("gray", 0),
surface: g("gray", 10),
onSurface: g("gray", 0),
surfaceVariant: g("gray", 9),
onSurfaceVariant: g("gray", 1),
outline: g("gray", 8),
outlineVariant: g("accent", 3),
shadow: g("gray", 0),
scrim: g("gray", 0),
inverseSurface: g("gray", 1),
inverseOnSurface: g("gray", 9),
inversePrimary: g("primary", 6),
elevation: {
level0: "transparent",
level1: g("gray", 9),
level2: g("gray", 8),
level3: g("gray", 7),
level4: g("gray", 6),
level5: g("gray", 5),
},
surfaceDisabled: "rgba(29, 27, 30, 0.12)",
onSurfaceDisabled: "rgba(29, 27, 30, 0.38)",
backdrop: "rgba(51, 47, 55, 0.4)",
};
const tokens = {
light: lightTheme,
dark: darkTheme,
};

return `export const tokens = ${JSON.stringify(tokens, null, 2)}`;
};

export default generateRNPTokens;
4 changes: 3 additions & 1 deletion apps/web/lib/colorHelper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BaseColors, ColorMode, RawColor } from "@/types/app";
import { BaseColors, ColorMode, RGB, RawColor } from "@/types/app";
import tinycolor from "tinycolor2";

const toHex = (color: string | RawColor): string =>
Expand All @@ -7,6 +7,7 @@ const toRgba = (color: string | RawColor): string =>
tinycolor(color).toRgbString();
const toHsla = (color: string | RawColor): string =>
tinycolor(color).toHslString();
const toRgb = (color: string | RawColor): RGB => tinycolor(color).toRgb();

const toColorMode = (color: any, mode: ColorMode): string => {
if (!tinycolor(color).isValid) return "#000";
Expand Down Expand Up @@ -55,6 +56,7 @@ const colorStringToBaseColors = (
export const colorHelper = {
toHex,
toRgba,
toRgb,
toHsla,
toColorMode,
toForeground,
Expand Down
3 changes: 2 additions & 1 deletion apps/web/lib/generateVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { colorStepMap } from "./colorStepMap";
import { colorHelper } from "./colorHelper";
import generateFigmaUrlToken from "./code-gen/generate-figma-url-token";
import generateRNPTokens from "./code-gen/generate-rnp-tokens";

export enum CodeGenerateType {
CODEGEN = "codegen",
Expand Down Expand Up @@ -163,7 +164,7 @@ export const generateCssVariables = async ({
case CodeButtonTitle.SHADCN:
return generateShadcnCss(colorPalettes);
case CodeButtonTitle.REACT_NATIVE_PAPER:
return "";
return generateRNPTokens(colorPalettes);
case CodeButtonTitle.FIGMA:
return await generateFigmaUrlToken(baseColors);
case CodeButtonTitle.WEBFLOW:
Expand Down
13 changes: 13 additions & 0 deletions apps/web/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
module.exports = {
reactStrictMode: true,
images: {
remotePatterns: [
{
protocol: 'http',
hostname: 'localhost',
},
{
protocol: 'https',
hostname: 'fluid-colors.vercel.app',
pathname: '*',
},
]
},
transpilePackages: ["ui"],
typescript: {
ignoreBuildErrors: true
Expand Down
6 changes: 6 additions & 0 deletions apps/web/types/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export type RawColor = {
a: number;
};

export type RGB = {
r: number;
g: number;
b: number;
};

export type BaseColorTypes = "primary" | "secondary" | "accent" | "gray";

export type BaseColors = Record<BaseColorTypes, RawColor>;
Expand Down

0 comments on commit a02686c

Please sign in to comment.