frappe-ui 0.0.13 → 0.0.17
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 +2 -2
- package/src/components/Avatar.vue +1 -1
- package/src/components/Dropdown.vue +2 -2
- package/src/components/Input.vue +1 -1
- package/src/components/TextEditor/Menu.vue +4 -93
- package/src/components/TextEditor/TextEditor.vue +115 -11
- package/src/components/TextEditor/commands.js +79 -0
- package/src/components/Toast.vue +135 -0
- package/src/utils/resources.js +143 -46
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frappe-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "A set of components and utilities for rapid UI development",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"author": "Frappe Technologies Pvt. Ltd.",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@headlessui/vue": "^1.
|
|
20
|
+
"@headlessui/vue": "^1.5.0",
|
|
21
21
|
"@popperjs/core": "^2.11.2",
|
|
22
22
|
"@tailwindcss/forms": "^0.4.0",
|
|
23
23
|
"@tailwindcss/typography": "^0.5.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
active ? 'bg-gray-100' : 'text-gray-900',
|
|
41
41
|
'group flex rounded-md items-center w-full px-2 py-2 text-sm',
|
|
42
42
|
]"
|
|
43
|
-
|
|
43
|
+
@click="item.onClick"
|
|
44
44
|
>
|
|
45
45
|
<FeatherIcon
|
|
46
46
|
v-if="item.icon"
|
|
@@ -97,7 +97,7 @@ export default {
|
|
|
97
97
|
groups() {
|
|
98
98
|
let groups = this.options[0]?.group
|
|
99
99
|
? this.options
|
|
100
|
-
: [{ group: '', items: this.
|
|
100
|
+
: [{ group: '', items: this.options }]
|
|
101
101
|
|
|
102
102
|
return groups.map((group, i) => {
|
|
103
103
|
return {
|
package/src/components/Input.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="inline-flex px-1 py-1 bg-white">
|
|
3
3
|
<div class="inline-flex items-center gap-1">
|
|
4
|
-
<template v-for="button in
|
|
4
|
+
<template v-for="button in buttons" :key="button.label">
|
|
5
5
|
<div
|
|
6
6
|
class="border-l w-[2px] h-4"
|
|
7
7
|
v-if="button.type === 'separator'"
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
<button
|
|
10
10
|
v-else
|
|
11
11
|
class="flex p-1 text-gray-800 transition-colors rounded"
|
|
12
|
-
:class="button.isActive() ? 'bg-gray-100' : 'hover:bg-gray-100'"
|
|
13
|
-
@click="button.action"
|
|
12
|
+
:class="button.isActive(editor) ? 'bg-gray-100' : 'hover:bg-gray-100'"
|
|
13
|
+
@click="() => button.action(editor)"
|
|
14
14
|
:title="button.label"
|
|
15
15
|
>
|
|
16
16
|
<FeatherIcon v-if="button.icon" :name="button.icon" class="w-4" />
|
|
@@ -26,96 +26,7 @@
|
|
|
26
26
|
import { FeatherIcon } from 'frappe-ui'
|
|
27
27
|
export default {
|
|
28
28
|
name: 'TipTapMenu',
|
|
29
|
-
props: ['editor'],
|
|
29
|
+
props: ['editor', 'buttons'],
|
|
30
30
|
components: { FeatherIcon },
|
|
31
|
-
computed: {
|
|
32
|
-
menuButtons() {
|
|
33
|
-
return [
|
|
34
|
-
{
|
|
35
|
-
label: 'Paragraph',
|
|
36
|
-
icon: 'type',
|
|
37
|
-
action: () => this.editor.chain().focus().setParagraph().run(),
|
|
38
|
-
isActive: () => this.editor.isActive('paragraph'),
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
label: 'Heading 2',
|
|
42
|
-
text: 'H2',
|
|
43
|
-
action: () =>
|
|
44
|
-
this.editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
45
|
-
isActive: () => this.editor.isActive('heading', { level: 2 }),
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
label: 'Heading 3',
|
|
49
|
-
text: 'H3',
|
|
50
|
-
action: () =>
|
|
51
|
-
this.editor.chain().focus().toggleHeading({ level: 3 }).run(),
|
|
52
|
-
isActive: () => this.editor.isActive('heading', { level: 3 }),
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
type: 'separator',
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
label: 'Bold',
|
|
59
|
-
icon: 'bold',
|
|
60
|
-
action: () => this.editor.chain().focus().toggleBold().run(),
|
|
61
|
-
isActive: () => this.editor.isActive('bold'),
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
label: 'Italic',
|
|
65
|
-
icon: 'italic',
|
|
66
|
-
action: () => this.editor.chain().focus().toggleItalic().run(),
|
|
67
|
-
isActive: () => this.editor.isActive('italic'),
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
type: 'separator',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
label: 'Bullet List',
|
|
74
|
-
icon: 'list',
|
|
75
|
-
action: () => this.editor.chain().focus().toggleBulletList().run(),
|
|
76
|
-
isActive: () => this.editor.isActive('bulletList'),
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
label: 'Numbered List',
|
|
80
|
-
text: '1.',
|
|
81
|
-
action: () => this.editor.chain().focus().toggleOrderedList().run(),
|
|
82
|
-
isActive: () => this.editor.isActive('orderedList'),
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
label: 'Blockquote',
|
|
86
|
-
icon: 'chevron-right',
|
|
87
|
-
action: () => this.editor.chain().focus().toggleBlockquote().run(),
|
|
88
|
-
isActive: () => this.editor.isActive('blockquote'),
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
label: 'Code',
|
|
92
|
-
icon: 'code',
|
|
93
|
-
action: () => this.editor.chain().focus().toggleCodeBlock().run(),
|
|
94
|
-
isActive: () => this.editor.isActive('codeBlock'),
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
label: 'Horizontal Rule',
|
|
98
|
-
icon: 'minus',
|
|
99
|
-
action: () => this.editor.chain().focus().setHorizontalRule().run(),
|
|
100
|
-
isActive: () => false,
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
type: 'separator',
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
label: 'Undo',
|
|
107
|
-
icon: 'corner-up-left',
|
|
108
|
-
action: () => this.editor.chain().focus().undo().run(),
|
|
109
|
-
isActive: () => false,
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
label: 'Redo',
|
|
113
|
-
icon: 'corner-up-right',
|
|
114
|
-
action: () => this.editor.chain().focus().redo().run(),
|
|
115
|
-
isActive: () => false,
|
|
116
|
-
},
|
|
117
|
-
]
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
31
|
}
|
|
121
32
|
</script>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative w-full" :class="
|
|
2
|
+
<div class="relative w-full" :class="$attrs.class" v-if="editor">
|
|
3
3
|
<BubbleMenu
|
|
4
|
-
v-if="
|
|
4
|
+
v-if="bubbleMenuButtons"
|
|
5
5
|
class="bubble-menu"
|
|
6
6
|
:tippy-options="{ duration: 100 }"
|
|
7
7
|
:editor="editor"
|
|
@@ -9,27 +9,48 @@
|
|
|
9
9
|
<Menu
|
|
10
10
|
:editor="editor"
|
|
11
11
|
class="border border-gray-100 rounded-md shadow-sm"
|
|
12
|
+
:buttons="bubbleMenuButtons"
|
|
12
13
|
/>
|
|
13
14
|
</BubbleMenu>
|
|
14
15
|
|
|
15
16
|
<Menu
|
|
16
|
-
v-if="
|
|
17
|
-
class="
|
|
17
|
+
v-if="fixedMenuButtons"
|
|
18
|
+
class="w-full border rounded-t-lg border-gray-50 border-b-gray-100"
|
|
18
19
|
:editor="editor"
|
|
20
|
+
:buttons="fixedMenuButtons"
|
|
19
21
|
/>
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
<FloatingMenu
|
|
24
|
+
v-if="floatingMenuButtons"
|
|
25
|
+
:tippy-options="{ duration: 100 }"
|
|
21
26
|
:editor="editor"
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
class="flex"
|
|
28
|
+
>
|
|
29
|
+
<button
|
|
30
|
+
v-for="button in floatingMenuButtons"
|
|
31
|
+
:key="button.label"
|
|
32
|
+
class="flex p-1 text-gray-800 transition-colors rounded"
|
|
33
|
+
:class="button.isActive(editor) ? 'bg-gray-100' : 'hover:bg-gray-100'"
|
|
34
|
+
@click="() => button.action(editor)"
|
|
35
|
+
:title="button.label"
|
|
36
|
+
>
|
|
37
|
+
<FeatherIcon v-if="button.icon" :name="button.icon" class="w-4" />
|
|
38
|
+
<span class="inline-block h-4 text-sm leading-4 min-w-[1rem]" v-else>
|
|
39
|
+
{{ button.text }}
|
|
40
|
+
</span>
|
|
41
|
+
</button>
|
|
42
|
+
</FloatingMenu>
|
|
43
|
+
<editor-content :editor="editor" />
|
|
24
44
|
</div>
|
|
25
45
|
</template>
|
|
26
46
|
|
|
27
47
|
<script>
|
|
28
|
-
import { Editor, EditorContent, BubbleMenu } from '@tiptap/vue-3'
|
|
48
|
+
import { Editor, EditorContent, BubbleMenu, FloatingMenu } from '@tiptap/vue-3'
|
|
29
49
|
import StarterKit from '@tiptap/starter-kit'
|
|
30
50
|
import Placeholder from '@tiptap/extension-placeholder'
|
|
31
51
|
import Image from '@tiptap/extension-image'
|
|
32
52
|
import Menu from './Menu.vue'
|
|
53
|
+
import commands from './commands'
|
|
33
54
|
|
|
34
55
|
export default {
|
|
35
56
|
name: 'TextEditor',
|
|
@@ -37,14 +58,17 @@ export default {
|
|
|
37
58
|
components: {
|
|
38
59
|
EditorContent,
|
|
39
60
|
BubbleMenu,
|
|
61
|
+
FloatingMenu,
|
|
40
62
|
Menu,
|
|
41
63
|
},
|
|
42
64
|
props: [
|
|
43
65
|
'content',
|
|
44
66
|
'placeholder',
|
|
45
67
|
'editorClass',
|
|
46
|
-
'
|
|
47
|
-
'
|
|
68
|
+
'fixedMenu',
|
|
69
|
+
'bubbleMenu',
|
|
70
|
+
'floatingMenu',
|
|
71
|
+
'extensions',
|
|
48
72
|
],
|
|
49
73
|
emits: ['change'],
|
|
50
74
|
expose: ['editor'],
|
|
@@ -58,7 +82,7 @@ export default {
|
|
|
58
82
|
content: this.content || '<p></p>',
|
|
59
83
|
editorProps: {
|
|
60
84
|
attributes: {
|
|
61
|
-
class: ['prose-p:my-1', this.editorClass].join(' '),
|
|
85
|
+
class: ['prose prose-sm prose-p:my-1', this.editorClass].join(' '),
|
|
62
86
|
},
|
|
63
87
|
},
|
|
64
88
|
extensions: [
|
|
@@ -67,6 +91,7 @@ export default {
|
|
|
67
91
|
Placeholder.configure({
|
|
68
92
|
placeholder: this.placeholder || 'Write something...',
|
|
69
93
|
}),
|
|
94
|
+
...(this.extensions || []),
|
|
70
95
|
],
|
|
71
96
|
onUpdate: ({ editor }) => {
|
|
72
97
|
this.$emit('change', editor.getHTML())
|
|
@@ -76,6 +101,85 @@ export default {
|
|
|
76
101
|
beforeUnmount() {
|
|
77
102
|
this.editor.destroy()
|
|
78
103
|
},
|
|
104
|
+
computed: {
|
|
105
|
+
fixedMenuButtons() {
|
|
106
|
+
if (!this.fixedMenu) return false
|
|
107
|
+
|
|
108
|
+
let buttons
|
|
109
|
+
if (Array.isArray(this.fixedMenu)) {
|
|
110
|
+
buttons = this.fixedMenu
|
|
111
|
+
} else {
|
|
112
|
+
buttons = [
|
|
113
|
+
'Paragraph',
|
|
114
|
+
'Heading 2',
|
|
115
|
+
'Heading 3',
|
|
116
|
+
'Separator',
|
|
117
|
+
'Bold',
|
|
118
|
+
'Italic',
|
|
119
|
+
'Separator',
|
|
120
|
+
'Bullet List',
|
|
121
|
+
'Numbered List',
|
|
122
|
+
'Blockquote',
|
|
123
|
+
'Code',
|
|
124
|
+
'Horizontal Rule',
|
|
125
|
+
'Separator',
|
|
126
|
+
'Undo',
|
|
127
|
+
'Redo',
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
return buttons.map(createEditorButton)
|
|
131
|
+
},
|
|
132
|
+
bubbleMenuButtons() {
|
|
133
|
+
if (!this.bubbleMenu) return false
|
|
134
|
+
|
|
135
|
+
let buttons
|
|
136
|
+
if (Array.isArray(this.bubbleMenu)) {
|
|
137
|
+
buttons = this.bubbleMenu
|
|
138
|
+
} else {
|
|
139
|
+
buttons = [
|
|
140
|
+
'Paragraph',
|
|
141
|
+
'Heading 2',
|
|
142
|
+
'Heading 3',
|
|
143
|
+
'Separator',
|
|
144
|
+
'Bold',
|
|
145
|
+
'Italic',
|
|
146
|
+
'Separator',
|
|
147
|
+
'Bullet List',
|
|
148
|
+
'Numbered List',
|
|
149
|
+
'Blockquote',
|
|
150
|
+
'Code',
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
return buttons.map(createEditorButton)
|
|
154
|
+
},
|
|
155
|
+
floatingMenuButtons() {
|
|
156
|
+
if (!this.floatingMenu) return false
|
|
157
|
+
|
|
158
|
+
let buttons
|
|
159
|
+
if (Array.isArray(this.floatingMenu)) {
|
|
160
|
+
buttons = this.floatingMenu
|
|
161
|
+
} else {
|
|
162
|
+
buttons = [
|
|
163
|
+
'Paragraph',
|
|
164
|
+
'Heading 2',
|
|
165
|
+
'Heading 3',
|
|
166
|
+
'Bullet List',
|
|
167
|
+
'Numbered List',
|
|
168
|
+
'Blockquote',
|
|
169
|
+
'Code',
|
|
170
|
+
'Horizontal Rule',
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
return buttons.map(createEditorButton)
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function createEditorButton(option) {
|
|
179
|
+
if (typeof option == 'object') {
|
|
180
|
+
return option
|
|
181
|
+
}
|
|
182
|
+
return commands[option]
|
|
79
183
|
}
|
|
80
184
|
</script>
|
|
81
185
|
<style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
Paragraph: {
|
|
3
|
+
label: 'Paragraph',
|
|
4
|
+
icon: 'type',
|
|
5
|
+
action: (editor) => editor.chain().focus().setParagraph().run(),
|
|
6
|
+
isActive: (editor) => editor.isActive('paragraph'),
|
|
7
|
+
},
|
|
8
|
+
'Heading 2': {
|
|
9
|
+
label: 'Heading 2',
|
|
10
|
+
text: 'H2',
|
|
11
|
+
action: (editor) =>
|
|
12
|
+
editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
13
|
+
isActive: (editor) => editor.isActive('heading', { level: 2 }),
|
|
14
|
+
},
|
|
15
|
+
'Heading 3': {
|
|
16
|
+
label: 'Heading 3',
|
|
17
|
+
text: 'H3',
|
|
18
|
+
action: (editor) =>
|
|
19
|
+
editor.chain().focus().toggleHeading({ level: 3 }).run(),
|
|
20
|
+
isActive: (editor) => editor.isActive('heading', { level: 3 }),
|
|
21
|
+
},
|
|
22
|
+
Bold: {
|
|
23
|
+
label: 'Bold',
|
|
24
|
+
icon: 'bold',
|
|
25
|
+
action: (editor) => editor.chain().focus().toggleBold().run(),
|
|
26
|
+
isActive: (editor) => editor.isActive('bold'),
|
|
27
|
+
},
|
|
28
|
+
Italic: {
|
|
29
|
+
label: 'Italic',
|
|
30
|
+
icon: 'italic',
|
|
31
|
+
action: (editor) => editor.chain().focus().toggleItalic().run(),
|
|
32
|
+
isActive: (editor) => editor.isActive('italic'),
|
|
33
|
+
},
|
|
34
|
+
'Bullet List': {
|
|
35
|
+
label: 'Bullet List',
|
|
36
|
+
icon: 'list',
|
|
37
|
+
action: (editor) => editor.chain().focus().toggleBulletList().run(),
|
|
38
|
+
isActive: (editor) => editor.isActive('bulletList'),
|
|
39
|
+
},
|
|
40
|
+
'Numbered List': {
|
|
41
|
+
label: 'Numbered List',
|
|
42
|
+
text: '1.',
|
|
43
|
+
action: (editor) => editor.chain().focus().toggleOrderedList().run(),
|
|
44
|
+
isActive: (editor) => editor.isActive('orderedList'),
|
|
45
|
+
},
|
|
46
|
+
Blockquote: {
|
|
47
|
+
label: 'Blockquote',
|
|
48
|
+
icon: 'chevron-right',
|
|
49
|
+
action: (editor) => editor.chain().focus().toggleBlockquote().run(),
|
|
50
|
+
isActive: (editor) => editor.isActive('blockquote'),
|
|
51
|
+
},
|
|
52
|
+
Code: {
|
|
53
|
+
label: 'Code',
|
|
54
|
+
icon: 'code',
|
|
55
|
+
action: (editor) => editor.chain().focus().toggleCodeBlock().run(),
|
|
56
|
+
isActive: (editor) => editor.isActive('codeBlock'),
|
|
57
|
+
},
|
|
58
|
+
'Horizontal Rule': {
|
|
59
|
+
label: 'Horizontal Rule',
|
|
60
|
+
icon: 'minus',
|
|
61
|
+
action: (editor) => editor.chain().focus().setHorizontalRule().run(),
|
|
62
|
+
isActive: (editor) => false,
|
|
63
|
+
},
|
|
64
|
+
Undo: {
|
|
65
|
+
label: 'Undo',
|
|
66
|
+
icon: 'corner-up-left',
|
|
67
|
+
action: (editor) => editor.chain().focus().undo().run(),
|
|
68
|
+
isActive: (editor) => false,
|
|
69
|
+
},
|
|
70
|
+
Redo: {
|
|
71
|
+
label: 'Redo',
|
|
72
|
+
icon: 'corner-up-right',
|
|
73
|
+
action: (editor) => editor.chain().focus().redo().run(),
|
|
74
|
+
isActive: (editor) => false,
|
|
75
|
+
},
|
|
76
|
+
Separator: {
|
|
77
|
+
type: 'separator',
|
|
78
|
+
},
|
|
79
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<teleport to="#frappeui-toast-root">
|
|
3
|
+
<transition :name="position.includes('top') ? 'toast-top' : 'toast-bottom'">
|
|
4
|
+
<div
|
|
5
|
+
v-if="shown"
|
|
6
|
+
:style="style"
|
|
7
|
+
:class="[
|
|
8
|
+
'absolute transition duration-200 ease-out m-4 pointer-events-auto',
|
|
9
|
+
position.includes('center') ? '-translate-x-1/2' : '',
|
|
10
|
+
]"
|
|
11
|
+
>
|
|
12
|
+
<div
|
|
13
|
+
class="px-2.5 py-2 bg-white border rounded-lg shadow-md min-w-[15rem]"
|
|
14
|
+
>
|
|
15
|
+
<div class="flex items-center justify-between">
|
|
16
|
+
<div class="text-lg">
|
|
17
|
+
<slot> Toast Content </slot>
|
|
18
|
+
</div>
|
|
19
|
+
<div>
|
|
20
|
+
<slot name="actions">
|
|
21
|
+
<Button icon="x" @click="shown = false" />
|
|
22
|
+
</slot>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</transition>
|
|
28
|
+
</teleport>
|
|
29
|
+
</template>
|
|
30
|
+
<script>
|
|
31
|
+
const positions = [
|
|
32
|
+
'top-right',
|
|
33
|
+
'top-center',
|
|
34
|
+
'top-left',
|
|
35
|
+
'bottom-right',
|
|
36
|
+
'bottom-center',
|
|
37
|
+
'bottom-left',
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
name: 'Toast',
|
|
42
|
+
props: {
|
|
43
|
+
position: {
|
|
44
|
+
type: String,
|
|
45
|
+
default: 'top-right',
|
|
46
|
+
},
|
|
47
|
+
text: {
|
|
48
|
+
type: String,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
created() {
|
|
52
|
+
if (!document.getElementById('frappeui-toast-root')) {
|
|
53
|
+
const root = document.createElement('div')
|
|
54
|
+
root.id = 'frappeui-toast-root'
|
|
55
|
+
root.style.position = 'fixed'
|
|
56
|
+
root.style.top = '16px'
|
|
57
|
+
root.style.right = '16px'
|
|
58
|
+
root.style.bottom = '16px'
|
|
59
|
+
root.style.left = '16px'
|
|
60
|
+
root.style.zIndex = '9999'
|
|
61
|
+
root.style.pointerEvents = 'none'
|
|
62
|
+
document.body.appendChild(root)
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
mounted() {
|
|
66
|
+
this.shown = true
|
|
67
|
+
// setTimeout(() => {
|
|
68
|
+
// this.shown = false
|
|
69
|
+
// }, 3000)
|
|
70
|
+
},
|
|
71
|
+
data() {
|
|
72
|
+
return {
|
|
73
|
+
shown: false,
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
computed: {
|
|
77
|
+
style() {
|
|
78
|
+
let style = {}
|
|
79
|
+
if (this.position.includes('top')) {
|
|
80
|
+
style.top = 0
|
|
81
|
+
}
|
|
82
|
+
if (this.position.includes('bottom')) {
|
|
83
|
+
style.bottom = 0
|
|
84
|
+
}
|
|
85
|
+
if (this.position.includes('right')) {
|
|
86
|
+
style.right = 0
|
|
87
|
+
}
|
|
88
|
+
if (this.position.includes('left')) {
|
|
89
|
+
style.left = 0
|
|
90
|
+
}
|
|
91
|
+
if (this.position.includes('center')) {
|
|
92
|
+
style.left = '50%'
|
|
93
|
+
// style.transform = 'translateX(-50%)'
|
|
94
|
+
}
|
|
95
|
+
return style
|
|
96
|
+
},
|
|
97
|
+
transitionProps() {
|
|
98
|
+
let props = {
|
|
99
|
+
enterActiveClass: 'transition duration-200 ease-out',
|
|
100
|
+
enterFromClass: 'opacity-0',
|
|
101
|
+
enterToClass: 'translate-y-0 opacity-100',
|
|
102
|
+
leaveActiveClass: 'transition duration-100 ease-in',
|
|
103
|
+
leaveFromClass: 'scale-100 translate-y-0 opacity-100',
|
|
104
|
+
leaveToClass: 'scale-75 translate-y-4 opacity-0',
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (this.position.includes('top')) {
|
|
108
|
+
props.enterFromClass += ' -translate-y-12'
|
|
109
|
+
}
|
|
110
|
+
if (this.position.includes('bottom')) {
|
|
111
|
+
props.enterFromClass += ' translate-y-12'
|
|
112
|
+
}
|
|
113
|
+
return props
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
</script>
|
|
118
|
+
<style>
|
|
119
|
+
.toast-top-enter-active,
|
|
120
|
+
.toast-bottom-enter-active {
|
|
121
|
+
transition: all 200ms ease-out;
|
|
122
|
+
}
|
|
123
|
+
.toast-top-leave-active,
|
|
124
|
+
.toast-bottom-leave-active {
|
|
125
|
+
transition: all 100ms ease-in;
|
|
126
|
+
}
|
|
127
|
+
.toast-top-enter-from {
|
|
128
|
+
opacity: 0;
|
|
129
|
+
transform: translateY(0);
|
|
130
|
+
}
|
|
131
|
+
.toast-top-enter-to {
|
|
132
|
+
opacity: 1;
|
|
133
|
+
transform: translateY(0);
|
|
134
|
+
}
|
|
135
|
+
</style>
|
package/src/utils/resources.js
CHANGED
|
@@ -167,61 +167,74 @@ export function createDocumentResource(options, vm) {
|
|
|
167
167
|
doctype: options.doctype,
|
|
168
168
|
name: options.name,
|
|
169
169
|
doc: null,
|
|
170
|
-
get: createResource(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
get: createResource(
|
|
171
|
+
{
|
|
172
|
+
method: 'frappe.client.get',
|
|
173
|
+
makeParams() {
|
|
174
|
+
return {
|
|
175
|
+
doctype: out.doctype,
|
|
176
|
+
name: out.name,
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
onSuccess(data) {
|
|
180
|
+
out.doc = postprocess(data)
|
|
181
|
+
},
|
|
177
182
|
},
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
vm
|
|
184
|
+
),
|
|
185
|
+
setValue: createResource(setValueOptions, vm),
|
|
186
|
+
setValueDebounced: createResource(
|
|
187
|
+
{
|
|
188
|
+
...setValueOptions,
|
|
189
|
+
debounce: options.debounce || 500,
|
|
180
190
|
},
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
onSuccess() {
|
|
196
|
-
out.doc = null
|
|
191
|
+
vm
|
|
192
|
+
),
|
|
193
|
+
delete: createResource(
|
|
194
|
+
{
|
|
195
|
+
method: 'frappe.client.delete',
|
|
196
|
+
makeParams() {
|
|
197
|
+
return {
|
|
198
|
+
doctype: out.doctype,
|
|
199
|
+
name: out.name,
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
onSuccess() {
|
|
203
|
+
out.doc = null
|
|
204
|
+
},
|
|
197
205
|
},
|
|
198
|
-
|
|
206
|
+
vm
|
|
207
|
+
),
|
|
199
208
|
update,
|
|
209
|
+
reload,
|
|
200
210
|
})
|
|
201
211
|
|
|
202
212
|
for (let method in options.whitelistedMethods) {
|
|
203
213
|
let methodName = options.whitelistedMethods[method]
|
|
204
|
-
out[method] = createResource(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
out.doc
|
|
219
|
-
|
|
214
|
+
out[method] = createResource(
|
|
215
|
+
{
|
|
216
|
+
method: 'run_doc_method',
|
|
217
|
+
makeParams(values) {
|
|
218
|
+
return {
|
|
219
|
+
dt: out.doctype,
|
|
220
|
+
dn: out.name,
|
|
221
|
+
method: methodName,
|
|
222
|
+
args: JSON.stringify(values),
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
onSuccess(data) {
|
|
226
|
+
if (data.docs) {
|
|
227
|
+
for (let doc of data.docs) {
|
|
228
|
+
if (doc.doctype === out.doctype && doc.name === out.name) {
|
|
229
|
+
out.doc = postprocess(doc)
|
|
230
|
+
break
|
|
231
|
+
}
|
|
220
232
|
}
|
|
221
233
|
}
|
|
222
|
-
}
|
|
234
|
+
},
|
|
223
235
|
},
|
|
224
|
-
|
|
236
|
+
vm
|
|
237
|
+
)
|
|
225
238
|
}
|
|
226
239
|
|
|
227
240
|
function update(updatedOptions) {
|
|
@@ -230,9 +243,13 @@ export function createDocumentResource(options, vm) {
|
|
|
230
243
|
out.get.fetch()
|
|
231
244
|
}
|
|
232
245
|
|
|
246
|
+
function reload() {
|
|
247
|
+
out.get.fetch()
|
|
248
|
+
}
|
|
249
|
+
|
|
233
250
|
function postprocess(doc) {
|
|
234
251
|
if (options.postprocess) {
|
|
235
|
-
let returnValue = options.postprocess(doc)
|
|
252
|
+
let returnValue = options.postprocess.call(vm, doc)
|
|
236
253
|
if (typeof returnValue === 'object') {
|
|
237
254
|
return returnValue
|
|
238
255
|
}
|
|
@@ -247,10 +264,79 @@ export function createDocumentResource(options, vm) {
|
|
|
247
264
|
return out
|
|
248
265
|
}
|
|
249
266
|
|
|
267
|
+
function createListResource(options, vm, getResource) {
|
|
268
|
+
if (!options.doctype) return
|
|
269
|
+
|
|
270
|
+
let out = reactive({
|
|
271
|
+
doctype: options.doctype,
|
|
272
|
+
fields: options.fields,
|
|
273
|
+
filters: options.filters,
|
|
274
|
+
order_by: options.order_by,
|
|
275
|
+
start: options.start,
|
|
276
|
+
limit: options.limit,
|
|
277
|
+
data: null,
|
|
278
|
+
list: createResource(
|
|
279
|
+
{
|
|
280
|
+
method: 'frappe.client.get_list',
|
|
281
|
+
makeParams() {
|
|
282
|
+
return {
|
|
283
|
+
doctype: out.doctype,
|
|
284
|
+
fields: out.fields,
|
|
285
|
+
filters: out.filters,
|
|
286
|
+
order_by: out.order_by,
|
|
287
|
+
limit_start: out.start,
|
|
288
|
+
limit_page_length: out.limit,
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
onSuccess(data) {
|
|
292
|
+
out.data = data
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
vm
|
|
296
|
+
),
|
|
297
|
+
insert: createResource(
|
|
298
|
+
{
|
|
299
|
+
method: 'frappe.client.insert',
|
|
300
|
+
makeParams(values) {
|
|
301
|
+
return {
|
|
302
|
+
doc: {
|
|
303
|
+
doctype: out.doctype,
|
|
304
|
+
...values,
|
|
305
|
+
},
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
onSuccess() {
|
|
309
|
+
out.list.fetch()
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
vm
|
|
313
|
+
),
|
|
314
|
+
update,
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
function update(updatedOptions) {
|
|
318
|
+
out.doctype = updatedOptions.doctype
|
|
319
|
+
out.fields = updatedOptions.fields
|
|
320
|
+
out.filters = updatedOptions.filters
|
|
321
|
+
out.order_by = updatedOptions.order_by
|
|
322
|
+
out.start = updatedOptions.start
|
|
323
|
+
out.limit = updatedOptions.limit
|
|
324
|
+
out.list.fetch()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// fetch list
|
|
328
|
+
out.list.fetch()
|
|
329
|
+
|
|
330
|
+
return out
|
|
331
|
+
}
|
|
332
|
+
|
|
250
333
|
function createResourceForOptions(options, vm, getResource) {
|
|
251
334
|
if (options.type === 'document') {
|
|
252
335
|
return createDocumentResource(options, vm, getResource)
|
|
253
336
|
}
|
|
337
|
+
if (options.type === 'list') {
|
|
338
|
+
return createListResource(options, vm, getResource)
|
|
339
|
+
}
|
|
254
340
|
return createResource(options, vm, getResource)
|
|
255
341
|
}
|
|
256
342
|
|
|
@@ -270,8 +356,19 @@ let createMixin = (mixinOptions) => ({
|
|
|
270
356
|
|
|
271
357
|
if (typeof options == 'function') {
|
|
272
358
|
watch(
|
|
273
|
-
() =>
|
|
359
|
+
() => {
|
|
360
|
+
try {
|
|
361
|
+
return options.call(this)
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.warn('Failed to get resource options\n\n', error)
|
|
364
|
+
return null
|
|
365
|
+
}
|
|
366
|
+
},
|
|
274
367
|
(updatedOptions, oldVal) => {
|
|
368
|
+
if (!updatedOptions) {
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
|
|
275
372
|
let changed =
|
|
276
373
|
!oldVal ||
|
|
277
374
|
JSON.stringify(updatedOptions) !== JSON.stringify(oldVal)
|