feat: adapter dark style

pull/44/head
fantasticit 2022-05-12 11:10:56 +08:00
parent d26c9a742e
commit 53e0732996
5 changed files with 94 additions and 10 deletions

View File

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

View File

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

View File

@ -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 ? (

View File

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

View File

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