refactor(planner): each card over the map sets its own left side again, where the reading direction check counts it

This commit is contained in:
Maurice
2026-10-12 21:14:28 +00:00
parent 8f6dab30a2
commit 8832632b4e
5 changed files with 28 additions and 18 deletions
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { centredMapCardBox } from './centredMapCard';
import { centredMapCard } from './centredMapCard';
// Resolves the CSS the helper returns for a given container width, so the
// geometry is checked in pixels rather than as strings.
@@ -13,17 +13,17 @@ function resolve(css: string, extent: number): number {
return Function(`return (${expr})`)() as number;
}
describe('centredMapCardBox', () => {
describe('centredMapCard', () => {
it('keeps the old centred box when there is no pill to clear', () => {
const box = centredMapCardBox(320, 320);
const box = centredMapCard(320, 320);
expect(resolve(box.width, 1280)).toBe(608);
expect(resolve(box.left, 1280)).toBe(640);
expect(resolve(box.centre, 1280)).toBe(640);
});
it('a narrow desktop map moves the card off the base-layer pill (#2604)', () => {
const box = centredMapCardBox(350, 310, { clearPill: true });
const box = centredMapCard(350, 310, { clearPill: true });
const width = resolve(box.width, 1280);
const leftEdge = resolve(box.left, 1280) - width / 2;
const leftEdge = resolve(box.centre, 1280) - width / 2;
// The pill spans 20 to 62 px right of the left panel.
expect(leftEdge).toBeGreaterThanOrEqual(350 + 62);
// And the card still stops 16 px short of the right panel.
@@ -31,8 +31,8 @@ describe('centredMapCardBox', () => {
});
it('a wide map keeps the card exactly centred and 800 wide', () => {
const box = centredMapCardBox(320, 320, { clearPill: true });
const box = centredMapCard(320, 320, { clearPill: true });
expect(resolve(box.width, 1920)).toBe(800);
expect(resolve(box.left, 1920)).toBe(960);
expect(resolve(box.centre, 1920)).toBe(960);
});
});
+8 -4
View File
@@ -20,15 +20,19 @@ const PILL_CLEARANCE = MAP_LAYER_SWITCHER_INSET + MAP_ROUND_CONTROL_SIZE + 8;
*
* `extent` is what the card is placed in: '100%' of the map, or '100vw' for the
* fixed day panel.
*
* `centre` is where the card's middle sits, measured from the map's left edge like
* the panel widths. Each card sets it as its own `left` next to translateX(-50%),
* so lint:rtl counts that physical side where the card is placed.
*/
export function centredMapCardBox(
export function centredMapCard(
leftWidth: number,
rightWidth: number,
{ extent = '100%', clearPill = false }: { extent?: string; clearPill?: boolean } = {}
): { left: string; width: string } {
): { centre: string; width: string } {
const free = `(${extent} - ${leftWidth}px - ${rightWidth}px)`;
const centre = `calc(${leftWidth}px + ${free} / 2)`;
if (!clearPill) return { left: centre, width: `min(800px, calc(${free} - ${2 * EDGE_GAP}px))` };
if (!clearPill) return { centre, width: `min(800px, calc(${free} - ${2 * EDGE_GAP}px))` };
const width = `min(800px, calc(${free} - ${PILL_CLEARANCE + EDGE_GAP}px))`;
return { width, left: `max(${centre}, calc(${leftWidth}px + ${PILL_CLEARANCE}px + ${width} / 2))` };
return { width, centre: `max(${centre}, calc(${leftWidth}px + ${PILL_CLEARANCE}px + ${width} / 2))` };
}
@@ -27,7 +27,7 @@ import { BOX, Eyebrow, Field, RoundAction, TypeTile, toneOf, toneColor, toneTint
import { SoftPill, TimePill, tintOf } from './planParts'
import { typeInfo } from './bookings/bookingsModel'
import { weatherIconFor } from '../Weather/weatherIcons'
import { centredMapCardBox } from '../Map/centredMapCard'
import { centredMapCard } from '../Map/centredMapCard'
function WIcon({ main, size = 14 }: { main: string; size?: number }) {
const Icon = weatherIconFor(main)
@@ -162,9 +162,10 @@ export default function DayDetailPanel({ day, days, places, categories = [], tri
setShowHotelPicker('edit')
}
const removeAccommodation = (acc: Accommodation) => { handleRemoveAccommodation(acc).catch(showError) }
const mapCard = centredMapCard(leftWidth, rightWidth, { extent: '100vw', clearPill: !mobile })
return (
<div className="fixed z-50" style={{ bottom: 'calc(var(--bottom-nav-h) + 20px)', transform: 'translateX(-50%)', ...centredMapCardBox(leftWidth, rightWidth, { extent: '100vw', clearPill: !mobile }), ...(mobile ? { zIndex: 10000 } : null), fontFamily: 'var(--font-system)' }}>
<div className="fixed z-50" style={{ bottom: 'calc(var(--bottom-nav-h) + 20px)', left: mapCard.centre, transform: 'translateX(-50%)', width: mapCard.width, ...(mobile ? { zIndex: 10000 } : null), fontFamily: 'var(--font-system)' }}>
<div ref={cardRef}
className="flex flex-col overflow-hidden rounded-[20px] border border-edge-faint bg-surface-elevated shadow-popover backdrop-blur-[40px] backdrop-saturate-[1.8]"
style={{ maxHeight: collapsed ? 'none' : '60vh' }}>
@@ -38,7 +38,7 @@ import { formatDistance, formatElevation } from '../../utils/units'
import { navigationTargetLabel, getNavigationTargets, openNavigationTarget } from './placeNavigation'
import { TRANSPORT_TYPES, getPlaceBookings } from '../../utils/dayMerge'
import { NavigationMenu } from '../shared/NavigationMenu'
import { centredMapCardBox } from '../Map/centredMapCard'
import { centredMapCard } from '../Map/centredMapCard'
import { resolveOpenNow, resolvePlaceTimeZone, getWeekdayIndex, type OpeningPeriod } from './placeOpenState'
import { convertHoursLine } from './placeHoursFormat'
import type { EndDayControlProps } from '../Roadtrip/EndDayControl'
@@ -370,13 +370,16 @@ export default function PlaceInspector({
const phone = place.phone || googleDetails?.phone
const description = place.description || googleDetails?.summary
const showRating = (mode === 'trip' && !!onRate) || !!shortReview
const mapCard = centredMapCard(leftWidth, rightWidth, { clearPill: clearMapPill })
return (
<div
style={{
position: 'absolute',
bottom: 20,
...centredMapCardBox(leftWidth, rightWidth, { clearPill: clearMapPill }), transform: 'translateX(-50%)',
left: mapCard.centre,
transform: 'translateX(-50%)',
width: mapCard.width,
zIndex: 50,
fontFamily: 'var(--font-system)',
}}
+4 -2
View File
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react'
import { X } from 'lucide-react'
import { NEUTRAL_TINT } from './DialogShell'
import { Tooltip } from './Tooltip'
import { centredMapCardBox } from '../Map/centredMapCard'
import { centredMapCard } from '../Map/centredMapCard'
interface DetailShellProps {
header: ReactNode
@@ -38,13 +38,15 @@ export default function DetailShell({
closeButtonClassName = '',
testId,
}: DetailShellProps) {
const mapCard = centredMapCard(leftWidth, rightWidth, { clearPill: clearMapPill })
return (
<div
style={{
position: 'absolute',
bottom: 20,
...centredMapCardBox(leftWidth, rightWidth, { clearPill: clearMapPill }),
left: mapCard.centre,
transform: 'translateX(-50%)',
width: mapCard.width,
zIndex: 50,
fontFamily: 'var(--font-system)',
}}