Skip to content

Commit ec38ab9

Browse files
Merge pull request #203 from community-scripts/feat/global-esc-close
fix/194 Add global Escape-to-close for custom modals
2 parents f3c68bf + cacd4b3 commit ec38ab9

15 files changed

+93
-6
lines changed

src/app/_components/AuthModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import { Button } from './ui/button';
55
import { Input } from './ui/input';
66
import { useAuth } from './AuthProvider';
77
import { Lock, User, AlertCircle } from 'lucide-react';
8+
import { useRegisterModal } from './modal/ModalStackProvider';
89

910
interface AuthModalProps {
1011
isOpen: boolean;
1112
}
1213

1314
export function AuthModal({ isOpen }: AuthModalProps) {
15+
useRegisterModal(isOpen, { id: 'auth-modal', allowEscape: false, onClose: () => null });
1416
const { login } = useAuth();
1517
const [username, setUsername] = useState('');
1618
const [password, setPassword] = useState('');

src/app/_components/ConfirmationModal.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use client';
22

3-
import { useState } from 'react';
3+
import { useMemo, useState } from 'react';
44
import { Button } from './ui/button';
55
import { AlertTriangle, Info } from 'lucide-react';
6+
import { useRegisterModal } from './modal/ModalStackProvider';
67

78
interface ConfirmationModalProps {
89
isOpen: boolean;
@@ -28,10 +29,12 @@ export function ConfirmationModal({
2829
cancelButtonText = 'Cancel'
2930
}: ConfirmationModalProps) {
3031
const [typedText, setTypedText] = useState('');
32+
const isDanger = variant === 'danger';
33+
const allowEscape = useMemo(() => !isDanger, [isDanger]);
3134

32-
if (!isOpen) return null;
35+
useRegisterModal(isOpen, { id: 'confirmation-modal', allowEscape, onClose });
3336

34-
const isDanger = variant === 'danger';
37+
if (!isOpen) return null;
3538
const isConfirmEnabled = isDanger ? typedText === confirmText : true;
3639

3740
const handleConfirm = () => {

src/app/_components/ErrorModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useEffect } from 'react';
44
import { Button } from './ui/button';
55
import { AlertCircle, CheckCircle } from 'lucide-react';
6+
import { useRegisterModal } from './modal/ModalStackProvider';
67

78
interface ErrorModalProps {
89
isOpen: boolean;
@@ -21,6 +22,7 @@ export function ErrorModal({
2122
details,
2223
type = 'error'
2324
}: ErrorModalProps) {
25+
useRegisterModal(isOpen, { id: 'error-modal', allowEscape: true, onClose });
2426
// Auto-close after 10 seconds
2527
useEffect(() => {
2628
if (isOpen) {

src/app/_components/ExecutionModeModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { Server } from '../../types/server';
55
import { Button } from './ui/button';
66
import { ColorCodedDropdown } from './ColorCodedDropdown';
77
import { SettingsModal } from './SettingsModal';
8+
import { useRegisterModal } from './modal/ModalStackProvider';
89

910

1011
interface ExecutionModeModalProps {
@@ -15,6 +16,7 @@ interface ExecutionModeModalProps {
1516
}
1617

1718
export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: ExecutionModeModalProps) {
19+
useRegisterModal(isOpen, { id: 'execution-mode-modal', allowEscape: true, onClose });
1820
const [servers, setServers] = useState<Server[]>([]);
1921
const [loading, setLoading] = useState(false);
2022
const [error, setError] = useState<string | null>(null);

src/app/_components/GeneralSettingsModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { Input } from './ui/input';
66
import { Toggle } from './ui/toggle';
77
import { ContextualHelpIcon } from './ContextualHelpIcon';
88
import { useTheme } from './ThemeProvider';
9+
import { useRegisterModal } from './modal/ModalStackProvider';
910

1011
interface GeneralSettingsModalProps {
1112
isOpen: boolean;
1213
onClose: () => void;
1314
}
1415

1516
export function GeneralSettingsModal({ isOpen, onClose }: GeneralSettingsModalProps) {
17+
useRegisterModal(isOpen, { id: 'general-settings-modal', allowEscape: true, onClose });
1618
const { theme, setTheme } = useTheme();
1719
const [activeTab, setActiveTab] = useState<'general' | 'github' | 'auth'>('general');
1820
const [githubToken, setGithubToken] = useState('');

src/app/_components/HelpModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from 'react';
44
import { Button } from './ui/button';
55
import { HelpCircle, Server, Settings, RefreshCw, Package, HardDrive, FolderOpen, Search, Download } from 'lucide-react';
6+
import { useRegisterModal } from './modal/ModalStackProvider';
67

78
interface HelpModalProps {
89
isOpen: boolean;
@@ -13,6 +14,7 @@ interface HelpModalProps {
1314
type HelpSection = 'server-settings' | 'general-settings' | 'sync-button' | 'available-scripts' | 'downloaded-scripts' | 'installed-scripts' | 'lxc-settings' | 'update-system';
1415

1516
export function HelpModal({ isOpen, onClose, initialSection = 'server-settings' }: HelpModalProps) {
17+
useRegisterModal(isOpen, { id: 'help-modal', allowEscape: true, onClose });
1618
const [activeSection, setActiveSection] = useState<HelpSection>(initialSection as HelpSection);
1719

1820
if (!isOpen) return null;

src/app/_components/LXCSettingsModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ContextualHelpIcon } from './ContextualHelpIcon';
99
import { LoadingModal } from './LoadingModal';
1010
import { ConfirmationModal } from './ConfirmationModal';
1111
import { RefreshCw, AlertTriangle, CheckCircle } from 'lucide-react';
12+
import { useRegisterModal } from './modal/ModalStackProvider';
1213

1314
interface InstalledScript {
1415
id: number;
@@ -41,6 +42,7 @@ interface LXCSettingsModalProps {
4142
}
4243

4344
export function LXCSettingsModal({ isOpen, script, onClose, onSave: _onSave }: LXCSettingsModalProps) {
45+
useRegisterModal(isOpen, { id: 'lxc-settings-modal', allowEscape: true, onClose });
4446
const [activeTab, setActiveTab] = useState<string>('common');
4547
const [showConfirmation, setShowConfirmation] = useState(false);
4648
const [showResultModal, setShowResultModal] = useState(false);

src/app/_components/LoadingModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
'use client';
22

33
import { Loader2 } from 'lucide-react';
4+
import { useRegisterModal } from './modal/ModalStackProvider';
45

56
interface LoadingModalProps {
67
isOpen: boolean;
78
action: string;
89
}
910

1011
export function LoadingModal({ isOpen, action }: LoadingModalProps) {
12+
useRegisterModal(isOpen, { id: 'loading-modal', allowEscape: false, onClose: () => null });
1113
if (!isOpen) return null;
1214

1315
return (

src/app/_components/PublicKeyModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from 'react';
44
import { X, Copy, Check, Server, Globe } from 'lucide-react';
55
import { Button } from './ui/button';
6+
import { useRegisterModal } from './modal/ModalStackProvider';
67

78
interface PublicKeyModalProps {
89
isOpen: boolean;
@@ -13,6 +14,7 @@ interface PublicKeyModalProps {
1314
}
1415

1516
export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverIp }: PublicKeyModalProps) {
17+
useRegisterModal(isOpen, { id: 'public-key-modal', allowEscape: true, onClose });
1618
const [copied, setCopied] = useState(false);
1719
const [commandCopied, setCommandCopied] = useState(false);
1820

src/app/_components/ReleaseNotesModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { api } from '~/trpc/react';
55
import { Button } from './ui/button';
66
import { Badge } from './ui/badge';
77
import { X, ExternalLink, Calendar, Tag, Loader2 } from 'lucide-react';
8+
import { useRegisterModal } from './modal/ModalStackProvider';
89
import ReactMarkdown from 'react-markdown';
910
import remarkGfm from 'remark-gfm';
1011

@@ -34,6 +35,7 @@ const markVersionAsSeen = (version: string): void => {
3435
};
3536

3637
export function ReleaseNotesModal({ isOpen, onClose, highlightVersion }: ReleaseNotesModalProps) {
38+
useRegisterModal(isOpen, { id: 'release-notes-modal', allowEscape: true, onClose });
3739
const [currentVersion, setCurrentVersion] = useState<string | null>(null);
3840
const { data: releasesData, isLoading, error } = api.version.getAllReleases.useQuery(undefined, {
3941
enabled: isOpen

0 commit comments

Comments
 (0)