mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-25 17:34:52 +10:00
141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
import * as React from "react";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from "@reactive-resume/ui/components/alert-dialog";
|
|
import { Input } from "@reactive-resume/ui/components/input";
|
|
import { cn } from "@reactive-resume/utils/style";
|
|
|
|
type PromptOptions = {
|
|
description?: string;
|
|
defaultValue?: string;
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
inputProps?: Omit<React.ComponentProps<typeof Input>, "value" | "onChange" | "onKeyDown">;
|
|
};
|
|
|
|
type PromptState = PromptOptions & {
|
|
open: boolean;
|
|
title: string;
|
|
value: string;
|
|
resolve: ((value: string | null) => void) | null;
|
|
};
|
|
|
|
type PromptContextType = {
|
|
prompt: (title: string, options?: PromptOptions) => Promise<string | null>;
|
|
};
|
|
|
|
const PromptContext = React.createContext<PromptContextType | null>(null);
|
|
|
|
export function PromptDialogProvider({ children }: { children: React.ReactNode }) {
|
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
|
|
const [state, setState] = React.useState<PromptState>({
|
|
open: false,
|
|
resolve: null,
|
|
title: "",
|
|
value: "",
|
|
});
|
|
|
|
const cancelText = state.cancelText ?? "Cancel";
|
|
const confirmText = state.confirmText ?? "Confirm";
|
|
|
|
React.useEffect(() => {
|
|
if (!state.open) return;
|
|
|
|
const timeoutId = window.setTimeout(() => {
|
|
if (!inputRef.current) return;
|
|
inputRef.current.focus();
|
|
}, 0);
|
|
|
|
return () => window.clearTimeout(timeoutId);
|
|
}, [state.open]);
|
|
|
|
const prompt = React.useCallback(async (title: string, options?: PromptOptions): Promise<string | null> => {
|
|
return new Promise<string | null>((resolve) => {
|
|
setState({
|
|
open: true,
|
|
resolve,
|
|
title,
|
|
value: options?.defaultValue ?? "",
|
|
...(options?.description !== undefined ? { description: options.description } : {}),
|
|
...(options?.defaultValue !== undefined ? { defaultValue: options.defaultValue } : {}),
|
|
...(options?.confirmText !== undefined ? { confirmText: options.confirmText } : {}),
|
|
...(options?.cancelText !== undefined ? { cancelText: options.cancelText } : {}),
|
|
...(options?.inputProps !== undefined ? { inputProps: options.inputProps } : {}),
|
|
});
|
|
});
|
|
}, []);
|
|
|
|
const handleConfirm = React.useCallback(() => {
|
|
if (state.resolve) state.resolve(state.value);
|
|
|
|
setState((prev) => ({ ...prev, open: false, resolve: null }));
|
|
}, [state.resolve, state.value]);
|
|
|
|
const handleCancel = React.useCallback(() => {
|
|
if (state.resolve) state.resolve(null);
|
|
|
|
setState((prev) => ({ ...prev, open: false, resolve: null }));
|
|
}, [state.resolve]);
|
|
|
|
const handleValueChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setState((prev) => ({ ...prev, value: e.target.value }));
|
|
}, []);
|
|
|
|
const handleKeyDown = React.useCallback(
|
|
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (e.key === "Enter") handleConfirm();
|
|
},
|
|
[handleConfirm],
|
|
);
|
|
|
|
const contextValue = React.useMemo<PromptContextType>(() => ({ prompt }), [prompt]);
|
|
|
|
return (
|
|
<PromptContext.Provider value={contextValue}>
|
|
{children}
|
|
|
|
<AlertDialog open={state.open} onOpenChange={(open) => !open && handleCancel()}>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>{state.title}</AlertDialogTitle>
|
|
<AlertDialogDescription className={cn(!state.description && "sr-only")}>
|
|
{state.description}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
|
|
<Input
|
|
ref={inputRef}
|
|
value={state.value}
|
|
onKeyDown={handleKeyDown}
|
|
onChange={handleValueChange}
|
|
{...state.inputProps}
|
|
/>
|
|
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel onClick={handleCancel}>{cancelText}</AlertDialogCancel>
|
|
<AlertDialogAction onClick={handleConfirm}>{confirmText}</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</PromptContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function usePrompt() {
|
|
const context = React.use(PromptContext);
|
|
|
|
if (!context) {
|
|
throw new Error("usePrompt must be used within a <PromptDialogProvider />.");
|
|
}
|
|
|
|
return context.prompt;
|
|
}
|