pimelon-ui 0.0.98

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 (93) hide show
  1. package/license.md +0 -0
  2. package/package.json +75 -0
  3. package/readme.md +0 -0
  4. package/src/components/Alert.vue +57 -0
  5. package/src/components/Autocomplete.vue +176 -0
  6. package/src/components/Avatar.vue +62 -0
  7. package/src/components/Badge.vue +42 -0
  8. package/src/components/Button.vue +155 -0
  9. package/src/components/Card.vue +37 -0
  10. package/src/components/DatePicker.vue +252 -0
  11. package/src/components/Dialog.vue +216 -0
  12. package/src/components/Dropdown.vue +145 -0
  13. package/src/components/ErrorMessage.vue +24 -0
  14. package/src/components/FeatherIcon.vue +59 -0
  15. package/src/components/FileUploader.vue +220 -0
  16. package/src/components/GreenCheckIcon.vue +16 -0
  17. package/src/components/Input.vue +214 -0
  18. package/src/components/Link.vue +28 -0
  19. package/src/components/ListItem.vue +28 -0
  20. package/src/components/LoadingIndicator.vue +27 -0
  21. package/src/components/LoadingText.vue +21 -0
  22. package/src/components/Popover.vue +253 -0
  23. package/src/components/Resource.vue +21 -0
  24. package/src/components/TextEditor/FontColor.vue +108 -0
  25. package/src/components/TextEditor/InsertImage.vue +74 -0
  26. package/src/components/TextEditor/InsertLink.vue +67 -0
  27. package/src/components/TextEditor/InsertVideo.vue +94 -0
  28. package/src/components/TextEditor/MentionList.vue +95 -0
  29. package/src/components/TextEditor/Menu.vue +99 -0
  30. package/src/components/TextEditor/TextEditor.vue +275 -0
  31. package/src/components/TextEditor/TextEditorBubbleMenu.vue +69 -0
  32. package/src/components/TextEditor/TextEditorFixedMenu.vue +72 -0
  33. package/src/components/TextEditor/TextEditorFloatingMenu.vue +55 -0
  34. package/src/components/TextEditor/commands.js +272 -0
  35. package/src/components/TextEditor/icons/align-center.vue +14 -0
  36. package/src/components/TextEditor/icons/align-justify.vue +14 -0
  37. package/src/components/TextEditor/icons/align-left.vue +14 -0
  38. package/src/components/TextEditor/icons/align-right.vue +14 -0
  39. package/src/components/TextEditor/icons/arrow-go-back-line.vue +14 -0
  40. package/src/components/TextEditor/icons/arrow-go-forward-line.vue +14 -0
  41. package/src/components/TextEditor/icons/bold.vue +14 -0
  42. package/src/components/TextEditor/icons/code-view.vue +14 -0
  43. package/src/components/TextEditor/icons/double-quotes-r.vue +14 -0
  44. package/src/components/TextEditor/icons/font-color.vue +14 -0
  45. package/src/components/TextEditor/icons/format-clear.vue +14 -0
  46. package/src/components/TextEditor/icons/h-1.vue +14 -0
  47. package/src/components/TextEditor/icons/h-2.vue +14 -0
  48. package/src/components/TextEditor/icons/h-3.vue +14 -0
  49. package/src/components/TextEditor/icons/h-4.vue +14 -0
  50. package/src/components/TextEditor/icons/h-5.vue +14 -0
  51. package/src/components/TextEditor/icons/h-6.vue +14 -0
  52. package/src/components/TextEditor/icons/image-add-line.vue +14 -0
  53. package/src/components/TextEditor/icons/italic.vue +14 -0
  54. package/src/components/TextEditor/icons/link.vue +14 -0
  55. package/src/components/TextEditor/icons/list-ordered.vue +14 -0
  56. package/src/components/TextEditor/icons/list-unordered.vue +14 -0
  57. package/src/components/TextEditor/icons/readme.md +1 -0
  58. package/src/components/TextEditor/icons/separator.vue +14 -0
  59. package/src/components/TextEditor/icons/strikethrough.vue +14 -0
  60. package/src/components/TextEditor/icons/table-2.vue +14 -0
  61. package/src/components/TextEditor/icons/text.vue +11 -0
  62. package/src/components/TextEditor/icons/underline.vue +14 -0
  63. package/src/components/TextEditor/icons/video-add-line.vue +14 -0
  64. package/src/components/TextEditor/image-extension.js +152 -0
  65. package/src/components/TextEditor/index.js +6 -0
  66. package/src/components/TextEditor/mention.js +72 -0
  67. package/src/components/TextEditor/utils.js +11 -0
  68. package/src/components/TextEditor/video-extension.js +60 -0
  69. package/src/components/Toast.vue +83 -0
  70. package/src/components/Tooltip.vue +36 -0
  71. package/src/components/toast.js +98 -0
  72. package/src/directives/onOutsideClick.js +34 -0
  73. package/src/directives/visibility.js +24 -0
  74. package/src/index.js +56 -0
  75. package/src/resources/documentResource.js +194 -0
  76. package/src/resources/index.js +4 -0
  77. package/src/resources/listResource.js +312 -0
  78. package/src/resources/local.js +16 -0
  79. package/src/resources/plugin.js +105 -0
  80. package/src/resources/resources.js +215 -0
  81. package/src/style.css +15 -0
  82. package/src/utils/call.js +98 -0
  83. package/src/utils/config.js +9 -0
  84. package/src/utils/debounce.js +15 -0
  85. package/src/utils/file-to-base64.js +9 -0
  86. package/src/utils/markdown.js +29 -0
  87. package/src/utils/melonRequest.js +105 -0
  88. package/src/utils/pageMeta.js +52 -0
  89. package/src/utils/plugin.js +24 -0
  90. package/src/utils/request.js +49 -0
  91. package/src/utils/socketio.js +11 -0
  92. package/src/utils/tailwind.config.js +117 -0
  93. package/src/utils/vite-dev-server.js +14 -0
@@ -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>
@@ -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 4c.552 0 1 .448 1 1v4.2l5.213-3.65c.226-.158.538-.103.697.124.058.084.09.184.09.286v12.08c0 .276-.224.5-.5.5-.103 0-.203-.032-.287-.09L17 14.8V19c0 .552-.448 1-1 1H2c-.552 0-1-.448-1-1V5c0-.552.448-1 1-1h14zm-1 2H3v12h12V6zM8 8h2v3h3v2H9.999L10 16H8l-.001-3H5v-2h3V8zm13 .841l-4 2.8v.718l4 2.8V8.84z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </template>
@@ -0,0 +1,152 @@
1
+ // Plugin adapted from the following examples:
2
+ // - https://github.com/ueberdosis/tiptap/blob/main/packages/extension-image/src/image.ts
3
+ // - https://gist.github.com/slava-vishnyakov/16076dff1a77ddaca93c4bccd4ec4521
4
+
5
+ import { mergeAttributes, Node, nodeInputRule } from '@tiptap/core'
6
+ import { Plugin } from 'prosemirror-state'
7
+ import fileToBase64 from '../../utils/file-to-base64'
8
+
9
+ export const inputRegex =
10
+ /(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/
11
+
12
+ export default Node.create({
13
+ name: 'image',
14
+ addOptions() {
15
+ return {
16
+ inline: false,
17
+ HTMLAttributes: {},
18
+ }
19
+ },
20
+ inline() {
21
+ return this.options.inline
22
+ },
23
+ group() {
24
+ return this.options.inline ? 'inline' : 'block'
25
+ },
26
+ draggable: true,
27
+ addAttributes() {
28
+ return {
29
+ src: {
30
+ default: null,
31
+ },
32
+ alt: {
33
+ default: null,
34
+ },
35
+ title: {
36
+ default: null,
37
+ },
38
+ }
39
+ },
40
+ parseHTML() {
41
+ return [
42
+ {
43
+ tag: 'img[src]',
44
+ },
45
+ ]
46
+ },
47
+ renderHTML({ HTMLAttributes }) {
48
+ return ['img', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]
49
+ },
50
+
51
+ addCommands() {
52
+ return {
53
+ setImage:
54
+ (options) =>
55
+ ({ commands }) => {
56
+ return commands.insertContent({
57
+ type: this.name,
58
+ attrs: options,
59
+ })
60
+ },
61
+ }
62
+ },
63
+
64
+ addInputRules() {
65
+ return [
66
+ nodeInputRule({
67
+ find: inputRegex,
68
+ type: this.type,
69
+ getAttributes: (match) => {
70
+ const [, , alt, src, title] = match
71
+
72
+ return { src, alt, title }
73
+ },
74
+ }),
75
+ ]
76
+ },
77
+
78
+ addProseMirrorPlugins() {
79
+ return [dropImagePlugin()]
80
+ },
81
+ })
82
+
83
+ const dropImagePlugin = () => {
84
+ return new Plugin({
85
+ props: {
86
+ handlePaste(view, event, slice) {
87
+ const items = Array.from(event.clipboardData?.items || [])
88
+ const { schema } = view.state
89
+
90
+ items.forEach((item) => {
91
+ const image = item.getAsFile()
92
+ if (!image) return
93
+
94
+ if (item.type.indexOf('image') === 0) {
95
+ event.preventDefault()
96
+
97
+ fileToBase64(image).then((base64) => {
98
+ const node = schema.nodes.image.create({
99
+ src: base64,
100
+ })
101
+ const transaction = view.state.tr.replaceSelectionWith(node)
102
+ view.dispatch(transaction)
103
+ })
104
+ }
105
+ })
106
+
107
+ return false
108
+ },
109
+ handleDOMEvents: {
110
+ drop: (view, event) => {
111
+ const hasFiles =
112
+ event.dataTransfer &&
113
+ event.dataTransfer.files &&
114
+ event.dataTransfer.files.length
115
+
116
+ if (!hasFiles) {
117
+ return false
118
+ }
119
+
120
+ const images = Array.from(event.dataTransfer?.files ?? []).filter(
121
+ (file) => /image/i.test(file.type)
122
+ )
123
+
124
+ if (images.length === 0) {
125
+ return false
126
+ }
127
+
128
+ event.preventDefault()
129
+
130
+ const { schema } = view.state
131
+ const coordinates = view.posAtCoords({
132
+ left: event.clientX,
133
+ top: event.clientY,
134
+ })
135
+ if (!coordinates) return false
136
+
137
+ images.forEach(async (image) => {
138
+ fileToBase64(image).then((base64) => {
139
+ const node = schema.nodes.image.create({
140
+ src: base64,
141
+ })
142
+ const transaction = view.state.tr.insert(coordinates.pos, node)
143
+ view.dispatch(transaction)
144
+ })
145
+ })
146
+
147
+ return true
148
+ },
149
+ },
150
+ },
151
+ })
152
+ }
@@ -0,0 +1,6 @@
1
+ export { default } from './TextEditor.vue'
2
+ export { default as TextEditor } from './TextEditor.vue'
3
+ export { default as TextEditorBubbleMenu } from './TextEditorBubbleMenu.vue'
4
+ export { default as TextEditorFixedMenu } from './TextEditorFixedMenu.vue'
5
+ export { default as TextEditorFloatingMenu } from './TextEditorFloatingMenu.vue'
6
+ export { EditorContent as TextEditorContent } from '@tiptap/vue-3'
@@ -0,0 +1,72 @@
1
+ import tippy from 'tippy.js'
2
+ import { VueRenderer } from '@tiptap/vue-3'
3
+ import Mention from '@tiptap/extension-mention'
4
+ import MentionList from './MentionList.vue'
5
+
6
+ export default function configureMention(options) {
7
+ return Mention.configure({
8
+ HTMLAttributes: {
9
+ class: 'mention',
10
+ },
11
+ suggestion: getSuggestionOptions(options),
12
+ })
13
+ }
14
+
15
+ function getSuggestionOptions(options) {
16
+ return {
17
+ items: ({ query }) => {
18
+ return options
19
+ .filter((item) =>
20
+ item.label.toLowerCase().startsWith(query.toLowerCase())
21
+ )
22
+ .slice(0, 5)
23
+ },
24
+
25
+ render: () => {
26
+ let component
27
+ let popup
28
+
29
+ return {
30
+ onStart: (props) => {
31
+ component = new VueRenderer(MentionList, {
32
+ props,
33
+ editor: props.editor,
34
+ })
35
+ if (!props.clientRect) {
36
+ return
37
+ }
38
+ popup = tippy('body', {
39
+ getReferenceClientRect: props.clientRect,
40
+ appendTo: () => document.body,
41
+ content: component.element,
42
+ showOnCreate: true,
43
+ interactive: true,
44
+ trigger: 'manual',
45
+ placement: 'bottom-start',
46
+ })
47
+ },
48
+ onUpdate(props) {
49
+ component.updateProps(props)
50
+ if (!props.clientRect) {
51
+ return
52
+ }
53
+ popup[0].setProps({
54
+ getReferenceClientRect: props.clientRect,
55
+ })
56
+ },
57
+ onKeyDown(props) {
58
+ if (props.event.key === 'Escape') {
59
+ popup[0].hide()
60
+
61
+ return true
62
+ }
63
+ return component.ref?.onKeyDown(props)
64
+ },
65
+ onExit() {
66
+ popup[0].destroy()
67
+ component.destroy()
68
+ },
69
+ }
70
+ },
71
+ }
72
+ }
@@ -0,0 +1,11 @@
1
+ import commands from './commands'
2
+
3
+ export function createEditorButton(option) {
4
+ if (option instanceof Array) {
5
+ return option.map(createEditorButton)
6
+ }
7
+ if (typeof option == 'object') {
8
+ return option
9
+ }
10
+ return commands[option]
11
+ }
@@ -0,0 +1,60 @@
1
+ import { Node, mergeAttributes } from '@tiptap/core'
2
+ // Inspired by this blog: https://www.codemzy.com/blog/tiptap-video-embed-extension
3
+
4
+ const Video = Node.create({
5
+ name: 'video',
6
+ group: 'block',
7
+ selectable: true,
8
+ draggable: true,
9
+ atom: true,
10
+
11
+ addAttributes() {
12
+ return {
13
+ src: {
14
+ default: null,
15
+ },
16
+ }
17
+ },
18
+
19
+ parseHTML() {
20
+ return [
21
+ {
22
+ tag: 'video',
23
+ },
24
+ ]
25
+ },
26
+
27
+ renderHTML({ HTMLAttributes }) {
28
+ return ['video', mergeAttributes(HTMLAttributes)]
29
+ },
30
+
31
+ addNodeView() {
32
+ return ({ editor, node }) => {
33
+ const div = document.createElement('div')
34
+ div.className =
35
+ 'relative aspect-w-16 aspect-h-9' +
36
+ (editor.isEditable ? ' cursor-pointer' : '')
37
+
38
+ const video = document.createElement('video')
39
+ if (editor.isEditable) {
40
+ video.className = 'pointer-events-none'
41
+ }
42
+ video.src = node.attrs.src
43
+ if (!editor.isEditable) {
44
+ video.setAttribute('controls', '')
45
+ } else {
46
+ let videoPill = document.createElement('div')
47
+ videoPill.className =
48
+ 'absolute top-0 right-0 text-xs m-2 bg-gray-800 text-white px-2 py-1 rounded-md'
49
+ videoPill.innerHTML = 'Video'
50
+ div.append(videoPill)
51
+ }
52
+ div.append(video)
53
+ return {
54
+ dom: div,
55
+ }
56
+ }
57
+ },
58
+ })
59
+
60
+ export default Video
@@ -0,0 +1,83 @@
1
+ <template>
2
+ <div
3
+ class="my-2 min-w-[15rem] max-w-[40rem] rounded-lg border bg-white p-4 shadow-md"
4
+ >
5
+ <div class="flex items-start">
6
+ <div v-if="icon" class="mr-3 grid h-5 w-5 place-items-center">
7
+ <FeatherIcon :name="icon" :class="['h-5 w-5', iconClasses]" />
8
+ </div>
9
+ <div>
10
+ <slot>
11
+ <p
12
+ v-if="title"
13
+ class="text-base font-medium text-gray-900"
14
+ :class="{ 'mb-1': text }"
15
+ >
16
+ {{ title }}
17
+ </p>
18
+ <p v-if="text" class="text-base text-gray-600">
19
+ {{ text }}
20
+ </p>
21
+ </slot>
22
+ </div>
23
+ <div class="ml-auto pl-2">
24
+ <slot name="actions">
25
+ <button
26
+ class="grid h-5 w-5 place-items-center rounded hover:bg-gray-100"
27
+ @click="$emit('close')"
28
+ >
29
+ <FeatherIcon name="x" class="h-4 w-4 text-gray-700" />
30
+ </button>
31
+ </slot>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </template>
36
+ <script>
37
+ import FeatherIcon from './FeatherIcon.vue'
38
+ const positions = [
39
+ 'top-right',
40
+ 'top-center',
41
+ 'top-left',
42
+ 'bottom-right',
43
+ 'bottom-center',
44
+ 'bottom-left',
45
+ ]
46
+
47
+ export default {
48
+ name: 'Toast',
49
+ props: {
50
+ position: {
51
+ type: String,
52
+ default: 'top-center',
53
+ },
54
+ icon: {
55
+ type: String,
56
+ },
57
+ iconClasses: {
58
+ type: String,
59
+ },
60
+ title: {
61
+ type: String,
62
+ },
63
+ text: {
64
+ type: String,
65
+ },
66
+ timeout: {
67
+ type: Number,
68
+ default: 5,
69
+ },
70
+ },
71
+ emits: ['close'],
72
+ components: {
73
+ FeatherIcon,
74
+ },
75
+ mounted() {
76
+ if (this.timeout > 0) {
77
+ setTimeout(() => {
78
+ this.$emit('close')
79
+ }, this.timeout * 1000)
80
+ }
81
+ },
82
+ }
83
+ </script>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <Popover trigger="hover" :hoverDelay="hoverDelay" :placement="placement">
3
+ <template #target>
4
+ <slot />
5
+ </template>
6
+ <template #body>
7
+ <slot name="body">
8
+ <div
9
+ v-if="text"
10
+ class="rounded-lg border border-gray-100 bg-gray-800 px-2 py-1 text-xs text-white shadow-xl"
11
+ >
12
+ {{ text }}
13
+ </div>
14
+ </slot>
15
+ </template>
16
+ </Popover>
17
+ </template>
18
+ <script>
19
+ import Popover from './Popover.vue'
20
+ export default {
21
+ name: 'Tooltip',
22
+ components: { Popover },
23
+ props: {
24
+ hoverDelay: {
25
+ default: 0.5,
26
+ },
27
+ placement: {
28
+ default: 'top',
29
+ },
30
+ text: {
31
+ type: String,
32
+ default: null,
33
+ },
34
+ },
35
+ }
36
+ </script>
@@ -0,0 +1,98 @@
1
+ import { h, reactive, TransitionGroup, ref, Teleport } from 'vue'
2
+ import Toast from './Toast.vue'
3
+
4
+ let toasts = ref([])
5
+
6
+ export let Toasts = {
7
+ name: 'Toasts',
8
+ created() {
9
+ if (typeof window === 'undefined') return
10
+ if (!document.getElementById('melonui-toast-root')) {
11
+ const root = document.createElement('div')
12
+ root.id = 'melonui-toast-root'
13
+ root.style.position = 'fixed'
14
+ root.style.top = '16px'
15
+ root.style.right = '16px'
16
+ root.style.bottom = '16px'
17
+ root.style.left = '16px'
18
+ root.style.zIndex = '9999'
19
+ root.style.pointerEvents = 'none'
20
+ document.body.appendChild(root)
21
+ }
22
+ },
23
+ render() {
24
+ return h(Teleport, { to: '#melonui-toast-root' }, [
25
+ getToastsGroup('top-left'),
26
+ getToastsGroup('top-center'),
27
+ getToastsGroup('top-right'),
28
+ getToastsGroup('bottom-left'),
29
+ getToastsGroup('bottom-center'),
30
+ getToastsGroup('bottom-right'),
31
+ ])
32
+ },
33
+ }
34
+
35
+ function getToastsGroup(position) {
36
+ let transition =
37
+ 'transition duration-[230ms] ease-[cubic-bezier(.21,1.02,.73,1)]'
38
+ let classes = ['absolute']
39
+ if (position === 'top-left') {
40
+ classes.push('top-0 left-0')
41
+ }
42
+ if (position === 'top-right') {
43
+ classes.push('top-0 right-0')
44
+ }
45
+ if (position === 'top-center') {
46
+ classes.push('top-0 left-1/2 -translate-x-1/2')
47
+ }
48
+ if (position === 'bottom-left') {
49
+ classes.push('bottom-0 left-0')
50
+ }
51
+ if (position === 'bottom-right') {
52
+ classes.push('bottom-0 right-0')
53
+ }
54
+ if (position === 'bottom-center') {
55
+ classes.push('bottom-0 left-1/2 -translate-x-1/2')
56
+ }
57
+
58
+ return h(
59
+ TransitionGroup,
60
+ {
61
+ tag: 'div',
62
+ class: classes,
63
+ moveClass: transition,
64
+ enterActiveClass: transition,
65
+ enterFromClass: 'translate-y-1 opacity-0',
66
+ enterToClass: 'translate-y-0 opacity-100',
67
+ leaveActiveClass: `${transition} absolute`,
68
+ leaveFromClass: 'translate-y-0 opacity-100',
69
+ leaveToClass: 'translate-y-1 opacity-0',
70
+ },
71
+ () =>
72
+ toasts.value
73
+ .filter((toast) => toast.position === position)
74
+ .map((toast) => {
75
+ return h(
76
+ 'div',
77
+ { key: toast.key, class: 'pointer-events-auto flex' },
78
+ h(Toast, {
79
+ ...toast,
80
+ onClose: () => {
81
+ toasts.value = toasts.value.filter((t) => t !== toast)
82
+ },
83
+ })
84
+ )
85
+ })
86
+ )
87
+ }
88
+
89
+ export function toast(options) {
90
+ let id = `toast-${Math.random().toString(36).slice(2, 9)}`
91
+ let toast = reactive({
92
+ key: id,
93
+ position: 'top-center',
94
+ ...options,
95
+ })
96
+ toasts.value.push(toast)
97
+ return id
98
+ }
@@ -0,0 +1,34 @@
1
+ const instanceMap = new Map()
2
+
3
+ function onDocumentClick(e, el, fn) {
4
+ let target = e.target
5
+ if (el !== target && !el.contains(target)) {
6
+ fn?.(e)
7
+ }
8
+ }
9
+
10
+ export default {
11
+ beforeMount(el, binding) {
12
+ const fn = binding.value
13
+ const clickHandler = function (e) {
14
+ onDocumentClick(e, el, fn)
15
+ }
16
+
17
+ removeHandlerIfPresent(el)
18
+ instanceMap.set(el, clickHandler)
19
+ document.addEventListener('click', clickHandler)
20
+ },
21
+ unmounted(el) {
22
+ removeHandlerIfPresent(el)
23
+ },
24
+ }
25
+
26
+ function removeHandlerIfPresent(el) {
27
+ const clickHandler = instanceMap.get(el)
28
+ if (!clickHandler) {
29
+ return
30
+ }
31
+
32
+ instanceMap.delete(el)
33
+ document.removeEventListener('click', clickHandler)
34
+ }
@@ -0,0 +1,24 @@
1
+ import { nextTick } from 'vue'
2
+
3
+ export default {
4
+ beforeMount(el, binding, vnode) {
5
+ let fn = binding.value
6
+ if (!fn) return
7
+
8
+ let observer = new IntersectionObserver((entries) => {
9
+ let entry = entries[0]
10
+ let visible = entry.isIntersecting && entry.intersectionRatio > 0
11
+ fn(visible, entry)
12
+ })
13
+ nextTick(() => {
14
+ observer.observe(el)
15
+ })
16
+ el._visibility_observer = observer
17
+ },
18
+ unmounted(el) {
19
+ if (el._visibility_observer) {
20
+ el._visibility_observer.disconnect()
21
+ delete el._visibility_observer
22
+ }
23
+ },
24
+ }
package/src/index.js ADDED
@@ -0,0 +1,56 @@
1
+ // components
2
+ export { default as Alert } from './components/Alert.vue'
3
+ export { default as Autocomplete } from './components/Autocomplete.vue'
4
+ export { default as Avatar } from './components/Avatar.vue'
5
+ export { default as Badge } from './components/Badge.vue'
6
+ export { default as Button } from './components/Button.vue'
7
+ export { default as Card } from './components/Card.vue'
8
+ export { default as DatePicker } from './components/DatePicker.vue'
9
+ export { default as Dialog } from './components/Dialog.vue'
10
+ export { default as Dropdown } from './components/Dropdown.vue'
11
+ export { default as ErrorMessage } from './components/ErrorMessage.vue'
12
+ export { default as FeatherIcon } from './components/FeatherIcon.vue'
13
+ export { default as FileUploader } from './components/FileUploader.vue'
14
+ export { default as GreenCheckIcon } from './components/GreenCheckIcon.vue'
15
+ export { default as Input } from './components/Input.vue'
16
+ export { default as Link } from './components/Link.vue'
17
+ export { default as ListItem } from './components/ListItem.vue'
18
+ export { default as LoadingIndicator } from './components/LoadingIndicator.vue'
19
+ export { default as LoadingText } from './components/LoadingText.vue'
20
+ export { default as Popover } from './components/Popover.vue'
21
+ export { default as Resource } from './components/Resource.vue'
22
+ export {
23
+ TextEditor,
24
+ TextEditorFixedMenu,
25
+ TextEditorBubbleMenu,
26
+ TextEditorFloatingMenu,
27
+ TextEditorContent,
28
+ } from './components/TextEditor'
29
+ export { default as Toast } from './components/Toast.vue'
30
+ export { toast, Toasts } from './components/toast.js'
31
+ export { default as Tooltip } from './components/Tooltip.vue'
32
+
33
+ // directives
34
+ export { default as onOutsideClickDirective } from './directives/onOutsideClick.js'
35
+ export { default as visibilityDirective } from './directives/visibility.js'
36
+
37
+ // utilities
38
+ export { default as call, createCall } from './utils/call.js'
39
+ export { default as debounce } from './utils/debounce.js'
40
+ export { default as fileToBase64 } from './utils/file-to-base64.js'
41
+
42
+ // data-fetching, resources
43
+ export {
44
+ createResource,
45
+ createDocumentResource,
46
+ createListResource,
47
+ resourcesPlugin,
48
+ } from './resources'
49
+ export { request } from './utils/request.js'
50
+ export { melonRequest } from './utils/melonRequest.js'
51
+ export { default as initSocket } from './utils/socketio.js'
52
+ export { setConfig, getConfig } from './utils/config.js'
53
+
54
+ // plugin
55
+ export { default as pageMetaPlugin } from './utils/pageMeta.js'
56
+ export { default as MelonUI } from './utils/plugin.js'