use-theme-editor
Version:
Zero configuration CSS variables based theme editor
112 lines (101 loc) • 4.34 kB
JavaScript
import React, { useContext, useMemo, useState } from "react";
import { scopesByProperty } from "../../functions/collectRuleVars";
import { rootScopes } from "../../functions/extractPageVariables";
import { useLocalStorage } from "../../hooks/useLocalStorage";
import { ACTIONS } from "../../hooks/useThemeEditor";
import { Checkbox } from "../controls/Checkbox";
import { ThemeEditorContext } from "../ThemeEditor";
import { ElementLocator } from "../ui/ElementLocator";
import { VariableControl } from "./VariableControl";
export function ScopeControl(props) {
const { dispatch } = useContext(ThemeEditorContext);
// Remove locator for now as it makes the UI jump too much.
const [showLocator, setShowLocator] = useLocalStorage('show-scope-locators', false);
// const showLocator = true;
const { scopes, vars, element } = props;
const nonRootScopes = useMemo(() => {
return scopes.filter(s => !rootScopes.includes(s.selector));
}, [scopes])
if (nonRootScopes.length === 0) {
return null;
}
const usedVars = [];
return <div style={{ background: 'lightyellow', marginBottom: '24px', padding: '4px', border: '1px solid black' }}>
<h5>Variations</h5>
<Checkbox controls={[showLocator, setShowLocator]} style={{float: 'right'}}>Find on page</Checkbox>
<ul>
{nonRootScopes.map(({ selector, matchingSelector, scopeVars }) => {
const selectors = selector.split();
// Scopes are already sorted by most specific first.
// If a scope is completely overridden, don't show it.
if (!scopeVars.some((v) => !usedVars.includes(v))) {
return null;
}
usedVars.push(...scopeVars);
const elementScopeVars = vars.filter((v) => v.currentScope === selector);
return (
<li
key={selector}
title={elementScopeVars
.map(
({ name }) =>
`${name}: ${(scopesByProperty[name] || {})[selector]}`
)
.join('\n')}
>
<span
style={{
border: '1px solid gray',
}}
className="monospace-code"
>
{selectors.map((selector) => {
if (selector === matchingSelector) {
return <b key="selector">{selector}</b>;
}
return selector;
})}
</span>
{showLocator && <ElementLocator
{...{ selector }}
initialized
// hideIfOne
showLabel={false}
/>}
<ul style={{marginBottom: '24px'}}>
{elementScopeVars
.map((cssVar) => (
<VariableControl
{...{
cssVar,
scopes,
element,
currentScope: selector,
}}
initialOpen={false}
key={cssVar.name}
onChange={(value) => {
dispatch({
type: ACTIONS.set,
payload: {
name: cssVar.name,
value,
scope: selector,
},
});
}}
onUnset={() => {
dispatch({
type: ACTIONS.unset,
payload: { name: cssVar.name, scope: selector },
});
}}
/>
))}
</ul>
</li>
);
})}
</ul>
</div>
}