@squiz/formatted-text-editor 1.34.1-alpha.1 → 1.34.1-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/lib/EditorToolbar/FloatingToolbar.js +7 -1
  2. package/lib/EditorToolbar/Toolbar.js +4 -2
  3. package/lib/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.d.ts +2 -0
  4. package/lib/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.js +56 -0
  5. package/lib/EditorToolbar/Tools/Image/ImageButton.js +1 -1
  6. package/lib/EditorToolbar/Tools/Link/LinkButton.js +1 -1
  7. package/lib/EditorToolbar/Tools/TextAlign/TextAlignButtons.js +2 -2
  8. package/lib/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.d.ts +2 -0
  9. package/lib/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.js +22 -0
  10. package/lib/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.js +3 -2
  11. package/lib/EditorToolbar/Tools/TextType/TextTypeDropdown.js +8 -2
  12. package/lib/Extensions/ClearFormattingExtension/ClearFormattingExtension.d.ts +5 -0
  13. package/lib/Extensions/ClearFormattingExtension/ClearFormattingExtension.js +63 -0
  14. package/lib/Extensions/CodeBlockExtension/CodeBlockExtension.d.ts +5 -0
  15. package/lib/Extensions/CodeBlockExtension/CodeBlockExtension.js +30 -0
  16. package/lib/Extensions/Extensions.d.ts +1 -0
  17. package/lib/Extensions/Extensions.js +5 -0
  18. package/lib/Extensions/PreformattedExtension/PreformattedExtension.d.ts +2 -0
  19. package/lib/Extensions/PreformattedExtension/PreformattedExtension.js +23 -0
  20. package/lib/index.css +50 -9
  21. package/lib/ui/ToolbarDropdownButton/ToolbarDropdownButton.d.ts +2 -1
  22. package/lib/ui/ToolbarDropdownButton/ToolbarDropdownButton.js +6 -4
  23. package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +7 -2
  24. package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +7 -0
  25. package/lib/utils/getMarkNamesByGroup.d.ts +2 -0
  26. package/lib/utils/getMarkNamesByGroup.js +9 -0
  27. package/lib/utils/getNodeNamesByGroup.d.ts +2 -0
  28. package/lib/utils/getNodeNamesByGroup.js +9 -0
  29. package/package.json +4 -4
  30. package/postcss.config.js +1 -1
  31. package/src/EditorToolbar/FloatingToolbar.tsx +10 -5
  32. package/src/EditorToolbar/Toolbar.tsx +3 -1
  33. package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.spec.tsx +34 -0
  34. package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.tsx +45 -0
  35. package/src/EditorToolbar/Tools/Image/ImageButton.tsx +3 -2
  36. package/src/EditorToolbar/Tools/Link/LinkButton.tsx +3 -2
  37. package/src/EditorToolbar/Tools/TextAlign/TextAlignButtons.tsx +2 -2
  38. package/src/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.spec.tsx +47 -0
  39. package/src/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.tsx +32 -0
  40. package/src/EditorToolbar/Tools/TextType/Heading/HeadingButton.spec.tsx +2 -2
  41. package/src/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.spec.tsx +1 -1
  42. package/src/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.spec.tsx +2 -2
  43. package/src/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.tsx +3 -1
  44. package/src/EditorToolbar/Tools/TextType/TextTypeDropdown.tsx +12 -2
  45. package/src/EditorToolbar/_floating-toolbar.scss +1 -1
  46. package/src/EditorToolbar/_toolbar.scss +2 -2
  47. package/src/Extensions/ClearFormattingExtension/ClearFormattingExtension.ts +57 -0
  48. package/src/Extensions/CodeBlockExtension/CodeBlockExtension.ts +34 -0
  49. package/src/Extensions/Extensions.ts +5 -0
  50. package/src/Extensions/PreformattedExtension/PreformattedExtension.spec.ts +4 -2
  51. package/src/Extensions/PreformattedExtension/PreformattedExtension.ts +31 -0
  52. package/src/index.scss +3 -0
  53. package/src/ui/ToolbarDropdownButton/ToolbarDropdownButton.tsx +8 -4
  54. package/src/ui/ToolbarDropdownButton/_toolbar-dropdown-button.scss +11 -0
  55. package/src/ui/_typography.scss +26 -0
  56. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +37 -0
  57. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +10 -2
  58. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +39 -2
  59. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +6 -0
  60. package/src/utils/getMarkNamesByGroup.spec.ts +20 -0
  61. package/src/utils/getMarkNamesByGroup.ts +7 -0
  62. package/src/utils/getNodeNamesByGroup.spec.ts +20 -0
  63. package/src/utils/getNodeNamesByGroup.ts +7 -0
  64. package/tailwind.config.cjs +4 -3
@@ -39,10 +39,12 @@ const react_components_1 = require("@remirror/react-components");
39
39
  const createToolbarPositioner_1 = require("../utils/createToolbarPositioner");
40
40
  const ImageButton_1 = __importDefault(require("./Tools/Image/ImageButton"));
41
41
  const Extensions_1 = require("../Extensions/Extensions");
42
+ const ClearFormattingButton_1 = __importDefault(require("./Tools/ClearFormatting/ClearFormattingButton"));
42
43
  const FloatingToolbar = () => {
43
44
  const watchedMarks = [Extensions_1.MarkName.Link, Extensions_1.MarkName.AssetLink];
44
45
  const extensionNames = (0, hooks_1.useExtensionNames)();
45
46
  const positioner = (0, react_1.useMemo)(() => (0, createToolbarPositioner_1.createToolbarPositioner)({ types: watchedMarks }), []);
47
+ const { clearFormatting } = (0, react_2.useCommands)();
46
48
  const active = (0, react_2.useActive)();
47
49
  const { data: { marks }, } = (0, react_2.usePositioner)(positioner, []);
48
50
  let buttons = [
@@ -63,7 +65,11 @@ const FloatingToolbar = () => {
63
65
  }
64
66
  else if (!marks?.[Extensions_1.MarkName.Link].isActive && !marks?.[Extensions_1.MarkName.AssetLink].isActive) {
65
67
  // if none of the selected text is a link show the option to create a link.
66
- buttons.push(react_1.default.createElement(react_components_1.VerticalDivider, { key: "link-divider", className: "editor-divider" }), react_1.default.createElement(LinkButton_1.default, { key: "add-link", inPopover: true }));
68
+ buttons.push(react_1.default.createElement(react_components_1.VerticalDivider, { key: "link-divider" }), react_1.default.createElement(LinkButton_1.default, { key: "add-link", inPopover: true }));
69
+ }
70
+ // Clear formatting will always be the last button in the toolbar
71
+ if (extensionNames.clearFormatting && clearFormatting.enabled()) {
72
+ buttons.push(react_1.default.createElement(ClearFormattingButton_1.default, { key: "clearFormatting" }));
67
73
  }
68
74
  return (react_1.default.createElement(react_2.FloatingToolbar, { className: "squiz-fte-scope squiz-fte-scope__floating-popover", positioner: positioner }, buttons));
69
75
  };
@@ -17,13 +17,14 @@ const hooks_1 = require("../hooks");
17
17
  const LinkButton_1 = __importDefault(require("./Tools/Link/LinkButton"));
18
18
  const ImageButton_1 = __importDefault(require("./Tools/Image/ImageButton"));
19
19
  const RemoveLinkButton_1 = __importDefault(require("./Tools/Link/RemoveLinkButton"));
20
+ const ClearFormattingButton_1 = __importDefault(require("./Tools/ClearFormatting/ClearFormattingButton"));
20
21
  const Toolbar = () => {
21
22
  const extensionNames = (0, hooks_1.useExtensionNames)();
22
23
  return (react_1.default.createElement(react_components_1.Toolbar, { className: "remirror-toolbar editor-toolbar" },
23
24
  extensionNames.history && (react_1.default.createElement(react_1.default.Fragment, null,
24
25
  react_1.default.createElement(UndoButton_1.default, null),
25
26
  react_1.default.createElement(RedoButton_1.default, null),
26
- react_1.default.createElement(react_components_1.VerticalDivider, { className: "editor-divider" }))),
27
+ react_1.default.createElement(react_components_1.VerticalDivider, null))),
27
28
  extensionNames.heading && extensionNames.paragraph && extensionNames.preformatted && react_1.default.createElement(TextTypeDropdown_1.default, null),
28
29
  extensionNames.bold && react_1.default.createElement(BoldButton_1.default, null),
29
30
  extensionNames.italic && react_1.default.createElement(ItalicButton_1.default, null),
@@ -32,6 +33,7 @@ const Toolbar = () => {
32
33
  extensionNames.link && (react_1.default.createElement(react_1.default.Fragment, null,
33
34
  react_1.default.createElement(LinkButton_1.default, null),
34
35
  react_1.default.createElement(RemoveLinkButton_1.default, null))),
35
- extensionNames.image && react_1.default.createElement(ImageButton_1.default, null)));
36
+ extensionNames.image && react_1.default.createElement(ImageButton_1.default, null),
37
+ extensionNames.clearFormatting && react_1.default.createElement(ClearFormattingButton_1.default, null)));
36
38
  };
37
39
  exports.Toolbar = Toolbar;
@@ -0,0 +1,2 @@
1
+ declare const ClearFormattingButton: () => JSX.Element;
2
+ export default ClearFormattingButton;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const react_1 = __importStar(require("react"));
30
+ const react_2 = require("@remirror/react");
31
+ const react_components_1 = require("@remirror/react-components");
32
+ const FormatClearRounded_1 = __importDefault(require("@mui/icons-material/FormatClearRounded"));
33
+ const Button_1 = __importDefault(require("../../../ui/Button/Button"));
34
+ const ClearFormattingButton = () => {
35
+ const { clearFormatting } = (0, react_2.useCommands)();
36
+ const { selection } = (0, react_2.useEditorState)();
37
+ // Checks wether we have specific content selected or not
38
+ const contentSelected = !selection.empty;
39
+ const handleSelect = () => {
40
+ if (clearFormatting.enabled()) {
41
+ clearFormatting();
42
+ }
43
+ };
44
+ const handleShortcut = (0, react_1.useCallback)(() => {
45
+ handleSelect();
46
+ // Prevent other key handlers being run
47
+ return true;
48
+ }, []);
49
+ // When Ctrl+\ is pressed clear formatting, only registered in the toolbar button instance to avoid the key press
50
+ // being double handled.
51
+ (0, react_2.useKeymap)('Mod-\\', handleShortcut);
52
+ return (react_1.default.createElement(react_1.default.Fragment, null,
53
+ react_1.default.createElement(react_components_1.VerticalDivider, null),
54
+ react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: false, isActive: false, icon: react_1.default.createElement(FormatClearRounded_1.default, null), label: `${contentSelected ? 'Clear formatting from selection' : 'Clear all formatting'} (cmd+\\)` })));
55
+ };
56
+ exports.default = ClearFormattingButton;
@@ -38,7 +38,7 @@ const ImageButton = ({ inPopover = false }) => {
38
38
  const active = (0, react_2.useActive)();
39
39
  const selection = (0, react_2.useCurrentSelection)();
40
40
  // if the active selection is not an image, disable the button as it means it will be text
41
- const disabled = !selection.empty && !active.image() && !active.assetImage();
41
+ const disabled = (!selection.empty && !active.image() && !active.assetImage()) || active.codeBlock();
42
42
  const handleClick = () => {
43
43
  if (!showModal) {
44
44
  setShowModal(true);
@@ -37,7 +37,7 @@ const LinkButton = ({ inPopover = false }) => {
37
37
  const { updateLink, updateAssetLink } = (0, react_2.useCommands)();
38
38
  const active = (0, react_2.useActive)();
39
39
  // If the image tool is active, disable the link tool as they shouldn't work at the same time
40
- const disabled = active.image();
40
+ const disabled = active.image() || active.codeBlock();
41
41
  const handleClick = () => {
42
42
  if (!showModal) {
43
43
  setShowModal(true);
@@ -11,11 +11,11 @@ const JustifyAlignButton_1 = __importDefault(require("./JustifyAlign/JustifyAlig
11
11
  const react_components_1 = require("@remirror/react-components");
12
12
  const TextAlignButtons = () => {
13
13
  return (react_1.default.createElement(react_1.default.Fragment, null,
14
- react_1.default.createElement(react_components_1.VerticalDivider, { className: "editor-divider" }),
14
+ react_1.default.createElement(react_components_1.VerticalDivider, null),
15
15
  react_1.default.createElement(LeftAlignButton_1.default, null),
16
16
  react_1.default.createElement(CenterAlignButton_1.default, null),
17
17
  react_1.default.createElement(RightAlignButton_1.default, null),
18
18
  react_1.default.createElement(JustifyAlignButton_1.default, null),
19
- react_1.default.createElement(react_components_1.VerticalDivider, { className: "editor-divider" })));
19
+ react_1.default.createElement(react_components_1.VerticalDivider, null)));
20
20
  };
21
21
  exports.default = TextAlignButtons;
@@ -0,0 +1,2 @@
1
+ declare const CodeBlockButton: () => JSX.Element;
2
+ export default CodeBlockButton;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const react_2 = require("@remirror/react");
8
+ const ToolbarDropdownButton_1 = __importDefault(require("../../../../ui/ToolbarDropdownButton/ToolbarDropdownButton"));
9
+ const CodeRounded_1 = __importDefault(require("@mui/icons-material/CodeRounded"));
10
+ const CodeBlockButton = () => {
11
+ const { toggleCodeBlock } = (0, react_2.useCommands)();
12
+ const active = (0, react_2.useActive)();
13
+ const enabled = toggleCodeBlock.enabled();
14
+ const handleSelect = () => {
15
+ if (toggleCodeBlock.enabled()) {
16
+ toggleCodeBlock();
17
+ }
18
+ };
19
+ return (react_1.default.createElement(ToolbarDropdownButton_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.codeBlock(), label: "Code block", icon: react_1.default.createElement(CodeRounded_1.default, null) },
20
+ react_1.default.createElement("p", null, "Code block")));
21
+ };
22
+ exports.default = CodeBlockButton;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  const react_2 = require("@remirror/react");
8
8
  const ToolbarDropdownButton_1 = __importDefault(require("../../../../ui/ToolbarDropdownButton/ToolbarDropdownButton"));
9
+ const ShortTextRounded_1 = __importDefault(require("@mui/icons-material/ShortTextRounded"));
9
10
  const PreformattedButton = () => {
10
11
  const { togglePreformatted } = (0, react_2.useCommands)();
11
12
  const active = (0, react_2.useActive)();
@@ -15,7 +16,7 @@ const PreformattedButton = () => {
15
16
  togglePreformatted();
16
17
  }
17
18
  };
18
- return (react_1.default.createElement(ToolbarDropdownButton_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.preformatted(), label: "Preformatted" },
19
- react_1.default.createElement("pre", null, "Preformatted")));
19
+ return (react_1.default.createElement(ToolbarDropdownButton_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.preformatted(), label: "Preformatted", icon: react_1.default.createElement(ShortTextRounded_1.default, null) },
20
+ react_1.default.createElement("p", null, "Preformatted")));
20
21
  };
21
22
  exports.default = PreformattedButton;
@@ -7,6 +7,7 @@ const react_1 = __importDefault(require("react"));
7
7
  const HeadingButton_1 = __importDefault(require("./Heading/HeadingButton"));
8
8
  const ParagraphButton_1 = __importDefault(require("./Paragraph/ParagraphButton"));
9
9
  const PreformattedButton_1 = __importDefault(require("./Preformatted/PreformattedButton"));
10
+ const CodeBlockButton_1 = __importDefault(require("./CodeBlock/CodeBlockButton"));
10
11
  const ToolbarDropdown_1 = __importDefault(require("../../../ui/ToolbarDropdown/ToolbarDropdown"));
11
12
  const react_2 = require("@remirror/react");
12
13
  const TextTypeDropdown = () => {
@@ -16,6 +17,10 @@ const TextTypeDropdown = () => {
16
17
  if (active.preformatted()) {
17
18
  return 'Preformatted';
18
19
  }
20
+ // Determine if codeblock is active
21
+ if (active.codeBlock()) {
22
+ return 'Code block';
23
+ }
19
24
  // Determine if a heading is active
20
25
  for (let i = 1; i <= 6; i++) {
21
26
  if (active.heading({ level: i })) {
@@ -34,7 +39,8 @@ const TextTypeDropdown = () => {
34
39
  react_1.default.createElement(HeadingButton_1.default, { level: 4 }),
35
40
  react_1.default.createElement(HeadingButton_1.default, { level: 5 }),
36
41
  react_1.default.createElement(HeadingButton_1.default, { level: 6 }),
37
- react_1.default.createElement(PreformattedButton_1.default, null)),
38
- react_1.default.createElement(react_2.VerticalDivider, { className: "editor-divider" })));
42
+ react_1.default.createElement(PreformattedButton_1.default, null),
43
+ react_1.default.createElement(CodeBlockButton_1.default, null)),
44
+ react_1.default.createElement(react_2.VerticalDivider, null)));
39
45
  };
40
46
  exports.default = TextTypeDropdown;
@@ -0,0 +1,5 @@
1
+ import { PlainExtension, CommandFunction } from '@remirror/core';
2
+ export declare class ClearFormattingExtension extends PlainExtension {
3
+ get name(): "clearFormatting";
4
+ clearFormatting(): CommandFunction;
5
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ClearFormattingExtension = void 0;
10
+ const core_1 = require("@remirror/core");
11
+ const getNodeNamesByGroup_1 = require("../../utils/getNodeNamesByGroup");
12
+ const getMarkNamesByGroup_1 = require("../../utils/getMarkNamesByGroup");
13
+ let ClearFormattingExtension = class ClearFormattingExtension extends core_1.PlainExtension {
14
+ get name() {
15
+ return 'clearFormatting';
16
+ }
17
+ clearFormatting() {
18
+ return ({ dispatch, tr, state }) => {
19
+ const { empty, ranges } = state.selection;
20
+ const schema = state.schema;
21
+ const formattingNodes = (0, getNodeNamesByGroup_1.getNodeNamesByGroup)(schema, core_1.ExtensionTag.FormattingNode);
22
+ const formattingMarks = (0, getMarkNamesByGroup_1.getMarkNamesByGroup)(schema, core_1.ExtensionTag.FormattingMark);
23
+ let isChanged = false;
24
+ ranges.forEach(({ $from, $to }) => {
25
+ // Check if there is a selection or not, if no selection use the doc content size as the range
26
+ state.doc.nodesBetween(empty ? 0 : $from.pos, empty ? state.doc.content.size : $to.pos, (node, pos) => {
27
+ // Clear marks (bold, italic, etc)
28
+ node.marks.forEach((mark) => {
29
+ if (formattingMarks.includes(mark.type.name)) {
30
+ tr.removeMark(pos, pos + node.nodeSize, mark);
31
+ isChanged = true;
32
+ }
33
+ });
34
+ // Leave non-foramtting nodes as-is
35
+ if (!formattingNodes.includes(node.type.name)) {
36
+ return;
37
+ }
38
+ // Clear node attributes & set to paragraph by default
39
+ if (node.type.name === schema.nodes.paragraph.name) {
40
+ const { nodeTextAlignment } = node.attrs;
41
+ if (nodeTextAlignment && nodeTextAlignment !== 'left') {
42
+ tr.setNodeAttribute(pos, 'nodeTextAlignment', null);
43
+ isChanged = true;
44
+ }
45
+ }
46
+ else {
47
+ tr.setNodeMarkup(pos, schema.nodes.paragraph, null, node.marks);
48
+ isChanged = true;
49
+ }
50
+ });
51
+ });
52
+ dispatch?.(tr);
53
+ return isChanged;
54
+ };
55
+ }
56
+ };
57
+ __decorate([
58
+ (0, core_1.command)()
59
+ ], ClearFormattingExtension.prototype, "clearFormatting", null);
60
+ ClearFormattingExtension = __decorate([
61
+ (0, core_1.extension)({})
62
+ ], ClearFormattingExtension);
63
+ exports.ClearFormattingExtension = ClearFormattingExtension;
@@ -0,0 +1,5 @@
1
+ import { CodeBlockExtension } from '@remirror/extension-code-block';
2
+ import { NodeViewMethod } from 'remirror';
3
+ export declare class ExtendedCodeBlockExtension extends CodeBlockExtension {
4
+ createNodeViews(): NodeViewMethod;
5
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExtendedCodeBlockExtension = void 0;
4
+ const extension_code_block_1 = require("@remirror/extension-code-block");
5
+ class ExtendedCodeBlockExtension extends extension_code_block_1.CodeBlockExtension {
6
+ createNodeViews() {
7
+ return (node) => {
8
+ const { language } = node.attrs;
9
+ // This is the pre container for the code block
10
+ const dom = document.createElement('pre');
11
+ dom.setAttribute('spellcheck', 'false');
12
+ dom.classList.add(`code-block`);
13
+ // This is the actual code content in the code block
14
+ const contentDOM = document.createElement('code');
15
+ contentDOM.setAttribute('data-code-block-language', language);
16
+ // Divider between code block and pre container
17
+ const dividerElement = document.createElement('div');
18
+ dividerElement.classList.add('block-divider');
19
+ // The material icon to use
20
+ const codeIcon = document.createElement('svg');
21
+ codeIcon.classList.add('material-symbols-rounded');
22
+ codeIcon.textContent = 'code';
23
+ dom.append(codeIcon);
24
+ dom.append(dividerElement);
25
+ dom.append(contentDOM);
26
+ return { dom, contentDOM };
27
+ };
28
+ }
29
+ }
30
+ exports.ExtendedCodeBlockExtension = ExtendedCodeBlockExtension;
@@ -2,6 +2,7 @@ import { Extension } from '@remirror/core';
2
2
  import { EditorContextOptions } from '../Editor/EditorContext';
3
3
  export declare enum NodeName {
4
4
  Image = "image",
5
+ CodeBlock = "codeBlock",
5
6
  AssetImage = "assetImage",
6
7
  Text = "text"
7
8
  }
@@ -8,9 +8,12 @@ const LinkExtension_1 = require("./LinkExtension/LinkExtension");
8
8
  const ImageExtension_1 = require("./ImageExtension/ImageExtension");
9
9
  const CommandsExtension_1 = require("./CommandsExtension/CommandsExtension");
10
10
  const AssetImageExtension_1 = require("./ImageExtension/AssetImageExtension");
11
+ const CodeBlockExtension_1 = require("./CodeBlockExtension/CodeBlockExtension");
12
+ const ClearFormattingExtension_1 = require("./ClearFormattingExtension/ClearFormattingExtension");
11
13
  var NodeName;
12
14
  (function (NodeName) {
13
15
  NodeName["Image"] = "image";
16
+ NodeName["CodeBlock"] = "codeBlock";
14
17
  NodeName["AssetImage"] = "assetImage";
15
18
  NodeName["Text"] = "text";
16
19
  })(NodeName = exports.NodeName || (exports.NodeName = {}));
@@ -29,6 +32,7 @@ const createExtensions = (context) => {
29
32
  new extensions_1.NodeFormattingExtension({ indents: [] }),
30
33
  new extensions_1.ParagraphExtension(),
31
34
  new PreformattedExtension_1.PreformattedExtension(),
35
+ new CodeBlockExtension_1.ExtendedCodeBlockExtension({ defaultWrap: true }),
32
36
  new extensions_1.UnderlineExtension(),
33
37
  new extensions_1.HistoryExtension(),
34
38
  new ImageExtension_1.ImageExtension(),
@@ -40,6 +44,7 @@ const createExtensions = (context) => {
40
44
  new AssetLinkExtension_1.AssetLinkExtension({
41
45
  matrixDomain: context.matrix.matrixDomain,
42
46
  }),
47
+ new ClearFormattingExtension_1.ClearFormattingExtension(),
43
48
  ];
44
49
  };
45
50
  };
@@ -1,8 +1,10 @@
1
1
  import { ApplySchemaAttributes, CommandFunction, NodeExtension, NodeExtensionSpec, NodeSpecOverride } from '@remirror/core';
2
+ import { NodeViewMethod } from 'remirror';
2
3
  export declare class PreformattedExtension extends NodeExtension {
3
4
  get name(): "preformatted";
4
5
  createTags(): ("block" | "formattingNode" | "textBlock")[];
5
6
  createNodeSpec(extra: ApplySchemaAttributes, override: NodeSpecOverride): NodeExtensionSpec;
7
+ createNodeViews(): NodeViewMethod;
6
8
  /**
7
9
  * Toggle the <pre> for the current block.
8
10
  */
@@ -34,6 +34,29 @@ let PreformattedExtension = class PreformattedExtension extends core_1.NodeExten
34
34
  },
35
35
  };
36
36
  }
37
+ createNodeViews() {
38
+ return (node) => {
39
+ const { nodeTextAlignment } = node.attrs;
40
+ // This is the pre container for the code block
41
+ const dom = document.createElement('div');
42
+ dom.classList.add(`preformatted`);
43
+ // This is the actual code content in the code block
44
+ const contentDOM = document.createElement('pre');
45
+ contentDOM.setAttribute('data-node-text-align', nodeTextAlignment);
46
+ contentDOM.setAttribute('style', `text-align:${nodeTextAlignment}`);
47
+ // Divider between code block and pre container
48
+ const dividerElement = document.createElement('div');
49
+ dividerElement.classList.add('block-divider');
50
+ // The material icon to use
51
+ const codeIcon = document.createElement('svg');
52
+ codeIcon.classList.add('material-symbols-rounded');
53
+ codeIcon.textContent = 'short_text';
54
+ dom.append(codeIcon);
55
+ dom.append(dividerElement);
56
+ dom.append(contentDOM);
57
+ return { dom, contentDOM };
58
+ };
59
+ }
37
60
  /**
38
61
  * Toggle the <pre> for the current block.
39
62
  */
package/lib/index.css CHANGED
@@ -1,3 +1,5 @@
1
+ @import "https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded";
2
+
1
3
  /* src/index.scss */
2
4
  .squiz-fte-scope *,
3
5
  .squiz-fte-scope ::before,
@@ -674,6 +676,36 @@
674
676
  letter-spacing: -0.2px;
675
677
  line-height: 1.25rem;
676
678
  }
679
+ .squiz-fte-scope .code-block,
680
+ .squiz-fte-scope .preformatted {
681
+ display: flex;
682
+ }
683
+ .squiz-fte-scope .code-block .material-symbols-rounded,
684
+ .squiz-fte-scope .preformatted .material-symbols-rounded {
685
+ font-size: 1.125rem;
686
+ --tw-text-opacity: 1;
687
+ color: rgb(79 79 79 / var(--tw-text-opacity));
688
+ pointer-events: none;
689
+ margin-right: 0.375rem;
690
+ }
691
+ .squiz-fte-scope .code-block .block-divider,
692
+ .squiz-fte-scope .preformatted .block-divider {
693
+ --tw-bg-opacity: 1;
694
+ background-color: rgb(237 237 237 / var(--tw-bg-opacity));
695
+ width: 4px;
696
+ border-radius: 0.75rem;
697
+ margin-right: 0.625rem;
698
+ }
699
+ .squiz-fte-scope .code-block code,
700
+ .squiz-fte-scope .code-block pre,
701
+ .squiz-fte-scope .preformatted code,
702
+ .squiz-fte-scope .preformatted pre {
703
+ --tw-text-opacity: 1;
704
+ color: rgb(79 79 79 / var(--tw-text-opacity));
705
+ font-size: 0.75rem;
706
+ padding-top: 0.75rem;
707
+ padding-bottom: 0.75rem;
708
+ }
677
709
  .squiz-fte-scope .squiz-fte-form-group {
678
710
  display: flex;
679
711
  flex-direction: column;
@@ -812,12 +844,12 @@
812
844
  display: flex;
813
845
  justify-items: center;
814
846
  }
815
- .squiz-fte-scope .editor-toolbar > *:not(:first-child, .editor-divider),
816
- .squiz-fte-scope .squiz-fte-scope__floating-popover > *:not(:first-child, .editor-divider) {
847
+ .squiz-fte-scope .editor-toolbar > *:not(:first-child, .MuiDivider-root),
848
+ .squiz-fte-scope__floating-popover > *:not(:first-child, .MuiDivider-root) {
817
849
  margin: 0 0 0 2px;
818
850
  }
819
- .squiz-fte-scope .editor-toolbar .editor-divider,
820
- .squiz-fte-scope .squiz-fte-scope__floating-popover .editor-divider {
851
+ .squiz-fte-scope .editor-toolbar .MuiDivider-root,
852
+ .squiz-fte-scope__floating-popover .MuiDivider-root {
821
853
  margin-top: -0.25rem;
822
854
  margin-bottom: -0.25rem;
823
855
  margin-left: 0.25rem;
@@ -827,12 +859,12 @@
827
859
  height: auto;
828
860
  }
829
861
  .squiz-fte-scope .editor-toolbar .squiz-fte-btn,
830
- .squiz-fte-scope .squiz-fte-scope__floating-popover .squiz-fte-btn {
862
+ .squiz-fte-scope__floating-popover .squiz-fte-btn {
831
863
  padding: 0.25rem;
832
864
  font-weight: 700;
833
865
  }
834
866
  .squiz-fte-scope .editor-toolbar .squiz-fte-btn ~ .squiz-fte-btn,
835
- .squiz-fte-scope .squiz-fte-scope__floating-popover .squiz-fte-btn ~ .squiz-fte-btn {
867
+ .squiz-fte-scope__floating-popover .squiz-fte-btn ~ .squiz-fte-btn {
836
868
  margin-left: 2px;
837
869
  }
838
870
  .squiz-fte-scope__floating-popover {
@@ -851,7 +883,7 @@
851
883
  var(--tw-ring-shadow, 0 0 #0000),
852
884
  var(--tw-shadow);
853
885
  }
854
- .squiz-fte-scope .squiz-fte-scope__floating-popover .editor-divider {
886
+ .squiz-fte-scope__floating-popover .MuiDivider-root {
855
887
  margin-top: 0px;
856
888
  margin-bottom: 0px;
857
889
  }
@@ -959,6 +991,15 @@
959
991
  .squiz-fte-scope .dropdown-button:focus {
960
992
  background-color: rgba(0, 0, 0, 0.04);
961
993
  }
994
+ .squiz-fte-scope .dropdown-button svg {
995
+ font-size: 18px;
996
+ }
997
+ .squiz-fte-scope .dropdown-button .dropdown-button-label {
998
+ display: flex;
999
+ align-items: center;
1000
+ font-size: 14px;
1001
+ gap: 5px;
1002
+ }
962
1003
  .squiz-fte-scope .squiz-fte-checkbox {
963
1004
  --tw-text-opacity: 1;
964
1005
  color: rgb(61 61 61 / var(--tw-text-opacity));
@@ -1042,12 +1083,12 @@
1042
1083
  font-weight: 700;
1043
1084
  }
1044
1085
  .squiz-fte-scope .editor-toolbar .squiz-fte-modal-footer__button,
1045
- .squiz-fte-scope .squiz-fte-scope__floating-popover .squiz-fte-modal-footer__button {
1086
+ .squiz-fte-scope__floating-popover .squiz-fte-modal-footer__button {
1046
1087
  padding: 0.25rem;
1047
1088
  font-weight: 700;
1048
1089
  }
1049
1090
  .squiz-fte-scope .editor-toolbar .squiz-fte-modal-footer__button ~ .squiz-fte-btn,
1050
- .squiz-fte-scope .squiz-fte-scope__floating-popover .squiz-fte-modal-footer__button ~ .squiz-fte-btn {
1091
+ .squiz-fte-scope__floating-popover .squiz-fte-modal-footer__button ~ .squiz-fte-btn {
1051
1092
  margin-left: 2px;
1052
1093
  }
1053
1094
  .squiz-fte-scope .squiz-fte-modal-footer__button {
@@ -4,6 +4,7 @@ type DropdownButtonProps = {
4
4
  isDisabled: boolean;
5
5
  isActive: boolean;
6
6
  label: string;
7
+ icon?: JSX.Element;
7
8
  };
8
- declare const DropdownButton: ({ children, handleOnClick, isDisabled, isActive, label }: DropdownButtonProps) => JSX.Element;
9
+ declare const DropdownButton: ({ children, handleOnClick, isDisabled, isActive, label, icon }: DropdownButtonProps) => JSX.Element;
9
10
  export default DropdownButton;
@@ -4,10 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
- const Check_1 = __importDefault(require("@mui/icons-material/Check"));
8
- const DropdownButton = ({ children, handleOnClick, isDisabled, isActive, label }) => {
7
+ const CheckRounded_1 = __importDefault(require("@mui/icons-material/CheckRounded"));
8
+ const DropdownButton = ({ children, handleOnClick, isDisabled, isActive, label, icon }) => {
9
9
  return (react_1.default.createElement("button", { "aria-label": label, id: "dropdownMenuButton", title: label, type: "button", onClick: handleOnClick, disabled: isDisabled, className: `btn dropdown-button ${isActive ? 'is-active' : ''}` },
10
- react_1.default.createElement("span", null, children || label),
11
- isActive && react_1.default.createElement(Check_1.default, { className: "dropdown-button-icon" })));
10
+ react_1.default.createElement("div", { className: "dropdown-button-label" },
11
+ icon && icon,
12
+ children || label),
13
+ isActive && react_1.default.createElement(CheckRounded_1.default, { className: "dropdown-button-icon" })));
12
14
  };
13
15
  exports.default = DropdownButton;
@@ -4,9 +4,12 @@ exports.remirrorNodeToSquizNode = exports.resolveNodeTag = void 0;
4
4
  const undefinedIfEmpty_1 = require("../../undefinedIfEmpty");
5
5
  const Extensions_1 = require("../../../Extensions/Extensions");
6
6
  const resolveNodeTag = (node) => {
7
- if (node.type.name === 'text') {
7
+ if (node.type.name === Extensions_1.NodeName.Text) {
8
8
  return 'span';
9
9
  }
10
+ if (node.type.name === Extensions_1.NodeName.CodeBlock) {
11
+ return 'code';
12
+ }
10
13
  if (node.type.spec?.toDOM) {
11
14
  const domNode = node.type.spec.toDOM(node);
12
15
  if (domNode instanceof window.Node) {
@@ -63,9 +66,11 @@ const transformFragment = (fragment) => {
63
66
  return transformed;
64
67
  };
65
68
  const transformNode = (node) => {
66
- const attributes = node.type.name === Extensions_1.NodeName.Image ? transformAttributes(node.attrs) : undefined;
67
69
  const formattingOptions = (0, undefinedIfEmpty_1.undefinedIfEmpty)(resolveFormattingOptions(node));
68
70
  const font = (0, undefinedIfEmpty_1.undefinedIfEmpty)(resolveFontOptions(node));
71
+ const attributes = node.type.name === Extensions_1.NodeName.Image || node.type.name === Extensions_1.NodeName.CodeBlock
72
+ ? transformAttributes(node.attrs)
73
+ : undefined;
69
74
  let transformedNode = { type: 'text', value: node.text || '' };
70
75
  // Squiz "text" nodes can't have formatting/font options but Remirror "text" nodes can.
71
76
  // If the node has formatting options wrap in a tag.
@@ -21,6 +21,7 @@ const getNodeType = (node) => {
21
21
  p: 'paragraph',
22
22
  a: Extensions_1.NodeName.Text,
23
23
  span: Extensions_1.NodeName.Text,
24
+ code: Extensions_1.NodeName.CodeBlock,
24
25
  };
25
26
  if (typeMap[node.type]) {
26
27
  return typeMap[node.type];
@@ -43,6 +44,12 @@ const getNodeAttributes = (node) => {
43
44
  title: node.attributes?.title,
44
45
  };
45
46
  }
47
+ else if (node.type === 'tag' && node.tag === 'code') {
48
+ return {
49
+ language: node.attributes?.language || 'markup',
50
+ wrap: node.attributes?.wrap || true,
51
+ };
52
+ }
46
53
  else if (node.type === 'matrix-image') {
47
54
  return {
48
55
  matrixAssetId: node.matrixAssetId,
@@ -0,0 +1,2 @@
1
+ import { EditorSchema, ExtensionTagType } from '@remirror/core';
2
+ export declare const getMarkNamesByGroup: (schema: EditorSchema, group: ExtensionTagType) => string[];
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getMarkNamesByGroup = void 0;
4
+ const getMarkNamesByGroup = (schema, group) => {
5
+ return Object.values(schema.marks)
6
+ .filter((mark) => mark.spec.group?.includes(group))
7
+ .map((mark) => mark.name);
8
+ };
9
+ exports.getMarkNamesByGroup = getMarkNamesByGroup;
@@ -0,0 +1,2 @@
1
+ import { EditorSchema, ExtensionTagType } from '@remirror/core';
2
+ export declare const getNodeNamesByGroup: (schema: EditorSchema, group: ExtensionTagType) => string[];
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getNodeNamesByGroup = void 0;
4
+ const getNodeNamesByGroup = (schema, group) => {
5
+ return Object.values(schema.nodes)
6
+ .filter((node) => node.spec.group?.includes(group))
7
+ .map((node) => node.name);
8
+ };
9
+ exports.getNodeNamesByGroup = getNodeNamesByGroup;