feat(notebook): support text direction controls (#6293)
This commit is contained in:
1 parent
c56b91570b
commit
d44bc06a93
5 files changed
+124
-20
No files matched your search
@@ -1,29 +1,25 @@
|
||||
---
|
||||
title: 'Notebook'
|
||||
description: 'A simple notebook widget that supports markdown.'
|
||||
title: "Notebook"
|
||||
description: "A simple notebook widget that supports markdown."
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
import { AddingWidget } from '@site/src/components/widgets/adding';
|
||||
import { WidgetConfig } from '@site/src/components/widgets/configuration';
|
||||
import { WidgetHeader } from '@site/src/components/widgets/header';
|
||||
import { notebookWidget } from '.';
|
||||
import { AddingWidget } from "@site/src/components/widgets/adding";
|
||||
import { WidgetConfig } from "@site/src/components/widgets/configuration";
|
||||
import { WidgetHeader } from "@site/src/components/widgets/header";
|
||||
import { notebookWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={notebookWidget} categories={["Notes"]} />
|
||||
|
||||
<WidgetHeader
|
||||
widget={notebookWidget}
|
||||
categories={['Notes']}
|
||||
/>
|
||||
|
||||
The notebook widget focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users.
|
||||
The notebook widget focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users.
|
||||
It is based on Tiptap.dev and supports most of its features.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import editOff from './img/normal.png';
|
||||
import editOn from './img/edit.png';
|
||||
import editOff from "./img/normal.png";
|
||||
import editOn from "./img/edit.png";
|
||||
|
||||
<div className='flex gap-1'>
|
||||
<div className="flex gap-1">
|
||||
<img src={editOff} alt="notebook edit-off" width={128 * 3} />
|
||||
<img src={editOn} alt="notebook edit-on" width={128 * 3} />
|
||||
</div>
|
||||
@@ -33,24 +29,27 @@ import editOn from './img/edit.png';
|
||||
<AddingWidget />
|
||||
|
||||
### Configuration
|
||||
|
||||
<WidgetConfig configuration={notebookWidget.configuration} />
|
||||
|
||||
### Editing
|
||||
|
||||
When the permissions of the user allows it, you can enter the editing of the text by clicking the following button in the top right.
|
||||
|
||||

|
||||
|
||||
|
||||
You can then decide to save your changes or cancel them to exit editing, using the buttons placed in the same location.
|
||||
|
||||

|
||||
|
||||
### Notebook toolbar functions
|
||||
The notebook offers a variaty of functions to edit the text and more.
|
||||
|
||||
The notebook offers a variety of functions to edit the text and more.
|
||||
|
||||

|
||||
|
||||
#### Common text formatting
|
||||
|
||||
| Function | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------- |
|
||||
| Bold | Makes the selected text bold |
|
||||
@@ -62,35 +61,51 @@ The notebook offers a variaty of functions to edit the text and more.
|
||||
| Clear formatting | Erases any of the preceding function's effects on the selected text |
|
||||
|
||||
#### Heading
|
||||
|
||||
Choose a heading size for the line, making the text bigger. Working like titles and sub-titles.
|
||||
|
||||
#### Text Alignement
|
||||
Select the alignement on the line. Left, center or right.
|
||||
#### Text Alignment
|
||||
|
||||
Select the alignment on the line. Left, center or right.
|
||||
|
||||
#### Text direction
|
||||
|
||||
Set the current paragraph or heading to left-to-right or right-to-left. This is useful when writing right-to-left text while Homarr is using a left-to-right language, or vice versa.
|
||||
|
||||
#### Misc functions
|
||||
|
||||
##### Blockquote
|
||||
|
||||
Puts the current line in a quoting block.
|
||||
|
||||
##### Horizontal line
|
||||
|
||||
Adds a line that goes from the far right to the far left as a separator.
|
||||
|
||||
#### Lists
|
||||
|
||||
##### Bullet lists
|
||||
|
||||
Simple list with a dot preceding each element.
|
||||
|
||||
##### Ordered lists
|
||||
|
||||
List with numbers preceding each element.
|
||||
|
||||
##### Check lists
|
||||
|
||||
List with a checkbox on each element.
|
||||
|
||||
##### Indentation changes
|
||||
|
||||

|
||||
|
||||
When you are currently in a list of any sort, the last 2 buttons will appear and will allow you to change the indentation of the current list item.
|
||||
|
||||
#### Insert functions
|
||||
|
||||
##### Link controls
|
||||
|
||||
The first buttons is to insert a link, or edit an existing link. The link will be applied on the selected text.
|
||||
|
||||
Pasting a link directly into the text will automatically put the link anchor on it too.
|
||||
@@ -102,16 +117,20 @@ The second button will remove the link while leaving the text intact.
|
||||
This is the modal for the link, The little button in the textbox can be clicked to make the link open in a new tab.
|
||||
|
||||
##### Image controls
|
||||
|
||||

|
||||
|
||||
##### Source
|
||||
|
||||
You can use a normal link to an image or you can also use locally stored images.
|
||||
|
||||
##### Width
|
||||
|
||||
The aspect ratio will always be respected, so only the width field is necessary.
|
||||
You can provide a value in a plain number or you can also use percents to have your image size dynamically change on different screens.
|
||||
|
||||
#### Table
|
||||
|
||||
This last control allows you to add a table, for which you can determine the number of columns and row before inserting it.
|
||||
|
||||

|
||||
@@ -119,6 +138,7 @@ This last control allows you to add a table, for which you can determine the num
|
||||
Clicking that button while in a table will delete it.
|
||||
|
||||
##### Table controls
|
||||
|
||||

|
||||
|
||||
While in a table, further controls will appear to help edit the table.
|
||||
@@ -134,4 +154,4 @@ All the following ones are for editing the wize of the table:
|
||||
- Delete Column
|
||||
- Add row on top
|
||||
- Add row below
|
||||
- Delete Row
|
||||
- Delete Row
|
||||
@@ -1956,6 +1956,8 @@
|
||||
"clear": "Clear formatting",
|
||||
"heading": "Heading {level}",
|
||||
"align": "Align text: {position}",
|
||||
"directionLtr": "Set text direction left to right",
|
||||
"directionRtl": "Set text direction right to left",
|
||||
"blockquote": "Blockquote",
|
||||
"horizontalLine": "Horizontal line",
|
||||
"bulletList": "Bullet list",
|
||||
|
||||
@@ -39,6 +39,8 @@ import {
|
||||
IconRowRemove,
|
||||
IconTableOff,
|
||||
IconTablePlus,
|
||||
IconTextDirectionLtr,
|
||||
IconTextDirectionRtl,
|
||||
IconX,
|
||||
} from "@tabler/icons-react";
|
||||
import { Color } from "@tiptap/extension-color";
|
||||
@@ -66,6 +68,7 @@ import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import type { TablerIcon } from "@homarr/ui";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import { NotebookTextDirection, setTextDirection } from "./text-direction";
|
||||
|
||||
import "@mantine/tiptap/styles.css";
|
||||
import "./notebook.css";
|
||||
@@ -171,6 +174,7 @@ export function Notebook({ options, setOptions, isEditMode, boardId, itemId }: W
|
||||
}),
|
||||
TaskList.configure({ itemTypeName: "taskItem" }),
|
||||
TextAlign.configure({ types: ["heading", "paragraph"] }),
|
||||
NotebookTextDirection,
|
||||
TextStyle,
|
||||
],
|
||||
shouldRerenderOnTransaction: true,
|
||||
@@ -347,6 +351,22 @@ export function Notebook({ options, setOptions, isEditMode, boardId, itemId }: W
|
||||
position: t("widget.notebook.align.right"),
|
||||
})}
|
||||
/>
|
||||
<RichTextEditor.Control
|
||||
title={tControls("directionLtr")}
|
||||
aria-label={tControls("directionLtr")}
|
||||
active={editor?.isActive({ dir: "ltr" })}
|
||||
onClick={() => editor && setTextDirection(editor, "ltr").run()}
|
||||
>
|
||||
<IconTextDirectionLtr {...controlIconProps} />
|
||||
</RichTextEditor.Control>
|
||||
<RichTextEditor.Control
|
||||
title={tControls("directionRtl")}
|
||||
aria-label={tControls("directionRtl")}
|
||||
active={editor?.isActive({ dir: "rtl" })}
|
||||
onClick={() => editor && setTextDirection(editor, "rtl").run()}
|
||||
>
|
||||
<IconTextDirectionRtl {...controlIconProps} />
|
||||
</RichTextEditor.Control>
|
||||
</RichTextEditor.ControlsGroup>
|
||||
|
||||
<RichTextEditor.ControlsGroup>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Editor } from "@tiptap/react";
|
||||
import { StarterKit } from "@tiptap/starter-kit";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { NotebookTextDirection, setTextDirection } from "./text-direction";
|
||||
|
||||
describe("notebook text direction", () => {
|
||||
it("persists right-to-left direction in the notebook HTML", () => {
|
||||
const editor = new Editor({
|
||||
content: "<p>יש לי 16GB</p>",
|
||||
extensions: [StarterKit, NotebookTextDirection],
|
||||
});
|
||||
|
||||
setTextDirection(editor, "rtl").run();
|
||||
|
||||
expect(editor.getHTML()).toBe('<p dir="rtl">יש לי 16GB</p>');
|
||||
editor.destroy();
|
||||
});
|
||||
|
||||
it("reads and changes an existing direction attribute", () => {
|
||||
const editor = new Editor({
|
||||
content: '<h2 dir="rtl">Heading</h2>',
|
||||
extensions: [StarterKit, NotebookTextDirection],
|
||||
});
|
||||
|
||||
expect(editor.getHTML()).toBe('<h2 dir="rtl">Heading</h2>');
|
||||
|
||||
editor.commands.setTextSelection(1);
|
||||
setTextDirection(editor, "ltr").run();
|
||||
|
||||
expect(editor.getHTML()).toContain('<h2 dir="ltr">Heading</h2>');
|
||||
editor.destroy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Extension } from "@tiptap/react";
|
||||
import type { Editor } from "@tiptap/react";
|
||||
|
||||
export type TextDirection = "ltr" | "rtl";
|
||||
|
||||
export const NotebookTextDirection = Extension.create({
|
||||
name: "notebookTextDirection",
|
||||
|
||||
addGlobalAttributes() {
|
||||
return [
|
||||
{
|
||||
types: ["heading", "paragraph"],
|
||||
attributes: {
|
||||
dir: {
|
||||
default: null,
|
||||
parseHTML: (element) => element.getAttribute("dir"),
|
||||
renderHTML: ({ dir }: { dir?: TextDirection }) => (dir ? { dir } : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
export function setTextDirection(editor: Editor, direction: TextDirection) {
|
||||
const nodeType = editor.isActive("heading") ? "heading" : "paragraph";
|
||||
return editor.chain().focus().updateAttributes(nodeType, { dir: direction });
|
||||
}
|
||||
Reference in new issue
Block a user