documentation-site/components/yard/theme-editor.tsx (158 lines of code) (raw):
/*
Copyright (c) Uber Technologies, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
import * as React from "react";
import { useValueDebounce } from "react-view";
import { Input, SIZE } from "baseui/input";
import { useStyletron } from "baseui";
import Link from "next/link";
import { StyledLink } from "baseui/link";
import { ParagraphXSmall } from "baseui/typography";
import { getActiveTheme, getThemeDiff } from "./provider";
type ThemeEditorProps = {
componentName: string;
theme: { [key: string]: string };
themeInit: { [key: string]: string };
set: (value: { [key: string]: string } | undefined) => void;
};
type ColumnProps = {
themeKeys: string[];
theme: { [key: string]: string };
themeInit: { [key: string]: string };
set: (value: { [key: string]: string } | undefined) => void;
};
const ColorInput: React.FC<{
themeKey: string;
themeInit: { [key: string]: string };
globalColor: string;
globalSet: (color: string) => void;
}> = ({ themeKey, themeInit, globalSet, globalColor }) => {
const [css, $theme] = useStyletron();
const [color, setColor] = useValueDebounce<string>(globalColor, globalSet);
return (
<label
className={css({
display: "flex",
alignItems: "center",
})}
>
<div
className={css({
width: "4px",
height: "36px",
backgroundColor: color,
})}
></div>
<Input
positive={color !== themeInit[themeKey]}
size={SIZE.compact}
placeholder={themeInit[themeKey]}
value={color}
onChange={(e) => setColor((e.target as HTMLInputElement).value)}
overrides={{ Root: { style: { width: "100px" } } }}
/>
<div
title={themeKey}
className={css({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...($theme.typography.font100 as any),
color: $theme.colors.contentPrimary,
marginLeft: $theme.sizing.scale300,
maxWidth: "150px",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
})}
>
{themeKey}
</div>
</label>
);
};
const Column: React.FC<ColumnProps> = ({
themeKeys,
themeInit,
theme,
set,
}) => {
const [css] = useStyletron();
return (
<div
className={css({
flexBasis: "50%",
})}
>
{themeKeys.map((key) => {
return (
<ColorInput
key={key}
themeKey={key}
globalColor={theme[key]}
globalSet={(color) => {
const diff = getThemeDiff(
{
...theme,
[key]: color,
},
themeInit,
);
set(Object.keys(diff).length > 0 ? diff : undefined);
}}
themeInit={themeInit}
/>
);
})}
</div>
);
};
const ThemeEditor: React.FC<ThemeEditorProps> = ({
theme,
themeInit,
set,
componentName,
}) => {
const [css, currentTheme] = useStyletron();
const activeTheme = getActiveTheme(theme, themeInit);
const themeKeys = Object.keys(activeTheme);
const midPoint =
themeKeys.length % 2 === 0
? themeKeys.length / 2
: themeKeys.length / 2 + 1;
const firstThemeKeys = themeKeys.slice(0, midPoint);
const secondThemeKeys = themeKeys.slice(midPoint);
return (
<React.Fragment>
<ParagraphXSmall
marginLeft="scale200"
marginRight="scale200"
marginBottom="scale400"
>
Do you want to change {componentName} colors globally? You can customize
the theme through ThemeProvider and set your own colors.{" "}
<Link href="/guides/theming/#a-custom-theme" legacyBehavior>
<StyledLink href="/guides/theming/#a-custom-theme">
Learn more
</StyledLink>
</Link>
. Try different values:
</ParagraphXSmall>
<div
className={css({
display: "flex",
flexDirection: "row",
[`@media screen and (max-width: ${currentTheme.breakpoints.medium}px)`]:
{
flexWrap: "wrap",
},
})}
>
<Column
themeKeys={firstThemeKeys}
theme={activeTheme}
themeInit={themeInit}
set={set}
/>
<Column
themeKeys={secondThemeKeys}
theme={activeTheme}
themeInit={themeInit}
set={set}
/>
</div>
</React.Fragment>
);
};
export default ThemeEditor;