frappe-ui 0.0.39 → 0.0.44
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/package.json +1 -1
- package/src/components/Avatar.vue +1 -1
- package/src/components/Dialog.vue +6 -2
- package/src/components/TextEditor/Menu.vue +63 -3
- package/src/components/TextEditor/TextEditor.vue +10 -1
- package/src/components/TextEditor/commands.js +6 -0
- package/src/components/TextEditor/icons/image-add-line.vue +14 -0
- package/src/utils/resources.js +22 -6
package/package.json
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<TransitionRoot
|
|
2
|
+
<TransitionRoot
|
|
3
|
+
as="template"
|
|
4
|
+
:show="open"
|
|
5
|
+
@after-leave="$emit('after-leave')"
|
|
6
|
+
>
|
|
3
7
|
<HDialog
|
|
4
8
|
as="div"
|
|
5
9
|
class="fixed inset-0 z-10 overflow-y-auto"
|
|
@@ -137,7 +141,7 @@ export default {
|
|
|
137
141
|
},
|
|
138
142
|
},
|
|
139
143
|
},
|
|
140
|
-
emits: ['update:modelValue', 'close'],
|
|
144
|
+
emits: ['update:modelValue', 'close', 'after-leave'],
|
|
141
145
|
components: {
|
|
142
146
|
HDialog,
|
|
143
147
|
DialogOverlay,
|
|
@@ -67,18 +67,56 @@
|
|
|
67
67
|
</Button>
|
|
68
68
|
</template>
|
|
69
69
|
</Dialog>
|
|
70
|
+
<Dialog
|
|
71
|
+
:options="{ title: 'Add Image' }"
|
|
72
|
+
v-model="addImageDialog.show"
|
|
73
|
+
@after-leave="resetAddImage"
|
|
74
|
+
>
|
|
75
|
+
<template #body-content>
|
|
76
|
+
<label
|
|
77
|
+
class="relative py-1 bg-gray-100 rounded-lg cursor-pointer focus-within:bg-gray-200 hover:bg-gray-200"
|
|
78
|
+
>
|
|
79
|
+
<input
|
|
80
|
+
type="file"
|
|
81
|
+
class="w-full opacity-0"
|
|
82
|
+
@change="onImageSelect"
|
|
83
|
+
accept="image/*"
|
|
84
|
+
/>
|
|
85
|
+
<span class="absolute inset-0 px-2 py-1 text-base select-none">
|
|
86
|
+
{{
|
|
87
|
+
addImageDialog.file ? 'Select another image' : 'Select an image'
|
|
88
|
+
}}
|
|
89
|
+
</span>
|
|
90
|
+
</label>
|
|
91
|
+
<img
|
|
92
|
+
v-if="addImageDialog.url"
|
|
93
|
+
:src="addImageDialog.url"
|
|
94
|
+
class="w-full mt-2 rounded-lg"
|
|
95
|
+
/>
|
|
96
|
+
</template>
|
|
97
|
+
<template #actions>
|
|
98
|
+
<Button appearance="primary" @click="addImage(addImageDialog.url)">
|
|
99
|
+
Insert Image
|
|
100
|
+
</Button>
|
|
101
|
+
</template>
|
|
102
|
+
</Dialog>
|
|
70
103
|
</div>
|
|
71
104
|
</template>
|
|
72
105
|
<script>
|
|
73
|
-
import { Popover } from 'frappe-ui'
|
|
74
|
-
|
|
106
|
+
import { Popover, Dialog, Input, Button } from 'frappe-ui'
|
|
75
107
|
export default {
|
|
76
108
|
name: 'TipTapMenu',
|
|
77
109
|
props: ['editor', 'buttons'],
|
|
78
|
-
components: {
|
|
110
|
+
components: {
|
|
111
|
+
Popover,
|
|
112
|
+
Dialog,
|
|
113
|
+
Input,
|
|
114
|
+
Button,
|
|
115
|
+
},
|
|
79
116
|
data() {
|
|
80
117
|
return {
|
|
81
118
|
setLinkDialog: { url: '', show: false },
|
|
119
|
+
addImageDialog: { url: '', file: null, show: false },
|
|
82
120
|
}
|
|
83
121
|
},
|
|
84
122
|
methods: {
|
|
@@ -89,6 +127,8 @@ export default {
|
|
|
89
127
|
if (existingURL) {
|
|
90
128
|
this.setLinkDialog.url = existingURL
|
|
91
129
|
}
|
|
130
|
+
} else if (button.label === 'Image') {
|
|
131
|
+
this.addImageDialog.show = true
|
|
92
132
|
} else {
|
|
93
133
|
button.action(this.editor)
|
|
94
134
|
}
|
|
@@ -110,6 +150,26 @@ export default {
|
|
|
110
150
|
this.setLinkDialog.show = false
|
|
111
151
|
this.setLinkDialog.url = ''
|
|
112
152
|
},
|
|
153
|
+
onImageSelect(e) {
|
|
154
|
+
let file = e.target.files[0]
|
|
155
|
+
if (!file) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
this.addImageDialog.file = file
|
|
159
|
+
let reader = new FileReader()
|
|
160
|
+
reader.onloadend = () => {
|
|
161
|
+
let base64string = reader.result
|
|
162
|
+
this.addImageDialog.url = base64string
|
|
163
|
+
}
|
|
164
|
+
reader.readAsDataURL(file)
|
|
165
|
+
},
|
|
166
|
+
addImage(src) {
|
|
167
|
+
this.editor.chain().focus().setImage({ src }).run()
|
|
168
|
+
this.resetAddImage()
|
|
169
|
+
},
|
|
170
|
+
resetAddImage() {
|
|
171
|
+
this.addImageDialog = { show: false, url: null, file: null }
|
|
172
|
+
},
|
|
113
173
|
},
|
|
114
174
|
}
|
|
115
175
|
</script>
|
|
@@ -41,6 +41,12 @@
|
|
|
41
41
|
</button>
|
|
42
42
|
</FloatingMenu>
|
|
43
43
|
<editor-content :editor="editor" />
|
|
44
|
+
<span
|
|
45
|
+
v-if="!content"
|
|
46
|
+
class="absolute inset-y-0 text-base text-gray-500 pointer-events-none"
|
|
47
|
+
>
|
|
48
|
+
{{ placeholder }}
|
|
49
|
+
</span>
|
|
44
50
|
</div>
|
|
45
51
|
</template>
|
|
46
52
|
|
|
@@ -113,7 +119,9 @@ export default {
|
|
|
113
119
|
TextAlign.configure({
|
|
114
120
|
types: ['heading', 'paragraph'],
|
|
115
121
|
}),
|
|
116
|
-
Image
|
|
122
|
+
Image.configure({
|
|
123
|
+
allowBase64: true,
|
|
124
|
+
}),
|
|
117
125
|
Link,
|
|
118
126
|
Placeholder.configure({
|
|
119
127
|
placeholder: this.placeholder || 'Write something...',
|
|
@@ -157,6 +165,7 @@ export default {
|
|
|
157
165
|
'Align Center',
|
|
158
166
|
'Align Right',
|
|
159
167
|
'Separator',
|
|
168
|
+
'Image',
|
|
160
169
|
'Blockquote',
|
|
161
170
|
'Code',
|
|
162
171
|
'Horizontal Rule',
|
|
@@ -16,6 +16,7 @@ import ListUnordered from './icons/list-unordered.vue'
|
|
|
16
16
|
import DoubleQuotes from './icons/double-quotes-r.vue'
|
|
17
17
|
import CodeView from './icons/code-view.vue'
|
|
18
18
|
import Link from './icons/link.vue'
|
|
19
|
+
import Image from './icons/image-add-line.vue'
|
|
19
20
|
import ArrowGoBack from './icons/arrow-go-back-line.vue'
|
|
20
21
|
import ArrowGoForward from './icons/arrow-go-forward-line.vue'
|
|
21
22
|
import Separator from './icons/separator.vue'
|
|
@@ -146,6 +147,11 @@ export default {
|
|
|
146
147
|
icon: Link,
|
|
147
148
|
isActive: (editor) => editor.isActive('link'),
|
|
148
149
|
},
|
|
150
|
+
Image: {
|
|
151
|
+
label: 'Image',
|
|
152
|
+
icon: Image,
|
|
153
|
+
isActive: (editor) => false,
|
|
154
|
+
},
|
|
149
155
|
Undo: {
|
|
150
156
|
label: 'Undo',
|
|
151
157
|
icon: ArrowGoBack,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
viewBox="0 0 24 24"
|
|
5
|
+
width="24"
|
|
6
|
+
height="24"
|
|
7
|
+
>
|
|
8
|
+
<path fill="none" d="M0 0h24v24H0z" />
|
|
9
|
+
<path
|
|
10
|
+
d="M21 15v3h3v2h-3v3h-2v-3h-3v-2h3v-3h2zm.008-12c.548 0 .992.445.992.993V13h-2V5H4v13.999L14 9l3 3v2.829l-3-3L6.827 19H14v2H2.992A.993.993 0 0 1 2 20.007V3.993A1 1 0 0 1 2.992 3h18.016zM8 7a2 2 0 1 1 0 4 2 2 0 0 1 0-4z"
|
|
11
|
+
fill="currentColor"
|
|
12
|
+
/>
|
|
13
|
+
</svg>
|
|
14
|
+
</template>
|
package/src/utils/resources.js
CHANGED
|
@@ -212,6 +212,7 @@ export function createDocumentResource(options, vm) {
|
|
|
212
212
|
},
|
|
213
213
|
onSuccess(data) {
|
|
214
214
|
out.doc = transform(data)
|
|
215
|
+
options.onSuccess?.call(vm, out.doc)
|
|
215
216
|
},
|
|
216
217
|
onError: options.onError,
|
|
217
218
|
},
|
|
@@ -316,10 +317,12 @@ export function createListResource(options, vm, getResource) {
|
|
|
316
317
|
fields: options.fields,
|
|
317
318
|
filters: options.filters,
|
|
318
319
|
order_by: options.order_by,
|
|
319
|
-
start: options.start,
|
|
320
|
-
limit: options.limit,
|
|
320
|
+
start: options.start || 0,
|
|
321
|
+
limit: options.limit || 20,
|
|
321
322
|
originalData: null,
|
|
322
323
|
data: null,
|
|
324
|
+
next,
|
|
325
|
+
hasNextPage: true,
|
|
323
326
|
list: createResource(
|
|
324
327
|
{
|
|
325
328
|
method: 'frappe.client.get_list',
|
|
@@ -334,8 +337,15 @@ export function createListResource(options, vm, getResource) {
|
|
|
334
337
|
}
|
|
335
338
|
},
|
|
336
339
|
onSuccess(data) {
|
|
337
|
-
out.
|
|
338
|
-
|
|
340
|
+
if (data.length < out.limit) {
|
|
341
|
+
out.hasNextPage = false
|
|
342
|
+
}
|
|
343
|
+
if (!out.start || out.start == 0) {
|
|
344
|
+
out.originalData = data
|
|
345
|
+
} else if (out.start > 0) {
|
|
346
|
+
out.originalData = out.originalData.concat(data)
|
|
347
|
+
}
|
|
348
|
+
out.data = transform(out.originalData)
|
|
339
349
|
options.onSuccess?.call(vm, out.data)
|
|
340
350
|
},
|
|
341
351
|
onError: options.onError,
|
|
@@ -455,6 +465,7 @@ export function createListResource(options, vm, getResource) {
|
|
|
455
465
|
}
|
|
456
466
|
|
|
457
467
|
function reload() {
|
|
468
|
+
out.start = 0
|
|
458
469
|
return out.list.fetch()
|
|
459
470
|
}
|
|
460
471
|
|
|
@@ -465,6 +476,11 @@ export function createListResource(options, vm, getResource) {
|
|
|
465
476
|
out.data = data
|
|
466
477
|
}
|
|
467
478
|
|
|
479
|
+
function next() {
|
|
480
|
+
out.start = out.start + out.limit
|
|
481
|
+
out.list.fetch()
|
|
482
|
+
}
|
|
483
|
+
|
|
468
484
|
// fetch list
|
|
469
485
|
out.list.fetch()
|
|
470
486
|
|
|
@@ -480,7 +496,7 @@ export function createListResource(options, vm, getResource) {
|
|
|
480
496
|
}
|
|
481
497
|
|
|
482
498
|
function updateRowInListResource(doctype, doc) {
|
|
483
|
-
let resources = listResources[doctype]
|
|
499
|
+
let resources = listResources[doctype] || []
|
|
484
500
|
for (let resource of resources) {
|
|
485
501
|
if (resource.originalData) {
|
|
486
502
|
for (let row of resource.originalData) {
|
|
@@ -500,7 +516,7 @@ function updateRowInListResource(doctype, doc) {
|
|
|
500
516
|
}
|
|
501
517
|
|
|
502
518
|
function revertRowInListResource(doctype, doc) {
|
|
503
|
-
let resources = listResources[doctype]
|
|
519
|
+
let resources = listResources[doctype] || []
|
|
504
520
|
for (let resource of resources) {
|
|
505
521
|
if (resource.originalData) {
|
|
506
522
|
for (let row of resource.originalData) {
|