@squiz/formatted-text-editor 1.16.0 → 1.21.1-alpha.10
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.
- package/.eslintrc.json +7 -0
- package/README.md +10 -0
- package/demo/App.tsx +18 -4
- package/demo/index.scss +16 -10
- package/jest.config.ts +8 -9
- package/lib/Editor/Editor.js +18 -13
- package/lib/EditorToolbar/FloatingToolbar.js +50 -20
- package/lib/EditorToolbar/Toolbar.js +33 -24
- package/lib/EditorToolbar/Tools/Bold/BoldButton.js +14 -9
- package/lib/EditorToolbar/Tools/Image/Form/ImageForm.d.ts +17 -0
- package/lib/EditorToolbar/Tools/Image/Form/ImageForm.js +84 -0
- package/lib/EditorToolbar/Tools/Image/ImageButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/Image/ImageButton.js +67 -0
- package/lib/EditorToolbar/Tools/Image/ImageModal.d.ts +8 -0
- package/lib/EditorToolbar/Tools/Image/ImageModal.js +19 -0
- package/lib/EditorToolbar/Tools/Italic/ItalicButton.js +14 -9
- package/lib/EditorToolbar/Tools/Link/Form/LinkForm.js +20 -15
- package/lib/EditorToolbar/Tools/Link/LinkButton.js +42 -14
- package/lib/EditorToolbar/Tools/Link/LinkModal.js +16 -11
- package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.js +13 -8
- package/lib/EditorToolbar/Tools/Redo/RedoButton.js +13 -8
- package/lib/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.js +13 -8
- package/lib/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.js +13 -8
- package/lib/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.js +13 -8
- package/lib/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.js +13 -8
- package/lib/EditorToolbar/Tools/TextAlign/TextAlignButtons.js +19 -14
- package/lib/EditorToolbar/Tools/TextType/Heading/HeadingButton.js +19 -14
- package/lib/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.js +14 -9
- package/lib/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.js +13 -8
- package/lib/EditorToolbar/Tools/TextType/TextTypeDropdown.js +24 -19
- package/lib/EditorToolbar/Tools/Underline/UnderlineButton.js +14 -9
- package/lib/EditorToolbar/Tools/Undo/UndoButton.js +13 -8
- package/lib/EditorToolbar/index.js +18 -2
- package/lib/Extensions/Extensions.d.ts +2 -4
- package/lib/Extensions/Extensions.js +19 -13
- package/lib/Extensions/ImageExtension/ImageExtension.d.ts +3 -0
- package/lib/Extensions/ImageExtension/ImageExtension.js +7 -0
- package/lib/Extensions/LinkExtension/LinkExtension.js +17 -11
- package/lib/Extensions/PreformattedExtension/PreformattedExtension.d.ts +1 -1
- package/lib/Extensions/PreformattedExtension/PreformattedExtension.js +10 -7
- package/lib/FormattedTextEditor.js +7 -2
- package/lib/hooks/index.js +17 -1
- package/lib/hooks/useExtensionNames.js +9 -5
- package/lib/index.css +133 -76
- package/lib/index.d.ts +3 -1
- package/lib/index.js +12 -2
- package/lib/ui/Button/Button.d.ts +11 -0
- package/lib/ui/Button/Button.js +14 -0
- package/lib/ui/Fields/Input/Input.d.ts +4 -0
- package/lib/ui/Fields/Input/Input.js +33 -0
- package/lib/ui/Fields/Select/Select.js +53 -0
- package/lib/ui/Modal/FormModal.js +33 -5
- package/lib/ui/Modal/Modal.js +50 -22
- package/lib/ui/ToolbarDropdown/ToolbarDropdown.js +38 -10
- package/lib/ui/ToolbarDropdownButton/ToolbarDropdownButton.js +11 -6
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.d.ts +10 -0
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +160 -0
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.d.ts +9 -0
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +105 -0
- package/lib/utils/converters/validNodeTypes.d.ts +2 -0
- package/lib/utils/converters/validNodeTypes.js +21 -0
- package/lib/utils/createToolbarPositioner.js +16 -12
- package/lib/utils/getCursorRect.js +5 -1
- package/package.json +7 -3
- package/src/Editor/_editor.scss +2 -49
- package/src/EditorToolbar/FloatingToolbar.tsx +1 -1
- package/src/EditorToolbar/Toolbar.tsx +2 -0
- package/src/EditorToolbar/Tools/Bold/BoldButton.spec.tsx +1 -1
- package/src/EditorToolbar/Tools/Bold/BoldButton.tsx +2 -2
- package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +23 -0
- package/src/EditorToolbar/Tools/Image/Form/ImageForm.tsx +92 -0
- package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +79 -0
- package/src/EditorToolbar/Tools/Image/ImageButton.tsx +57 -0
- package/src/EditorToolbar/Tools/Image/ImageModal.spec.tsx +83 -0
- package/src/EditorToolbar/Tools/Image/ImageModal.tsx +29 -0
- package/src/EditorToolbar/Tools/Italic/ItalicButton.spec.tsx +1 -1
- package/src/EditorToolbar/Tools/Italic/ItalicButton.tsx +2 -2
- package/src/EditorToolbar/Tools/Link/Form/LinkForm.tsx +5 -5
- package/src/EditorToolbar/Tools/Link/LinkButton.tsx +2 -2
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.tsx +2 -2
- package/src/EditorToolbar/Tools/Redo/RedoButton.tsx +2 -2
- package/src/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.tsx +2 -2
- package/src/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.tsx +2 -2
- package/src/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.tsx +2 -2
- package/src/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.tsx +2 -2
- package/src/EditorToolbar/Tools/Underline/Underline.spec.tsx +1 -1
- package/src/EditorToolbar/Tools/Underline/UnderlineButton.tsx +2 -2
- package/src/EditorToolbar/Tools/Undo/UndoButton.tsx +2 -2
- package/src/EditorToolbar/_floating-toolbar.scss +6 -0
- package/src/EditorToolbar/_toolbar.scss +8 -2
- package/src/Extensions/Extensions.ts +5 -2
- package/src/Extensions/ImageExtension/ImageExtension.ts +3 -0
- package/src/Extensions/LinkExtension/LinkExtension.ts +8 -5
- package/src/index.scss +2 -2
- package/src/index.ts +3 -1
- package/src/ui/Button/Button.spec.tsx +44 -0
- package/src/ui/Button/Button.tsx +31 -0
- package/src/ui/{_buttons.scss → Button/_button.scss} +19 -1
- package/src/ui/{Inputs/Text/TextInput.spec.tsx → Fields/Input/Input.spec.tsx} +8 -8
- package/src/ui/{Inputs/Text/TextInput.tsx → Fields/Input/Input.tsx} +4 -4
- package/src/ui/Modal/Modal.tsx +1 -0
- package/src/ui/ToolbarDropdown/ToolbarDropdown.spec.tsx +4 -2
- package/src/ui/ToolbarDropdown/ToolbarDropdown.tsx +1 -1
- package/src/ui/_typography.scss +46 -0
- package/src/utils/converters/mocks/squizNodeJson.mock.ts +75 -0
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +445 -0
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +191 -0
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +307 -0
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +123 -0
- package/src/utils/converters/validNodeTypes.spec.ts +33 -0
- package/src/utils/converters/validNodeTypes.ts +21 -0
- package/tests/renderWithEditor.tsx +2 -2
- package/tsconfig.json +1 -1
- package/lib/ui/Inputs/Select/Select.js +0 -23
- package/lib/ui/Inputs/Text/TextInput.d.ts +0 -4
- package/lib/ui/Inputs/Text/TextInput.js +0 -7
- package/lib/ui/ToolbarButton/ToolbarButton.d.ts +0 -10
- package/lib/ui/ToolbarButton/ToolbarButton.js +0 -5
- package/src/ui/ToolbarButton/ToolbarButton.tsx +0 -26
- package/src/ui/ToolbarButton/_toolbar-button.scss +0 -17
- /package/lib/ui/{Inputs → Fields}/Select/Select.d.ts +0 -0
- /package/src/ui/{Inputs → Fields}/Select/Select.spec.tsx +0 -0
- /package/src/ui/{Inputs → Fields}/Select/Select.tsx +0 -0
package/.eslintrc.json
CHANGED
@@ -30,5 +30,12 @@
|
|
30
30
|
"extensions": [".js", ".jsx", ".ts", ".tsx"]
|
31
31
|
}
|
32
32
|
}
|
33
|
+
},
|
34
|
+
"rules": {
|
35
|
+
// The rules below are listed in the order they appear on the eslint-plugin-react rules page.
|
36
|
+
|
37
|
+
// React
|
38
|
+
// https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules
|
39
|
+
"react/button-has-type": "error"
|
33
40
|
}
|
34
41
|
}
|
package/README.md
CHANGED
@@ -53,6 +53,16 @@ npm run test:watch
|
|
53
53
|
|
54
54
|
When testing text input in the text editor, it is recommended to use the `<MockEditor />` component, which is a simple wrapper around the Remirror component that provides additional functionality for testing (supplying text into the editor).
|
55
55
|
|
56
|
+
### Test coverage
|
57
|
+
|
58
|
+
Test coverage is enforced globally in this package. You can run the test coverage script from the package root:
|
59
|
+
|
60
|
+
```sh
|
61
|
+
npm run test:coverage
|
62
|
+
```
|
63
|
+
|
64
|
+
Coverage will also run as part of the normal test script. Please see ./jest.config.ts for the currently configured thresholds.
|
65
|
+
|
56
66
|
### End to end testing
|
57
67
|
|
58
68
|
This package uses [Cypress](https://docs.cypress.io/) for end to end testing.
|
package/demo/App.tsx
CHANGED
@@ -1,13 +1,24 @@
|
|
1
1
|
import React, { useState } from 'react';
|
2
2
|
import FormattedTextEditor from '../src/FormattedTextEditor';
|
3
3
|
import { RemirrorEventListener, Extension } from '@remirror/core';
|
4
|
+
import { remirrorNodeToSquizNode } from '../src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode';
|
5
|
+
import { squizNodeToRemirrorNode } from '../src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode';
|
6
|
+
import ReactDiffViewer from 'react-diff-viewer-continued';
|
4
7
|
|
5
8
|
function App() {
|
6
|
-
const [doc, setDoc] = useState(
|
9
|
+
const [doc, setDoc] = useState('');
|
10
|
+
const [squizDoc, setSquizDoc] = useState('');
|
11
|
+
const [reconvertedDoc, setReconvertedDoc] = useState('');
|
7
12
|
const [editable, setEditable] = useState(true);
|
13
|
+
|
8
14
|
const handleEditorChange: RemirrorEventListener<Extension> = (parameter) => {
|
9
15
|
if (doc !== parameter.state.doc) {
|
10
|
-
|
16
|
+
const newSquizDoc = remirrorNodeToSquizNode(parameter.state.doc);
|
17
|
+
const newReconvertedDoc = parameter.state.schema.nodeFromJSON(squizNodeToRemirrorNode(newSquizDoc));
|
18
|
+
|
19
|
+
setDoc(JSON.stringify(parameter.state.doc, null, 2));
|
20
|
+
setSquizDoc(JSON.stringify(newSquizDoc, null, 2));
|
21
|
+
setReconvertedDoc(JSON.stringify(newReconvertedDoc, null, 2));
|
11
22
|
}
|
12
23
|
};
|
13
24
|
|
@@ -24,13 +35,16 @@ function App() {
|
|
24
35
|
<div className="page-section">
|
25
36
|
<FormattedTextEditor
|
26
37
|
editable={editable}
|
27
|
-
content={`<p>Hello <a href="https://www.google.com"><strong>Mr Bean</strong></a>, nice to <a href="https://www.google.com">meet you</a
|
38
|
+
content={`<p>Hello <a href="https://www.google.com"><strong>Mr Bean</strong></a>, nice to <a href="https://www.google.com">meet you</a>.<img src="https://media2.giphy.com/media/3o6ozsIxg5legYvggo/giphy.gif" height="150" width="200"/></p>`}
|
28
39
|
onChange={handleEditorChange}
|
29
40
|
/>
|
30
41
|
</div>
|
31
42
|
<h1>Document</h1>
|
32
43
|
<div className="page-section">
|
33
|
-
<
|
44
|
+
<div className="code-section">
|
45
|
+
<ReactDiffViewer oldValue={doc} newValue={reconvertedDoc} splitView={false} showDiffOnly={false} />
|
46
|
+
<ReactDiffViewer oldValue={squizDoc} newValue={squizDoc} splitView={false} showDiffOnly={false} />
|
47
|
+
</div>
|
34
48
|
</div>
|
35
49
|
</div>
|
36
50
|
);
|
package/demo/index.scss
CHANGED
@@ -22,19 +22,25 @@ h1 {
|
|
22
22
|
align-items: center;
|
23
23
|
}
|
24
24
|
|
25
|
-
code {
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
font-size: 0.8rem;
|
31
|
-
height: 40vh;
|
32
|
-
max-height: 40vh;
|
25
|
+
.code-section {
|
26
|
+
display: flex;
|
27
|
+
flex-direction: row;
|
28
|
+
gap: 8px;
|
29
|
+
height: 35vh;
|
33
30
|
overflow: scroll;
|
34
31
|
}
|
35
32
|
|
36
33
|
.remirror-editor {
|
37
|
-
height:
|
38
|
-
max-height:
|
34
|
+
height: 35vh;
|
35
|
+
max-height: 35vh;
|
39
36
|
overflow: scroll;
|
40
37
|
}
|
38
|
+
|
39
|
+
.squiz-fte-modal-footer__button {
|
40
|
+
margin-top: 1rem;
|
41
|
+
}
|
42
|
+
|
43
|
+
.mock-buttons {
|
44
|
+
display: flex;
|
45
|
+
gap: 0.5rem;
|
46
|
+
}
|
package/jest.config.ts
CHANGED
@@ -4,6 +4,14 @@ export default {
|
|
4
4
|
clearMocks: true,
|
5
5
|
collectCoverage: true,
|
6
6
|
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
|
7
|
+
coverageThreshold: {
|
8
|
+
global: {
|
9
|
+
branches: 90,
|
10
|
+
functions: 90,
|
11
|
+
lines: 90,
|
12
|
+
statements: 90,
|
13
|
+
},
|
14
|
+
},
|
7
15
|
errorOnDeprecated: true,
|
8
16
|
fakeTimers: {
|
9
17
|
enableGlobally: true,
|
@@ -18,13 +26,4 @@ export default {
|
|
18
26
|
'^react-dom($|/.+)': '<rootDir>/node_modules/react-dom$1',
|
19
27
|
},
|
20
28
|
setupFilesAfterEnv: ['<rootDir>/jest.bootstrap.ts'],
|
21
|
-
// TODO: enable once directory structure is sorted and we have tests/more complete code being written.
|
22
|
-
// coverageThreshold: {
|
23
|
-
// global: {
|
24
|
-
// branches: 90,
|
25
|
-
// functions: 90,
|
26
|
-
// lines: 90,
|
27
|
-
// statements: 90,
|
28
|
-
// },
|
29
|
-
// },
|
30
29
|
};
|
package/lib/Editor/Editor.js
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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 EditorToolbar_1 = require("../EditorToolbar");
|
9
|
+
const Extensions_1 = require("../Extensions/Extensions");
|
5
10
|
const Editor = ({ content, editable, onChange }) => {
|
6
|
-
const { manager, state, setState } = useRemirror({
|
7
|
-
extensions: Extensions,
|
11
|
+
const { manager, state, setState } = (0, react_2.useRemirror)({
|
12
|
+
extensions: Extensions_1.Extensions,
|
8
13
|
content,
|
9
14
|
selection: 'start',
|
10
15
|
stringHandler: 'html',
|
@@ -13,11 +18,11 @@ const Editor = ({ content, editable, onChange }) => {
|
|
13
18
|
setState(parameter.state);
|
14
19
|
onChange?.(parameter);
|
15
20
|
};
|
16
|
-
return (
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
return (react_1.default.createElement("div", { className: "squiz-fte-scope" },
|
22
|
+
react_1.default.createElement("div", { className: "remirror-theme formatted-text-editor editor-wrapper" },
|
23
|
+
react_1.default.createElement(react_2.Remirror, { manager: manager, state: state, editable: editable, onChange: handleChange, placeholder: "Write something", label: "Text editor" },
|
24
|
+
react_1.default.createElement(EditorToolbar_1.Toolbar, null),
|
25
|
+
react_1.default.createElement(react_2.EditorComponent, null),
|
26
|
+
react_1.default.createElement(EditorToolbar_1.FloatingToolbar, null)))));
|
22
27
|
};
|
23
|
-
|
28
|
+
exports.default = Editor;
|
@@ -1,31 +1,61 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
+
exports.FloatingToolbar = void 0;
|
30
|
+
const react_1 = __importStar(require("react"));
|
31
|
+
const ItalicButton_1 = __importDefault(require("./Tools/Italic/ItalicButton"));
|
32
|
+
const UnderlineButton_1 = __importDefault(require("./Tools/Underline/UnderlineButton"));
|
33
|
+
const BoldButton_1 = __importDefault(require("./Tools/Bold/BoldButton"));
|
34
|
+
const hooks_1 = require("../hooks");
|
35
|
+
const RemoveLinkButton_1 = __importDefault(require("./Tools/Link/RemoveLinkButton"));
|
36
|
+
const LinkButton_1 = __importDefault(require("./Tools/Link/LinkButton"));
|
37
|
+
const react_2 = require("@remirror/react");
|
38
|
+
const react_components_1 = require("@remirror/react-components");
|
39
|
+
const createToolbarPositioner_1 = require("../utils/createToolbarPositioner");
|
11
40
|
// The editor main toolbar
|
12
|
-
|
13
|
-
const extensionNames = useExtensionNames();
|
14
|
-
const positioner = useMemo(() => createToolbarPositioner({ types: ['link'] }), []);
|
15
|
-
const { data } = usePositioner(positioner, []);
|
41
|
+
const FloatingToolbar = () => {
|
42
|
+
const extensionNames = (0, hooks_1.useExtensionNames)();
|
43
|
+
const positioner = (0, react_1.useMemo)(() => (0, createToolbarPositioner_1.createToolbarPositioner)({ types: ['link'] }), []);
|
44
|
+
const { data } = (0, react_2.usePositioner)(positioner, []);
|
16
45
|
let buttons = [
|
17
|
-
extensionNames.bold &&
|
18
|
-
extensionNames.italic &&
|
19
|
-
extensionNames.underline &&
|
46
|
+
extensionNames.bold && react_1.default.createElement(BoldButton_1.default, { key: "bold" }),
|
47
|
+
extensionNames.italic && react_1.default.createElement(ItalicButton_1.default, { key: "italic" }),
|
48
|
+
extensionNames.underline && react_1.default.createElement(UnderlineButton_1.default, { key: "underline" }),
|
20
49
|
];
|
21
50
|
if (data.marks?.link.isExclusivelyActive) {
|
22
51
|
// if all of the selected text is a link show the options to update/remove the link instead of the regular
|
23
52
|
// formatting options.
|
24
|
-
buttons = [
|
53
|
+
buttons = [react_1.default.createElement(LinkButton_1.default, { key: "update-link", inPopover: true }), react_1.default.createElement(RemoveLinkButton_1.default, { key: "remove-link" })];
|
25
54
|
}
|
26
55
|
else if (!data.marks?.link.isActive) {
|
27
56
|
// if none of the selected text is a link show the option to create a link.
|
28
|
-
buttons.push(
|
57
|
+
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 }));
|
29
58
|
}
|
30
|
-
return (
|
59
|
+
return (react_1.default.createElement(react_2.FloatingToolbar, { className: "squiz-fte-scope squiz-fte-scope__floating-popover", positioner: positioner }, buttons));
|
31
60
|
};
|
61
|
+
exports.FloatingToolbar = FloatingToolbar;
|
@@ -1,25 +1,34 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
import UnderlineButton from './Tools/Underline/UnderlineButton';
|
5
|
-
import BoldButton from './Tools/Bold/BoldButton';
|
6
|
-
import TextAlignButtons from './Tools/TextAlign/TextAlignButtons';
|
7
|
-
import UndoButton from './Tools/Undo/UndoButton';
|
8
|
-
import RedoButton from './Tools/Redo/RedoButton';
|
9
|
-
import TextTypeDropdown from './Tools/TextType/TextTypeDropdown';
|
10
|
-
import { useExtensionNames } from '../hooks';
|
11
|
-
import LinkButton from './Tools/Link/LinkButton';
|
12
|
-
export const Toolbar = () => {
|
13
|
-
const extensionNames = useExtensionNames();
|
14
|
-
return (React.createElement(RemirrorToolbar, { className: "remirror-toolbar editor-toolbar" },
|
15
|
-
extensionNames.history && (React.createElement(React.Fragment, null,
|
16
|
-
React.createElement(UndoButton, null),
|
17
|
-
React.createElement(RedoButton, null),
|
18
|
-
React.createElement(VerticalDivider, { className: "editor-divider" }))),
|
19
|
-
extensionNames.heading && extensionNames.paragraph && extensionNames.preformatted && React.createElement(TextTypeDropdown, null),
|
20
|
-
extensionNames.bold && React.createElement(BoldButton, null),
|
21
|
-
extensionNames.italic && React.createElement(ItalicButton, null),
|
22
|
-
extensionNames.underline && React.createElement(UnderlineButton, null),
|
23
|
-
extensionNames.nodeFormatting && React.createElement(TextAlignButtons, null),
|
24
|
-
extensionNames.link && React.createElement(LinkButton, null)));
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
25
4
|
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.Toolbar = void 0;
|
7
|
+
const react_1 = __importDefault(require("react"));
|
8
|
+
const react_components_1 = require("@remirror/react-components");
|
9
|
+
const ItalicButton_1 = __importDefault(require("./Tools/Italic/ItalicButton"));
|
10
|
+
const UnderlineButton_1 = __importDefault(require("./Tools/Underline/UnderlineButton"));
|
11
|
+
const BoldButton_1 = __importDefault(require("./Tools/Bold/BoldButton"));
|
12
|
+
const TextAlignButtons_1 = __importDefault(require("./Tools/TextAlign/TextAlignButtons"));
|
13
|
+
const UndoButton_1 = __importDefault(require("./Tools/Undo/UndoButton"));
|
14
|
+
const RedoButton_1 = __importDefault(require("./Tools/Redo/RedoButton"));
|
15
|
+
const TextTypeDropdown_1 = __importDefault(require("./Tools/TextType/TextTypeDropdown"));
|
16
|
+
const hooks_1 = require("../hooks");
|
17
|
+
const LinkButton_1 = __importDefault(require("./Tools/Link/LinkButton"));
|
18
|
+
const ImageButton_1 = __importDefault(require("./Tools/Image/ImageButton"));
|
19
|
+
const Toolbar = () => {
|
20
|
+
const extensionNames = (0, hooks_1.useExtensionNames)();
|
21
|
+
return (react_1.default.createElement(react_components_1.Toolbar, { className: "remirror-toolbar editor-toolbar" },
|
22
|
+
extensionNames.history && (react_1.default.createElement(react_1.default.Fragment, null,
|
23
|
+
react_1.default.createElement(UndoButton_1.default, null),
|
24
|
+
react_1.default.createElement(RedoButton_1.default, null),
|
25
|
+
react_1.default.createElement(react_components_1.VerticalDivider, { className: "editor-divider" }))),
|
26
|
+
extensionNames.heading && extensionNames.paragraph && extensionNames.preformatted && react_1.default.createElement(TextTypeDropdown_1.default, null),
|
27
|
+
extensionNames.bold && react_1.default.createElement(BoldButton_1.default, null),
|
28
|
+
extensionNames.italic && react_1.default.createElement(ItalicButton_1.default, null),
|
29
|
+
extensionNames.underline && react_1.default.createElement(UnderlineButton_1.default, null),
|
30
|
+
extensionNames.nodeFormatting && react_1.default.createElement(TextAlignButtons_1.default, null),
|
31
|
+
extensionNames.link && react_1.default.createElement(LinkButton_1.default, null),
|
32
|
+
extensionNames.image && react_1.default.createElement(ImageButton_1.default, null)));
|
33
|
+
};
|
34
|
+
exports.Toolbar = Toolbar;
|
@@ -1,17 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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 Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
|
+
const FormatBoldRounded_1 = __importDefault(require("@mui/icons-material/FormatBoldRounded"));
|
5
10
|
const BoldButton = () => {
|
6
|
-
const { toggleBold } = useCommands();
|
7
|
-
const chain = useChainedCommands();
|
8
|
-
const active = useActive();
|
11
|
+
const { toggleBold } = (0, react_2.useCommands)();
|
12
|
+
const chain = (0, react_2.useChainedCommands)();
|
13
|
+
const active = (0, react_2.useActive)();
|
9
14
|
const enabled = toggleBold.enabled();
|
10
15
|
const handleSelect = () => {
|
11
16
|
if (toggleBold.enabled()) {
|
12
17
|
chain.toggleBold().focus().run();
|
13
18
|
}
|
14
19
|
};
|
15
|
-
return (
|
20
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.bold(), icon: react_1.default.createElement(FormatBoldRounded_1.default, null), label: "Bold (cmd+B)" }));
|
16
21
|
};
|
17
|
-
|
22
|
+
exports.default = BoldButton;
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { ReactElement } from 'react';
|
2
|
+
import { SubmitHandler } from 'react-hook-form';
|
3
|
+
import { ImageAttributes } from '@remirror/extension-image/dist-types/image-extension';
|
4
|
+
export type UpdateImageOptions = ImageAttributes & {
|
5
|
+
src: string;
|
6
|
+
alt: string;
|
7
|
+
width: number;
|
8
|
+
height: number;
|
9
|
+
};
|
10
|
+
export type ImageFormData = Pick<UpdateImageOptions, 'src' | 'alt' | 'width' | 'height'>;
|
11
|
+
export type FormProps = {
|
12
|
+
data: Partial<ImageFormData>;
|
13
|
+
onSubmit: SubmitHandler<ImageFormData>;
|
14
|
+
};
|
15
|
+
export type Dimensions = 'width' | 'height';
|
16
|
+
declare const ImageForm: ({ data, onSubmit }: FormProps) => ReactElement;
|
17
|
+
export default ImageForm;
|
@@ -0,0 +1,84 @@
|
|
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 Input_1 = require("../../../../ui/Fields/Input/Input");
|
31
|
+
const react_hook_form_1 = require("react-hook-form");
|
32
|
+
const react_image_size_1 = require("react-image-size");
|
33
|
+
const Button_1 = __importDefault(require("../../../../ui/Button/Button"));
|
34
|
+
const LinkOff_1 = __importDefault(require("@mui/icons-material/LinkOff"));
|
35
|
+
const InsertLinkRounded_1 = __importDefault(require("@mui/icons-material/InsertLinkRounded"));
|
36
|
+
const ImageForm = ({ data, onSubmit }) => {
|
37
|
+
const { register, handleSubmit, setValue } = (0, react_hook_form_1.useForm)({
|
38
|
+
defaultValues: data,
|
39
|
+
});
|
40
|
+
const [aspectRatioFromWidth, setAspectRatioFromWidth] = (0, react_1.useState)(9 / 16);
|
41
|
+
const [aspectRatioFromHeight, setAspectRatioFromHeight] = (0, react_1.useState)(16 / 9);
|
42
|
+
const [aspectRatioLocked, setAspectRatioLocked] = (0, react_1.useState)(true);
|
43
|
+
const setDimensionsFromURL = (e) => {
|
44
|
+
// get the new url, calculate the width and height and set those fields
|
45
|
+
(0, react_image_size_1.getImageSize)(e.target.value)
|
46
|
+
.then(({ width, height }) => {
|
47
|
+
setValue('width', width);
|
48
|
+
setValue('height', height);
|
49
|
+
setAspectRatioFromWidth(height / width);
|
50
|
+
setAspectRatioFromHeight(width / height);
|
51
|
+
})
|
52
|
+
.catch((errorMessage) => {
|
53
|
+
// TODO: we will use this when we add validation in a follow-up ticket
|
54
|
+
console.log(errorMessage);
|
55
|
+
});
|
56
|
+
};
|
57
|
+
const calculateDimensions = () => {
|
58
|
+
if (aspectRatioLocked) {
|
59
|
+
const currentTarget = event?.target;
|
60
|
+
const type = currentTarget.name;
|
61
|
+
const currentValue = currentTarget.value;
|
62
|
+
const otherValue = type === 'width' ? 'height' : 'width';
|
63
|
+
const aspectRatio = type === 'width' ? aspectRatioFromWidth : aspectRatioFromHeight;
|
64
|
+
const newValue = Math.round(aspectRatio * Number(currentValue) * 100) / 100;
|
65
|
+
setValue(otherValue, newValue);
|
66
|
+
}
|
67
|
+
};
|
68
|
+
const toggleAspectRatio = () => {
|
69
|
+
setAspectRatioLocked(!aspectRatioLocked);
|
70
|
+
};
|
71
|
+
return (react_1.default.createElement("form", { className: "squiz-fte-form", onSubmit: handleSubmit(onSubmit) },
|
72
|
+
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
73
|
+
react_1.default.createElement(Input_1.Input, { label: "Source", ...register('src'), onBlur: setDimensionsFromURL })),
|
74
|
+
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
75
|
+
react_1.default.createElement(Input_1.Input, { label: "Alternative description", ...register('alt') })),
|
76
|
+
react_1.default.createElement("div", { className: "flex flex-row items-end" },
|
77
|
+
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
78
|
+
react_1.default.createElement(Input_1.Input, { label: "Width", ...register('width'), type: "number", name: "width", onChange: calculateDimensions })),
|
79
|
+
react_1.default.createElement("div", { className: "flex mx-1 mb-2" },
|
80
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: toggleAspectRatio, isActive: false, icon: aspectRatioLocked ? react_1.default.createElement(InsertLinkRounded_1.default, null) : react_1.default.createElement(LinkOff_1.default, null), label: "Constrain properties", isDisabled: false })),
|
81
|
+
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
82
|
+
react_1.default.createElement(Input_1.Input, { label: "Height", ...register('height'), type: "number", name: "height", onChange: calculateDimensions })))));
|
83
|
+
};
|
84
|
+
exports.default = ImageForm;
|
@@ -0,0 +1,67 @@
|
|
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 ImageRounded_1 = __importDefault(require("@mui/icons-material/ImageRounded"));
|
31
|
+
const ImageModal_1 = __importDefault(require("./ImageModal"));
|
32
|
+
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
33
|
+
const react_2 = require("@remirror/react");
|
34
|
+
const ImageButton = () => {
|
35
|
+
const [showModal, setShowModal] = (0, react_1.useState)(false);
|
36
|
+
const { insertImage } = (0, react_2.useCommands)();
|
37
|
+
const active = showModal;
|
38
|
+
const handleClick = () => {
|
39
|
+
if (!showModal) {
|
40
|
+
// form element are uncontrolled, let the event loop run to
|
41
|
+
// update the selected text in state before showing the modal.
|
42
|
+
requestAnimationFrame(() => {
|
43
|
+
setShowModal(true);
|
44
|
+
});
|
45
|
+
}
|
46
|
+
};
|
47
|
+
const insertImageFromData = (data) => {
|
48
|
+
const { src, alt, width, height } = data;
|
49
|
+
if (src) {
|
50
|
+
insertImage({ src, alt, width, height });
|
51
|
+
}
|
52
|
+
};
|
53
|
+
const handleSubmit = (data) => {
|
54
|
+
insertImageFromData(data);
|
55
|
+
setShowModal(false);
|
56
|
+
};
|
57
|
+
const handleShortcut = (0, react_1.useCallback)(() => {
|
58
|
+
handleClick();
|
59
|
+
// Prevent other key handlers being run
|
60
|
+
return true;
|
61
|
+
}, []);
|
62
|
+
(0, react_2.useKeymap)('Mod-l', handleShortcut);
|
63
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
64
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active, icon: react_1.default.createElement(ImageRounded_1.default, null), label: "Image (cmd+L)", isDisabled: false }),
|
65
|
+
showModal && react_1.default.createElement(ImageModal_1.default, { onCancel: () => setShowModal(false), onSubmit: handleSubmit })));
|
66
|
+
};
|
67
|
+
exports.default = ImageButton;
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { ImageFormData } from './Form/ImageForm';
|
2
|
+
import { SubmitHandler } from 'react-hook-form';
|
3
|
+
type ImageModalProps = {
|
4
|
+
onCancel: () => void;
|
5
|
+
onSubmit: SubmitHandler<ImageFormData>;
|
6
|
+
};
|
7
|
+
declare const ImageModal: ({ onCancel, onSubmit }: ImageModalProps) => JSX.Element;
|
8
|
+
export default ImageModal;
|
@@ -0,0 +1,19 @@
|
|
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 remirror_1 = require("remirror");
|
7
|
+
const ImageForm_1 = __importDefault(require("./Form/ImageForm"));
|
8
|
+
const react_1 = __importDefault(require("react"));
|
9
|
+
const react_2 = require("@remirror/react");
|
10
|
+
const FormModal_1 = __importDefault(require("../../../ui/Modal/FormModal"));
|
11
|
+
const ImageModal = ({ onCancel, onSubmit }) => {
|
12
|
+
const { helpers, view: { state }, } = (0, react_2.useRemirrorContext)();
|
13
|
+
const selection = (0, react_2.useCurrentSelection)();
|
14
|
+
const currentImage = (0, remirror_1.getMarkRanges)(selection, 'image')[0];
|
15
|
+
const selectedImage = helpers.getTextBetween(selection.from, selection.to, state.doc);
|
16
|
+
return (react_1.default.createElement(FormModal_1.default, { title: "Image", onCancel: onCancel },
|
17
|
+
react_1.default.createElement(ImageForm_1.default, { data: { ...currentImage?.mark.attrs, src: selectedImage }, onSubmit: onSubmit })));
|
18
|
+
};
|
19
|
+
exports.default = ImageModal;
|
@@ -1,17 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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 Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
|
+
const FormatItalicRounded_1 = __importDefault(require("@mui/icons-material/FormatItalicRounded"));
|
5
10
|
const ItalicButton = () => {
|
6
|
-
const { toggleItalic } = useCommands();
|
7
|
-
const chain = useChainedCommands();
|
8
|
-
const active = useActive();
|
11
|
+
const { toggleItalic } = (0, react_2.useCommands)();
|
12
|
+
const chain = (0, react_2.useChainedCommands)();
|
13
|
+
const active = (0, react_2.useActive)();
|
9
14
|
const enabled = toggleItalic.enabled();
|
10
15
|
const handleSelect = () => {
|
11
16
|
if (toggleItalic.enabled()) {
|
12
17
|
chain.toggleItalic().focus().run();
|
13
18
|
}
|
14
19
|
};
|
15
|
-
return (
|
20
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.italic(), icon: react_1.default.createElement(FormatItalicRounded_1.default, null), label: "Italic (cmd+I)" }));
|
16
21
|
};
|
17
|
-
|
22
|
+
exports.default = ItalicButton;
|