frappe-ui 0.1.166 → 0.1.168

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frappe-ui",
3
- "version": "0.1.166",
3
+ "version": "0.1.168",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "main": "./src/index.ts",
6
6
  "type": "module",
@@ -8,7 +8,8 @@
8
8
  "test": "vitest --run",
9
9
  "type-check": "tsc --noEmit",
10
10
  "prettier": "yarn prettier -w ./src",
11
- "bump-and-release": "yarn test && git pull --rebase origin main && yarn version --patch && git push && git push --tags",
11
+ "bump-and-release": "yarn test && git pull --rebase origin main && yarn run release-patch",
12
+ "release-patch": "yarn version --patch && git push && git push --tags",
12
13
  "dev": "vite",
13
14
  "build": "vite build",
14
15
  "preview": "vite preview",
@@ -44,7 +44,12 @@ interface ComboboxProps {
44
44
  }
45
45
 
46
46
  const props = defineProps<ComboboxProps>()
47
- const emit = defineEmits(['update:modelValue', 'update:selectedOption'])
47
+ const emit = defineEmits([
48
+ 'update:modelValue',
49
+ 'update:selectedOption',
50
+ 'focus',
51
+ 'blur',
52
+ ])
48
53
 
49
54
  const searchTerm = ref(getDisplayValue(props.modelValue))
50
55
  const internalModelValue = ref(props.modelValue)
@@ -196,6 +201,14 @@ const handleOpenChange = (open: boolean) => {
196
201
  userHasTyped.value = false
197
202
  }
198
203
  }
204
+
205
+ const handleFocus = (event: FocusEvent) => {
206
+ emit('focus', event)
207
+ }
208
+
209
+ const handleBlur = (event: FocusEvent) => {
210
+ emit('blur', event)
211
+ }
199
212
  </script>
200
213
 
201
214
  <template>
@@ -215,6 +228,8 @@ const handleOpenChange = (open: boolean) => {
215
228
  <ComboboxInput
216
229
  :value="searchTerm"
217
230
  @input="handleInputChange"
231
+ @focus="handleFocus"
232
+ @blur="handleBlur"
218
233
  class="bg-transparent p-0 focus:outline-0 border-0 focus:border-0 focus:ring-0 text-base text-ink-gray-8 h-full placeholder:text-ink-gray-4 w-full"
219
234
  :placeholder="placeholder || ''"
220
235
  :disabled="disabled"
@@ -1,97 +1,14 @@
1
1
  <template>
2
- <slot v-bind="{ onClick: openDialog }"></slot>
3
- <Dialog
4
- :options="{ title: 'Add Video' }"
5
- v-model="addVideoDialog.show"
6
- @after-leave="reset"
7
- >
8
- <template #body-content>
9
- <FileUploader
10
- file-types="video/*"
11
- @success="(file) => (addVideoDialog.url = file.file_url)"
12
- >
13
- <template v-slot="{ file, progress, uploading, openFileSelector }">
14
- <div class="flex items-center space-x-2">
15
- <Button @click="openFileSelector">
16
- {{
17
- uploading
18
- ? `Uploading ${progress}%`
19
- : addVideoDialog.url
20
- ? 'Change Video'
21
- : 'Upload Video'
22
- }}
23
- </Button>
24
- <Button
25
- v-if="addVideoDialog.url"
26
- @click="
27
- () => {
28
- addVideoDialog.url = null
29
- addVideoDialog.file = null
30
- }
31
- "
32
- >
33
- Remove
34
- </Button>
35
- </div>
36
- </template>
37
- </FileUploader>
38
- <video
39
- v-if="addVideoDialog.url"
40
- :src="addVideoDialog.url"
41
- class="mt-2 w-full rounded-lg"
42
- type="video/mp4"
43
- controls
44
- />
45
- </template>
46
- <template #actions>
47
- <div class="flex gap-2">
48
- <Button variant="solid" @click="addVideo(addVideoDialog.url)">
49
- Insert Video
50
- </Button>
51
- <Button @click="reset">Cancel</Button>
52
- </div>
53
- </template>
54
- </Dialog>
2
+ <slot v-bind="{ onClick: selectAndUploadVideo }"></slot>
55
3
  </template>
56
- <script>
57
- import { Button } from '../Button'
58
- import { Dialog } from '../Dialog'
59
- import { FileUploader } from '../FileUploader'
4
+ <script setup lang="ts">
5
+ import { Editor } from '@tiptap/vue-3'
60
6
 
61
- export default {
62
- name: 'InsertImage',
63
- props: ['editor'],
64
- expose: ['openDialog'],
65
- data() {
66
- return {
67
- addVideoDialog: { url: '', file: null, show: false },
68
- }
69
- },
70
- components: { Button, Dialog, FileUploader },
71
- methods: {
72
- openDialog() {
73
- this.addVideoDialog.show = true
74
- },
75
- onVideoSelect(e) {
76
- let file = e.target.files[0]
77
- if (!file) {
78
- return
79
- }
80
- this.addVideoDialog.file = file
81
- },
7
+ const props = defineProps<{
8
+ editor: Editor
9
+ }>()
82
10
 
83
- addVideo(src) {
84
- if (!src) return
85
- this.editor
86
- .chain()
87
- .focus()
88
- .insertContent(`<video src="${src}"></video>`)
89
- .run()
90
- this.reset()
91
- },
92
- reset() {
93
- this.addVideoDialog = this.$options.data().addVideoDialog
94
- },
95
- },
11
+ function selectAndUploadVideo() {
12
+ props.editor.chain().focus().selectAndUploadVideo().run()
96
13
  }
97
14
  </script>
@@ -59,7 +59,7 @@ import TextEditorBubbleMenu from './TextEditorBubbleMenu.vue'
59
59
  import TextEditorFloatingMenu from './TextEditorFloatingMenu.vue'
60
60
  import EmojiExtension from './extensions/emoji/emoji-extension'
61
61
  import SlashCommands from './extensions/slash-commands/slash-commands-extension'
62
- import { MarkdownPasteExtension } from './extensions/markdown-paste-extension'
62
+ import { ContentPasteExtension } from './extensions/content-paste-extension'
63
63
  import { TagNode, TagExtension } from './extensions/tag/tag-extension'
64
64
  import { Heading } from './extensions/heading/heading'
65
65
  import { ImageGroup } from './extensions/image-group/image-group-extension'
@@ -202,9 +202,10 @@ onMounted(() => {
202
202
  TagExtension.configure({
203
203
  tags: () => props.tags,
204
204
  }),
205
- MarkdownPasteExtension.configure({
205
+ ContentPasteExtension.configure({
206
206
  enabled: true,
207
207
  showConfirmation: true,
208
+ uploadFunction: props.uploadFunction || defaultUploadFunction,
208
209
  }),
209
210
  ...(props.extensions || []),
210
211
  ],
@@ -1,31 +1,53 @@
1
1
  import { Extension } from '@tiptap/core'
2
2
  import { Plugin, PluginKey } from '@tiptap/pm/state'
3
3
  import { DOMParser } from '@tiptap/pm/model'
4
+ import { EditorView } from 'prosemirror-view'
4
5
  import { detectMarkdown, markdownToHTML } from '../../../utils/markdown'
6
+ import { processMultipleImages } from './image/image-extension'
5
7
 
6
- export interface MarkdownPasteOptions {
8
+ export interface ContentPasteOptions {
7
9
  enabled: boolean
8
10
  showConfirmation: boolean
11
+ uploadFunction: Function | null
9
12
  }
10
13
 
11
- export const MarkdownPasteExtension = Extension.create<MarkdownPasteOptions>({
12
- name: 'markdownPaste',
14
+ export const ContentPasteExtension = Extension.create<ContentPasteOptions>({
15
+ name: 'contentPaste',
13
16
 
14
17
  addOptions() {
15
18
  return {
16
19
  enabled: true,
17
20
  showConfirmation: true,
21
+ uploadFunction: null,
18
22
  }
19
23
  },
20
24
 
21
25
  addProseMirrorPlugins() {
26
+ const extensionThis = this
22
27
  return [
23
28
  new Plugin({
24
- key: new PluginKey('markdownPaste'),
29
+ key: new PluginKey('contentPaste'),
25
30
  props: {
26
- handlePaste: (view, event, slice) => {
31
+ handlePaste: (
32
+ view: EditorView,
33
+ event: ClipboardEvent,
34
+ slice: any,
35
+ ) => {
27
36
  if (!this.options.enabled) return false
28
37
 
38
+ // handle image pasting
39
+ const files: File[] | [] = Array.from(
40
+ event.clipboardData?.files || [],
41
+ )
42
+ const images = Array.from(files).filter((file) =>
43
+ file.type.startsWith('image/'),
44
+ )
45
+ if (images.length > 0) {
46
+ processMultipleImages(images, view, null, extensionThis.options)
47
+ return true
48
+ }
49
+
50
+ // handle markdown pasting
29
51
  const text = event.clipboardData?.getData('text/plain')
30
52
  if (!text) return false
31
53
 
@@ -554,7 +554,7 @@ function getImageDimensions(
554
554
  /**
555
555
  * Process multiple image uploads sequentially
556
556
  */
557
- function processMultipleImages(
557
+ export function processMultipleImages(
558
558
  images: File[],
559
559
  view: EditorView,
560
560
  pos: number | null,
package/src/index.ts CHANGED
@@ -90,6 +90,7 @@ export { default as fileToBase64 } from './utils/file-to-base64'
90
90
  export { default as FileUploadHandler } from './utils/fileUploadHandler'
91
91
  export { usePageMeta } from './utils/pageMeta'
92
92
  export { dayjsLocal, dayjs } from './utils/dayjs'
93
+ export * from './utils/useFileUpload'
93
94
 
94
95
  // data-fetching, resources
95
96
  export {
@@ -1,17 +1,4 @@
1
- interface UploadOptions {
2
- private?: boolean
3
- folder?: string
4
- file_url?: string
5
- doctype?: string
6
- docname?: string
7
- fieldname?: string
8
- method?: string
9
- type?: string
10
- upload_endpoint?: string
11
- optimize?: boolean
12
- max_width?: number
13
- max_height?: number
14
- }
1
+ import { UploadOptions } from "./useFileUpload"
15
2
 
16
3
  type EventListenerOption = 'start' | 'progress' | 'finish' | 'error'
17
4