refactor: widget theme color

This commit is contained in:
Tristan Yang
2023-02-08 22:36:45 +08:00
parent c6f9cc2cef
commit 4435d83580
9 changed files with 78 additions and 16 deletions
+34
View File
@@ -342,4 +342,38 @@ export const compareVersion = (v1: string, v2: string, options?: { lexicographic
}
return 0;
};
/*!
* Get the contrasting color for any hex color
* (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
* Derived from work by Brian Suda, https://24ways.org/2010/calculating-color-contrast/
* @param {String} A hexcolor value
* @return {String} The contrasting color (black or white)
*/
export const getContrastColor = (hexcolor: string) => {
// If a leading # is provided, remove it
if (hexcolor.slice(0, 1) === '#') {
hexcolor = hexcolor.slice(1);
}
// If a three-character hexcode, make six-character
if (hexcolor.length === 3) {
hexcolor = hexcolor.split('').map(function (hex) {
return hex + hex;
}).join('');
}
// Convert to RGB value
let r = parseInt(hexcolor.substr(0, 2), 16);
let g = parseInt(hexcolor.substr(2, 2), 16);
let b = parseInt(hexcolor.substr(4, 2), 16);
// Get YIQ ratio
let yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
// Check contrast
return (yiq >= 128) ? 'black' : 'white';
};
+6 -5
View File
@@ -1,25 +1,26 @@
import { FC } from 'react';
import { useAppSelector } from '../../app/store';
import { useTheme } from '../ThemeContext';
import IconClose from './close.svg';
type Props = {
handleClose: () => void;
themeColor?: string
};
const Index: FC<Props> = ({ handleClose, themeColor = "#1fe1f9" }) => {
const Index: FC<Props> = ({ handleClose }) => {
const { color, fgColor } = useTheme();
const { name, logo } = useAppSelector(store => store.server);
return (
<header className="relative flex justify-between items-center h-14 px-4" style={{ backgroundColor: themeColor }}>
<header className="relative flex justify-between items-center h-14 px-4" style={{ backgroundColor: color }}>
<div className="relative w-8 h-8">
<img src={logo} alt="logo" className="w-full h-full rounded-full" />
</div>
<div className="flex-1 px-4 pr-2 text-lg">
<span className="text-lg font-bold truncate text-gray-50">{name}</span>
<span className="text-lg font-bold truncate text-gray-50" style={{ color: fgColor }}>{name}</span>
</div>
<button type='button' className='w-6 h-6'>
<IconClose onClick={handleClose} />
<IconClose onClick={handleClose} className={fgColor == "black" ? `fill-black` : "fill-white"} />
</button>
</header>
);
+3 -1
View File
@@ -1,5 +1,6 @@
// import React from 'react';
import clsx from 'clsx';
import { useTheme } from '../../ThemeContext';
type Props = {
uid: number,
@@ -9,13 +10,14 @@ type Props = {
}
const Text = ({ content, host, sending }: Props) => {
const { color, fgColor } = useTheme();
return host ?
<div className="text-md text-gray-900 bg-gray-100 rounded-lg px-3 py-1.5 break-words" style={{ maxWidth: 'min(((100vw - 56px) - 20px) - 64px, 360px)' }}>
{content}
</div>
:
<div className={clsx("text-md text-white bg-[#1fe1f9] rounded-lg px-3 py-1.5 transition-all break-words", sending ? 'opacity-70' : "")} style={{ maxWidth: 'min(((100vw - 56px) - 20px) - 64px, 360px)' }}>
<div className={clsx("text-md text-white bg-[#1fe1f9] rounded-lg px-3 py-1.5 transition-all break-words", sending ? 'opacity-70' : "")} style={{ maxWidth: 'min(((100vw - 56px) - 20px) - 64px, 360px)', backgroundColor: color, color: fgColor }}>
{content}
</div>
;
+4 -1
View File
@@ -17,7 +17,10 @@ const Index = ({ needLogin = false }: Props) => {
<div className="pl-14">
<div className="rounded-lg" style={{ maxWidth: 'min(((100vw - 56px) - 20px) - 64px, 360px)' }}>
<div className="text-md text-gray-900 px-3 py-1.5 bg-gray-100 rounded-lg mb-1.5">
👋 Hi there, Nice to meet you! Please login before we have a nice talk 👇
{needLogin ?
`👋 Hi there, Nice to meet you! Please login before we have a nice talk 👇`
:
`👋 Hi there, Nice to meet you! `}
</div>
</div>
</div>
+2 -3
View File
@@ -9,11 +9,10 @@ import useSSE from '../useSSE';
type Props = {
hostId: number,
themeColor?: string,
handleClose: () => void
}
const Index = ({ handleClose, hostId, themeColor }: Props) => {
const Index = ({ handleClose, hostId }: Props) => {
// 建立SSE连接
useSSE();
const { user: loginUser, token, guest: isGuest } = useAppSelector(store => store.authData);
@@ -22,7 +21,7 @@ const Index = ({ handleClose, hostId, themeColor }: Props) => {
const notLogin = !token || isGuest;
return (
<aside className="flex flex-col bg-white w-full h-full rounded-md overflow-hidden">
<Header themeColor={themeColor} handleClose={handleClose} />
<Header handleClose={handleClose} />
{/* message list */}
<main id='MESSAGE_LIST_CONTAINER' className="relative flex-1 overflow-y-auto scroll-smooth">
{/* placeholder */}
+22
View File
@@ -0,0 +1,22 @@
import { createContext, useContext, ReactNode } from 'react';
import { getContrastColor } from '../common/utils';
const color = decodeURIComponent(new URLSearchParams(location.search).get("themeColor") || "#1fe1f9");
const fgColor = getContrastColor(color);
const ThemeContext = createContext({ color, fgColor });
function ThemeProvider({ children }: { children: ReactNode }) {
return <ThemeContext.Provider value={{ color, fgColor }} >{children}</ThemeContext.Provider>;
}
function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
export { ThemeProvider, useTheme };
+3 -4
View File
@@ -1,15 +1,14 @@
import { useState } from "react";
import { useGetServerQuery } from "../app/services/server";
import { ThemeProvider } from "./ThemeContext";
import Icon from "./Icon";
import Popup from "./Popup";
import useCache from "./useCache";
type Props = {
hostId: number,
themeColor?: string
};
function Widget({ hostId, themeColor }: Props) {
function Widget({ hostId }: Props) {
const { rehydrated } = useCache();
const [visible, setVisible] = useState(!!new URLSearchParams(location.search).get("open"));
const { isLoading, isError } = useGetServerQuery();
@@ -22,7 +21,7 @@ function Widget({ hostId, themeColor }: Props) {
setVisible((prev) => !prev);
};
if (isLoading || isError || !rehydrated) return null;
return visible ? <Popup handleClose={toggleVisible} hostId={hostId} themeColor={themeColor} /> : <Icon handleClick={toggleVisible} />;
return <ThemeProvider> {visible ? <Popup handleClose={toggleVisible} hostId={hostId} /> : <Icon handleClick={toggleVisible} />}</ThemeProvider>;
}
export default Widget;