tiptap-editor-custom-stg 1.0.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.
- package/README.md +304 -0
- package/dist/index.css +1 -0
- package/dist/index.d.mts +79 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +1336 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1292 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.d.ts +3 -0
- package/package.json +91 -0
package/README.md
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# tiptap-editor-custom-stg
|
|
2
|
+
|
|
3
|
+
Custom Tiptap rich text editor with toolbar, table support, and file upload integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📝 Rich text editing powered by Tiptap
|
|
8
|
+
- 🎨 Toolbar with font family, size, text/background colors
|
|
9
|
+
- 📊 Table support (insert, resize, merge/split cells)
|
|
10
|
+
- 🖼️ Image upload (click or paste screenshot)
|
|
11
|
+
- 📎 File attachment support
|
|
12
|
+
- 🔗 Link insert/edit
|
|
13
|
+
- 🎯 Read-only mode
|
|
14
|
+
- ⚡ Full TypeScript support
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## For Users
|
|
19
|
+
|
|
20
|
+
### Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install tiptap-editor-custom-stg
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then install the required peer dependencies:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install react react-dom \
|
|
30
|
+
@tiptap/react @tiptap/core @tiptap/pm \
|
|
31
|
+
@tiptap/starter-kit \
|
|
32
|
+
@tiptap/extension-link \
|
|
33
|
+
@tiptap/extension-underline \
|
|
34
|
+
@tiptap/extension-image \
|
|
35
|
+
@tiptap/extension-text-style \
|
|
36
|
+
@tiptap/extension-color \
|
|
37
|
+
@tiptap/extension-font-family \
|
|
38
|
+
@tiptap/extension-superscript \
|
|
39
|
+
@tiptap/extension-subscript \
|
|
40
|
+
@tiptap/extension-text-align \
|
|
41
|
+
@tiptap/extension-placeholder \
|
|
42
|
+
@tiptap/extension-table \
|
|
43
|
+
@tiptap/extension-table-row \
|
|
44
|
+
@tiptap/extension-table-cell \
|
|
45
|
+
@tiptap/extension-table-header
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Basic Usage
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { TiptapEditor } from 'tiptap-editor-custom-stg';
|
|
52
|
+
import 'tiptap-editor-custom-stg/styles'; // required
|
|
53
|
+
|
|
54
|
+
function MyComponent() {
|
|
55
|
+
const [content, setContent] = React.useState('<p>Hello World!</p>');
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<TiptapEditor
|
|
59
|
+
elementId="my-editor"
|
|
60
|
+
content={content}
|
|
61
|
+
editMode={true}
|
|
62
|
+
docNum="DOC-001"
|
|
63
|
+
placeholder="Start typing..."
|
|
64
|
+
toolbarConfig={{
|
|
65
|
+
groups: ['history', 'font', 'fontSize', 'textColor', 'list', 'link', 'insert'],
|
|
66
|
+
showDividers: true,
|
|
67
|
+
}}
|
|
68
|
+
receiveData={(id, html) => setContent(html)}
|
|
69
|
+
onUploadFile={async (file, docNum) => {
|
|
70
|
+
const url = await uploadToYourStorage(file, docNum);
|
|
71
|
+
return url;
|
|
72
|
+
}}
|
|
73
|
+
onAlert={(msg) => alert(msg)}
|
|
74
|
+
onLoadingChange={(isLoading) => setLoading(isLoading)}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Props
|
|
81
|
+
|
|
82
|
+
| Prop | Type | Required | Default | Description |
|
|
83
|
+
|------|------|----------|---------|-------------|
|
|
84
|
+
| `elementId` | `string` | ✅ | — | Unique ID for the editor instance |
|
|
85
|
+
| `content` | `string` | ✅ | — | HTML content |
|
|
86
|
+
| `editMode` | `boolean` | ✅ | — | `true` = editable, `false` = read-only |
|
|
87
|
+
| `docNum` | `string` | ✅ | — | Document number passed to `onUploadFile` |
|
|
88
|
+
| `receiveData` | `(id, html) => void` | — | — | Called on every content change |
|
|
89
|
+
| `receiveStatus` | `(id, loading) => void` | — | — | Called when per-editor upload status changes |
|
|
90
|
+
| `onUploadFile` | `(file, docNum) => Promise<string>` | — | — | Upload handler, must return the file URL |
|
|
91
|
+
| `onAlert` | `(message) => void` | — | `window.alert` | Custom alert/notification function |
|
|
92
|
+
| `onLoadingChange` | `(isLoading) => void` | — | — | Global loading state callback |
|
|
93
|
+
| `onRegisterReset` | `(callback) => () => void` | — | — | Register a reset callback, returns cleanup fn |
|
|
94
|
+
| `maxFileSizeMB` | `number` | — | `50` | Max upload file size in MB |
|
|
95
|
+
| `placeholder` | `string` | — | `'Start typing...'` | Editor placeholder text. This value is passed into Tiptap's `Placeholder` extension and is shown when the editor is empty |
|
|
96
|
+
| `toolbarConfig` | `{ groups?: ToolbarGroup[]; showDividers?: boolean }` | — | default toolbar config | Customize which toolbar groups are shown and whether dividers are rendered |
|
|
97
|
+
|
|
98
|
+
### Styling
|
|
99
|
+
|
|
100
|
+
Styles are shipped as a separate CSS file. Import it once in your app:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import 'tiptap-editor-custom-stg/styles';
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
To override default styles, target these CSS classes:
|
|
107
|
+
|
|
108
|
+
```css
|
|
109
|
+
.tiptap-editor-wrapper { /* outer container */ }
|
|
110
|
+
.tiptap-toolbar { /* toolbar */ }
|
|
111
|
+
.ProseMirror { /* editor content area */ }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Toolbar Configuration
|
|
115
|
+
|
|
116
|
+
Use `toolbarConfig` to control which toolbar groups are displayed and whether dividers appear between them.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
<TiptapEditor
|
|
120
|
+
elementId="my-editor"
|
|
121
|
+
content={content}
|
|
122
|
+
editMode={true}
|
|
123
|
+
docNum="DOC-001"
|
|
124
|
+
toolbarConfig={{
|
|
125
|
+
groups: ['history', 'font', 'fontSize', 'textColor', 'list', 'link', 'insert'],
|
|
126
|
+
showDividers: true,
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Available toolbar groups:
|
|
132
|
+
|
|
133
|
+
- `history`
|
|
134
|
+
- `font`
|
|
135
|
+
- `fontSize`
|
|
136
|
+
- `textColor`
|
|
137
|
+
- `highlight`
|
|
138
|
+
- `list`
|
|
139
|
+
- `alignment`
|
|
140
|
+
- `table`
|
|
141
|
+
- `inline`
|
|
142
|
+
- `link`
|
|
143
|
+
- `insert`
|
|
144
|
+
|
|
145
|
+
Default toolbar groups:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
[
|
|
149
|
+
'history',
|
|
150
|
+
'font',
|
|
151
|
+
'fontSize',
|
|
152
|
+
'textColor',
|
|
153
|
+
'highlight',
|
|
154
|
+
'list',
|
|
155
|
+
'alignment',
|
|
156
|
+
'table',
|
|
157
|
+
'inline',
|
|
158
|
+
'link',
|
|
159
|
+
'insert',
|
|
160
|
+
]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Example: minimal toolbar without dividers
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<TiptapEditor
|
|
167
|
+
elementId="my-editor"
|
|
168
|
+
content={content}
|
|
169
|
+
editMode={true}
|
|
170
|
+
docNum="DOC-001"
|
|
171
|
+
toolbarConfig={{
|
|
172
|
+
groups: ['history', 'inline', 'link'],
|
|
173
|
+
showDividers: false,
|
|
174
|
+
}}
|
|
175
|
+
/>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Placeholder
|
|
179
|
+
|
|
180
|
+
You can customize the empty-state placeholder text with the `placeholder` prop:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<TiptapEditor
|
|
184
|
+
elementId="my-editor"
|
|
185
|
+
content=""
|
|
186
|
+
editMode={true}
|
|
187
|
+
docNum="DOC-001"
|
|
188
|
+
placeholder="Start typing..."
|
|
189
|
+
/>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The placeholder is driven by Tiptap's `Placeholder` extension configuration, so the `placeholder` prop is the source of truth for the text shown in the editor.
|
|
193
|
+
|
|
194
|
+
### Content Reset
|
|
195
|
+
|
|
196
|
+
Use `onRegisterReset` to let a parent component reset the editor content programmatically:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
const resetRef = React.useRef<((content: string) => void) | null>(null);
|
|
200
|
+
|
|
201
|
+
<TiptapEditor
|
|
202
|
+
onRegisterReset={(callback) => {
|
|
203
|
+
resetRef.current = callback;
|
|
204
|
+
return () => { resetRef.current = null; };
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
|
|
208
|
+
// Reset from anywhere:
|
|
209
|
+
resetRef.current?.('<p>New content</p>');
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## For Developers
|
|
215
|
+
|
|
216
|
+
### Project Structure
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
tiptap-editor-custom-stg/
|
|
220
|
+
├── src/
|
|
221
|
+
│ ├── index.ts # public exports
|
|
222
|
+
│ ├── TiptapEditor.tsx # main component
|
|
223
|
+
│ ├── ToolbarGroups.tsx # toolbar sub-components
|
|
224
|
+
│ ├── Icons.tsx # SVG icons
|
|
225
|
+
│ ├── extensions.ts # Tiptap extension factory (exports createTiptapExtensions)
|
|
226
|
+
│ ├── fontSize.ts # custom FontSize extension
|
|
227
|
+
│ ├── backgroundColor.ts # custom BackgroundColor extension
|
|
228
|
+
│ ├── constants.ts # colors, fonts, sizes
|
|
229
|
+
│ ├── types.ts # TypeScript types
|
|
230
|
+
│ ├── utils.ts # upload helper
|
|
231
|
+
│ ├── tiptap.scss # editor styles
|
|
232
|
+
│ └── styles.d.ts # CSS type declaration
|
|
233
|
+
├── dist/ # build output (generated)
|
|
234
|
+
├── package.json
|
|
235
|
+
├── tsconfig.json
|
|
236
|
+
└── tsup.config.ts
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Build
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
npm install
|
|
243
|
+
npm run build
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
This runs two steps:
|
|
247
|
+
1. `build:js` — compiles TypeScript via tsup → `dist/index.js`, `dist/index.mjs`, `dist/index.d.ts`
|
|
248
|
+
2. `build:css` — compiles SCSS via sass → `dist/index.css`, copies `dist/styles.d.ts`
|
|
249
|
+
|
|
250
|
+
### Local Testing (without publishing)
|
|
251
|
+
|
|
252
|
+
Use `npm pack` to create a local tarball and install it directly into your project.
|
|
253
|
+
|
|
254
|
+
`npm pack` creates a local `.tgz` package archive using npm's publish rules. In practice, it lets you verify and install the same package contents that would be published to npm, without actually publishing.
|
|
255
|
+
|
|
256
|
+
For this project, because `package.json` includes `"files": ["dist"]`, the packed tarball mainly contains:
|
|
257
|
+
|
|
258
|
+
- `dist/**`
|
|
259
|
+
- `package.json`
|
|
260
|
+
- `README.md`
|
|
261
|
+
- `LICENSE` (if present)
|
|
262
|
+
|
|
263
|
+
So `npm pack` is not packing the entire repository by default. It is packing the publishable package contents.
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# 1. Build and pack
|
|
267
|
+
cd tiptap-editor-custom-stg
|
|
268
|
+
npm run build
|
|
269
|
+
npm pack
|
|
270
|
+
# Creates: tiptap-editor-custom-stg-1.0.0.tgz
|
|
271
|
+
|
|
272
|
+
# 2. Install into your project
|
|
273
|
+
cd ../your-project
|
|
274
|
+
npm install ../tiptap-editor-custom-stg/tiptap-editor-custom-stg-1.0.0.tgz
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
After making code changes, rebuild and reinstall:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
cd tiptap-editor-custom-stg
|
|
281
|
+
npm run build && npm pack
|
|
282
|
+
|
|
283
|
+
cd ../your-project
|
|
284
|
+
npm install ../tiptap-editor-custom-stg/tiptap-editor-custom-stg-1.0.0.tgz --force
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Publishing to npm
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npm login
|
|
291
|
+
npm publish
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
After publishing, install normally:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
npm install tiptap-editor-custom-stg
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT
|
package/dist/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.tiptap-editor-wrapper{border:1px solid #e5e7eb;border-radius:.75rem;overflow:hidden;font-size:15px;font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;background:#fff;box-shadow:0 1px 3px 0 rgba(0,0,0,.05),0 1px 2px -1px rgba(0,0,0,.05)}.tiptap-editor-wrapper .tiptap-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;padding:.35rem .75rem;background:#fff;border-bottom:1px solid #f3f4f6}.tiptap-editor-wrapper .tiptap-toolbar button{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:none;border-radius:.375rem;background:rgba(0,0,0,0);cursor:pointer;color:#4b5563;font-size:14px;font-weight:500;transition:all .15s ease;line-height:1}.tiptap-editor-wrapper .tiptap-toolbar button:hover:not(:disabled){background:#f3f4f6;color:#111827}.tiptap-editor-wrapper .tiptap-toolbar button.is-active{background:#e5e7eb;color:#111827}.tiptap-editor-wrapper .tiptap-toolbar button:disabled{opacity:.4;cursor:not-allowed}.tiptap-editor-wrapper .tiptap-toolbar button.toolbar-button-dropdown{width:44px;padding:0 4px 0 6px;gap:2px}.tiptap-editor-wrapper .tiptap-toolbar button.toolbar-button-dropdown .dropdown-arrow{width:10px;height:10px;opacity:.6;margin-left:-1px}.tiptap-editor-wrapper .tiptap-toolbar button svg{width:18px;height:18px}.tiptap-editor-wrapper .tiptap-toolbar .toolbar-divider{width:1px;height:20px;background:#e5e7eb;margin:0 .5rem;flex-shrink:0}.tiptap-editor-wrapper .tiptap-toolbar .toolbar-select-wrapper{position:relative;display:inline-flex;align-items:center}.tiptap-editor-wrapper .tiptap-toolbar .toolbar-select{appearance:none;height:32px;border:none;border-radius:.375rem;background:rgba(0,0,0,0);font-size:14.5px;cursor:pointer;padding:0 1.5rem 0 .5rem;color:#4b5563;font-family:inherit;font-weight:500;transition:all .15s ease}.tiptap-editor-wrapper .tiptap-toolbar .toolbar-select:hover{background:#f3f4f6;color:#111827}.tiptap-editor-wrapper .tiptap-toolbar .toolbar-select:focus{outline:none;box-shadow:0 0 0 2px rgba(15,98,254,.2)}.tiptap-editor-wrapper .tiptap-toolbar .select-caret{position:absolute;right:.5rem;pointer-events:none;width:16px;height:16px;fill:#6b7280}.tiptap-editor-wrapper .tiptap-highlight-menu{position:absolute;top:110%;left:50%;transform:translateX(-50%);background:#fff;border:1px solid #e5e7eb;border-radius:9999px;padding:4px;display:flex;gap:6px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);z-index:100}.tiptap-editor-wrapper .tiptap-highlight-menu .color-swatch{width:18px !important;height:18px !important;min-width:18px !important;min-height:18px !important;border-radius:50% !important;border:1px solid rgba(0,0,0,.1) !important;cursor:pointer;padding:0 !important;background-color:#fff;display:flex;align-items:center;justify-content:center;transition:transform .1s ease}.tiptap-editor-wrapper .tiptap-highlight-menu .color-swatch:hover{transform:scale(1.2)}.tiptap-editor-wrapper .tiptap-highlight-menu .color-swatch.clear-swatch{background-color:rgba(0,0,0,0) !important;border:1px solid #d1d5db !important}.tiptap-editor-wrapper .tiptap-highlight-menu .color-swatch svg{width:12px;height:12px;color:#6b7280}.tiptap-editor-wrapper .tiptap-font-menu{position:absolute;top:110%;left:0;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:4px;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);z-index:100;min-width:180px;max-height:300px;overflow-y:auto}.tiptap-editor-wrapper .tiptap-font-menu .font-option{width:100%;text-align:left;padding:8px 12px;border-radius:4px;background:none;border:none;font-size:14px;color:#374151;cursor:pointer;transition:all .15s ease}.tiptap-editor-wrapper .tiptap-font-menu .font-option:hover:not(.is-active){background:#f3f4f6;color:#111827}.tiptap-editor-wrapper .tiptap-font-menu .font-option.is-active{background:#0062ff;color:#fff;font-weight:500}.tiptap-editor-wrapper .tiptap-heading-menu{position:absolute;top:110%;left:0;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:4px;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);z-index:100;min-width:140px}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option{width:100%;text-align:left;padding:6px 12px;border-radius:4px;background:none;border:none;font-size:14px;color:#374151;cursor:pointer;transition:all .15s ease}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option:hover{background:#f3f4f6;color:#111827}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option.is-active{background:#eff6ff;color:#2563eb;font-weight:600}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option.h1{font-size:1.35rem;font-weight:700}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option.h2{font-size:1.25rem;font-weight:600}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option.h3{font-size:1.15rem;font-weight:600}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option.h4{font-size:1.05rem;font-weight:600}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option.h5{font-size:1rem;font-weight:600}.tiptap-editor-wrapper .tiptap-heading-menu .heading-option.h6{font-size:.9rem;font-weight:600}.tiptap-editor-wrapper .tiptap-color-picker{position:absolute;top:110%;left:50%;transform:translateX(-50%);background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:10px;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);z-index:100;width:160px}.tiptap-editor-wrapper .tiptap-color-picker .color-remove-btn{width:100%;display:flex;align-items:center;justify-content:center;gap:8px;padding:8px;margin-bottom:8px;background:#fdfdfd;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;color:#4b5563;cursor:pointer;transition:all .15s ease}.tiptap-editor-wrapper .tiptap-color-picker .color-remove-btn:hover{background:#f3f4f6;border-color:#d1d5db;color:#111827}.tiptap-editor-wrapper .tiptap-color-picker .color-remove-btn svg{width:14px;height:14px}.tiptap-editor-wrapper .tiptap-color-picker .color-grid{display:grid;grid-template-columns:repeat(5, 1fr);gap:4px}.tiptap-editor-wrapper .tiptap-color-picker .color-grid .color-swatch{width:24px !important;height:24px !important;min-width:24px !important;min-height:24px !important;border-radius:4px !important;border:1px solid rgba(0,0,0,.08) !important;padding:0 !important;cursor:pointer;transition:transform .15s ease,border-color .15s ease}.tiptap-editor-wrapper .tiptap-color-picker .color-grid .color-swatch:hover{transform:scale(1.15);border-color:rgba(0,0,0,.2) !important;z-index:2}.tiptap-editor-wrapper .tiptap-link-menu{position:absolute;top:110%;left:50%;transform:translateX(-50%);background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:4px;display:flex;align-items:center;gap:2px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);z-index:100;width:300px}.tiptap-editor-wrapper .tiptap-link-menu input{flex:1;border:1px solid #d1d5db;border-radius:4px;padding:4px 8px;font-size:14px;outline:none;transition:border-color .15s ease}.tiptap-editor-wrapper .tiptap-link-menu input:focus{border-color:#3b82f6;box-shadow:0 0 0 1px #3b82f6}.tiptap-editor-wrapper .tiptap-link-menu button{width:28px !important;height:28px !important;padding:0 !important;min-width:28px !important;border-radius:4px !important;display:flex;align-items:center;justify-content:center}.tiptap-editor-wrapper .tiptap-link-menu button svg{width:14px;height:14px}.tiptap-editor-wrapper .tiptap-link-menu button.link-submit-btn{color:#22c55e}.tiptap-editor-wrapper .tiptap-link-menu button.link-submit-btn:hover{background:#dcfce7}.tiptap-editor-wrapper .tiptap-link-menu button.link-clear-btn{color:#ef4444}.tiptap-editor-wrapper .tiptap-link-menu button.link-clear-btn:hover{background:#fee2e2}.tiptap-editor-wrapper .tiptap-table-menu{position:absolute;top:110%;left:0;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:8px;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);z-index:100;min-width:200px}.tiptap-editor-wrapper .tiptap-table-menu .table-grid-picker-wrapper{padding:4px;background:#fff}.tiptap-editor-wrapper .tiptap-table-menu .table-grid-picker-wrapper .table-grid-picker{display:flex;flex-direction:column;gap:2px;padding-bottom:8px}.tiptap-editor-wrapper .tiptap-table-menu .table-grid-picker-wrapper .grid-row{display:flex;gap:2px}.tiptap-editor-wrapper .tiptap-table-menu .table-grid-picker-wrapper .grid-cell{width:16px;height:16px;border:1px solid #d1d5db;border-radius:1px;cursor:pointer;transition:background-color .1s,border-color .1s}.tiptap-editor-wrapper .tiptap-table-menu .table-grid-picker-wrapper .grid-cell:hover{border-color:#3b82f6}.tiptap-editor-wrapper .tiptap-table-menu .table-grid-picker-wrapper .grid-cell.is-active{background-color:#bfdbfe;border-color:#3b82f6}.tiptap-editor-wrapper .tiptap-table-menu .table-grid-picker-wrapper .table-grid-label{text-align:center;font-size:13px;color:#4b5563;font-weight:500;border-top:1px solid #f3f4f6;padding-top:8px;margin-top:2px}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-item{width:100%;display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:4px;background:none;border:none;font-size:13px;color:#374151;transition:background .15s ease;white-space:nowrap;cursor:pointer}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-item:hover{background:#f3f4f6}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-item svg{width:16px;height:16px;color:#6b7280}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-grid{display:grid;grid-template-columns:repeat(3, 1fr);gap:6px}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-grid button{width:32px !important;height:32px !important;padding:0 !important;min-width:32px !important;display:flex;align-items:center;justify-content:center;border-radius:4px;background:#f9fafb !important;border:1px solid #e5e7eb !important;color:#4b5563 !important;cursor:pointer}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-grid button:hover{background:#f3f4f6 !important;border-color:#d1d5db !important}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-grid button.danger{color:#ef4444 !important}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-grid button.danger:hover{background:#fee2e2 !important;border-color:#fca5a5 !important}.tiptap-editor-wrapper .tiptap-table-menu .table-menu-grid button svg{width:16px;height:16px}.tiptap-editor-wrapper .ProseMirror{min-height:300px;padding:1.5rem;color:#1f2937;outline:none;line-height:1.7}.tiptap-editor-wrapper .ProseMirror p.is-editor-empty:first-child::before{color:#9ca3af;content:attr(data-placeholder);float:left;height:0;pointer-events:none}.tiptap-editor-wrapper .ProseMirror h1,.tiptap-editor-wrapper .ProseMirror h2,.tiptap-editor-wrapper .ProseMirror h3,.tiptap-editor-wrapper .ProseMirror h4{line-height:1.2;color:#111827}.tiptap-editor-wrapper .ProseMirror h1{font-size:2.25rem;font-weight:700;margin:1em 0 .5em}.tiptap-editor-wrapper .ProseMirror h2{font-size:1.875rem;font-weight:600;margin:1em 0 .5em}.tiptap-editor-wrapper .ProseMirror h3{font-size:1.5rem;font-weight:600;margin:1em 0 .5em}.tiptap-editor-wrapper .ProseMirror h4{font-size:1.25rem;font-weight:600;margin:1em 0 .5em}.tiptap-editor-wrapper .ProseMirror [style*="font-size: 0.7em"],.tiptap-editor-wrapper .ProseMirror .text-tiny{font-size:.7em}.tiptap-editor-wrapper .ProseMirror [style*="font-size: 0.85em"],.tiptap-editor-wrapper .ProseMirror .text-small{font-size:.85em}.tiptap-editor-wrapper .ProseMirror [style*="font-size: 1.4em"],.tiptap-editor-wrapper .ProseMirror .text-big{font-size:1.4em}.tiptap-editor-wrapper .ProseMirror [style*="font-size: 1.8em"],.tiptap-editor-wrapper .ProseMirror .text-huge{font-size:1.8em}.tiptap-editor-wrapper .ProseMirror p{margin:.5em 0}.tiptap-editor-wrapper .ProseMirror ul{list-style-type:disc;padding-left:1.5rem;margin:.5em 0}.tiptap-editor-wrapper .ProseMirror ol{list-style-type:decimal;padding-left:1.5rem;margin:.5em 0}.tiptap-editor-wrapper .ProseMirror blockquote{border-left:4px solid #e5e7eb;padding-left:1rem;margin:1em 0;color:#4b5563;font-style:italic}.tiptap-editor-wrapper .ProseMirror code{background:#f3f4f6;border-radius:4px;padding:.2rem .4rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.875em}.tiptap-editor-wrapper .ProseMirror pre{background:#111827;color:#f3f4f6;border-radius:.5rem;padding:1rem;overflow-x:auto;margin:1em 0}.tiptap-editor-wrapper .ProseMirror pre code{background:none;padding:0;color:inherit}.tiptap-editor-wrapper .ProseMirror img{max-width:100%;height:auto;display:block;margin:1rem 0;border-radius:.375rem;box-shadow:0 1px 3px 0 rgba(0,0,0,.1)}.tiptap-editor-wrapper .ProseMirror a{color:#2563eb;text-decoration:underline;text-underline-offset:2px;font-weight:500;cursor:pointer}.tiptap-editor-wrapper .ProseMirror a:hover{color:#1d4ed8}.tiptap-editor-wrapper .ProseMirror a[data-attachment=true]{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .6rem;background:#f3f4f6;border:1px solid #e5e7eb;border-radius:.375rem;font-size:14px;text-decoration:none;color:#111827;font-weight:500;transition:all .15s ease}.tiptap-editor-wrapper .ProseMirror a[data-attachment=true]::before{content:"📎";font-size:13px;opacity:.7}.tiptap-editor-wrapper .ProseMirror a[data-attachment=true]:hover{background:#e5e7eb;border-color:#d1d5db}.tiptap-editor-wrapper .ProseMirror mark{background-color:#fde047;border-radius:.125rem;padding:.125rem .25rem;color:#854d0e}.tiptap-editor-wrapper .ProseMirror sup{vertical-align:super;font-size:.75em}.tiptap-editor-wrapper .ProseMirror sub{vertical-align:sub;font-size:.75em}.tiptap-editor-wrapper .ProseMirror table{border-collapse:collapse;table-layout:fixed;width:100%;margin:1.5rem 0;overflow:hidden}.tiptap-editor-wrapper .ProseMirror table td,.tiptap-editor-wrapper .ProseMirror table th{min-width:1em;border:1px solid #ced4da;padding:8px 12px;vertical-align:top;box-sizing:border-box;position:relative}.tiptap-editor-wrapper .ProseMirror table td>*,.tiptap-editor-wrapper .ProseMirror table th>*{margin-bottom:0}.tiptap-editor-wrapper .ProseMirror table th{font-weight:bold;text-align:left;background-color:#f8f9fa}.tiptap-editor-wrapper .ProseMirror table .selectedCell:after{z-index:2;position:absolute;content:"";left:0;right:0;top:0;bottom:0;background:rgba(200,200,255,.4);pointer-events:none}.tiptap-editor-wrapper .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:-2px;width:4px;background-color:#adf;pointer-events:none}.tiptap-editor-wrapper .ProseMirror .tableWrapper{overflow-x:auto;margin:1.5rem 0}.tiptap-editor-wrapper.read-only .tiptap-toolbar{background:#f3f3f3;pointer-events:none}.tiptap-editor-wrapper.read-only .tiptap-toolbar button,.tiptap-editor-wrapper.read-only .tiptap-toolbar .toolbar-select{opacity:.5;filter:grayscale(1)}.tiptap-editor-wrapper.read-only .ProseMirror{background:#f3f3f3;color:#6b7280;cursor:default;min-height:100px}.tiptap-editor-wrapper .tiptap-toolbar.toolbar-disabled{background:#f3f3f3;border-bottom-color:#e5e7eb}.tiptap-editor-wrapper .tiptap-toolbar.toolbar-disabled *{cursor:not-allowed !important}.tiptap-editor-wrapper .tiptap-loading-bar{height:3px;background:linear-gradient(90deg, #0f62fe 0%, #4589ff 50%, #0f62fe 100%);background-size:200% 100%;animation:tiptap-loading-anim 1.2s linear infinite;border-radius:0}@keyframes tiptap-loading-anim{0%{background-position:200% 0}100%{background-position:-200% 0}}.tiptap-hidden-input{display:none !important}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import * as _tiptap_extension_table_row from '@tiptap/extension-table-row';
|
|
3
|
+
import * as _tiptap_extension_underline from '@tiptap/extension-underline';
|
|
4
|
+
import * as _tiptap_core from '@tiptap/core';
|
|
5
|
+
import { Extension } from '@tiptap/core';
|
|
6
|
+
|
|
7
|
+
type ToolbarGroup = 'history' | 'font' | 'fontSize' | 'textColor' | 'highlight' | 'list' | 'alignment' | 'table' | 'inline' | 'link' | 'insert';
|
|
8
|
+
interface ToolbarConfig {
|
|
9
|
+
groups?: ToolbarGroup[];
|
|
10
|
+
showDividers?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface TiptapEditorProps {
|
|
13
|
+
elementId: string;
|
|
14
|
+
content: string;
|
|
15
|
+
editMode: boolean;
|
|
16
|
+
docNum: string;
|
|
17
|
+
receiveData?: (elementId: string, data: string) => void;
|
|
18
|
+
receiveStatus?: (elementId: string, data: boolean) => void;
|
|
19
|
+
/** Actual file upload function. Returns the uploaded file URL. */
|
|
20
|
+
onUploadFile?: (file: File, docNum: string) => Promise<string>;
|
|
21
|
+
/** Custom alert/notification function (defaults to window.alert) */
|
|
22
|
+
onAlert?: (message: string) => void;
|
|
23
|
+
/** Callback for external loading state changes */
|
|
24
|
+
onLoadingChange?: (isLoading: boolean) => void;
|
|
25
|
+
/** Register a content reset callback */
|
|
26
|
+
onRegisterReset?: (resetCallback: (newContent: string) => void) => (() => void);
|
|
27
|
+
maxFileSizeMB?: number;
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
toolbarConfig?: ToolbarConfig;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare const TiptapEditor: React.FC<TiptapEditorProps>;
|
|
33
|
+
|
|
34
|
+
declare const createTiptapExtensions: (placeholder?: string) => (_tiptap_core.Extension<any, any> | _tiptap_core.Mark<_tiptap_extension_underline.UnderlineOptions, any> | _tiptap_core.Node<_tiptap_extension_table_row.TableRowOptions, any>)[];
|
|
35
|
+
|
|
36
|
+
declare module '@tiptap/core' {
|
|
37
|
+
interface Commands<ReturnType> {
|
|
38
|
+
fontSize: {
|
|
39
|
+
/**
|
|
40
|
+
* Set the font size
|
|
41
|
+
*/
|
|
42
|
+
setFontSize: (size: string) => ReturnType;
|
|
43
|
+
/**
|
|
44
|
+
* Unset the font size
|
|
45
|
+
*/
|
|
46
|
+
unsetFontSize: () => ReturnType;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
declare const FontSize: Extension<any, any>;
|
|
51
|
+
|
|
52
|
+
declare module '@tiptap/core' {
|
|
53
|
+
interface Commands<ReturnType> {
|
|
54
|
+
backgroundColor: {
|
|
55
|
+
/**
|
|
56
|
+
* Set the background color
|
|
57
|
+
*/
|
|
58
|
+
setBackgroundColor: (color: string) => ReturnType;
|
|
59
|
+
/**
|
|
60
|
+
* Unset the background color
|
|
61
|
+
*/
|
|
62
|
+
unsetBackgroundColor: () => ReturnType;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
declare const BackgroundColor: Extension<any, any>;
|
|
67
|
+
|
|
68
|
+
declare const TIPTAP_COLORS: string[];
|
|
69
|
+
declare const FONT_FAMILIES: {
|
|
70
|
+
label: string;
|
|
71
|
+
value: string;
|
|
72
|
+
}[];
|
|
73
|
+
declare const FONT_SIZES: {
|
|
74
|
+
label: string;
|
|
75
|
+
value: string;
|
|
76
|
+
}[];
|
|
77
|
+
declare const IMAGE_MIME_TYPES: string[];
|
|
78
|
+
|
|
79
|
+
export { BackgroundColor, FONT_FAMILIES, FONT_SIZES, FontSize, IMAGE_MIME_TYPES, TIPTAP_COLORS, TiptapEditor, type TiptapEditorProps, createTiptapExtensions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import * as _tiptap_extension_table_row from '@tiptap/extension-table-row';
|
|
3
|
+
import * as _tiptap_extension_underline from '@tiptap/extension-underline';
|
|
4
|
+
import * as _tiptap_core from '@tiptap/core';
|
|
5
|
+
import { Extension } from '@tiptap/core';
|
|
6
|
+
|
|
7
|
+
type ToolbarGroup = 'history' | 'font' | 'fontSize' | 'textColor' | 'highlight' | 'list' | 'alignment' | 'table' | 'inline' | 'link' | 'insert';
|
|
8
|
+
interface ToolbarConfig {
|
|
9
|
+
groups?: ToolbarGroup[];
|
|
10
|
+
showDividers?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface TiptapEditorProps {
|
|
13
|
+
elementId: string;
|
|
14
|
+
content: string;
|
|
15
|
+
editMode: boolean;
|
|
16
|
+
docNum: string;
|
|
17
|
+
receiveData?: (elementId: string, data: string) => void;
|
|
18
|
+
receiveStatus?: (elementId: string, data: boolean) => void;
|
|
19
|
+
/** Actual file upload function. Returns the uploaded file URL. */
|
|
20
|
+
onUploadFile?: (file: File, docNum: string) => Promise<string>;
|
|
21
|
+
/** Custom alert/notification function (defaults to window.alert) */
|
|
22
|
+
onAlert?: (message: string) => void;
|
|
23
|
+
/** Callback for external loading state changes */
|
|
24
|
+
onLoadingChange?: (isLoading: boolean) => void;
|
|
25
|
+
/** Register a content reset callback */
|
|
26
|
+
onRegisterReset?: (resetCallback: (newContent: string) => void) => (() => void);
|
|
27
|
+
maxFileSizeMB?: number;
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
toolbarConfig?: ToolbarConfig;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare const TiptapEditor: React.FC<TiptapEditorProps>;
|
|
33
|
+
|
|
34
|
+
declare const createTiptapExtensions: (placeholder?: string) => (_tiptap_core.Extension<any, any> | _tiptap_core.Mark<_tiptap_extension_underline.UnderlineOptions, any> | _tiptap_core.Node<_tiptap_extension_table_row.TableRowOptions, any>)[];
|
|
35
|
+
|
|
36
|
+
declare module '@tiptap/core' {
|
|
37
|
+
interface Commands<ReturnType> {
|
|
38
|
+
fontSize: {
|
|
39
|
+
/**
|
|
40
|
+
* Set the font size
|
|
41
|
+
*/
|
|
42
|
+
setFontSize: (size: string) => ReturnType;
|
|
43
|
+
/**
|
|
44
|
+
* Unset the font size
|
|
45
|
+
*/
|
|
46
|
+
unsetFontSize: () => ReturnType;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
declare const FontSize: Extension<any, any>;
|
|
51
|
+
|
|
52
|
+
declare module '@tiptap/core' {
|
|
53
|
+
interface Commands<ReturnType> {
|
|
54
|
+
backgroundColor: {
|
|
55
|
+
/**
|
|
56
|
+
* Set the background color
|
|
57
|
+
*/
|
|
58
|
+
setBackgroundColor: (color: string) => ReturnType;
|
|
59
|
+
/**
|
|
60
|
+
* Unset the background color
|
|
61
|
+
*/
|
|
62
|
+
unsetBackgroundColor: () => ReturnType;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
declare const BackgroundColor: Extension<any, any>;
|
|
67
|
+
|
|
68
|
+
declare const TIPTAP_COLORS: string[];
|
|
69
|
+
declare const FONT_FAMILIES: {
|
|
70
|
+
label: string;
|
|
71
|
+
value: string;
|
|
72
|
+
}[];
|
|
73
|
+
declare const FONT_SIZES: {
|
|
74
|
+
label: string;
|
|
75
|
+
value: string;
|
|
76
|
+
}[];
|
|
77
|
+
declare const IMAGE_MIME_TYPES: string[];
|
|
78
|
+
|
|
79
|
+
export { BackgroundColor, FONT_FAMILIES, FONT_SIZES, FontSize, IMAGE_MIME_TYPES, TIPTAP_COLORS, TiptapEditor, type TiptapEditorProps, createTiptapExtensions };
|