@bigbinary/neeto-editor 0.2.0 → 0.2.4

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 (61) hide show
  1. package/index.js +28 -7
  2. package/package.json +7 -5
  3. package/tailwind.config.js +13 -9
  4. package/webpack.config.js +9 -1
  5. package/webpack.dev.config.js +9 -1
  6. package/public/favicon.ico +0 -0
  7. package/public/index.html +0 -28
  8. package/public/robots.txt +0 -3
  9. package/src/App.js +0 -8
  10. package/src/Common/Avatar.js +0 -168
  11. package/src/Common/CodeBlock.js +0 -11
  12. package/src/Common/Description.js +0 -12
  13. package/src/Common/Dropdown/index.js +0 -118
  14. package/src/Common/Heading.js +0 -13
  15. package/src/Common/HighlightText.js +0 -7
  16. package/src/Common/Icons/HashtagFilled.js +0 -59
  17. package/src/Common/Icons/TextColor.js +0 -35
  18. package/src/Common/Icons/index.js +0 -2
  19. package/src/Common/ListItems.js +0 -17
  20. package/src/Editor/CustomExtensions/BubbleMenu/index.js +0 -92
  21. package/src/Editor/CustomExtensions/CodeBlock/CodeBlockComponent.js +0 -12
  22. package/src/Editor/CustomExtensions/CodeBlock/ExtensionConfig.js +0 -10
  23. package/src/Editor/CustomExtensions/Embeds.js +0 -72
  24. package/src/Editor/CustomExtensions/FixedMenu/FontSizeOption.js +0 -32
  25. package/src/Editor/CustomExtensions/FixedMenu/TextColorOption.js +0 -29
  26. package/src/Editor/CustomExtensions/FixedMenu/constants.js +0 -3
  27. package/src/Editor/CustomExtensions/FixedMenu/index.js +0 -186
  28. package/src/Editor/CustomExtensions/Image/ExtensionConfig.js +0 -47
  29. package/src/Editor/CustomExtensions/Image/Uploader.js +0 -41
  30. package/src/Editor/CustomExtensions/Mention/ExtensionConfig.js +0 -67
  31. package/src/Editor/CustomExtensions/Mention/MentionList.js +0 -96
  32. package/src/Editor/CustomExtensions/Mention/helpers.js +0 -23
  33. package/src/Editor/CustomExtensions/Placeholder/ExtensionConfig.js +0 -81
  34. package/src/Editor/CustomExtensions/Placeholder/helpers.js +0 -18
  35. package/src/Editor/CustomExtensions/SlashCommands/Commands.js +0 -20
  36. package/src/Editor/CustomExtensions/SlashCommands/CommandsList.js +0 -109
  37. package/src/Editor/CustomExtensions/SlashCommands/ExtensionConfig.js +0 -205
  38. package/src/Editor/CustomExtensions/Variable/ExtensionConfig.js +0 -208
  39. package/src/Editor/CustomExtensions/Variable/VariableList.js +0 -45
  40. package/src/Editor/CustomExtensions/Variable/VariableSuggestion.js +0 -20
  41. package/src/Editor/CustomExtensions/Variable/helpers.js +0 -31
  42. package/src/Editor/CustomExtensions/Variable/index.js +0 -35
  43. package/src/Editor/CustomExtensions/useCustomExtensions.js +0 -87
  44. package/src/Editor/index.js +0 -92
  45. package/src/Editor/sharedState.js +0 -8
  46. package/src/examples/constants.js +0 -95
  47. package/src/examples/index.js +0 -186
  48. package/src/hooks/useOutsideClick.js +0 -19
  49. package/src/index.js +0 -10
  50. package/src/index.scss +0 -41
  51. package/src/styles/abstracts/_mixins.scss +0 -20
  52. package/src/styles/abstracts/_neeto-ui-variables.scss +0 -36
  53. package/src/styles/abstracts/_variables.scss +0 -10
  54. package/src/styles/components/_avatar.scss +0 -105
  55. package/src/styles/components/_codeblock.scss +0 -16
  56. package/src/styles/components/_command-list.scss +0 -19
  57. package/src/styles/components/_dropdown.scss +0 -69
  58. package/src/styles/components/_editor-variables.scss +0 -12
  59. package/src/styles/components/_editor.scss +0 -98
  60. package/src/styles/components/_fixed-menu.scss +0 -13
  61. package/src/utils/common.js +0 -13
@@ -1,23 +0,0 @@
1
- export const createMentionSuggestions = (
2
- items = [],
3
- { limit = 5, showImage = false } = {}
4
- ) => {
5
- const allSuggestions = items.map((item) => {
6
- let suggestionObj;
7
- if (typeof item === "string") {
8
- suggestionObj = { key: item, label: item };
9
- } else if (typeof item === "object") {
10
- suggestionObj = { ...item };
11
- }
12
- suggestionObj.showImage = showImage;
13
-
14
- return suggestionObj;
15
- });
16
-
17
- return ({ query }) =>
18
- allSuggestions
19
- .filter((suggestion) =>
20
- suggestion.label.toLowerCase().startsWith(query.toLowerCase())
21
- )
22
- .slice(0, limit);
23
- };
@@ -1,81 +0,0 @@
1
- import { Extension } from "@tiptap/core";
2
- import { Decoration, DecorationSet } from "prosemirror-view";
3
- import { Plugin } from "prosemirror-state";
4
-
5
- import { placeholderGenerator } from "./helpers";
6
-
7
- export default Extension.create({
8
- name: "placeholder",
9
-
10
- addOptions() {
11
- return {
12
- excludeNodeTypes: ["variable"],
13
- emptyEditorClass: "is-editor-empty",
14
- emptyNodeClass: "is-empty",
15
- placeholder: "Write something …",
16
- showOnlyWhenEditable: true,
17
- showOnlyCurrent: true,
18
- includeChildren: false,
19
- };
20
- },
21
-
22
- addProseMirrorPlugins() {
23
- return [
24
- new Plugin({
25
- props: {
26
- decorations: ({ doc, selection }) => {
27
- const active =
28
- this.editor.isEditable || !this.options.showOnlyWhenEditable;
29
- const { anchor } = selection;
30
- const decorations = [];
31
-
32
- if (!active) {
33
- return;
34
- }
35
-
36
- doc.descendants((node, pos) => {
37
- const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
38
- const isEmpty = !node.isLeaf && !node.childCount;
39
-
40
- const isExcluded = this.options.excludeNodeTypes.includes(
41
- node.type.name
42
- );
43
-
44
- if (
45
- (hasAnchor || !this.options.showOnlyCurrent) &&
46
- !isExcluded &&
47
- isEmpty
48
- ) {
49
- const classes = [this.options.emptyNodeClass];
50
-
51
- if (this.editor.isEmpty) {
52
- classes.push(this.options.emptyEditorClass);
53
- }
54
-
55
- const decoration = Decoration.node(pos, pos + node.nodeSize, {
56
- class: classes.join(" "),
57
- "data-placeholder":
58
- typeof this.options.placeholder === "function"
59
- ? this.options.placeholder({
60
- editor: this.editor,
61
- node,
62
- pos,
63
- })
64
- : this.options.placeholder,
65
- });
66
-
67
- decorations.push(decoration);
68
- }
69
-
70
- return this.options.includeChildren;
71
- });
72
-
73
- return DecorationSet.create(doc, decorations);
74
- },
75
- },
76
- }),
77
- ];
78
- },
79
- });
80
-
81
- export { placeholderGenerator };
@@ -1,18 +0,0 @@
1
- import isPlainObject from "lodash.isplainobject";
2
-
3
- export const placeholderGenerator = (placeholder) => {
4
- const type = typeof placeholder;
5
-
6
- if (type === "string" || type === "funtion") {
7
- return placeholder;
8
- }
9
-
10
- if (isPlainObject(placeholder)) {
11
- return ({ node }) => {
12
- const { name } = node.type;
13
- return placeholder[name];
14
- };
15
- }
16
-
17
- return "";
18
- };
@@ -1,20 +0,0 @@
1
- import { Extension } from "@tiptap/core";
2
- import { PluginKey } from "prosemirror-state";
3
- import Suggestion from "@tiptap/suggestion";
4
-
5
- export const CommandsPluginKey = new PluginKey("commands");
6
-
7
- export default Extension.create({
8
- name: "commands",
9
-
10
- defaultOptions: {},
11
-
12
- addProseMirrorPlugins() {
13
- return [
14
- Suggestion({
15
- editor: this.editor,
16
- ...this.options.suggestion,
17
- }),
18
- ];
19
- },
20
- });
@@ -1,109 +0,0 @@
1
- import React from "react";
2
- import classnames from "classnames";
3
-
4
- class CommandsList extends React.Component {
5
- constructor(props) {
6
- super(props);
7
-
8
- this.state = {
9
- selectedIndex: 0,
10
- };
11
- }
12
-
13
- componentDidUpdate(oldProps) {
14
- if (this.props.items !== oldProps.items) {
15
- this.setState({
16
- selectedIndex: 0,
17
- });
18
- }
19
- }
20
-
21
- onKeyDown = ({ event }) => {
22
- if (event.key === "ArrowUp") {
23
- this.upHandler();
24
- return true;
25
- }
26
-
27
- if (event.key === "ArrowDown") {
28
- this.downHandler();
29
- return true;
30
- }
31
-
32
- if (event.key === "Enter") {
33
- this.enterHandler();
34
- return true;
35
- }
36
-
37
- return false;
38
- };
39
-
40
- upHandler = () => {
41
- this.setState({
42
- selectedIndex:
43
- (this.state.selectedIndex + this.props.items.length - 1) %
44
- this.props.items.length,
45
- });
46
- };
47
-
48
- downHandler = () => {
49
- this.setState({
50
- selectedIndex: (this.state.selectedIndex + 1) % this.props.items.length,
51
- });
52
- };
53
-
54
- enterHandler = () => {
55
- this.selectItem(this.state.selectedIndex);
56
- };
57
-
58
- selectItem = (index) => {
59
- const item = this.props.items[index];
60
-
61
- if (item) {
62
- const { editor, range } = this.props;
63
- item.command({ editor, range });
64
- }
65
- };
66
-
67
- render() {
68
- return (
69
- <div className="relative p-3 space-y-2 overflow-hidden rounded shadow editor-command-list--root">
70
- {this.props.items.map((item, index) => (
71
- <Item
72
- key={item.title}
73
- item={item}
74
- index={index}
75
- selectedIndex={this.state.selectedIndex}
76
- selectItem={() => this.selectItem(index)}
77
- />
78
- ))}
79
- </div>
80
- );
81
- }
82
- }
83
-
84
- const Item = ({ item, selectedIndex, index, selectItem }) => {
85
- const Icon = item.Icon;
86
- return (
87
- <div
88
- className={classnames(
89
- "flex items-center w-full px-4 py-2 space-x-4 transition-all duration-100 ease-in-out editor-command-list--item cursor-pointer rounded",
90
- {
91
- selected_item: index === selectedIndex,
92
- }
93
- )}
94
- onClick={() => selectItem(index)}
95
- >
96
- {Icon && (
97
- <div className="p-1 text-gray-100 rounded-sm">
98
- <Icon size={18} />
99
- </div>
100
- )}
101
- <div className="flex flex-col text-gray-100">
102
- <span className="text-sm font-semibold">{item.title}</span>
103
- <span className="text-xs text-gray-300">{item.description}</span>
104
- </div>
105
- </div>
106
- );
107
- };
108
-
109
- export default CommandsList;
@@ -1,205 +0,0 @@
1
- import sharedState from "../../sharedState";
2
- import { Extension } from "@tiptap/core";
3
- import CommandsList from "./CommandsList";
4
- import { PluginKey } from "prosemirror-state";
5
- import Suggestion from "@tiptap/suggestion";
6
- import tippy from "tippy.js";
7
- import { ReactRenderer } from "@tiptap/react";
8
- import {
9
- Paragraph,
10
- TextH1,
11
- TextH2,
12
- ListDot,
13
- ListNumber,
14
- Blockquote,
15
- Image,
16
- Video,
17
- Code,
18
- } from "@bigbinary/neeto-icons";
19
-
20
- export const CommandsPluginKey = new PluginKey("commands");
21
-
22
- export default Extension.create({
23
- addOptions() {
24
- return {
25
- HTMLAttributes: {
26
- class: "commands",
27
- },
28
- suggestion: {
29
- char: "/",
30
- startOfLine: false,
31
- pluginKey: CommandsPluginKey,
32
- command: ({ editor, range, props }) => {
33
- props.command({ editor, range });
34
- },
35
-
36
- items: () => {
37
- return [
38
- {
39
- title: "Paragraph",
40
- description: "Add a plain text block",
41
- Icon: Paragraph,
42
- command: ({ editor, range }) => {
43
- editor
44
- .chain()
45
- .focus()
46
- .deleteRange(range)
47
- .setNode("paragraph")
48
- .run();
49
- },
50
- },
51
- {
52
- title: "H1",
53
- description: "Add a big heading",
54
- Icon: TextH1,
55
- command: ({ editor, range }) => {
56
- editor
57
- .chain()
58
- .focus()
59
- .deleteRange(range)
60
- .setNode("heading", { level: 1 })
61
- .run();
62
- },
63
- },
64
- {
65
- title: "H2",
66
- description: "Add a sub-heading",
67
- Icon: TextH2,
68
- command: ({ editor, range }) => {
69
- editor
70
- .chain()
71
- .focus()
72
- .deleteRange(range)
73
- .setNode("heading", { level: 2 })
74
- .run();
75
- },
76
- },
77
- {
78
- title: "Numbered list",
79
- description: "Add a list with numbering",
80
- Icon: ListNumber,
81
- command: ({ editor, range }) => {
82
- editor
83
- .chain()
84
- .focus()
85
- .deleteRange(range)
86
- .toggleOrderedList()
87
- .run();
88
- },
89
- },
90
- {
91
- title: "Bulleted list",
92
- description: "Add an list bullets",
93
- Icon: ListDot,
94
- command: ({ editor, range }) => {
95
- editor
96
- .chain()
97
- .focus()
98
- .deleteRange(range)
99
- .toggleBulletList()
100
- .run();
101
- },
102
- },
103
- {
104
- title: "Blockquote",
105
- description: "Add a quote",
106
- Icon: Blockquote,
107
- command: ({ editor, range }) => {
108
- editor
109
- .chain()
110
- .focus()
111
- .deleteRange(range)
112
- .toggleBlockquote()
113
- .run();
114
- },
115
- },
116
- {
117
- title: "Image",
118
- description: "Add an image",
119
- Icon: Image,
120
- command: ({ editor, range }) => {
121
- sharedState.showImageUpload = true;
122
- sharedState.range = range;
123
- editor.chain().focus().deleteRange(range).run();
124
- },
125
- },
126
- {
127
- title: "Youtube/Vimeo",
128
- description: "Embed a video from major services",
129
- Icon: Video,
130
- command: ({ editor, range }) => {
131
- const embedURL = prompt("Please enter Youtube/Vimeo embed URL");
132
- editor
133
- .chain()
134
- .focus()
135
- .deleteRange(range)
136
- .setExternalVideo({ src: embedURL })
137
- .run();
138
- },
139
- },
140
- {
141
- title: "Code block",
142
- description: "Add a code block with syntax highlighting",
143
- Icon: Code,
144
- command: ({ editor, range }) => {
145
- editor
146
- .chain()
147
- .focus()
148
- .deleteRange(range)
149
- .toggleCodeBlock()
150
- .run();
151
- },
152
- },
153
- ];
154
- },
155
-
156
- render: () => {
157
- let reactRenderer;
158
- let popup;
159
-
160
- return {
161
- onStart: (props) => {
162
- reactRenderer = new ReactRenderer(CommandsList, {
163
- props,
164
- editor: props.editor,
165
- });
166
-
167
- popup = tippy("body", {
168
- getReferenceClientRect: props.clientRect,
169
- appendTo: () => document.body,
170
- content: reactRenderer.element,
171
- showOnCreate: true,
172
- interactive: true,
173
- trigger: "manual",
174
- placement: "bottom-start",
175
- });
176
- },
177
- onUpdate(props) {
178
- reactRenderer.updateProps(props);
179
-
180
- popup[0].setProps({
181
- getReferenceClientRect: props.clientRect,
182
- });
183
- },
184
- onKeyDown(props) {
185
- return reactRenderer.ref?.onKeyDown(props);
186
- },
187
- onExit() {
188
- popup[0].destroy();
189
- reactRenderer.destroy();
190
- },
191
- };
192
- },
193
- },
194
- };
195
- },
196
-
197
- addProseMirrorPlugins() {
198
- return [
199
- Suggestion({
200
- editor: this.editor,
201
- ...this.options.suggestion,
202
- }),
203
- ];
204
- },
205
- });
@@ -1,208 +0,0 @@
1
- import tippy from "tippy.js";
2
- import Suggestion from "@tiptap/suggestion";
3
- import { Node, mergeAttributes } from "@tiptap/core";
4
- import { ReactRenderer } from "@tiptap/react";
5
- import { PluginKey } from "prosemirror-state";
6
-
7
- import VariableSuggestion from "./VariableSuggestion";
8
-
9
- export const VariablePluginKey = new PluginKey("variables");
10
-
11
- const Variable = Node.create({
12
- name: "variable",
13
-
14
- addOptions() {
15
- return {
16
- HTMLAttributes: { class: "variable__text" },
17
- charOpen: "{{",
18
- charClose: "}}",
19
- renderLabel({ options, node }) {
20
- return `${options.charOpen}${node.attrs.label || node.attrs.id}${
21
- options.charClose
22
- }`;
23
- },
24
- suggestion: {},
25
- };
26
- },
27
-
28
- group: "inline",
29
-
30
- inline: true,
31
-
32
- selectable: false,
33
-
34
- atom: true,
35
-
36
- addAttributes() {
37
- return {
38
- id: {
39
- default: null,
40
- parseHTML: (element) => element.getAttribute("data-id"),
41
- renderHTML: (attributes) => {
42
- if (!attributes.id) {
43
- return {};
44
- }
45
-
46
- return {
47
- "data-id": attributes.id,
48
- };
49
- },
50
- },
51
-
52
- label: {
53
- default: null,
54
- parseHTML: (element) => element.getAttribute("data-label"),
55
- renderHTML: (attributes) => {
56
- if (!attributes.label) {
57
- return {};
58
- }
59
-
60
- return {
61
- "data-label": attributes.label,
62
- };
63
- },
64
- },
65
- };
66
- },
67
-
68
- parseHTML() {
69
- return [
70
- {
71
- tag: "span[data-variable]",
72
- },
73
- ];
74
- },
75
-
76
- renderHTML({ node, HTMLAttributes }) {
77
- return [
78
- "span",
79
- mergeAttributes(
80
- { "data-variable": "" },
81
- this.options.HTMLAttributes,
82
- HTMLAttributes
83
- ),
84
- this.options.renderLabel({
85
- options: this.options,
86
- node,
87
- }),
88
- ];
89
- },
90
-
91
- renderText({ node }) {
92
- return this.options.renderLabel({
93
- options: this.options,
94
- node,
95
- });
96
- },
97
-
98
- addCommands() {
99
- return {
100
- setVariable:
101
- (attributes) =>
102
- ({ chain }) => {
103
- chain()
104
- .focus()
105
- .insertContent([
106
- {
107
- type: this.name,
108
- attrs: attributes,
109
- },
110
- {
111
- type: "text",
112
- text: " ",
113
- },
114
- ])
115
- .run();
116
- },
117
- };
118
- },
119
-
120
- addKeyboardShortcuts() {
121
- return {
122
- Backspace: () =>
123
- this.editor.commands.command(({ tr, state }) => {
124
- let isVariable = false;
125
- const { selection } = state;
126
- const { empty, anchor } = selection;
127
-
128
- if (!empty) {
129
- return false;
130
- }
131
-
132
- state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
133
- if (node.type.name === this.name) {
134
- isVariable = true;
135
- tr.insertText("", pos, pos + node.nodeSize);
136
-
137
- return false;
138
- }
139
- });
140
-
141
- return isVariable;
142
- }),
143
- };
144
- },
145
-
146
- addProseMirrorPlugins() {
147
- return [
148
- Suggestion({
149
- editor: this.editor,
150
- ...this.options.suggestion,
151
- }),
152
- ];
153
- },
154
- });
155
-
156
- const suggestionConfig = {
157
- char: "{{",
158
- startOfLine: false,
159
- pluginKey: VariablePluginKey,
160
- command: ({ editor, range, props }) => {
161
- editor.chain().focus().deleteRange(range).setVariable(props).run();
162
- },
163
-
164
- items: () => [],
165
- render: () => {
166
- let reactRenderer;
167
- let popup;
168
-
169
- return {
170
- onStart: (props) => {
171
- reactRenderer = new ReactRenderer(VariableSuggestion, {
172
- props,
173
- editor: props.editor,
174
- });
175
-
176
- popup = tippy("body", {
177
- getReferenceClientRect: props.clientRect,
178
- appendTo: () => document.body,
179
- content: reactRenderer.element,
180
- showOnCreate: true,
181
- interactive: true,
182
- trigger: "manual",
183
- placement: "bottom-start",
184
- });
185
- },
186
- onUpdate(props) {
187
- reactRenderer.updateProps(props);
188
-
189
- popup[0].setProps({
190
- getReferenceClientRect: props.clientRect,
191
- });
192
- },
193
-
194
- onExit() {
195
- popup[0].destroy();
196
- reactRenderer.destroy();
197
- },
198
- };
199
- },
200
- };
201
-
202
- export default {
203
- configure: ({ suggestion = {} }) => {
204
- return Variable.configure({
205
- suggestion: { ...suggestionConfig, ...suggestion },
206
- });
207
- },
208
- };
@@ -1,45 +0,0 @@
1
- import React from "react";
2
-
3
- import { parseVariables } from "./helpers";
4
-
5
- const VariableList = ({ onClickVariable, variables }) => {
6
- const parsedVariables = parseVariables(variables);
7
-
8
- if (!(variables && variables.length)) {
9
- return null;
10
- }
11
-
12
- return (
13
- <div className="p-3 bg-white">
14
- {parsedVariables.map(({ label, variables }) => (
15
- <VariableCategory
16
- key={label}
17
- title={label}
18
- variables={variables}
19
- onClickItem={onClickVariable}
20
- />
21
- ))}
22
- </div>
23
- );
24
- };
25
-
26
- export default VariableList;
27
-
28
- const VariableCategory = ({ title, variables, onClickItem }) => {
29
- return (
30
- <div className="my-3">
31
- {title ? <p className="text-xs font-semibold">{title}</p> : null}
32
- <div className="mt-1 space-x-2">
33
- {variables.map((item) => (
34
- <button
35
- onClick={() => onClickItem(item)}
36
- key={`${item.label}--${item.value}`}
37
- className="px-2 py-1 text-xs text-white bg-blue-400 rounded-sm"
38
- >
39
- {item.label}
40
- </button>
41
- ))}
42
- </div>
43
- </div>
44
- );
45
- };