Skip to content

Commit

Permalink
css hilight and dark theme support
Browse files Browse the repository at this point in the history
  • Loading branch information
YuanboXue-Amber committed Apr 26, 2022
1 parent 3466692 commit f503004
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 15 deletions.
3 changes: 2 additions & 1 deletion packages/devtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
"dependencies": {
"@griffel/react": "^1.0.3",
"js-beautify": "^1.14.0",
"react": ">=16.8.0 <18.0.0",
"prism-react-renderer": "1.2.1",
"react-dom": ">=16.8.0 <18.0.0",
"react": ">=16.8.0 <18.0.0",
"stylis": "^4.0.13"
}
}
104 changes: 104 additions & 0 deletions packages/devtools/src/HighlightedCSS.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import Highlight, { defaultProps, PrismTheme } from 'prism-react-renderer';
import { default as themeDark } from 'prism-react-renderer/themes/vsDark';
import { default as themeLight } from 'prism-react-renderer/themes/vsLight';
import * as React from 'react';

import { makeStyles, shorthands } from '@griffel/react';

import { DARK_THEME_COLOR_TOKENS, LIGHT_THEME_COLOR_TOKENS } from './colorTokens';
import { useThemeContext } from './ThemeContext';
import type { BrowserTheme } from './types';

function getCustomTheme(theme?: BrowserTheme): PrismTheme {
const colortokens = theme === 'dark' ? DARK_THEME_COLOR_TOKENS : LIGHT_THEME_COLOR_TOKENS;
return {
plain: {
color: colortokens.foreground,
},
styles: [
...(theme === 'dark' ? themeDark.styles : themeLight.styles),
{
types: ['keyword', 'atrule'],
style: {
color: colortokens.foreground,
},
},
{
types: ['rule'],
style: {
color: colortokens.cssAtRule,
},
},
{
types: ['property'],
style: {
color: colortokens.cssProperty,
},
},
{
types: ['punctuation'],
style: {
color: colortokens.cssPunctuation,
},
},
{
types: ['selector'],
style: {
color: colortokens.cssSelector,
},
},
],
};
}

const useColorIndicatorStyles = makeStyles({
colorIndicator: {
height: '10px',
width: '10px',
display: 'inline-block',
marginLeft: '2px',
marginRight: '2px',
...shorthands.border('1px', 'dotted', 'grey'),
},
});

export const HighlightedCSS: React.FC<{ code: string }> = ({ code }) => {
const theme = useThemeContext();
const customTheme = React.useMemo(() => getCustomTheme(theme), [theme]);

const { colorIndicator } = useColorIndicatorStyles();

return (
<Highlight {...defaultProps} code={code} language="css" theme={customTheme}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style, display: 'inline-block', textDecorationLine: 'inherit' }}>
{tokens.map((line, i) => (
<div {...getLineProps({ line, key: i })}>
{line.map((token, key) => {
const tokenProps = getTokenProps({ token, key });
return tokenProps.className.includes('color') && token.content !== 'transparent' ? (
<span
{...tokenProps}
children={
<>
{/* show a square color indicator for colors in css */}
<span
key={`${i}-color`}
className={colorIndicator}
style={{ backgroundColor: token.content }}
/>
{tokenProps.children}
</>
}
/>
) : (
<span {...tokenProps} />
);
})}
</div>
))}
</pre>
)}
</Highlight>
);
};
7 changes: 4 additions & 3 deletions packages/devtools/src/MonolithicRulesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import beautify from 'js-beautify';
import * as React from 'react';
import { makeStyles, mergeClasses, shorthands } from '@griffel/react';

import { HighlightedCSS } from './HighlightedCSS';
import { useViewContext } from './ViewContext';
import type { MonolithicRules, RuleDetail } from './types';

Expand Down Expand Up @@ -47,7 +48,7 @@ const SingleRuleView: React.FC<{ rule: RuleDetail; indent?: boolean }> = ({ rule

return (
<div onClick={handleClick} className={className}>
<pre>{formatCSS(rule.css)}</pre>
<HighlightedCSS code={formatCSS(rule.css)} />
</div>
);
};
Expand Down Expand Up @@ -79,15 +80,15 @@ export const MonolithicRulesView: React.FC<MonolithicRulesViewProps> = props =>
{Object.entries(rules).map(([selector, rules]) => {
return (
<div key={selector} className={noLineSpacing ? classes.noLineSpacing : classes.lineSpacing}>
{selector && <pre>{`${selector} {`} </pre>}
{selector && <HighlightedCSS code={`${selector} {`} />}
{Array.isArray(rules) ? (
rules.map(rule => <SingleRuleView key={rule.css} rule={rule} indent={!!selector} />)
) : (
<div className={classes.atRulesIndent}>
<MonolithicRulesView rules={rules} noLineSpacing />
</div>
)}
{selector && <pre>{`}`} </pre>}
{selector && <HighlightedCSS code={`}`} />}
</div>
);
})}
Expand Down
18 changes: 14 additions & 4 deletions packages/devtools/src/SlotCSSRules.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import * as React from 'react';
import { makeStyles, shorthands } from '@griffel/react';
import { makeStyles, mergeClasses, shorthands } from '@griffel/react';

import { DARK_THEME_COLOR_TOKENS, LIGHT_THEME_COLOR_TOKENS } from './colorTokens';
import { getMonolithicCSSRules } from './getMonolithicCSSRules';
import { MonolithicRulesView } from './MonolithicRulesView';
import { useThemeContext } from './ThemeContext';
import { useViewContext } from './ViewContext';

import type { AtomicRules } from './types';
Expand All @@ -11,8 +13,14 @@ const useStyles = makeStyles({
slotName: {
...shorthands.padding('2px', '5px'),
...shorthands.margin('5px', 0),
...shorthands.borderTop('1px', 'solid', 'grey'),
...shorthands.borderBottom('1px', 'solid', 'grey'),
...shorthands.borderTop('1px', 'solid', LIGHT_THEME_COLOR_TOKENS.slotNameBorder),
...shorthands.borderBottom('1px', 'solid', LIGHT_THEME_COLOR_TOKENS.slotNameBorder),
backgroundColor: LIGHT_THEME_COLOR_TOKENS.slotNameBackground,
},
slotNameDark: {
backgroundColor: DARK_THEME_COLOR_TOKENS.slotNameBackground,
borderTopColor: DARK_THEME_COLOR_TOKENS.slotNameBorder,
borderBottomColor: DARK_THEME_COLOR_TOKENS.slotNameBorder,
},
rules: {
...shorthands.padding(0, '5px'),
Expand All @@ -22,14 +30,16 @@ const useStyles = makeStyles({
export const SlotCSSRules: React.FC<{ slot: string; atomicRules: AtomicRules[] }> = ({ slot, atomicRules }) => {
const rules = React.useMemo(() => getMonolithicCSSRules(atomicRules), [atomicRules]);

const theme = useThemeContext();
const classes = useStyles();
const slotNameClassName = mergeClasses(classes.slotName, theme === 'dark' && classes.slotNameDark);

const { setHighlightedClass } = useViewContext();
const undoHighlight = () => setHighlightedClass('');

return (
<>
<pre className={classes.slotName}>{slot}</pre>
<pre className={slotNameClassName}>{slot}</pre>
<div className={classes.rules} onClick={undoHighlight}>
<MonolithicRulesView rules={rules} />
</div>
Expand Down
8 changes: 8 additions & 0 deletions packages/devtools/src/ThemeContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as React from 'react';
import type { BrowserTheme } from './types';

export const ThemeContext = React.createContext<BrowserTheme>('light');

export function useThemeContext() {
return React.useContext(ThemeContext);
}
28 changes: 28 additions & 0 deletions packages/devtools/src/colorTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { ColorTokens } from './types';

export const LIGHT_THEME_COLOR_TOKENS: ColorTokens = {
foreground: 'rgb(34, 34, 34)',
background: 'white',

cssAtRule: 'rgb(128, 134, 139)',
cssProperty: 'rgb(19, 44, 121)',
cssPunctuation: 'rgb(102, 102, 102)',
cssSelector: 'rgb(158, 51, 121)',

slotNameBackground: 'rgb(247, 247, 247)',
slotNameBorder: 'rgb(202, 205, 209)',
};

export const DARK_THEME_COLOR_TOKENS: ColorTokens = {
...LIGHT_THEME_COLOR_TOKENS,
background: 'rgb(32, 33, 36)',
foreground: 'rgb(213, 213, 213)',

cssAtRule: 'rgb(128, 134, 139)',
cssProperty: 'rgb(156, 220, 254)',
cssPunctuation: 'rgb(167, 167, 167)',
cssSelector: 'rgb(175, 165, 143)',

slotNameBackground: 'rgb(51, 51, 51)',
slotNameBorder: 'rgb(73, 76, 80)',
};
31 changes: 29 additions & 2 deletions packages/devtools/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';

import { makeStyles } from '@griffel/react';

import { DARK_THEME_COLOR_TOKENS, LIGHT_THEME_COLOR_TOKENS } from './colorTokens';
import { DefaultMessage } from './DefaultMessage';
import { FlattenView } from './FlattenView';
import { ThemeContext } from './ThemeContext';
import { useMakeStylesState } from './useMakeStylesState';

const useStyles = makeStyles({
root: {
color: LIGHT_THEME_COLOR_TOKENS.foreground,
backgroundColor: LIGHT_THEME_COLOR_TOKENS.background,
},
rootDark: {
color: DARK_THEME_COLOR_TOKENS.foreground,
backgroundColor: DARK_THEME_COLOR_TOKENS.background,
},
});

const DevTools: React.FC = () => {
const state = useMakeStylesState();

const classes = useStyles();
const browserTheme = chrome.devtools?.panels?.themeName === 'dark' ? 'dark' : 'light';

React.useLayoutEffect(() => {
if (browserTheme === 'dark') {
document.body.classList.add(...classes.rootDark.split(' '));
} else {
document.body.classList.add(...classes.root.split(' '));
}
}, []);

let children = <DefaultMessage />;
if (state && Object.keys(state).length > 0) {
return <FlattenView debugResultRoot={state} />;
children = <FlattenView debugResultRoot={state} />;
}

return <DefaultMessage />;
return <ThemeContext.Provider value={browserTheme}>{children}</ThemeContext.Provider>;
};

ReactDOM.render(
Expand Down
35 changes: 30 additions & 5 deletions packages/devtools/src/stories/FlattenView.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import * as React from 'react';
import { FlattenView } from '../FlattenView';
import { Story } from '@storybook/react';
import type { DebugResult } from '@griffel/core';

import { DARK_THEME_COLOR_TOKENS, LIGHT_THEME_COLOR_TOKENS } from '../colorTokens';
import { FlattenView } from '../FlattenView';
import { ThemeContext } from '../ThemeContext';

import type { BrowserTheme } from '../types';

const debugResultRoot: DebugResult = {
sequenceHash: '___29aowz0_1rg0tlg',
direction: 'ltr',
Expand Down Expand Up @@ -93,12 +99,31 @@ const debugResultRoot: DebugResult = {
],
};

export const Default = () => (
<div style={{ border: '3px solid gray', width: 400 }}>
<FlattenView debugResultRoot={debugResultRoot} />
</div>
export const Default: Story<{ theme: BrowserTheme }> = ({ theme }) => (
<ThemeContext.Provider value={theme}>
<div
style={{
border: '3px solid gray',
width: 400,
color: theme === 'dark' ? DARK_THEME_COLOR_TOKENS.foreground : LIGHT_THEME_COLOR_TOKENS.foreground,
backgroundColor: theme === 'dark' ? DARK_THEME_COLOR_TOKENS.background : LIGHT_THEME_COLOR_TOKENS.background,
}}
>
<FlattenView debugResultRoot={debugResultRoot} />
</div>
</ThemeContext.Provider>
);

Default.args = {
theme: 'light',
};

export default {
title: 'FlattenView',
argTypes: {
theme: {
options: ['light', 'dark'],
control: { type: 'radio' },
},
},
};
16 changes: 16 additions & 0 deletions packages/devtools/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
export type BrowserTheme = 'dark' | 'light';

export type ColorTokens = {
foreground: string;
background: string;

slotNameBackground: string;
slotNameBorder: string;

/** tokens for css hightlighting */
cssAtRule: string;
cssProperty: string;
cssPunctuation: string;
cssSelector: string;
};

export type SlotInfo = { slot: string; rules: AtomicRules[] };

export type AtomicRules = { cssRule: string; overriddenBy?: string };
Expand Down

0 comments on commit f503004

Please sign in to comment.