feat(design): titles in click surfaces stay selectable (E28 S3e4b)
A button whose text is a title or a name is now `selectable` (FocusSafeButton, under Pressable and Button): Chrome selects inside a button once it is user-select: text (measured), and the click that ends a selection, or a press dragged past the slop, opens nothing. 23 surfaces carry it (cards, rows, people, channels, playlists, users, views, messages, log lines, description links); the Plex caption div gets the same guard. A contract test pins every plain Pressable with its reason, so a new one has to choose.
This commit is contained in:
@@ -13,3 +13,19 @@ export function endsTextSelection(el: Element): boolean {
|
||||
(!!sel.focusNode && el.contains(sel.focusNode))
|
||||
);
|
||||
}
|
||||
|
||||
// The rule's other half: a press that travelled before its release was a drag (across a row's empty
|
||||
// padding, say, where nothing selects), not a click, so it opens nothing either.
|
||||
|
||||
/** How far, in px, the pointer may move between press and release and still count as a click. */
|
||||
const CLICK_SLOP = 6;
|
||||
|
||||
/** True when a click's pointer moved past the slop since its press at `from`. A keyboard click
|
||||
* (`detail` 0) carries no pointer, so it never did. */
|
||||
export function movedPastSlop(
|
||||
from: { x: number; y: number } | null,
|
||||
e: { detail: number; clientX: number; clientY: number },
|
||||
): boolean {
|
||||
if (!from || e.detail === 0) return false;
|
||||
return Math.abs(e.clientX - from.x) > CLICK_SLOP || Math.abs(e.clientY - from.y) > CLICK_SLOP;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user