pimelon-ui 0.0.19

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 (39) hide show
  1. package/license.md +0 -0
  2. package/package.json +33 -0
  3. package/readme.md +0 -0
  4. package/src/components/Alert.vue +57 -0
  5. package/src/components/Avatar.vue +61 -0
  6. package/src/components/Badge.vue +40 -0
  7. package/src/components/Button.vue +134 -0
  8. package/src/components/Card.vue +37 -0
  9. package/src/components/Dialog.vue +195 -0
  10. package/src/components/Dropdown.vue +113 -0
  11. package/src/components/ErrorMessage.vue +15 -0
  12. package/src/components/FeatherIcon.vue +55 -0
  13. package/src/components/FileUploader.vue +220 -0
  14. package/src/components/GreenCheckIcon.vue +16 -0
  15. package/src/components/Input.vue +169 -0
  16. package/src/components/Link.vue +28 -0
  17. package/src/components/ListItem.vue +28 -0
  18. package/src/components/LoadingIndicator.vue +12 -0
  19. package/src/components/LoadingText.vue +21 -0
  20. package/src/components/Modal.vue +67 -0
  21. package/src/components/Popover.vue +192 -0
  22. package/src/components/Resource.vue +21 -0
  23. package/src/components/Spinner.vue +27 -0
  24. package/src/components/SuccessMessage.vue +15 -0
  25. package/src/components/TextEditor/Menu.vue +32 -0
  26. package/src/components/TextEditor/TextEditor.vue +193 -0
  27. package/src/components/TextEditor/commands.js +79 -0
  28. package/src/components/TextEditor/index.js +1 -0
  29. package/src/components/Toast.vue +167 -0
  30. package/src/directives/onOutsideClick.js +28 -0
  31. package/src/index.js +33 -0
  32. package/src/style.css +15 -0
  33. package/src/utils/call.js +98 -0
  34. package/src/utils/debounce.js +15 -0
  35. package/src/utils/plugin.js +24 -0
  36. package/src/utils/resources.js +510 -0
  37. package/src/utils/socketio.js +9 -0
  38. package/src/utils/tailwind.config.js +110 -0
  39. package/src/utils/vite-dev-server.js +14 -0
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <teleport to="#melonui-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] max-w-[40rem]"
14
+ >
15
+ <div class="flex items-start justify-between space-x-2">
16
+ <div
17
+ class="grid flex-shrink-0 w-6 h-6 rounded-full place-items-center"
18
+ :class="{
19
+ 'bg-red-100': appearance == 'danger',
20
+ }"
21
+ v-if="icon"
22
+ >
23
+ <FeatherIcon
24
+ :name="icon"
25
+ class="w-4 h-4"
26
+ :class="{
27
+ 'text-red-600': appearance == 'danger',
28
+ }"
29
+ />
30
+ </div>
31
+ <div>
32
+ <slot>
33
+ <p class="text-base">
34
+ {{ text }}
35
+ </p>
36
+ </slot>
37
+ </div>
38
+ <div>
39
+ <slot name="actions">
40
+ <Button appearance="minimal" icon="x" @click="shown = false" />
41
+ </slot>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </transition>
47
+ </teleport>
48
+ </template>
49
+ <script>
50
+ import { FeatherIcon } from 'pimelon-ui'
51
+ const positions = [
52
+ 'top-right',
53
+ 'top-center',
54
+ 'top-left',
55
+ 'bottom-right',
56
+ 'bottom-center',
57
+ 'bottom-left',
58
+ ]
59
+
60
+ export default {
61
+ name: 'Toast',
62
+ props: {
63
+ position: {
64
+ type: String,
65
+ default: 'top-right',
66
+ },
67
+ icon: {
68
+ type: String,
69
+ },
70
+ text: {
71
+ type: String,
72
+ },
73
+ appearance: {
74
+ type: String,
75
+ },
76
+ timeout: {
77
+ type: Number,
78
+ default: 5,
79
+ },
80
+ },
81
+ components: {
82
+ FeatherIcon,
83
+ },
84
+ created() {
85
+ if (!document.getElementById('melonui-toast-root')) {
86
+ const root = document.createElement('div')
87
+ root.id = 'melonui-toast-root'
88
+ root.style.position = 'fixed'
89
+ root.style.top = '16px'
90
+ root.style.right = '16px'
91
+ root.style.bottom = '16px'
92
+ root.style.left = '16px'
93
+ root.style.zIndex = '9999'
94
+ root.style.pointerEvents = 'none'
95
+ document.body.appendChild(root)
96
+ }
97
+ },
98
+ mounted() {
99
+ this.shown = true
100
+ setTimeout(() => {
101
+ this.shown = false
102
+ }, this.timeout * 1000)
103
+ },
104
+ data() {
105
+ return {
106
+ shown: false,
107
+ }
108
+ },
109
+ computed: {
110
+ style() {
111
+ let style = {}
112
+ if (this.position.includes('top')) {
113
+ style.top = 0
114
+ }
115
+ if (this.position.includes('bottom')) {
116
+ style.bottom = 0
117
+ }
118
+ if (this.position.includes('right')) {
119
+ style.right = 0
120
+ }
121
+ if (this.position.includes('left')) {
122
+ style.left = 0
123
+ }
124
+ if (this.position.includes('center')) {
125
+ style.left = '50%'
126
+ // style.transform = 'translateX(-50%)'
127
+ }
128
+ return style
129
+ },
130
+ transitionProps() {
131
+ let props = {
132
+ enterActiveClass: 'transition duration-200 ease-out',
133
+ enterFromClass: 'opacity-0',
134
+ enterToClass: 'translate-y-0 opacity-100',
135
+ leaveActiveClass: 'transition duration-100 ease-in',
136
+ leaveFromClass: 'scale-100 translate-y-0 opacity-100',
137
+ leaveToClass: 'scale-75 translate-y-4 opacity-0',
138
+ }
139
+ if (this.position.includes('top')) {
140
+ props.enterFromClass += ' -translate-y-12'
141
+ }
142
+ if (this.position.includes('bottom')) {
143
+ props.enterFromClass += ' translate-y-12'
144
+ }
145
+ return props
146
+ },
147
+ },
148
+ }
149
+ </script>
150
+ <style>
151
+ .toast-top-enter-active,
152
+ .toast-bottom-enter-active {
153
+ transition: all 200ms ease-out;
154
+ }
155
+ .toast-top-leave-active,
156
+ .toast-bottom-leave-active {
157
+ transition: all 100ms ease-in;
158
+ }
159
+ .toast-top-enter-from {
160
+ opacity: 0;
161
+ transform: translateY(0);
162
+ }
163
+ .toast-top-enter-to {
164
+ opacity: 1;
165
+ transform: translateY(0);
166
+ }
167
+ </style>
@@ -0,0 +1,28 @@
1
+ let instances = []
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
+ el.dataset.outsideClickIndex = instances.length
13
+
14
+ const fn = binding.value
15
+ const click = function (e) {
16
+ onDocumentClick(e, el, fn)
17
+ }
18
+
19
+ document.addEventListener('click', click)
20
+ instances.push(click)
21
+ },
22
+ unmounted(el) {
23
+ const index = el.dataset.outsideClickIndex
24
+ const handler = instances[index]
25
+ document.addEventListener('click', handler)
26
+ instances.splice(index, 1)
27
+ },
28
+ }
package/src/index.js ADDED
@@ -0,0 +1,33 @@
1
+ // components
2
+ export { default as Alert } from './components/Alert.vue'
3
+ export { default as Avatar } from './components/Avatar.vue'
4
+ export { default as Badge } from './components/Badge.vue'
5
+ export { default as Button } from './components/Button.vue'
6
+ export { default as Card } from './components/Card.vue'
7
+ export { default as Dialog } from './components/Dialog.vue'
8
+ export { default as Dropdown } from './components/Dropdown.vue'
9
+ export { default as ErrorMessage } from './components/ErrorMessage.vue'
10
+ export { default as FeatherIcon } from './components/FeatherIcon.vue'
11
+ export { default as FileUploader } from './components/FileUploader.vue'
12
+ export { default as GreenCheckIcon } from './components/GreenCheckIcon.vue'
13
+ export { default as Input } from './components/Input.vue'
14
+ export { default as Link } from './components/Link.vue'
15
+ export { default as ListItem } from './components/ListItem.vue'
16
+ export { default as LoadingIndicator } from './components/LoadingIndicator.vue'
17
+ export { default as LoadingText } from './components/LoadingText.vue'
18
+ export { default as Modal } from './components/Modal.vue'
19
+ export { default as Popover } from './components/Popover.vue'
20
+ export { default as Resource } from './components/Resource.vue'
21
+ export { default as Spinner } from './components/Spinner.vue'
22
+ export { default as SuccessMessage } from './components/SuccessMessage.vue'
23
+ export { default as TextEditor } from './components/TextEditor'
24
+
25
+ // directives
26
+ export { default as onOutsideClickDirective } from './directives/onOutsideClick.js'
27
+
28
+ // utilities
29
+ export { default as call, createCall } from './utils/call.js'
30
+ export { default as debounce } from './utils/debounce.js'
31
+
32
+ // plugin
33
+ export { default as MelonUI } from './utils/plugin.js'
package/src/style.css ADDED
@@ -0,0 +1,15 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer components {
6
+ .form-input,
7
+ .form-textarea,
8
+ .form-select {
9
+ @apply py-1 text-base leading-5 placeholder-gray-700 bg-gray-100 border-0 rounded-md focus:ring-0 focus:bg-gray-200 focus:shadow-none;
10
+ }
11
+
12
+ .form-checkbox {
13
+ @apply text-blue-500 rounded-md;
14
+ }
15
+ }
@@ -0,0 +1,98 @@
1
+ export default async function call(method, args, options = {}) {
2
+ if (!args) {
3
+ args = {}
4
+ }
5
+
6
+ let headers = Object.assign(
7
+ {
8
+ Accept: 'application/json',
9
+ 'Content-Type': 'application/json; charset=utf-8',
10
+ 'X-Melon-Site-Name': window.location.hostname,
11
+ },
12
+ options.headers || {}
13
+ )
14
+
15
+ if (window.csrf_token && window.csrf_token !== '{{ csrf_token }}') {
16
+ headers['X-Melon-CSRF-Token'] = window.csrf_token
17
+ }
18
+
19
+ let path = method.startsWith('/') ? method : `/api/method/${method}`
20
+ const res = await fetch(path, {
21
+ method: 'POST',
22
+ headers,
23
+ body: JSON.stringify(args),
24
+ })
25
+
26
+ if (res.ok) {
27
+ const data = await res.json()
28
+ if (data.docs || method === 'login') {
29
+ return data
30
+ }
31
+ if (data.exc) {
32
+ try {
33
+ console.groupCollapsed(method)
34
+ console.log(`method: ${method}`)
35
+ console.log(`params:`, args)
36
+ let warning = JSON.parse(data.exc)
37
+ for (let text of warning) {
38
+ console.log(text)
39
+ }
40
+ console.groupEnd()
41
+ } catch (e) {
42
+ console.warn('Error printing debug messages', e)
43
+ }
44
+ }
45
+ return data.message
46
+ } else {
47
+ let response = await res.text()
48
+ let error, exception
49
+ try {
50
+ error = JSON.parse(response)
51
+ // eslint-disable-next-line no-empty
52
+ } catch (e) {}
53
+ let errorParts = [
54
+ [method, error.exc_type, error._error_message].filter(Boolean).join(' '),
55
+ ]
56
+ if (error.exc) {
57
+ exception = error.exc
58
+ try {
59
+ exception = JSON.parse(exception)[0]
60
+ console.log(exception)
61
+ // eslint-disable-next-line no-empty
62
+ } catch (e) {}
63
+ }
64
+ let e = new Error(errorParts.join('\n'))
65
+ e.exc_type = error.exc_type
66
+ e.exc = exception
67
+ e.status = res.status
68
+ e.messages = error._server_messages
69
+ ? JSON.parse(error._server_messages)
70
+ : []
71
+ e.messages = e.messages.concat(error.message)
72
+ e.messages = e.messages.map((m) => {
73
+ try {
74
+ return JSON.parse(m).message
75
+ } catch (error) {
76
+ return m
77
+ }
78
+ })
79
+ e.messages = e.messages.filter(Boolean)
80
+ if (!e.messages.length) {
81
+ e.messages = error._error_message
82
+ ? [error._error_message]
83
+ : ['Internal Server Error']
84
+ }
85
+
86
+ if (options.onError) {
87
+ options.onError({ response: res, status: res.status, error: e })
88
+ }
89
+
90
+ throw e
91
+ }
92
+ }
93
+
94
+ export function createCall(options) {
95
+ return function customCall(method, args) {
96
+ return call(method, args, options)
97
+ }
98
+ }
@@ -0,0 +1,15 @@
1
+ export default function debounce(func, wait, immediate) {
2
+ var timeout
3
+ return function () {
4
+ var context = this,
5
+ args = arguments
6
+ var later = function () {
7
+ timeout = null
8
+ if (!immediate) func.apply(context, args)
9
+ }
10
+ var callNow = immediate && !timeout
11
+ clearTimeout(timeout)
12
+ timeout = setTimeout(later, wait)
13
+ if (callNow) func.apply(context, args)
14
+ }
15
+ }
@@ -0,0 +1,24 @@
1
+ import resources from './resources'
2
+ import call from './call'
3
+ import socket from './socketio'
4
+
5
+ let defaultOptions = {
6
+ resources: true,
7
+ call: true,
8
+ socketio: true,
9
+ }
10
+
11
+ export default {
12
+ install(app, options = {}) {
13
+ options = Object.assign({}, defaultOptions, options)
14
+ options.resources && app.use(resources, options.resources)
15
+
16
+ if (options.call) {
17
+ let callFunction = typeof options.call == 'function' ? options.call : call
18
+ app.config.globalProperties.$call = callFunction
19
+ }
20
+ if (options.socketio) {
21
+ app.config.globalProperties.$socket = socket
22
+ }
23
+ },
24
+ }