Files
TREK/client/src/mobile/components/railAction.ts
T
Maurice 5346182807 fix(mobile): the rail, the docks and the top-level screens on a large screen
From the large-screen review (visual findings of the shell and the top-level
screens):

- Rail: 80px wide, one caption size for every name (no shrinking per item),
  balanced two-line names with every entry as tall as the tallest, short rail
  names where a language's word is too long (mobileNav.rail*, all locales),
  and near-opaque glass so a map's colours no longer smear through it.
- Rail head: always one 56px circle, so the entries never jump between pages:
  the create action, the screen's own action (the vacation calendar's
  year/editor switch moves there instead of floating over the days) or the
  quiet logo on settings and administration. It stays put while a short rail
  scrolls.
- Vacation calendar: weekday letters over each month on a wide window; the
  editor's mode switch sits at the foot with room kept for it.
- Docks: More is marked with a ring rather than the centre button's fill, the
  dock glass is denser, and lists fade out under the dock instead of showing
  in the gap below it.
- Dashboard: trip names get the card's width with the dates under them, the
  hero's facts wrap as whole chips, the filter chips scroll with a fade, the
  hero ends above the dock on a phone on its side and its buttons sit on a
  dark ground; the header bar stands in the content's column.
- Journeys: on a wide window the header is the same glass bar, in the column
  of the cards, and the other journeys' label starts with them.
- Notifications: the round chevron back button, in the list's column.
- Settings and administration share one frame and width. Admin's new-user
  action sits under the section list's title, the users card header wraps
  inside the card, stat labels ellipsize, role badges read in dark mode.
- Collections: one card per place with the status inside it, worded in a
  readable mix of its colour.
- Atlas and the journey map frame clear of the rail and the header; the
  journey's day bar sits on a glass plate, and a phone on its side gets
  smaller cards.
- Rail label tests rewritten for the new rule; FE-MOB-PANES-016 duplicate
  renumbered to 017.
2026-11-07 23:25:15 +01:00

45 lines
1.6 KiB
TypeScript

import type { LucideIcon } from 'lucide-react';
import { useEffect, useLayoutEffect, useRef } from 'react';
import { create } from 'zustand';
/**
* A screen's own action at the head of the navigation rail, in the place the
* "+" holds elsewhere: the vacation calendar's switch between the year and the
* editor. On the phone the same screen draws it into the dock's centre slot.
*/
export interface RailAction {
label: string;
icon: LucideIcon;
/** On: drawn as a ring rather than filled (the editor is open). */
pressed?: boolean;
run: () => void;
}
const useRailActionStore = create<{ action: RailAction | null }>(() => ({ action: null }));
/** The action the screen on show has put at the head of the rail, if any. */
export function useRailAction(): RailAction | null {
return useRailActionStore((s) => s.action);
}
/**
* Puts the screen's action at the head of the rail while it is mounted; null
* takes it away. Pressing it always runs the newest handler, and the rail only
* redraws when what it shows changes.
*/
export function useScreenRailAction(action: RailAction | null): void {
const run = useRef(action?.run);
useLayoutEffect(() => {
run.current = action?.run;
});
const shown = action !== null;
const label = action?.label ?? '';
const icon = action?.icon;
const pressed = action?.pressed ?? false;
useEffect(() => {
if (!shown || !icon) return;
useRailActionStore.setState({ action: { label, icon, pressed, run: () => run.current?.() } });
return () => useRailActionStore.setState({ action: null });
}, [shown, label, icon, pressed]);
}