tiptop-editor 1.0.18 → 1.2.0

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 (28) hide show
  1. package/README.md +223 -43
  2. package/dist/components/editor/TableSelectionMenu.d.ts +4 -0
  3. package/dist/components/editor/TiptopEditor.d.ts +3 -0
  4. package/dist/components/editor/TiptopEditor.stories.d.ts +16 -0
  5. package/dist/components/{ColorButton.d.ts → menus/ColorButton.d.ts} +1 -1
  6. package/dist/components/menus/TableButtonMenu.d.ts +7 -0
  7. package/dist/components/{TextSelectionMenu.d.ts → menus/TextSelectionMenu.d.ts} +1 -1
  8. package/dist/components/{EditorButton.d.ts → ui/EditorButton.d.ts} +1 -1
  9. package/dist/extensions/slashCommand/SlashCommand.d.ts +6 -1
  10. package/dist/extensions/slashCommand/SlashCommandSuggestion.d.ts +3 -29
  11. package/dist/helpers.d.ts +3 -3
  12. package/dist/index.d.ts +1 -1
  13. package/dist/tiptop-editor.css +1 -1
  14. package/dist/tiptop-editor.es.js +5284 -4288
  15. package/dist/tiptop-editor.umd.js +30 -11
  16. package/dist/types.d.ts +38 -15
  17. package/package.json +12 -2
  18. package/dist/components/TiptopEditor.d.ts +0 -7
  19. /package/dist/components/{DragHandleColorList.d.ts → editor/DragHandleColorList.d.ts} +0 -0
  20. /package/dist/components/{TiptopDragHandle.d.ts → editor/TiptopDragHandle.d.ts} +0 -0
  21. /package/dist/components/{TransformIntoIcon.d.ts → editor/TransformIntoIcon.d.ts} +0 -0
  22. /package/dist/components/{ColorButtonMenu.d.ts → menus/ColorButtonMenu.d.ts} +0 -0
  23. /package/dist/components/{LinkButtonMenu.d.ts → menus/LinkButtonMenu.d.ts} +0 -0
  24. /package/dist/components/{MoreOptionsButtonMenu.d.ts → menus/MoreOptionsButtonMenu.d.ts} +0 -0
  25. /package/dist/components/{TransformIntoButtonMenu.d.ts → menus/TransformIntoButtonMenu.d.ts} +0 -0
  26. /package/dist/components/{CloseIcon.d.ts → ui/CloseIcon.d.ts} +0 -0
  27. /package/dist/components/{ColorIcon.d.ts → ui/ColorIcon.d.ts} +0 -0
  28. /package/dist/components/{Icon.d.ts → ui/Icon.d.ts} +0 -0
package/README.md CHANGED
@@ -1,69 +1,249 @@
1
- # 📝 Tiptop Editor
1
+ # Tiptop Editor
2
2
 
3
- A Notion-like rich text editor built with [Tiptap v3](https://tiptap.dev/), [HeroUI](https://heroui.dev/), [Tailwind v4](https://https://tailwindcss.com) packaged as a plug-and-play React component.
4
- Inspired from [TipTap Notion-like](https://tiptap.dev/docs/ui-components/templates/notion-like-editor).
3
+ A Notion-like rich text editor built with [Tiptap v3](https://tiptap.dev/), [HeroUI](https://www.heroui.com/), and Tailwind CSS, packaged as a plug-and-play React component.
4
+
5
+ Inspired by Tiptap's Notion-like editor template:
6
+ https://tiptap.dev/docs/ui-components/templates/notion-like-editor
5
7
 
6
8
  ![npm version](https://img.shields.io/npm/v/tiptop-editor.svg)
7
9
  ![bundle size](https://img.shields.io/bundlephobia/minzip/tiptop-editor)
8
10
  ![license](https://img.shields.io/npm/l/tiptop-editor)
9
11
 
10
- ---
12
+ ## Features
11
13
 
12
- ## Features
14
+ - Tiptap v3 editor with a ready-to-use Notion-like UI
15
+ - Slash commands for inserting blocks
16
+ - Table support with row, column, header, split, and merge actions
17
+ - Emoji suggestions triggered with `:`
18
+ - Built-in image uploader block
19
+ - Text formatting, lists, code blocks, highlights, alignment, subscript, and superscript
20
+ - TypeScript support
13
21
 
14
- - Built on **Tiptap v3** — a powerful, headless rich-text editor
15
- - Styled with **HeroUI** + **Tailwind**
16
- - Fully typed with **TypeScript**
17
- - Ready to embed in any React app
18
- - Designed for **Notion-like UX**
22
+ ## Installation
19
23
 
24
+ ```bash
25
+ npm install tiptop-editor
26
+ ```
20
27
 
21
- https://github.com/user-attachments/assets/cb7d907d-bae0-4b3b-b6e7-8493180afd75
28
+ ## Basic Usage
22
29
 
30
+ ```tsx
31
+ import { TiptopEditor } from 'tiptop-editor'
32
+ import 'tiptop-editor/dist/tiptop-editor.css'
33
+
34
+ export function Editor() {
35
+ return (
36
+ <TiptopEditor
37
+ editorOptions={{
38
+ content: '<p>I am the Tiptop Editor</p>',
39
+ immediatelyRender: false,
40
+ }}
41
+ />
42
+ )
43
+ }
44
+ ```
23
45
 
24
- ---
46
+ `editorOptions` accepts the same options as `useEditor` from `@tiptap/react`, except `extensions`, which is managed internally by the package.
25
47
 
26
- ## ⚙️ Installation
27
- ```bash
28
- npm install tiptop-editor
48
+ ## Editor Ref and Events
49
+
50
+ You can access the editor instance and bind Tiptap runtime event listeners through the component ref.
51
+
52
+ ```tsx
53
+ import { useEffect, useRef } from 'react'
54
+ import { TiptopEditor, type TiptopEditorHandle } from 'tiptop-editor'
55
+
56
+ export function EditorWithEvents() {
57
+ const editorRef = useRef<TiptopEditorHandle>(null)
58
+
59
+ useEffect(() => {
60
+ const handleUpdate = ({ editor }: { editor: NonNullable<ReturnType<TiptopEditorHandle['getEditor']>> }) => {
61
+ console.log(editor.getHTML())
62
+ }
63
+
64
+ editorRef.current?.on('update', handleUpdate)
65
+
66
+ return () => {
67
+ editorRef.current?.off('update', handleUpdate)
68
+ }
69
+ }, [])
70
+
71
+ return <TiptopEditor ref={editorRef} />
72
+ }
29
73
  ```
30
74
 
31
- ## 🚀 Usage
75
+ Available ref methods:
32
76
 
33
- **Import the component in your app**
34
- ```tsx
35
- import { TiptopEditor } from "tiptop-editor";
77
+ - `getEditor()`
78
+ - `on(event, callback)`
79
+ - `off(event, callback?)`
80
+ - `once(event, callback)`
81
+
82
+ ## Custom Editor UI Options
83
+
84
+ `TiptopEditor` also supports a few package-specific options inside `editorOptions`:
36
85
 
37
- <TiptopEditor />
38
- ```
39
- **Add the CSS code to your app**
40
- For the package to behave like it should, you have to import the compiled CSS file. Add this line in your main css file, or import it directly in the component file that's going to host the **TiptopEditor**.
41
- - In your main css file
42
- ```css
43
- @import '../node_modules/tiptop-editor/dist/tiptop-editor.css';
44
- - In any component file
45
- ```tsx
46
- import 'tiptop-editor/dist/tiptop-editor.css'
47
- ## 🎨 Example
48
- The Tiptop component takes as props all the props from the `UseEditorOptions` from [*@tiptap/react*](https://www.npmjs.com/package/@tiptap/react), except the `extensions` prop.
49
- *Why only that prop, you ask ? Well, since this package is intended to *replicate* the Notion-like style with all their blocks/extensions and plug-and-play, as of now, I have not allowed users to pass their own extensions. But that can change in the future, just not now.*
50
- Anyway, to use the package, just pass your props to `editorOptions` and you're good to go. Customize the Tiptop component will the props you want, as if you were using *EditorContent and passing props to the editor*.
51
86
  ```tsx
52
- <TiptopEditor editorOptions={{
53
- immediatelyRender: false // If using SSR (ex. a NextJS project) otherwise you can omit it
54
- content: '<p>I am the Tiptop Editor</p>'
55
- ... // Other props
87
+ <TiptopEditor
88
+ editorOptions={{
89
+ content: '<p>Custom layout</p>',
90
+ disableDefaultContainer: true,
91
+ showDragHandle: false,
56
92
  }}
57
93
  />
58
94
  ```
59
95
 
96
+ - `disableDefaultContainer`
97
+ Disables the default HeroUI `Card` wrapper and removes the editor's built-in padding. Use this when you want the editor to live inside your own container/layout.
98
+ - `showDragHandle`
99
+ Controls whether the block drag handle is rendered. Default: `true`.
100
+
101
+ ## Built-in Extensions
102
+
103
+ The package ships with these extensions enabled out of the box:
104
+
105
+ - `StarterKit`
106
+ - `ListKit`
107
+ - `Placeholder`
108
+ - custom slash command menu
109
+ - custom code block
110
+ - custom horizontal rule
111
+ - `TextStyle` and `Color`
112
+ - `Highlight`
113
+ - `TextAlign`
114
+ - `Subscript`
115
+ - `Superscript`
116
+ - emoji suggestions
117
+ - `TableKit`
118
+ - image uploader block and upload handler
119
+
120
+ ### Tables
121
+
122
+ Type `/table` to insert a table.
123
+
124
+ Inside a table you can:
125
+
126
+ - add or remove rows
127
+ - add or remove columns
128
+ - toggle header row or header column
129
+ - split a merged cell
130
+ - merge adjacent selected cells
131
+
132
+ To merge cells, drag across adjacent cells first, then use the table controls.
133
+
134
+ ### Emoji
135
+
136
+ Type `:` followed by an emoji name to open emoji suggestions.
137
+
138
+ ## Image Extension
139
+
140
+ The image feature is built around an `imageUploader` block.
141
+
142
+ ### How to insert an image block
143
+
144
+ - Type `/image`
145
+ - or use the slash menu and select `Image`
146
+
147
+ Once inserted, the block lets the user click to upload or drag and drop an image.
148
+
149
+ ### Supported files
150
+
151
+ - `image/png`
152
+ - `image/jpeg`
153
+ - `image/jpg`
154
+ - max size: `5MB`
155
+
156
+ ### Demo mode with no backend
157
+
158
+ If you do not provide upload options, the editor simulates an upload and displays the image using a local object URL. This is useful for local demos and prototypes.
159
+
160
+ ```tsx
161
+ <TiptopEditor
162
+ editorOptions={{
163
+ content: '<p>Upload demo</p>',
164
+ }}
165
+ />
166
+ ```
167
+
168
+ ### Real upload mode
169
+
170
+ To upload files to your backend, set both `imgUploadUrl` and `imgUploadResponseKey`.
171
+
172
+ ```tsx
173
+ <TiptopEditor
174
+ editorOptions={{
175
+ content: '<p>Upload to my API</p>',
176
+ imgUploadUrl: '/api/upload',
177
+ imgUploadResponseKey: 'url',
178
+ }}
179
+ />
180
+ ```
181
+
182
+ The editor sends a `POST` request with `multipart/form-data` and the file under the `file` field.
183
+
184
+ `imgUploadResponseKey` is flexible. It supports:
185
+
186
+ - a top-level key like `'url'`
187
+ Example:
188
+ ```tsx
189
+ <TiptopEditor
190
+ editorOptions={{
191
+ imgUploadUrl: '/api/upload',
192
+ imgUploadResponseKey: 'url',
193
+ }}
194
+ />
195
+ ```
196
+ - a nested path like `'data.url'`
197
+ Example:
198
+ ```tsx
199
+ <TiptopEditor
200
+ editorOptions={{
201
+ imgUploadUrl: '/api/upload',
202
+ imgUploadResponseKey: 'data.url',
203
+ }}
204
+ />
205
+ ```
206
+ - a path array like `['data', 'url']`
207
+ Example:
208
+ ```tsx
209
+ <TiptopEditor
210
+ editorOptions={{
211
+ imgUploadUrl: '/api/upload',
212
+ imgUploadResponseKey: ['data', 'url'],
213
+ }}
214
+ />
215
+ ```
216
+ - a resolver function
217
+ Example:
218
+ ```tsx
219
+ <TiptopEditor
220
+ editorOptions={{
221
+ imgUploadUrl: '/api/upload',
222
+ imgUploadResponseKey: (response) => {
223
+ const asset = response.asset as { cdnUrl?: string } | undefined
224
+ return asset?.cdnUrl
225
+ },
226
+ }}
227
+ />
228
+ ```
229
+
230
+ Your server response must include the uploaded image URL at the location you describe with `imgUploadResponseKey`.
231
+
232
+ Example:
233
+
234
+ ```json
235
+ {
236
+ "data": {
237
+ "url": "https://cdn.example.com/uploads/image-123.jpg"
238
+ }
239
+ }
240
+ ```
60
241
 
61
- ##### Of course, I will continue to improve this project over time, as I have many more ideas (more extensions, more customizations, etc..)
62
- ##### Emoji Extension, Image extension, and more coming in next updates 🏃‍♂ ...
242
+ ## Notes
63
243
 
64
- I will also document the Changelogs and releases, as well as continue to update this Readme with relevant information.
244
+ - If you use SSR, keep `immediatelyRender: false`.
245
+ - The package manages the editor extensions internally, so custom `extensions` are intentionally not accepted yet.
65
246
 
66
- *If you have any suggestions/recommendations to improve this project, any feedback is much appreciated (PRs welcome) !*
67
- *I also encourage you to open up *Issues* if you find releveant bugs inside the package.*
247
+ ## Feedback
68
248
 
69
- **Thank you, and Happy Coding !**
249
+ Issues and pull requests are welcome.
@@ -0,0 +1,4 @@
1
+ import { default as React } from 'react';
2
+ import { TextSelectionMenuProps } from '../../types';
3
+ declare const _default: React.MemoExoticComponent<({ editor }: TextSelectionMenuProps) => import("react/jsx-runtime").JSX.Element>;
4
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import { TiptopEditorHandle, TiptopEditorProps } from '../../types';
2
+ declare const TiptopEditor: import('react').ForwardRefExoticComponent<Omit<TiptopEditorProps, "ref"> & import('react').RefAttributes<TiptopEditorHandle>>;
3
+ export default TiptopEditor;
@@ -0,0 +1,16 @@
1
+ import { StoryObj } from '@storybook/react-vite';
2
+ declare const meta: {
3
+ title: string;
4
+ component: import('react').ForwardRefExoticComponent<Omit<import('../..').TiptopEditorProps, "ref"> & import('react').RefAttributes<import('../..').TiptopEditorHandle>>;
5
+ tags: string[];
6
+ args: {
7
+ editorOptions: {
8
+ content: string;
9
+ immediatelyRender: false;
10
+ };
11
+ };
12
+ };
13
+ export default meta;
14
+ type Story = StoryObj<typeof meta>;
15
+ export declare const Default: Story;
16
+ export declare const Frameless: Story;
@@ -1,4 +1,4 @@
1
1
  import { default as React } from 'react';
2
- import { ColorButtonProps } from '../types';
2
+ import { ColorButtonProps } from '../../types';
3
3
  declare const _default: React.MemoExoticComponent<({ editor, buttonType, hsl, color, bgColor, tooltipText, tooltipDisabled, }: ColorButtonProps) => import("react/jsx-runtime").JSX.Element>;
4
4
  export default _default;
@@ -0,0 +1,7 @@
1
+ import { default as React } from 'react';
2
+ import { Editor } from '@tiptap/react';
3
+ interface TableButtonMenuProps {
4
+ editor: Editor;
5
+ }
6
+ declare const _default: React.MemoExoticComponent<({ editor }: TableButtonMenuProps) => import("react/jsx-runtime").JSX.Element | null>;
7
+ export default _default;
@@ -1,4 +1,4 @@
1
1
  import { default as React } from 'react';
2
- import { TextSelectionMenuProps } from '../types';
2
+ import { TextSelectionMenuProps } from '../../types';
3
3
  declare const _default: React.MemoExoticComponent<({ editor, prepend, append }: TextSelectionMenuProps) => import("react/jsx-runtime").JSX.Element>;
4
4
  export default _default;
@@ -1,4 +1,4 @@
1
- import { EditorButtonProps } from '../types';
1
+ import { EditorButtonProps } from '../../types';
2
2
  import { default as React } from 'react';
3
3
  declare const _default: React.MemoExoticComponent<({ editor, buttonKey, tooltipText, isIconOnly, color, variant, isDisabled, icon, iconClass, text, withActive, onPressed, }: EditorButtonProps) => import("react/jsx-runtime").JSX.Element>;
4
4
  export default _default;
@@ -1,3 +1,8 @@
1
1
  import { Extension } from '@tiptap/core';
2
- declare const _default: Extension<any, any>;
2
+ import { SuggestionOptions } from '@tiptap/suggestion';
3
+ import { SlashCommandGroupCommandsProps, SlashCommandGroupProps } from '../../types';
4
+ export type SlashCommandSuggestionOptions = Omit<SuggestionOptions<SlashCommandGroupProps, SlashCommandGroupCommandsProps>, 'editor'>;
5
+ declare const _default: Extension<{
6
+ suggestion: SlashCommandSuggestionOptions;
7
+ }, any>;
3
8
  export default _default;
@@ -1,29 +1,3 @@
1
- import { Editor } from '@tiptap/react';
2
- import { PluginKey } from '@tiptap/pm/state';
3
- import { SuggestionKeyDownProps, SuggestionProps } from '@tiptap/suggestion';
4
- declare const _default: {
5
- pluginKey: PluginKey<any>;
6
- items: ({ query }: {
7
- query: string;
8
- }) => {
9
- commands: {
10
- key: string;
11
- title: string;
12
- icon: string;
13
- description: string;
14
- command: ({ editor, range }: {
15
- editor: Editor;
16
- range: import('@tiptap/core').Range;
17
- }) => void;
18
- }[];
19
- key: string;
20
- title: string;
21
- }[];
22
- render: () => {
23
- onStart: (props: SuggestionProps) => void;
24
- onUpdate(props: SuggestionProps): void;
25
- onKeyDown(props: SuggestionKeyDownProps): boolean | undefined;
26
- onExit(): void;
27
- };
28
- };
29
- export default _default;
1
+ import { SlashCommandSuggestionOptions } from './SlashCommand';
2
+ declare const SlashCommandSuggestion: SlashCommandSuggestionOptions;
3
+ export default SlashCommandSuggestion;
package/dist/helpers.d.ts CHANGED
@@ -4,6 +4,7 @@ import { SlashCommandGroupCommandsProps } from './types';
4
4
  import { Node } from '@tiptap/pm/model';
5
5
  export declare const isTextSelected: (editor: Editor) => boolean;
6
6
  export declare const hasTextNodeInSelection: (editor: Editor) => boolean;
7
+ export declare const isTableCellSelection: (editor: Editor) => boolean;
7
8
  export declare const isForbiddenNodeSelected: (editor: Editor) => boolean;
8
9
  export declare const canShowColorTransform: (editor: Editor) => string | false | undefined;
9
10
  export declare const canShowNodeTransform: (editor: Editor) => boolean | undefined;
@@ -23,9 +24,8 @@ export declare const uploadWithProgress: ({ file, url, onProgress, signal }: {
23
24
  url: string;
24
25
  onProgress: (percent: number) => boolean | void;
25
26
  signal?: AbortSignal;
26
- }) => Promise<{
27
- url: string;
28
- }>;
27
+ }) => Promise<Record<string, unknown>>;
28
+ export declare const getValueAtPath: (source: Record<string, unknown>, path: string | string[]) => unknown;
29
29
  export declare const generateUniqueId: () => string;
30
30
  export declare const updateNodeByPos: (editor: Editor, find: {
31
31
  id?: string;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { default as TiptopEditor } from './components/TiptopEditor';
1
+ export { default as TiptopEditor } from './components/editor/TiptopEditor';
2
2
  export * from './types';