mirror of
https://github.com/zadam/trilium.git
synced 2025-10-21 07:38:53 +02:00
chore(react/ribbon): set up formatting toolbar
This commit is contained in:
parent
b99d01ad7b
commit
01e4cd2e78
@ -25,7 +25,6 @@ import IncorrectCpuArchDialog from "../widgets/dialogs/incorrect_cpu_arch.js";
|
|||||||
import PopupEditorDialog from "../widgets/dialogs/popup_editor.js";
|
import PopupEditorDialog from "../widgets/dialogs/popup_editor.js";
|
||||||
import FlexContainer from "../widgets/containers/flex_container.js";
|
import FlexContainer from "../widgets/containers/flex_container.js";
|
||||||
import NoteIconWidget from "../widgets/note_icon";
|
import NoteIconWidget from "../widgets/note_icon";
|
||||||
import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
|
|
||||||
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
|
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
|
||||||
import NoteDetailWidget from "../widgets/note_detail.js";
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
import NoteListWidget from "../widgets/note_list.js";
|
import NoteListWidget from "../widgets/note_list.js";
|
||||||
@ -63,7 +62,7 @@ export function applyModals(rootContainer: RootContainer) {
|
|||||||
.cssBlock(".title-row > * { margin: 5px; }")
|
.cssBlock(".title-row > * { margin: 5px; }")
|
||||||
.child(<NoteIconWidget />)
|
.child(<NoteIconWidget />)
|
||||||
.child(<NoteTitleWidget />))
|
.child(<NoteTitleWidget />))
|
||||||
.child(new ClassicEditorToolbar())
|
// .child(new ClassicEditorToolbar())
|
||||||
.child(new PromotedAttributesWidget())
|
.child(new PromotedAttributesWidget())
|
||||||
.child(new NoteDetailWidget())
|
.child(new NoteDetailWidget())
|
||||||
.child(new NoteListWidget(true)))
|
.child(new NoteListWidget(true)))
|
||||||
|
21
apps/client/src/widgets/ribbon/FormattingTab.tsx
Normal file
21
apps/client/src/widgets/ribbon/FormattingTab.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { CSSProperties } from "preact/compat";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the editing toolbar when the CKEditor is in decoupled mode.
|
||||||
|
*
|
||||||
|
* This toolbar is only enabled if the user has selected the classic CKEditor.
|
||||||
|
*
|
||||||
|
* The ribbon item is active by default for text notes, as long as they are not in read-only mode.
|
||||||
|
*/
|
||||||
|
export default function FormattingTab({ hidden }) {
|
||||||
|
const style: CSSProperties = {};
|
||||||
|
if (hidden) {
|
||||||
|
style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="classic-toolbar-widget" style={style}>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
@ -1,25 +1,41 @@
|
|||||||
import { useMemo, useState } from "preact/hooks";
|
import { useMemo, useState } from "preact/hooks";
|
||||||
import FNote from "../../entities/fnote";
|
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import { useNoteContext } from "../react/hooks";
|
import { useNoteContext } from "../react/hooks";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
import { VNode } from "preact";
|
import { VNode } from "preact";
|
||||||
import BasicPropertiesTab from "./BasicPropertiesTab";
|
import BasicPropertiesTab from "./BasicPropertiesTab";
|
||||||
|
import FormattingTab from "./FormattingTab";
|
||||||
import { numberObjectsInPlace } from "../../services/utils";
|
import { numberObjectsInPlace } from "../../services/utils";
|
||||||
import { TabContext } from "./ribbon-interface";
|
import { TabContext } from "./ribbon-interface";
|
||||||
|
import options from "../../services/options";
|
||||||
|
import { CommandNames } from "../../components/app_context";
|
||||||
|
import FNote from "../../entities/fnote";
|
||||||
|
|
||||||
|
interface TitleContext {
|
||||||
|
note: FNote | null | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
interface TabConfiguration {
|
interface TabConfiguration {
|
||||||
title: string | ((context: TabContext) => string);
|
title: string | ((context: TitleContext) => string);
|
||||||
icon: string;
|
icon: string;
|
||||||
// TODO: Mark as required after porting them all.
|
// TODO: Mark as required after porting them all.
|
||||||
content?: (context: TabContext) => VNode;
|
content?: (context: TabContext) => VNode;
|
||||||
show?: (context: TabContext) => boolean;
|
show?: (context: TitleContext) => boolean;
|
||||||
|
toggleCommand?: CommandNames;
|
||||||
|
/**
|
||||||
|
* By default the tab content will not be rendered unless the tab is active (i.e. selected by the user). Setting to `true` will ensure that the tab is rendered even when inactive, for cases where the tab needs to be accessible at all times (e.g. for the detached editor toolbar).
|
||||||
|
*/
|
||||||
|
stayInDom?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>([
|
const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>([
|
||||||
{
|
{
|
||||||
title: t("classic_editor_toolbar.title"),
|
title: t("classic_editor_toolbar.title"),
|
||||||
icon: "bx bx-text"
|
icon: "bx bx-text",
|
||||||
// ClassicEditorToolbar
|
show: ({ note }) => note?.type === "text" && options.get("textNoteEditorType") === "ckeditor-classic",
|
||||||
|
toggleCommand: "toggleRibbonTabClassicEditor",
|
||||||
|
content: FormattingTab,
|
||||||
|
stayInDom: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: ({ note }) => note?.isTriliumSqlite() ? t("script_executor.query") : t("script_executor.script"),
|
title: ({ note }) => note?.isTriliumSqlite() ? t("script_executor.query") : t("script_executor.script"),
|
||||||
@ -61,7 +77,8 @@ const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>([
|
|||||||
title: t("basic_properties.basic_properties"),
|
title: t("basic_properties.basic_properties"),
|
||||||
icon: "bx bx-slider",
|
icon: "bx bx-slider",
|
||||||
content: BasicPropertiesTab,
|
content: BasicPropertiesTab,
|
||||||
show: ({note}) => !note?.isLaunchBarConfig()
|
show: ({note}) => !note?.isLaunchBarConfig(),
|
||||||
|
toggleCommand: "toggleRibbonTabBasicProperties"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// OwnedAttributeListWidget
|
// OwnedAttributeListWidget
|
||||||
@ -97,10 +114,9 @@ const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>([
|
|||||||
|
|
||||||
export default function Ribbon() {
|
export default function Ribbon() {
|
||||||
const { note } = useNoteContext();
|
const { note } = useNoteContext();
|
||||||
const context: TabContext = { note };
|
const titleContext: TitleContext = { note };
|
||||||
const [ activeTabIndex, setActiveTabIndex ] = useState<number | undefined>();
|
const [ activeTabIndex, setActiveTabIndex ] = useState<number | undefined>();
|
||||||
const filteredTabs = useMemo(() => TAB_CONFIGURATION.filter(tab => tab.show?.(context)), [ context, note ])
|
const filteredTabs = useMemo(() => TAB_CONFIGURATION.filter(tab => tab.show?.(titleContext)), [ titleContext, note ])
|
||||||
const activeTabConfiguration = activeTabIndex ? filteredTabs.find(tab => tab.index === activeTabIndex) : undefined;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="ribbon-container" style={{ contain: "none" }}>
|
<div class="ribbon-container" style={{ contain: "none" }}>
|
||||||
@ -109,7 +125,7 @@ export default function Ribbon() {
|
|||||||
{filteredTabs.map(({ title, icon, index }) => (
|
{filteredTabs.map(({ title, icon, index }) => (
|
||||||
<RibbonTab
|
<RibbonTab
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={typeof title === "string" ? title : title(context)}
|
title={typeof title === "string" ? title : title(titleContext)}
|
||||||
active={index === activeTabIndex}
|
active={index === activeTabIndex}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (activeTabIndex !== index) {
|
if (activeTabIndex !== index) {
|
||||||
@ -127,7 +143,14 @@ export default function Ribbon() {
|
|||||||
|
|
||||||
<div className="ribbon-body-container">
|
<div className="ribbon-body-container">
|
||||||
<div className="ribbon-body">
|
<div className="ribbon-body">
|
||||||
{activeTabConfiguration?.content && activeTabConfiguration.content({ note })}
|
{filteredTabs.map(tab => {
|
||||||
|
const isActive = tab.index === activeTabIndex;
|
||||||
|
if (!isActive && !tab.stayInDom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tab?.content && tab.content({ note, hidden: !isActive });
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,4 +2,5 @@ import FNote from "../../entities/fnote";
|
|||||||
|
|
||||||
export interface TabContext {
|
export interface TabContext {
|
||||||
note: FNote | null | undefined;
|
note: FNote | null | undefined;
|
||||||
|
hidden: boolean;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,8 @@
|
|||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #region Basic Properties */
|
||||||
|
|
||||||
.basic-properties-widget {
|
.basic-properties-widget {
|
||||||
padding: 0px 12px 6px 12px;
|
padding: 0px 12px 6px 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -124,4 +126,25 @@
|
|||||||
|
|
||||||
.editability-dropdown {
|
.editability-dropdown {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #endregion */
|
||||||
|
|
||||||
|
/* #region Formatting Toolbar */
|
||||||
|
|
||||||
|
.classic-toolbar-widget {
|
||||||
|
--ck-color-toolbar-background: transparent;
|
||||||
|
--ck-color-button-default-background: transparent;
|
||||||
|
--ck-color-button-default-disabled-background: transparent;
|
||||||
|
min-height: 39px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.classic-toolbar-widget .ck.ck-toolbar {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.classic-toolbar-widget .ck.ck-button.ck-disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #endregion */
|
@ -1,82 +0,0 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
|
||||||
import options from "../../services/options.js";
|
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
|
||||||
|
|
||||||
const TPL = /*html*/`\
|
|
||||||
<div class="classic-toolbar-widget"></div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.classic-toolbar-widget {
|
|
||||||
--ck-color-toolbar-background: transparent;
|
|
||||||
--ck-color-button-default-background: transparent;
|
|
||||||
--ck-color-button-default-disabled-background: transparent;
|
|
||||||
min-height: 39px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.classic-toolbar-widget .ck.ck-toolbar {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.classic-toolbar-widget .ck.ck-button.ck-disabled {
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the editing toolbar when the CKEditor is in decoupled mode.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This toolbar is only enabled if the user has selected the classic CKEditor.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* The ribbon item is active by default for text notes, as long as they are not in read-only mode.
|
|
||||||
*/
|
|
||||||
export default class ClassicEditorToolbar extends NoteContextAwareWidget {
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return "classicEditor";
|
|
||||||
}
|
|
||||||
|
|
||||||
get toggleCommand() {
|
|
||||||
return "toggleRibbonTabClassicEditor";
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
this.$widget = $(TPL);
|
|
||||||
this.contentSized();
|
|
||||||
}
|
|
||||||
|
|
||||||
isEnabled(): boolean | null | undefined {
|
|
||||||
if (options.get("textNoteEditorType") !== "ckeditor-classic") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.note || this.note.type !== "text") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getTitle() {
|
|
||||||
return {
|
|
||||||
show: await this.#shouldDisplay(),
|
|
||||||
activate: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async #shouldDisplay() {
|
|
||||||
if (!this.isEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await this.noteContext?.isReadOnly()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user