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.
- package/license.md +0 -0
- package/package.json +33 -0
- package/readme.md +0 -0
- package/src/components/Alert.vue +57 -0
- package/src/components/Avatar.vue +61 -0
- package/src/components/Badge.vue +40 -0
- package/src/components/Button.vue +134 -0
- package/src/components/Card.vue +37 -0
- package/src/components/Dialog.vue +195 -0
- package/src/components/Dropdown.vue +113 -0
- package/src/components/ErrorMessage.vue +15 -0
- package/src/components/FeatherIcon.vue +55 -0
- package/src/components/FileUploader.vue +220 -0
- package/src/components/GreenCheckIcon.vue +16 -0
- package/src/components/Input.vue +169 -0
- package/src/components/Link.vue +28 -0
- package/src/components/ListItem.vue +28 -0
- package/src/components/LoadingIndicator.vue +12 -0
- package/src/components/LoadingText.vue +21 -0
- package/src/components/Modal.vue +67 -0
- package/src/components/Popover.vue +192 -0
- package/src/components/Resource.vue +21 -0
- package/src/components/Spinner.vue +27 -0
- package/src/components/SuccessMessage.vue +15 -0
- package/src/components/TextEditor/Menu.vue +32 -0
- package/src/components/TextEditor/TextEditor.vue +193 -0
- package/src/components/TextEditor/commands.js +79 -0
- package/src/components/TextEditor/index.js +1 -0
- package/src/components/Toast.vue +167 -0
- package/src/directives/onOutsideClick.js +28 -0
- package/src/index.js +33 -0
- package/src/style.css +15 -0
- package/src/utils/call.js +98 -0
- package/src/utils/debounce.js +15 -0
- package/src/utils/plugin.js +24 -0
- package/src/utils/resources.js +510 -0
- package/src/utils/socketio.js +9 -0
- package/src/utils/tailwind.config.js +110 -0
- 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
|
+
}
|