mirror of
https://github.com/zadam/trilium.git
synced 2025-11-01 10:55:55 +01:00
feat(react/floating_buttons): port in-app help button
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { byBookType, byNoteType } from "./help_button.js";
|
import { byBookType, byNoteType } from "./in_app_help.js";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import type { HiddenSubtreeItem } from "@triliumnext/commons";
|
import type { HiddenSubtreeItem } from "@triliumnext/commons";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
@@ -25,7 +25,7 @@ describe("Help button", () => {
|
|||||||
...Object.values(byBookType)
|
...Object.values(byBookType)
|
||||||
].filter((noteId) => noteId) as string[];
|
].filter((noteId) => noteId) as string[];
|
||||||
|
|
||||||
const metaPath = path.resolve(path.join(__dirname, "../../../../server/src/assets/doc_notes/en/User Guide/!!!meta.json"));
|
const metaPath = path.resolve(path.join(__dirname, "../../../server/src/assets/doc_notes/en/User Guide/!!!meta.json"));
|
||||||
const meta: HiddenSubtreeItem[] = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
const meta: HiddenSubtreeItem[] = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
||||||
const allNoteIds = new Set(getNoteIds(meta));
|
const allNoteIds = new Set(getNoteIds(meta));
|
||||||
|
|
||||||
43
apps/client/src/services/in_app_help.ts
Normal file
43
apps/client/src/services/in_app_help.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { NoteType } from "@triliumnext/commons";
|
||||||
|
import { ViewTypeOptions } from "./note_list_renderer";
|
||||||
|
import { FNote } from "./frontend_script_entrypoint";
|
||||||
|
|
||||||
|
export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
|
||||||
|
canvas: null,
|
||||||
|
code: null,
|
||||||
|
contentWidget: null,
|
||||||
|
doc: null,
|
||||||
|
file: null,
|
||||||
|
image: null,
|
||||||
|
launcher: null,
|
||||||
|
mermaid: null,
|
||||||
|
mindMap: null,
|
||||||
|
noteMap: null,
|
||||||
|
relationMap: null,
|
||||||
|
render: null,
|
||||||
|
search: null,
|
||||||
|
text: null,
|
||||||
|
webView: null,
|
||||||
|
aiChat: null
|
||||||
|
};
|
||||||
|
|
||||||
|
export const byBookType: Record<ViewTypeOptions, string | null> = {
|
||||||
|
list: "mULW0Q3VojwY",
|
||||||
|
grid: "8QqnMzx393bx",
|
||||||
|
calendar: "xWbu3jpNWapp",
|
||||||
|
table: "2FvYrpmOXm29",
|
||||||
|
geoMap: "81SGnPGMk7Xc",
|
||||||
|
board: "CtBQqbwXDx1w"
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getHelpUrlForNote(note: FNote | null | undefined) {
|
||||||
|
if (note && note.type !== "book" && byNoteType[note.type]) {
|
||||||
|
return byNoteType[note.type];
|
||||||
|
} else if (note?.hasLabel("calendarRoot")) {
|
||||||
|
return "l0tKav7yLHGF";
|
||||||
|
} else if (note?.hasLabel("textSnippet")) {
|
||||||
|
return "pwc194wlRzcH";
|
||||||
|
} else if (note && note.type === "book") {
|
||||||
|
return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import "./FloatingButtons.css";
|
import "./FloatingButtons.css";
|
||||||
import Button from "./react/Button";
|
import Button from "./react/Button";
|
||||||
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumEvents, useTriliumOption, useTriliumOptionBool } from "./react/hooks";
|
import { useNoteContext, useNoteProperty, useTriliumEvent, useTriliumEvents } from "./react/hooks";
|
||||||
import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
||||||
import { ParentComponent } from "./react/react_utils";
|
import { ParentComponent } from "./react/react_utils";
|
||||||
import attributes from "../services/attributes";
|
import attributes from "../services/attributes";
|
||||||
import { EventData, EventNames } from "../components/app_context";
|
import { EventData, EventNames } from "../components/app_context";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import NoteContext from "../components/note_context";
|
|||||||
import FNote from "../entities/fnote";
|
import FNote from "../entities/fnote";
|
||||||
import ActionButton, { ActionButtonProps } from "./react/ActionButton";
|
import ActionButton, { ActionButtonProps } from "./react/ActionButton";
|
||||||
import { useNoteLabelBoolean, useTriliumOption } from "./react/hooks";
|
import { useNoteLabelBoolean, useTriliumOption } from "./react/hooks";
|
||||||
import { useEffect, useRef, useState } from "preact/hooks";
|
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||||
import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils";
|
import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils";
|
||||||
import server from "../services/server";
|
import server from "../services/server";
|
||||||
import { SaveSqlConsoleResponse } from "@triliumnext/commons";
|
import { SaveSqlConsoleResponse } from "@triliumnext/commons";
|
||||||
@@ -15,6 +15,7 @@ import { copyImageReferenceToClipboard } from "../services/image";
|
|||||||
import tree from "../services/tree";
|
import tree from "../services/tree";
|
||||||
import protected_session_holder from "../services/protected_session_holder";
|
import protected_session_holder from "../services/protected_session_holder";
|
||||||
import options from "../services/options";
|
import options from "../services/options";
|
||||||
|
import { getHelpUrlForNote } from "../services/in_app_help";
|
||||||
|
|
||||||
export interface FloatingButtonDefinition {
|
export interface FloatingButtonDefinition {
|
||||||
component: (context: FloatingButtonContext) => VNode;
|
component: (context: FloatingButtonContext) => VNode;
|
||||||
@@ -104,6 +105,10 @@ export const FLOATING_BUTTON_DEFINITIONS: FloatingButtonDefinition[] = [
|
|||||||
isEnabled: ({ note, noteContext }) =>
|
isEnabled: ({ note, noteContext }) =>
|
||||||
["mermaid", "mindMap"].includes(note?.type ?? "")
|
["mermaid", "mindMap"].includes(note?.type ?? "")
|
||||||
&& note?.isContentAvailable() && noteContext?.viewScope?.viewMode === "default"
|
&& note?.isContentAvailable() && noteContext?.viewScope?.viewMode === "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: InAppHelpButton,
|
||||||
|
isEnabled: ({ note }) => !!getHelpUrlForNote(note)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -303,4 +308,16 @@ function ExportImageButtons({ triggerEvent }: FloatingButtonContext) {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function InAppHelpButton({ note }: FloatingButtonContext) {
|
||||||
|
const helpUrl = getHelpUrlForNote(note);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FloatingButton
|
||||||
|
icon="bx bx-help-circle"
|
||||||
|
text={t("help-button.title")}
|
||||||
|
onClick={() => helpUrl && openInAppHelpFromUrl(helpUrl)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import { type EventData } from "../../components/app_context.js";
|
|
||||||
import type FNote from "../../entities/fnote.js";
|
|
||||||
import type { NoteType } from "../../entities/fnote.js";
|
|
||||||
import { t } from "../../services/i18n.js";
|
|
||||||
import type { ViewTypeOptions } from "../../services/note_list_renderer.js";
|
|
||||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
|
||||||
<button class="open-contextual-help-button" title="${t("help-button.title")}">
|
|
||||||
<span class="bx bx-help-circle"></span>
|
|
||||||
</button>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
|
|
||||||
canvas: null,
|
|
||||||
code: null,
|
|
||||||
contentWidget: null,
|
|
||||||
doc: null,
|
|
||||||
file: null,
|
|
||||||
image: null,
|
|
||||||
launcher: null,
|
|
||||||
mermaid: null,
|
|
||||||
mindMap: null,
|
|
||||||
noteMap: null,
|
|
||||||
relationMap: null,
|
|
||||||
render: null,
|
|
||||||
search: null,
|
|
||||||
text: null,
|
|
||||||
webView: null,
|
|
||||||
aiChat: null
|
|
||||||
};
|
|
||||||
|
|
||||||
export const byBookType: Record<ViewTypeOptions, string | null> = {
|
|
||||||
list: "mULW0Q3VojwY",
|
|
||||||
grid: "8QqnMzx393bx",
|
|
||||||
calendar: "xWbu3jpNWapp",
|
|
||||||
table: "2FvYrpmOXm29",
|
|
||||||
geoMap: "81SGnPGMk7Xc",
|
|
||||||
board: "CtBQqbwXDx1w"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class ContextualHelpButton extends NoteContextAwareWidget {
|
|
||||||
|
|
||||||
isEnabled() {
|
|
||||||
if (!super.isEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!ContextualHelpButton.#getUrlToOpen(this.note);
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
this.$widget = $(TPL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static #getUrlToOpen(note: FNote | null | undefined) {
|
|
||||||
if (note && note.type !== "book" && byNoteType[note.type]) {
|
|
||||||
return byNoteType[note.type];
|
|
||||||
} else if (note?.hasLabel("calendarRoot")) {
|
|
||||||
return "l0tKav7yLHGF";
|
|
||||||
} else if (note?.hasLabel("textSnippet")) {
|
|
||||||
return "pwc194wlRzcH";
|
|
||||||
} else if (note && note.type === "book") {
|
|
||||||
return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async refreshWithNote(note: FNote | null | undefined): Promise<void> {
|
|
||||||
this.$widget.attr("data-in-app-help", ContextualHelpButton.#getUrlToOpen(this.note) ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
|
||||||
if (this.note?.type === "book" && loadResults.getAttributeRows().find((attr) => attr.noteId === this.noteId && attr.name === "viewType")) {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user