fix(clip): a time's error line sits in the flow, the remove button off the cut, and every player surface rings amber
- useTimecodeField holds a time field's draft for its caller, which places TimecodeError (role alert) in its own flow under the fields' row: it no longer lies over the crop row taking its clicks, runs out of the editor's card, or joins the field's <label> name. - The editor's remove-cut buttons sit in a row under the track at their cut (trackLeft), off the cut's grab area, where a press drags it; RangeTrack's adornment slot is gone. - media-focus re-points --focus to the media amber on the Plex player, the YouTube stage and every bg-media-chrome surface; a contract test holds each such class string to media-focus or media-scope, the field's focus edge to the focus token, and --color-focus to the inline theme. - An upload's quota headline says the converted file does not fit, not "Storage full". - DESIGN.md: the tokens paragraph says what is re-pointed where; the decision row measures all three colours on the chrome.
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import clsx from "clsx";
|
||||
import { trackFrac, trackPct } from "./trackPosition";
|
||||
|
||||
// A horizontal time track: the shared body of every timeline that marks points in a piece of media
|
||||
// (the download editor's cut list, the players' clip panel). It draws what it is given — regions
|
||||
@@ -48,10 +49,6 @@ export interface TrackHandle {
|
||||
onChange: (time: number, final: boolean) => void;
|
||||
/** The drawn bar's look (its colour); the hit area around it is the track's. */
|
||||
barClassName?: string;
|
||||
/** Drawn at the handle, e.g. a remove button; it should stop its own pointer events. Beside the
|
||||
* slider, not in it: a slider's content is not a control to assistive tech, and its keys are the
|
||||
* slider's. */
|
||||
adornment?: ReactNode;
|
||||
}
|
||||
|
||||
interface TrackRegion {
|
||||
@@ -108,9 +105,8 @@ export function RangeTrack({
|
||||
playheadClassName = "bg-white",
|
||||
}: Props) {
|
||||
const win: RangeWindow = view ?? { from: 0, to: duration };
|
||||
const span = Math.max(win.to - win.from, 1e-6);
|
||||
const frac = (t: number) => (t - win.from) / span;
|
||||
const pct = (f: number) => `${(clamp(f, 0, 1) * 100).toFixed(3)}%`;
|
||||
const frac = (t: number) => trackFrac(t, win);
|
||||
const pct = trackPct;
|
||||
// The handle a pointer is dragging, and the press on the bare track (a scrub), so a move is only
|
||||
// acted on between its own down and up.
|
||||
const [dragging, setDragging] = useState<{
|
||||
@@ -239,7 +235,7 @@ export function RangeTrack({
|
||||
e.stopPropagation();
|
||||
move(next, true);
|
||||
};
|
||||
return [
|
||||
return (
|
||||
<div
|
||||
key={h.id}
|
||||
role="slider"
|
||||
@@ -279,17 +275,8 @@ export function RangeTrack({
|
||||
}}
|
||||
>
|
||||
<div className={clsx("h-full w-1 rounded-sm", h.barClassName ?? "bg-white/90")} />
|
||||
</div>,
|
||||
h.adornment != null && (
|
||||
<div
|
||||
key={`${h.id}-adornment`}
|
||||
className="pointer-events-none absolute inset-y-0 z-base w-6 -translate-x-1/2"
|
||||
style={{ left: pct(f) }}
|
||||
>
|
||||
{h.adornment}
|
||||
</div>
|
||||
),
|
||||
];
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user