@net-advantage/jo-ui-segmented-control
v2.1.0
Published
Publishable single-select segmented control.
Readme
@net-advantage/jo-ui-segmented-control
Publishable single-select segmented control.
This is its own package, not a mode of ButtonGroup. ButtonGroup lays out independent actions, and each action is a Button with its own click. A year-span control is one choice: a radiogroup of radio items, one selected value, and a roving tab stop. Joined segments also cannot be Buttons, because a Button owns its own border, radius, and height.
The control follows a strict MVVM split. The model chooses the size, the tab stop, and the next enabled segment. The view-model owns the selected value and the keyboard commit. The view renders that view-model.
MVVM
flowchart LR
Props["SegmentedControlProps"] --> ViewModel["useSegmentedControlViewModel"]
Model["jo-ui-segmented-control.model.ts<br/>size, tab stop, and arrow movement"] --> ViewModel
ViewModel --> View["SegmentedControl.tsx"]
View -->|"onValueChange"| ViewModel
ViewModel -->|"onValueChange"| Host["Host application"]| Layer | File | Responsibility | | --- | --- | --- | | Contract | jo-ui-segmented-control.types.ts | Items, value, size, and view-model | | Model | jo-ui-segmented-control.model.ts | Normalize size, decide the roving tab stop, and move across enabled segments | | View-model | jo-ui-segmented-control.view-model.ts | Hold an uncontrolled value, commit a selection, and move focus | | View | SegmentedControl.tsx | Render the radiogroup from the view-model |
Models
flowchart TD
Props["SegmentedControlProps"] --> Size["SegmentedControlSize"]
Props --> Items["items"]
Props --> Value["value or defaultValue"]
Size --> ClassName["className"]
Items --> TabStop["roving tab stop"]
Value --> Checked["checked segment"]
Items --> Next["next enabled segment"]- A missing size is
sm.xsis the compact year-span size: 24px tall, 10.5px text, and 8px of inline padding, from--jo-ui-segmented-control-xs-height,--jo-ui-segmented-control-xs-font-size, and--jo-ui-segmented-control-xs-padding-inline. Segments touch. The control setsgap: 0, so there is no 8px gap between them. With Inter, 5Y / 10Y / All atxsmeasures 94px in Chromium, the same width as.tl-modesin the Sarah mock. The issue's 94.5px figure is half a pixel above that render. smis 32px tall with 13px text.- Pass
valuefor a controlled selection. Omit it and passdefaultValuefor an uncontrolled selection. - The root is
role="radiogroup". Each segment isrole="radio". Arrow keys, Home, and End move the selection and skip disabled segments. Only the selected enabled segment is in the tab order. - A disabled control, and a disabled segment, do not call
onValueChange. - Colour comes from the year-bar theme tokens. Unselected labels use
--jo-color-year-bar-span-text(#6b7280light,#e9e6dedark). The surface, border, hover, active fill, and active text use--jo-color-year-bar-surface,--jo-color-year-bar-border,--jo-color-year-bar-hover,--jo-color-year-bar-active, and--jo-color-year-bar-active-text. The segment font follows--jo-ui-control-font-family, then--jo-font-family. :focus-visibledraws a 2px outline in--jo-color-focus, offset by 2px.
Structure
- src: runtime component, model, view-model, and types.
- stories: Storybook stories for documentation and visual testing.
Publish footprint
This package publishes only:
- dist
- README.md
Stories and tests are excluded from the published artifact by design.
