mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
1e05501b9d
Mechanical, and it ends the migration visibly.
45 files imported the whole react-dom default export to reach one function.
They now import createPortal by name, across 54 call sites; six files already
did it this way. main.tsx keeps its react-dom/client import, which is a
different module and already correct.
forwardRef is gone from every component. In React 19 ref is an ordinary prop,
so the wrapper only obscured the signature:
NumericInput — a plain DOM ref, no imperative handle involved
JourneyMap — useImperativeHandle stays, only the wrapping goes
JourneyMapGL — same
JourneyMapAuto — same
Each takes `ref?: Ref<Handle>` in its own Props interface now, so the ref is
part of the declared surface instead of arriving through a generic parameter.
The two journey bindings in glLazy shrink to one line each. They existed as
forwardRef wrappers purely to carry the ref past the lazy boundary; with ref an
ordinary prop it rides through the spread like everything else, which makes all
six bindings in that file identical again.
The five React.FC annotations go. Three were `Record<string, React.FC<{size?:
number}>>` for provider icons in files that never import React at all — those
only typechecked through the UMD global — and are now ComponentType. The two
pages become function declarations.
TranslationContext uses the 19 context shorthand, which leaves its React default
import unused, so that goes too.
Also drops two dead vi.mocks of react-dom/server. Their comment still said "used
in createMarkerIcon", which stopped being true when marker icons moved to the
in-house serializer — nothing under src/ imports react-dom/server any more
except the parity test that exists to compare against it.
109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
import React, { useEffect, useCallback } from 'react'
|
|
import { createPortal } from 'react-dom'
|
|
import { Check, X } from 'lucide-react'
|
|
import { useTranslation } from '../../i18n'
|
|
|
|
interface CopyTripDialogProps {
|
|
isOpen: boolean
|
|
tripTitle: string
|
|
onClose: () => void
|
|
onConfirm: () => void
|
|
}
|
|
|
|
const WILL_COPY_KEYS = [
|
|
'dashboard.confirm.copy.will1',
|
|
'dashboard.confirm.copy.will2',
|
|
'dashboard.confirm.copy.will3',
|
|
'dashboard.confirm.copy.will4',
|
|
'dashboard.confirm.copy.will5',
|
|
'dashboard.confirm.copy.will6',
|
|
]
|
|
|
|
const WONT_COPY_KEYS = [
|
|
'dashboard.confirm.copy.wont1',
|
|
'dashboard.confirm.copy.wont2',
|
|
'dashboard.confirm.copy.wont3',
|
|
'dashboard.confirm.copy.wont4',
|
|
]
|
|
|
|
export default function CopyTripDialog({ isOpen, tripTitle, onClose, onConfirm }: CopyTripDialogProps) {
|
|
const { t } = useTranslation()
|
|
|
|
const handleEsc = useCallback((e: KeyboardEvent) => {
|
|
if (e.key === 'Escape') onClose()
|
|
}, [onClose])
|
|
|
|
useEffect(() => {
|
|
if (isOpen) document.addEventListener('keydown', handleEsc)
|
|
return () => document.removeEventListener('keydown', handleEsc)
|
|
}, [isOpen, handleEsc])
|
|
|
|
if (!isOpen) return null
|
|
|
|
return createPortal(
|
|
<div
|
|
className="fixed inset-0 z-[10000] flex items-center justify-center px-4 trek-backdrop-enter bg-[rgba(15,23,42,0.5)]"
|
|
style={{ paddingBottom: 'var(--bottom-nav-h)' }}
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className="trek-modal-enter rounded-2xl shadow-2xl w-full max-w-md p-6 bg-surface-card"
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
<h3 className="text-base font-semibold mb-1 text-content">
|
|
{t('dashboard.confirm.copy.title')}
|
|
</h3>
|
|
<p className="text-sm mb-4 text-content-secondary">
|
|
{tripTitle}
|
|
</p>
|
|
|
|
<div className="flex flex-col gap-3">
|
|
<div className="rounded-xl p-3 border border-edge-secondary" style={{ background: 'var(--bg-subtle)' }}>
|
|
<p className="text-xs font-semibold uppercase tracking-wide mb-2 text-[#16a34a]">
|
|
{t('dashboard.confirm.copy.willCopy')}
|
|
</p>
|
|
<ul className="flex flex-col gap-1">
|
|
{WILL_COPY_KEYS.map(key => (
|
|
<li key={key} className="flex items-center gap-2 text-sm text-content-secondary">
|
|
<Check size={13} className="flex-shrink-0 text-[#16a34a]" />
|
|
{t(key)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="rounded-xl p-3 border border-edge-secondary" style={{ background: 'var(--bg-subtle)' }}>
|
|
<p className="text-xs font-semibold uppercase tracking-wide mb-2 text-content-muted">
|
|
{t('dashboard.confirm.copy.wontCopy')}
|
|
</p>
|
|
<ul className="flex flex-col gap-1">
|
|
{WONT_COPY_KEYS.map(key => (
|
|
<li key={key} className="flex items-center gap-2 text-sm text-content-secondary">
|
|
<X size={13} className="flex-shrink-0 text-content-muted" />
|
|
{t(key)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-3 mt-5">
|
|
<button
|
|
onClick={onClose}
|
|
className="px-4 py-2 text-sm font-medium rounded-lg transition-colors text-content-secondary border border-edge-secondary"
|
|
>
|
|
{t('common.cancel')}
|
|
</button>
|
|
<button
|
|
onClick={() => { onConfirm(); onClose() }}
|
|
className="px-4 py-2 text-sm font-medium rounded-lg transition-opacity hover:opacity-90 bg-content text-surface-card"
|
|
>
|
|
{t('dashboard.confirm.copy.confirm')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>,
|
|
document.body
|
|
)
|
|
}
|