Theme Builder: accordion panels across the full config surface, vertical-tab nav, reflective gallery

This commit is contained in:
2026-06-21 17:14:58 +03:00
parent 318a0919c6
commit d76a7a5014
24 changed files with 2420 additions and 845 deletions
+104
View File
@@ -0,0 +1,104 @@
/**
* Theme Builder — Headers panel (docs/chart-theming-scope.md §5).
*
* The base facet-header config (`config.header.*`) — the titles and labels that
* caption each panel of a faceted chart (the `row`/`column`/`facet` channels).
* The per-position variants (`headerRow`, `headerColumn`, `headerFacet`) stay in
* the JSON; this is the common surface a brand tunes. Each control writes a
* minimal diff through the shared setter.
*/
import type { JsonObject } from '@core/spec-config';
import {
asNumber,
asString,
type ConfigPath,
countSet,
getConfigValue,
} from '@core/theme-controls';
import { useConfigSetter } from '../stores/CustomThemeStore';
import { Accordion, type AccordionSection, ColorRow, NumberRow, WeightRow } from './ThemeFields';
const TEXT_GREY = '#888888';
const TITLE_COLOR: ConfigPath = ['header', 'titleColor'];
const TITLE_SIZE: ConfigPath = ['header', 'titleFontSize'];
const TITLE_WEIGHT: ConfigPath = ['header', 'titleFontWeight'];
const LABEL_COLOR: ConfigPath = ['header', 'labelColor'];
const LABEL_SIZE: ConfigPath = ['header', 'labelFontSize'];
const LABEL_WEIGHT: ConfigPath = ['header', 'labelFontWeight'];
export function HeaderControls({ config }: { config: JsonObject }) {
const set = useConfigSetter();
const sections: AccordionSection[] = [
{
id: 'titles',
title: 'Facet titles',
hint: "The caption naming each facet's field.",
badge: countSet(config, [TITLE_COLOR, TITLE_SIZE, TITLE_WEIGHT]),
children: (
<>
<ColorRow
label="Color"
name="Header title color"
value={asString(getConfigValue(config, TITLE_COLOR))}
fallback={TEXT_GREY}
onChange={(hex) => set(TITLE_COLOR, hex)}
onClear={() => set(TITLE_COLOR, undefined)}
/>
<NumberRow
label="Size"
name="Header title size"
value={asNumber(getConfigValue(config, TITLE_SIZE))}
min={0}
unit="px"
onChange={(n) => set(TITLE_SIZE, n)}
/>
<WeightRow
id="header-title-weight"
label="Weight"
name="Header title weight"
value={getConfigValue(config, TITLE_WEIGHT)}
onChange={(w) => set(TITLE_WEIGHT, w)}
/>
</>
),
},
{
id: 'labels',
title: 'Facet labels',
hint: 'The per-panel value labels.',
badge: countSet(config, [LABEL_COLOR, LABEL_SIZE, LABEL_WEIGHT]),
children: (
<>
<ColorRow
label="Color"
name="Header label color"
value={asString(getConfigValue(config, LABEL_COLOR))}
fallback={TEXT_GREY}
onChange={(hex) => set(LABEL_COLOR, hex)}
onClear={() => set(LABEL_COLOR, undefined)}
/>
<NumberRow
label="Size"
name="Header label size"
value={asNumber(getConfigValue(config, LABEL_SIZE))}
min={0}
unit="px"
onChange={(n) => set(LABEL_SIZE, n)}
/>
<WeightRow
id="header-label-weight"
label="Weight"
name="Header label weight"
value={getConfigValue(config, LABEL_WEIGHT)}
onChange={(w) => set(LABEL_WEIGHT, w)}
/>
</>
),
},
];
return <Accordion sections={sections} idPrefix="headers" />;
}