new feature

This commit is contained in:
pqcqaq 2024-07-18 18:00:31 +08:00
parent ce89729bac
commit 51d65f9f45
11 changed files with 375 additions and 196 deletions

View File

@ -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 {

View File

@ -1,6 +1,5 @@
import React, { useContext } from "react";
import LiveContext from "./LiveContext";
import "./style.css"
type Props<T extends React.ElementType = React.ElementType> = {
Component?: T;

View File

@ -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;
} */

View File

@ -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;
}

View File

@ -85,7 +85,7 @@ const ClickContextProvider: React.FC<ClickContextProviderProps> = ({
return (
<ClickContext.Provider value={{ selectedElement }}>
{children}
<div className="click-container">{children}</div>
{showMenu && (
<div
className="context-menu"

View File

@ -57,36 +57,36 @@ export const themeColorSchema: ColorSchema = {
"data-18": "rgb(137, 233, 224)",
"data-19": "rgb(232, 101, 223)",
"data-20": "rgb(247, 186, 239)",
"color-border-1": "rgb(242, 243, 245)",
"color-border-2": "rgb(229, 230, 235)",
"color-border-3": "rgb(201, 205, 212)",
"color-border-4": "rgb(134, 144, 156)",
"color-border-5": "rgb(78, 89, 105)",
"color-border-6": "rgb(29, 33, 41)",
"color-border-7": "rgb(14, 16, 20)",
"color-fill-1": "rgb(247, 248, 250)",
"color-fill-2": "rgb(242, 243, 245)",
"color-fill-3": "rgb(229, 230, 235)",
"color-fill-4": "rgb(201, 205, 212)",
"color-fill-5": "rgb(134, 144, 156)",
"color-fill-6": "rgb(78, 89, 105)",
"color-fill-7": "rgb(29, 33, 41)",
"color-text-1": "rgb(29, 33, 41)",
"color-text-2": "rgb(78, 89, 105)",
"color-text-3": "rgb(134, 144, 156)",
"color-text-4": "rgb(201, 205, 212)",
"color-text-5": "rgb(229, 230, 235)",
"color-text-6": "rgb(242, 243, 245)",
"color-text-7": "rgb(247, 248, 250)",
"color-bg-1": "rgb(255, 255, 255)",
"color-bg-2": "rgb(255, 255, 255)",
"color-bg-3": "rgb(255, 255, 255)",
"color-bg-4": "rgb(255, 255, 255)",
"color-bg-5": "rgb(255, 255, 255)",
"color-bg-6": "rgb(255, 255, 255)",
"color-bg-7": "rgb(255, 255, 255)",
"color-bg-white": "rgb(255, 255, 255)",
"color-bg-black": "rgb(0, 0, 0)",
"color-shadow": "rgba(255, 255, 255, 0.5)",
"border-1": "rgb(242, 243, 245)",
"border-2": "rgb(229, 230, 235)",
"border-3": "rgb(201, 205, 212)",
"border-4": "rgb(134, 144, 156)",
"border-5": "rgb(78, 89, 105)",
"border-6": "rgb(29, 33, 41)",
"border-7": "rgb(14, 16, 20)",
"fill-1": "rgb(247, 248, 250)",
"fill-2": "rgb(242, 243, 245)",
"fill-3": "rgb(229, 230, 235)",
"fill-4": "rgb(201, 205, 212)",
"fill-5": "rgb(134, 144, 156)",
"fill-6": "rgb(78, 89, 105)",
"fill-7": "rgb(29, 33, 41)",
"text-1": "rgb(29, 33, 41)",
"text-2": "rgb(78, 89, 105)",
"text-3": "rgb(134, 144, 156)",
"text-4": "rgb(201, 205, 212)",
"text-5": "rgb(229, 230, 235)",
"text-6": "rgb(242, 243, 245)",
"text-7": "rgb(247, 248, 250)",
"background-1": "rgb(255, 255, 255)",
"background-2": "rgb(255, 255, 255)",
"background-3": "rgb(255, 255, 255)",
"background-4": "rgb(255, 255, 255)",
"background-5": "rgb(255, 255, 255)",
"background-6": "rgb(255, 255, 255)",
"background-7": "rgb(255, 255, 255)",
"background-white": "rgb(255, 255, 255)",
"background-black": "rgb(0, 0, 0)",
"shadow": "rgba(255, 255, 255, 0.5)",
},
};

View File

@ -3,17 +3,15 @@ import ReactDOM from "react-dom/client";
import "./index.scss";
import { RouterProvider } from "react-router-dom";
import { router } from "./router";
import { setTailwindExtendProps } from "./utils/theme";
import { createDarkTheme, createLightTheme, generateTailwindProps, getColorAsync, setTailwindExtendProps } from "./utils/theme";
// 当tailwindcss加载完毕开始修改config
document.addEventListener("DOMContentLoaded", () => {
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(

View File

@ -96,7 +96,7 @@ const ColorPalette: React.FC<ColorPaletteProps> = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<div className="font-semibold mb-2">Light Colors</div>
<div className="grid grid-cols-2 gap-2">
<div className="grid grid-cols-3 gap-1">
{Object.keys(colors.onLight).map((key) => (
<div key={key} className="flex items-center space-x-2">
<span>{`${key}:`}</span>
@ -113,7 +113,7 @@ const ColorPalette: React.FC<ColorPaletteProps> = () => {
</div>
<div>
<div className="font-semibold mb-2">Dark Colors</div>
<div className="grid grid-cols-2 gap-2">
<div className="grid grid-cols-3 gap-1">
{Object.keys(colors.onDark!).map((key) => (
<div key={key} className="flex items-center space-x-2">
<span>{`${key}:`}</span>

View File

@ -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<string, string>
obj: Record<string, string>,
config?: ReverseHandlerConfig
): Record<string, string> {
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<string, string> = {};
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<string, string>) => {
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<string, string>
@ -313,3 +460,29 @@ export const overrideColor = (
}
}
};
/**
* Tailwind CSS的扩展属性
* @param colors
* @returns Tailwind CSS的扩展属性
*/
export const generateTailwindProps = (colors: ThemeVariables) => {
const colorsExtend: Record<string, any> = {};
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;
};

View File

@ -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"
// },
// },
},
};

View File

@ -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<string, string>,
onDark?: Record<string, string>,
}
onLight: Record<string, string>;
onDark?: Record<string, string>;
};
export type ThemeSection = "dark" | "light" | "system";
export type ThemeSection = "dark" | "light" | "system";
export type ReverseHandlerType = "reverse" | "none" | "lighten" | "darken";
export type ReverseHandlerConfig = Record<string, ReverseHandlerType>;