@monolith-forensics/monolith-ui 1.1.46 → 1.1.47
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/dist/MonolithUIProvider/GlobalStyle.js +18 -0
- package/dist/MonolithUIProvider/MonolithUIProvider.d.ts +1 -1
- package/dist/RichTextEditor/Enums/Extensions.d.ts +15 -0
- package/dist/RichTextEditor/Enums/Extensions.js +16 -0
- package/dist/RichTextEditor/Enums/SlashCommands.d.ts +13 -0
- package/dist/RichTextEditor/Enums/SlashCommands.js +14 -0
- package/dist/RichTextEditor/Extensions/SlashCommandList.d.ts +9 -0
- package/dist/RichTextEditor/Extensions/SlashCommandList.js +138 -0
- package/dist/RichTextEditor/Extensions/getSlashCommand.d.ts +10 -0
- package/dist/RichTextEditor/Extensions/getSlashCommand.js +296 -0
- package/dist/RichTextEditor/Extensions/getTiptapExtensions.d.ts +11 -0
- package/dist/RichTextEditor/Extensions/getTiptapExtensions.js +178 -0
- package/dist/RichTextEditor/Plugins/UploadImagesPlugin.d.ts +16 -0
- package/dist/RichTextEditor/Plugins/UploadImagesPlugin.js +114 -0
- package/dist/RichTextEditor/RichTextEditor.d.ts +22 -0
- package/dist/RichTextEditor/RichTextEditor.js +302 -0
- package/dist/RichTextEditor/Toolbar/Control.d.ts +14 -0
- package/dist/RichTextEditor/Toolbar/Control.js +33 -0
- package/dist/RichTextEditor/Toolbar/Controls.d.ts +21 -0
- package/dist/RichTextEditor/Toolbar/Controls.js +120 -0
- package/dist/RichTextEditor/Toolbar/ControlsGroup.d.ts +8 -0
- package/dist/RichTextEditor/Toolbar/ControlsGroup.js +26 -0
- package/dist/RichTextEditor/Toolbar/Labels.d.ts +41 -0
- package/dist/RichTextEditor/Toolbar/Labels.js +46 -0
- package/dist/RichTextEditor/Toolbar/Toolbar.d.ts +7 -0
- package/dist/RichTextEditor/Toolbar/Toolbar.js +17 -0
- package/dist/RichTextEditor/Toolbar/index.d.ts +1 -0
- package/dist/RichTextEditor/Toolbar/index.js +1 -0
- package/dist/RichTextEditor/index.d.ts +3 -0
- package/dist/RichTextEditor/index.js +3 -0
- package/dist/Utilities/calculateFileHash.d.ts +8 -0
- package/dist/Utilities/calculateFileHash.js +38 -0
- package/dist/Utilities/readFileAsBuffer.d.ts +2 -0
- package/dist/Utilities/readFileAsBuffer.js +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +19 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import TiptapImage from "@tiptap/extension-image";
|
|
2
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
3
|
+
import HorizontalRule from "@tiptap/extension-horizontal-rule";
|
|
4
|
+
import Underline from "@tiptap/extension-underline";
|
|
5
|
+
import TextAlign from "@tiptap/extension-text-align";
|
|
6
|
+
import Table from "@tiptap/extension-table";
|
|
7
|
+
import TableCell from "@tiptap/extension-table-cell";
|
|
8
|
+
import TableHeader from "@tiptap/extension-table-header";
|
|
9
|
+
import TableRow from "@tiptap/extension-table-row";
|
|
10
|
+
import Placeholder from "@tiptap/extension-placeholder";
|
|
11
|
+
import { InputRule, Extension } from "@tiptap/core";
|
|
12
|
+
import Focus from "@tiptap/extension-focus";
|
|
13
|
+
import UploadImagesPlugin from "../Plugins/UploadImagesPlugin";
|
|
14
|
+
import getSlashCommand from "./getSlashCommand";
|
|
15
|
+
import Extensions from "../Enums/Extensions";
|
|
16
|
+
const CustomImage = TiptapImage.extend({
|
|
17
|
+
// Add data-uuid attribute to image
|
|
18
|
+
addAttributes() {
|
|
19
|
+
var _a;
|
|
20
|
+
return Object.assign(Object.assign({}, (_a = this.parent) === null || _a === void 0 ? void 0 : _a.call(this)), { "data-uuid": {
|
|
21
|
+
default: null,
|
|
22
|
+
} });
|
|
23
|
+
},
|
|
24
|
+
addProseMirrorPlugins() {
|
|
25
|
+
return [UploadImagesPlugin()];
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
const CustomStorage = Extension.create({
|
|
29
|
+
name: "customStorage",
|
|
30
|
+
addStorage() {
|
|
31
|
+
const data = {}; // Add index signature to the data object
|
|
32
|
+
return {
|
|
33
|
+
get: (key) => {
|
|
34
|
+
return data[key];
|
|
35
|
+
},
|
|
36
|
+
set: (key, value) => {
|
|
37
|
+
data[key] = value;
|
|
38
|
+
},
|
|
39
|
+
delete: (key) => {
|
|
40
|
+
delete data[key];
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const getTipTapExtensions = ({ extensions = [], slashCommands = [], handleImageUpload, }) => {
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
name: "starterKit",
|
|
49
|
+
category: "default",
|
|
50
|
+
extension: StarterKit.configure({
|
|
51
|
+
bulletList: {
|
|
52
|
+
HTMLAttributes: {
|
|
53
|
+
class: "",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
orderedList: {
|
|
57
|
+
HTMLAttributes: {
|
|
58
|
+
class: "",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
listItem: {
|
|
62
|
+
HTMLAttributes: {
|
|
63
|
+
class: "",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
horizontalRule: false,
|
|
67
|
+
dropcursor: {
|
|
68
|
+
color: "#DBEAFE",
|
|
69
|
+
width: 4,
|
|
70
|
+
},
|
|
71
|
+
gapcursor: false,
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: Extensions.Underline,
|
|
76
|
+
category: "default",
|
|
77
|
+
extension: Underline,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: Extensions.TextAlign,
|
|
81
|
+
category: "default",
|
|
82
|
+
extension: TextAlign.configure({ types: ["heading", "paragraph"] }),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: Extensions.Focus,
|
|
86
|
+
category: "default",
|
|
87
|
+
extension: Focus.configure({ className: "has-focus", mode: "all" }),
|
|
88
|
+
},
|
|
89
|
+
// patch to fix horizontal rule bug: https://github.com/ueberdosis/tiptap/pull/3859#issuecomment-1536799740
|
|
90
|
+
{
|
|
91
|
+
name: Extensions.HorizontalRule,
|
|
92
|
+
extension: HorizontalRule.extend({
|
|
93
|
+
addInputRules() {
|
|
94
|
+
return [
|
|
95
|
+
new InputRule({
|
|
96
|
+
find: /^(?:---|—-|___\s|\*\*\*\s)$/,
|
|
97
|
+
handler: ({ state, range }) => {
|
|
98
|
+
const attributes = {};
|
|
99
|
+
const { tr } = state;
|
|
100
|
+
const start = range.from;
|
|
101
|
+
let end = range.to;
|
|
102
|
+
tr.insert(start - 1, this.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end));
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
];
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: Extensions.Table,
|
|
111
|
+
extension: Table.configure({
|
|
112
|
+
resizable: true,
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: Extensions.TableRow,
|
|
117
|
+
extension: TableRow,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: Extensions.TableHeader,
|
|
121
|
+
extension: TableHeader,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: Extensions.TableCell,
|
|
125
|
+
extension: TableCell,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: Extensions.Image,
|
|
129
|
+
extension: CustomImage.configure({
|
|
130
|
+
allowBase64: true,
|
|
131
|
+
inline: true,
|
|
132
|
+
HTMLAttributes: {
|
|
133
|
+
class: "monolith-image",
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: Extensions.Placeholder,
|
|
139
|
+
extension: Placeholder.configure({
|
|
140
|
+
placeholder: ({ node }) => {
|
|
141
|
+
// check if node type is tablcell
|
|
142
|
+
// if it is, return empty string
|
|
143
|
+
// table cells always contain a paragraph node, so no need to add placeholder to it
|
|
144
|
+
// other it shows two placeholders wihtin the cell
|
|
145
|
+
if (node.type.name === "tableCell") {
|
|
146
|
+
return "";
|
|
147
|
+
}
|
|
148
|
+
if (!extensions.includes(Extensions.SlashCommand)) {
|
|
149
|
+
return "Enter text...";
|
|
150
|
+
}
|
|
151
|
+
if (node.type.name === "heading") {
|
|
152
|
+
return `Heading ${node.attrs.level}`;
|
|
153
|
+
}
|
|
154
|
+
return "Press '/' for commands...";
|
|
155
|
+
},
|
|
156
|
+
includeChildren: true,
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: Extensions.CustomStorage,
|
|
161
|
+
category: "default",
|
|
162
|
+
extension: CustomStorage,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: Extensions.SlashCommand,
|
|
166
|
+
extension: getSlashCommand({
|
|
167
|
+
commands: slashCommands,
|
|
168
|
+
handleImageUpload,
|
|
169
|
+
}),
|
|
170
|
+
},
|
|
171
|
+
]
|
|
172
|
+
.filter((ext) => {
|
|
173
|
+
return (extensions.includes(ext.name) ||
|
|
174
|
+
ext.category === "default");
|
|
175
|
+
})
|
|
176
|
+
.map((ext) => ext.extension);
|
|
177
|
+
};
|
|
178
|
+
export default getTipTapExtensions;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Plugin } from "@tiptap/pm/state";
|
|
2
|
+
import { DecorationSet, EditorView } from "@tiptap/pm/view";
|
|
3
|
+
interface HandleImageUploadProps {
|
|
4
|
+
file: File;
|
|
5
|
+
name: string;
|
|
6
|
+
id: string;
|
|
7
|
+
md5: string;
|
|
8
|
+
sha1: string;
|
|
9
|
+
sha256: string;
|
|
10
|
+
}
|
|
11
|
+
export interface HandleImageUpload {
|
|
12
|
+
(props: HandleImageUploadProps): Promise<string> | undefined;
|
|
13
|
+
}
|
|
14
|
+
export declare const startImageUpload: (file: File, view: EditorView, pos: number, handleImageUpload: HandleImageUpload | undefined) => void;
|
|
15
|
+
declare const UploadImagesPlugin: () => Plugin<DecorationSet>;
|
|
16
|
+
export default UploadImagesPlugin;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
2
|
+
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
3
|
+
import { nanoid } from "nanoid";
|
|
4
|
+
import calculateFileHash from "../../Utilities/calculateFileHash";
|
|
5
|
+
const uploadKey = new PluginKey("upload-image");
|
|
6
|
+
function findPlaceholder(state, id) {
|
|
7
|
+
const decos = uploadKey.getState(state);
|
|
8
|
+
const found = decos.find(null, null, (spec) => spec.id === id);
|
|
9
|
+
return found.length ? found[0].from : null;
|
|
10
|
+
}
|
|
11
|
+
export const startImageUpload = (file, view, pos, handleImageUpload) => {
|
|
12
|
+
// check if the file is an image
|
|
13
|
+
if (!file.type.includes("image/")) {
|
|
14
|
+
alert("File type not supported.");
|
|
15
|
+
return;
|
|
16
|
+
// check if the file size is less than 20MB
|
|
17
|
+
}
|
|
18
|
+
else if (file.size / 1024 / 1024 > 20) {
|
|
19
|
+
alert("File size too big (max 20MB).");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// A fresh object to act as the ID for this upload
|
|
23
|
+
const id = nanoid(25);
|
|
24
|
+
// Replace the selection with a placeholder
|
|
25
|
+
const tr = view.state.tr;
|
|
26
|
+
if (!tr.selection.empty)
|
|
27
|
+
tr.deleteSelection();
|
|
28
|
+
const reader = new FileReader();
|
|
29
|
+
reader.readAsDataURL(file);
|
|
30
|
+
reader.onload = () => {
|
|
31
|
+
tr.setMeta(uploadKey, {
|
|
32
|
+
add: {
|
|
33
|
+
id,
|
|
34
|
+
pos,
|
|
35
|
+
src: reader.result,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
view.dispatch(tr);
|
|
39
|
+
};
|
|
40
|
+
calculateFileHash(file).then((hashes) => {
|
|
41
|
+
var _a;
|
|
42
|
+
const md5 = hashes.md5Hash;
|
|
43
|
+
const sha1 = hashes.sha1Hash;
|
|
44
|
+
const sha256 = hashes.sha256Hash;
|
|
45
|
+
(_a = handleImageUpload === null || handleImageUpload === void 0 ? void 0 : handleImageUpload({
|
|
46
|
+
file,
|
|
47
|
+
name: `${id}.png`,
|
|
48
|
+
id,
|
|
49
|
+
md5,
|
|
50
|
+
sha1,
|
|
51
|
+
sha256,
|
|
52
|
+
})) === null || _a === void 0 ? void 0 : _a.then((src) => {
|
|
53
|
+
if (!src)
|
|
54
|
+
return;
|
|
55
|
+
const { schema } = view.state;
|
|
56
|
+
let pos = findPlaceholder(view.state, id);
|
|
57
|
+
// If the content around the placeholder has been deleted, drop
|
|
58
|
+
// the image
|
|
59
|
+
if (pos == null)
|
|
60
|
+
return;
|
|
61
|
+
// Otherwise, insert it at the placeholder's position, and remove
|
|
62
|
+
// the placeholder
|
|
63
|
+
// When BLOB_READ_WRITE_TOKEN is not valid or unavailable, read
|
|
64
|
+
// the image locally
|
|
65
|
+
const imageSrc = typeof src === "object" ? reader.result : src;
|
|
66
|
+
const node = schema.nodes.image.create({
|
|
67
|
+
src: imageSrc,
|
|
68
|
+
alt: `${id}.png`,
|
|
69
|
+
"data-uuid": id,
|
|
70
|
+
title: `Filename: ${id}.png`,
|
|
71
|
+
});
|
|
72
|
+
const transaction = view.state.tr
|
|
73
|
+
.replaceWith(pos, pos, node)
|
|
74
|
+
.setMeta(uploadKey, { remove: { id } });
|
|
75
|
+
view.dispatch(transaction);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
const UploadImagesPlugin = () => new Plugin({
|
|
80
|
+
key: uploadKey,
|
|
81
|
+
state: {
|
|
82
|
+
init() {
|
|
83
|
+
return DecorationSet.empty;
|
|
84
|
+
},
|
|
85
|
+
apply(tr, set) {
|
|
86
|
+
set = set.map(tr.mapping, tr.doc);
|
|
87
|
+
// See if the transaction adds or removes any placeholders
|
|
88
|
+
const action = tr.getMeta(this);
|
|
89
|
+
if (action && action.add) {
|
|
90
|
+
const { id, pos, src } = action.add;
|
|
91
|
+
const placeholder = document.createElement("div");
|
|
92
|
+
placeholder.setAttribute("class", "img-placeholder");
|
|
93
|
+
const image = document.createElement("img");
|
|
94
|
+
image.setAttribute("class", "monolith-image uploading");
|
|
95
|
+
image.src = src;
|
|
96
|
+
placeholder.appendChild(image);
|
|
97
|
+
const deco = Decoration.widget(pos + 1, placeholder, {
|
|
98
|
+
id,
|
|
99
|
+
});
|
|
100
|
+
set = set.add(tr.doc, [deco]);
|
|
101
|
+
}
|
|
102
|
+
else if (action && action.remove) {
|
|
103
|
+
set = set.remove(set.find(undefined, undefined, (spec) => spec.id === action.remove.id));
|
|
104
|
+
}
|
|
105
|
+
return set;
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
props: {
|
|
109
|
+
decorations(state) {
|
|
110
|
+
return this.getState(state);
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
export default UploadImagesPlugin;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Editor } from "@tiptap/react";
|
|
2
|
+
import { ExtensionType } from "./Extensions/getTiptapExtensions";
|
|
3
|
+
import { HandleImageUpload } from "./Plugins/UploadImagesPlugin";
|
|
4
|
+
interface RichTextEditorProps {
|
|
5
|
+
className?: string;
|
|
6
|
+
editorInstanceRef?: React.RefObject<Editor>;
|
|
7
|
+
extensions?: ExtensionType[];
|
|
8
|
+
slashCommands?: any[];
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
readOnly?: boolean;
|
|
11
|
+
height?: string;
|
|
12
|
+
font?: string;
|
|
13
|
+
showToolbar?: boolean;
|
|
14
|
+
saving?: boolean;
|
|
15
|
+
onChange?: (value: string) => void;
|
|
16
|
+
handleImageUpload?: HandleImageUpload;
|
|
17
|
+
style?: React.CSSProperties;
|
|
18
|
+
}
|
|
19
|
+
declare const RichTextEditor: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<RichTextEditorProps & import("react").RefAttributes<unknown>, "ref"> & {
|
|
20
|
+
ref?: ((instance: unknown) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<unknown> | null | undefined;
|
|
21
|
+
}, never>> & string & Omit<import("react").ForwardRefExoticComponent<RichTextEditorProps & import("react").RefAttributes<unknown>>, keyof import("react").Component<any, {}, any>>;
|
|
22
|
+
export default RichTextEditor;
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect } from "react";
|
|
3
|
+
import styled from "styled-components";
|
|
4
|
+
import { EditorContent, useEditor } from "@tiptap/react";
|
|
5
|
+
import Toolbar from "./Toolbar";
|
|
6
|
+
import getTipTapExtensions from "./Extensions/getTiptapExtensions";
|
|
7
|
+
import Extensions from "./Enums/Extensions";
|
|
8
|
+
const RichTextEditor = styled(forwardRef(({ className, editorInstanceRef, defaultValue = "", readOnly = false, font, showToolbar = true, saving = false, extensions = [], slashCommands = [], onChange, handleImageUpload, style, }, ref) => {
|
|
9
|
+
// check if image extension is included
|
|
10
|
+
if (extensions === null || extensions === void 0 ? void 0 : extensions.includes(Extensions.Image)) {
|
|
11
|
+
// Ensure that handleImageUpload is provided
|
|
12
|
+
if (!handleImageUpload) {
|
|
13
|
+
throw new Error("handleImageUpload is required when using the image extension.");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const editor = useEditor({
|
|
17
|
+
content: defaultValue,
|
|
18
|
+
editable: !readOnly,
|
|
19
|
+
extensions: getTipTapExtensions({
|
|
20
|
+
extensions,
|
|
21
|
+
slashCommands,
|
|
22
|
+
handleImageUpload,
|
|
23
|
+
}),
|
|
24
|
+
onUpdate: ({ editor }) => {
|
|
25
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(editor.getHTML());
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const _ref = editorInstanceRef;
|
|
30
|
+
if (editorInstanceRef) {
|
|
31
|
+
_ref.current = editor;
|
|
32
|
+
}
|
|
33
|
+
}, [editor]);
|
|
34
|
+
return (_jsxs("div", { className: className, children: [showToolbar && _jsx(Toolbar, { editor: editor }), _jsx(EditorContent, { className: "editor-content", editor: editor, "data-font": font || null, style: style })] }));
|
|
35
|
+
})) `
|
|
36
|
+
position: relative;
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
flex: 1 1 auto;
|
|
40
|
+
overflow-y: auto;
|
|
41
|
+
justify-content: flex-start;
|
|
42
|
+
align-items: center;
|
|
43
|
+
|
|
44
|
+
.editor-content {
|
|
45
|
+
height: ${({ height }) => height || "100%"};
|
|
46
|
+
width: 100%;
|
|
47
|
+
overflow-y: auto;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.editor-content[data-font="Times New Roman"] {
|
|
51
|
+
font-family: "Times New Roman", Times, serif;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.editor-content[data-font="Courier New"] {
|
|
55
|
+
font-family: "Courier New", Courier, monospace;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.editor-content[data-font="Arial"] {
|
|
59
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.editor-content[data-font="Segoe UI"] {
|
|
63
|
+
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.editor-content[data-font="Verdana"] {
|
|
67
|
+
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.editor-content[data-font="Roboto"] {
|
|
71
|
+
font-family: Roboto, sans-serif;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.ProseMirror {
|
|
75
|
+
font-weight: 400;
|
|
76
|
+
outline: none;
|
|
77
|
+
height: 100%;
|
|
78
|
+
padding: 1rem 4rem;
|
|
79
|
+
border-radius: 8px;
|
|
80
|
+
border: 1px solid transparent;
|
|
81
|
+
white-space: break-spaces;
|
|
82
|
+
word-break: break-word;
|
|
83
|
+
text-rendering: optimizeLegibility;
|
|
84
|
+
|
|
85
|
+
table {
|
|
86
|
+
border-collapse: collapse;
|
|
87
|
+
width: 100%;
|
|
88
|
+
|
|
89
|
+
th,
|
|
90
|
+
td {
|
|
91
|
+
border: 1px solid ${({ theme }) => theme.palette.divider};
|
|
92
|
+
padding: 0.5rem;
|
|
93
|
+
min-width: 100px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// for table header
|
|
97
|
+
th {
|
|
98
|
+
border: 1px solid ${({ theme }) => theme.palette.divider};
|
|
99
|
+
padding: 0.5rem;
|
|
100
|
+
min-width: 100px;
|
|
101
|
+
background-color: ${({ theme }) => theme.palette.action.hover};
|
|
102
|
+
font-weight: 500;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
h1 {
|
|
107
|
+
font-size: 1.5rem;
|
|
108
|
+
line-height: 2rem;
|
|
109
|
+
margin: 0;
|
|
110
|
+
}
|
|
111
|
+
h2 {
|
|
112
|
+
font-size: 1.25rem;
|
|
113
|
+
line-height: 1.75rem;
|
|
114
|
+
margin: 0;
|
|
115
|
+
}
|
|
116
|
+
h3 {
|
|
117
|
+
font-size: 1.125rem;
|
|
118
|
+
line-height: 1.75rem;
|
|
119
|
+
margin: 0;
|
|
120
|
+
}
|
|
121
|
+
h4 {
|
|
122
|
+
font-size: 1rem;
|
|
123
|
+
line-height: 1.5rem;
|
|
124
|
+
margin: 0;
|
|
125
|
+
}
|
|
126
|
+
p {
|
|
127
|
+
margin: 0;
|
|
128
|
+
font-size: 0.8rem;
|
|
129
|
+
line-height: 1.5rem;
|
|
130
|
+
}
|
|
131
|
+
ul {
|
|
132
|
+
margin: 0;
|
|
133
|
+
}
|
|
134
|
+
ol {
|
|
135
|
+
margin: 0;
|
|
136
|
+
}
|
|
137
|
+
.editor-link {
|
|
138
|
+
color: ${({ theme }) => theme.palette.primary.main};
|
|
139
|
+
}
|
|
140
|
+
.editor-link:hover {
|
|
141
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
142
|
+
text-decoration: underline;
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
// Set title attribute
|
|
145
|
+
title: "Click to open link";
|
|
146
|
+
}
|
|
147
|
+
img {
|
|
148
|
+
max-width: 100%;
|
|
149
|
+
max-height: 25rem;
|
|
150
|
+
border: 1px solid ${({ theme }) => theme.palette.divider};
|
|
151
|
+
}
|
|
152
|
+
img.has-focus {
|
|
153
|
+
border: 1px solid ${({ theme }) => theme.palette.primary.main};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.ProseMirror .is-editor-empty:first-child::before {
|
|
158
|
+
content: attr(data-placeholder);
|
|
159
|
+
float: left;
|
|
160
|
+
color: #888;
|
|
161
|
+
pointer-events: none;
|
|
162
|
+
height: 0;
|
|
163
|
+
}
|
|
164
|
+
.ProseMirror .is-empty::before {
|
|
165
|
+
content: attr(data-placeholder);
|
|
166
|
+
float: left;
|
|
167
|
+
color: #888;
|
|
168
|
+
pointer-events: none;
|
|
169
|
+
height: 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.ProseMirror .monolith-image.uploading {
|
|
173
|
+
opacity: 0.25;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.ProseMirror .img-placeholder {
|
|
177
|
+
position: relative;
|
|
178
|
+
width: fit-content;
|
|
179
|
+
|
|
180
|
+
&:before {
|
|
181
|
+
content: "";
|
|
182
|
+
box-sizing: border-box;
|
|
183
|
+
position: absolute;
|
|
184
|
+
top: calc(50% - 18px);
|
|
185
|
+
left: calc(50% - 18px);
|
|
186
|
+
width: 36px;
|
|
187
|
+
height: 36px;
|
|
188
|
+
border-radius: 50%;
|
|
189
|
+
border: 3px solid ${({ theme }) => theme.palette.primary.main};
|
|
190
|
+
border-top-color: ${({ theme }) => theme.palette.divider};
|
|
191
|
+
animation: spinning 0.6s linear infinite;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@keyframes spinning {
|
|
195
|
+
to {
|
|
196
|
+
transform: rotate(360deg);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.floating-menu {
|
|
202
|
+
display: flex;
|
|
203
|
+
background-color: #0d0d0d10;
|
|
204
|
+
padding: 0.2rem;
|
|
205
|
+
border-radius: 0.5rem;
|
|
206
|
+
|
|
207
|
+
button {
|
|
208
|
+
border: none;
|
|
209
|
+
outline: none;
|
|
210
|
+
background: none;
|
|
211
|
+
font-size: 0.5rem;
|
|
212
|
+
font-weight: 500;
|
|
213
|
+
padding: 0 0.2rem;
|
|
214
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
215
|
+
|
|
216
|
+
&:hover,
|
|
217
|
+
&:focus,
|
|
218
|
+
&.is-active {
|
|
219
|
+
opacity: 1;
|
|
220
|
+
color: ${({ theme }) => theme.palette.primary.main};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
ul[data-type="taskList"] {
|
|
226
|
+
list-style: none;
|
|
227
|
+
padding: 0;
|
|
228
|
+
|
|
229
|
+
li {
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: center;
|
|
232
|
+
margin: 0.25rem 0;
|
|
233
|
+
|
|
234
|
+
> label {
|
|
235
|
+
flex: 0 0 auto;
|
|
236
|
+
margin-right: 0.5rem;
|
|
237
|
+
user-select: none;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
> div {
|
|
241
|
+
flex: 1 1 auto;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
input[type="checkbox"] {
|
|
246
|
+
cursor: pointer;
|
|
247
|
+
margin: 0;
|
|
248
|
+
-webkit-appearance: none;
|
|
249
|
+
appearance: none;
|
|
250
|
+
border: 2px solid ${({ theme }) => theme.palette.primary.main};
|
|
251
|
+
border-radius: 0.125rem;
|
|
252
|
+
background-color: ${({ theme }) => theme.palette.background.default};
|
|
253
|
+
width: 1.25rem;
|
|
254
|
+
height: 1.25rem;
|
|
255
|
+
display: grid;
|
|
256
|
+
place-content: center;
|
|
257
|
+
|
|
258
|
+
&:hover {
|
|
259
|
+
border-color: ${({ theme }) => theme.palette.primary.main};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
&:active {
|
|
263
|
+
background-color: ${({ theme }) => theme.palette.divider};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
&::before {
|
|
267
|
+
content: "";
|
|
268
|
+
width: 0.75em;
|
|
269
|
+
height: 0.75em;
|
|
270
|
+
transform: scale(0);
|
|
271
|
+
transition: 120ms transform ease-in-out;
|
|
272
|
+
box-shadow: inset 1em 1em;
|
|
273
|
+
transform-origin: center;
|
|
274
|
+
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
|
275
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
&:checked::before {
|
|
279
|
+
transform: scale(1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
|
285
|
+
color: ${({ theme }) => theme.palette.text.secondary};
|
|
286
|
+
opacity: 0.5;
|
|
287
|
+
text-decoration: line-through;
|
|
288
|
+
text-decoration-thickness: 1px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.ProseMirror .note-tag {
|
|
292
|
+
border: 1px solid ${({ theme }) => theme.palette.divider};
|
|
293
|
+
border-radius: 0.25rem;
|
|
294
|
+
padding: 0.125rem 0.25rem;
|
|
295
|
+
font-size: 0.75rem;
|
|
296
|
+
line-height: 1rem;
|
|
297
|
+
color: ${({ theme }) => theme.palette.text.secondary};
|
|
298
|
+
font-weight: 500;
|
|
299
|
+
margin: 0 0.125rem;
|
|
300
|
+
}
|
|
301
|
+
`;
|
|
302
|
+
export default RichTextEditor;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Editor } from "@tiptap/react";
|
|
2
|
+
interface ControlProps {
|
|
3
|
+
className?: string;
|
|
4
|
+
editor: Editor | null;
|
|
5
|
+
isActive?: any;
|
|
6
|
+
operation: {
|
|
7
|
+
name: string;
|
|
8
|
+
attributes?: any;
|
|
9
|
+
};
|
|
10
|
+
label: string;
|
|
11
|
+
icon: any;
|
|
12
|
+
}
|
|
13
|
+
declare const Control: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<ControlProps, never>> & string & Omit<({ className, editor, isActive, operation, label, icon: Icon, }: ControlProps) => import("react/jsx-runtime").JSX.Element, keyof import("react").Component<any, {}, any>>;
|
|
14
|
+
export default Control;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import styled from "styled-components";
|
|
3
|
+
import Labels from "./Labels";
|
|
4
|
+
const Control = styled(({ className, editor, isActive, operation, label, icon: Icon, }) => {
|
|
5
|
+
var _a;
|
|
6
|
+
const _label = Labels[label];
|
|
7
|
+
const active = (isActive === null || isActive === void 0 ? void 0 : isActive.name)
|
|
8
|
+
? (_a = editor === null || editor === void 0 ? void 0 : editor.isActive) === null || _a === void 0 ? void 0 : _a.call(editor, isActive.name, isActive.attributes)
|
|
9
|
+
: false;
|
|
10
|
+
return (_jsx("button", { className: className + (active ? " active" : ""), "aria-label": _label, "data-active": active, title: _label, onClick: () => {
|
|
11
|
+
const focus = editor === null || editor === void 0 ? void 0 : editor.chain().focus();
|
|
12
|
+
focus[operation.name](operation.attributes).run();
|
|
13
|
+
}, children: _jsx(Icon, { size: "16px" }) }));
|
|
14
|
+
}) `
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
align-items: center;
|
|
18
|
+
width: 1.5rem;
|
|
19
|
+
height: 1.5rem;
|
|
20
|
+
padding: 0px;
|
|
21
|
+
background-color: transparent;
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
24
|
+
&:hover {
|
|
25
|
+
background-color: ${({ theme }) => theme.palette.action.hover};
|
|
26
|
+
}
|
|
27
|
+
&.active {
|
|
28
|
+
background-color: ${({ theme }) => theme.palette.action.hover};
|
|
29
|
+
color: ${({ theme }) => theme.palette.primary.main};
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
Control.displayName = "Control";
|
|
33
|
+
export default Control;
|