Point the range picker at the shared segmented control
RangePicker carried a byte-for-byte copy of ui/segmented.tsx's markup; it
now renders that component instead.
The dashboard's own segmented widget stays where it is: it is not a copy
but a look-aware control, drawn per look in
Dashboard/ui/{fluksio,glass,material}/Controls.tsx over the shared
useSegmented behaviour. Note that in segmented.tsx in place of the stale
size-prop plan.
This commit is contained in:
@@ -1,8 +1,4 @@
|
|||||||
// The segmented shape's thumb transition lives beside the dashboard's own
|
import { Segmented } from "@/components/ui/segmented"
|
||||||
// widgets, and CSS is chunked per entry — so the rule is pulled in wherever
|
|
||||||
// this picker is used, or the two copies of one shape would move differently.
|
|
||||||
import "@/components/Dashboard/dashboard.css"
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A window of history, and everything a query needs to ask for it.
|
* A window of history, and everything a query needs to ask for it.
|
||||||
@@ -39,12 +35,7 @@ export const DEFAULT_RANGE = RANGES[2]
|
|||||||
export const rangeStart = (range: Range) =>
|
export const rangeStart = (range: Range) =>
|
||||||
new Date(Date.now() - range.hours * 3600_000).toISOString()
|
new Date(Date.now() - range.hours * 3600_000).toISOString()
|
||||||
|
|
||||||
/**
|
/** The window a screen is showing, as presets. */
|
||||||
* The window a screen is showing, as presets.
|
|
||||||
*
|
|
||||||
* The one segmented shape: a single border pill, transparent segments,
|
|
||||||
* bg-accent on the selected one (root DESIGN-GUIDELINES.md).
|
|
||||||
*/
|
|
||||||
export function RangePicker({
|
export function RangePicker({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@@ -52,47 +43,20 @@ export function RangePicker({
|
|||||||
value: Range
|
value: Range
|
||||||
onChange: (range: Range) => void
|
onChange: (range: Range) => void
|
||||||
}) {
|
}) {
|
||||||
const chosen = RANGES.findIndex((range) => range.hours === value.hours)
|
|
||||||
return (
|
return (
|
||||||
// A `fieldset` carries `min-inline-size: min-content` from the UA sheet,
|
<Segmented
|
||||||
// which no width utility overrides. Equal tracks and no gap put the
|
testId="range-picker"
|
||||||
// sliding thumb at its share of the padded box without measuring — a grid
|
label="Time range"
|
||||||
// rather than a flex row because `flex-1` under `w-fit` sizes the segments
|
value={String(value.hours)}
|
||||||
// to a share of the widest label instead of to the label itself.
|
options={RANGES.map(
|
||||||
<fieldset
|
(range) => [String(range.hours), range.label] as const,
|
||||||
data-testid="range-picker"
|
)}
|
||||||
className="relative grid w-fit min-w-0 items-center rounded-full border border-border p-1"
|
onChange={(hours) =>
|
||||||
style={{
|
onChange(
|
||||||
gridTemplateColumns: `repeat(${RANGES.length}, minmax(0, 1fr))`,
|
RANGES.find((range) => String(range.hours) === hours) ??
|
||||||
}}
|
DEFAULT_RANGE,
|
||||||
>
|
)
|
||||||
<legend className="sr-only">Time range</legend>
|
}
|
||||||
{chosen >= 0 ? (
|
/>
|
||||||
<span
|
|
||||||
aria-hidden
|
|
||||||
className="widget-segment-thumb pointer-events-none absolute inset-y-1 rounded-full bg-accent"
|
|
||||||
style={{
|
|
||||||
left: `calc(0.25rem + ${chosen} * (100% - 0.5rem) / ${RANGES.length})`,
|
|
||||||
width: `calc((100% - 0.5rem) / ${RANGES.length})`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{RANGES.map((range, index) => (
|
|
||||||
<button
|
|
||||||
key={range.label}
|
|
||||||
type="button"
|
|
||||||
aria-pressed={index === chosen}
|
|
||||||
onClick={() => onChange(range)}
|
|
||||||
className={cn(
|
|
||||||
"relative z-10 min-w-0 rounded-full px-2.5 py-1 text-xs transition-colors",
|
|
||||||
index === chosen
|
|
||||||
? "text-accent-foreground"
|
|
||||||
: "text-muted-foreground hover:bg-accent/50",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{range.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</fieldset>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// The thumb's transition lives beside the dashboard's own widgets, and CSS is
|
// The thumb's transition lives beside the dashboard's own widgets, and CSS is
|
||||||
// chunked per entry — so the rule is pulled in wherever this control is used,
|
// chunked per entry — so the rule is pulled in wherever this control is used.
|
||||||
// or two copies of one shape would move differently.
|
|
||||||
import "@/components/Dashboard/dashboard.css"
|
import "@/components/Dashboard/dashboard.css"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
@@ -9,9 +8,11 @@ import { cn } from "@/lib/utils"
|
|||||||
* transparent segments, and `bg-accent` held by a thumb that slides rather than
|
* transparent segments, and `bg-accent` held by a thumb that slides rather than
|
||||||
* a fill that jumps from cell to cell (root DESIGN-GUIDELINES.md).
|
* a fill that jumps from cell to cell (root DESIGN-GUIDELINES.md).
|
||||||
*
|
*
|
||||||
* ponytail: sized for editor chrome — `w-fit`, `text-xs`, a mouse-sized target.
|
* App-side chrome only — `w-fit`, `text-xs`, a mouse-sized target. The
|
||||||
* The widget-side copy in `Dashboard/widgets.tsx` is a full-width pill with a
|
* dashboard's own segmented widget is not this component and never will be:
|
||||||
* 44px touch target, so pointing that one here needs a size prop first.
|
* it is drawn per look by `Dashboard/ui/{fluksio,glass,material}/Controls.tsx`
|
||||||
|
* over the shared `useSegmented` behaviour, as a full-width pill with a 44px
|
||||||
|
* touch target. Folding the two together would flatten the three looks.
|
||||||
*/
|
*/
|
||||||
export function Segmented({
|
export function Segmented({
|
||||||
value,
|
value,
|
||||||
|
|||||||
Reference in New Issue
Block a user