UNPKG

use-theme-editor

Version:

Zero configuration CSS variables based theme editor

181 lines (164 loc) 6.13 kB
import React, {useContext, useMemo, useState} from 'react'; import {ThemeEditorContext} from '../ThemeEditor'; import {VariableControl} from '../inspector/VariableControl'; import {ACTIONS} from '../../hooks/useThemeEditor'; import {Checkbox} from '../controls/Checkbox'; import {ElementLocator} from './ElementLocator'; import {ToggleButton} from '../controls/ToggleButton'; import {allStateSelectorsRegexp} from '../../functions/getMatchingVars'; import {useLocalStorage} from '../../hooks/useLocalStorage'; import {varMatchesTerm} from '../../functions/filterSearched'; import {isColorProperty} from '../inspector/TypedControl'; import { get } from '../../state'; export function CurrentTheme() { const { propertyFilter, propertySearch } = get; const { theme, dispatch, allVars, defaultValues, } = useContext(ThemeEditorContext); const [initialized, setInitialized] = useState(false); const [showObsolete, setShowObsolete] = useState(false); const [showActive, setShowActive] = useState(true); const [useDefaultValues, setUseDefaultValues] = useState(false); const [hideNotFound, setHideNotFound] = useState(false); const [isOpen, setIsOpen] = useLocalStorage('current-theme-open', true); const UNFOUND = 'UNFOUND'; const groupedBySelector = useMemo(() => { if (!isOpen) { return {}; } const base = !useDefaultValues ? theme : {...defaultValues, ...theme}; return Object.keys(base).sort().reduce((grouped, k) => { const cssVar = allVars.find(allVar => allVar.name === k); const term = propertySearch.replace(/^\!/, ''); const isInverse = term.length !== propertySearch.length if (!cssVar) { if (term && k.replace(/-+/g, ' ').match(term) || isInverse) { return grouped; } if (!grouped[UNFOUND]) { grouped[UNFOUND] = []; } grouped[UNFOUND].push({name: k}); return grouped; } if (!varMatchesTerm(cssVar, term) || isInverse) { return grouped; } if (propertyFilter !== 'all' && !cssVar.usages.some(usage => isColorProperty(usage.property))) { return grouped; } const selector = [...new Set(cssVar.usages.map(usage => usage.selector))] .join() .replace(allStateSelectorsRegexp, '') .replace(/:?:(before|after)/g, '') .split(',') .map(s=>s.trim()) .filter((value,index,self) => self.indexOf(value) === index) .join(); if (!grouped[selector]) { grouped[selector] = []; } grouped[selector].push(cssVar); return grouped; }, {}); }, [theme, isOpen, useDefaultValues, propertyFilter, propertySearch]); return ( <div> <h4> Current theme ({Object.keys(theme).length} out of{' '} {Object.keys(defaultValues).length}) <ToggleButton style={{ float: 'right' }} controls={[isOpen, setIsOpen]}> {isOpen ? 'Close' : 'Open'} </ToggleButton> </h4> {isOpen && ( <div> <Checkbox controls={[showActive, setShowActive]}> Show active </Checkbox> <Checkbox controls={[showObsolete, setShowObsolete]}> Show unknown </Checkbox> <Checkbox controls={[useDefaultValues, setUseDefaultValues]}> Include default values </Checkbox> <Checkbox controls={[hideNotFound, setHideNotFound]}> Hide not found </Checkbox> <ToggleButton controls={[initialized, setInitialized]}> INIT {initialized ? '' : '*'} </ToggleButton> </div> )} {isOpen && ( <ul style={{ background: 'white', listStyleType: 'none', paddingLeft: 0, }} > {Object.entries(groupedBySelector).map(([selector, cssVars]) => ( <li style={{ marginTop: '12px' }} key={selector}> <ElementLocator hideIfNotFound={hideNotFound} {...{ initialized, selector }} > <ul> {cssVars.map((cssVar) => { const k = cssVar.name; if (selector === UNFOUND && showObsolete) { return ( <li key={k}> <p> <em>{k}</em> was not found. <button onClick={() => { dispatch({ type: ACTIONS.unset, payload: { name: k }, }); }} > unset </button> </p> </li> ); } if (!showActive || selector === UNFOUND) { return null; } return ( <VariableControl key={k} onChange={(value) => { dispatch({ type: ACTIONS.set, payload: { name: cssVar.name, value }, }); }} onUnset={() => { dispatch({ type: ACTIONS.unset, payload: { name: cssVar.name }, }); }} {...{ cssVar, }} /> ); })} </ul> </ElementLocator> </li> ))} </ul> )} </div> ); }