mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
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:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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)',
|
||||
}}
|
||||
|
||||
@@ -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)',
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user