@mixd-id/web-scaffold 0.1.2207220538 → 0.1.2207250711
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/.env +1 -0
- package/index.html +24 -0
- package/package.json +4 -10
- package/postcss.config.js +6 -0
- package/server.js +95 -0
- package/src/App.vue +3 -0
- package/src/components/Alert.js +151 -0
- package/src/components/Alert.vue +71 -0
- package/src/components/Button.vue +146 -0
- package/src/components/ButtonGroup.vue +101 -0
- package/src/components/Carousel.vue +269 -0
- package/src/components/ContextMenu.vue +172 -0
- package/src/components/ContextMenu0.vue +180 -0
- package/src/components/DatePicker.vue +330 -0
- package/src/components/Dropdown.vue +119 -0
- package/src/components/Grid.vue +29 -0
- package/src/components/GridColumn.vue +31 -0
- package/src/components/Image.vue +93 -0
- package/src/components/ImageFullScreen.vue +101 -0
- package/src/components/Modal.vue +219 -0
- package/src/components/Slider.vue +265 -0
- package/src/components/Switch.vue +62 -0
- package/src/components/TabView.vue +106 -0
- package/src/components/TimePicker.vue +106 -0
- package/src/components/UI.vue +784 -0
- package/src/components/VirtualScroll.vue +261 -0
- package/src/components/VirtualScroll0.vue +375 -0
- package/src/entry-client.js +21 -0
- package/src/entry-server.js +72 -0
- package/src/helper.js +104 -0
- package/src/index.js +76 -0
- package/src/main.js +32 -0
- package/src/router.js +24 -0
- package/src/themes/index.js +66 -0
- package/tailwind.config.js +66 -0
- package/vite.config.js +30 -0
- package/dist/style.css +0 -1
- package/dist/webfxfy.es.js +0 -7935
- package/dist/webfxfy.umd.js +0 -40
package/.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
NODE_ENV=local
|
package/index.html
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" href="/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<meta name="description" content="/* default description */" />
|
|
8
|
+
<meta name="keywords" content="/* default keywords */" />
|
|
9
|
+
<meta name="author" content="/* default author */" />
|
|
10
|
+
<title><!-- default title --></title>
|
|
11
|
+
<script id="hydration">window.__hydration = JSON.parse('<!--serialize-pinia-->')</script>
|
|
12
|
+
<script>
|
|
13
|
+
document.querySelector('html').setAttribute('data-theme', localStorage.getItem('theme') ?? '')
|
|
14
|
+
</script>
|
|
15
|
+
<!--preload-links-->
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<div><!--app-html--><div class="context-overlay"><!--context-overlay--></div></div>
|
|
19
|
+
<div class="alert-overlay"><!--alert-overlay--></div>
|
|
20
|
+
<div class="modal-overlay"><!--modal-overlay--></div>
|
|
21
|
+
|
|
22
|
+
<script type="module" src="/src/entry-client.js"></script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mixd-id/web-scaffold",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2207250711",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite serve",
|
|
7
7
|
"build": "vite build",
|
|
8
8
|
"start": "node server.js"
|
|
9
9
|
},
|
|
10
|
-
"main": "./
|
|
10
|
+
"main": "./src/index.js",
|
|
11
11
|
"exports": {
|
|
12
|
-
".":
|
|
13
|
-
|
|
14
|
-
"require": "./dist/webfxfy.umd.js"
|
|
15
|
-
},
|
|
16
|
-
"./style.css": "./dist/style.css"
|
|
12
|
+
".": "./src/index.js",
|
|
13
|
+
"./themes/default": "./src/themes/index.js"
|
|
17
14
|
},
|
|
18
|
-
"files": [
|
|
19
|
-
"dist"
|
|
20
|
-
],
|
|
21
15
|
"dependencies": {
|
|
22
16
|
"@faker-js/faker": "^7.3.0",
|
|
23
17
|
"@tailwindcss/line-clamp": "^0.4.0",
|
package/server.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const express = require('express')
|
|
5
|
+
const cors = require('cors')
|
|
6
|
+
const cookieParser = require('cookie-parser')
|
|
7
|
+
|
|
8
|
+
const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
|
|
9
|
+
|
|
10
|
+
const root = process.cwd()
|
|
11
|
+
|
|
12
|
+
async function createServer() {
|
|
13
|
+
const resolve = (p) => path.resolve(__dirname, p)
|
|
14
|
+
|
|
15
|
+
const indexProd = ''
|
|
16
|
+
|
|
17
|
+
const manifest = {}
|
|
18
|
+
|
|
19
|
+
const app = express()
|
|
20
|
+
app.disable('x-powered-by')
|
|
21
|
+
app.use(cookieParser())
|
|
22
|
+
|
|
23
|
+
app.use(express.json())
|
|
24
|
+
app.use(cors())
|
|
25
|
+
|
|
26
|
+
const http = require('http').createServer(app)
|
|
27
|
+
|
|
28
|
+
const vite = await require('vite').createServer({
|
|
29
|
+
root,
|
|
30
|
+
logLevel: isTest ? 'error' : 'info',
|
|
31
|
+
server: {
|
|
32
|
+
middlewareMode: 'ssr',
|
|
33
|
+
watch: {
|
|
34
|
+
usePolling: true,
|
|
35
|
+
interval: 100
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
app.use(vite.middlewares)
|
|
40
|
+
|
|
41
|
+
app.use('*', async (req, res, next) => {
|
|
42
|
+
|
|
43
|
+
if(req.originalUrl.indexOf('socket.io') >= 0){
|
|
44
|
+
return next()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const url = req.originalUrl
|
|
49
|
+
|
|
50
|
+
let template, render
|
|
51
|
+
template = fs.readFileSync(resolve('index.html'), 'utf-8')
|
|
52
|
+
template = await vite.transformIndexHtml(url, template)
|
|
53
|
+
render = (await vite.ssrLoadModule('/src/entry-server.js')).render
|
|
54
|
+
|
|
55
|
+
const [appHtml, preloadLinks, serializePinia, redirect, cookie, modalOverlay, alertOverlay, contextOverlay ] = await render(req, manifest)
|
|
56
|
+
|
|
57
|
+
if(cookie.length > 0){
|
|
58
|
+
for(var i in cookie){
|
|
59
|
+
res.cookie(cookie[i][0], cookie[i][1])
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if(typeof redirect !== 'undefined'){
|
|
64
|
+
res.redirect(redirect)
|
|
65
|
+
}
|
|
66
|
+
else{
|
|
67
|
+
const html = template
|
|
68
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
69
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
70
|
+
.replace(`<!--serialize-pinia-->`, serializePinia)
|
|
71
|
+
.replace(`<!--context-overlay-->`, contextOverlay)
|
|
72
|
+
.replace(`<!--modal-overlay-->`, modalOverlay)
|
|
73
|
+
.replace(`<!--alert-overlay-->`, alertOverlay)
|
|
74
|
+
|
|
75
|
+
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
vite && vite.ssrFixStacktrace(e)
|
|
80
|
+
console.log(e.stack)
|
|
81
|
+
res.status(500).end(e.stack)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return { http, app, vite }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
createServer().then(({ http }) =>{
|
|
90
|
+
|
|
91
|
+
http.listen(3001, () => {
|
|
92
|
+
console.log('Listening on port ' + 3001)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
})
|
package/src/App.vue
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const createAlert = () => {
|
|
2
|
+
|
|
3
|
+
let overlay = document.querySelector('.alert-overlay')
|
|
4
|
+
let alert = document.querySelector('.alert')
|
|
5
|
+
|
|
6
|
+
if(!overlay){
|
|
7
|
+
overlay = document.createElement("div")
|
|
8
|
+
overlay.className = "alert-overlay"
|
|
9
|
+
document.body.appendChild(overlay)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if(!alert){
|
|
13
|
+
alert = document.createElement("div")
|
|
14
|
+
alert.className = "alert"
|
|
15
|
+
alert.innerHTML = `
|
|
16
|
+
<div class="alert-body text-center">
|
|
17
|
+
<svg width="120" height="120" class="inline-block fill-red-600" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
18
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 13.75C12.4142 13.75 12.75 13.4142 12.75 13V8.00001C12.75 7.5858 12.4142 7.25001 12 7.25001C11.5858 7.25001 11.25 7.5858 11.25 8.00001V13C11.25 13.4142 11.5858 13.75 12 13.75Z"/>
|
|
19
|
+
<path d="M13 16C13 16.5523 12.5523 17 12 17C11.4477 17 11 16.5523 11 16C11 15.4477 11.4477 15 12 15C12.5523 15 13 15.4477 13 16Z"/>
|
|
20
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20.5C16.6944 20.5 20.5 16.6944 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 16.6944 7.30558 20.5 12 20.5ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"/>
|
|
21
|
+
</svg>
|
|
22
|
+
<div class="my-4">
|
|
23
|
+
<label class="text-xl alert-title font-bold text-xl"></label>
|
|
24
|
+
<p class="alert-desc text-lg"></p>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="alert-foot"></div>`
|
|
28
|
+
overlay.appendChild(alert)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
overlay,
|
|
33
|
+
alert
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const openAlert = (type) => {
|
|
38
|
+
|
|
39
|
+
const { alert, overlay } = createAlert()
|
|
40
|
+
|
|
41
|
+
overlay.appendChild(alert)
|
|
42
|
+
|
|
43
|
+
overlay.classList.add('on')
|
|
44
|
+
|
|
45
|
+
alert.classList.add('on')
|
|
46
|
+
window.setTimeout(() => {
|
|
47
|
+
alert.classList.add('active')
|
|
48
|
+
}, 30)
|
|
49
|
+
|
|
50
|
+
alert.classList.remove('alert-error')
|
|
51
|
+
alert.classList.remove('alert-confirm')
|
|
52
|
+
alert.classList.remove('alert-info')
|
|
53
|
+
alert.classList.add('alert-' + type)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const closeAlert = () => {
|
|
57
|
+
|
|
58
|
+
const { alert, overlay } = createAlert()
|
|
59
|
+
|
|
60
|
+
const transitionEnd = () => {
|
|
61
|
+
|
|
62
|
+
overlay.classList.remove('on')
|
|
63
|
+
alert.classList.remove('on')
|
|
64
|
+
alert.removeEventListener('transitionend', transitionEnd)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
alert.addEventListener('transitionend', transitionEnd)
|
|
68
|
+
alert.classList.remove('active')
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const showAlert = (text, callback) => {
|
|
73
|
+
|
|
74
|
+
const { alert } = createAlert()
|
|
75
|
+
|
|
76
|
+
let { title, description } = typeof text === 'object' ? text : { title:text }
|
|
77
|
+
|
|
78
|
+
if(!title) title = 'Error'
|
|
79
|
+
if(!description) description = ''
|
|
80
|
+
|
|
81
|
+
const eTitle = alert.querySelector('.alert-title')
|
|
82
|
+
const eDesc = alert.querySelector('.alert-desc')
|
|
83
|
+
const foot = alert.querySelector('.alert-foot')
|
|
84
|
+
|
|
85
|
+
if(eTitle) eTitle.innerHTML = title
|
|
86
|
+
if(eDesc) eDesc.innerHTML = description
|
|
87
|
+
|
|
88
|
+
if(foot){
|
|
89
|
+
foot.innerHTML = `<button class="alert-ok">OK</button>`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const alertOk = () => {
|
|
93
|
+
closeAlert()
|
|
94
|
+
if(typeof callback === 'function')
|
|
95
|
+
callback()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
alert.querySelectorAll(".alert-ok").forEach((el) => {
|
|
99
|
+
el.addEventListener('click', alertOk)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
openAlert('error')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const showInfo = () => {
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const showConfirm = (text, callback) => {
|
|
110
|
+
|
|
111
|
+
const { alert } = createAlert()
|
|
112
|
+
|
|
113
|
+
let { title, description } = typeof text === 'object' ? text : { title:text }
|
|
114
|
+
|
|
115
|
+
if(!title) title = 'Confirm'
|
|
116
|
+
if(!description) description = ''
|
|
117
|
+
|
|
118
|
+
const eTitle = alert.querySelector('.alert-title')
|
|
119
|
+
const eDesc = alert.querySelector('.alert-desc')
|
|
120
|
+
const foot = alert.querySelector('.alert-foot')
|
|
121
|
+
|
|
122
|
+
if(eTitle) eTitle.innerHTML = title
|
|
123
|
+
if(eDesc) eDesc.innerHTML = description
|
|
124
|
+
|
|
125
|
+
if(foot){
|
|
126
|
+
foot.innerHTML = `<button class="alert-ok">OK</button>
|
|
127
|
+
<button class="alert-close">Cancel</button>`
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const alertOk = () => {
|
|
131
|
+
closeAlert()
|
|
132
|
+
if(typeof callback === 'function')
|
|
133
|
+
callback()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
alert.querySelectorAll(".alert-ok").forEach((el) => {
|
|
137
|
+
el.addEventListener('click', alertOk)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
alert.querySelectorAll(".alert-close").forEach((el) => {
|
|
141
|
+
el.addEventListener('click', closeAlert)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
openAlert('confirm')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
showAlert,
|
|
149
|
+
showInfo,
|
|
150
|
+
showConfirm
|
|
151
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport to=".alert-overlay">
|
|
3
|
+
<Transition name="alert-outer" @after-leave="onAfterLeave">
|
|
4
|
+
<div v-if="state" :class="$style.alert" ref="alert">
|
|
5
|
+
<Transition name="alert" @after-leave="onAfterLeave" appear>
|
|
6
|
+
<slot></slot>
|
|
7
|
+
</Transition>
|
|
8
|
+
</div>
|
|
9
|
+
</Transition>
|
|
10
|
+
</Teleport>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script>
|
|
14
|
+
|
|
15
|
+
export default{
|
|
16
|
+
|
|
17
|
+
props: {
|
|
18
|
+
state: [ Boolean ],
|
|
19
|
+
default: 0
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
methods: {
|
|
23
|
+
|
|
24
|
+
onAfterLeave(){
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<style module>
|
|
35
|
+
|
|
36
|
+
.alert{
|
|
37
|
+
@apply fixed top-0 left-0 flex w-screen h-screen items-center justify-center bg-black/70 backdrop-blur-md;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.alert>*{
|
|
41
|
+
@apply w-[480px] max-w-[80vw] max-h-[50vh] overflow-y-auto bg-base-600 rounded-xl;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
</style>
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
<style>
|
|
48
|
+
|
|
49
|
+
.alert-outer-enter-active,
|
|
50
|
+
.alert-outer-leave-active {
|
|
51
|
+
transition: opacity 100ms cubic-bezier(0.25, 1, 0.5, 1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.alert-outer-enter-from,
|
|
55
|
+
.alert-outer-leave-to {
|
|
56
|
+
opacity: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.alert-enter-active,
|
|
60
|
+
.alert-leave-active {
|
|
61
|
+
transition: all 300ms cubic-bezier(0.25, 1, 0.5, 1);
|
|
62
|
+
transform: translate3d(0, 0, 0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.alert-enter-from,
|
|
66
|
+
.alert-leave-to {
|
|
67
|
+
transform: translate3d(0, 50px, 0);
|
|
68
|
+
opacity: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
</style>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button :class="computedClass" :disabled="state === -1">
|
|
3
|
+
<slot v-if="state !== 2"></slot>
|
|
4
|
+
<svg v-else-if="state === 2" class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
5
|
+
<circle :class="$style.svgBg" cx="12" cy="12" r="10" stroke-width="4"></circle>
|
|
6
|
+
<path :class="$style.svgHg" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
7
|
+
</svg>
|
|
8
|
+
</button>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
|
|
13
|
+
export default{
|
|
14
|
+
|
|
15
|
+
props: {
|
|
16
|
+
|
|
17
|
+
type: {
|
|
18
|
+
type: [ String, Number ],
|
|
19
|
+
default: "primary"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
state: {
|
|
23
|
+
type: [ String, Number ],
|
|
24
|
+
default: 1
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
computed:{
|
|
30
|
+
|
|
31
|
+
computedClass(){
|
|
32
|
+
|
|
33
|
+
const className = []
|
|
34
|
+
|
|
35
|
+
switch(this.type){
|
|
36
|
+
case 1:
|
|
37
|
+
case 'primary':
|
|
38
|
+
className.push(this.$style.primary)
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
case 2:
|
|
42
|
+
case 'secondary':
|
|
43
|
+
className.push(this.$style.secondary)
|
|
44
|
+
break
|
|
45
|
+
|
|
46
|
+
case 3:
|
|
47
|
+
case 'outline':
|
|
48
|
+
className.push(this.$style.outline)
|
|
49
|
+
break
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
switch(this.state){
|
|
53
|
+
case 2:
|
|
54
|
+
case 'loading':
|
|
55
|
+
className.push(this.$style.loading)
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return className.join(' ')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<style module>
|
|
69
|
+
|
|
70
|
+
.primary{
|
|
71
|
+
@apply bg-primary-500 p-2 text-white rounded-md border-[2px] border-primary-500;
|
|
72
|
+
@apply hover:bg-primary-600 hover:border-primary-600;
|
|
73
|
+
@apply active:top-[1px] active:left-[1px] active:relative;
|
|
74
|
+
@apply disabled:opacity-50 disabled:top-0 disabled:left-0 disabled:cursor-not-allowed;
|
|
75
|
+
}
|
|
76
|
+
.primary *{
|
|
77
|
+
@apply text-white fill-white;
|
|
78
|
+
}
|
|
79
|
+
.primary.loading{
|
|
80
|
+
@apply top-0 left-0 hover:bg-primary-500 hover:border-primary-500;
|
|
81
|
+
}
|
|
82
|
+
.primary.loading *{
|
|
83
|
+
fill: transparent;
|
|
84
|
+
}
|
|
85
|
+
.primary .svgBg{
|
|
86
|
+
stroke: var(--text);
|
|
87
|
+
stroke-opacity: 25%;
|
|
88
|
+
}
|
|
89
|
+
.primary .svgHg{
|
|
90
|
+
fill: #fff;
|
|
91
|
+
fill-opacity: 75%;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.outline{
|
|
95
|
+
@apply flex items-center justify-center;
|
|
96
|
+
@apply bg-transparent p-2 text-primary-500 rounded-md border-[2px] border-primary-500;
|
|
97
|
+
@apply hover:border-primary-300 hover:text-primary-300;
|
|
98
|
+
@apply active:top-[1px] active:left-[1px] active:relative;
|
|
99
|
+
@apply disabled:opacity-50 disabled:top-0 disabled:left-0 disabled:cursor-not-allowed;
|
|
100
|
+
@apply disabled:hover:text-primary-500 disabled:hover:border-primary-500;
|
|
101
|
+
}
|
|
102
|
+
.outline *{
|
|
103
|
+
@apply text-primary-500 fill-primary-500;
|
|
104
|
+
}
|
|
105
|
+
.outline.loading{
|
|
106
|
+
@apply top-0 left-0 hover:border-primary-500 hover:text-primary-500;
|
|
107
|
+
}
|
|
108
|
+
.outline.loading *{
|
|
109
|
+
fill: transparent;
|
|
110
|
+
}
|
|
111
|
+
.outline .svgBg{
|
|
112
|
+
stroke: var(--primary);
|
|
113
|
+
stroke-opacity: 25%;
|
|
114
|
+
}
|
|
115
|
+
.outline .svgHg{
|
|
116
|
+
fill: var(--primary);
|
|
117
|
+
fill-opacity: 75%;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.secondary{
|
|
121
|
+
@apply flex items-center justify-center;
|
|
122
|
+
@apply bg-secondary-500 p-2 px-4 text-white rounded-md border-[2px] border-secondary-500;
|
|
123
|
+
@apply hover:bg-secondary-600 hover:border-secondary-600;
|
|
124
|
+
@apply active:top-[1px] active:left-[1px] active:relative;
|
|
125
|
+
@apply disabled:opacity-50 disabled:top-0 disabled:left-0 disabled:cursor-not-allowed;
|
|
126
|
+
@apply disabled:hover:bg-secondary-500 disabled:hover:border-secondary-500;
|
|
127
|
+
}
|
|
128
|
+
.secondary *{
|
|
129
|
+
@apply text-white fill-white;
|
|
130
|
+
}
|
|
131
|
+
.secondary.loading{
|
|
132
|
+
@apply top-0 left-0 hover:bg-secondary-500 hover:border-secondary-500;
|
|
133
|
+
}
|
|
134
|
+
.secondary.loading *{
|
|
135
|
+
fill: transparent;
|
|
136
|
+
}
|
|
137
|
+
.secondary .svgBg{
|
|
138
|
+
stroke: var(--text);
|
|
139
|
+
stroke-opacity: 25%;
|
|
140
|
+
}
|
|
141
|
+
.secondary .svgHg{
|
|
142
|
+
fill: #fff;
|
|
143
|
+
fill-opacity: 75%;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
</style>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="button-group">
|
|
3
|
+
<div class="flex items-center" @click="showPopup">
|
|
4
|
+
<div class="flex-1">
|
|
5
|
+
{{ text }}
|
|
6
|
+
</div>
|
|
7
|
+
<div>
|
|
8
|
+
<slot name="icon"></slot>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="bg-popup">
|
|
12
|
+
<slot></slot>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
|
|
21
|
+
name: "ButtonGroup",
|
|
22
|
+
|
|
23
|
+
props: {
|
|
24
|
+
text: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: ""
|
|
27
|
+
},
|
|
28
|
+
anchor: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: "left"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
methods: {
|
|
35
|
+
|
|
36
|
+
hidePopup(){
|
|
37
|
+
|
|
38
|
+
const popup = this.$el.querySelector('.bg-popup')
|
|
39
|
+
const transitionEnd = () => {
|
|
40
|
+
popup.classList.remove('on')
|
|
41
|
+
popup.removeEventListener('transitionend', transitionEnd)
|
|
42
|
+
window.removeEventListener('click', this.hidePopup)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
popup.addEventListener('transitionend', transitionEnd)
|
|
46
|
+
popup.classList.remove('active')
|
|
47
|
+
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
showPopup(e){
|
|
51
|
+
|
|
52
|
+
e.preventDefault()
|
|
53
|
+
e.stopPropagation()
|
|
54
|
+
|
|
55
|
+
const popup = this.$el.querySelector('.bg-popup')
|
|
56
|
+
popup.style.top = (this.$el.clientHeight + 10) + "px"
|
|
57
|
+
popup.style.left = this.anchor === 'left' ? 0 : ''
|
|
58
|
+
popup.style.right = this.anchor === 'right' ? 0 : ''
|
|
59
|
+
popup.classList.add('on')
|
|
60
|
+
|
|
61
|
+
window.setTimeout(() => {
|
|
62
|
+
popup.classList.add('active')
|
|
63
|
+
}, 100)
|
|
64
|
+
|
|
65
|
+
window.addEventListener('click', this.hidePopup)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<style>
|
|
75
|
+
|
|
76
|
+
.button-group{
|
|
77
|
+
position: relative;
|
|
78
|
+
@apply inline-block;
|
|
79
|
+
@apply cursor-pointer;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.bg-popup{
|
|
83
|
+
display: none;
|
|
84
|
+
position: absolute;
|
|
85
|
+
background: #fff;
|
|
86
|
+
}
|
|
87
|
+
.bg-popup.on{
|
|
88
|
+
display: block;
|
|
89
|
+
opacity: 0;
|
|
90
|
+
transform: translate3d(0, -10%, 0);
|
|
91
|
+
transition: all 200ms cubic-bezier(0.25, 1, 0.5, 1);
|
|
92
|
+
}
|
|
93
|
+
.bg-popup.active {
|
|
94
|
+
opacity: 1;
|
|
95
|
+
transform: translate3d(0, 0, 0);
|
|
96
|
+
}
|
|
97
|
+
.bg-popup button{
|
|
98
|
+
white-space: nowrap;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
</style>
|