mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Unify segmented controls on a shared module with grey selection
This commit is contained in:
@@ -25,41 +25,8 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Segmented control — the four fit modes (spec §04). */
|
||||
.fit {
|
||||
display: inline-flex;
|
||||
border: var(--border-width) solid var(--border-strong);
|
||||
}
|
||||
|
||||
.fitOption {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: var(--bg);
|
||||
color: var(--text-secondary);
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background var(--dur-fast) var(--ease),
|
||||
color var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.fitOption + .fitOption {
|
||||
border-left: var(--border-width) solid var(--border-strong);
|
||||
}
|
||||
|
||||
.fitOption:hover {
|
||||
background: var(--layer-01);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.fitActive,
|
||||
.fitActive:hover {
|
||||
background: var(--accent);
|
||||
color: var(--accent-contrast);
|
||||
}
|
||||
/* Fit-mode segmented control (spec §04) styling now lives in
|
||||
SegmentedControl.module.css; the per-mode sizing classes below stay here. */
|
||||
|
||||
/*
|
||||
* Chart sizing. The host (passed to vega-embed) is branded `.vega-embed`
|
||||
|
||||
@@ -62,9 +62,6 @@ function FitControl() {
|
||||
options={FIT_OPTIONS}
|
||||
value={fitMode}
|
||||
onChange={setFitMode}
|
||||
className={styles.fit}
|
||||
optionClassName={styles.fitOption}
|
||||
activeClassName={styles.fitActive}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Shared look for every SegmentedControl. The control's appearance is owned here
|
||||
* — once — so the Draft/Published and fit-mode instances can't drift apart again.
|
||||
* Selected option uses --layer-02 (a neutral high-contrast fill, per Carbon's
|
||||
* content switcher); --accent stays reserved for primary actions (e.g. Publish),
|
||||
* so "current selection" never reads as "the action to take". Call sites may still
|
||||
* pass className/optionClassName/activeClassName for layout-specific additions.
|
||||
*/
|
||||
.group {
|
||||
display: inline-flex;
|
||||
border: var(--border-width) solid var(--border-strong);
|
||||
}
|
||||
|
||||
.option {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: var(--bg);
|
||||
color: var(--text-secondary);
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background var(--dur-fast) var(--ease),
|
||||
color var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.option + .option {
|
||||
border-left: var(--border-width) solid var(--border-strong);
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
background: var(--layer-01);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.active,
|
||||
.active:hover {
|
||||
background: var(--layer-02);
|
||||
color: var(--text);
|
||||
}
|
||||
@@ -9,10 +9,13 @@
|
||||
* move focus *and* select. Native `<button>` gives Enter/Space-to-select for
|
||||
* free. One widget so every segmented control gets the same, documented model.
|
||||
*
|
||||
* Styling is injected via class props so each call site keeps its own look.
|
||||
* The control owns its base look (SegmentedControl.module.css) so every instance
|
||||
* stays visually consistent; call sites may pass class props for layout-specific
|
||||
* additions, which compose on top of the base classes.
|
||||
*/
|
||||
|
||||
import { useRef } from 'react';
|
||||
import styles from './SegmentedControl.module.css';
|
||||
|
||||
export interface SegmentedOption<T extends string> {
|
||||
value: T;
|
||||
@@ -25,11 +28,11 @@ interface SegmentedControlProps<T extends string> {
|
||||
options: ReadonlyArray<SegmentedOption<T>>;
|
||||
value: T;
|
||||
onChange: (value: T) => void;
|
||||
/** Class for the radiogroup container. */
|
||||
/** Extra class for the radiogroup container (composed onto the base look). */
|
||||
className?: string;
|
||||
/** Class for each option button. */
|
||||
/** Extra class for each option button (composed onto the base look). */
|
||||
optionClassName?: string;
|
||||
/** Extra class applied to the selected option. */
|
||||
/** Extra class applied to the selected option (composed onto the base active look). */
|
||||
activeClassName?: string;
|
||||
}
|
||||
|
||||
@@ -74,7 +77,11 @@ export function SegmentedControl<T extends string>({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className} role="radiogroup" aria-label={label}>
|
||||
<div
|
||||
className={[styles.group, className].filter(Boolean).join(' ')}
|
||||
role="radiogroup"
|
||||
aria-label={label}
|
||||
>
|
||||
{options.map((opt, i) => {
|
||||
const selected = opt.value === value;
|
||||
return (
|
||||
@@ -89,7 +96,14 @@ export function SegmentedControl<T extends string>({
|
||||
// Roving tabindex: only the selected option is a tab stop; arrows
|
||||
// move within the group.
|
||||
tabIndex={selected ? 0 : -1}
|
||||
className={[optionClassName, selected ? activeClassName : ''].filter(Boolean).join(' ')}
|
||||
className={[
|
||||
styles.option,
|
||||
optionClassName,
|
||||
selected && styles.active,
|
||||
selected && activeClassName,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
onClick={() => onChange(opt.value)}
|
||||
onKeyDown={(e) => onKeyDown(e, i)}
|
||||
>
|
||||
|
||||
@@ -19,41 +19,7 @@
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
/* Draft / Published segmented toggle. */
|
||||
.viewToggle {
|
||||
display: inline-flex;
|
||||
border: var(--border-width) solid var(--border-strong);
|
||||
}
|
||||
|
||||
.viewOption {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: var(--bg);
|
||||
color: var(--text-secondary);
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background var(--dur-fast) var(--ease),
|
||||
color var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.viewOption + .viewOption {
|
||||
border-left: var(--border-width) solid var(--border-strong);
|
||||
}
|
||||
|
||||
.viewOption:hover {
|
||||
background: var(--layer-01);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.viewActive,
|
||||
.viewActive:hover {
|
||||
background: var(--layer-02);
|
||||
color: var(--text);
|
||||
}
|
||||
/* Draft/Published toggle styling now lives in SegmentedControl.module.css. */
|
||||
|
||||
/* Publish / Revert buttons. */
|
||||
.action {
|
||||
|
||||
@@ -81,9 +81,6 @@ function EditorToolbar() {
|
||||
options={VIEW_OPTIONS}
|
||||
value={editorView}
|
||||
onChange={setEditorView}
|
||||
className={styles.viewToggle}
|
||||
optionClassName={styles.viewOption}
|
||||
activeClassName={styles.viewActive}
|
||||
/>
|
||||
|
||||
<span className={styles.spacer} />
|
||||
|
||||
Reference in New Issue
Block a user