mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Theme Builder: Layout/Axes/Legend/Type panels, scheme-render fix, fail-loud gallery
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Theme Builder — Legend panel (docs/chart-theming-scope.md §5).
|
||||
*
|
||||
* Structured controls over the base `legend` config: placement (`orient`), the
|
||||
* title's and labels' color and size, and the symbol size. Legend type (size)
|
||||
* lives here rather than the Type panel so every legend property a brand tunes
|
||||
* sits together — the scope's "label/title color+size" grouping.
|
||||
*/
|
||||
|
||||
import type { JsonObject } from '@core/spec-config';
|
||||
import { asNumber, asString, type ConfigPath, getConfigValue } from '@core/theme-controls';
|
||||
import { useConfigSetter } from '../stores/CustomThemeStore';
|
||||
import { ColorRow, ControlSection, NumberRow, SelectRow } from './ThemeFields';
|
||||
import type { SelectControlOption } from './SelectControl';
|
||||
import styles from './ThemeFields.module.css';
|
||||
|
||||
const ORIENT: ConfigPath = ['legend', 'orient'];
|
||||
const TITLE_COLOR: ConfigPath = ['legend', 'titleColor'];
|
||||
const TITLE_SIZE: ConfigPath = ['legend', 'titleFontSize'];
|
||||
const LABEL_COLOR: ConfigPath = ['legend', 'labelColor'];
|
||||
const LABEL_SIZE: ConfigPath = ['legend', 'labelFontSize'];
|
||||
const SYMBOL_SIZE: ConfigPath = ['legend', 'symbolSize'];
|
||||
|
||||
const TEXT_GREY = '#888888';
|
||||
|
||||
// Vega-Lite legend `orient` values; '' is the unset (theme default) sentinel.
|
||||
type Orient = '' | 'right' | 'left' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'none';
|
||||
const orientOptions: SelectControlOption<Orient>[] = [
|
||||
{ value: '', label: 'Theme default' },
|
||||
{ value: 'right', label: 'Right' },
|
||||
{ value: 'left', label: 'Left' },
|
||||
{ value: 'top', label: 'Top' },
|
||||
{ value: 'bottom', label: 'Bottom' },
|
||||
{ value: 'top-left', label: 'Top-left' },
|
||||
{ value: 'top-right', label: 'Top-right' },
|
||||
{ value: 'none', label: 'Hidden' },
|
||||
];
|
||||
const orientValue = (v: unknown): Orient => {
|
||||
const s = asString(v);
|
||||
return s !== undefined && orientOptions.some((o) => o.value === s) ? (s as Orient) : '';
|
||||
};
|
||||
|
||||
export function LegendControls({ config }: { config: JsonObject }) {
|
||||
const set = useConfigSetter();
|
||||
|
||||
const orient = orientValue(getConfigValue(config, ORIENT));
|
||||
const titleColor = asString(getConfigValue(config, TITLE_COLOR));
|
||||
const titleSize = asNumber(getConfigValue(config, TITLE_SIZE));
|
||||
const labelColor = asString(getConfigValue(config, LABEL_COLOR));
|
||||
const labelSize = asNumber(getConfigValue(config, LABEL_SIZE));
|
||||
const symbolSize = asNumber(getConfigValue(config, SYMBOL_SIZE));
|
||||
|
||||
return (
|
||||
<div className={styles.panel}>
|
||||
<ControlSection title="Placement" hint="Where the legend sits relative to the plot.">
|
||||
<SelectRow
|
||||
id="legend-orient"
|
||||
label="Position"
|
||||
options={orientOptions}
|
||||
value={orient}
|
||||
onSelect={(o) => set(ORIENT, o === '' ? undefined : o)}
|
||||
/>
|
||||
</ControlSection>
|
||||
|
||||
<ControlSection title="Title">
|
||||
<ColorRow
|
||||
label="Color"
|
||||
name="Legend title color"
|
||||
value={titleColor}
|
||||
fallback={TEXT_GREY}
|
||||
onChange={(hex) => set(TITLE_COLOR, hex)}
|
||||
onClear={() => set(TITLE_COLOR, undefined)}
|
||||
/>
|
||||
<NumberRow
|
||||
label="Size"
|
||||
name="Legend title size"
|
||||
value={titleSize}
|
||||
min={0}
|
||||
unit="px"
|
||||
onChange={(n) => set(TITLE_SIZE, n)}
|
||||
/>
|
||||
</ControlSection>
|
||||
|
||||
<ControlSection title="Labels">
|
||||
<ColorRow
|
||||
label="Color"
|
||||
name="Legend label color"
|
||||
value={labelColor}
|
||||
fallback={TEXT_GREY}
|
||||
onChange={(hex) => set(LABEL_COLOR, hex)}
|
||||
onClear={() => set(LABEL_COLOR, undefined)}
|
||||
/>
|
||||
<NumberRow
|
||||
label="Size"
|
||||
name="Legend label size"
|
||||
value={labelSize}
|
||||
min={0}
|
||||
unit="px"
|
||||
onChange={(n) => set(LABEL_SIZE, n)}
|
||||
/>
|
||||
</ControlSection>
|
||||
|
||||
<ControlSection title="Symbols" hint="The colored keys beside each label.">
|
||||
<NumberRow
|
||||
label="Symbol size"
|
||||
value={symbolSize}
|
||||
min={0}
|
||||
unit="px²"
|
||||
onChange={(n) => set(SYMBOL_SIZE, n)}
|
||||
/>
|
||||
</ControlSection>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user