mirror of https://github.com/fantasticit/think.git
feat: adapter dark style
parent
d26c9a742e
commit
53e0732996
|
@ -23,3 +23,44 @@ const colors = [
|
|||
|
||||
const total = colors.length;
|
||||
export const getRandomColor = () => colors[~~(Math.random() * total)];
|
||||
|
||||
/**
|
||||
* 将颜色转换为带 alpha 通道的 rgba 值
|
||||
* @param hexCode
|
||||
* @param opacity
|
||||
* @returns
|
||||
*/
|
||||
export const convertColorToRGBA = (hexCode: string, opacity = 1) => {
|
||||
let r = 0;
|
||||
let g = 0;
|
||||
let b = 0;
|
||||
|
||||
if (hexCode.startsWith('rgb')) {
|
||||
const rgb = hexCode
|
||||
.replace(/\s/g, '')
|
||||
.match(/rgb\((.*)\)$/)[1]
|
||||
.split(',');
|
||||
|
||||
r = +rgb[0];
|
||||
g = +rgb[1];
|
||||
b = +rgb[2];
|
||||
} else if (hexCode.startsWith('#')) {
|
||||
let hex = hexCode.replace('#', '');
|
||||
|
||||
if (hex.length === 3) {
|
||||
hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`;
|
||||
}
|
||||
|
||||
r = parseInt(hex.substring(0, 2), 16);
|
||||
g = parseInt(hex.substring(2, 4), 16);
|
||||
b = parseInt(hex.substring(4, 6), 16);
|
||||
} else {
|
||||
return hexCode;
|
||||
}
|
||||
|
||||
if (opacity > 1 && opacity <= 100) {
|
||||
opacity = opacity / 100;
|
||||
}
|
||||
|
||||
return `rgba(${r},${g},${b},${opacity})`;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export enum Theme {
|
||||
'dark' = 'dark',
|
||||
|
@ -8,10 +8,10 @@ export enum Theme {
|
|||
export const useTheme = () => {
|
||||
const [theme, setTheme] = useState(Theme.light);
|
||||
|
||||
const toggle = () => {
|
||||
const toggle = useCallback(() => {
|
||||
const nextTheme = theme === 'dark' ? Theme.light : Theme.dark;
|
||||
setTheme(nextTheme);
|
||||
};
|
||||
}, [theme]);
|
||||
|
||||
useEffect(() => {
|
||||
const body = document.body;
|
||||
|
@ -40,6 +40,19 @@ export const useTheme = () => {
|
|||
mql.addEventListener('change', matchMode);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const config = { attributes: true };
|
||||
const callback = function () {
|
||||
setTheme(document.body.getAttribute('theme-mode') as Theme);
|
||||
};
|
||||
const observer = new MutationObserver(callback);
|
||||
observer.observe(document.body, config);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return {
|
||||
theme,
|
||||
toggle,
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { NodeViewWrapper, NodeViewContent } from '@tiptap/react';
|
||||
import cls from 'classnames';
|
||||
import { EmojiPicker } from 'components/emoji-picker';
|
||||
import { convertColorToRGBA } from 'helpers/color';
|
||||
import { Theme, useTheme } from 'hooks/use-theme';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const CalloutWrapper = ({ editor, node, updateAttributes }) => {
|
||||
const { isEditable } = editor;
|
||||
const { emoji, textColor, borderColor, backgroundColor } = node.attrs;
|
||||
const { theme } = useTheme();
|
||||
const backgroundColorOpacity = useMemo(() => {
|
||||
if (!backgroundColor) return backgroundColor;
|
||||
if (theme === Theme.dark) return convertColorToRGBA(backgroundColor, 0.85);
|
||||
return backgroundColor;
|
||||
}, [backgroundColor, theme]);
|
||||
|
||||
const onSelectEmoji = useCallback(
|
||||
(emoji) => {
|
||||
|
@ -21,7 +29,7 @@ export const CalloutWrapper = ({ editor, node, updateAttributes }) => {
|
|||
className={cls(styles.innerWrap, 'render-wrapper')}
|
||||
style={{
|
||||
borderColor,
|
||||
backgroundColor,
|
||||
backgroundColor: backgroundColorOpacity,
|
||||
}}
|
||||
>
|
||||
{isEditable ? (
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
.renderWrap {
|
||||
display: flex;
|
||||
background-color: var(--semi-color-bg-1);
|
||||
border: 1px solid var(--node-border-color);
|
||||
border-radius: var(--border-radius);
|
||||
align-items: center;
|
||||
|
@ -15,13 +14,17 @@
|
|||
&::after {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
svg {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbarWrap {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
z-index: 1000;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
padding: 2px 4px;
|
||||
overflow-x: auto;
|
||||
|
|
|
@ -2,8 +2,10 @@ import { NodeViewWrapper } from '@tiptap/react';
|
|||
import cls from 'classnames';
|
||||
import { Button, Space } from '@douyinfe/semi-ui';
|
||||
import { IconMindCenter, IconZoomOut, IconZoomIn } from 'components/icons';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Resizeable } from 'components/resizeable';
|
||||
import { convertColorToRGBA } from 'helpers/color';
|
||||
import { Theme, useTheme } from 'hooks/use-theme';
|
||||
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||
import { Flow } from 'tiptap/core/extensions/flow';
|
||||
import styles from './index.module.scss';
|
||||
|
@ -16,8 +18,15 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const isActive = editor.isActive(Flow.name);
|
||||
const { width: maxWidth } = getEditorContainerDOMSize(editor);
|
||||
const { data, width, height } = node.attrs;
|
||||
const { theme } = useTheme();
|
||||
const $viewer = useRef(null);
|
||||
const $container = useRef<HTMLElement>();
|
||||
const [bgColor, setBgColor] = useState('var(--semi-color-fill-0)');
|
||||
const bgColorOpacity = useMemo(() => {
|
||||
if (!bgColor) return bgColor;
|
||||
if (theme === Theme.dark) return convertColorToRGBA(bgColor, 0.85);
|
||||
return bgColor;
|
||||
}, [bgColor, theme]);
|
||||
|
||||
const graphData = useMemo(() => {
|
||||
if (!data) return null;
|
||||
|
@ -71,6 +80,8 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
|||
div.innerHTML = '';
|
||||
DrawioViewer.createViewerForElement(div, (viewer) => {
|
||||
$viewer.current = viewer;
|
||||
const background = viewer?.graph?.background;
|
||||
background && setBgColor(background);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
@ -90,9 +101,17 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
|
|||
return (
|
||||
<NodeViewWrapper className={cls(styles.wrap, isActive && styles.isActive)}>
|
||||
<Resizeable isEditable={isEditable} width={width} height={height} maxWidth={maxWidth} onChangeEnd={onResize}>
|
||||
<div className={cls(styles.renderWrap, 'render-wrapper')} style={INHERIT_SIZE_STYLE}>
|
||||
<div
|
||||
className={cls(styles.renderWrap, 'render-wrapper')}
|
||||
style={{ ...INHERIT_SIZE_STYLE, backgroundColor: bgColorOpacity }}
|
||||
>
|
||||
{graphData && (
|
||||
<div className="mxgraph" style={{ width, height }} ref={setMxgraph} data-mxgraph={graphData}></div>
|
||||
<div
|
||||
className="mxgraph"
|
||||
style={{ width: maxWidth, height }}
|
||||
ref={setMxgraph}
|
||||
data-mxgraph={graphData}
|
||||
></div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in New Issue