Files
TREK/client/src/components/Packing/packingPopoverStyles.ts
T
Maurice 0dd07befa7 feat(planner): bring the plan tab and every trip dialog into the new look
The Plan tab was the last trip tab still in the old design. It now reads
like Bookings, Costs, Lists, Files and Collab, without losing a function:

- Day plan: each day is a card with a head band (day tile with weather,
  stay and rental pills, a "+" menu for place, stay, transport, transit
  and note), rows with "..." menus, white badges and a white route bar.
- Places column: add, search and filters in three rows, a show dropdown,
  category and rating filters, and a selection bar at the foot.
- Place inspector, day details and the stay editor on the shared dialog
  frame; the place form in the editor look with the name in the head band.
- Clicking a booking or transport anywhere on the plan opens the booking
  detail popup first; Edit leads to the editor. The popup is one host
  shared with the Bookings and Transports tabs.
- Files and Collab dialogs, notes, polls, links and chat moved to the same
  frame; shared DialogShell and dialogParts hold the building blocks.
- The map hover card shows the place photo and follows the theme.

Every new and reworded string is translated into all locales, and a few
older English leftovers (help center, cost split, import hints) with them.
Phone screens keep their own look.
2026-09-27 21:00:19 +02:00

51 lines
2.5 KiB
TypeScript

import { useEffect, type CSSProperties, type RefObject } from 'react'
/**
* The one look every packing popover shares (the item menu, the bag picker, a
* list's menu): a soft card, entries with an icon column, a tick on the
* current choice.
*/
export const POPOVER: CSSProperties = {
background: 'var(--bg-card)', border: '1px solid var(--border-secondary)', borderRadius: 14,
boxShadow: '0 12px 32px -8px rgba(0,0,0,0.25), 0 2px 6px rgba(0,0,0,0.06)', padding: 6,
}
export const POPOVER_DIVIDER: CSSProperties = { height: 1, background: 'var(--border-faint)', margin: '5px 6px' }
export const POPOVER_CAPTION: CSSProperties = {
padding: '5px 10px 4px', fontSize: 'calc(10.5px * var(--fs-scale-caption, 1))', fontWeight: 700,
color: 'var(--text-faint)', textTransform: 'uppercase', letterSpacing: '0.05em',
}
/** An inline field that adds something (an item, a bag): framed, with a soft accent ring. */
export const COMPOSER: CSSProperties = {
display: 'flex', alignItems: 'center', gap: 8, borderRadius: 10,
border: '1px solid var(--border-primary)', background: 'var(--bg-card)',
boxShadow: '0 0 0 3px color-mix(in srgb, var(--accent) 10%, transparent)',
}
/** The square accent button that confirms a composer; faded while there is nothing to add. */
export const composerConfirm = (enabled: boolean): CSSProperties => ({
width: 28, height: 28, borderRadius: 8, border: 'none', background: 'var(--accent)', color: 'var(--accent-text)',
cursor: enabled ? 'pointer' : 'default', opacity: enabled ? 1 : 0.35, display: 'grid', placeItems: 'center',
flexShrink: 0, transition: 'opacity 120ms ease',
})
/**
* Closes a popover or composer on a press anywhere outside `ref`. A copy that
* is mounted but not displayed (the bag sidebar below its breakpoint) has no
* layout box, and must not close the visible copy's twin.
*/
export function useDismissOnOutside(ref: RefObject<HTMLElement | null>, open: boolean, onClose: () => void, panelRef?: RefObject<HTMLElement | null>) {
useEffect(() => {
if (!open) return
const onDown = (e: MouseEvent) => {
const el = ref.current
if (!el || el.getClientRects().length === 0 || el.contains(e.target as Node)) return
// A panel portalled to the body counts as inside too.
if (panelRef?.current?.contains(e.target as Node)) return
onClose()
}
document.addEventListener('mousedown', onDown)
return () => document.removeEventListener('mousedown', onDown)
}, [ref, open, onClose, panelRef])
}