123 lines
6.7 KiB
TypeScript
123 lines
6.7 KiB
TypeScript
// ────────────────────────────────────────────────────────────────────
|
||
// StreamHeader geometry — shared constants for the curtain layout.
|
||
//
|
||
// Mental model: the chats card is a curtain layered ABOVE the header
|
||
// (z-index higher). The curtain's `top` is the visible part of the
|
||
// header below the always-pinned tabs row. When the curtain is fully
|
||
// closed it sits flush under the tabs row (covering chips + form area
|
||
// beneath). Dragging it DOWN reveals more of the header from underneath.
|
||
// Dragging UP raises the curtain back over the header.
|
||
//
|
||
// Snap stops (curtain.top, px):
|
||
// pinned = 0 (curtain sits flush at top of the stage, tabs row
|
||
// covered; the safe-top status-bar strip above the
|
||
// stage stays painted by the surrounding context —
|
||
// see «pinned visual contract» below)
|
||
// closed = TABS_ROW_PX
|
||
// peek = TABS_ROW_PX + 2·CHIP_ROW_PX + CHIP_GAP_PX
|
||
// + CURTAIN_BREATHER_PX
|
||
// form:* = TABS_ROW_PX + formHeight + CURTAIN_BREATHER_PX
|
||
//
|
||
// Pinned visual contract: at `pinned` the curtain's top edge lands at
|
||
// y = safe-top in viewport coords (because the stage starts after the
|
||
// PageNav / appBody padding-top: var(--vojo-safe-top)). The system tray
|
||
// strip stays painted by appBody / PageNav-inner / MobileTabsPager's
|
||
// static header — all of which use `SurfaceVariant.Container` for that
|
||
// zone, so the colour is continuous across surfaces. The curtain MUST
|
||
// NOT extend into the safe-top zone (otherwise system text is covered)
|
||
// and MUST NOT add internal padding-top (otherwise the chat list grows
|
||
// visually taller). The clamp on the up-drag (= -TABS_ROW_PX) enforces
|
||
// the first invariant; we deliberately do not add any padding inside
|
||
// the curtain to enforce the second.
|
||
// ────────────────────────────────────────────────────────────────────
|
||
|
||
// Tabs row height. Always visible above the curtain.
|
||
export const TABS_ROW_PX = 64;
|
||
|
||
// Each peek-chip row. Reveals one chip's pill (h=48) + 8px top breather.
|
||
export const CHIP_ROW_PX = 56;
|
||
|
||
// Vertical gap BETWEEN two consecutive chip rows. Separate from
|
||
// `CURTAIN_BREATHER_PX` so the inter-chip spacing can read tighter
|
||
// than the breather between the last chip and the curtain's rounded
|
||
// top (the curtain's straight edge against a chip pill needs more
|
||
// air to avoid feeling «clamped», while two pills sitting in a
|
||
// vertical stack want to read as a pair).
|
||
export const CHIP_GAP_PX = 14;
|
||
|
||
// Initial estimate for the search form's outer height. The actual
|
||
// height is measured at runtime via ResizeObserver and adapts to the
|
||
// available viewport so the form never overflows the chats card.
|
||
export const SEARCH_FORM_BASE_PX = 360;
|
||
|
||
// Breathing strip between the bottom of any header content (revealed
|
||
// chip pill, form's last actionable element) and the top of the
|
||
// curtain. Painted by the header's `SurfaceVariant.Container` (light-
|
||
// blue) so the chip / Create button / search results never visually
|
||
// touch the curtain's rounded top — the user reads chips that sit
|
||
// flush with the curtain as «зажатые» rather than two separate
|
||
// affordances. Not applied at `closed` (nothing to breathe to).
|
||
export const CURTAIN_BREATHER_PX = 20;
|
||
|
||
// Curtain snap transition. Tuned tight for an in-app reveal —
|
||
// emphasized-decelerate territory.
|
||
export const CURTAIN_SNAP_MS = 280;
|
||
export const CURTAIN_SNAP_EASING = 'cubic-bezier(0.22, 1, 0.36, 1)';
|
||
|
||
// Curtain card top-corner radius. Matches the composer card and the
|
||
// horseshoe surfaces elsewhere in the app.
|
||
export const CURTAIN_RADIUS_PX = 24;
|
||
|
||
// Total vertical travel of the curtain between `closed` and `peek` —
|
||
// the resting-top delta between the two snaps. Used as the basis for
|
||
// the peek-commit threshold: the user must drag (rubber-banded) at
|
||
// least COMMIT_THRESHOLD × PEEK_TRAVEL_PX before release for the snap
|
||
// to flip. Anything shorter reads as accidental and springs back.
|
||
export const PEEK_TRAVEL_PX = CHIP_ROW_PX + CHIP_GAP_PX + CHIP_ROW_PX + CURTAIN_BREATHER_PX;
|
||
|
||
// Touch gesture tuning. RUBBER_BAND dampens finger→curtain motion so
|
||
// the chip reveal feels resistive; COMMIT_THRESHOLD is the fraction of
|
||
// the full peek travel the user must cross on release for the snap to
|
||
// commit. Tuned high (≈90%) so anything below «дотянул почти до конца»
|
||
// reads as accidental and snaps back to `closed`.
|
||
export const RUBBER_BAND = 0.65;
|
||
export const DIRECTION_DEAD_ZONE_PX = 10;
|
||
export const COMMIT_THRESHOLD = 0.9;
|
||
// Pull-up distance (raw finger px) required to close an active form.
|
||
export const ACTIVE_CLOSE_THRESHOLD_PX = 100;
|
||
|
||
// Total vertical CURTAIN travel for the closed ↔ pinned gesture.
|
||
// Equals the tabs row height because pinning lifts the curtain by
|
||
// exactly that distance (from y = TABS_ROW_PX down to y = 0 inside
|
||
// the stage).
|
||
export const PIN_TRAVEL_PX = TABS_ROW_PX;
|
||
|
||
// Commit threshold for pin / unpin. Tuned very high (≈95%) so the
|
||
// user must drag the curtain almost-all-the-way to the cap before
|
||
// release for the snap to flip. Anything shorter reads as accidental
|
||
// and springs back to the previous resting snap.
|
||
//
|
||
// With 1:1 finger ↔ curtain tracking (no rubber-band on pin / unpin
|
||
// — see `useCurtainGesture` and `useCurtainHandleGesture`), the
|
||
// committing finger pull is `PIN_COMMIT_THRESHOLD × PIN_TRAVEL_PX` ≈
|
||
// 61 px — essentially «drag the curtain across the full tabs-row
|
||
// height». The anti-accidental gate that previously came from
|
||
// rubber-band amplification is now provided by the dedicated handle
|
||
// hit-zone (intentional surface) plus the list-bound scroll-aware
|
||
// bail (no list scroll = no scroll-up to confuse with pin).
|
||
export const PIN_COMMIT_THRESHOLD = 0.95;
|
||
|
||
// Drag-handle hit-zone at the top of the curtain. Hosts the pin /
|
||
// unpin gesture as a dedicated touch surface so it doesn't compete
|
||
// with the chat list's vertical scroll. Drag on this handle tracks
|
||
// the finger 1:1 (no rubber-band) — finger displacement equals
|
||
// curtain displacement. The list-bound gesture in
|
||
// `useCurtainGesture` still owns peek / form-close, plus the pin path
|
||
// when the list has no scrollable content (so single-screen lists
|
||
// keep the «drag-from-anywhere» pin behaviour).
|
||
//
|
||
// Size: 32 px tall — enough touch target to land on comfortably with
|
||
// a thumb (the visible grabber pill inside is much smaller, see
|
||
// `StreamHeader.css.ts::handleBar`). The list (or DirectEmpty / the
|
||
// equivalent placeholder) starts 32 px below the curtain's top edge.
|
||
export const HANDLE_HEIGHT_PX = 32;
|