mirror of
https://github.com/zadam/trilium.git
synced 2025-11-12 08:15:52 +01:00
chore(react/ribbon): add menu
This commit is contained in:
@@ -1,25 +1,12 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||||
import server from "../../services/server.js";
|
|
||||||
import contextMenuService from "../../menus/context_menu.js";
|
import contextMenuService from "../../menus/context_menu.js";
|
||||||
import attributeParser, { type Attribute } from "../../services/attribute_parser.js";
|
|
||||||
import { AttributeEditor, type EditorConfig, type ModelElement, type MentionFeed, type ModelNode, type ModelPosition } from "@triliumnext/ckeditor5";
|
import { AttributeEditor, type EditorConfig, type ModelElement, type MentionFeed, type ModelNode, type ModelPosition } from "@triliumnext/ckeditor5";
|
||||||
import froca from "../../services/froca.js";
|
|
||||||
import noteCreateService from "../../services/note_create.js";
|
import noteCreateService from "../../services/note_create.js";
|
||||||
import attributeService from "../../services/attributes.js";
|
import attributeService from "../../services/attributes.js";
|
||||||
import linkService from "../../services/link.js";
|
|
||||||
import type AttributeDetailWidget from "./attribute_detail.js";
|
import type AttributeDetailWidget from "./attribute_detail.js";
|
||||||
import type { CommandData, EventData, EventListener, FilteredCommandNames } from "../../components/app_context.js";
|
import type { CommandData, EventData, EventListener, FilteredCommandNames } from "../../components/app_context.js";
|
||||||
import type { default as FAttribute, AttributeType } from "../../entities/fattribute.js";
|
import type { default as FAttribute, AttributeType } from "../../entities/fattribute.js";
|
||||||
import { escapeQuotes } from "../../services/utils.js";
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
|
||||||
|
|
||||||
<div class="bx bx-plus add-new-attribute-button tn-tool-button" title="${escapeQuotes(t("attribute_editor.add_a_new_attribute"))}"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
type AttributeCommandNames = FilteredCommandNames<CommandData>;
|
|
||||||
|
|
||||||
export default class AttributeEditorWidget extends NoteContextAwareWidget implements EventListener<"entitiesReloaded">, EventListener<"addNewLabel">, EventListener<"addNewRelation"> {
|
export default class AttributeEditorWidget extends NoteContextAwareWidget implements EventListener<"entitiesReloaded">, EventListener<"addNewLabel">, EventListener<"addNewRelation"> {
|
||||||
private attributeDetailWidget: AttributeDetailWidget;
|
private attributeDetailWidget: AttributeDetailWidget;
|
||||||
@@ -48,30 +35,9 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
|
|||||||
|
|
||||||
this.$editor.on("blur", () => setTimeout(() => this.save(), 100)); // Timeout to fix https://github.com/zadam/trilium/issues/4160
|
this.$editor.on("blur", () => setTimeout(() => this.save(), 100)); // Timeout to fix https://github.com/zadam/trilium/issues/4160
|
||||||
|
|
||||||
this.$addNewAttributeButton = this.$widget.find(".add-new-attribute-button");
|
|
||||||
this.$addNewAttributeButton.on("click", (e) => this.addNewAttribute(e));
|
|
||||||
|
|
||||||
this.$saveAttributesButton.on("click", () => this.save());
|
this.$saveAttributesButton.on("click", () => this.save());
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewAttribute(e: JQuery.ClickEvent) {
|
|
||||||
contextMenuService.show<AttributeCommandNames>({
|
|
||||||
x: e.pageX,
|
|
||||||
y: e.pageY,
|
|
||||||
orientation: "left",
|
|
||||||
items: [
|
|
||||||
{ title: t("attribute_editor.add_new_label"), command: "addNewLabel", uiIcon: "bx bx-hash" },
|
|
||||||
{ title: t("attribute_editor.add_new_relation"), command: "addNewRelation", uiIcon: "bx bx-transfer" },
|
|
||||||
{ title: "----" },
|
|
||||||
{ title: t("attribute_editor.add_new_label_definition"), command: "addNewLabelDefinition", uiIcon: "bx bx-empty" },
|
|
||||||
{ title: t("attribute_editor.add_new_relation_definition"), command: "addNewRelationDefinition", uiIcon: "bx bx-empty" }
|
|
||||||
],
|
|
||||||
selectMenuItemHandler: ({ command }) => this.handleAddNewAttributeCommand(command)
|
|
||||||
});
|
|
||||||
// Prevent automatic hiding of the context menu due to the button being clicked.
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
// triggered from keyboard shortcut
|
// triggered from keyboard shortcut
|
||||||
async addNewLabelEvent({ ntxId }: EventData<"addNewLabel">) {
|
async addNewLabelEvent({ ntxId }: EventData<"addNewLabel">) {
|
||||||
if (this.isNoteContext(ntxId)) {
|
if (this.isNoteContext(ntxId)) {
|
||||||
@@ -90,66 +56,6 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleAddNewAttributeCommand(command: AttributeCommandNames | undefined) {
|
|
||||||
// TODO: Not sure what the relation between FAttribute[] and Attribute[] is.
|
|
||||||
const attrs = this.parseAttributes() as FAttribute[];
|
|
||||||
|
|
||||||
if (!attrs) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let type: AttributeType;
|
|
||||||
let name;
|
|
||||||
let value;
|
|
||||||
|
|
||||||
if (command === "addNewLabel") {
|
|
||||||
type = "label";
|
|
||||||
name = "myLabel";
|
|
||||||
value = "";
|
|
||||||
} else if (command === "addNewRelation") {
|
|
||||||
type = "relation";
|
|
||||||
name = "myRelation";
|
|
||||||
value = "";
|
|
||||||
} else if (command === "addNewLabelDefinition") {
|
|
||||||
type = "label";
|
|
||||||
name = "label:myLabel";
|
|
||||||
value = "promoted,single,text";
|
|
||||||
} else if (command === "addNewRelationDefinition") {
|
|
||||||
type = "label";
|
|
||||||
name = "relation:myRelation";
|
|
||||||
value = "promoted,single";
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Incomplete type
|
|
||||||
//@ts-ignore
|
|
||||||
attrs.push({
|
|
||||||
type,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
isInheritable: false
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.renderOwnedAttributes(attrs, false);
|
|
||||||
|
|
||||||
this.$editor.scrollTop(this.$editor[0].scrollHeight);
|
|
||||||
|
|
||||||
const rect = this.$editor[0].getBoundingClientRect();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
// showing a little bit later because there's a conflict with outside click closing the attr detail
|
|
||||||
this.attributeDetailWidget.showAttributeDetail({
|
|
||||||
allAttributes: attrs,
|
|
||||||
attribute: attrs[attrs.length - 1],
|
|
||||||
isOwned: true,
|
|
||||||
x: (rect.left + rect.right) / 2,
|
|
||||||
y: rect.bottom,
|
|
||||||
focus: "name"
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if (this.lastUpdatedNoteId !== this.noteId) {
|
if (this.lastUpdatedNoteId !== this.noteId) {
|
||||||
// https://github.com/zadam/trilium/issues/3090
|
// https://github.com/zadam/trilium/issues/3090
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ interface ActionButtonProps {
|
|||||||
text: string;
|
text: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: () => void;
|
onClick?: (e: MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ActionButton({ text, icon, className, onClick }: ActionButtonProps) {
|
export default function ActionButton({ text, icon, className, onClick }: ActionButtonProps) {
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ import { ParentComponent } from "../../react/react_utils";
|
|||||||
import Component from "../../../components/component";
|
import Component from "../../../components/component";
|
||||||
import link from "../../../services/link";
|
import link from "../../../services/link";
|
||||||
import froca from "../../../services/froca";
|
import froca from "../../../services/froca";
|
||||||
|
import contextMenu from "../../../menus/context_menu";
|
||||||
|
import type { CommandData, FilteredCommandNames } from "../../../components/app_context";
|
||||||
|
import { AttributeType } from "@triliumnext/commons";
|
||||||
|
|
||||||
|
type AttributeCommandNames = FilteredCommandNames<CommandData>;
|
||||||
|
|
||||||
const HELP_TEXT = `
|
const HELP_TEXT = `
|
||||||
<p>${t("attribute_editor.help_text_body1")}</p>
|
<p>${t("attribute_editor.help_text_body1")}</p>
|
||||||
@@ -143,6 +148,65 @@ export default function AttributeEditor({ note, componentId }: { note: FNote, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleAddNewAttributeCommand(command: AttributeCommandNames | undefined) {
|
||||||
|
// TODO: Not sure what the relation between FAttribute[] and Attribute[] is.
|
||||||
|
const attrs = parseAttributes() as FAttribute[];
|
||||||
|
|
||||||
|
if (!attrs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let type: AttributeType;
|
||||||
|
let name;
|
||||||
|
let value;
|
||||||
|
|
||||||
|
if (command === "addNewLabel") {
|
||||||
|
type = "label";
|
||||||
|
name = "myLabel";
|
||||||
|
value = "";
|
||||||
|
} else if (command === "addNewRelation") {
|
||||||
|
type = "relation";
|
||||||
|
name = "myRelation";
|
||||||
|
value = "";
|
||||||
|
} else if (command === "addNewLabelDefinition") {
|
||||||
|
type = "label";
|
||||||
|
name = "label:myLabel";
|
||||||
|
value = "promoted,single,text";
|
||||||
|
} else if (command === "addNewRelationDefinition") {
|
||||||
|
type = "label";
|
||||||
|
name = "relation:myRelation";
|
||||||
|
value = "promoted,single";
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Incomplete type
|
||||||
|
//@ts-ignore
|
||||||
|
attrs.push({
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
isInheritable: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await renderOwnedAttributes(attrs, false);
|
||||||
|
|
||||||
|
// this.$editor.scrollTop(this.$editor[0].scrollHeight);
|
||||||
|
const rect = wrapperRef.current?.getBoundingClientRect();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// showing a little bit later because there's a conflict with outside click closing the attr detail
|
||||||
|
attributeDetailWidget.showAttributeDetail({
|
||||||
|
allAttributes: attrs,
|
||||||
|
attribute: attrs[attrs.length - 1],
|
||||||
|
isOwned: true,
|
||||||
|
x: rect ? (rect.left + rect.right) / 2 : 0,
|
||||||
|
y: rect?.bottom ?? 0,
|
||||||
|
focus: "name"
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
renderOwnedAttributes(note.getOwnedAttributes(), true);
|
renderOwnedAttributes(note.getOwnedAttributes(), true);
|
||||||
}, [ note ]);
|
}, [ note ]);
|
||||||
@@ -228,6 +292,30 @@ export default function AttributeEditor({ note, componentId }: { note: FNote, co
|
|||||||
onClick={save}
|
onClick={save}
|
||||||
/> }
|
/> }
|
||||||
|
|
||||||
|
<ActionButton
|
||||||
|
icon="bx bx-plus"
|
||||||
|
className="add-new-attribute-button"
|
||||||
|
text={escapeQuotes(t("attribute_editor.add_a_new_attribute"))}
|
||||||
|
onClick={(e) => {
|
||||||
|
// Prevent automatic hiding of the context menu due to the button being clicked.
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
contextMenu.show<AttributeCommandNames>({
|
||||||
|
x: e.pageX,
|
||||||
|
y: e.pageY,
|
||||||
|
orientation: "left",
|
||||||
|
items: [
|
||||||
|
{ title: t("attribute_editor.add_new_label"), command: "addNewLabel", uiIcon: "bx bx-hash" },
|
||||||
|
{ title: t("attribute_editor.add_new_relation"), command: "addNewRelation", uiIcon: "bx bx-transfer" },
|
||||||
|
{ title: "----" },
|
||||||
|
{ title: t("attribute_editor.add_new_label_definition"), command: "addNewLabelDefinition", uiIcon: "bx bx-empty" },
|
||||||
|
{ title: t("attribute_editor.add_new_relation_definition"), command: "addNewRelationDefinition", uiIcon: "bx bx-empty" }
|
||||||
|
],
|
||||||
|
selectMenuItemHandler: (item) => handleAddNewAttributeCommand(item.command)
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{ error && (
|
{ error && (
|
||||||
<div className="attribute-errors">
|
<div className="attribute-errors">
|
||||||
{typeof error === "object" && "message" in error && typeof error.message === "string" && error.message}
|
{typeof error === "object" && "message" in error && typeof error.message === "string" && error.message}
|
||||||
|
|||||||
Reference in New Issue
Block a user