Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 'use client' // SegmentedControl — the studio's dark, cyan-active pill toggle (Gitea epic #5, // full-bleed CP1a). Factored from the viewer's hand-rolled "My colors / Print // preview" lens so the same primitive drives the fabrication-target chooser (the // old fork), the preview lens, and — later — the start-policy toggle. Styled // from theme.ts, which owns the one cyan gradient the studio is allowed to use // (the local copy of it is gone). import { STUDIO } from './theme' export interface SegmentedOption<T extends string> { value: T label: string /** optional leading glyph (emoji/icon char) */ icon?: string } export interface SegmentedControlProps<T extends string> { options: readonly SegmentedOption<T>[] value: T onChange: (v: T) => void /** required — the group needs an accessible name */ ariaLabel: string /** data-element on the group wrapper */ dataElement?: string /** data-action stamped on each segment button */ dataAction?: string /** compact (preview lens) vs larger (target chooser) */ size?: 'sm' | 'md' } export function SegmentedControl<T extends string>({ options, value, onChange, ariaLabel, dataElement = 'segmented-control', dataAction = 'segmented-select', size = 'sm', }: SegmentedControlProps<T>) { const pad = size === 'md' ? '10px' : '7px 8px' const fontSize = size === 'md' ? 12 : 11 return ( <div data-component="segmented-control" data-element={dataElement} role="group" aria-label={ariaLabel} style={{ display: 'flex', borderRadius: STUDIO.radius.segmented, overflow: 'hidden', border: `1px solid ${STUDIO.color.borderStrong}`, }} > {options.map((opt) => { const active = opt.value === value return ( <button key={opt.value} type="button" data-action={dataAction} data-value={opt.value} aria-pressed={active} onClick={() => onChange(opt.value)} style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6, padding: pad, border: 'none', background: active ? STUDIO.gradient.accent : 'transparent', color: active ? STUDIO.color.onAccent : STUDIO.color.text2, fontSize, fontWeight: 600, cursor: 'pointer', }} > {opt.icon && <span aria-hidden="true">{opt.icon}</span>} {opt.label} </button> ) })} </div> ) } |