frappe-ui 0.0.31 → 0.0.36

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 (37) hide show
  1. package/package.json +2 -1
  2. package/src/components/Autocomplete.vue +121 -0
  3. package/src/components/FeatherIcon.vue +7 -3
  4. package/src/components/Popover.vue +2 -1
  5. package/src/components/TextEditor/Menu.vue +35 -3
  6. package/src/components/TextEditor/TextEditor.vue +41 -8
  7. package/src/components/TextEditor/commands.js +91 -11
  8. package/src/components/TextEditor/icons/align-center.vue +14 -0
  9. package/src/components/TextEditor/icons/align-justify.vue +14 -0
  10. package/src/components/TextEditor/icons/align-left.vue +14 -0
  11. package/src/components/TextEditor/icons/align-right.vue +14 -0
  12. package/src/components/TextEditor/icons/arrow-go-back-line.vue +14 -0
  13. package/src/components/TextEditor/icons/arrow-go-forward-line.vue +14 -0
  14. package/src/components/TextEditor/icons/bold.vue +14 -0
  15. package/src/components/TextEditor/icons/code-view.vue +14 -0
  16. package/src/components/TextEditor/icons/double-quotes-r.vue +14 -0
  17. package/src/components/TextEditor/icons/font-color.vue +14 -0
  18. package/src/components/TextEditor/icons/format-clear.vue +14 -0
  19. package/src/components/TextEditor/icons/h-1.vue +14 -0
  20. package/src/components/TextEditor/icons/h-2.vue +14 -0
  21. package/src/components/TextEditor/icons/h-3.vue +14 -0
  22. package/src/components/TextEditor/icons/h-4.vue +14 -0
  23. package/src/components/TextEditor/icons/h-5.vue +14 -0
  24. package/src/components/TextEditor/icons/h-6.vue +14 -0
  25. package/src/components/TextEditor/icons/italic.vue +14 -0
  26. package/src/components/TextEditor/icons/link.vue +14 -0
  27. package/src/components/TextEditor/icons/list-ordered.vue +14 -0
  28. package/src/components/TextEditor/icons/list-unordered.vue +14 -0
  29. package/src/components/TextEditor/icons/readme.md +1 -0
  30. package/src/components/TextEditor/icons/separator.vue +14 -0
  31. package/src/components/TextEditor/icons/strikethrough.vue +14 -0
  32. package/src/components/TextEditor/icons/table-2.vue +14 -0
  33. package/src/components/TextEditor/icons/text.vue +11 -0
  34. package/src/components/TextEditor/icons/underline.vue +14 -0
  35. package/src/index.js +1 -0
  36. package/src/utils/pageMeta.js +4 -1
  37. package/src/utils/resources.js +16 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frappe-ui",
3
- "version": "0.0.31",
3
+ "version": "0.0.36",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
@@ -24,6 +24,7 @@
24
24
  "@tiptap/extension-image": "^2.0.0-beta.27",
25
25
  "@tiptap/extension-link": "^2.0.0-beta.37",
26
26
  "@tiptap/extension-placeholder": "^2.0.0-beta.48",
27
+ "@tiptap/extension-text-align": "^2.0.0-beta.31",
27
28
  "@tiptap/starter-kit": "^2.0.0-beta.183",
28
29
  "@tiptap/vue-3": "^2.0.0-beta.90",
29
30
  "autoprefixer": "^10.4.2",
@@ -0,0 +1,121 @@
1
+ <template>
2
+ <Combobox v-model="selectedValue" v-slot="{ open: isComboBoxOpen }" nullable>
3
+ <Popover class="w-full">
4
+ <template #target="{ open: openPopover }">
5
+ <ComboboxInput
6
+ :displayValue="displayValue"
7
+ :class="['w-full form-input', { 'rounded-b-none': isComboBoxOpen }]"
8
+ type="text"
9
+ @change="
10
+ (e) => {
11
+ query = e.target.value
12
+ openPopover()
13
+ }
14
+ "
15
+ @focus="
16
+ () => {
17
+ openPopover()
18
+ showCombobox = true
19
+ }
20
+ "
21
+ @keydown.enter="showCombobox = false"
22
+ autocomplete="off"
23
+ v-bind="$attrs"
24
+ />
25
+ </template>
26
+ <template #body>
27
+ <ComboboxOptions
28
+ :class="[
29
+ 'p-1.5 bg-white rounded-md shadow-md rounded-t-none',
30
+ { hidden: !showCombobox },
31
+ ]"
32
+ :static="true"
33
+ >
34
+ <ComboboxOption
35
+ as="template"
36
+ v-for="option in filteredOptions"
37
+ :key="option.value"
38
+ :value="option"
39
+ v-slot="{ active, selected }"
40
+ >
41
+ <li
42
+ :class="[
43
+ 'px-2.5 py-1.5 rounded-md text-base',
44
+ { 'bg-gray-100': active },
45
+ ]"
46
+ >
47
+ {{ option.label }}
48
+ </li>
49
+ </ComboboxOption>
50
+ <li
51
+ v-if="filteredOptions.length == 0"
52
+ class="px-2.5 py-1.5 rounded-md text-base text-gray-600"
53
+ >
54
+ No results found
55
+ </li>
56
+ </ComboboxOptions>
57
+ </template>
58
+ </Popover>
59
+ </Combobox>
60
+ </template>
61
+ <script>
62
+ import {
63
+ Combobox,
64
+ ComboboxInput,
65
+ ComboboxOptions,
66
+ ComboboxOption,
67
+ } from '@headlessui/vue'
68
+ import Popover from './Popover.vue'
69
+
70
+ export default {
71
+ name: 'Autocomplete',
72
+ inheritAttrs: false,
73
+ props: ['modelValue', 'options', 'value'],
74
+ emits: ['update:modelValue', 'change'],
75
+ components: {
76
+ Popover,
77
+ Combobox,
78
+ ComboboxInput,
79
+ ComboboxOptions,
80
+ ComboboxOption,
81
+ },
82
+ data() {
83
+ return {
84
+ showCombobox: false,
85
+ query: '',
86
+ }
87
+ },
88
+ computed: {
89
+ valuePropPassed() {
90
+ return 'value' in this.$props && this.$props.value !== undefined
91
+ },
92
+ selectedValue: {
93
+ get() {
94
+ return this.valuePropPassed ? this.value : this.modelValue
95
+ },
96
+ set(val) {
97
+ this.$emit(this.valuePropPassed ? 'change' : 'update:modelValue', val)
98
+ },
99
+ },
100
+ filteredOptions() {
101
+ if (!this.query) {
102
+ return this.options
103
+ }
104
+ return this.options.filter((option) => {
105
+ let searchTexts = [option.label, option.value]
106
+ return searchTexts.some((text) =>
107
+ (text || '').toLowerCase().includes(this.query.toLowerCase())
108
+ )
109
+ })
110
+ },
111
+ },
112
+ methods: {
113
+ displayValue(option) {
114
+ if (typeof option === 'string') {
115
+ return option
116
+ }
117
+ return option?.label
118
+ },
119
+ },
120
+ }
121
+ </script>
@@ -12,10 +12,11 @@ export default {
12
12
  validator(value) {
13
13
  const valid = validIcons.includes(value)
14
14
  if (!valid) {
15
- console.warn(
16
- `name property for feather-icon must be one of `,
17
- validIcons
15
+ console.groupCollapsed(
16
+ '[frappe-ui] name property for feather-icon must be one of '
18
17
  )
18
+ console.dir(validIcons)
19
+ console.groupEnd()
19
20
  }
20
21
  return valid
21
22
  },
@@ -31,6 +32,9 @@ export default {
31
32
  },
32
33
  render() {
33
34
  let icon = feather.icons[this.name]
35
+ if (!icon) {
36
+ icon = feather.icons['circle']
37
+ }
34
38
  return h(
35
39
  'svg',
36
40
  mergeProps(
@@ -2,7 +2,7 @@
2
2
  <div ref="reference">
3
3
  <div
4
4
  ref="target"
5
- class="inline-block"
5
+ :class="['inline-block', $attrs.class]"
6
6
  @click="updatePosition"
7
7
  @focusin="updatePosition"
8
8
  @keydown="updatePosition"
@@ -53,6 +53,7 @@ import { createPopper } from '@popperjs/core'
53
53
 
54
54
  export default {
55
55
  name: 'Popover',
56
+ inheritAttrs: false,
56
57
  props: {
57
58
  show: {
58
59
  default: undefined,
@@ -6,6 +6,37 @@
6
6
  class="border-l w-[2px] h-4"
7
7
  v-if="button.type === 'separator'"
8
8
  ></div>
9
+ <div v-else-if="button.map">
10
+ <Popover>
11
+ <template #target="{ togglePopover }">
12
+ <button
13
+ class="px-2 py-1 text-base font-medium text-gray-800 transition-colors rounded hover:bg-gray-100"
14
+ @click="togglePopover"
15
+ >
16
+ {{
17
+ (button.find((b) => b.isActive(editor)) || button[0]).label
18
+ }}
19
+ </button>
20
+ </template>
21
+ <template #body="{ close }">
22
+ <ul class="p-1 bg-white border rounded shadow-md">
23
+ <li class="w-full" v-for="option in button">
24
+ <button
25
+ class="w-full px-2 py-1 text-base rounded hover:bg-gray-50"
26
+ @click="
27
+ () => {
28
+ onClick(option)
29
+ close()
30
+ }
31
+ "
32
+ >
33
+ {{ option.label }}
34
+ </button>
35
+ </li>
36
+ </ul>
37
+ </template>
38
+ </Popover>
39
+ </div>
9
40
  <button
10
41
  v-else
11
42
  class="flex p-1 text-gray-800 transition-colors rounded"
@@ -13,7 +44,7 @@
13
44
  @click="onClick(button)"
14
45
  :title="button.label"
15
46
  >
16
- <FeatherIcon v-if="button.icon" :name="button.icon" class="w-4" />
47
+ <component v-if="button.icon" :is="button.icon" class="w-4 h-4" />
17
48
  <span class="inline-block h-4 text-sm leading-4 min-w-[1rem]" v-else>
18
49
  {{ button.text }}
19
50
  </span>
@@ -39,11 +70,12 @@
39
70
  </div>
40
71
  </template>
41
72
  <script>
42
- import { FeatherIcon } from 'frappe-ui'
73
+ import { Popover } from 'frappe-ui'
74
+
43
75
  export default {
44
76
  name: 'TipTapMenu',
45
77
  props: ['editor', 'buttons'],
46
- components: { FeatherIcon },
78
+ components: { Popover },
47
79
  data() {
48
80
  return {
49
81
  setLinkDialog: { url: '', show: false },
@@ -34,7 +34,7 @@
34
34
  @click="() => button.action(editor)"
35
35
  :title="button.label"
36
36
  >
37
- <FeatherIcon v-if="button.icon" :name="button.icon" class="w-4" />
37
+ <component v-if="button.icon" :is="button.icon" class="w-4 h-4" />
38
38
  <span class="inline-block h-4 text-sm leading-4 min-w-[1rem]" v-else>
39
39
  {{ button.text }}
40
40
  </span>
@@ -48,10 +48,12 @@
48
48
  import { Editor, EditorContent, BubbleMenu, FloatingMenu } from '@tiptap/vue-3'
49
49
  import StarterKit from '@tiptap/starter-kit'
50
50
  import Placeholder from '@tiptap/extension-placeholder'
51
+ import TextAlign from '@tiptap/extension-text-align'
51
52
  import Image from '@tiptap/extension-image'
52
53
  import Link from '@tiptap/extension-link'
53
54
  import Menu from './Menu.vue'
54
55
  import commands from './commands'
56
+ import { normalizeClass } from 'vue'
55
57
 
56
58
  export default {
57
59
  name: 'TextEditor',
@@ -90,20 +92,27 @@ export default {
90
92
  editable(value) {
91
93
  this.editor.setEditable(value)
92
94
  },
95
+ editorProps: {
96
+ deep: true,
97
+ handler(value) {
98
+ this.editor.setOptions({
99
+ editorProps: value,
100
+ })
101
+ },
102
+ },
93
103
  },
94
104
  mounted() {
95
105
  this.editor = new Editor({
96
106
  content: this.content || null,
97
- editorProps: {
98
- attributes: {
99
- class: ['prose prose-sm prose-p:my-1', this.editorClass].join(' '),
100
- },
101
- },
107
+ editorProps: this.editorProps,
102
108
  editable: this.editable,
103
109
  extensions: [
104
110
  StarterKit.configure({
105
111
  ...this.starterkitOptions,
106
112
  }),
113
+ TextAlign.configure({
114
+ types: ['heading', 'paragraph'],
115
+ }),
107
116
  Image,
108
117
  Link,
109
118
  Placeholder.configure({
@@ -128,15 +137,26 @@ export default {
128
137
  buttons = this.fixedMenu
129
138
  } else {
130
139
  buttons = [
140
+ [
141
+ 'Heading 1',
142
+ 'Heading 2',
143
+ 'Heading 3',
144
+ 'Heading 4',
145
+ 'Heading 5',
146
+ 'Heading 6',
147
+ ],
131
148
  'Paragraph',
132
- 'Heading 2',
133
- 'Heading 3',
134
149
  'Separator',
135
150
  'Bold',
136
151
  'Italic',
137
152
  'Separator',
138
153
  'Bullet List',
139
154
  'Numbered List',
155
+ 'Separator',
156
+ 'Align Left',
157
+ 'Align Center',
158
+ 'Align Right',
159
+ 'Separator',
140
160
  'Blockquote',
141
161
  'Code',
142
162
  'Horizontal Rule',
@@ -191,10 +211,23 @@ export default {
191
211
  }
192
212
  return buttons.map(createEditorButton)
193
213
  },
214
+ editorProps() {
215
+ return {
216
+ attributes: {
217
+ class: normalizeClass([
218
+ 'prose prose-sm prose-p:my-1',
219
+ this.editorClass,
220
+ ]),
221
+ },
222
+ }
223
+ },
194
224
  },
195
225
  }
196
226
 
197
227
  function createEditorButton(option) {
228
+ if (option instanceof Array) {
229
+ return option.map(createEditorButton)
230
+ }
198
231
  if (typeof option == 'object') {
199
232
  return option
200
233
  }
@@ -1,13 +1,44 @@
1
+ import H1 from './icons/h-1.vue'
2
+ import H2 from './icons/h-2.vue'
3
+ import H3 from './icons/h-3.vue'
4
+ import H4 from './icons/h-4.vue'
5
+ import H5 from './icons/h-5.vue'
6
+ import H6 from './icons/h-6.vue'
7
+ import Text from './icons/text.vue'
8
+ import Bold from './icons/bold.vue'
9
+ import Italic from './icons/italic.vue'
10
+ import Underline from './icons/underline.vue'
11
+ import AlignCenter from './icons/align-center.vue'
12
+ import AlignLeft from './icons/align-left.vue'
13
+ import AlignRight from './icons/align-right.vue'
14
+ import ListOrdered from './icons/list-ordered.vue'
15
+ import ListUnordered from './icons/list-unordered.vue'
16
+ import DoubleQuotes from './icons/double-quotes-r.vue'
17
+ import CodeView from './icons/code-view.vue'
18
+ import Link from './icons/link.vue'
19
+ import ArrowGoBack from './icons/arrow-go-back-line.vue'
20
+ import ArrowGoForward from './icons/arrow-go-forward-line.vue'
21
+ import Separator from './icons/separator.vue'
22
+
1
23
  export default {
2
24
  Paragraph: {
3
25
  label: 'Paragraph',
4
- icon: 'type',
26
+ icon: Text,
5
27
  action: (editor) => editor.chain().focus().setParagraph().run(),
6
28
  isActive: (editor) => editor.isActive('paragraph'),
7
29
  },
30
+ 'Heading 1': {
31
+ label: 'Heading 1',
32
+ text: 'H1',
33
+ icon: H1,
34
+ action: (editor) =>
35
+ editor.chain().focus().toggleHeading({ level: 1 }).run(),
36
+ isActive: (editor) => editor.isActive('heading', { level: 1 }),
37
+ },
8
38
  'Heading 2': {
9
39
  label: 'Heading 2',
10
40
  text: 'H2',
41
+ icon: H2,
11
42
  action: (editor) =>
12
43
  editor.chain().focus().toggleHeading({ level: 2 }).run(),
13
44
  isActive: (editor) => editor.isActive('heading', { level: 2 }),
@@ -15,66 +46,115 @@ export default {
15
46
  'Heading 3': {
16
47
  label: 'Heading 3',
17
48
  text: 'H3',
49
+ icon: H3,
18
50
  action: (editor) =>
19
51
  editor.chain().focus().toggleHeading({ level: 3 }).run(),
20
52
  isActive: (editor) => editor.isActive('heading', { level: 3 }),
21
53
  },
54
+ 'Heading 4': {
55
+ label: 'Heading 4',
56
+ text: 'H4',
57
+ icon: H4,
58
+ action: (editor) =>
59
+ editor.chain().focus().toggleHeading({ level: 4 }).run(),
60
+ isActive: (editor) => editor.isActive('heading', { level: 4 }),
61
+ },
62
+ 'Heading 5': {
63
+ label: 'Heading 5',
64
+ text: 'H5',
65
+ icon: H5,
66
+ action: (editor) =>
67
+ editor.chain().focus().toggleHeading({ level: 5 }).run(),
68
+ isActive: (editor) => editor.isActive('heading', { level: 5 }),
69
+ },
70
+ 'Heading 6': {
71
+ label: 'Heading 6',
72
+ text: 'H6',
73
+ icon: H6,
74
+ action: (editor) =>
75
+ editor.chain().focus().toggleHeading({ level: 6 }).run(),
76
+ isActive: (editor) => editor.isActive('heading', { level: 6 }),
77
+ },
22
78
  Bold: {
23
79
  label: 'Bold',
24
- icon: 'bold',
80
+ icon: Bold,
25
81
  action: (editor) => editor.chain().focus().toggleBold().run(),
26
82
  isActive: (editor) => editor.isActive('bold'),
27
83
  },
28
84
  Italic: {
29
85
  label: 'Italic',
30
- icon: 'italic',
86
+ icon: Italic,
31
87
  action: (editor) => editor.chain().focus().toggleItalic().run(),
32
88
  isActive: (editor) => editor.isActive('italic'),
33
89
  },
90
+ Underline: {
91
+ label: 'Underline',
92
+ icon: Underline,
93
+ action: (editor) => editor.chain().focus().toggleUnderline().run(),
94
+ isActive: (editor) => editor.isActive('underline'),
95
+ },
34
96
  'Bullet List': {
35
97
  label: 'Bullet List',
36
- icon: 'list',
98
+ icon: ListOrdered,
37
99
  action: (editor) => editor.chain().focus().toggleBulletList().run(),
38
100
  isActive: (editor) => editor.isActive('bulletList'),
39
101
  },
40
102
  'Numbered List': {
41
103
  label: 'Numbered List',
42
- text: '1.',
104
+ icon: ListUnordered,
43
105
  action: (editor) => editor.chain().focus().toggleOrderedList().run(),
44
106
  isActive: (editor) => editor.isActive('orderedList'),
45
107
  },
108
+ 'Align Center': {
109
+ label: 'Align Center',
110
+ icon: AlignCenter,
111
+ action: (editor) => editor.chain().focus().setTextAlign('center').run(),
112
+ isActive: (editor) => editor.isActive({ textAlign: 'center' }),
113
+ },
114
+ 'Align Left': {
115
+ label: 'Align Left',
116
+ icon: AlignLeft,
117
+ action: (editor) => editor.chain().focus().setTextAlign('left').run(),
118
+ isActive: (editor) => editor.isActive({ textAlign: 'left' }),
119
+ },
120
+ 'Align Right': {
121
+ label: 'Align Right',
122
+ icon: AlignRight,
123
+ action: (editor) => editor.chain().focus().setTextAlign('right').run(),
124
+ isActive: (editor) => editor.isActive({ textAlign: 'right' }),
125
+ },
46
126
  Blockquote: {
47
127
  label: 'Blockquote',
48
- icon: 'chevron-right',
128
+ icon: DoubleQuotes,
49
129
  action: (editor) => editor.chain().focus().toggleBlockquote().run(),
50
130
  isActive: (editor) => editor.isActive('blockquote'),
51
131
  },
52
132
  Code: {
53
133
  label: 'Code',
54
- icon: 'code',
134
+ icon: CodeView,
55
135
  action: (editor) => editor.chain().focus().toggleCodeBlock().run(),
56
136
  isActive: (editor) => editor.isActive('codeBlock'),
57
137
  },
58
138
  'Horizontal Rule': {
59
139
  label: 'Horizontal Rule',
60
- icon: 'minus',
140
+ icon: Separator,
61
141
  action: (editor) => editor.chain().focus().setHorizontalRule().run(),
62
142
  isActive: (editor) => false,
63
143
  },
64
144
  Link: {
65
145
  label: 'Link',
66
- icon: 'link',
146
+ icon: Link,
67
147
  isActive: (editor) => editor.isActive('link'),
68
148
  },
69
149
  Undo: {
70
150
  label: 'Undo',
71
- icon: 'corner-up-left',
151
+ icon: ArrowGoBack,
72
152
  action: (editor) => editor.chain().focus().undo().run(),
73
153
  isActive: (editor) => false,
74
154
  },
75
155
  Redo: {
76
156
  label: 'Redo',
77
- icon: 'corner-up-right',
157
+ icon: ArrowGoForward,
78
158
  action: (editor) => editor.chain().focus().redo().run(),
79
159
  isActive: (editor) => false,
80
160
  },
@@ -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="M3 4h18v2H3V4zm2 15h14v2H5v-2zm-2-5h18v2H3v-2zm2-5h14v2H5V9z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M3 4h18v2H3V4zm0 15h18v2H3v-2zm0-5h18v2H3v-2zm0-5h18v2H3V9z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M3 4h18v2H3V4zm0 15h14v2H3v-2zm0-5h18v2H3v-2zm0-5h14v2H3V9z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M3 4h18v2H3V4zm4 15h14v2H7v-2zm-4-5h18v2H3v-2zm4-5h14v2H7V9z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M5.828 7l2.536 2.536L6.95 10.95 2 6l4.95-4.95 1.414 1.414L5.828 5H13a8 8 0 1 1 0 16H4v-2h9a6 6 0 1 0 0-12H5.828z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M18.172 7H11a6 6 0 1 0 0 12h9v2h-9a8 8 0 1 1 0-16h7.172l-2.536-2.536L17.05 1.05 22 6l-4.95 4.95-1.414-1.414L18.172 7z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M8 11h4.5a2.5 2.5 0 1 0 0-5H8v5zm10 4.5a4.5 4.5 0 0 1-4.5 4.5H6V4h6.5a4.5 4.5 0 0 1 3.256 7.606A4.498 4.498 0 0 1 18 15.5zM8 13v5h5.5a2.5 2.5 0 1 0 0-5H8z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M16.95 8.464l1.414-1.414 4.95 4.95-4.95 4.95-1.414-1.414L20.485 12 16.95 8.464zm-9.9 0L3.515 12l3.535 3.536-1.414 1.414L.686 12l4.95-4.95L7.05 8.464z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M19.417 6.679C20.447 7.773 21 9 21 10.989c0 3.5-2.457 6.637-6.03 8.188l-.893-1.378c3.335-1.804 3.987-4.145 4.247-5.621-.537.278-1.24.375-1.929.311-1.804-.167-3.226-1.648-3.226-3.489a3.5 3.5 0 0 1 3.5-3.5c1.073 0 2.099.49 2.748 1.179zm-10 0C10.447 7.773 11 9 11 10.989c0 3.5-2.457 6.637-6.03 8.188l-.893-1.378c3.335-1.804 3.987-4.145 4.247-5.621-.537.278-1.24.375-1.929.311C4.591 12.322 3.17 10.841 3.17 9a3.5 3.5 0 0 1 3.5-3.5c1.073 0 2.099.49 2.748 1.179z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M15.246 14H8.754l-1.6 4H5l6-15h2l6 15h-2.154l-1.6-4zm-.8-2L12 5.885 9.554 12h4.892zM3 20h18v2H3v-2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M12.651 14.065L11.605 20H9.574l1.35-7.661-7.41-7.41L4.93 3.515 20.485 19.07l-1.414 1.414-6.42-6.42zm-.878-6.535l.27-1.53h-1.8l-2-2H20v2h-5.927L13.5 9.257 11.773 7.53z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M13 20h-2v-7H4v7H2V4h2v7h7V4h2v16zm8-12v12h-2v-9.796l-2 .536V8.67L19.5 8H21z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M4 4v7h7V4h2v16h-2v-7H4v7H2V4h2zm14.5 4c2.071 0 3.75 1.679 3.75 3.75 0 .857-.288 1.648-.772 2.28l-.148.18L18.034 18H22v2h-7v-1.556l4.82-5.546c.268-.307.43-.709.43-1.148 0-.966-.784-1.75-1.75-1.75-.918 0-1.671.707-1.744 1.606l-.006.144h-2C14.75 9.679 16.429 8 18.5 8z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M22 8l-.002 2-2.505 2.883c1.59.435 2.757 1.89 2.757 3.617 0 2.071-1.679 3.75-3.75 3.75-1.826 0-3.347-1.305-3.682-3.033l1.964-.382c.156.806.866 1.415 1.718 1.415.966 0 1.75-.784 1.75-1.75s-.784-1.75-1.75-1.75c-.286 0-.556.069-.794.19l-1.307-1.547L19.35 10H15V8h7zM4 4v7h7V4h2v16h-2v-7H4v7H2V4h2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M13 20h-2v-7H4v7H2V4h2v7h7V4h2v16zm9-12v8h1.5v2H22v2h-2v-2h-5.5v-1.34l5-8.66H22zm-2 3.133L17.19 16H20v-4.867z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M22 8v2h-4.323l-.464 2.636c.33-.089.678-.136 1.037-.136 2.21 0 4 1.79 4 4s-1.79 4-4 4c-1.827 0-3.367-1.224-3.846-2.897l1.923-.551c.24.836 1.01 1.448 1.923 1.448 1.105 0 2-.895 2-2s-.895-2-2-2c-.63 0-1.193.292-1.56.748l-1.81-.904L16 8h6zM4 4v7h7V4h2v16h-2v-7H4v7H2V4h2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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.097 8l-2.598 4.5c2.21 0 4.001 1.79 4.001 4s-1.79 4-4 4-4-1.79-4-4c0-.736.199-1.426.546-2.019L18.788 8h2.309zM4 4v7h7V4h2v16h-2v-7H4v7H2V4h2zm14.5 10.5c-1.105 0-2 .895-2 2s.895 2 2 2 2-.895 2-2-.895-2-2-2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M15 20H7v-2h2.927l2.116-12H9V4h8v2h-2.927l-2.116 12H15z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M18.364 15.536L16.95 14.12l1.414-1.414a5 5 0 1 0-7.071-7.071L9.879 7.05 8.464 5.636 9.88 4.222a7 7 0 0 1 9.9 9.9l-1.415 1.414zm-2.828 2.828l-1.415 1.414a7 7 0 0 1-9.9-9.9l1.415-1.414L7.05 9.88l-1.414 1.414a5 5 0 1 0 7.071 7.071l1.414-1.414 1.415 1.414zm-.708-10.607l1.415 1.415-7.071 7.07-1.415-1.414 7.071-7.07z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M8 4h13v2H8V4zM5 3v3h1v1H3V6h1V4H3V3h2zM3 14v-2.5h2V11H3v-1h3v2.5H4v.5h2v1H3zm2 5.5H3v-1h2V18H3v-1h3v4H3v-1h2v-.5zM8 11h13v2H8v-2zm0 7h13v2H8v-2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M8 4h13v2H8V4zM4.5 6.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm0 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm0 6.9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM8 11h13v2H8v-2zm0 7h13v2H8v-2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -0,0 +1 @@
1
+ Icons from https://remixicon.com/
@@ -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="M2 11h2v2H2v-2zm4 0h12v2H6v-2zm14 0h2v2h-2v-2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M17.154 14c.23.516.346 1.09.346 1.72 0 1.342-.524 2.392-1.571 3.147C14.88 19.622 13.433 20 11.586 20c-1.64 0-3.263-.381-4.87-1.144V16.6c1.52.877 3.075 1.316 4.666 1.316 2.551 0 3.83-.732 3.839-2.197a2.21 2.21 0 0 0-.648-1.603l-.12-.117H3v-2h18v2h-3.846zm-4.078-3H7.629a4.086 4.086 0 0 1-.481-.522C6.716 9.92 6.5 9.246 6.5 8.452c0-1.236.466-2.287 1.397-3.153C8.83 4.433 10.271 4 12.222 4c1.471 0 2.879.328 4.222.984v2.152c-1.2-.687-2.515-1.03-3.946-1.03-2.48 0-3.719.782-3.719 2.346 0 .42.218.786.654 1.099.436.313.974.562 1.613.75.62.18 1.297.414 2.03.699z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -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="M13 10v4h6v-4h-6zm-2 0H5v4h6v-4zm2 9h6v-3h-6v3zm-2 0v-3H5v3h6zm2-14v3h6V5h-6zm-2 0H5v3h6V5zM4 3h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -0,0 +1,11 @@
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 d="M13 6v15h-2V6H5V4h14v2z" fill="currentColor" />
10
+ </svg>
11
+ </template>
@@ -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="M8 3v9a4 4 0 1 0 8 0V3h2v9a6 6 0 1 1-12 0V3h2zM4 20h16v2H4v-2z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // components
2
2
  export { default as Alert } from './components/Alert.vue'
3
+ export { default as Autocomplete } from './components/Autocomplete.vue'
3
4
  export { default as Avatar } from './components/Avatar.vue'
4
5
  export { default as Badge } from './components/Badge.vue'
5
6
  export { default as Button } from './components/Button.vue'
@@ -18,7 +18,10 @@ function createMixin() {
18
18
  try {
19
19
  return this.$options.pageMeta.call(this)
20
20
  } catch (error) {
21
- console.warn('Failed to parse pageMeta\n\n', error)
21
+ let debugInfo = `${this.$options.name} (${
22
+ this.$options.__file || ''
23
+ })`
24
+ console.warn('Failed to parse pageMeta in', debugInfo)
22
25
  return null
23
26
  }
24
27
  },
@@ -58,6 +58,10 @@ export function createResource(options, vm, getResource) {
58
58
  options.onFetch.call(vm, out.params)
59
59
  }
60
60
 
61
+ if (options.beforeSubmit) {
62
+ options.beforeSubmit.call(vm, out.params)
63
+ }
64
+
61
65
  let validateFunction = tempOptions.validate || options.validate
62
66
  let errorFunctions = [options.onError, tempOptions.onError]
63
67
  let successFunctions = [options.onSuccess, tempOptions.onSuccess]
@@ -67,14 +71,14 @@ export function createResource(options, vm, getResource) {
67
71
  try {
68
72
  invalidMessage = await validateFunction.call(vm, out.params)
69
73
  if (invalidMessage && typeof invalidMessage == 'string') {
74
+ out.loading = false
70
75
  let error = new Error(invalidMessage)
71
76
  handleError(error, errorFunctions)
72
- out.loading = false
73
77
  return
74
78
  }
75
79
  } catch (error) {
76
- handleError(error, errorFunctions)
77
80
  out.loading = false
81
+ handleError(error, errorFunctions)
78
82
  return
79
83
  }
80
84
  }
@@ -92,6 +96,7 @@ export function createResource(options, vm, getResource) {
92
96
  handleError(error, errorFunctions)
93
97
  }
94
98
  out.loading = false
99
+ return out.data
95
100
  }
96
101
 
97
102
  function update({ method, params, auto }) {
@@ -117,7 +122,6 @@ export function createResource(options, vm, getResource) {
117
122
  }
118
123
 
119
124
  function handleError(error, errorFunctions) {
120
- console.error(error)
121
125
  if (out.previousData) {
122
126
  out.data = out.previousData
123
127
  }
@@ -127,6 +131,7 @@ export function createResource(options, vm, getResource) {
127
131
  fn.call(vm, error)
128
132
  }
129
133
  }
134
+ throw error
130
135
  }
131
136
 
132
137
  // usage:
@@ -173,11 +178,18 @@ export function createDocumentResource(options, vm) {
173
178
  fieldname: values,
174
179
  }
175
180
  },
181
+ beforeSubmit(params) {
182
+ out.previousDoc = JSON.stringify(out.doc)
183
+ Object.assign(out.doc, params.fieldname || {})
184
+ },
176
185
  onSuccess(data) {
177
186
  out.doc = transform(data)
178
187
  options.setValue?.onSuccess?.call(vm, data)
179
188
  },
180
- onError: options.setValue?.onError,
189
+ onError(error) {
190
+ out.doc = JSON.parse(out.previousDoc)
191
+ options.setValue?.onError?.call(vm, error)
192
+ },
181
193
  }
182
194
 
183
195
  let out = reactive({