diff --git a/public/default.css b/public/default.css index 48e11dc..3b80cf6 100644 --- a/public/default.css +++ b/public/default.css @@ -1,15 +1,15 @@ /* 下面是统一的颜色定义 */ /* 默认的主题样式 */ :root { - background-color: var(--color-bg-1); - color: var(--color-text-1); + background-color: var(--background-1); + color: var(--text-1); font: 14px/1.5 "Helvetica Neue",Helvetica,Arial,"Microsoft Yahei","Hiragino Sans GB","Heiti SC","WenQuanYi Micro Hei",sans-serif; } button { background-color: var(--primary-6); - color: var(--color-bg-white); - border: var(--border-1) solid var(--color-border-3); + color: var(--background-white); + border: var(--border-1) solid var(--border-3); border-radius: var(--border-radius-small); padding: var(--size-3) var(--size-4); font-size: var(--font-size-body-3); @@ -18,18 +18,18 @@ button:hover { background-color: var(--primary-5); - border-color: var(--color-border-4); + border-color: var(--border-4); } button:active { background-color: var(--primary-7); - border-color: var(--color-border-4); + border-color: var(--border-4); } input, textarea { - background-color: var(--color-fill-1); - color: var(--color-text-1); - border: var(--border-1) solid var(--color-border-2); + background-color: var(--fill-1); + color: var(--text-1); + border: var(--border-1) solid var(--border-2); border-radius: var(--border-radius-small); padding: var(--size-3); font-size: var(--font-size-body-3); @@ -37,9 +37,9 @@ } .card { - background-color: var(--color-bg-2); - color: var(--color-text-1); - border: var(--border-1) solid var(--color-border-1); + background-color: var(--background-2); + color: var(--text-1); + border: var(--border-1) solid var(--border-1); border-radius: var(--border-radius-large); padding: var(--size-5); box-shadow: var(--shadow2-center); @@ -57,11 +57,11 @@ } h1, h2, h3, h4, h5, h6 { - color: var(--color-text-1); + color: var(--text-1); } p { - color: var(--color-text-1); + color: var(--text-1); font-size: var(--font-size-body-3); } @@ -71,13 +71,13 @@ } th, td { - border: var(--border-1) solid var(--color-border-2); + border: var(--border-1) solid var(--border-2); padding: var(--size-3); text-align: left; } thead { - background-color: var(--color-bg-2); + background-color: var(--background-2); } .alert { diff --git a/src/components/Live/LivePreview.tsx b/src/components/Live/LivePreview.tsx index 359c89b..cdcfd88 100644 --- a/src/components/Live/LivePreview.tsx +++ b/src/components/Live/LivePreview.tsx @@ -1,6 +1,5 @@ import React, { useContext } from "react"; import LiveContext from "./LiveContext"; -import "./style.css" type Props = { Component?: T; diff --git a/src/components/Live/style.css b/src/components/Live/style.css deleted file mode 100644 index 7a87a63..0000000 --- a/src/components/Live/style.css +++ /dev/null @@ -1,9 +0,0 @@ -.preview-box > div, -.preview-box > div div { - cursor: crosshair; -} - -/* .preview-box > div *:not(div), -.preview-box > div div *:not(div) { - cursor: initial; -} */ \ No newline at end of file diff --git a/src/components/Selected/ClickContext.css b/src/components/Selected/ClickContext.css index 06d0cb9..0ca5af5 100644 --- a/src/components/Selected/ClickContext.css +++ b/src/components/Selected/ClickContext.css @@ -1,15 +1,20 @@ -.selected { - border: 3px solid var(--color-text-1); +.click-container .selected { + border: 3px solid var(--text-1); box-shadow: var(--shadow3-center); transition: border 0.15s ease, box-shadow 0.15s ease; } .context-menu { position: absolute; - background-color: var(--color-bg-5); - border: 1px solid var(--color-border-2); + background-color: var(--background-5); + border: 1px solid var(--border-2); box-shadow: var(--shadow2-center); padding: 10px; z-index: 1000; border-radius: var(--border-radius-medium); +} + +.click-container > .preview-box > div, +.click-container > .preview-box > div div { + cursor: crosshair; } \ No newline at end of file diff --git a/src/components/Selected/ClickContext.tsx b/src/components/Selected/ClickContext.tsx index c62d354..ac0abf8 100644 --- a/src/components/Selected/ClickContext.tsx +++ b/src/components/Selected/ClickContext.tsx @@ -85,7 +85,7 @@ const ClickContextProvider: React.FC = ({ return ( - {children} +
{children}
{showMenu && (
{ - setTailwindExtendProps({ - primary: "var(--primary)", - success: "var(--success)", - warning: "var(--warning)", - danger: "var(--danger)", - link: "var(--link)", - }); + getColorAsync().then((colors) => { + setTailwindExtendProps(generateTailwindProps(colors.onLight)); + createLightTheme(colors.onLight); + createDarkTheme(colors.onDark!); + }) }); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/src/pages/creation/components/colorPalette.tsx b/src/pages/creation/components/colorPalette.tsx index 15f6057..dd2f77b 100644 --- a/src/pages/creation/components/colorPalette.tsx +++ b/src/pages/creation/components/colorPalette.tsx @@ -96,7 +96,7 @@ const ColorPalette: React.FC = () => {
Light Colors
-
+
{Object.keys(colors.onLight).map((key) => (
{`${key}:`} @@ -113,7 +113,7 @@ const ColorPalette: React.FC = () => {
Dark Colors
-
+
{Object.keys(colors.onDark!).map((key) => (
{`${key}:`} diff --git a/src/utils/theme/index.ts b/src/utils/theme/index.ts index 0d17155..f6658f3 100644 --- a/src/utils/theme/index.ts +++ b/src/utils/theme/index.ts @@ -1,5 +1,6 @@ import { ColorSchema, + ReverseHandlerConfig, RGBString, RGBValue, ThemeSection, @@ -9,6 +10,10 @@ import { import { themeColorSchema } from "../../data/colors"; import { useDebounce } from "../function"; +/** + * 更新主题颜色 + * @param param0 更新主题颜色的参数 + */ export function updateThemeColor({ themeClass, colorVariable, @@ -25,6 +30,12 @@ export function updateThemeColor({ } } +/** + * 创建主题 + * @param themeClass 主题类名 + * @param themeVariables 主题变量 + * @returns 主题id + */ export function createTheme( themeClass: string, themeVariables: ThemeVariables @@ -60,6 +71,11 @@ export function createTheme( return id; } +/** + * 解析RGB颜色 + * @param rgb RGB颜色字符串 + * @returns RGB颜色值 + */ export function parseRGB(rgb: RGBString): RGBValue { const result = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.exec(rgb); if (!result) { @@ -72,14 +88,32 @@ export function parseRGB(rgb: RGBString): RGBValue { }; } +/** + * 将RGB颜色值转换为字符串 + * @param param0 RGB颜色值 + * @returns RGB字符串 + */ export function toRGBString({ r, g, b }: RGBValue): string { return `rgb(${r}, ${g}, ${b})`; } +/** + * 限制数值在指定范围内 + * @param value 数值 + * @param min 最小值 + * @param max 最大值 + * @returns 限制后的数值 + */ export function clamp(value: number, min: number, max: number): number { return Math.max(min, Math.min(max, value)); } +/** + * 变暗颜色 + * @param rgb RGB颜色字符串 + * @param percent 变暗百分比 + * @returns 变暗后的颜色 + */ export function darken(rgb: RGBString, percent: number): string { const { r, g, b } = parseRGB(rgb); const factor = 1 - percent / 100; @@ -90,6 +124,12 @@ export function darken(rgb: RGBString, percent: number): string { }); } +/** + * 变亮颜色 + * @param rgb RGB颜色字符串 + * @param percent 变亮百分比 + * @returns 变亮后的颜色 + */ export function lighten(rgb: RGBString, percent: number): string { const { r, g, b } = parseRGB(rgb); const factor = 1 + percent / 100; @@ -100,18 +140,13 @@ export function lighten(rgb: RGBString, percent: number): string { }); } -function parseRGBString(rgbString: RGBString): RGBValue { - const matches = rgbString.match(/(\d+)/g); - if (!matches) { - throw new Error("Invalid RGB string"); - } - return { - r: parseInt(matches[0], 10), - g: parseInt(matches[1], 10), - b: parseInt(matches[2], 10), - }; -} - +/** + * 将RGB颜色值转换为HSL颜色值 + * @param r 红色值 + * @param g 绿色值 + * @param b 蓝色值 + * @returns HSL颜色值 + */ function rgbToHsl(r: number, g: number, b: number): [number, number, number] { r /= 255; g /= 255; @@ -143,6 +178,13 @@ function rgbToHsl(r: number, g: number, b: number): [number, number, number] { return [h, s, l]; } +/** + * 将HSL颜色值转换为RGB颜色值 + * @param h 色相 + * @param s 饱和度 + * @param l 亮度 + * @returns + */ function hslToRgb(h: number, s: number, l: number): [number, number, number] { let r: number, g: number, b: number; @@ -169,6 +211,13 @@ function hslToRgb(h: number, s: number, l: number): [number, number, number] { return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; } +/** + * 调整颜色亮度 + * @param rgb RGB颜色值 + * @param low 亮度下限 + * @param top 亮度上限 + * @returns + */ function getAdjustedBrightness( rgb: RGBValue, low: number, @@ -180,32 +229,105 @@ function getAdjustedBrightness( return { r, g, b }; } +/** + * 格式化RGB颜色字符串 + * @param rgb RGB颜色值 + * @returns RGB颜色字符串 + */ function formatRGBString(rgb: RGBValue): RGBString { return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`; } +/** + * 获取调整后的颜色 + * @param rgbString RGB颜色字符串 + * @param low 亮度下限 + * @param top 亮度上限 + * @returns 调整后的颜色 + */ export function getAdjustedColor( rgbString: RGBString, low: number = 0.1, top: number = 0.9 ): RGBString { - const rgb = parseRGBString(rgbString); + const rgb = parseRGB(rgbString); const adjustedRgb = getAdjustedBrightness(rgb, low, top); return formatRGBString(adjustedRgb); } -// 把一个对象中是rgb颜色的值全部反转 +const defaultReverseConfig: ReverseHandlerConfig = { + dark: "none", + light: "none", + black: "none", + white: "none", + primary: "reverse", +}; +/** + * 反转颜色 + * @param obj 颜色对象 + * @returns 反转后的颜色对象 + */ export function reverseColor( - obj: Record + obj: Record, + config?: ReverseHandlerConfig ): Record { + config = { + ...defaultReverseConfig, + ...config, + }; + const noneHandler = (value: RGBString) => { + return value; + }; + + const reverseHandler = (value: RGBString) => { + return getAdjustedColor(value); + }; + + const lightenHandler = (value: RGBString) => { + return lighten(value, 20); + }; + + const darkenHandler = (value: RGBString) => { + return darken(value, 20); + }; + + const isConfiged = (key: string) => { + if (!config) { + return false; + } + let con = false; + Object.keys(config).forEach((c) => { + if (key.includes(c)) { + con = true; + } + }); + return con; + }; + const result: Record = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; if (value.startsWith("rgb")) { try { - result[key] = getAdjustedColor(value as RGBString); + if (isConfiged(key)) { + switch (config![key.split("-")[0]]) { + case "reverse": + result[key] = reverseHandler(value as RGBString); + break; + case "lighten": + result[key] = lightenHandler(value as RGBString); + break; + case "darken": + result[key] = darkenHandler(value as RGBString); + break; + default: + result[key] = noneHandler(value as RGBString); + } + } else { + result[key] = reverseHandler(value as RGBString); + } } catch (error) { console.log(`对象中存在属性${key}的值为${result[key]}无法转换`); } @@ -217,6 +339,10 @@ export function reverseColor( return result; } +/** + * 设置Tailwind CSS的扩展属性 + * @param colors 颜色对象 + */ export const setTailwindExtendProps = (colors: Record) => { if (!tailwind) { throw new Error("Tailwind CSS not found"); @@ -238,6 +364,11 @@ const themeMap = { system: "", } as const; +/** + * 创建浅色主题 + * @param themeVariables 主题变量 + * @returns 创建浅色主题的函数 + */ export const createLightTheme = (themeVariables: ThemeVariables) => { return useDebounce(() => { if (themeLightId) { @@ -247,6 +378,11 @@ export const createLightTheme = (themeVariables: ThemeVariables) => { }, 50)(); }; +/** + * 创建深色主题 + * @param themeVariables 主题变量 + * @returns 创建深色主题的函数 + */ export const createDarkTheme = (themeVariables: ThemeVariables) => { return useDebounce(() => { if (themeDarkId) { @@ -256,6 +392,10 @@ export const createDarkTheme = (themeVariables: ThemeVariables) => { }, 50)(); }; +/** + * 异步获取颜色 + * @returns 颜色对象 + */ export const getColorAsync = async () => { if (!themeColorSchema.onDark) { themeColorSchema.onDark = reverseColor(themeColorSchema.onLight); @@ -267,14 +407,12 @@ export const getColorAsync = async () => { }); }; +/** + * 主题ID + */ let themeLightId: string | null = null; let themeDarkId: string | null = null; -getColorAsync().then((color) => { - createLightTheme(color.onLight); - createDarkTheme(color.onDark!); -}); - // 在退出的时候删除这两个主题,防止重复加载 window.addEventListener("unload", () => { setTimeout(() => { @@ -287,6 +425,10 @@ window.addEventListener("unload", () => { }, 0); }); +/** + * 修改主题颜色 + * @param schema 颜色模式 + */ export const changeThemeSchema = (schema: ColorSchema) => { // 通过id删除原有的主题,然后重新生成 if (themeLightId) { @@ -299,6 +441,11 @@ export const changeThemeSchema = (schema: ColorSchema) => { themeDarkId = createTheme(themeMap.dark, schema.onDark!); }; +/** + * 覆盖颜色 + * @param theme 主题 + * @param color 颜色 + */ export const overrideColor = ( theme: ThemeSection, color: Record @@ -313,3 +460,29 @@ export const overrideColor = ( } } }; + +/** + * 生成Tailwind CSS的扩展属性 + * @param colors 颜色对象 + * @returns Tailwind CSS的扩展属性 + */ +export const generateTailwindProps = (colors: ThemeVariables) => { + const colorsExtend: Record = {}; + + Object.keys(colors).forEach((key) => { + // 将--或者-或者---替换为_,然后分割 + const newKey = key.replace(/-+/g, "_").replace(/^--/, "").split("_"); + + // 使用reduce嵌套对象 + newKey.reduce((acc, curr, index) => { + if (index === newKey.length - 1) { + acc[curr] = `var(--${key})`; + } else { + acc[curr] = acc[curr] || {}; + } + return acc[curr]; + }, colorsExtend); + }); + + return colorsExtend; +}; diff --git a/tailwind.config.cjs b/tailwind.config.cjs index b229b7f..c66a5e4 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -2,104 +2,104 @@ module.exports = { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { - extend: { - colors: { - primary: { - 1: "var(--primary-1)", - 2: "var(--primary-2)", - 3: "var(--primary-3)", - 4: "var(--primary-4)", - 5: "var(--primary-5)", - 6: "var(--primary-6)", - 7: "var(--primary-7)", - }, - success: { - 1: "var(--success-1)", - 2: "var(--success-2)", - 3: "var(--success-3)", - 4: "var(--success-4)", - 5: "var(--success-5)", - 6: "var(--success-6)", - 7: "var(--success-7)", - }, - warning: { - 1: "var(--warning-1)", - 2: "var(--warning-2)", - 3: "var(--warning-3)", - 4: "var(--warning-4)", - 5: "var(--warning-5)", - 6: "var(--warning-6)", - 7: "var(--warning-7)", - }, - danger: { - 1: "var(--danger-1)", - 2: "var(--danger-2)", - 3: "var(--danger-3)", - 4: "var(--danger-4)", - 5: "var(--danger-5)", - 6: "var(--danger-6)", - 7: "var(--danger-7)", - }, - link: { - 1: "var(--link-1)", - 2: "var(--link-2)", - 3: "var(--link-3)", - 4: "var(--link-4)", - 5: "var(--link-5)", - 6: "var(--link-6)", - 7: "var(--link-7)", - }, - background: { - 1: "var(--color-bg-1)", - 2: "var(--color-bg-2)", - 3: "var(--color-bg-3)", - 4: "var(--color-bg-4)", - 5: "var(--color-bg-5)", - white: "var(--color-bg-white)", - }, - text: { - 1: "var(--color-text-1)", - 2: "var(--color-text-2)", - 3: "var(--color-text-3)", - 4: "var(--color-text-4)", - }, - border: { - 1: "var(--color-border-1)", - 2: "var(--color-border-2)", - 3: "var(--color-border-3)", - 4: "var(--color-border-4)", - }, - fill: { - 1: "var(--color-fill-1)", - 2: "var(--color-fill-2)", - 3: "var(--color-fill-3)", - 4: "var(--color-fill-4)", - }, - // data - data: { - 1: "var(--data-1)", - 2: "var(--data-2)", - 3: "var(--data-3)", - 4: "var(--data-4)", - 5: "var(--data-5)", - 6: "var(--data-6)", - 7: "var(--data-7)", - 8: "var(--data-8)", - 9: "var(--data-9)", - 10: "var(--data-10)", - 11: "var(--data-11)", - 12: "var(--data-12)", - 13: "var(--data-13)", - 14: "var(--data-14)", - 15: "var(--data-15)", - 16: "var(--data-16)", - 17: "var(--data-17)", - 18: "var(--data-18)", - 19: "var(--data-19)", - 20: "var(--data-20)", - }, - shadow: "--color-shadow" - }, - }, + // extend: { + // colors: { + // primary: { + // 1: "var(--primary-1)", + // 2: "var(--primary-2)", + // 3: "var(--primary-3)", + // 4: "var(--primary-4)", + // 5: "var(--primary-5)", + // 6: "var(--primary-6)", + // 7: "var(--primary-7)", + // }, + // success: { + // 1: "var(--success-1)", + // 2: "var(--success-2)", + // 3: "var(--success-3)", + // 4: "var(--success-4)", + // 5: "var(--success-5)", + // 6: "var(--success-6)", + // 7: "var(--success-7)", + // }, + // warning: { + // 1: "var(--warning-1)", + // 2: "var(--warning-2)", + // 3: "var(--warning-3)", + // 4: "var(--warning-4)", + // 5: "var(--warning-5)", + // 6: "var(--warning-6)", + // 7: "var(--warning-7)", + // }, + // danger: { + // 1: "var(--danger-1)", + // 2: "var(--danger-2)", + // 3: "var(--danger-3)", + // 4: "var(--danger-4)", + // 5: "var(--danger-5)", + // 6: "var(--danger-6)", + // 7: "var(--danger-7)", + // }, + // link: { + // 1: "var(--link-1)", + // 2: "var(--link-2)", + // 3: "var(--link-3)", + // 4: "var(--link-4)", + // 5: "var(--link-5)", + // 6: "var(--link-6)", + // 7: "var(--link-7)", + // }, + // background: { + // 1: "var(--color-bg-1)", + // 2: "var(--color-bg-2)", + // 3: "var(--color-bg-3)", + // 4: "var(--color-bg-4)", + // 5: "var(--color-bg-5)", + // white: "var(--color-bg-white)", + // }, + // text: { + // 1: "var(--color-text-1)", + // 2: "var(--color-text-2)", + // 3: "var(--color-text-3)", + // 4: "var(--color-text-4)", + // }, + // border: { + // 1: "var(--color-border-1)", + // 2: "var(--color-border-2)", + // 3: "var(--color-border-3)", + // 4: "var(--color-border-4)", + // }, + // fill: { + // 1: "var(--color-fill-1)", + // 2: "var(--color-fill-2)", + // 3: "var(--color-fill-3)", + // 4: "var(--color-fill-4)", + // }, + // // data + // data: { + // 1: "var(--data-1)", + // 2: "var(--data-2)", + // 3: "var(--data-3)", + // 4: "var(--data-4)", + // 5: "var(--data-5)", + // 6: "var(--data-6)", + // 7: "var(--data-7)", + // 8: "var(--data-8)", + // 9: "var(--data-9)", + // 10: "var(--data-10)", + // 11: "var(--data-11)", + // 12: "var(--data-12)", + // 13: "var(--data-13)", + // 14: "var(--data-14)", + // 15: "var(--data-15)", + // 16: "var(--data-16)", + // 17: "var(--data-17)", + // 18: "var(--data-18)", + // 19: "var(--data-19)", + // 20: "var(--data-20)", + // }, + // shadow: "--color-shadow" + // }, + // }, }, }; diff --git a/typings/index.ts b/typings/index.ts index fe71aa9..4eeeabd 100644 --- a/typings/index.ts +++ b/typings/index.ts @@ -5,8 +5,17 @@ export type CodeBlock = { }; export type UpdateThemeColor = { + /** + * @description 主题类名 + */ themeClass: string; + /** + * @description 颜色变量 + */ colorVariable: string; + /** + * @description 新颜色 + */ newColor: string; }; @@ -26,8 +35,12 @@ export type ThemeVariables = { }; export type ColorSchema = { - onLight: Record, - onDark?: Record, -} + onLight: Record; + onDark?: Record; +}; -export type ThemeSection = "dark" | "light" | "system"; \ No newline at end of file +export type ThemeSection = "dark" | "light" | "system"; + +export type ReverseHandlerType = "reverse" | "none" | "lighten" | "darken"; + +export type ReverseHandlerConfig = Record; \ No newline at end of file