From 33e2bea2b6a026b314b19f16a66a9d15c29d0fbf Mon Sep 17 00:00:00 2001 From: Maharshi Alpesh Date: Wed, 22 Jan 2025 10:26:16 +0530 Subject: [PATCH 01/16] chore: adding xylish's contributions + modifying styles --- apps/docs/app/themes/page.tsx | 9 + apps/docs/components/navbar.tsx | 11 + apps/docs/components/theme-panel.tsx | 44 +++ .../themes/components/color-picker.tsx | 142 ++++++++++ .../themes/components/config-section.tsx | 24 ++ .../components/configuration/actions.tsx | 60 ++++ .../components/configuration/base-colors.tsx | 69 +++++ .../configuration/border-widths.tsx | 38 +++ .../components/configuration/brand-colors.tsx | 76 ++++++ .../components/configuration/font-sizes.tsx | 41 +++ .../themes/components/configuration/index.tsx | 146 ++++++++++ .../components/configuration/line-heights.tsx | 50 ++++ .../components/configuration/other-colors.tsx | 48 ++++ .../themes/components/configuration/other.tsx | 38 +++ .../components/configuration/radiuses.tsx | 42 +++ .../themes/components/copy-button.tsx | 61 +++++ .../themes/components/number-input.tsx | 21 ++ .../themes/components/select-template.tsx | 54 ++++ .../themes/components/showcase-component.tsx | 125 +++++++++ .../themes/components/showcase/avatar.tsx | 52 ++++ .../themes/components/showcase/badge.tsx | 32 +++ .../components/showcase/breadcrumbs.tsx | 32 +++ .../themes/components/showcase/button.tsx | 35 +++ .../themes/components/showcase/checkbox.tsx | 32 +++ .../themes/components/showcase/chip.tsx | 20 ++ .../components/showcase/circular-progress.tsx | 31 +++ .../themes/components/showcase/code.tsx | 20 ++ .../themes/components/showcase/content.tsx | 37 +++ .../themes/components/showcase/divider.tsx | 26 ++ .../themes/components/showcase/dropdown.tsx | 48 ++++ .../themes/components/showcase/font-size.tsx | 14 + .../themes/components/showcase/index.tsx | 47 ++++ .../themes/components/showcase/input.tsx | 26 ++ .../components/showcase/line-height.tsx | 14 + .../themes/components/showcase/link.tsx | 31 +++ .../themes/components/showcase/pagination.tsx | 39 +++ .../themes/components/showcase/select.tsx | 60 ++++ .../themes/components/showcase/slider.tsx | 27 ++ .../themes/components/showcase/switch.tsx | 31 +++ .../themes/components/showcase/tabs.tsx | 29 ++ apps/docs/components/themes/constants.ts | 107 ++++++++ apps/docs/components/themes/css-vars.ts | 152 +++++++++++ apps/docs/components/themes/index.tsx | 27 ++ apps/docs/components/themes/provider.tsx | 257 ++++++++++++++++++ .../components/themes/templates/coffee.ts | 54 ++++ .../components/themes/templates/emerald.ts | 44 +++ .../docs/components/themes/templates/index.ts | 11 + .../components/themes/templates/nextui.ts | 8 + apps/docs/components/themes/types.ts | 123 +++++++++ apps/docs/components/themes/utils/colors.ts | 138 ++++++++++ apps/docs/components/themes/utils/config.ts | 135 +++++++++ apps/docs/components/themes/utils/shared.ts | 17 ++ apps/docs/hooks/use-previous.ts | 18 ++ apps/docs/package.json | 4 +- pnpm-lock.yaml | 61 +++++ 55 files changed, 2937 insertions(+), 1 deletion(-) create mode 100644 apps/docs/app/themes/page.tsx create mode 100644 apps/docs/components/theme-panel.tsx create mode 100644 apps/docs/components/themes/components/color-picker.tsx create mode 100644 apps/docs/components/themes/components/config-section.tsx create mode 100644 apps/docs/components/themes/components/configuration/actions.tsx create mode 100644 apps/docs/components/themes/components/configuration/base-colors.tsx create mode 100644 apps/docs/components/themes/components/configuration/border-widths.tsx create mode 100644 apps/docs/components/themes/components/configuration/brand-colors.tsx create mode 100644 apps/docs/components/themes/components/configuration/font-sizes.tsx create mode 100644 apps/docs/components/themes/components/configuration/index.tsx create mode 100644 apps/docs/components/themes/components/configuration/line-heights.tsx create mode 100644 apps/docs/components/themes/components/configuration/other-colors.tsx create mode 100644 apps/docs/components/themes/components/configuration/other.tsx create mode 100644 apps/docs/components/themes/components/configuration/radiuses.tsx create mode 100644 apps/docs/components/themes/components/copy-button.tsx create mode 100644 apps/docs/components/themes/components/number-input.tsx create mode 100644 apps/docs/components/themes/components/select-template.tsx create mode 100644 apps/docs/components/themes/components/showcase-component.tsx create mode 100644 apps/docs/components/themes/components/showcase/avatar.tsx create mode 100644 apps/docs/components/themes/components/showcase/badge.tsx create mode 100644 apps/docs/components/themes/components/showcase/breadcrumbs.tsx create mode 100644 apps/docs/components/themes/components/showcase/button.tsx create mode 100644 apps/docs/components/themes/components/showcase/checkbox.tsx create mode 100644 apps/docs/components/themes/components/showcase/chip.tsx create mode 100644 apps/docs/components/themes/components/showcase/circular-progress.tsx create mode 100644 apps/docs/components/themes/components/showcase/code.tsx create mode 100644 apps/docs/components/themes/components/showcase/content.tsx create mode 100644 apps/docs/components/themes/components/showcase/divider.tsx create mode 100644 apps/docs/components/themes/components/showcase/dropdown.tsx create mode 100644 apps/docs/components/themes/components/showcase/font-size.tsx create mode 100644 apps/docs/components/themes/components/showcase/index.tsx create mode 100644 apps/docs/components/themes/components/showcase/input.tsx create mode 100644 apps/docs/components/themes/components/showcase/line-height.tsx create mode 100644 apps/docs/components/themes/components/showcase/link.tsx create mode 100644 apps/docs/components/themes/components/showcase/pagination.tsx create mode 100644 apps/docs/components/themes/components/showcase/select.tsx create mode 100644 apps/docs/components/themes/components/showcase/slider.tsx create mode 100644 apps/docs/components/themes/components/showcase/switch.tsx create mode 100644 apps/docs/components/themes/components/showcase/tabs.tsx create mode 100644 apps/docs/components/themes/constants.ts create mode 100644 apps/docs/components/themes/css-vars.ts create mode 100644 apps/docs/components/themes/index.tsx create mode 100644 apps/docs/components/themes/provider.tsx create mode 100644 apps/docs/components/themes/templates/coffee.ts create mode 100644 apps/docs/components/themes/templates/emerald.ts create mode 100644 apps/docs/components/themes/templates/index.ts create mode 100644 apps/docs/components/themes/templates/nextui.ts create mode 100644 apps/docs/components/themes/types.ts create mode 100644 apps/docs/components/themes/utils/colors.ts create mode 100644 apps/docs/components/themes/utils/config.ts create mode 100644 apps/docs/components/themes/utils/shared.ts create mode 100644 apps/docs/hooks/use-previous.ts diff --git a/apps/docs/app/themes/page.tsx b/apps/docs/app/themes/page.tsx new file mode 100644 index 0000000000..17cd5544c5 --- /dev/null +++ b/apps/docs/app/themes/page.tsx @@ -0,0 +1,9 @@ +import {ThemeBuilder} from "@/components/themes"; + +export default function ThemesPage() { + return ( +
+ +
+ ); +} diff --git a/apps/docs/components/navbar.tsx b/apps/docs/components/navbar.tsx index 9be396c459..d56df3df0c 100644 --- a/apps/docs/components/navbar.tsx +++ b/apps/docs/components/navbar.tsx @@ -333,6 +333,17 @@ export const Navbar: FC = ({children, routes, mobileRoutes = [], sl + + handlePressNavbarItem("Themes", "/themes")} + > + Theme + + diff --git a/apps/docs/components/theme-panel.tsx b/apps/docs/components/theme-panel.tsx new file mode 100644 index 0000000000..f3c01947a1 --- /dev/null +++ b/apps/docs/components/theme-panel.tsx @@ -0,0 +1,44 @@ +"use client"; + +import {Button} from "@nextui-org/react"; +import {CopyIcon, MoonIcon, SunIcon} from "@nextui-org/shared-icons"; +import {useTheme} from "next-themes"; +import {useEffect, useState} from "react"; + +const ThemePanel = () => { + const {theme, setTheme} = useTheme(); + const [isMounted, setIsMounted] = useState(false); + + useEffect(() => { + setIsMounted(true); + }, []); + + if (!isMounted) { + return null; + } + + return ( +
+

Theme

+
+ + +
+
+

Color

+
+
+ ); +}; + +export default ThemePanel; diff --git a/apps/docs/components/themes/components/color-picker.tsx b/apps/docs/components/themes/components/color-picker.tsx new file mode 100644 index 0000000000..946dc336e3 --- /dev/null +++ b/apps/docs/components/themes/components/color-picker.tsx @@ -0,0 +1,142 @@ +import {useEffect, useState} from "react"; +import {Button, Popover, PopoverContent, PopoverTrigger} from "@nextui-org/react"; +import {HexColorInput, HexColorPicker} from "react-colorful"; +import Values from "values.js"; +import {readableColor} from "color2k"; +import waterDrop from "@iconify/icons-solar/waterdrop-linear"; +import {Icon} from "@iconify/react/dist/offline"; +import {useTheme} from "next-themes"; +import {clsx} from "@nextui-org/shared-utils"; + +import {ColorPickerType, ThemeType} from "../types"; +import {colorValuesToRgb, getColorWeight} from "../utils/colors"; + +import {CopyButton} from "./copy-button"; + +interface ColorPickerProps { + hexColor: string; + icon?: React.ReactNode; + label: string; + type: ColorPickerType; + onChange: (hexColor: string) => void; + onClose: (hexColor: string) => void; + onCopy: (theme: ThemeType) => void; +} + +export function ColorPicker({ + hexColor, + icon, + label, + type, + onChange, + onClose, + onCopy, +}: ColorPickerProps) { + const [selectedColor, setSelectedColor] = useState(hexColor); + + const [isOpen, setIsOpen] = useState(false); + const theme = useTheme().theme as ThemeType; + const selectedColorWeight = getColorWeight(type, theme); + const selectedColorValues = new Values(selectedColor).all(selectedColorWeight); + + function handleChange(updatedHexColor: string) { + onChange(updatedHexColor); + setSelectedColor(updatedHexColor); + } + + /** + * Update the selected color when the popover is opened. + */ + useEffect(() => { + setSelectedColor(hexColor); + }, [hexColor, isOpen]); + + return ( +
+ onClose(selectedColor)} + onOpenChange={setIsOpen} + > + + + + +
+
+ {selectedColorValues + ?.slice(0, selectedColorValues.length - 1) + .map((colorValue, index: number) => ( +
+
+ {index === 0 ? 50 : index * 100} +
+ ))} +
+ + +
+ + + +
+ ); +} + +function getColor(type: ColorPickerType) { + switch (type) { + case "primary": + return "bg-primary text-primary-foreground"; + case "secondary": + return "bg-secondary text-secondary-foreground"; + case "success": + return "bg-success text-success-foreground"; + case "warning": + return "bg-warning text-warning-foreground"; + case "danger": + return "bg-danger text-danger-foreground"; + case "background": + return "bg-background text-foreground"; + case "foreground": + return "bg-foreground text-black"; + case "default": + return "bg-default"; + case "content1": + return "bg-content1 text-content1-foreground"; + case "content2": + return "bg-content2 text-content2-foreground"; + case "content3": + return "bg-content3 text-content3-foreground"; + case "content4": + return "bg-content4 text-content4-foreground"; + case "divider": + return "bg-divider"; + case "focus": + return "bg-focus"; + case "overlay": + return "bg-overlay"; + default: + return undefined; + } +} diff --git a/apps/docs/components/themes/components/config-section.tsx b/apps/docs/components/themes/components/config-section.tsx new file mode 100644 index 0000000000..8822244fc3 --- /dev/null +++ b/apps/docs/components/themes/components/config-section.tsx @@ -0,0 +1,24 @@ +import {clsx} from "@nextui-org/shared-utils"; + +interface ConfigurationSectionProps { + children: React.ReactNode; + cols?: number; + id?: string; + title: string; +} + +export function ConfigSection({children, cols = 2, id, title}: ConfigurationSectionProps) { + return ( +
+ {title} +
+ {children} +
+
+ ); +} diff --git a/apps/docs/components/themes/components/configuration/actions.tsx b/apps/docs/components/themes/components/configuration/actions.tsx new file mode 100644 index 0000000000..43b42efb4b --- /dev/null +++ b/apps/docs/components/themes/components/configuration/actions.tsx @@ -0,0 +1,60 @@ +import {useState} from "react"; +import {Button, Tooltip} from "@nextui-org/react"; +import {Icon} from "@iconify/react/dist/offline"; +import SunIcon from "@iconify/icons-solar/sun-linear"; +import MoonIcon from "@iconify/icons-solar/moon-linear"; +import CopyIcon from "@iconify/icons-solar/copy-linear"; +import UndoLeftIcon from "@iconify/icons-solar/undo-left-linear"; +import CheckCircleIcon from "@iconify/icons-solar/check-circle-linear"; + +import {ThemeType} from "../../types"; + +interface ActionsProps { + theme: ThemeType; + onCopy: () => unknown; + onResetTheme: () => void; + onToggleTheme: () => void; +} + +export function Actions({theme, onCopy, onResetTheme, onToggleTheme}: ActionsProps) { + const [copied, setCopied] = useState(false); + const isLight = theme === "light"; + + /** + * Handle the copying of the configuration. + */ + function handleCopyConfig() { + navigator.clipboard.writeText(JSON.stringify(onCopy(), null, 2)); + + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } + + return ( +
+ + + + + + + + + +
+ ); +} diff --git a/apps/docs/components/themes/components/configuration/base-colors.tsx b/apps/docs/components/themes/components/configuration/base-colors.tsx new file mode 100644 index 0000000000..c09c14a197 --- /dev/null +++ b/apps/docs/components/themes/components/configuration/base-colors.tsx @@ -0,0 +1,69 @@ +import {baseColorsId} from "../../constants"; +import {setCssBackground, setCssColor, setCssContentColor} from "../../css-vars"; +import {useThemeBuilder} from "../../provider"; +import {Config, ThemeType} from "../../types"; +import {copyBaseColorConfig} from "../../utils/config"; +import {ColorPicker} from "../color-picker"; +import {ConfigSection} from "../config-section"; + +interface BaseColorsProps { + config: Config; + theme: ThemeType; +} + +export function BaseColors({config, theme}: BaseColorsProps) { + const {setBaseColor} = useThemeBuilder(); + + return ( + + setCssBackground(hexColor)} + onClose={(hexColor) => setBaseColor({background: hexColor}, theme)} + onCopy={(theme) => copyBaseColorConfig(config, "background", theme)} + /> + setCssColor("foreground", hexColor, theme)} + onClose={(hexColor) => setBaseColor({foreground: hexColor}, theme)} + onCopy={(theme) => copyBaseColorConfig(config, "foreground", theme)} + /> + setCssContentColor(1, hexColor)} + onClose={(hexColor) => setBaseColor({content1: hexColor}, theme)} + onCopy={(theme) => copyBaseColorConfig(config, "content1", theme)} + /> + setCssContentColor(2, hexColor)} + onClose={(hexColor) => setBaseColor({content2: hexColor}, theme)} + onCopy={(theme) => copyBaseColorConfig(config, "content2", theme)} + /> + setCssContentColor(3, hexColor)} + onClose={(hexColor) => setBaseColor({content3: hexColor}, theme)} + onCopy={(theme) => copyBaseColorConfig(config, "content3", theme)} + /> + setCssContentColor(4, hexColor)} + onClose={(hexColor) => setBaseColor({content4: hexColor}, theme)} + onCopy={(theme) => copyBaseColorConfig(config, "content4", theme)} + /> + + ); +} diff --git a/apps/docs/components/themes/components/configuration/border-widths.tsx b/apps/docs/components/themes/components/configuration/border-widths.tsx new file mode 100644 index 0000000000..eccd819872 --- /dev/null +++ b/apps/docs/components/themes/components/configuration/border-widths.tsx @@ -0,0 +1,38 @@ +import {setCssBorderWidth} from "../../css-vars"; +import {useThemeBuilder} from "../../provider"; +import {Config} from "../../types"; +import {ConfigSection} from "../config-section"; +import {NumberInput} from "../number-input"; + +interface BorderWidthsProps { + config: Config; +} + +export function BorderWidths({config}: BorderWidthsProps) { + const {setBorderWidth} = useThemeBuilder(); + + const handleChange = (key: keyof Config["layout"]["borderWidth"], value: string) => { + setBorderWidth({[key]: value}); + setCssBorderWidth(key, value); + }; + + return ( + + handleChange("small", value)} + /> + handleChange("medium", value)} + /> + handleChange("large", value)} + /> + + ); +} diff --git a/apps/docs/components/themes/components/configuration/brand-colors.tsx b/apps/docs/components/themes/components/configuration/brand-colors.tsx new file mode 100644 index 0000000000..b5f1371e76 --- /dev/null +++ b/apps/docs/components/themes/components/configuration/brand-colors.tsx @@ -0,0 +1,76 @@ +import {colorsId} from "../../constants"; +import {setCssColor} from "../../css-vars"; +import {useThemeBuilder} from "../../provider"; +import {Config, ThemeType} from "../../types"; +import {copyBrandColorConfig} from "../../utils/config"; +import {ColorPicker} from "../color-picker"; +import {ConfigSection} from "../config-section"; + +interface BrandColorsProps { + config: Config; + syncIcon: React.ReactNode; + syncThemes: boolean; + theme: ThemeType; +} + +export function BrandColors({config, syncIcon, syncThemes, theme}: BrandColorsProps) { + const {setBrandColor} = useThemeBuilder(); + + return ( + + setCssColor("default", hexColor, theme)} + onClose={(hexColor) => setBrandColor({default: hexColor}, theme, false)} + onCopy={(theme) => copyBrandColorConfig(config, "default", theme)} + /> + setCssColor("primary", hexColor, theme)} + onClose={(hexColor) => setBrandColor({primary: hexColor}, theme, syncThemes)} + onCopy={(theme) => copyBrandColorConfig(config, "primary", theme)} + /> + setCssColor("secondary", hexColor, theme)} + onClose={(hexColor) => setBrandColor({secondary: hexColor}, theme, syncThemes)} + onCopy={(theme) => copyBrandColorConfig(config, "secondary", theme)} + /> + setCssColor("success", hexColor, theme)} + onClose={(hexColor) => setBrandColor({success: hexColor}, theme, syncThemes)} + onCopy={(theme) => copyBrandColorConfig(config, "success", theme)} + /> + setCssColor("warning", hexColor, theme)} + onClose={(hexColor) => setBrandColor({warning: hexColor}, theme, syncThemes)} + onCopy={(theme) => copyBrandColorConfig(config, "warning", theme)} + /> + setCssColor("danger", hexColor, theme)} + onClose={(hexColor) => setBrandColor({danger: hexColor}, theme, syncThemes)} + onCopy={(theme) => copyBrandColorConfig(config, "danger", theme)} + /> + + ); +} diff --git a/apps/docs/components/themes/components/configuration/font-sizes.tsx b/apps/docs/components/themes/components/configuration/font-sizes.tsx new file mode 100644 index 0000000000..ad472d8168 --- /dev/null +++ b/apps/docs/components/themes/components/configuration/font-sizes.tsx @@ -0,0 +1,41 @@ +import {setCssFontSize} from "../../css-vars"; +import {useThemeBuilder} from "../../provider"; +import {Config, ConfigLayout} from "../../types"; +import {ConfigSection} from "../config-section"; +import {NumberInput} from "../number-input"; + +interface FontSizesProps { + config: Config; +} + +export function FontSizes({config}: FontSizesProps) { + return ( + + + + + + + ); +} + +interface FontSizeInputProps { + label: string; + type: keyof ConfigLayout["fontSize"]; + value: string; +} + +function FontSizeInput({label, type, value}: FontSizeInputProps) { + const {setFontSize} = useThemeBuilder(); + + return ( + { + setFontSize({[type]: value}); + setCssFontSize(type, value); + }} + /> + ); +} diff --git a/apps/docs/components/themes/components/configuration/index.tsx b/apps/docs/components/themes/components/configuration/index.tsx new file mode 100644 index 0000000000..fdb1b199a0 --- /dev/null +++ b/apps/docs/components/themes/components/configuration/index.tsx @@ -0,0 +1,146 @@ +import {useEffect, useState} from "react"; +import { + Card, + CardBody, + CardHeader, + Divider, + Button, + CardFooter, + Link, + ScrollShadow, +} from "@nextui-org/react"; +import {useTheme} from "next-themes"; +import {useLocalStorage} from "usehooks-ts"; +import {Icon} from "@iconify/react/dist/offline"; +import LinkSquareIcon from "@iconify/icons-solar/link-square-linear"; + +import {useThemeBuilder} from "../../provider"; +import {Config, Template, ThemeType} from "../../types"; +import {configKey, syncThemesKey, initialConfig} from "../../constants"; +import {SelectTemplate} from "../select-template"; +import {generatePluginConfig} from "../../utils/config"; +import {setAllCssVars} from "../../css-vars"; + +import {BrandColors} from "./brand-colors"; +import {BaseColors} from "./base-colors"; +import {OtherColors} from "./other-colors"; +import {FontSizes} from "./font-sizes"; +import {LineHeights} from "./line-heights"; +import {Radiuses} from "./radiuses"; +import {BorderWidths} from "./border-widths"; +import {Other} from "./other"; + +import usePrevious from "@/hooks/use-previous"; +import {RotateLeftLinearIcon} from "@/components/icons"; +import {ThemeSwitch} from "@/components/theme-switch"; + +export default function Configuration() { + const [selectedTemplate, setSelectedTemplate] = useState