mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 07:08:55 +02:00
chore(react/ribbon): start implementing attribute editor
This commit is contained in:
parent
2c33ef2b0d
commit
6d37e19b40
@ -24,58 +24,6 @@ const HELP_TEXT = `
|
||||
<p>${t("attribute_editor.help_text_body3")}</p>`;
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div style="position: relative; padding-top: 10px; padding-bottom: 10px">
|
||||
<style>
|
||||
.attribute-list-editor {
|
||||
border: 0 !important;
|
||||
outline: 0 !important;
|
||||
box-shadow: none !important;
|
||||
padding: 0 0 0 5px !important;
|
||||
margin: 0 !important;
|
||||
max-height: 100px;
|
||||
overflow: auto;
|
||||
transition: opacity .1s linear;
|
||||
}
|
||||
|
||||
.attribute-list-editor.ck-content .mention {
|
||||
color: var(--muted-text-color) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.save-attributes-button {
|
||||
color: var(--muted-text-color);
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
right: 25px;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
font-size: 130%;
|
||||
}
|
||||
|
||||
.add-new-attribute-button {
|
||||
color: var(--muted-text-color);
|
||||
position: absolute;
|
||||
bottom: 13px;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
font-size: 130%;
|
||||
}
|
||||
|
||||
.add-new-attribute-button:hover, .save-attributes-button:hover {
|
||||
border: 1px solid var(--button-border-color);
|
||||
border-radius: var(--button-border-radius);
|
||||
background: var(--button-background-color);
|
||||
color: var(--button-text-color);
|
||||
}
|
||||
|
||||
.attribute-errors {
|
||||
color: red;
|
||||
padding: 5px 50px 0px 5px; /* large right padding to avoid buttons */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="attribute-list-editor" tabindex="200"></div>
|
||||
|
||||
<div class="bx bx-save save-attributes-button tn-tool-button" title="${escapeQuotes(t("attribute_editor.save_attributes"))}"></div>
|
||||
<div class="bx bx-plus add-new-attribute-button tn-tool-button" title="${escapeQuotes(t("attribute_editor.add_a_new_attribute"))}"></div>
|
||||
@ -84,61 +32,6 @@ const TPL = /*html*/`
|
||||
</div>
|
||||
`;
|
||||
|
||||
const mentionSetup: MentionFeed[] = [
|
||||
{
|
||||
marker: "@",
|
||||
feed: (queryText) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
|
||||
itemRenderer: (_item) => {
|
||||
const item = _item as Suggestion;
|
||||
const itemElement = document.createElement("button");
|
||||
|
||||
itemElement.innerHTML = `${item.highlightedNotePathTitle} `;
|
||||
|
||||
return itemElement;
|
||||
},
|
||||
minimumCharacters: 0
|
||||
},
|
||||
{
|
||||
marker: "#",
|
||||
feed: async (queryText) => {
|
||||
const names = await server.get<string[]>(`attribute-names/?type=label&query=${encodeURIComponent(queryText)}`);
|
||||
|
||||
return names.map((name) => {
|
||||
return {
|
||||
id: `#${name}`,
|
||||
name: name
|
||||
};
|
||||
});
|
||||
},
|
||||
minimumCharacters: 0
|
||||
},
|
||||
{
|
||||
marker: "~",
|
||||
feed: async (queryText) => {
|
||||
const names = await server.get<string[]>(`attribute-names/?type=relation&query=${encodeURIComponent(queryText)}`);
|
||||
|
||||
return names.map((name) => {
|
||||
return {
|
||||
id: `~${name}`,
|
||||
name: name
|
||||
};
|
||||
});
|
||||
},
|
||||
minimumCharacters: 0
|
||||
}
|
||||
];
|
||||
|
||||
const editorConfig: EditorConfig = {
|
||||
toolbar: {
|
||||
items: []
|
||||
},
|
||||
placeholder: t("attribute_editor.placeholder"),
|
||||
mention: {
|
||||
feeds: mentionSetup
|
||||
},
|
||||
licenseKey: "GPL"
|
||||
};
|
||||
|
||||
type AttributeCommandNames = FilteredCommandNames<CommandData>;
|
||||
|
||||
export default class AttributeEditorWidget extends NoteContextAwareWidget implements EventListener<"entitiesReloaded">, EventListener<"addNewLabel">, EventListener<"addNewRelation"> {
|
||||
@ -324,7 +217,6 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
|
||||
|
||||
this.$editor.on("click", (e) => this.handleEditorClick(e));
|
||||
|
||||
this.textEditor = await AttributeEditor.create(this.$editor[0], editorConfig);
|
||||
this.textEditor.model.document.on("change:data", () => this.dataChanged());
|
||||
this.textEditor.editing.view.document.on(
|
||||
"enter",
|
||||
|
10
apps/client/src/widgets/ribbon/OwnedAttributesTab.tsx
Normal file
10
apps/client/src/widgets/ribbon/OwnedAttributesTab.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import AttributeEditor from "./components/AttributeEditor";
|
||||
import { TabContext } from "./ribbon-interface";
|
||||
|
||||
export default function OwnedAttributesTab({ note }: TabContext) {
|
||||
return (
|
||||
<div className="attribute-list">
|
||||
<AttributeEditor />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -19,6 +19,7 @@ import FilePropertiesTab from "./FilePropertiesTab";
|
||||
import ImagePropertiesTab from "./ImagePropertiesTab";
|
||||
import NotePathsTab from "./NotePathsTab";
|
||||
import NoteMapTab from "./NoteMapTab";
|
||||
import OwnedAttributesTab from "./OwnedAttributesTab";
|
||||
|
||||
interface TitleContext {
|
||||
note: FNote | null | undefined;
|
||||
@ -105,9 +106,11 @@ const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>([
|
||||
toggleCommand: "toggleRibbonTabBasicProperties"
|
||||
},
|
||||
{
|
||||
// OwnedAttributeListWidget
|
||||
title: t("owned_attribute_list.owned_attributes"),
|
||||
icon: "bx bx-list-check"
|
||||
icon: "bx bx-list-check",
|
||||
content: OwnedAttributesTab,
|
||||
show: ({note}) => !note?.isLaunchBarConfig(),
|
||||
toggleCommand: "toggleRibbonTabOwnedAttributes"
|
||||
},
|
||||
{
|
||||
// InheritedAttributesWidget
|
||||
|
@ -0,0 +1,78 @@
|
||||
import { useEffect, useRef } from "preact/hooks"
|
||||
import { AttributeEditor as CKEditor, EditorConfig, MentionFeed } from "@triliumnext/ckeditor5";
|
||||
import { t } from "../../../services/i18n";
|
||||
import server from "../../../services/server";
|
||||
import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete";
|
||||
|
||||
const mentionSetup: MentionFeed[] = [
|
||||
{
|
||||
marker: "@",
|
||||
feed: (queryText) => note_autocomplete.autocompleteSourceForCKEditor(queryText),
|
||||
itemRenderer: (_item) => {
|
||||
const item = _item as Suggestion;
|
||||
const itemElement = document.createElement("button");
|
||||
|
||||
itemElement.innerHTML = `${item.highlightedNotePathTitle} `;
|
||||
|
||||
return itemElement;
|
||||
},
|
||||
minimumCharacters: 0
|
||||
},
|
||||
{
|
||||
marker: "#",
|
||||
feed: async (queryText) => {
|
||||
const names = await server.get<string[]>(`attribute-names/?type=label&query=${encodeURIComponent(queryText)}`);
|
||||
|
||||
return names.map((name) => {
|
||||
return {
|
||||
id: `#${name}`,
|
||||
name: name
|
||||
};
|
||||
});
|
||||
},
|
||||
minimumCharacters: 0
|
||||
},
|
||||
{
|
||||
marker: "~",
|
||||
feed: async (queryText) => {
|
||||
const names = await server.get<string[]>(`attribute-names/?type=relation&query=${encodeURIComponent(queryText)}`);
|
||||
|
||||
return names.map((name) => {
|
||||
return {
|
||||
id: `~${name}`,
|
||||
name: name
|
||||
};
|
||||
});
|
||||
},
|
||||
minimumCharacters: 0
|
||||
}
|
||||
];
|
||||
|
||||
const editorConfig: EditorConfig = {
|
||||
toolbar: {
|
||||
items: []
|
||||
},
|
||||
placeholder: t("attribute_editor.placeholder"),
|
||||
mention: {
|
||||
feeds: mentionSetup
|
||||
},
|
||||
licenseKey: "GPL"
|
||||
};
|
||||
|
||||
export default function AttributeEditor() {
|
||||
const editorContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorContainerRef.current) return;
|
||||
|
||||
CKEditor.create(editorContainerRef.current, editorConfig).then((textEditor) => {
|
||||
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style="position: relative; padding-top: 10px; padding-bottom: 10px">
|
||||
<div ref={editorContainerRef} class="attribute-list-editor" tabindex={200} />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -267,4 +267,68 @@
|
||||
color: var(--muted-text-color);
|
||||
display: none;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region Attribute editor */
|
||||
.attribute-list-editor {
|
||||
border: 0 !important;
|
||||
outline: 0 !important;
|
||||
box-shadow: none !important;
|
||||
padding: 0 0 0 5px !important;
|
||||
margin: 0 !important;
|
||||
max-height: 100px;
|
||||
overflow: auto;
|
||||
transition: opacity .1s linear;
|
||||
}
|
||||
|
||||
.attribute-list-editor.ck-content .mention {
|
||||
color: var(--muted-text-color) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.save-attributes-button {
|
||||
color: var(--muted-text-color);
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
right: 25px;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
font-size: 130%;
|
||||
}
|
||||
|
||||
.add-new-attribute-button {
|
||||
color: var(--muted-text-color);
|
||||
position: absolute;
|
||||
bottom: 13px;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
font-size: 130%;
|
||||
}
|
||||
|
||||
.add-new-attribute-button:hover, .save-attributes-button:hover {
|
||||
border: 1px solid var(--button-border-color);
|
||||
border-radius: var(--button-border-radius);
|
||||
background: var(--button-background-color);
|
||||
color: var(--button-text-color);
|
||||
}
|
||||
|
||||
.attribute-errors {
|
||||
color: red;
|
||||
padding: 5px 50px 0px 5px; /* large right padding to avoid buttons */
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region Owned Attributes */
|
||||
.attribute-list {
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.attribute-list-editor p {
|
||||
margin: 0 !important;
|
||||
}
|
||||
/* #endregion */
|
@ -5,40 +5,12 @@ import AttributeEditorWidget from "../attribute_widgets/attribute_editor.js";
|
||||
import type { CommandListenerData } from "../../components/app_context.js";
|
||||
import type FAttribute from "../../entities/fattribute.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="attribute-list">
|
||||
<style>
|
||||
.attribute-list {
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.attribute-list-editor p {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="attr-editor-placeholder"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export default class OwnedAttributeListWidget extends NoteContextAwareWidget {
|
||||
|
||||
private attributeDetailWidget: AttributeDetailWidget;
|
||||
private attributeEditorWidget: AttributeEditorWidget;
|
||||
private $title!: JQuery<HTMLElement>;
|
||||
|
||||
get name() {
|
||||
return "ownedAttributes";
|
||||
}
|
||||
|
||||
get toggleCommand() {
|
||||
return "toggleRibbonTabOwnedAttributes";
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@ -49,23 +21,6 @@ export default class OwnedAttributeListWidget extends NoteContextAwareWidget {
|
||||
this.child(this.attributeEditorWidget, this.attributeDetailWidget);
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
return {
|
||||
show: !this.note?.isLaunchBarConfig(),
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.contentSized();
|
||||
|
||||
this.$widget.find(".attr-editor-placeholder").replaceWith(this.attributeEditorWidget.render());
|
||||
this.$widget.append(this.attributeDetailWidget.render());
|
||||
|
||||
this.$title = $("<div>");
|
||||
}
|
||||
|
||||
async saveAttributesCommand() {
|
||||
await this.attributeEditorWidget.save();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user