summaryrefslogtreecommitdiff
path: root/examples/server/webui/src/components/ModalProvider.tsx
diff options
context:
space:
mode:
authorfirecoperana <xuqiaowei1124@gmail.com>2025-07-20 05:33:55 -0500
committerGitHub <noreply@github.com>2025-07-20 12:33:55 +0200
commitd44c2d3f5aeab25a9405896f48a36082cee5d8ac (patch)
tree6768d4d8c72fb0b5c7b4a5a4187d2eccb292f0ad /examples/server/webui/src/components/ModalProvider.tsx
parentf989fb03bd12752ad6e93717ca4bd298d5001d99 (diff)
Webui: New Features for Conversations, Settings, and Chat Messages (#618)main
* Webui: add Rename/Upload conversation in header and sidebar webui: don't change modified date when renaming conversation * webui: add a preset feature to the settings #14649 * webui: Add editing assistant messages #13522 Webui: keep the following message while editing assistance response. webui: change icon to edit message * webui: DB import and export #14347 * webui: Wrap long numbers instead of infinite horizontal scroll (#14062) fix sidebar being covered by main content #14082 --------- Co-authored-by: firecoperana <firecoperana>
Diffstat (limited to 'examples/server/webui/src/components/ModalProvider.tsx')
-rw-r--r--examples/server/webui/src/components/ModalProvider.tsx151
1 files changed, 151 insertions, 0 deletions
diff --git a/examples/server/webui/src/components/ModalProvider.tsx b/examples/server/webui/src/components/ModalProvider.tsx
new file mode 100644
index 00000000..f2ebf8e0
--- /dev/null
+++ b/examples/server/webui/src/components/ModalProvider.tsx
@@ -0,0 +1,151 @@
+import React, { createContext, useState, useContext } from 'react';
+
+type ModalContextType = {
+ showConfirm: (message: string) => Promise<boolean>;
+ showPrompt: (
+ message: string,
+ defaultValue?: string
+ ) => Promise<string | undefined>;
+ showAlert: (message: string) => Promise<void>;
+};
+const ModalContext = createContext<ModalContextType>(null!);
+
+interface ModalState<T> {
+ isOpen: boolean;
+ message: string;
+ defaultValue?: string;
+ resolve: ((value: T) => void) | null;
+}
+
+export function ModalProvider({ children }: { children: React.ReactNode }) {
+ const [confirmState, setConfirmState] = useState<ModalState<boolean>>({
+ isOpen: false,
+ message: '',
+ resolve: null,
+ });
+ const [promptState, setPromptState] = useState<
+ ModalState<string | undefined>
+ >({ isOpen: false, message: '', resolve: null });
+ const [alertState, setAlertState] = useState<ModalState<void>>({
+ isOpen: false,
+ message: '',
+ resolve: null,
+ });
+ const inputRef = React.useRef<HTMLInputElement>(null);
+
+ const showConfirm = (message: string): Promise<boolean> => {
+ return new Promise((resolve) => {
+ setConfirmState({ isOpen: true, message, resolve });
+ });
+ };
+
+ const showPrompt = (
+ message: string,
+ defaultValue?: string
+ ): Promise<string | undefined> => {
+ return new Promise((resolve) => {
+ setPromptState({ isOpen: true, message, defaultValue, resolve });
+ });
+ };
+
+ const showAlert = (message: string): Promise<void> => {
+ return new Promise((resolve) => {
+ setAlertState({ isOpen: true, message, resolve });
+ });
+ };
+
+ const handleConfirm = (result: boolean) => {
+ confirmState.resolve?.(result);
+ setConfirmState({ isOpen: false, message: '', resolve: null });
+ };
+
+ const handlePrompt = (result?: string) => {
+ promptState.resolve?.(result);
+ setPromptState({ isOpen: false, message: '', resolve: null });
+ };
+
+ const handleAlertClose = () => {
+ alertState.resolve?.();
+ setAlertState({ isOpen: false, message: '', resolve: null });
+ };
+
+ return (
+ <ModalContext.Provider value={{ showConfirm, showPrompt, showAlert }}>
+ {children}
+
+ {/* Confirm Modal */}
+ {confirmState.isOpen && (
+ <dialog className="modal modal-open z-[1100]">
+ <div className="modal-box">
+ <h3 className="font-bold text-lg">{confirmState.message}</h3>
+ <div className="modal-action">
+ <button
+ className="btn btn-ghost"
+ onClick={() => handleConfirm(false)}
+ >
+ Cancel
+ </button>
+ <button
+ className="btn btn-error"
+ onClick={() => handleConfirm(true)}
+ >
+ Confirm
+ </button>
+ </div>
+ </div>
+ </dialog>
+ )}
+
+ {/* Prompt Modal */}
+ {promptState.isOpen && (
+ <dialog className="modal modal-open z-[1100]">
+ <div className="modal-box">
+ <h3 className="font-bold text-lg">{promptState.message}</h3>
+ <input
+ type="text"
+ className="input input-bordered w-full mt-2"
+ defaultValue={promptState.defaultValue}
+ ref={inputRef}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ handlePrompt((e.target as HTMLInputElement).value);
+ }
+ }}
+ />
+ <div className="modal-action">
+ <button className="btn btn-ghost" onClick={() => handlePrompt()}>
+ Cancel
+ </button>
+ <button
+ className="btn btn-primary"
+ onClick={() => handlePrompt(inputRef.current?.value)}
+ >
+ Submit
+ </button>
+ </div>
+ </div>
+ </dialog>
+ )}
+
+ {/* Alert Modal */}
+ {alertState.isOpen && (
+ <dialog className="modal modal-open z-[1100]">
+ <div className="modal-box">
+ <h3 className="font-bold text-lg">{alertState.message}</h3>
+ <div className="modal-action">
+ <button className="btn" onClick={handleAlertClose}>
+ OK
+ </button>
+ </div>
+ </div>
+ </dialog>
+ )}
+ </ModalContext.Provider>
+ );
+}
+
+export function useModals() {
+ const context = useContext(ModalContext);
+ if (!context) throw new Error('useModals must be used within ModalProvider');
+ return context;
+}