@mpdev_ab/document-creator 0.1.1 → 0.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.
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ npm install vue@^3.3.0 @tiptap/core @tiptap/vue-3 @tiptap/pm @tiptap/starter-kit
|
|
|
22
22
|
<template>
|
|
23
23
|
<DocumentEditor
|
|
24
24
|
v-model="content"
|
|
25
|
-
|
|
25
|
+
@save="onSave"
|
|
26
26
|
/>
|
|
27
27
|
</template>
|
|
28
28
|
|
|
@@ -32,11 +32,9 @@ import { DocumentEditor } from '@mpdev_ab/document-creator'
|
|
|
32
32
|
|
|
33
33
|
const content = ref('')
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
loadTemplates: '/api/templates',
|
|
39
|
-
deleteTemplate: '/api/templates',
|
|
35
|
+
function onSave(html) {
|
|
36
|
+
// html is a string — send it to your API however you like
|
|
37
|
+
console.log(html)
|
|
40
38
|
}
|
|
41
39
|
</script>
|
|
42
40
|
```
|
|
@@ -58,7 +56,7 @@ Then use it in any page/component:
|
|
|
58
56
|
```vue
|
|
59
57
|
<template>
|
|
60
58
|
<ClientOnly>
|
|
61
|
-
<DocumentEditor v-model="content"
|
|
59
|
+
<DocumentEditor v-model="content" @save="onSave" />
|
|
62
60
|
</ClientOnly>
|
|
63
61
|
</template>
|
|
64
62
|
```
|
|
@@ -70,9 +68,8 @@ Wrap in `<ClientOnly>` since the editor requires the DOM.
|
|
|
70
68
|
| Prop | Type | Required | Default | Description |
|
|
71
69
|
|------|------|----------|---------|-------------|
|
|
72
70
|
| `v-model` | `string` | Yes | — | HTML content (two-way binding) |
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `templateMeta` | `TemplateMeta` | No | — | Name and type sent when saving in template mode |
|
|
71
|
+
| `templates` | `Template[]` | No | `[]` | List of templates to show in the load modal |
|
|
72
|
+
| `templatesLoading` | `boolean` | No | `false` | Shows loading state in the load templates modal |
|
|
76
73
|
| `image` | `ImageConfig` | No | `{ maxSize: 5MB, allowedTypes: ['image/jpeg', 'image/png', 'image/gif'] }` | Image upload constraints |
|
|
77
74
|
| `theme` | `Partial<ThemeConfig>` | No | See defaults below | Tailwind class overrides |
|
|
78
75
|
| `placeholder` | `string` | No | `''` | Placeholder text when editor is empty |
|
|
@@ -83,36 +80,110 @@ Wrap in `<ClientOnly>` since the editor requires the DOM.
|
|
|
83
80
|
| Event | Payload | Description |
|
|
84
81
|
|-------|---------|-------------|
|
|
85
82
|
| `update:modelValue` | `string` | Emitted on every content change (used by v-model) |
|
|
86
|
-
| `save` | `
|
|
87
|
-
| `
|
|
83
|
+
| `save` | `string` | Emitted with the HTML string when the user clicks Save |
|
|
84
|
+
| `saveTemplate` | `{ name: string, type: 'document' \| 'snippet', content: string }` | Emitted when the user saves a template |
|
|
85
|
+
| `loadTemplates` | — | Emitted when the load templates modal opens — fetch your templates and pass them via the `templates` prop |
|
|
86
|
+
| `deleteTemplate` | `Template` | Emitted when the user clicks delete on a template |
|
|
87
|
+
| `error` | `{ message: string, detail: unknown }` | Emitted on image upload errors |
|
|
88
88
|
|
|
89
|
-
##
|
|
89
|
+
## How saving works
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
The editor does **not** make any API calls. When the user clicks Save, it emits the `save` event with the HTML string. Your app handles persistence:
|
|
92
92
|
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
93
|
+
```vue
|
|
94
|
+
<template>
|
|
95
|
+
<DocumentEditor v-model="content" @save="onSave" />
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<script setup>
|
|
99
|
+
import { ref } from 'vue'
|
|
100
|
+
import { DocumentEditor } from '@mpdev_ab/document-creator'
|
|
101
|
+
import { useMutation } from '@vue/apollo-composable'
|
|
102
|
+
import gql from 'graphql-tag'
|
|
103
|
+
|
|
104
|
+
const content = ref('')
|
|
105
|
+
|
|
106
|
+
const SAVE_DOCUMENT = gql`
|
|
107
|
+
mutation SaveDocument($content: String!) {
|
|
108
|
+
saveDocument(content: $content) {
|
|
109
|
+
id
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
`
|
|
113
|
+
|
|
114
|
+
const { mutate } = useMutation(SAVE_DOCUMENT)
|
|
115
|
+
|
|
116
|
+
async function onSave(html) {
|
|
117
|
+
await mutate({ content: html })
|
|
102
118
|
}
|
|
119
|
+
</script>
|
|
103
120
|
```
|
|
104
121
|
|
|
105
|
-
|
|
122
|
+
## Templates
|
|
123
|
+
|
|
124
|
+
Templates are managed by your app. The editor emits events — you handle fetching, saving, and deleting.
|
|
125
|
+
|
|
126
|
+
```vue
|
|
127
|
+
<template>
|
|
128
|
+
<DocumentEditor
|
|
129
|
+
v-model="content"
|
|
130
|
+
:templates="templates"
|
|
131
|
+
:templates-loading="loading"
|
|
132
|
+
@save="onSave"
|
|
133
|
+
@load-templates="fetchTemplates"
|
|
134
|
+
@save-template="onSaveTemplate"
|
|
135
|
+
@delete-template="onDeleteTemplate"
|
|
136
|
+
/>
|
|
137
|
+
</template>
|
|
138
|
+
|
|
139
|
+
<script setup>
|
|
140
|
+
import { ref } from 'vue'
|
|
141
|
+
import { DocumentEditor } from '@mpdev_ab/document-creator'
|
|
142
|
+
|
|
143
|
+
const content = ref('')
|
|
144
|
+
const templates = ref([])
|
|
145
|
+
const loading = ref(false)
|
|
146
|
+
|
|
147
|
+
async function fetchTemplates() {
|
|
148
|
+
loading.value = true
|
|
149
|
+
// Fetch from your GraphQL API, REST, etc.
|
|
150
|
+
const result = await yourApi.getTemplates()
|
|
151
|
+
templates.value = result // must be { id, name, type, content }[]
|
|
152
|
+
loading.value = false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function onSave(html) {
|
|
156
|
+
await yourApi.saveDocument(html)
|
|
157
|
+
}
|
|
106
158
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
159
|
+
async function onSaveTemplate(data) {
|
|
160
|
+
// data = { name: string, type: 'document' | 'snippet', content: string }
|
|
161
|
+
await yourApi.createTemplate(data)
|
|
162
|
+
await fetchTemplates() // refresh the list
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function onDeleteTemplate(template) {
|
|
166
|
+
// template = { id, name, type, content }
|
|
167
|
+
await yourApi.deleteTemplate(template.id)
|
|
168
|
+
await fetchTemplates() // refresh the list
|
|
169
|
+
}
|
|
170
|
+
</script>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Template object shape
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
interface Template {
|
|
177
|
+
id: string | number
|
|
178
|
+
name: string
|
|
179
|
+
type: 'document' | 'snippet'
|
|
180
|
+
content: string
|
|
181
|
+
}
|
|
113
182
|
```
|
|
114
183
|
|
|
115
|
-
|
|
184
|
+
Template types:
|
|
185
|
+
- `document` — replaces the entire editor content when loaded
|
|
186
|
+
- `snippet` — inserts at the cursor position when loaded
|
|
116
187
|
|
|
117
188
|
## ImageConfig
|
|
118
189
|
|
|
@@ -132,7 +203,6 @@ Override any part of the default Tailwind classes:
|
|
|
132
203
|
```vue
|
|
133
204
|
<DocumentEditor
|
|
134
205
|
v-model="content"
|
|
135
|
-
:api="api"
|
|
136
206
|
:theme="{
|
|
137
207
|
toolbar: 'bg-gray-900 border-b border-gray-700 p-2 flex flex-wrap items-center gap-1',
|
|
138
208
|
toolbarButton: 'hover:bg-gray-700 rounded p-1.5 text-gray-300 cursor-pointer',
|
|
@@ -181,47 +251,24 @@ if (isEmpty()) {
|
|
|
181
251
|
|
|
182
252
|
This must be called from a component that is a child of `<DocumentEditor>`.
|
|
183
253
|
|
|
184
|
-
## Template mode
|
|
185
|
-
|
|
186
|
-
Save the editor content as a reusable template:
|
|
187
|
-
|
|
188
|
-
```vue
|
|
189
|
-
<DocumentEditor
|
|
190
|
-
v-model="content"
|
|
191
|
-
mode="template"
|
|
192
|
-
:template-meta="{ name: 'My Template', type: 'document' }"
|
|
193
|
-
:api="api"
|
|
194
|
-
@save="(res) => console.log('Template saved', res)"
|
|
195
|
-
/>
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
When `mode="template"`, clicking Save sends `{ name, type, content }` to `api.saveTemplate` instead of `api.saveDocument`.
|
|
199
|
-
|
|
200
|
-
Template types:
|
|
201
|
-
- `document` — replaces the entire editor content when loaded
|
|
202
|
-
- `snippet` — inserts at the cursor position when loaded
|
|
203
|
-
|
|
204
254
|
## Full example
|
|
205
255
|
|
|
206
256
|
```vue
|
|
207
257
|
<template>
|
|
208
258
|
<DocumentEditor
|
|
209
259
|
v-model="content"
|
|
210
|
-
mode="document"
|
|
211
260
|
placeholder="Write your document..."
|
|
212
261
|
:readonly="false"
|
|
213
|
-
:
|
|
214
|
-
|
|
215
|
-
saveTemplate: '/api/templates',
|
|
216
|
-
loadTemplates: '/api/templates',
|
|
217
|
-
deleteTemplate: '/api/templates',
|
|
218
|
-
headers: { 'Authorization': `Bearer ${token}` },
|
|
219
|
-
}"
|
|
262
|
+
:templates="templates"
|
|
263
|
+
:templates-loading="templatesLoading"
|
|
220
264
|
:image="{
|
|
221
265
|
maxSize: 10 * 1024 * 1024,
|
|
222
266
|
allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
|
|
223
267
|
}"
|
|
224
268
|
@save="onSave"
|
|
269
|
+
@save-template="onSaveTemplate"
|
|
270
|
+
@load-templates="onLoadTemplates"
|
|
271
|
+
@delete-template="onDeleteTemplate"
|
|
225
272
|
@error="onError"
|
|
226
273
|
/>
|
|
227
274
|
</template>
|
|
@@ -231,10 +278,25 @@ import { ref } from 'vue'
|
|
|
231
278
|
import { DocumentEditor } from '@mpdev_ab/document-creator'
|
|
232
279
|
|
|
233
280
|
const content = ref('<p>Hello world</p>')
|
|
234
|
-
const
|
|
281
|
+
const templates = ref([])
|
|
282
|
+
const templatesLoading = ref(false)
|
|
283
|
+
|
|
284
|
+
function onSave(html) {
|
|
285
|
+
// Send html to your backend
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function onSaveTemplate(data) {
|
|
289
|
+
// data = { name, type, content }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function onLoadTemplates() {
|
|
293
|
+
templatesLoading.value = true
|
|
294
|
+
templates.value = await fetchTemplatesFromApi()
|
|
295
|
+
templatesLoading.value = false
|
|
296
|
+
}
|
|
235
297
|
|
|
236
|
-
function
|
|
237
|
-
|
|
298
|
+
function onDeleteTemplate(template) {
|
|
299
|
+
// template = { id, name, type, content }
|
|
238
300
|
}
|
|
239
301
|
|
|
240
302
|
function onError(error) {
|