mirror of
https://github.com/zadam/trilium.git
synced 2025-11-01 19:05:59 +01:00
refactor(react/dialogs): use shown everywhere
This commit is contained in:
@@ -30,6 +30,7 @@ import type CodeMirror from "@triliumnext/codemirror";
|
|||||||
import { StartupChecks } from "./startup_checks.js";
|
import { StartupChecks } from "./startup_checks.js";
|
||||||
import type { CreateNoteOpts } from "../services/note_create.js";
|
import type { CreateNoteOpts } from "../services/note_create.js";
|
||||||
import { ColumnComponent } from "tabulator-tables";
|
import { ColumnComponent } from "tabulator-tables";
|
||||||
|
import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx";
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
getRootWidget: (appContext: AppContext) => RootWidget;
|
getRootWidget: (appContext: AppContext) => RootWidget;
|
||||||
@@ -370,6 +371,9 @@ export type CommandMappings = {
|
|||||||
};
|
};
|
||||||
refreshTouchBar: CommandData;
|
refreshTouchBar: CommandData;
|
||||||
reloadTextEditor: CommandData;
|
reloadTextEditor: CommandData;
|
||||||
|
chooseNoteType: CommandData & {
|
||||||
|
callback: ChooseNoteTypeCallback
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type EventMappings = {
|
type EventMappings = {
|
||||||
|
|||||||
@@ -109,8 +109,6 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
|
|||||||
|
|
||||||
async function chooseNoteType() {
|
async function chooseNoteType() {
|
||||||
return new Promise<ChooseNoteTypeResponse>((res) => {
|
return new Promise<ChooseNoteTypeResponse>((res) => {
|
||||||
// TODO: Remove ignore after callback for chooseNoteType is defined in app_context.ts
|
|
||||||
//@ts-ignore
|
|
||||||
appContext.triggerCommand("chooseNoteType", { callback: res });
|
appContext.triggerCommand("chooseNoteType", { callback: res });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,51 @@
|
|||||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||||
import Modal from "../react/Modal";
|
import Modal from "../react/Modal";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
import { closeActiveDialog } from "../../services/dialog";
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import FormCheckbox from "../react/FormCheckbox";
|
import FormCheckbox from "../react/FormCheckbox";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
interface ConfirmDialogProps {
|
interface ConfirmDialogProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
message?: string | HTMLElement;
|
message?: string | HTMLElement;
|
||||||
callback?: ConfirmDialogCallback;
|
callback?: ConfirmDialogCallback;
|
||||||
lastElementToFocus?: HTMLElement | null;
|
|
||||||
isConfirmDeleteNoteBox?: boolean;
|
isConfirmDeleteNoteBox?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConfirmDialogComponent({ title, message, callback, lastElementToFocus, isConfirmDeleteNoteBox }: ConfirmDialogProps) {
|
function ConfirmDialogComponent() {
|
||||||
|
const [ opts, setOpts ] = useState<ConfirmDialogProps>();
|
||||||
const [ confirmed, setConfirmed ] = useState<boolean>(false);
|
const [ confirmed, setConfirmed ] = useState<boolean>(false);
|
||||||
const [ isDeleteNoteChecked, setIsDeleteNoteChecked ] = useState<boolean>(false);
|
const [ isDeleteNoteChecked, setIsDeleteNoteChecked ] = useState(false);
|
||||||
|
const [ shown, setShown ] = useState(false);
|
||||||
|
|
||||||
|
function showDialog(title: string | null, message: MessageType, callback: ConfirmDialogCallback, isConfirmDeleteNoteBox: boolean) {
|
||||||
|
setOpts({
|
||||||
|
title: title ?? undefined,
|
||||||
|
message: (typeof message === "object" && "length" in message ? message[0] : message),
|
||||||
|
callback,
|
||||||
|
isConfirmDeleteNoteBox
|
||||||
|
});
|
||||||
|
setShown(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
useTriliumEvent("showConfirmDialog", ({ message, callback }) => showDialog(null, message, callback, false));
|
||||||
|
useTriliumEvent("showConfirmDeleteNoteBoxWithNoteDialog", ({ title, callback }) => showDialog(title, t("confirm.are_you_sure_remove_note", { title: title }), callback, true));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="confirm-dialog"
|
className="confirm-dialog"
|
||||||
title={title ?? t("confirm.confirmation")}
|
title={opts?.title ?? t("confirm.confirmation")}
|
||||||
size="md"
|
size="md"
|
||||||
zIndex={2000}
|
zIndex={2000}
|
||||||
scrollable={true}
|
scrollable={true}
|
||||||
onHidden={() => {
|
onHidden={() => {
|
||||||
callback?.({
|
opts?.callback?.({
|
||||||
confirmed,
|
confirmed,
|
||||||
isDeleteNoteChecked
|
isDeleteNoteChecked
|
||||||
});
|
});
|
||||||
lastElementToFocus?.focus();
|
setShown(false);
|
||||||
}}
|
}}
|
||||||
footer={<>
|
footer={<>
|
||||||
<Button text={t("confirm.cancel")} onClick={() => closeActiveDialog()} />
|
<Button text={t("confirm.cancel")} onClick={() => closeActiveDialog()} />
|
||||||
@@ -39,12 +54,13 @@ function ConfirmDialogComponent({ title, message, callback, lastElementToFocus,
|
|||||||
closeActiveDialog();
|
closeActiveDialog();
|
||||||
}} />
|
}} />
|
||||||
</>}
|
</>}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
{!message || typeof message === "string"
|
{!opts?.message || typeof opts?.message === "string"
|
||||||
? <div>{(message as string) ?? ""}</div>
|
? <div>{(opts?.message as string) ?? ""}</div>
|
||||||
: <div dangerouslySetInnerHTML={{ __html: message.outerHTML ?? "" }} />}
|
: <div dangerouslySetInnerHTML={{ __html: opts?.message.outerHTML ?? "" }} />}
|
||||||
|
|
||||||
{isConfirmDeleteNoteBox && (
|
{opts?.isConfirmDeleteNoteBox && (
|
||||||
<FormCheckbox
|
<FormCheckbox
|
||||||
name="confirm-dialog-delete-note"
|
name="confirm-dialog-delete-note"
|
||||||
label={t("confirm.also_delete_note")}
|
label={t("confirm.also_delete_note")}
|
||||||
@@ -77,31 +93,8 @@ export interface ConfirmWithTitleOptions {
|
|||||||
|
|
||||||
export default class ConfirmDialog extends ReactBasicWidget {
|
export default class ConfirmDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props: ConfirmDialogProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <ConfirmDialogComponent {...this.props} />;
|
return <ConfirmDialogComponent />;
|
||||||
}
|
|
||||||
|
|
||||||
showConfirmDialogEvent({ message, callback }: ConfirmWithMessageOptions) {
|
|
||||||
this.showDialog(null, message, callback, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
showConfirmDeleteNoteBoxWithNoteDialogEvent({ title, callback }: ConfirmWithTitleOptions) {
|
|
||||||
const message = t("confirm.are_you_sure_remove_note", { title: title });
|
|
||||||
this.showDialog(title, message, callback, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private showDialog(title: string | null, message: MessageType, callback: ConfirmDialogCallback, isConfirmDeleteNoteBox: boolean) {
|
|
||||||
this.props = {
|
|
||||||
title: title ?? undefined,
|
|
||||||
message: (typeof message === "object" && "length" in message ? message[0] : message),
|
|
||||||
lastElementToFocus: (document.activeElement as HTMLElement),
|
|
||||||
callback,
|
|
||||||
isConfirmDeleteNoteBox
|
|
||||||
};
|
|
||||||
this.doRender();
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useRef, useState } from "preact/hooks";
|
import { useRef, useState } from "preact/hooks";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
|
import { closeActiveDialog } from "../../services/dialog.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import FormCheckbox from "../react/FormCheckbox.js";
|
import FormCheckbox from "../react/FormCheckbox.js";
|
||||||
import Modal from "../react/Modal.js";
|
import Modal from "../react/Modal.js";
|
||||||
@@ -12,6 +12,7 @@ import FNote from "../../entities/fnote.js";
|
|||||||
import link from "../../services/link.js";
|
import link from "../../services/link.js";
|
||||||
import Button from "../react/Button.jsx";
|
import Button from "../react/Button.jsx";
|
||||||
import Alert from "../react/Alert.jsx";
|
import Alert from "../react/Alert.jsx";
|
||||||
|
import useTriliumEvent from "../react/hooks.jsx";
|
||||||
|
|
||||||
export interface ResolveOptions {
|
export interface ResolveOptions {
|
||||||
proceed: boolean;
|
proceed: boolean;
|
||||||
@@ -25,22 +26,28 @@ interface ShowDeleteNotesDialogOpts {
|
|||||||
forceDeleteAllClones?: boolean;
|
forceDeleteAllClones?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ShowDeleteNotesDialogProps extends ShowDeleteNotesDialogOpts { }
|
|
||||||
|
|
||||||
interface BrokenRelationData {
|
interface BrokenRelationData {
|
||||||
note: string;
|
note: string;
|
||||||
relation: string;
|
relation: string;
|
||||||
source: string;
|
source: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DeleteNotesDialogComponent({ forceDeleteAllClones, branchIdsToDelete, callback }: ShowDeleteNotesDialogProps) {
|
function DeleteNotesDialogComponent() {
|
||||||
|
const [ opts, setOpts ] = useState<ShowDeleteNotesDialogOpts>({});
|
||||||
const [ deleteAllClones, setDeleteAllClones ] = useState(false);
|
const [ deleteAllClones, setDeleteAllClones ] = useState(false);
|
||||||
const [ eraseNotes, setEraseNotes ] = useState(!!forceDeleteAllClones);
|
const [ eraseNotes, setEraseNotes ] = useState(!!opts.forceDeleteAllClones);
|
||||||
const [ brokenRelations, setBrokenRelations ] = useState<DeleteNotesPreview["brokenRelations"]>([]);
|
const [ brokenRelations, setBrokenRelations ] = useState<DeleteNotesPreview["brokenRelations"]>([]);
|
||||||
const [ noteIdsToBeDeleted, setNoteIdsToBeDeleted ] = useState<DeleteNotesPreview["noteIdsToBeDeleted"]>([]);
|
const [ noteIdsToBeDeleted, setNoteIdsToBeDeleted ] = useState<DeleteNotesPreview["noteIdsToBeDeleted"]>([]);
|
||||||
|
const [ shown, setShown ] = useState(false);
|
||||||
const okButtonRef = useRef<HTMLButtonElement>(null);
|
const okButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
useTriliumEvent("showDeleteNotesDialog", (opts) => {
|
||||||
|
setOpts(opts);
|
||||||
|
setShown(true);
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const { branchIdsToDelete, forceDeleteAllClones } = opts;
|
||||||
if (!branchIdsToDelete || branchIdsToDelete.length === 0) {
|
if (!branchIdsToDelete || branchIdsToDelete.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -52,7 +59,7 @@ function DeleteNotesDialogComponent({ forceDeleteAllClones, branchIdsToDelete, c
|
|||||||
setBrokenRelations(response.brokenRelations);
|
setBrokenRelations(response.brokenRelations);
|
||||||
setNoteIdsToBeDeleted(response.noteIdsToBeDeleted);
|
setNoteIdsToBeDeleted(response.noteIdsToBeDeleted);
|
||||||
});
|
});
|
||||||
}, [ branchIdsToDelete, deleteAllClones, forceDeleteAllClones ]);
|
}, [ opts ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -61,24 +68,28 @@ function DeleteNotesDialogComponent({ forceDeleteAllClones, branchIdsToDelete, c
|
|||||||
scrollable
|
scrollable
|
||||||
title={t("delete_notes.delete_notes_preview")}
|
title={t("delete_notes.delete_notes_preview")}
|
||||||
onShown={() => okButtonRef.current?.focus()}
|
onShown={() => okButtonRef.current?.focus()}
|
||||||
onHidden={() => callback?.({ proceed: false })}
|
onHidden={() => {
|
||||||
|
opts.callback?.({ proceed: false })
|
||||||
|
setShown(false);
|
||||||
|
}}
|
||||||
footer={<>
|
footer={<>
|
||||||
<Button text={t("delete_notes.cancel")}
|
<Button text={t("delete_notes.cancel")}
|
||||||
onClick={() => closeActiveDialog()} />
|
onClick={() => closeActiveDialog()} />
|
||||||
<Button text={t("delete_notes.ok")} primary
|
<Button text={t("delete_notes.ok")} primary
|
||||||
buttonRef={okButtonRef}
|
buttonRef={okButtonRef}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
callback?.({ proceed: true, deleteAllClones, eraseNotes });
|
opts.callback?.({ proceed: true, deleteAllClones, eraseNotes });
|
||||||
closeActiveDialog();
|
closeActiveDialog();
|
||||||
}} />
|
}} />
|
||||||
</>}
|
</>}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
<FormCheckbox name="delete-all-clones" label={t("delete_notes.delete_all_clones_description")}
|
<FormCheckbox name="delete-all-clones" label={t("delete_notes.delete_all_clones_description")}
|
||||||
currentValue={deleteAllClones} onChange={setDeleteAllClones}
|
currentValue={deleteAllClones} onChange={setDeleteAllClones}
|
||||||
/>
|
/>
|
||||||
<FormCheckbox
|
<FormCheckbox
|
||||||
name="erase-notes" label={t("delete_notes.erase_notes_warning")}
|
name="erase-notes" label={t("delete_notes.erase_notes_warning")}
|
||||||
disabled={forceDeleteAllClones}
|
disabled={opts.forceDeleteAllClones}
|
||||||
currentValue={eraseNotes} onChange={setEraseNotes}
|
currentValue={eraseNotes} onChange={setEraseNotes}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -161,20 +172,8 @@ function BrokenRelations({ brokenRelations }: { brokenRelations: DeleteNotesPrev
|
|||||||
|
|
||||||
export default class DeleteNotesDialog extends ReactBasicWidget {
|
export default class DeleteNotesDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props: ShowDeleteNotesDialogProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <DeleteNotesDialogComponent {...this.props} />;
|
return <DeleteNotesDialogComponent />;
|
||||||
}
|
|
||||||
|
|
||||||
async showDeleteNotesDialogEvent({ branchIdsToDelete, callback, forceDeleteAllClones }: ShowDeleteNotesDialogOpts) {
|
|
||||||
this.props = {
|
|
||||||
branchIdsToDelete,
|
|
||||||
callback,
|
|
||||||
forceDeleteAllClones
|
|
||||||
};
|
|
||||||
this.doRender();
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { EventData } from "../../components/app_context";
|
import { closeActiveDialog } from "../../services/dialog";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import tree from "../../services/tree";
|
import tree from "../../services/tree";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
@@ -13,6 +12,7 @@ import toastService, { ToastOptions } from "../../services/toast";
|
|||||||
import utils from "../../services/utils";
|
import utils from "../../services/utils";
|
||||||
import open from "../../services/open";
|
import open from "../../services/open";
|
||||||
import froca from "../../services/froca";
|
import froca from "../../services/froca";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
interface ExportDialogProps {
|
interface ExportDialogProps {
|
||||||
branchId?: string | null;
|
branchId?: string | null;
|
||||||
@@ -20,24 +20,48 @@ interface ExportDialogProps {
|
|||||||
defaultType?: "subtree" | "single";
|
defaultType?: "subtree" | "single";
|
||||||
}
|
}
|
||||||
|
|
||||||
function ExportDialogComponent({ branchId, noteTitle, defaultType }: ExportDialogProps) {
|
function ExportDialogComponent() {
|
||||||
const [ exportType, setExportType ] = useState<string>(defaultType ?? "subtree");
|
const [ opts, setOpts ] = useState<ExportDialogProps>();
|
||||||
const [ subtreeFormat, setSubtreeFormat ] = useState<string>("html");
|
const [ exportType, setExportType ] = useState(opts?.defaultType ?? "subtree");
|
||||||
const [ singleFormat, setSingleFormat ] = useState<string>("html");
|
const [ subtreeFormat, setSubtreeFormat ] = useState("html");
|
||||||
const [ opmlVersion, setOpmlVersion ] = useState<string>("2.0");
|
const [ singleFormat, setSingleFormat ] = useState("html");
|
||||||
|
const [ opmlVersion, setOpmlVersion ] = useState("2.0");
|
||||||
|
const [ shown, setShown ] = useState(false);
|
||||||
|
|
||||||
return (branchId &&
|
useTriliumEvent("showExportDialog", async ({ notePath, defaultType }) => {
|
||||||
|
const { noteId, parentNoteId } = tree.getNoteIdAndParentIdFromUrl(notePath);
|
||||||
|
if (!parentNoteId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const branchId = await froca.getBranchId(parentNoteId, noteId);
|
||||||
|
|
||||||
|
setOpts({
|
||||||
|
noteTitle: noteId && await tree.getNoteTitle(noteId),
|
||||||
|
defaultType,
|
||||||
|
branchId
|
||||||
|
});
|
||||||
|
setShown(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="export-dialog"
|
className="export-dialog"
|
||||||
title={`${t("export.export_note_title")} ${noteTitle ?? ""}`}
|
title={`${t("export.export_note_title")} ${opts?.noteTitle ?? ""}`}
|
||||||
size="lg"
|
size="lg"
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
|
if (!opts || !opts.branchId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const format = (exportType === "subtree" ? subtreeFormat : singleFormat);
|
const format = (exportType === "subtree" ? subtreeFormat : singleFormat);
|
||||||
const version = (format === "opml" ? opmlVersion : "1.0");
|
const version = (format === "opml" ? opmlVersion : "1.0");
|
||||||
exportBranch(branchId, exportType, format, version);
|
exportBranch(opts.branchId, exportType, format, version);
|
||||||
closeActiveDialog();
|
closeActiveDialog();
|
||||||
}}
|
}}
|
||||||
|
onHidden={() => setShown(false)}
|
||||||
footer={<Button className="export-button" text={t("export.export")} primary />}
|
footer={<Button className="export-button" text={t("export.export")} primary />}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
|
|
||||||
<FormRadioGroup
|
<FormRadioGroup
|
||||||
@@ -104,29 +128,10 @@ function ExportDialogComponent({ branchId, noteTitle, defaultType }: ExportDialo
|
|||||||
|
|
||||||
export default class ExportDialog extends ReactBasicWidget {
|
export default class ExportDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props: ExportDialogProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <ExportDialogComponent {...this.props} />
|
return <ExportDialogComponent />
|
||||||
}
|
}
|
||||||
|
|
||||||
async showExportDialogEvent({ notePath, defaultType }: EventData<"showExportDialog">) {
|
|
||||||
const { noteId, parentNoteId } = tree.getNoteIdAndParentIdFromUrl(notePath);
|
|
||||||
if (!parentNoteId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const branchId = await froca.getBranchId(parentNoteId, noteId);
|
|
||||||
|
|
||||||
this.props = {
|
|
||||||
noteTitle: noteId && await tree.getNoteTitle(noteId),
|
|
||||||
defaultType,
|
|
||||||
branchId
|
|
||||||
};
|
|
||||||
this.doRender();
|
|
||||||
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportBranch(branchId: string, type: string, format: string, version: string) {
|
function exportBranch(branchId: string, type: string, format: string, version: string) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { EventData } from "../../components/app_context";
|
import { closeActiveDialog } from "../../services/dialog";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import tree from "../../services/tree";
|
import tree from "../../services/tree";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
@@ -11,13 +10,11 @@ import Modal from "../react/Modal";
|
|||||||
import RawHtml from "../react/RawHtml";
|
import RawHtml from "../react/RawHtml";
|
||||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||||
import importService, { UploadFilesOptions } from "../../services/import";
|
import importService, { UploadFilesOptions } from "../../services/import";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
interface ImportDialogComponentProps {
|
function ImportDialogComponent() {
|
||||||
parentNoteId?: string;
|
const [ parentNoteId, setParentNoteId ] = useState<string>();
|
||||||
noteTitle?: string;
|
const [ noteTitle, setNoteTitle ] = useState<string>();
|
||||||
}
|
|
||||||
|
|
||||||
function ImportDialogComponent({ parentNoteId, noteTitle }: ImportDialogComponentProps) {
|
|
||||||
const [ files, setFiles ] = useState<FileList | null>(null);
|
const [ files, setFiles ] = useState<FileList | null>(null);
|
||||||
const [ safeImport, setSafeImport ] = useState(true);
|
const [ safeImport, setSafeImport ] = useState(true);
|
||||||
const [ explodeArchives, setExplodeArchives ] = useState(true);
|
const [ explodeArchives, setExplodeArchives ] = useState(true);
|
||||||
@@ -25,14 +22,21 @@ function ImportDialogComponent({ parentNoteId, noteTitle }: ImportDialogCompone
|
|||||||
const [ textImportedAsText, setTextImportedAsText ] = useState(true);
|
const [ textImportedAsText, setTextImportedAsText ] = useState(true);
|
||||||
const [ codeImportedAsCode, setCodeImportedAsCode ] = useState(true);
|
const [ codeImportedAsCode, setCodeImportedAsCode ] = useState(true);
|
||||||
const [ replaceUnderscoresWithSpaces, setReplaceUnderscoresWithSpaces ] = useState(true);
|
const [ replaceUnderscoresWithSpaces, setReplaceUnderscoresWithSpaces ] = useState(true);
|
||||||
|
const [ shown, setShown ] = useState(false);
|
||||||
|
|
||||||
return (parentNoteId &&
|
useTriliumEvent("showImportDialog", ({ noteId }) => {
|
||||||
|
setParentNoteId(noteId);
|
||||||
|
tree.getNoteTitle(noteId).then(setNoteTitle);
|
||||||
|
setShown(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="import-dialog"
|
className="import-dialog"
|
||||||
size="lg"
|
size="lg"
|
||||||
title={t("import.importIntoNote")}
|
title={t("import.importIntoNote")}
|
||||||
onSubmit={async () => {
|
onSubmit={async () => {
|
||||||
if (!files) {
|
if (!files || !parentNoteId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +52,9 @@ function ImportDialogComponent({ parentNoteId, noteTitle }: ImportDialogCompone
|
|||||||
closeActiveDialog();
|
closeActiveDialog();
|
||||||
await importService.uploadFiles("notes", parentNoteId, Array.from(files), options);
|
await importService.uploadFiles("notes", parentNoteId, Array.from(files), options);
|
||||||
}}
|
}}
|
||||||
|
onHidden={() => setShown(false)}
|
||||||
footer={<Button text={t("import.import")} primary disabled={!files} />}
|
footer={<Button text={t("import.import")} primary disabled={!files} />}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
<FormGroup label={t("import.chooseImportFile")} description={<>{t("import.importDescription")} <strong>{ noteTitle }</strong></>}>
|
<FormGroup label={t("import.chooseImportFile")} description={<>{t("import.importDescription")} <strong>{ noteTitle }</strong></>}>
|
||||||
<FormFileUpload multiple onChange={setFiles} />
|
<FormFileUpload multiple onChange={setFiles} />
|
||||||
@@ -86,20 +92,8 @@ function ImportDialogComponent({ parentNoteId, noteTitle }: ImportDialogCompone
|
|||||||
|
|
||||||
export default class ImportDialog extends ReactBasicWidget {
|
export default class ImportDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props?: ImportDialogComponentProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <ImportDialogComponent {...this.props} />
|
return <ImportDialogComponent />
|
||||||
}
|
|
||||||
|
|
||||||
async showImportDialogEvent({ noteId }: EventData<"showImportDialog">) {
|
|
||||||
this.props = {
|
|
||||||
parentNoteId: noteId,
|
|
||||||
noteTitle: await tree.getNoteTitle(noteId)
|
|
||||||
}
|
|
||||||
this.doRender();
|
|
||||||
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useRef, useState } from "preact/compat";
|
import { useRef, useState } from "preact/compat";
|
||||||
import type { EventData } from "../../components/app_context";
|
import { closeActiveDialog } from "../../services/dialog";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import FormGroup from "../react/FormGroup";
|
import FormGroup from "../react/FormGroup";
|
||||||
import FormRadioGroup from "../react/FormRadioGroup";
|
import FormRadioGroup from "../react/FormRadioGroup";
|
||||||
@@ -12,24 +11,30 @@ import { Suggestion, triggerRecentNotes } from "../../services/note_autocomplete
|
|||||||
import tree from "../../services/tree";
|
import tree from "../../services/tree";
|
||||||
import froca from "../../services/froca";
|
import froca from "../../services/froca";
|
||||||
import EditableTextTypeWidget from "../type_widgets/editable_text";
|
import EditableTextTypeWidget from "../type_widgets/editable_text";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
interface IncludeNoteDialogProps {
|
function IncludeNoteDialogComponent() {
|
||||||
textTypeWidget?: EditableTextTypeWidget;
|
const [textTypeWidget, setTextTypeWidget] = useState<EditableTextTypeWidget>();
|
||||||
}
|
|
||||||
|
|
||||||
function IncludeNoteDialogComponent({ textTypeWidget }: IncludeNoteDialogProps) {
|
|
||||||
const [suggestion, setSuggestion] = useState<Suggestion | null>(null);
|
const [suggestion, setSuggestion] = useState<Suggestion | null>(null);
|
||||||
const [boxSize, setBoxSize] = useState("medium");
|
const [boxSize, setBoxSize] = useState("medium");
|
||||||
|
const [shown, setShown] = useState(false);
|
||||||
|
|
||||||
|
useTriliumEvent("showIncludeNoteDialog", ({ textTypeWidget }) => {
|
||||||
|
setTextTypeWidget(textTypeWidget);
|
||||||
|
setShown(true);
|
||||||
|
});
|
||||||
|
|
||||||
const autoCompleteRef = useRef<HTMLInputElement>(null);
|
const autoCompleteRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
return (textTypeWidget &&
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="include-note-dialog"
|
className="include-note-dialog"
|
||||||
title={t("include_note.dialog_title")}
|
title={t("include_note.dialog_title")}
|
||||||
size="lg"
|
size="lg"
|
||||||
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
|
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
|
||||||
|
onHidden={() => setShown(false)}
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
if (!suggestion?.notePath) {
|
if (!suggestion?.notePath || !textTypeWidget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +42,7 @@ function IncludeNoteDialogComponent({ textTypeWidget }: IncludeNoteDialogProps)
|
|||||||
includeNote(suggestion.notePath, textTypeWidget);
|
includeNote(suggestion.notePath, textTypeWidget);
|
||||||
}}
|
}}
|
||||||
footer={<Button text={t("include_note.button_include")} keyboardShortcut="Enter" />}
|
footer={<Button text={t("include_note.button_include")} keyboardShortcut="Enter" />}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
<FormGroup label={t("include_note.label_note")}>
|
<FormGroup label={t("include_note.label_note")}>
|
||||||
<NoteAutocomplete
|
<NoteAutocomplete
|
||||||
@@ -66,16 +72,8 @@ function IncludeNoteDialogComponent({ textTypeWidget }: IncludeNoteDialogProps)
|
|||||||
|
|
||||||
export default class IncludeNoteDialog extends ReactBasicWidget {
|
export default class IncludeNoteDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props: IncludeNoteDialogProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <IncludeNoteDialogComponent {...this.props} />;
|
return <IncludeNoteDialogComponent />;
|
||||||
}
|
|
||||||
|
|
||||||
async showIncludeNoteDialogEvent({ textTypeWidget }: EventData<"showIncludeDialog">) {
|
|
||||||
this.props = { textTypeWidget };
|
|
||||||
this.doRender();
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
import { EventData } from "../../components/app_context";
|
import { EventData } from "../../components/app_context";
|
||||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||||
import { ConfirmDialogCallback } from "./confirm";
|
import { closeActiveDialog } from "../../services/dialog";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
|
||||||
import Modal from "../react/Modal";
|
import Modal from "../react/Modal";
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
import { useRef } from "preact/compat";
|
import { useRef, useState } from "preact/hooks";
|
||||||
import { RawHtmlBlock } from "../react/RawHtml";
|
import { RawHtmlBlock } from "../react/RawHtml";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
interface ShowInfoDialogProps {
|
function ShowInfoDialogComponent() {
|
||||||
message?: string | HTMLElement;
|
const [ opts, setOpts ] = useState<EventData<"showInfoDialog">>();
|
||||||
callback?: ConfirmDialogCallback;
|
const [ shown, setShown ] = useState(false);
|
||||||
lastElementToFocus?: HTMLElement | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ShowInfoDialogComponent({ message, callback, lastElementToFocus }: ShowInfoDialogProps) {
|
|
||||||
const okButtonRef = useRef<HTMLButtonElement>(null);
|
const okButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
return (message && <Modal
|
useTriliumEvent("showInfoDialog", (opts) => {
|
||||||
|
setOpts(opts);
|
||||||
|
setShown(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (<Modal
|
||||||
className="info-dialog"
|
className="info-dialog"
|
||||||
size="sm"
|
size="sm"
|
||||||
title={t("info.modalTitle")}
|
title={t("info.modalTitle")}
|
||||||
onHidden={() => {
|
onHidden={() => {
|
||||||
callback?.();
|
opts?.callback?.();
|
||||||
lastElementToFocus?.focus();
|
setShown(false);
|
||||||
}}
|
}}
|
||||||
onShown={() => okButtonRef.current?.focus?.()}
|
onShown={() => okButtonRef.current?.focus?.()}
|
||||||
footer={<Button
|
footer={<Button
|
||||||
@@ -31,27 +32,16 @@ function ShowInfoDialogComponent({ message, callback, lastElementToFocus }: Show
|
|||||||
text={t("info.okButton")}
|
text={t("info.okButton")}
|
||||||
onClick={() => closeActiveDialog()}
|
onClick={() => closeActiveDialog()}
|
||||||
/>}
|
/>}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
<RawHtmlBlock className="info-dialog-content" html={message} />
|
<RawHtmlBlock className="info-dialog-content" html={opts?.message ?? ""} />
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class InfoDialog extends ReactBasicWidget {
|
export default class InfoDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props: ShowInfoDialogProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <ShowInfoDialogComponent {...this.props} />;
|
return <ShowInfoDialogComponent />;
|
||||||
}
|
|
||||||
|
|
||||||
showInfoDialogEvent({ message, callback }: EventData<"showInfoDialog">) {
|
|
||||||
this.props = {
|
|
||||||
message: Array.isArray(message) ? message[0] : message,
|
|
||||||
callback,
|
|
||||||
lastElementToFocus: (document.activeElement as HTMLElement)
|
|
||||||
};
|
|
||||||
this.doRender();
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
import { closeActiveDialog } from "../../services/dialog";
|
||||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||||
import Modal from "../react/Modal";
|
import Modal from "../react/Modal";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
@@ -9,20 +9,45 @@ import note_autocomplete, { Suggestion } from "../../services/note_autocomplete"
|
|||||||
import appContext from "../../components/app_context";
|
import appContext from "../../components/app_context";
|
||||||
import commandRegistry from "../../services/command_registry";
|
import commandRegistry from "../../services/command_registry";
|
||||||
import { refToJQuerySelector } from "../react/react_utils";
|
import { refToJQuerySelector } from "../react/react_utils";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
const KEEP_LAST_SEARCH_FOR_X_SECONDS = 120;
|
const KEEP_LAST_SEARCH_FOR_X_SECONDS = 120;
|
||||||
|
|
||||||
type Mode = "last-search" | "recent-notes" | "commands";
|
type Mode = "last-search" | "recent-notes" | "commands";
|
||||||
|
|
||||||
interface JumpToNoteDialogProps {
|
function JumpToNoteDialogComponent() {
|
||||||
mode: Mode;
|
const [ mode, setMode ] = useState<Mode>("last-search");
|
||||||
}
|
const [ lastOpenedTs, setLastOpenedTs ] = useState<number>(0);
|
||||||
|
|
||||||
function JumpToNoteDialogComponent({ mode }: JumpToNoteDialogProps) {
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const autocompleteRef = useRef<HTMLInputElement>(null);
|
const autocompleteRef = useRef<HTMLInputElement>(null);
|
||||||
const [ isCommandMode, setIsCommandMode ] = useState(mode === "commands");
|
const [ isCommandMode, setIsCommandMode ] = useState(mode === "commands");
|
||||||
const [ text, setText ] = useState(isCommandMode ? "> " : "");
|
const [ text, setText ] = useState(isCommandMode ? "> " : "");
|
||||||
|
const [ shown, setShown ] = useState(false);
|
||||||
|
|
||||||
|
async function openDialog(commandMode: boolean) {
|
||||||
|
let newMode: Mode;
|
||||||
|
if (commandMode) {
|
||||||
|
newMode = "commands";
|
||||||
|
} else if (Date.now() - lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) {
|
||||||
|
// if you open the Jump To dialog soon after using it previously, it can often mean that you
|
||||||
|
// actually want to search for the same thing (e.g., you opened the wrong note at first try)
|
||||||
|
// so we'll keep the content.
|
||||||
|
// if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead.
|
||||||
|
newMode = "recent-notes";
|
||||||
|
} else {
|
||||||
|
newMode = "last-search";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode !== newMode) {
|
||||||
|
setMode(newMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
setShown(true);
|
||||||
|
setLastOpenedTs(Date.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
useTriliumEvent("jumpToNote", () => openDialog(false));
|
||||||
|
useTriliumEvent("commandPalette", () => openDialog(true));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsCommandMode(text.startsWith(">"));
|
setIsCommandMode(text.startsWith(">"));
|
||||||
@@ -77,7 +102,9 @@ function JumpToNoteDialogComponent({ mode }: JumpToNoteDialogProps) {
|
|||||||
onChange={onItemSelected}
|
onChange={onItemSelected}
|
||||||
/>}
|
/>}
|
||||||
onShown={onShown}
|
onShown={onShown}
|
||||||
|
onHidden={() => setShown(false)}
|
||||||
footer={!isCommandMode && <Button className="show-in-full-text-button" text={t("jump_to_note.search_button")} keyboardShortcut="Ctrl+Enter" />}
|
footer={!isCommandMode && <Button className="show-in-full-text-button" text={t("jump_to_note.search_button")} keyboardShortcut="Ctrl+Enter" />}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
<div className="algolia-autocomplete-container jump-to-note-results" ref={containerRef}></div>
|
<div className="algolia-autocomplete-container jump-to-note-results" ref={containerRef}></div>
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -86,45 +113,8 @@ function JumpToNoteDialogComponent({ mode }: JumpToNoteDialogProps) {
|
|||||||
|
|
||||||
export default class JumpToNoteDialog extends ReactBasicWidget {
|
export default class JumpToNoteDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private lastOpenedTs?: number;
|
|
||||||
private props: JumpToNoteDialogProps = {
|
|
||||||
mode: "last-search"
|
|
||||||
};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <JumpToNoteDialogComponent {...this.props} />;
|
return <JumpToNoteDialogComponent />;
|
||||||
}
|
|
||||||
|
|
||||||
async openDialog(commandMode = false) {
|
|
||||||
this.lastOpenedTs = Date.now();
|
|
||||||
|
|
||||||
let newMode: Mode;
|
|
||||||
if (commandMode) {
|
|
||||||
newMode = "commands";
|
|
||||||
} else if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) {
|
|
||||||
// if you open the Jump To dialog soon after using it previously, it can often mean that you
|
|
||||||
// actually want to search for the same thing (e.g., you opened the wrong note at first try)
|
|
||||||
// so we'll keep the content.
|
|
||||||
// if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead.
|
|
||||||
newMode = "recent-notes";
|
|
||||||
} else {
|
|
||||||
newMode = "last-search";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.mode !== newMode) {
|
|
||||||
this.props.mode = newMode;
|
|
||||||
this.doRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
async jumpToNoteEvent() {
|
|
||||||
await this.openDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
async commandPaletteEvent() {
|
|
||||||
await this.openDialog(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||||
import Modal from "../react/Modal";
|
import Modal from "../react/Modal";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
import { closeActiveDialog } from "../../services/dialog";
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import FormGroup from "../react/FormGroup";
|
import FormGroup from "../react/FormGroup";
|
||||||
import NoteAutocomplete from "../react/NoteAutocomplete";
|
import NoteAutocomplete from "../react/NoteAutocomplete";
|
||||||
@@ -11,6 +11,7 @@ import { MenuCommandItem, MenuItem } from "../../menus/context_menu";
|
|||||||
import { TreeCommandNames } from "../../menus/tree_context_menu";
|
import { TreeCommandNames } from "../../menus/tree_context_menu";
|
||||||
import { Suggestion } from "../../services/note_autocomplete";
|
import { Suggestion } from "../../services/note_autocomplete";
|
||||||
import Badge from "../react/Badge";
|
import Badge from "../react/Badge";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
export interface ChooseNoteTypeResponse {
|
export interface ChooseNoteTypeResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -19,20 +20,24 @@ export interface ChooseNoteTypeResponse {
|
|||||||
notePath?: string;
|
notePath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Callback = (data: ChooseNoteTypeResponse) => void;
|
export type ChooseNoteTypeCallback = (data: ChooseNoteTypeResponse) => void;
|
||||||
|
|
||||||
const SEPARATOR_TITLE_REPLACEMENTS = [
|
const SEPARATOR_TITLE_REPLACEMENTS = [
|
||||||
t("note_type_chooser.builtin_templates"),
|
t("note_type_chooser.builtin_templates"),
|
||||||
t("note_type_chooser.templates")
|
t("note_type_chooser.templates")
|
||||||
];
|
];
|
||||||
|
|
||||||
interface NoteTypeChooserDialogProps {
|
function NoteTypeChooserDialogComponent() {
|
||||||
callback?: Callback;
|
const [ callback, setCallback ] = useState<ChooseNoteTypeCallback>();
|
||||||
}
|
const [ shown, setShown ] = useState(false);
|
||||||
|
const [ parentNote, setParentNote ] = useState<Suggestion | null>();
|
||||||
function NoteTypeChooserDialogComponent({ callback }: NoteTypeChooserDialogProps) {
|
|
||||||
const [ parentNote, setParentNote ] = useState<Suggestion>();
|
|
||||||
const [ noteTypes, setNoteTypes ] = useState<MenuItem<TreeCommandNames>[]>([]);
|
const [ noteTypes, setNoteTypes ] = useState<MenuItem<TreeCommandNames>[]>([]);
|
||||||
|
|
||||||
|
useTriliumEvent("chooseNoteType", ({ callback }) => {
|
||||||
|
setCallback(() => callback);
|
||||||
|
setShown(true);
|
||||||
|
});
|
||||||
|
|
||||||
if (!noteTypes.length) {
|
if (!noteTypes.length) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
note_types.getNoteTypeItems().then(noteTypes => {
|
note_types.getNoteTypeItems().then(noteTypes => {
|
||||||
@@ -72,7 +77,11 @@ function NoteTypeChooserDialogComponent({ callback }: NoteTypeChooserDialogProps
|
|||||||
size="md"
|
size="md"
|
||||||
zIndex={1100} // note type chooser needs to be higher than other dialogs from which it is triggered, e.g. "add link"
|
zIndex={1100} // note type chooser needs to be higher than other dialogs from which it is triggered, e.g. "add link"
|
||||||
scrollable
|
scrollable
|
||||||
onHidden={() => callback?.({ success: false })}
|
onHidden={() => {
|
||||||
|
callback?.({ success: false });
|
||||||
|
setShown(false);
|
||||||
|
}}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
<FormGroup label={t("note_type_chooser.change_path_prompt")}>
|
<FormGroup label={t("note_type_chooser.change_path_prompt")}>
|
||||||
<NoteAutocomplete
|
<NoteAutocomplete
|
||||||
@@ -114,16 +123,8 @@ function NoteTypeChooserDialogComponent({ callback }: NoteTypeChooserDialogProps
|
|||||||
|
|
||||||
export default class NoteTypeChooserDialog extends ReactBasicWidget {
|
export default class NoteTypeChooserDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props: NoteTypeChooserDialogProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <NoteTypeChooserDialogComponent {...this.props} />
|
return <NoteTypeChooserDialogComponent />
|
||||||
}
|
|
||||||
|
|
||||||
async chooseNoteTypeEvent({ callback }: { callback: Callback }) {
|
|
||||||
this.props = { callback };
|
|
||||||
this.doRender();
|
|
||||||
openDialog(this.$widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useRef, useState } from "preact/hooks";
|
import { useRef, useState } from "preact/hooks";
|
||||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
import Modal from "../react/Modal";
|
import Modal from "../react/Modal";
|
||||||
@@ -8,6 +7,7 @@ import ReactBasicWidget from "../react/ReactBasicWidget";
|
|||||||
import FormTextBox from "../react/FormTextBox";
|
import FormTextBox from "../react/FormTextBox";
|
||||||
import FormGroup from "../react/FormGroup";
|
import FormGroup from "../react/FormGroup";
|
||||||
import { refToJQuerySelector } from "../react/react_utils";
|
import { refToJQuerySelector } from "../react/react_utils";
|
||||||
|
import useTriliumEvent from "../react/hooks";
|
||||||
|
|
||||||
// JQuery here is maintained for compatibility with existing code.
|
// JQuery here is maintained for compatibility with existing code.
|
||||||
interface ShownCallbackData {
|
interface ShownCallbackData {
|
||||||
@@ -27,24 +27,29 @@ export interface PromptDialogOptions {
|
|||||||
callback?: (value: string | null) => void;
|
callback?: (value: string | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PromptDialogProps extends PromptDialogOptions { }
|
function PromptDialogComponent() {
|
||||||
|
|
||||||
function PromptDialogComponent({ title, message, shown: shownCallback, callback }: PromptDialogProps) {
|
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
const labelRef = useRef<HTMLLabelElement>(null);
|
const labelRef = useRef<HTMLLabelElement>(null);
|
||||||
const answerRef = useRef<HTMLInputElement>(null);
|
const answerRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [ opts, setOpts ] = useState<PromptDialogOptions>();
|
||||||
const [ value, setValue ] = useState("");
|
const [ value, setValue ] = useState("");
|
||||||
|
const [ shown, setShown ] = useState(false);
|
||||||
|
|
||||||
|
useTriliumEvent("showPromptDialog", (opts) => {
|
||||||
|
setOpts(opts);
|
||||||
|
setShown(true);
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="prompt-dialog"
|
className="prompt-dialog"
|
||||||
title={title ?? t("prompt.title")}
|
title={opts?.title ?? t("prompt.title")}
|
||||||
size="lg"
|
size="lg"
|
||||||
zIndex={2000}
|
zIndex={2000}
|
||||||
modalRef={modalRef} formRef={formRef}
|
modalRef={modalRef} formRef={formRef}
|
||||||
onShown={() => {
|
onShown={() => {
|
||||||
shownCallback?.({
|
opts?.shown?.({
|
||||||
$dialog: refToJQuerySelector(modalRef),
|
$dialog: refToJQuerySelector(modalRef),
|
||||||
$question: refToJQuerySelector(labelRef),
|
$question: refToJQuerySelector(labelRef),
|
||||||
$answer: refToJQuerySelector(answerRef),
|
$answer: refToJQuerySelector(answerRef),
|
||||||
@@ -56,12 +61,16 @@ function PromptDialogComponent({ title, message, shown: shownCallback, callback
|
|||||||
const modal = BootstrapModal.getOrCreateInstance(modalRef.current!);
|
const modal = BootstrapModal.getOrCreateInstance(modalRef.current!);
|
||||||
modal.hide();
|
modal.hide();
|
||||||
|
|
||||||
callback?.(value);
|
opts?.callback?.(value);
|
||||||
|
}}
|
||||||
|
onHidden={() => {
|
||||||
|
opts?.callback?.(null);
|
||||||
|
setShown(false);
|
||||||
}}
|
}}
|
||||||
onHidden={() => callback?.(null)}
|
|
||||||
footer={<Button text={t("prompt.ok")} keyboardShortcut="Enter" primary />}
|
footer={<Button text={t("prompt.ok")} keyboardShortcut="Enter" primary />}
|
||||||
|
show={shown}
|
||||||
>
|
>
|
||||||
<FormGroup label={message} labelRef={labelRef}>
|
<FormGroup label={opts?.message} labelRef={labelRef}>
|
||||||
<FormTextBox
|
<FormTextBox
|
||||||
name="prompt-dialog-answer"
|
name="prompt-dialog-answer"
|
||||||
inputRef={answerRef}
|
inputRef={answerRef}
|
||||||
@@ -73,16 +82,8 @@ function PromptDialogComponent({ title, message, shown: shownCallback, callback
|
|||||||
|
|
||||||
export default class PromptDialog extends ReactBasicWidget {
|
export default class PromptDialog extends ReactBasicWidget {
|
||||||
|
|
||||||
private props: PromptDialogProps = {};
|
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return <PromptDialogComponent {...this.props} />;
|
return <PromptDialogComponent />;
|
||||||
}
|
|
||||||
|
|
||||||
showPromptDialogEvent(props: PromptDialogOptions) {
|
|
||||||
this.props = props;
|
|
||||||
this.doRender();
|
|
||||||
openDialog(this.$widget, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user