Skip to content

Commit e14f1ad

Browse files
committed
feat: add break tag and fix focus and keyboard behavior
1 parent 6bdf9c7 commit e14f1ad

File tree

8 files changed

+80
-11
lines changed

8 files changed

+80
-11
lines changed

packages/pluggableWidgets/rich-text-web/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1313
### Changed
1414

1515
- We changed Tab keyboard behavior to add indentation instead of exiting focus from editor.
16+
- We changed `&nbsp;` mark for empty line in favor for `<br />` break tag instead.
1617

1718
### Added
1819

19-
- We added alt+F11 keyboard shortcut to do focus next.
20+
- We added alt+F11 keyboard shortcut to do focus next, and alt+F10 to focus on toolbar.
21+
- We added shift+enter keyboard shortcut to add `<br />` break tag.
2022

2123
## [4.10.0] - 2025-10-02
2224

packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,18 @@ function EditorWrapperInner(props: EditorWrapperProps): ReactElement {
211211
formOrientation={formOrientation}
212212
/>
213213
</div>
214-
<If condition={enableStatusBar}>
215-
<div className="widget-rich-text-footer" tabIndex={-1}>
214+
<div
215+
className={classNames("widget-rich-text-footer", { "hide-status-bar": !enableStatusBar })}
216+
tabIndex={-1}
217+
>
218+
<If condition={enableStatusBar}>
216219
<span>
217220
<span>{wordCount}</span>
218221
<span>{` ${statusBarContent === "wordCount" ? "word" : "character"}`}</span>
219222
<span>{wordCount === 1 ? "" : "s"}</span>
220223
</span>
221-
</div>
222-
</If>
224+
</If>
225+
</div>
223226
</div>
224227
);
225228
}

packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ $rte-brand-primary: #264ae5;
101101
position: relative;
102102
text-transform: none;
103103
justify-content: end;
104+
105+
&.hide-status-bar {
106+
border-top: none;
107+
height: 0;
108+
}
104109
}
105110

106111
&.editor-readPanel {

packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import CustomListItem from "./formats/customList";
66
import CustomLink from "./formats/link";
77
import CustomVideo from "./formats/video";
88
import CustomImage from "./formats/image";
9+
import SoftBreak from "./formats/softBreak";
910
import Button from "./formats/button";
1011
import { Attributor } from "parchment";
1112
const direction = Quill.import("attributors/style/direction") as Attributor;
@@ -18,6 +19,7 @@ import MxUploader from "./modules/uploader";
1819
import MxBlock from "./formats/block";
1920
import CustomClipboard from "./modules/clipboard";
2021
import { WhiteSpaceStyle } from "./formats/whiteSpace";
22+
2123
class Empty {
2224
doSomething(): string {
2325
return "";
@@ -33,6 +35,7 @@ Quill.register(WhiteSpaceStyle, true);
3335
Quill.register(CustomLink, true);
3436
Quill.register(CustomVideo, true);
3537
Quill.register(CustomImage, true);
38+
Quill.register({ "formats/softbreak": SoftBreak }, true);
3639
Quill.register(direction, true);
3740
Quill.register(alignment, true);
3841
Quill.register(IndentLeftStyle, true);

packages/pluggableWidgets/rich-text-web/src/utils/formats/block.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ class MxBlock extends Block {
55
// quill return empty paragraph when there is no content (just empty line)
66
// to preserve the line breaks, we add empty space
77
if (this.domNode.childElementCount === 1 && this.domNode.children[0] instanceof HTMLBRElement) {
8-
return this.domNode.outerHTML.replace(/<br>/g, "&nbsp;");
8+
return this.domNode.outerHTML.replace(/<br>/g, "<br />");
99
} else if (this.domNode.childElementCount === 0 && this.domNode.textContent?.trim() === "") {
10-
this.domNode.innerHTML = "&nbsp;";
10+
this.domNode.innerHTML = "<br />";
1111
return this.domNode.outerHTML;
1212
} else {
1313
return this.domNode.outerHTML;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// import { BlockEmbed } from "quill/blots/block";
2+
import { EmbedBlot } from "parchment";
3+
/**
4+
* custom video link handler, allowing width and height config
5+
*/
6+
class SoftBreak extends EmbedBlot {
7+
static create(_value: unknown): Element {
8+
const node = super.create() as HTMLElement;
9+
return node;
10+
}
11+
}
12+
13+
// SoftBreak.scope = Scope.INLINE_BLOT;
14+
SoftBreak.blotName = "softbreak";
15+
SoftBreak.tagName = "BR";
16+
17+
export default SoftBreak;

packages/pluggableWidgets/rich-text-web/src/utils/modules/keyboard.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import {
33
enterKeyKeyboardHandler,
44
exitFullscreenKeyboardHandler,
55
gotoStatusBarKeyboardHandler,
6-
gotoToolbarKeyboardHandler
6+
gotoToolbarKeyboardHandler,
7+
movePrevFocus,
8+
shiftEnterKeyKeyboardHandler
79
} from "./toolbarHandlers";
810
import QuillTableBetter from "../formats/quill-table-better/quill-table-better";
911

@@ -13,19 +15,28 @@ export function getKeyboardBindings(): Record<string, unknown> {
1315
key: "Enter",
1416
handler: enterKeyKeyboardHandler
1517
},
18+
shiftEnter: {
19+
key: "Enter",
20+
shiftKey: true,
21+
collapsed: true,
22+
handler: shiftEnterKeyKeyboardHandler
23+
},
1624
focusTab: {
1725
key: "F10",
1826
altKey: true,
27+
collapsed: true,
1928
handler: gotoToolbarKeyboardHandler
2029
},
2130
shiftTab: {
2231
key: "Tab",
2332
shiftKey: true,
24-
handler: gotoToolbarKeyboardHandler
33+
collapsed: true,
34+
handler: movePrevFocus
2535
},
2636
nextFocusTab: {
2737
key: "F11",
2838
altKey: true,
39+
collapsed: true,
2940
handler: gotoStatusBarKeyboardHandler
3041
},
3142
escape: {

packages/pluggableWidgets/rich-text-web/src/utils/modules/toolbarHandlers.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,45 @@ export function enterKeyKeyboardHandler(this: Keyboard, range: Range, context: C
7070
});
7171
}
7272

73+
export function shiftEnterKeyKeyboardHandler(this: Keyboard, range: Range, context: Context): any {
74+
if (context.format.table) {
75+
return true;
76+
}
77+
this.quill.insertEmbed(range.index, "softbreak", true, Quill.sources.USER);
78+
this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
79+
return false;
80+
}
81+
82+
export function movePrevFocus(this: Keyboard, range: Range, context: Context): any {
83+
if (context.format.table || context.format.indent || context.format.list || context.format.blockquote) {
84+
context.event.stopPropagation();
85+
context.event.preventDefault();
86+
return true;
87+
}
88+
89+
gotoToolbarKeyboardHandler.call(this, range, context);
90+
}
91+
7392
// focus to first toolbar button
74-
export function gotoToolbarKeyboardHandler(this: Keyboard, _range: Range, _context: Context): void {
93+
export function gotoToolbarKeyboardHandler(this: Keyboard, _range: Range, context: Context): any {
94+
if (context.format.table) {
95+
return true;
96+
}
97+
7598
const toolbar = this.quill.container.parentElement?.parentElement?.querySelector(".widget-rich-text-toolbar");
76-
(toolbar?.querySelector(".ql-formats button") as HTMLElement)?.focus();
99+
if (toolbar) {
100+
(toolbar?.querySelector(".ql-formats button") as HTMLElement)?.focus();
101+
} else {
102+
this.quill.blur();
103+
}
77104
}
78105

79106
// move to next element focus : status bar button (exit editor)
80107
export function gotoStatusBarKeyboardHandler(this: Keyboard, _range: Range, context: Context): boolean | void {
81108
if (context.format.table) {
82109
return true;
83110
}
111+
84112
const statusBar = this.quill.container.parentElement?.parentElement?.nextElementSibling;
85113
if (statusBar) {
86114
(statusBar as HTMLElement)?.focus();

0 commit comments

Comments
 (0)