mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
feat(react/dialogs): port prompt
This commit is contained in:
parent
b3c81ce5f2
commit
bde4545afc
@ -244,7 +244,7 @@
|
||||
"prompt": {
|
||||
"title": "提示",
|
||||
"close": "关闭",
|
||||
"ok": "确定 <kbd>回车</kbd>",
|
||||
"ok": "确定",
|
||||
"defaultTitle": "提示"
|
||||
},
|
||||
"protected_session_password": {
|
||||
|
@ -244,7 +244,7 @@
|
||||
"prompt": {
|
||||
"title": "Prompt",
|
||||
"close": "Schließen",
|
||||
"ok": "OK <kbd>Eingabe</kbd>",
|
||||
"ok": "OK",
|
||||
"defaultTitle": "Prompt"
|
||||
},
|
||||
"protected_session_password": {
|
||||
|
@ -250,7 +250,7 @@
|
||||
"prompt": {
|
||||
"title": "Prompt",
|
||||
"close": "Close",
|
||||
"ok": "OK <kbd>enter</kbd>",
|
||||
"ok": "OK",
|
||||
"defaultTitle": "Prompt"
|
||||
},
|
||||
"protected_session_password": {
|
||||
|
@ -247,7 +247,7 @@
|
||||
"prompt": {
|
||||
"title": "Aviso",
|
||||
"close": "Cerrar",
|
||||
"ok": "Aceptar <kbd>enter</kbd>",
|
||||
"ok": "Aceptar",
|
||||
"defaultTitle": "Aviso"
|
||||
},
|
||||
"protected_session_password": {
|
||||
|
@ -244,7 +244,7 @@
|
||||
"prompt": {
|
||||
"title": "Prompt",
|
||||
"close": "Fermer",
|
||||
"ok": "OK <kbd>entrer</kbd>",
|
||||
"ok": "OK",
|
||||
"defaultTitle": "Prompt"
|
||||
},
|
||||
"protected_session_password": {
|
||||
|
@ -970,7 +970,7 @@
|
||||
},
|
||||
"prompt": {
|
||||
"defaultTitle": "Aviz",
|
||||
"ok": "OK <kbd>enter</kbd>",
|
||||
"ok": "OK",
|
||||
"title": "Aviz",
|
||||
"close": "Închide"
|
||||
},
|
||||
|
@ -222,7 +222,7 @@
|
||||
},
|
||||
"prompt": {
|
||||
"title": "提示",
|
||||
"ok": "確定 <kbd>Enter</kbd>",
|
||||
"ok": "確定",
|
||||
"defaultTitle": "提示"
|
||||
},
|
||||
"protected_session_password": {
|
||||
|
@ -1,115 +0,0 @@
|
||||
import { openDialog } from "../../services/dialog.js";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import BasicWidget from "../basic_widget.js";
|
||||
import { Modal } from "bootstrap";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="prompt-dialog modal mx-auto" tabindex="-1" role="dialog" style="z-index: 2000;">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<form class="prompt-dialog-form">
|
||||
<div class="modal-header">
|
||||
<h5 class="prompt-title modal-title">${t("prompt.title")}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("prompt.close")}"></button>
|
||||
</div>
|
||||
<div class="modal-body"></div>
|
||||
<div class="modal-footer">
|
||||
<button class="prompt-dialog-ok-button btn btn-primary btn-sm">${t("prompt.ok")}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
interface ShownCallbackData {
|
||||
$dialog: JQuery<HTMLElement>;
|
||||
$question: JQuery<HTMLElement> | null;
|
||||
$answer: JQuery<HTMLElement> | null;
|
||||
$form: JQuery<HTMLElement>;
|
||||
}
|
||||
|
||||
export interface PromptDialogOptions {
|
||||
title?: string;
|
||||
message?: string;
|
||||
defaultValue?: string;
|
||||
shown?: PromptShownDialogCallback;
|
||||
callback?: (value: string | null) => void;
|
||||
}
|
||||
|
||||
export type PromptShownDialogCallback = ((callback: ShownCallbackData) => void) | null;
|
||||
|
||||
export default class PromptDialog extends BasicWidget {
|
||||
private resolve?: ((value: string | null) => void) | undefined | null;
|
||||
private shownCb?: PromptShownDialogCallback | null;
|
||||
|
||||
private modal!: Modal;
|
||||
private $dialogBody!: JQuery<HTMLElement>;
|
||||
private $question!: JQuery<HTMLElement> | null;
|
||||
private $answer!: JQuery<HTMLElement> | null;
|
||||
private $form!: JQuery<HTMLElement>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.resolve = null;
|
||||
this.shownCb = null;
|
||||
}
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.modal = Modal.getOrCreateInstance(this.$widget[0]);
|
||||
this.$dialogBody = this.$widget.find(".modal-body");
|
||||
this.$form = this.$widget.find(".prompt-dialog-form");
|
||||
this.$question = null;
|
||||
this.$answer = null;
|
||||
|
||||
this.$widget.on("shown.bs.modal", () => {
|
||||
if (this.shownCb) {
|
||||
this.shownCb({
|
||||
$dialog: this.$widget,
|
||||
$question: this.$question,
|
||||
$answer: this.$answer,
|
||||
$form: this.$form
|
||||
});
|
||||
}
|
||||
|
||||
this.$answer?.trigger("focus").select();
|
||||
});
|
||||
|
||||
this.$widget.on("hidden.bs.modal", () => {
|
||||
if (this.resolve) {
|
||||
this.resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
this.$form.on("submit", (e) => {
|
||||
e.preventDefault();
|
||||
if (this.resolve) {
|
||||
this.resolve(this.$answer?.val() as string);
|
||||
}
|
||||
|
||||
this.modal.hide();
|
||||
});
|
||||
}
|
||||
|
||||
showPromptDialogEvent({ title, message, defaultValue, shown, callback }: PromptDialogOptions) {
|
||||
this.shownCb = shown;
|
||||
this.resolve = callback;
|
||||
|
||||
this.$widget.find(".prompt-title").text(title || t("prompt.defaultTitle"));
|
||||
|
||||
this.$question = $("<label>")
|
||||
.prop("for", "prompt-dialog-answer")
|
||||
.text(message || "");
|
||||
|
||||
this.$answer = $("<input>")
|
||||
.prop("type", "text")
|
||||
.prop("id", "prompt-dialog-answer")
|
||||
.addClass("form-control")
|
||||
.val(defaultValue || "");
|
||||
|
||||
this.$dialogBody.empty().append($("<div>").addClass("form-group").append(this.$question).append(this.$answer));
|
||||
|
||||
openDialog(this.$widget, false);
|
||||
}
|
||||
}
|
87
apps/client/src/widgets/dialogs/prompt.tsx
Normal file
87
apps/client/src/widgets/dialogs/prompt.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import { useRef, useState } from "preact/hooks";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import Button from "../react/Button";
|
||||
import Modal from "../react/Modal";
|
||||
import { Modal as BootstrapModal } from "bootstrap";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
import FormTextBox from "../react/FormTextBox";
|
||||
import FormGroup from "../react/FormGroup";
|
||||
|
||||
// JQuery here is maintained for compatibility with existing code.
|
||||
interface ShownCallbackData {
|
||||
$dialog: JQuery<HTMLDivElement>;
|
||||
$question: JQuery<HTMLLabelElement> | null;
|
||||
$answer: JQuery<HTMLElement> | null;
|
||||
$form: JQuery<HTMLFormElement>;
|
||||
}
|
||||
|
||||
export type PromptShownDialogCallback = ((callback: ShownCallbackData) => void) | null;
|
||||
|
||||
export interface PromptDialogOptions {
|
||||
title?: string;
|
||||
message?: string;
|
||||
defaultValue?: string;
|
||||
shown?: PromptShownDialogCallback;
|
||||
callback?: (value: string | null) => void;
|
||||
}
|
||||
|
||||
interface PromptDialogProps extends PromptDialogOptions { }
|
||||
|
||||
function PromptDialogComponent({ title, message, shown: shownCallback, callback }: PromptDialogProps) {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
const labelRef = useRef<HTMLLabelElement>(null);
|
||||
const answerRef = useRef<HTMLInputElement>(null);
|
||||
const [ value, setValue ] = useState("");
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="prompt-dialog"
|
||||
title={title ?? t("prompt.title")}
|
||||
size="lg"
|
||||
zIndex={2000}
|
||||
modalRef={modalRef} formRef={formRef}
|
||||
onShown={() => {
|
||||
shownCallback?.({
|
||||
$dialog: $(modalRef.current),
|
||||
$question: $(labelRef.current),
|
||||
$answer: $(answerRef.current),
|
||||
$form: $(formRef.current) as JQuery<HTMLFormElement>
|
||||
});
|
||||
answerRef.current?.focus();
|
||||
}}
|
||||
onSubmit={() => {
|
||||
const modal = BootstrapModal.getOrCreateInstance(modalRef.current!);
|
||||
modal.hide();
|
||||
|
||||
callback?.(value);
|
||||
}}
|
||||
onHidden={() => callback?.(null)}
|
||||
footer={<Button text={t("prompt.ok")} keyboardShortcut="Enter" primary />}
|
||||
>
|
||||
<FormGroup label={message} labelRef={labelRef}>
|
||||
<FormTextBox
|
||||
name="prompt-dialog-answer"
|
||||
inputRef={answerRef}
|
||||
currentValue={value} onChange={setValue} />
|
||||
</FormGroup>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default class PromptDialog extends ReactBasicWidget {
|
||||
|
||||
private props: PromptDialogProps;
|
||||
|
||||
get component() {
|
||||
return <PromptDialogComponent {...this.props} />;
|
||||
}
|
||||
|
||||
showPromptDialogEvent(props: PromptDialogOptions) {
|
||||
this.props = props;
|
||||
this.doRender();
|
||||
openDialog(this.$widget, false);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { ComponentChildren } from "preact";
|
||||
import { ComponentChildren, RefObject } from "preact";
|
||||
|
||||
interface FormGroupProps {
|
||||
labelRef?: RefObject<HTMLLabelElement>;
|
||||
label: string;
|
||||
title?: string;
|
||||
className?: string;
|
||||
@ -8,10 +9,10 @@ interface FormGroupProps {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export default function FormGroup({ label, title, className, children, description }: FormGroupProps) {
|
||||
export default function FormGroup({ label, title, className, children, description, labelRef }: FormGroupProps) {
|
||||
return (
|
||||
<div className={`form-group ${className}`} title={title}>
|
||||
<label style={{ width: "100%" }}>
|
||||
<label style={{ width: "100%" }} ref={labelRef}>
|
||||
{label}
|
||||
{children}
|
||||
</label>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { HTMLInputTypeAttribute } from "preact/compat";
|
||||
import { HTMLInputTypeAttribute, RefObject } from "preact/compat";
|
||||
|
||||
interface FormTextBoxProps {
|
||||
id?: string;
|
||||
@ -8,13 +8,15 @@ interface FormTextBoxProps {
|
||||
className?: string;
|
||||
autoComplete?: string;
|
||||
onChange?(newValue: string): void;
|
||||
inputRef?: RefObject<HTMLInputElement>;
|
||||
}
|
||||
|
||||
export default function FormTextBox({ id, type, name, className, currentValue, onChange, autoComplete }: FormTextBoxProps) {
|
||||
export default function FormTextBox({ id, type, name, className, currentValue, onChange, autoComplete, inputRef }: FormTextBoxProps) {
|
||||
return (
|
||||
<input
|
||||
ref={inputRef}
|
||||
type={type ?? "text"}
|
||||
className={`form-control ${className}`}
|
||||
className={`form-control ${className ?? ""}`}
|
||||
id={id}
|
||||
name={name}
|
||||
value={currentValue}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef } from "preact/hooks";
|
||||
import { t } from "../../services/i18n";
|
||||
import { ComponentChildren } from "preact";
|
||||
import type { CSSProperties } from "preact/compat";
|
||||
import type { CSSProperties, RefObject } from "preact/compat";
|
||||
|
||||
interface ModalProps {
|
||||
className: string;
|
||||
@ -29,10 +29,20 @@ interface ModalProps {
|
||||
/** Called when the modal is hidden, either via close button, backdrop click or submit. */
|
||||
onHidden?: () => void;
|
||||
helpPageId?: string;
|
||||
/**
|
||||
* Gives access to the underlying modal element. This is useful for manipulating the modal directly
|
||||
* or for attaching event listeners.
|
||||
*/
|
||||
modalRef?: RefObject<HTMLDivElement>;
|
||||
/**
|
||||
* Gives access to the underlying form element of the modal. This is only set if `onSubmit` is provided.
|
||||
*/
|
||||
formRef?: RefObject<HTMLFormElement>;
|
||||
}
|
||||
|
||||
export default function Modal({ children, className, size, title, footer, footerAlignment, onShown, onSubmit, helpPageId, maxWidth, zIndex, scrollable, onHidden: onHidden }: ModalProps) {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
export default function Modal({ children, className, size, title, footer, footerAlignment, onShown, onSubmit, helpPageId, maxWidth, zIndex, scrollable, onHidden: onHidden, modalRef: _modalRef, formRef: _formRef }: ModalProps) {
|
||||
const modalRef = _modalRef ?? useRef<HTMLDivElement>(null);
|
||||
const formRef = _formRef ?? useRef<HTMLFormElement>(null);
|
||||
|
||||
if (onShown || onHidden) {
|
||||
useEffect(() => {
|
||||
@ -84,7 +94,7 @@ export default function Modal({ children, className, size, title, footer, footer
|
||||
</div>
|
||||
|
||||
{onSubmit ? (
|
||||
<form onSubmit={(e) => {
|
||||
<form ref={formRef} onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
onSubmit();
|
||||
}}>
|
||||
|
Loading…
x
Reference in New Issue
Block a user