1- import { useRef , useState , type ComponentProps } from 'react' ;
1+ import { useRef , useState , type ComponentProps , type ReactNode } from 'react' ;
22import { Root , Portal , Content , Item , Trigger } from '@radix-ui/react-context-menu' ;
3+ import * as RadixDialog from '@radix-ui/react-dialog' ;
34import picomatch from 'picomatch/posix' ;
45import type { FileDescriptor , I18n } from '@tutorialkit/types' ;
56
@@ -44,7 +45,8 @@ export function ContextMenu({
4445 triggerProps,
4546 ...props
4647} : Props ) {
47- const [ state , setState ] = useState < 'idle' | 'add_file' | 'add_folder' > ( 'idle' ) ;
48+ const [ state , setState ] = useState < 'idle' | 'add_file' | 'add_folder' | { error : string } > ( 'idle' ) ;
49+ const error = typeof state === 'string' ? false : state . error ;
4850 const inputRef = useRef < HTMLInputElement > ( null ) ;
4951
5052 if ( ! onFileChange ) {
@@ -65,8 +67,7 @@ export function ContextMenu({
6567 method : 'add' ,
6668 } ) ;
6769 } else {
68- // TODO: Use `@radix-ui/react-dialog` instead
69- alert ( `File "${ value } " is not allowed. Allowed patterns: [${ allowEditPatterns . join ( ', ' ) } ].` ) ;
70+ return setState ( { error : `Failed to create "${ name } "` } ) ;
7071 }
7172 }
7273
@@ -126,6 +127,19 @@ export function ContextMenu({
126127 </ MenuItem >
127128 </ Content >
128129 </ Portal >
130+
131+ { error && (
132+ < Dialog onClose = { ( ) => setState ( 'idle' ) } >
133+ { error } . Allowed patterns are:
134+ < ul className = "list-disc ml-4" >
135+ { allowEditPatterns . map ( ( pattern ) => (
136+ < li key = { pattern } >
137+ < code > { pattern } </ code >
138+ </ li >
139+ ) ) }
140+ </ ul >
141+ </ Dialog >
142+ ) }
129143 </ Root >
130144 ) ;
131145}
@@ -141,3 +155,25 @@ function MenuItem({ icon, children, ...props }: { icon: string } & ComponentProp
141155 </ Item >
142156 ) ;
143157}
158+
159+ function Dialog ( { onClose, children } : { onClose : ( ) => void ; children : ReactNode } ) {
160+ return (
161+ < RadixDialog . Root open = { true } onOpenChange = { ( open ) => ! open && onClose ( ) } >
162+ < RadixDialog . Portal >
163+ < RadixDialog . Overlay className = "fixed inset-0 opacity-50 bg-black" />
164+
165+ < RadixDialog . Content className = "fixed top-50% left-50% transform-translate--50% w-90vw max-w-450px max-h-85vh rounded-xl text-tk-text-primary bg-tk-background-negative" >
166+ < div className = "relative py-4 px-10" >
167+ < RadixDialog . Title className = "text-6 mb-2" > Failed to create file</ RadixDialog . Title >
168+
169+ { children }
170+
171+ < RadixDialog . Close title = "Close" className = "absolute top-4 right-4 w-6 h-6" >
172+ < span aria-hidden className = "i-ph-x block w-full h-full" > </ span >
173+ </ RadixDialog . Close >
174+ </ div >
175+ </ RadixDialog . Content >
176+ </ RadixDialog . Portal >
177+ </ RadixDialog . Root >
178+ ) ;
179+ }
0 commit comments