fix(react/dialogs): some type errors

This commit is contained in:
Elian Doran 2025-08-06 18:38:52 +03:00
parent edd18b53d0
commit bde270b73f
No known key found for this signature in database
12 changed files with 75 additions and 28 deletions

View File

@ -13,6 +13,7 @@ import note_autocomplete, { Suggestion } from "../../services/note_autocomplete"
import type { default as TextTypeWidget } from "../type_widgets/editable_text.js";
import { logError } from "../../services/ws";
import FormGroup from "../react/FormGroup.js";
import { refToJQuerySelector } from "../react/react_utils";
type LinkType = "reference-link" | "external-link" | "hyper-link";
@ -26,7 +27,7 @@ function AddLinkDialogComponent({ text: _text, textTypeWidget }: AddLinkDialogPr
const [ linkTitle, setLinkTitle ] = useState("");
const hasSelection = textTypeWidget?.hasSelection();
const [ linkType, setLinkType ] = useState<LinkType>(hasSelection ? "hyper-link" : "reference-link");
const [ suggestion, setSuggestion ] = useState<Suggestion>(null);
const [ suggestion, setSuggestion ] = useState<Suggestion | null>(null);
async function setDefaultLinkTitle(noteId: string) {
const noteTitle = await tree.getNoteTitle(noteId);
@ -60,7 +61,7 @@ function AddLinkDialogComponent({ text: _text, textTypeWidget }: AddLinkDialogPr
}, [suggestion]);
function onShown() {
const $autocompleteEl = $(autocompleteRef.current);
const $autocompleteEl = refToJQuerySelector(autocompleteRef);
if (!text) {
note_autocomplete.showRecentNotes($autocompleteEl);
} else {
@ -74,11 +75,11 @@ function AddLinkDialogComponent({ text: _text, textTypeWidget }: AddLinkDialogPr
}
function onSubmit() {
if (suggestion.notePath) {
if (suggestion?.notePath) {
// Handle note link
closeActiveDialog();
textTypeWidget?.addLink(suggestion.notePath, linkType === "reference-link" ? null : linkTitle);
} else if (suggestion.externalLink) {
} else if (suggestion?.externalLink) {
// Handle external link
closeActiveDialog();
textTypeWidget?.addLink(suggestion.externalLink, linkTitle, true);

View File

@ -17,15 +17,19 @@ import toast from "../../services/toast";
import NoteList from "../react/NoteList";
interface CloneToDialogProps {
clonedNoteIds: string[];
clonedNoteIds?: string[];
}
function CloneToDialogComponent({ clonedNoteIds }: CloneToDialogProps) {
const [ prefix, setPrefix ] = useState("");
const [ suggestion, setSuggestion ] = useState<Suggestion>(null);
const [ suggestion, setSuggestion ] = useState<Suggestion | null>(null);
const autoCompleteRef = useRef<HTMLInputElement>(null);
function onSubmit() {
if (!clonedNoteIds) {
return;
}
const notePath = suggestion?.notePath;
if (!notePath) {
logError(t("clone_to.no_path_to_clone_to"));
@ -64,7 +68,7 @@ function CloneToDialogComponent({ clonedNoteIds }: CloneToDialogProps) {
export default class CloneToDialog extends ReactBasicWidget {
private props: CloneToDialogProps;
private props: CloneToDialogProps = {};
get component() {
return <CloneToDialogComponent {...this.props} />;
@ -75,7 +79,7 @@ export default class CloneToDialog extends ReactBasicWidget {
noteIds = [appContext.tabManager.getActiveContextNoteId() ?? ""];
}
const clonedNoteIds = [];
const clonedNoteIds: string[] = [];
for (const noteId of noteIds) {
if (!clonedNoteIds.includes(noteId)) {

View File

@ -20,6 +20,7 @@ function ConfirmDialogComponent({ title, message, callback, lastElementToFocus,
return (
<Modal
className="confirm-dialog"
title={title ?? t("confirm.confirmation")}
size="md"
zIndex={2000}
@ -93,7 +94,7 @@ export default class ConfirmDialog extends ReactBasicWidget {
private showDialog(title: string | null, message: MessageType, callback: ConfirmDialogCallback, isConfirmDeleteNoteBox: boolean) {
this.props = {
title: title,
title: title ?? undefined,
message: (typeof message === "object" && "length" in message ? message[0] : message),
lastElementToFocus: (document.activeElement as HTMLElement),
callback,

View File

@ -94,7 +94,7 @@ function DeletedNotes({ noteIdsToBeDeleted }: { noteIdsToBeDeleted: DeleteNotesP
useEffect(() => {
froca.getNotes(noteIdsToBeDeleted).then(async (notes: FNote[]) => {
const noteLinks = [];
const noteLinks: string[] = [];
for (const note of notes) {
noteLinks.push((await link.createLink(note.noteId, { showNotePath: true })).html());
@ -121,7 +121,9 @@ function BrokenRelations({ brokenRelations }: { brokenRelations: DeleteNotesPrev
const [ notesWithBrokenRelations, setNotesWithBrokenRelations ] = useState<BrokenRelationData[]>([]);
useEffect(() => {
const noteIds = brokenRelations.map(relation => relation.noteId);
const noteIds = brokenRelations
.map(relation => relation.noteId)
.filter(noteId => noteId) as string[];
froca.getNotes(noteIds).then(async (notes) => {
const notesWithBrokenRelations: BrokenRelationData[] = [];
for (const attr of brokenRelations) {
@ -142,7 +144,7 @@ function BrokenRelations({ brokenRelations }: { brokenRelations: DeleteNotesPrev
{brokenRelations.map((_, index) => {
return (
<li key={index}>
<span dangerouslySetInnerHTML={{ __html: t("delete_notes.deleted_relation_text", notesWithBrokenRelations[index]) }} />
<span dangerouslySetInnerHTML={{ __html: t("delete_notes.deleted_relation_text", notesWithBrokenRelations[index] as unknown as Record<string, string>) }} />
</li>
);
})}

View File

@ -1,5 +1,5 @@
import { useRef } from "react";
import { openDialog } from "../../services/dialog.js";
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
import { t } from "../../services/i18n.js";
import utils from "../../services/utils.js";
import Button from "../react/Button.js";
@ -31,7 +31,7 @@ function IncorrectCpuArchDialogComponent() {
}
}}/>
<Button text={t("cpu_arch_warning.continue_anyway")}
onClick={() => openDialog(null)} />
onClick={() => closeActiveDialog()} />
</>}
>
<p>{utils.isMac() ? t("cpu_arch_warning.message_macos") : t("cpu_arch_warning.message_windows")}</p>

View File

@ -6,6 +6,7 @@ import Modal from "../react/Modal";
import { t } from "../../services/i18n";
import Button from "../react/Button";
import { useRef } from "preact/compat";
import { RawHtmlBlock } from "../react/RawHtml";
interface ShowInfoDialogProps {
message?: string | HTMLElement;
@ -31,7 +32,7 @@ function ShowInfoDialogComponent({ message, callback, lastElementToFocus }: Show
onClick={() => closeActiveDialog()}
/>}
>
<div className="info-dialog-content" dangerouslySetInnerHTML={{ __html: message ?? "" }}></div>
<RawHtmlBlock className="info-dialog-content" html={message} />
</Modal>);
}

View File

@ -8,6 +8,7 @@ import { useEffect, useRef, useState } from "preact/hooks";
import note_autocomplete, { Suggestion } from "../../services/note_autocomplete";
import appContext from "../../components/app_context";
import commandRegistry from "../../services/command_registry";
import { refToJQuerySelector } from "../react/react_utils";
const KEEP_LAST_SEARCH_FOR_X_SECONDS = 120;
@ -37,7 +38,7 @@ function JumpToNoteDialogComponent({ mode }: JumpToNoteDialogProps) {
}
function onShown() {
const $autoComplete = $(autocompleteRef.current);
const $autoComplete = refToJQuerySelector(autocompleteRef);
switch (mode) {
case "last-search":
// Fall-through if there is no text, in order to display the recent notes.
@ -85,7 +86,7 @@ function JumpToNoteDialogComponent({ mode }: JumpToNoteDialogProps) {
export default class JumpToNoteDialog extends ReactBasicWidget {
private lastOpenedTs: number;
private lastOpenedTs?: number;
private props: JumpToNoteDialogProps = {
mode: "last-search"
};

View File

@ -7,6 +7,7 @@ import { Modal as BootstrapModal } from "bootstrap";
import ReactBasicWidget from "../react/ReactBasicWidget";
import FormTextBox from "../react/FormTextBox";
import FormGroup from "../react/FormGroup";
import { refToJQuerySelector } from "../react/react_utils";
// JQuery here is maintained for compatibility with existing code.
interface ShownCallbackData {
@ -44,10 +45,10 @@ function PromptDialogComponent({ title, message, shown: shownCallback, callback
modalRef={modalRef} formRef={formRef}
onShown={() => {
shownCallback?.({
$dialog: $(modalRef.current),
$question: $(labelRef.current),
$answer: $(answerRef.current),
$form: $(formRef.current) as JQuery<HTMLFormElement>
$dialog: refToJQuerySelector(modalRef),
$question: refToJQuerySelector(labelRef),
$answer: refToJQuerySelector(answerRef),
$form: refToJQuerySelector(formRef)
});
answerRef.current?.focus();
}}
@ -72,7 +73,7 @@ function PromptDialogComponent({ title, message, shown: shownCallback, callback
export default class PromptDialog extends ReactBasicWidget {
private props: PromptDialogProps;
private props: PromptDialogProps = {};
get component() {
return <PromptDialogComponent {...this.props} />;

View File

@ -180,7 +180,7 @@ function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: Revisi
switch (revisionItem.type) {
case "text": {
const contentRef = useRef<HTMLDivElement>();
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (contentRef.current?.querySelector("span.math-tex")) {
renderMathInElement(contentRef.current, { trust: true });

View File

@ -2,7 +2,7 @@ import { ComponentChildren, RefObject } from "preact";
interface FormGroupProps {
labelRef?: RefObject<HTMLLabelElement>;
label: string;
label?: string;
title?: string;
className?: string;
children: ComponentChildren;
@ -14,7 +14,7 @@ export default function FormGroup({ label, title, className, children, descripti
<div className={`form-group ${className}`} title={title}
style={{ "margin-bottom": "15px" }}>
<label style={{ width: "100%" }} ref={labelRef}>
<div style={{ "margin-bottom": "10px" }}>{label}</div>
{label && <div style={{ "margin-bottom": "10px" }}>{label}</div> }
{children}
</label>

View File

@ -1,7 +1,28 @@
interface RawHtmlProps {
html: string;
className?: string;
html: string | HTMLElement;
}
export default function RawHtml({ html }: RawHtmlProps) {
return <span dangerouslySetInnerHTML={{ __html: html }} />;
export default function RawHtml({ className, html }: RawHtmlProps) {
return <span
className={className}
dangerouslySetInnerHTML={getHtml(html)}
/>;
}
export function RawHtmlBlock({ className, html }: RawHtmlProps) {
return <div
className={className}
dangerouslySetInnerHTML={getHtml(html)}
/>
}
function getHtml(html: string | HTMLElement) {
if (typeof html !== "string") {
html = html.outerHTML;
}
return {
__html: html
};
}

View File

@ -0,0 +1,15 @@
import type { RefObject } from "preact";
/**
* Takes in a React ref and returns a corresponding JQuery selector.
*
* @param ref the React ref from which to obtain the jQuery selector.
* @returns the corresponding jQuery selector.
*/
export function refToJQuerySelector<T extends HTMLElement>(ref: RefObject<T> | null): JQuery<T> {
if (ref?.current) {
return $(ref.current);
} else {
return $();
}
}