perf(ui): the dialogs you type in no longer sit on a blurred backdrop
A backdrop-filter covers the whole window and is recomputed every time anything above it repaints. Behind these dialogs is an application that never stops moving — CAT polls four times a second, spots arrive, meters sweep, maps redraw — so the filter was being recomputed continuously, and each keystroke's repaint dragged a full-window blur with it. That is why the lag was felt in Preferences and nowhere else, and why fixing the cluster macros did not end it. Worse in one place: the cluster server editor opens FROM Preferences, so its overlay was the SECOND full-window filter stacked over the first. That is exactly where the delay was first reported. The dialogs an operator types in for minutes — Preferences, the cluster editor, the QSO editor, bulk edit, alert rules, award definitions — now dim the background harder instead of blurring it. Everything else keeps the blur: a confirmation you click through in a second costs nothing.
This commit is contained in:
@@ -141,7 +141,7 @@ export function AlertsModal({ onClose, bands, modes, countries }: {
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(o) => { if (!o) onClose(); }}>
|
||||
<DialogContent className="max-w-4xl">
|
||||
<DialogContent overlayBlur={false} className="max-w-4xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2"><Bell className="size-4 text-primary" /> {t('altm.title')}</DialogTitle>
|
||||
<DialogDescription>{t('altm.desc')}</DialogDescription>
|
||||
|
||||
@@ -403,7 +403,7 @@ export function AwardEditor({ open, onClose, onSaved }: Props) {
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(o) => { if (!o) onClose(); }}>
|
||||
<DialogContent className="max-w-6xl w-[95vw] max-h-[92vh] grid grid-rows-[auto_1fr_auto] gap-0 p-0">
|
||||
<DialogContent overlayBlur={false} className="max-w-6xl w-[95vw] max-h-[92vh] grid grid-rows-[auto_1fr_auto] gap-0 p-0">
|
||||
<DialogHeader className="px-5 py-3 border-b">
|
||||
<DialogTitle>{t('awed.awardManagement')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -193,7 +193,7 @@ export function BulkEditModal({ open, ids, onClose, onApplied }: Props) {
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(o) => { if (!o) onClose(); }}>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogContent overlayBlur={false} className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('bulk.title')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
|
||||
@@ -542,7 +542,7 @@ export function QSOEditModal({ qso, onSave, onDelete, onClose, countries = [], b
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(o) => { if (!o) onClose(); }}>
|
||||
<DialogContent className="max-w-5xl max-h-[92vh] grid grid-rows-[auto_1fr_auto] gap-0 p-0">
|
||||
<DialogContent overlayBlur={false} className="max-w-5xl max-h-[92vh] grid grid-rows-[auto_1fr_auto] gap-0 p-0">
|
||||
<DialogHeader className="flex-row items-baseline gap-2">
|
||||
<DialogTitle>{t('qedit.title')}</DialogTitle>
|
||||
<span className="font-mono text-xs text-muted-foreground">#{draft.id} — {draft.callsign}</span>
|
||||
|
||||
@@ -8858,7 +8858,11 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(o) => { if (!o) onClose(); }}>
|
||||
<DialogContent className="max-w-[1180px] w-full max-h-[90vh] grid grid-rows-[auto_1fr_auto] gap-0 p-0">
|
||||
{/* No backdrop blur. This dialog is open for minutes with the operator
|
||||
typing in it, and the application behind it never stops repainting —
|
||||
a full-window backdrop filter is then recomputed under every one of
|
||||
those repaints, which is what the delay between key and letter was. */}
|
||||
<DialogContent overlayBlur={false} className="max-w-[1180px] w-full max-h-[90vh] grid grid-rows-[auto_1fr_auto] gap-0 p-0">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('settings.title')}</DialogTitle>
|
||||
<DialogDescription className="sr-only">Configure OpsLog modules — station, lookup, hardware…</DialogDescription>
|
||||
@@ -8971,7 +8975,10 @@ function ClusterServerEditor({ value, onCancel, onSave }: ClusterEditorProps) {
|
||||
(p) => p.host.toLowerCase() === (s.host ?? '').trim().toLowerCase() && p.port === s.port);
|
||||
return (
|
||||
<Dialog open onOpenChange={(o) => { if (!o) onCancel(); }}>
|
||||
<DialogContent className="max-w-[640px] px-6">
|
||||
{/* Opened from Preferences, so its overlay would be the SECOND
|
||||
full-window backdrop filter stacked over a moving page — which is
|
||||
where the typing delay was first noticed. */}
|
||||
<DialogContent overlayBlur={false} className="max-w-[640px] px-6">
|
||||
<DialogHeader className="px-2">
|
||||
<DialogTitle>{s.id ? `Edit cluster · ${s.name || 'unnamed'}` : 'New cluster'}</DialogTitle>
|
||||
<DialogDescription className="text-xs">
|
||||
|
||||
@@ -8,14 +8,23 @@ const DialogTrigger = DialogPrimitive.Trigger;
|
||||
const DialogPortal = DialogPrimitive.Portal;
|
||||
const DialogClose = DialogPrimitive.Close;
|
||||
|
||||
// blur=false drops the backdrop filter and dims harder instead.
|
||||
//
|
||||
// A backdrop-filter over the whole window is recomputed every time anything
|
||||
// above it repaints — and underneath this one sits an application that never
|
||||
// stops moving: CAT polls four times a second, spots arrive, meters sweep, maps
|
||||
// redraw. On a long-lived dialog with text fields in it, that shows as a delay
|
||||
// between the key and the letter. Ornament is not worth a keyboard that feels
|
||||
// slow, so the dialogs an operator TYPES in for minutes at a time turn it off.
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> & { blur?: boolean }
|
||||
>(({ className, blur = true, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed inset-0 z-50 bg-stone-900/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
'fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
blur ? 'bg-stone-900/40 backdrop-blur-sm' : 'bg-stone-900/60',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -25,10 +34,10 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { hideClose?: boolean; hideOverlay?: boolean }
|
||||
>(({ className, children, hideClose, hideOverlay, ...props }, ref) => (
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { hideClose?: boolean; hideOverlay?: boolean; overlayBlur?: boolean }
|
||||
>(({ className, children, hideClose, hideOverlay, overlayBlur, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
{!hideOverlay && <DialogOverlay />}
|
||||
{!hideOverlay && <DialogOverlay blur={overlayBlur} />}
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
|
||||
Reference in New Issue
Block a user