init
commit
5673d107e7
|
@ -0,0 +1,16 @@
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
.idea
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
|
||||||
|
|
||||||
|
build
|
||||||
|
build/**
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
node_modules/**
|
||||||
|
|
||||||
|
package-lock.json
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug Main Process",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||||
|
"windows": {
|
||||||
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||||
|
},
|
||||||
|
"args": ["."]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,4 @@
|
||||||
|
## E-pub Reader
|
||||||
|
> E-pub Reader, 它不是功能最强的, 但可能是UI最耐看的, 最适合看技术类电子书的。
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"name": "org.bytedo.epub",
|
||||||
|
"version": "2.1.2",
|
||||||
|
"description": "E-pub Reader",
|
||||||
|
"main": "src/main.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "electron .",
|
||||||
|
"pack": "electron-builder",
|
||||||
|
"pack:mac": "electron-builder --mac"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "yutent",
|
||||||
|
"email": "yutent.io@gmail.com"
|
||||||
|
},
|
||||||
|
"homepage": "https://yutent.top",
|
||||||
|
"license": "MIT",
|
||||||
|
"build": {
|
||||||
|
"appId": "org.bytedo.epub",
|
||||||
|
"productName": "E-pub Reader",
|
||||||
|
"copyright": "Copyright © 2019 ${author}",
|
||||||
|
"directories": {
|
||||||
|
"buildResources": "icons",
|
||||||
|
"output": "build"
|
||||||
|
},
|
||||||
|
"electronDownload": {
|
||||||
|
"version": "11.0.4",
|
||||||
|
"mirror": "https://npm.taobao.org/mirrors/electron/"
|
||||||
|
},
|
||||||
|
"files": ["src/**/*", "node_modules/iofs/*"],
|
||||||
|
"mac": {
|
||||||
|
"category": "public.app-category.developer-tools",
|
||||||
|
"target": "dmg",
|
||||||
|
"icon": "icons/app.icns",
|
||||||
|
"darkModeSupport": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron": "11.0.4",
|
||||||
|
"electron-builder": "^22.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"iofs": "^1.3.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
html{font-size:12.8px;width:100%;height:100vh}body{overflow:hidden;display:flex;flex-direction:column;width:100%;height:100%;line-height:1.25;font-size:14px;color:var(--color-dark-1);background:rgba(255,255,255,0.3)}.app-drag{-webkit-app-region:drag;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.app-nodrag{-webkit-app-region:no-drag}.app{position:relative;display:flex;height:100%}.app .sidebar{display:flex;flex-direction:column;justify-content:space-between;width:76px;height:100%;padding:48px 22px 24px;background:var(--color-dark-1);color:var(--color-plain-1)}.app .sidebar .item{cursor:pointer}.app .sidebar .item:hover,.app .sidebar .item.active{color:var(--color-orange-1)}.app .sidebar .item:active{color:var(--color-orange-2)}.app .select-box{display:flex;flex-direction:column;width:200px;height:100%;background:rgba(255,255,255,0.5)}.app .select-box .form{display:flex;align-items:center;width:100%;height:35px;padding:0 6px;background:#fff;border-bottom:1px solid var(--color-plain-2)}.app .select-box .form wc-input{flex:1}.app .select-box .list{flex:1}.app .select-box .list .item{display:flex;flex-direction:column;justify-content:center;height:48px;padding:6px;border-bottom:1px solid rgba(200,200,200,0.3);transition:color 0.15s ease-in-out, background 0.15s ease-in-out;cursor:pointer}.app .select-box .list .item section{display:flex;justify-content:space-between;align-items:center}.app .select-box .list .item cite{color:var(--color-grey-2)}.app .select-box .list .item .percent{padding:0 4px;border-radius:2px;font-size:12px;font-weight:bold;color:var(--color-grey-1)}.app .select-box .list .item .percent.red{color:var(--color-red-1)}.app .select-box .list .item .percent.green{color:var(--color-green-3)}.app .select-box .list .item:last-child{border-bottom:0}.app .select-box .list .item:hover{color:var(--color-blue-1);background:rgba(255,255,255,0.7)}.app .select-box .list .item.active{color:var(--color-plain-1);background:var(--color-blue-1)}.app .select-box .list .item.active cite{color:inherit}.app .select-box .list .item.active .percent{color:#fff}.app .detail{position:relative;flex:1;height:100%;border-left:1px solid var(--color-plain-2);background:#fff}.app .detail .title{display:flex;justify-content:space-between;align-items:center;width:100%;height:35px;padding:0 16px;font-size:16px;font-weight:bold}.app .detail .title span{display:inline-flex}.app .detail .title wc-button{margin:0 6px}.app .detail .card{width:96%;padding:12px 12px 16px;margin:12px 2% 24px;border:0;background:#fff;box-shadow:0 0 8px rgba(0,0,0,0.075)}.app .detail .card legend{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#64b5f6}.app .detail.blur::after{display:flex;justify-content:center;align-items:center;position:absolute;left:0;top:0;z-index:999;width:100%;height:100%;content:'搞基数据';background:#fff;color:#f7f8fb;font-size:100px}.app .preferences{width:640px;height:360px;border-radius:10px}.app .preferences .titlebar{width:100%;height:72px;border-bottom:1px solid var(--color-plain-3);background:var(--color-plain-1)}.app .preferences .titlebar .title{width:100%;height:24px;line-height:24px;text-align:center}.app .preferences .titlebar nav{display:flex;width:100%;height:40px;padding:0 16px;--size: 18px}.app .preferences .titlebar nav span{display:flex;flex-direction:column;align-items:center;justify-content:center;width:52px;height:40px;margin:0 6px;border-radius:6px;font-size:12px}.app .preferences .titlebar nav span.active{background:var(--color-plain-2);color:var(--color-blue-1)}.app .preferences .titlebar nav span:hover{background:var(--color-plain-2)}.app .preferences .tab-panel{padding:64px}.app .preferences .tab-panel p{margin-bottom:16px}.app .preferences .tab-panel .field{display:flex;align-items:center;height:64px}.app .preferences .tab-panel .field .label{width:200px;color:var(--color-grey-1)}.app.loading::after{display:flex;justify-content:center;align-items:center;position:absolute;left:0;top:0;z-index:999;width:100%;height:100%;content:'🌑';background:rgba(255,255,255,0.01);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);color:var(--color-orange-1);font-size:50px;-webkit-animation:loading 1s infinite;animation:loading 1s infinite}@-webkit-keyframes loading{1%,100%{content:'🌑'}12.5%{content:'🌒'}25%{content:'🌓'}37.5%{content:'🌔'}50%{content:'🌕'}62.5%{content:'🌖'}75%{content:'🌗'}87.5%{content:'🌘'}}@keyframes loading{1%,100%{content:'🌑'}12.5%{content:'🌒'}25%{content:'🌓'}37.5%{content:'🌔'}50%{content:'🌕'}62.5%{content:'🌖'}75%{content:'🌗'}87.5%{content:'🌘'}}
|
|
@ -0,0 +1,319 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
/**
|
||||||
|
* 主窗口样式
|
||||||
|
* @authors yutent<yutent@doui.cc>
|
||||||
|
* @date 2018/12/16 17:15:07
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 12.8px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 1.25;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-dark-1);
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-drag {
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.app-nodrag {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 76px;
|
||||||
|
height: 100%;
|
||||||
|
padding: 48px 22px 24px;
|
||||||
|
background: var(--color-dark-1);
|
||||||
|
color: var(--color-plain-1);
|
||||||
|
|
||||||
|
.item {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
color: var(--color-orange-1);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
color: var(--color-orange-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 200px;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 35px;
|
||||||
|
padding: 0 6px;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid var(--color-plain-2);
|
||||||
|
|
||||||
|
wc-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
height: 48px;
|
||||||
|
padding: 6px;
|
||||||
|
border-bottom: 1px solid rgba(200, 200, 200, 0.3);
|
||||||
|
transition: color 0.15s ease-in-out, background 0.15s ease-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
cite {
|
||||||
|
color: var(--color-grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.percent {
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-grey-1);
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
color: var(--color-red-1);
|
||||||
|
}
|
||||||
|
&.green {
|
||||||
|
color: var(--color-green-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-blue-1);
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: var(--color-plain-1);
|
||||||
|
background: var(--color-blue-1);
|
||||||
|
|
||||||
|
cite {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.percent {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
border-left: 1px solid var(--color-plain-2);
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 35px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
wc-button {
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 96%;
|
||||||
|
padding: 12px 12px 16px;
|
||||||
|
margin: 12px 2% 24px;
|
||||||
|
border: 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.075);
|
||||||
|
|
||||||
|
legend {
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
user-select: none;
|
||||||
|
color: #64b5f6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.blur {
|
||||||
|
&::after {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 999;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
content: '搞基数据';
|
||||||
|
background: #fff;
|
||||||
|
color: #f7f8fb;
|
||||||
|
font-size: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preferences {
|
||||||
|
width: 640px;
|
||||||
|
height: 360px;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
.titlebar {
|
||||||
|
width: 100%;
|
||||||
|
height: 72px;
|
||||||
|
border-bottom: 1px solid var(--color-plain-3);
|
||||||
|
background: var(--color-plain-1);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 52px;
|
||||||
|
height: 40px;
|
||||||
|
margin: 0 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--color-plain-2);
|
||||||
|
color: var(--color-blue-1);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-plain-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-panel {
|
||||||
|
padding: 64px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 64px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 200px;
|
||||||
|
color: var(--color-grey-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
&::after {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 999;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
content: '🌑';
|
||||||
|
background: rgba(255, 255, 255, 0.01);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
color: var(--color-orange-1);
|
||||||
|
font-size: 50px;
|
||||||
|
animation: loading 1s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
1%,
|
||||||
|
100% {
|
||||||
|
content: '🌑';
|
||||||
|
}
|
||||||
|
12.5% {
|
||||||
|
content: '🌒';
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
content: '🌓';
|
||||||
|
}
|
||||||
|
37.5% {
|
||||||
|
content: '🌔';
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
content: '🌕';
|
||||||
|
}
|
||||||
|
62.5% {
|
||||||
|
content: '🌖';
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
content: '🌗';
|
||||||
|
}
|
||||||
|
87.5% {
|
||||||
|
content: '🌘';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
html{font-size:12.8px;width:100%;height:100vh}body{display:flex;flex-direction:column;width:100%;height:100%;line-height:1.25;font-size:14px;color:var(--color-dark-1);background:rgba(255,255,255,0.3)}.app-drag{-webkit-app-region:drag;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.app-nodrag{-webkit-app-region:no-drag}.app{position:relative;display:flex;flex-direction:column;height:100%;padding:6px 0}.app .list{flex:1}.app .list .item{display:flex;align-items:center;height:54px;padding:10px 12px;line-height:15px;border-bottom:1px solid rgba(155,155,155,0.3)}.app .list .item:last-child{border-bottom:0}.app .list .item .info{overflow:hidden;flex:1}.app .list .item .info h3{font-size:14px}.app .list .item .info cite{color:var(--color-grey-2)}.app .list .item .last-days{display:flex;width:64px;height:30px;margin:0 6px}.app .list .item .today{width:52px;font-size:12px;text-align:right}.app .list .item .today span{display:block;padding:0 4px}.app .list .item .today .percent{border-radius:2px;color:#fff}.app .list .item .today .percent.red{background:var(--color-red-1)}.app .list .item .today .percent.green{background:var(--color-green-3)}
|
|
@ -0,0 +1,97 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
/**
|
||||||
|
* 浮窗样式
|
||||||
|
* @authors yutent<yutent@doui.cc>
|
||||||
|
* @date 2018/12/16 17:15:07
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 12.8px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 1.25;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-dark-1);
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-drag {
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.app-nodrag {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 6px 0;
|
||||||
|
|
||||||
|
.list {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 54px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
line-height: 15px;
|
||||||
|
border-bottom: 1px solid rgba(155, 155, 155, 0.3);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
cite {
|
||||||
|
color: var(--color-grey-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-days {
|
||||||
|
display: flex;
|
||||||
|
width: 64px;
|
||||||
|
height: 30px;
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.today {
|
||||||
|
width: 52px;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
.percent {
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
background: var(--color-red-1);
|
||||||
|
}
|
||||||
|
&.green {
|
||||||
|
background: var(--color-green-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 257 B |
Binary file not shown.
After Width: | Height: | Size: 229 B |
|
@ -0,0 +1,120 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
|
||||||
|
<title></title>
|
||||||
|
<link href="/lib/css/reset-basic.css" rel="stylesheet">
|
||||||
|
<link href="/css/app.css" rel="stylesheet">
|
||||||
|
<script src="/js/app.js" type="module"></script>
|
||||||
|
</head>
|
||||||
|
<body class="noselect">
|
||||||
|
|
||||||
|
<div class="app" anot="app" :class="{loading: loading}">
|
||||||
|
|
||||||
|
<aside class="sidebar app-drag">
|
||||||
|
<wc-icon class="app-nodrag item stat active" is="chart"></wc-icon>
|
||||||
|
<wc-icon class="app-nodrag item opt" is="menu-dot" @click="showPreferencesPanel"></wc-icon>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class="select-box">
|
||||||
|
<section class="form">
|
||||||
|
<wc-input
|
||||||
|
maxlength="6"
|
||||||
|
placeholder="输入编号搞新基"
|
||||||
|
round
|
||||||
|
@submit="addGay"
|
||||||
|
:duplex="input"
|
||||||
|
size="mini">
|
||||||
|
</wc-input>
|
||||||
|
</section>
|
||||||
|
<wc-scroll class="list">
|
||||||
|
<item
|
||||||
|
class="item"
|
||||||
|
:class="{active: curr.code === it.code}"
|
||||||
|
@click="viewGay(it)"
|
||||||
|
:for="it in list">
|
||||||
|
<strong class="text-ell" :text="it.name"></strong>
|
||||||
|
<section>
|
||||||
|
<cite :text="it.code"></cite>
|
||||||
|
<span
|
||||||
|
class="percent"
|
||||||
|
:class="{red: it.cp > 0, green: it.cp < 0}"
|
||||||
|
:text="it.cp + '%'">
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
|
</item>
|
||||||
|
</wc-scroll>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="detail" :class="{blur: !curr.code}">
|
||||||
|
<section class="title app-drag">
|
||||||
|
<span>[{{curr.code}}] {{curr.name}}</span>
|
||||||
|
<span>
|
||||||
|
<wc-button
|
||||||
|
circle
|
||||||
|
size="mini"
|
||||||
|
@click="removeGay"
|
||||||
|
icon="trash">
|
||||||
|
</wc-button>
|
||||||
|
<wc-button
|
||||||
|
circle
|
||||||
|
color="red"
|
||||||
|
size="mini"
|
||||||
|
@click="updateGays"
|
||||||
|
icon="eye">
|
||||||
|
</wc-button>
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<fieldset class="card">
|
||||||
|
<legend>实时数据</legend>
|
||||||
|
<wc-rank :attr-stat="curr.stat"></wc-rank>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="card">
|
||||||
|
<legend>单位净值走势</legend>
|
||||||
|
<wc-line :attr-list="curr.line"></wc-line>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<wc-layer ref="pre" mask mask-close radius="10px" >
|
||||||
|
<div class="preferences">
|
||||||
|
<div class="titlebar">
|
||||||
|
<div class="title">hello</div>
|
||||||
|
<nav>
|
||||||
|
<span :class="{active: preferences.tab === 1}" @click="switchTab(1)">
|
||||||
|
<wc-icon is="setting"></wc-icon>
|
||||||
|
常规
|
||||||
|
</span>
|
||||||
|
<span :class="{active: preferences.tab === 2}" @click="switchTab(2)">
|
||||||
|
<wc-icon is="info"></wc-icon>
|
||||||
|
关于
|
||||||
|
</span>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel" :visible="preferences.tab === 1">
|
||||||
|
<section class="field">
|
||||||
|
<span class="label">神奇的2点半提醒</span>
|
||||||
|
<wc-switch :duplex="preferences.notify"></wc-switch>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel" :visible="preferences.tab === 2">
|
||||||
|
<p>“搞基爱啪啪” 是一款开源的, 非专业的搞基软件, 上面的数据全来自网络, 不对准确性作任何保证. </p>
|
||||||
|
<p>搞基有风险, 入行需谨慎. 你亏了别找我, 赚了可以给我发红包.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</wc-layer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,311 @@
|
||||||
|
/**
|
||||||
|
* {sonist app}
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
* @author yutent<yutent@doui.cc>
|
||||||
|
* @date 2018/12/16 17:15:57
|
||||||
|
*/
|
||||||
|
|
||||||
|
import '/lib/anot.js'
|
||||||
|
import '/lib/form/button.js'
|
||||||
|
import '/lib/form/switch.js'
|
||||||
|
import '/lib/scroll/index.js'
|
||||||
|
import '/lib/chart/rank.js'
|
||||||
|
import '/lib/chart/line.js'
|
||||||
|
|
||||||
|
import layer from '/lib/layer/index.js'
|
||||||
|
import Utils from '/lib/utils.js'
|
||||||
|
|
||||||
|
import app from '/lib/socket.js'
|
||||||
|
|
||||||
|
const log = console.log
|
||||||
|
|
||||||
|
function getJsonp(str) {
|
||||||
|
if (~str.indexOf('jsonpgz')) {
|
||||||
|
return new Function(`function jsonpgz(d){return d}; return ${str}`)()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLineStat(str) {
|
||||||
|
return new Function(`${str}; return {line: Data_netWorthTrend.map(it => ({
|
||||||
|
x: ~~(it.x/1000),
|
||||||
|
y: +(it.y * 10000).toFixed(0),
|
||||||
|
p: it.equityReturn
|
||||||
|
})), e1: +syl_1y, e3: +syl_3y, e6: +syl_6y, e12: +syl_1n}`)()
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(_ => setTimeout(_, ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
window.app = app
|
||||||
|
|
||||||
|
Anot({
|
||||||
|
$id: 'app',
|
||||||
|
state: {
|
||||||
|
input: '',
|
||||||
|
curr: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
stat: '',
|
||||||
|
line: ''
|
||||||
|
},
|
||||||
|
list: [],
|
||||||
|
$dict: {},
|
||||||
|
loading: false,
|
||||||
|
preferences: {
|
||||||
|
tab: 1,
|
||||||
|
notify: Anot.ls('notify') === '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'preferences.notify'(v) {
|
||||||
|
Anot.ls('notify', v ^ 0)
|
||||||
|
if (v) {
|
||||||
|
app.dispatch('notify')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
var old = this.syncOldStat()
|
||||||
|
|
||||||
|
if (old === false) {
|
||||||
|
this.reloadGays()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.preferences.notify) {
|
||||||
|
app.dispatch('notify')
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('data-reload', data => {
|
||||||
|
this.reloadGays()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reloadGays() {
|
||||||
|
var gays = Anot.ls('gays') || '{}'
|
||||||
|
var list = []
|
||||||
|
gays = JSON.parse(gays)
|
||||||
|
|
||||||
|
for (let code in gays) {
|
||||||
|
let { name, cm, cp, t } = gays[code]
|
||||||
|
list.push({ code, name, cm, cp, t: t || 0 })
|
||||||
|
this.$dict[code] = 1
|
||||||
|
}
|
||||||
|
list.sort((a, b) => b.cp - a.cp)
|
||||||
|
|
||||||
|
this.list = list
|
||||||
|
},
|
||||||
|
|
||||||
|
syncOldStat() {
|
||||||
|
var old = Anot.ls('watch_list')
|
||||||
|
var list = []
|
||||||
|
var dict = {}
|
||||||
|
|
||||||
|
if (old) {
|
||||||
|
old = JSON.parse(old)
|
||||||
|
for (let it of old) {
|
||||||
|
dict[it.code] = {
|
||||||
|
name: it.name,
|
||||||
|
cm: +it.curr,
|
||||||
|
cp: it.percent,
|
||||||
|
t: Date.now()
|
||||||
|
}
|
||||||
|
list.push({ code: it.code, ...dict[it.code] })
|
||||||
|
}
|
||||||
|
|
||||||
|
list.sort((a, b) => b.cp - a.cp)
|
||||||
|
this.list = list
|
||||||
|
|
||||||
|
Anot.ls('gays', dict)
|
||||||
|
Anot.ls('watch_list', null)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
|
showPreferencesPanel() {
|
||||||
|
this.$refs.pre.show()
|
||||||
|
},
|
||||||
|
|
||||||
|
switchTab(n) {
|
||||||
|
this.preferences.tab = n
|
||||||
|
},
|
||||||
|
|
||||||
|
getGayStat(id) {
|
||||||
|
var res = app.dispatch(
|
||||||
|
'fetch',
|
||||||
|
`https://fundgz.1234567.com.cn/js/${id}.js`
|
||||||
|
)
|
||||||
|
return getJsonp(res)
|
||||||
|
},
|
||||||
|
|
||||||
|
addGay() {
|
||||||
|
var code = this.input
|
||||||
|
var gay
|
||||||
|
|
||||||
|
if (this.$dict[code]) {
|
||||||
|
layer.toast('这个鸡精在列表呢~~~', 'warn')
|
||||||
|
this.input = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code === 'debug') {
|
||||||
|
this.input = ''
|
||||||
|
return app.dispatch('devtools')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code.length < 6) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/[^\d]/.test(code)) {
|
||||||
|
layer.toast('只能通过鸡精编号添加', 'error')
|
||||||
|
this.input = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gay = this.getGayStat(code)
|
||||||
|
|
||||||
|
if (gay) {
|
||||||
|
let tmp = {
|
||||||
|
code: gay.fundcode,
|
||||||
|
name: gay.name,
|
||||||
|
cm: +gay.gsz,
|
||||||
|
cp: +gay.gszzl,
|
||||||
|
t: Date.now()
|
||||||
|
}
|
||||||
|
this.input = ''
|
||||||
|
this.list.push(tmp)
|
||||||
|
this.$dict[tmp.code] = 1
|
||||||
|
this.list.sort((a, b) => b.cp - a.cp)
|
||||||
|
this.saveCache()
|
||||||
|
} else {
|
||||||
|
layer.toast('鸡精不存在', 'error')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateGays() {
|
||||||
|
var { code, stat } = this.curr
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
for (let it of this.list) {
|
||||||
|
//
|
||||||
|
let info = this.getGayStat(it.code)
|
||||||
|
let time, needUpdate
|
||||||
|
|
||||||
|
it.cm = +info.gsz
|
||||||
|
it.cp = +info.gszzl
|
||||||
|
|
||||||
|
time = new Date(info.gztime.slice(0, 10) + ' 00:00:00')
|
||||||
|
time = ~~(time.getTime() / 1000) - 24 * 3600
|
||||||
|
|
||||||
|
// 如果走势最后的日期比当前最新的小, 则全量更新
|
||||||
|
if (it.t < time) {
|
||||||
|
it.t = time
|
||||||
|
needUpdate = this.updateLine(it.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.code === code) {
|
||||||
|
stat = JSON.parse(stat)
|
||||||
|
stat.cm = it.cm
|
||||||
|
stat.cp = it.cp
|
||||||
|
if (needUpdate) {
|
||||||
|
stat.rank = needUpdate.line.slice(-60).map(_ => _.p)
|
||||||
|
stat.e1 = needUpdate.e1
|
||||||
|
stat.e3 = needUpdate.e3
|
||||||
|
stat.e6 = needUpdate.e6
|
||||||
|
stat.e12 = needUpdate.e12
|
||||||
|
this.curr.line = JSON.stringify(needUpdate.line)
|
||||||
|
}
|
||||||
|
this.curr.stat = JSON.stringify(stat)
|
||||||
|
}
|
||||||
|
await sleep(500)
|
||||||
|
}
|
||||||
|
//
|
||||||
|
this.loading = false
|
||||||
|
Anot.ss('last_update', Date.now())
|
||||||
|
layer.toast('数据更新成功', 'success')
|
||||||
|
this.list.sort((a, b) => b.cp - a.cp)
|
||||||
|
this.saveCache()
|
||||||
|
},
|
||||||
|
|
||||||
|
removeGay() {
|
||||||
|
var { code, name } = this.curr
|
||||||
|
layer
|
||||||
|
.confirm(`是否移除「${name}」?`)
|
||||||
|
.then(_ => {
|
||||||
|
for (let it of this.list) {
|
||||||
|
if (it.code === code) {
|
||||||
|
this.list.remove(it)
|
||||||
|
delete this.$dict[code]
|
||||||
|
Anot.ls(code, null)
|
||||||
|
this.saveCache()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.viewGay(this.list[0])
|
||||||
|
})
|
||||||
|
.catch(Anot.noop)
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCache() {
|
||||||
|
var dict = {}
|
||||||
|
for (let it of this.list) {
|
||||||
|
var { code, name, cm, cp, t } = it
|
||||||
|
dict[code] = { name, cm, cp, t }
|
||||||
|
}
|
||||||
|
Anot.ls('gays', dict)
|
||||||
|
},
|
||||||
|
|
||||||
|
updateLine(code) {
|
||||||
|
var gay = app.dispatch(
|
||||||
|
'fetch',
|
||||||
|
`http://fund.eastmoney.com/pingzhongdata/${code}.js?v=${Date.now()}`
|
||||||
|
)
|
||||||
|
gay = getLineStat(gay)
|
||||||
|
Anot.ls(code, JSON.stringify(gay))
|
||||||
|
return gay
|
||||||
|
},
|
||||||
|
|
||||||
|
viewGay(item) {
|
||||||
|
var gay = Anot.ls(item.code)
|
||||||
|
var rank, line
|
||||||
|
var { cm, cp, t } = item
|
||||||
|
|
||||||
|
this.curr.code = item.code
|
||||||
|
this.curr.name = item.name
|
||||||
|
|
||||||
|
if (gay) {
|
||||||
|
gay = JSON.parse(gay)
|
||||||
|
var last = gay.line[gay.line.length - 1].x
|
||||||
|
if (last < t) {
|
||||||
|
gay = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gay) {
|
||||||
|
gay = this.updateLine(item.code)
|
||||||
|
item.t = gay.line[gay.line.length - 1].x
|
||||||
|
this.saveCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
rank = gay.line.slice(-60).map(_ => _.p)
|
||||||
|
line = JSON.stringify(gay.line)
|
||||||
|
|
||||||
|
this.curr.stat = JSON.stringify({
|
||||||
|
rank,
|
||||||
|
e1: gay.e1,
|
||||||
|
e3: gay.e3,
|
||||||
|
e6: gay.e6,
|
||||||
|
e12: gay.e12,
|
||||||
|
cm,
|
||||||
|
cp
|
||||||
|
})
|
||||||
|
this.curr.line = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author yutent<yutent.io@gmail.com>
|
||||||
|
* @date 2020/12/10 19:53:05
|
||||||
|
*/
|
||||||
|
|
||||||
|
import '/lib/anot.js'
|
||||||
|
|
||||||
|
import '/lib/scroll/index.js'
|
||||||
|
import layer from '/lib/layer/index.js'
|
||||||
|
import app from '/lib/socket.js'
|
||||||
|
|
||||||
|
function getJsonp(str) {
|
||||||
|
if (~str.indexOf('jsonpgz')) {
|
||||||
|
return new Function(`function jsonpgz(d){return d}; return ${str}`)()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
Anot({
|
||||||
|
$id: 'app',
|
||||||
|
state: {
|
||||||
|
list: []
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.reloadGays()
|
||||||
|
|
||||||
|
app.on('float-visible', data => {
|
||||||
|
var time = +Anot.ss('last_update') || 0
|
||||||
|
var now = Date.now()
|
||||||
|
|
||||||
|
// 有触发小窗口显示时, 更新通知提醒
|
||||||
|
if (Anot.ls('notify') === '1') {
|
||||||
|
app.dispatch('notify')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadGays()
|
||||||
|
setTimeout(() => {
|
||||||
|
// 如果离上次更新超过15分钟, 则自动更新
|
||||||
|
if (now - time > 15 * 60 * 1000) {
|
||||||
|
this.updateGays()
|
||||||
|
Anot.ss('last_update', now)
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reloadGays() {
|
||||||
|
var gays = Anot.ls('gays') || '{}'
|
||||||
|
var list = []
|
||||||
|
|
||||||
|
gays = JSON.parse(gays)
|
||||||
|
|
||||||
|
for (let code in gays) {
|
||||||
|
let { name, cm, cp, t } = gays[code]
|
||||||
|
list.push({ code, name, cm, cp, t })
|
||||||
|
}
|
||||||
|
list.sort((a, b) => b.cp - a.cp)
|
||||||
|
|
||||||
|
this.list = list
|
||||||
|
},
|
||||||
|
|
||||||
|
getGayStat(id) {
|
||||||
|
var res = app.dispatch(
|
||||||
|
'fetch',
|
||||||
|
`https://fundgz.1234567.com.cn/js/${id}.js`
|
||||||
|
)
|
||||||
|
return getJsonp(res)
|
||||||
|
},
|
||||||
|
|
||||||
|
updateGay(item) {
|
||||||
|
var info = this.getGayStat(item.code)
|
||||||
|
item.cm = +info.gsz
|
||||||
|
item.cp = +info.gszzl
|
||||||
|
},
|
||||||
|
|
||||||
|
updateGays() {
|
||||||
|
for (let it of this.list) {
|
||||||
|
this.updateGay(it)
|
||||||
|
}
|
||||||
|
this.list.sort((a, b) => b.cp - a.cp)
|
||||||
|
this.saveCache()
|
||||||
|
app.dispatch('data-reload')
|
||||||
|
},
|
||||||
|
saveCache() {
|
||||||
|
var dict = {}
|
||||||
|
for (let it of this.list) {
|
||||||
|
var { code, name, cm, cp, t } = it
|
||||||
|
dict[code] = { name, cm, cp, t }
|
||||||
|
}
|
||||||
|
Anot.ls('gays', dict)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,253 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import $ from '../utils.js'
|
||||||
|
import '../form/button.js'
|
||||||
|
|
||||||
|
const DARK = '#62778d'
|
||||||
|
const BLUE = '#64b5f6'
|
||||||
|
const PLAIN = '#f2f5fc'
|
||||||
|
|
||||||
|
export default class Line extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['list']
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
list: []
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
key: 1,
|
||||||
|
list: []
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
width: 680px; }
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
padding: 24px 0 0; }
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 680px;
|
||||||
|
height: 230px; }
|
||||||
|
|
||||||
|
section {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0; }
|
||||||
|
</style>
|
||||||
|
<div class="container">
|
||||||
|
<canvas></canvas>
|
||||||
|
<section>
|
||||||
|
<wc-button color="blue" data-key="1" size="mini">1月</wc-button>
|
||||||
|
<wc-button data-key="3" size="mini">3月</wc-button>
|
||||||
|
<wc-button data-key="6" size="mini">半年</wc-button>
|
||||||
|
<wc-button data-key="12" size="mini">1年</wc-button>
|
||||||
|
<wc-button data-key="36" size="mini">3年</wc-button>
|
||||||
|
<wc-button data-key="999" size="mini">所有</wc-button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
var elem = this.root.children[1]
|
||||||
|
this.__SCENE__ = elem.firstElementChild
|
||||||
|
this.__FILTER__ = elem.lastElementChild
|
||||||
|
this.__CTX__ = this.__SCENE__.getContext('2d')
|
||||||
|
this.__SCENE__.width = 680
|
||||||
|
this.__SCENE__.height = 230
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTime(n) {
|
||||||
|
var now = new Date()
|
||||||
|
var time = { getTime: _ => 0 }
|
||||||
|
var Y = now.getFullYear()
|
||||||
|
var m = now.getMonth()
|
||||||
|
var d = now.getDate()
|
||||||
|
|
||||||
|
switch (n) {
|
||||||
|
case 1:
|
||||||
|
time = new Date(Y, m - 1, d, 0, 0, 0)
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
time = new Date(Y, m - 3, d, 0, 0, 0)
|
||||||
|
break
|
||||||
|
case 6:
|
||||||
|
time = new Date(Y, m - 6, d, 0, 0, 0)
|
||||||
|
break
|
||||||
|
case 12:
|
||||||
|
time = new Date(Y - 1, m, d, 0, 0, 0)
|
||||||
|
break
|
||||||
|
case 36:
|
||||||
|
time = new Date(Y - 3, m, d, 0, 0, 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return time.getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
_filter(n) {
|
||||||
|
if (n < 999) {
|
||||||
|
var time = this._getTime(n)
|
||||||
|
this.state.list = this.props.list.filter(it => it.x >= time)
|
||||||
|
} else {
|
||||||
|
this.state.list = this.props.list.concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
var { list, key } = this.state
|
||||||
|
var ctx = this.__CTX__
|
||||||
|
var x = 36
|
||||||
|
var max = 0
|
||||||
|
var min = Number.MAX_SAFE_INTEGER
|
||||||
|
var step = 0 // 纵坐标间隔
|
||||||
|
var dis = +(640 / list.length).toFixed(2) || 1 // 横坐标间隔
|
||||||
|
var point
|
||||||
|
var p1, p2, p3, p4
|
||||||
|
var format = key > 12 ? 'Y/m' : 'm/d'
|
||||||
|
|
||||||
|
for (let it of list) {
|
||||||
|
if (max < it.y) {
|
||||||
|
max = it.y
|
||||||
|
}
|
||||||
|
if (min > it.y) {
|
||||||
|
min = it.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min = ~~(min / 100)
|
||||||
|
max = Math.ceil(max / 100)
|
||||||
|
step = ~~((max - min) / 3)
|
||||||
|
|
||||||
|
p1 = Math.floor(list.length / 4)
|
||||||
|
p2 = Math.floor(list.length / 2)
|
||||||
|
p3 = Math.floor((list.length * 3) / 4)
|
||||||
|
p4 = list.length - 1
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, 680, 230)
|
||||||
|
|
||||||
|
// 纵坐标数值
|
||||||
|
ctx.font = '12px Arial'
|
||||||
|
ctx.textAlign = 'right'
|
||||||
|
ctx.fillStyle = DARK
|
||||||
|
ctx.fillText(min / 100, 32, 205)
|
||||||
|
ctx.fillText((min + step) / 100, 32, 155)
|
||||||
|
ctx.fillText((min + step + step) / 100, 32, 105)
|
||||||
|
ctx.fillText((min + step + step + step) / 100, 32, 55)
|
||||||
|
|
||||||
|
ctx.font = '10px Arial'
|
||||||
|
ctx.textAlign = 'left'
|
||||||
|
ctx.fillText(new Date(list[0].x).format(format), x - 12, 225)
|
||||||
|
ctx.fillText(new Date(list[p1].x).format(format), x + dis * p1 - 12, 225)
|
||||||
|
ctx.fillText(new Date(list[p2].x).format(format), x + dis * p2 - 12, 225)
|
||||||
|
ctx.fillText(new Date(list[p3].x).format(format), x + dis * p3 - 12, 225)
|
||||||
|
ctx.fillText(
|
||||||
|
new Date(list[p4].x).format(format),
|
||||||
|
x + dis * p4 - 12 - (key > 12 ? 24 : 4),
|
||||||
|
225
|
||||||
|
)
|
||||||
|
|
||||||
|
// x轴参考线
|
||||||
|
ctx.fillStyle = PLAIN
|
||||||
|
ctx.fillRect(x, 50, 648, 1)
|
||||||
|
ctx.fillRect(x, 100, 648, 1)
|
||||||
|
ctx.fillRect(x, 150, 648, 1)
|
||||||
|
ctx.fillRect(x, 200, 648, 1)
|
||||||
|
|
||||||
|
// y轴参考 线
|
||||||
|
ctx.fillRect(x, 0, 1, 210)
|
||||||
|
ctx.fillRect(x + dis * p1, 0, 1, 210)
|
||||||
|
ctx.fillRect(x + dis * p2, 0, 1, 210)
|
||||||
|
ctx.fillRect(x + dis * p3, 0, 1, 210)
|
||||||
|
ctx.fillRect(x + dis * p4, 0, 1, 210)
|
||||||
|
|
||||||
|
point = list.shift()
|
||||||
|
|
||||||
|
// 曲线
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.strokeStyle = BLUE
|
||||||
|
ctx.lineWidth = 1
|
||||||
|
ctx.moveTo(x, 200 - (((point.y / 100 - min) / step) * 50).toFixed(0))
|
||||||
|
|
||||||
|
while (list.length) {
|
||||||
|
let y
|
||||||
|
|
||||||
|
point = list.shift()
|
||||||
|
|
||||||
|
y = 200 - (((point.y / 100 - min) / step) * 50).toFixed(0)
|
||||||
|
x += dis
|
||||||
|
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
}
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
$.bind(this.__FILTER__, 'click', ev => {
|
||||||
|
var el = ev.target
|
||||||
|
if (this.props.list.length < 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (el.tagName === 'WC-BUTTON') {
|
||||||
|
var k = +el.dataset.key
|
||||||
|
|
||||||
|
$.each(this.__FILTER__.children, function(it) {
|
||||||
|
it.removeAttribute('color')
|
||||||
|
})
|
||||||
|
el.setAttribute('color', 'blue')
|
||||||
|
|
||||||
|
this.state.key = k
|
||||||
|
this._filter(k)
|
||||||
|
this.draw()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch (name) {
|
||||||
|
case 'list':
|
||||||
|
try {
|
||||||
|
var list = JSON.parse(val)
|
||||||
|
list.forEach(it => (it.x = it.x * 1000))
|
||||||
|
this.props.list = list
|
||||||
|
this._filter(this.state.key)
|
||||||
|
this.removeAttribute('list')
|
||||||
|
this.draw()
|
||||||
|
} catch (e) {}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!customElements.get('wc-line')) {
|
||||||
|
customElements.define('wc-line', Line)
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const RED = '#ff5061'
|
||||||
|
const GREEN = '#4caf50'
|
||||||
|
const BLUE = '#64b5f6'
|
||||||
|
const GREY = '#bdbdbd'
|
||||||
|
const PLAIN = '#f2f5fc'
|
||||||
|
const DARK = '#62778d'
|
||||||
|
|
||||||
|
export default class Rank extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['stat']
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
stat: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: flex; }
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 680px;
|
||||||
|
height: 100px; }
|
||||||
|
</style>
|
||||||
|
<canvas></canvas>
|
||||||
|
`
|
||||||
|
|
||||||
|
this.__SCENE__ = this.root.children[1]
|
||||||
|
this.__CTX__ = this.__SCENE__.getContext('2d')
|
||||||
|
this.__SCENE__.width = 680
|
||||||
|
this.__SCENE__.height = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
var { rank, e1, e3, e6, e12, cm, cp } = this.props.stat
|
||||||
|
var ctx = this.__CTX__
|
||||||
|
var x = 32
|
||||||
|
|
||||||
|
while (rank.length < 60) {
|
||||||
|
rank.unshift(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, 680, 101)
|
||||||
|
|
||||||
|
ctx.font = '10px Arial'
|
||||||
|
ctx.textAlign = 'right'
|
||||||
|
ctx.fillStyle = RED
|
||||||
|
ctx.fillText('10%', 28, 10)
|
||||||
|
ctx.fillText('5%', 28, 30)
|
||||||
|
ctx.fillStyle = GREEN
|
||||||
|
ctx.fillText('-5%', 28, 80)
|
||||||
|
ctx.fillText('-10%', 28, 100)
|
||||||
|
|
||||||
|
ctx.font = '10px menlo,Hiragino Sans GB'
|
||||||
|
ctx.textAlign = 'left'
|
||||||
|
ctx.fillStyle = DARK
|
||||||
|
ctx.fillText('60天红绿榜', 160, 10)
|
||||||
|
|
||||||
|
ctx.font = '12px menlo,Hiragino Sans GB'
|
||||||
|
ctx.fillText(`最近1个月收益: ${e1}%`, 360, 25)
|
||||||
|
ctx.fillText(`最近3个月收益: ${e3}%`, 360, 45)
|
||||||
|
ctx.fillText(`最近半年收益: ${e6}%`, 528, 25)
|
||||||
|
ctx.fillText(`最近一年收益: ${e12}%`, 528, 45)
|
||||||
|
|
||||||
|
ctx.fillStyle = cp > 0 ? RED : cp === 0 ? GREY : GREEN
|
||||||
|
ctx.fillRect(360, 65, 140, 20)
|
||||||
|
ctx.fillRect(526, 65, 140, 20)
|
||||||
|
ctx.fillStyle = '#fff'
|
||||||
|
ctx.font = 'bold 14px menlo,Hiragino Sans GB'
|
||||||
|
ctx.fillText(`实时净值: ¥${cm}`, 364, 80)
|
||||||
|
ctx.fillText(`实时涨跌: ${cp}%`, 532, 80)
|
||||||
|
|
||||||
|
ctx.fillStyle = PLAIN
|
||||||
|
ctx.fillRect(28, 25, 320, 1)
|
||||||
|
ctx.fillRect(28, 75, 320, 1)
|
||||||
|
ctx.fillStyle = GREY
|
||||||
|
ctx.fillRect(28, 0, 1, 140)
|
||||||
|
ctx.fillRect(0, 50, 348, 1)
|
||||||
|
|
||||||
|
while (rank.length) {
|
||||||
|
var n = rank.shift()
|
||||||
|
var y = Math.ceil(50 - (n / 10) * 50)
|
||||||
|
|
||||||
|
ctx.fillStyle = n > 0 ? RED : GREEN
|
||||||
|
|
||||||
|
if (y > 50) {
|
||||||
|
ctx.fillRect(x, 50, 3, y - 50)
|
||||||
|
} else {
|
||||||
|
ctx.fillRect(x, y, 3, 50 - y)
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch (name) {
|
||||||
|
case 'stat':
|
||||||
|
try {
|
||||||
|
var stat = JSON.parse(val)
|
||||||
|
this.props.stat = stat
|
||||||
|
this.removeAttribute('stat')
|
||||||
|
this.draw()
|
||||||
|
} catch (e) {}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!customElements.get('wc-rank')) {
|
||||||
|
customElements.define('wc-rank', Rank)
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2014-10-10 00:45:09
|
||||||
|
*
|
||||||
|
* doui的CSS规范
|
||||||
|
*
|
||||||
|
* 不能出现大写,以连字符风格命名
|
||||||
|
* 表示状态的应该用do-st-*命名
|
||||||
|
* 表示功能的应该用do-fn-*命名
|
||||||
|
* 表示页面模块的应该用do-mod-modname 命名
|
||||||
|
* 表示UI组件的应该用do-uiname命名, 它的子元素应该全部包在 .do-uiname这个根类下
|
||||||
|
* 如 .do-layer .body { ... }
|
||||||
|
*
|
||||||
|
* 样式规则的出现顺序
|
||||||
|
* 1 display float position overflow z-index 表示定位/布局的属性
|
||||||
|
* 2 width height margin padding border 表示盒子模型的属性
|
||||||
|
* 3 line-height font-size vertical-align text-align user-select outline ....排版相关的属性
|
||||||
|
* 4 color background opacity cursor ...表示装饰相关的属性
|
||||||
|
* 5 content list-style quotes ... 内容生成相关的属性
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
* {margin: 0;padding: 0;vertical-align: baseline;box-sizing:border-box;}
|
||||||
|
::before,::after {box-sizing:border-box;}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section,content {display: block;}
|
||||||
|
img {border:0;display:inline-block;}
|
||||||
|
ol, ul {list-style: none;}
|
||||||
|
blockquote, q {quotes: none;}
|
||||||
|
blockquote::before, blockquote::after,
|
||||||
|
q::before, q::after {content: '';content: none;}
|
||||||
|
table {border-collapse: collapse;border-spacing: 0;}
|
||||||
|
a:focus,input,textarea,button:focus,input:focus,textarea:focus {outline:none;}
|
||||||
|
::-moz-focus-inner {
|
||||||
|
border:none;outline:none;
|
||||||
|
}
|
||||||
|
body {font-family:"Helvetica Neue", Arial,"WenQuanYi Micro Hei","PingFang SC","Hiragino Sans GB","Segoe UI", "Microsoft Yahei", sans-serif;-webkit-font-smoothing: antialiased;text-size-adjust: 100%;-webkit-tap-highlight-color: transparent;}
|
||||||
|
code,pre,samp {font-family:Menlo,Monaco,Consolas,"Courier New",monospace;}
|
||||||
|
[anot],[\:repeat],[\:if] {visibility:hidden;}
|
||||||
|
|
||||||
|
|
||||||
|
.noselect {-webkit-touch-callout: none;-webkit-user-select: none;-moz-user-select: none;user-select: none;}
|
||||||
|
.noselect img, .noselect a {-webkit-user-drag:none;}
|
||||||
|
.text-ell {overflow:hidden; white-space:nowrap; text-overflow:ellipsis }
|
||||||
|
.osx-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
"use strict";import $ from"../utils.js";const DEF_OPT={axis:"",limit:!1,overflow:!0};export default class Drag{constructor(t){this.$elem=t,this._init()}_init(){this.$elem.style.transform="";var{x:t,y:s}=this.$elem.getBoundingClientRect();this.pos={x:t,y:s,_x:0,_y:0}}by(t,s={}){return this.$drag=t,this.opt=Object.assign(Object.create(null),DEF_OPT,s),!1!==this.opt.limit&&(this.opt.overflow=!1),t.style.cursor="move",this._handleResize=$.bind(window,"resize",this._init.bind(this)),this._handleMousedown=$.bind(t,"mousedown",t=>{if(this.disabled)return;var s=this.$elem.getBoundingClientRect();s.x-this.pos._x!==this.pos.x&&(this.pos.x=s.x-this.pos._x),s.y-this.pos._y!==this.pos.y&&(this.pos.y=s.y-this.pos._y);let e=t.pageX,i=t.pageY,o=document.documentElement.clientWidth,n=document.documentElement.clientHeight,h=s.width,p=s.height,d=[0,o-h,n-p,0];if("parent"===this.opt.limit){let t=this.$elem.parentNode.getBoundingClientRect();d=[t.top,t.right-h,t.bottom-p,t.left]}let l=$.bind(document,"mousemove",t=>{t.preventDefault();let o=t.pageX-e+(s.x-this.pos.x),n=t.pageY-i+(s.y-this.pos.y);"x"===this.opt.axis&&(n=0),"y"===this.opt.axis&&(o=0),!1===this.opt.overflow&&(o<d[3]-this.pos.x?o=d[3]-this.pos.x:o>d[1]-this.pos.x&&(o=d[1]-this.pos.x),n<d[0]-this.pos.y?n=d[0]-this.pos.y:n>d[2]-this.pos.y&&(n=d[2]-this.pos.y)),this.pos._x=o,this.pos._y=n,this.$elem.dispatchEvent(new CustomEvent("dragging",{detail:{offset:{x:this.pos.x+o,y:this.pos.y+n},move:{x:o,y:n}}})),this.$elem.style.transform=`translate(${o}px, ${n}px)`}),m=$.bind(document,"mouseup",t=>{this.$elem.dispatchEvent(new CustomEvent("dragged",{detail:{offset:{x:this.pos.x+this.pos._x,y:this.pos.y+this.pos._y},move:{x:this.pos._x,y:this.pos._y}}})),$.unbind(document,"mousemove",l),$.unbind(document,"mouseup",m)})}),this}on(t,s){if(t&&"function"==typeof s)return $.bind(this,t,s)}off(t,s){$.unbind(this,t,s)}destroy(){$.unbind(window,"resize",this._handleResize),$.unbind(this.$drag,"mousedown",this._handleMousedown),delete this.$elem,delete this.$drag}};
|
|
@ -0,0 +1 @@
|
||||||
|
"use strict";import Drag from"./core.js";Anot.directive("drag",{priority:1500,init:function(e){e.expr='"'+e.expr+'"',e.overflow=!0,e.axis="xy",e.element.dataset.axis&&(e.axis=e.element.dataset.axis,delete e.element.dataset.axis),e.limit=!1,e.element.dataset.limit&&(e.limit=e.element.dataset.limit,e.overflow=!1,delete e.element.dataset.limit)},update:function(e){var t=this.element;if(e)for(t=this.element.parentNode;t;){if(t.classList||Anot.error(`${this.name}=${this.expr}, 解析异常[元素不存在]`),"WC-LAYER"===t.tagName&&"layer"===e){t=t.root.children[1];break}if(t.classList.contains(e)||t.id===e)break;t=t.parentNode}new Drag(t).by(this.element,{limit:this.limit,axis:this.axis,overflow:this.overflow})}});
|
|
@ -0,0 +1,484 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import "../icon/index.js"
|
||||||
|
import $ from "../utils.js"
|
||||||
|
|
||||||
|
const IS_FIREFOX = !!window.sidebar
|
||||||
|
|
||||||
|
export default class Button extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["icon","autofocus","loading","disabled","lazy"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
icon: '',
|
||||||
|
autofocus: '',
|
||||||
|
loading: false,
|
||||||
|
disabled: false,
|
||||||
|
lazy: 0 // 并发拦截时间, 单位毫秒
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 64px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 2px;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
color: var(--color-dark-2);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer; }
|
||||||
|
:host button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: inherit;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: auto;
|
||||||
|
line-height: 0;
|
||||||
|
border: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: inherit;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: #fff;
|
||||||
|
font-size: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
outline: none;
|
||||||
|
color: inherit;
|
||||||
|
cursor: inherit; }
|
||||||
|
:host button:hover {
|
||||||
|
background: var(--color-plain-1); }
|
||||||
|
:host button:active {
|
||||||
|
border-color: var(--color-grey-1); }
|
||||||
|
:host button::-moz-focus-inner {
|
||||||
|
border: none; }
|
||||||
|
:host .icon {
|
||||||
|
--size: 18px;
|
||||||
|
margin-right: 3px; }
|
||||||
|
|
||||||
|
:host([size='large']) {
|
||||||
|
min-width: 120px;
|
||||||
|
height: 42px;
|
||||||
|
font-size: 16px; }
|
||||||
|
:host([size='large']) .icon {
|
||||||
|
--size: 20px; }
|
||||||
|
|
||||||
|
:host([size='large'][circle]) {
|
||||||
|
min-width: 62px;
|
||||||
|
height: 62px; }
|
||||||
|
:host([size='large'][circle]) button {
|
||||||
|
padding: 0 20px; }
|
||||||
|
|
||||||
|
:host([size='medium']) {
|
||||||
|
min-width: 90px;
|
||||||
|
height: 36px; }
|
||||||
|
:host([size='medium']) button {
|
||||||
|
padding: 0 15px; }
|
||||||
|
|
||||||
|
:host([size='medium'][circle]) {
|
||||||
|
min-width: 36px; }
|
||||||
|
|
||||||
|
:host([size='mini']),
|
||||||
|
:host([text]) {
|
||||||
|
min-width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: 12px; }
|
||||||
|
:host([size='mini']) button,
|
||||||
|
:host([text]) button {
|
||||||
|
padding: 0 5px; }
|
||||||
|
:host([size='mini']) .icon,
|
||||||
|
:host([text]) .icon {
|
||||||
|
--size: 14px; }
|
||||||
|
|
||||||
|
:host([size='mini'][circle]) {
|
||||||
|
min-width: 24px; }
|
||||||
|
|
||||||
|
:host([text]) {
|
||||||
|
height: 18px; }
|
||||||
|
:host([text]) button {
|
||||||
|
padding: 0;
|
||||||
|
border: 0; }
|
||||||
|
:host([text]) button:hover {
|
||||||
|
background: none;
|
||||||
|
text-decoration: underline; }
|
||||||
|
|
||||||
|
:host([round]) {
|
||||||
|
border-radius: 21px; }
|
||||||
|
|
||||||
|
:host([circle]) {
|
||||||
|
min-width: 32px;
|
||||||
|
border-radius: 50%; }
|
||||||
|
:host([circle]) button {
|
||||||
|
padding: 0; }
|
||||||
|
:host([circle]) .icon {
|
||||||
|
margin-right: 0; }
|
||||||
|
|
||||||
|
:host([loading]),
|
||||||
|
:host([disabled]) {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: var(--color-grey-1);
|
||||||
|
opacity: 0.6; }
|
||||||
|
:host([loading]) .icon,
|
||||||
|
:host([disabled]) .icon {
|
||||||
|
color: var(--color-grey-1); }
|
||||||
|
:host([loading]) button,
|
||||||
|
:host([disabled]) button {
|
||||||
|
background: #fff;
|
||||||
|
border-color: var(--color-plain-3); }
|
||||||
|
|
||||||
|
:host([color]) {
|
||||||
|
color: #fff; }
|
||||||
|
:host([color]) button {
|
||||||
|
border-color: transparent; }
|
||||||
|
:host([color]) .icon {
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
:host([color='red']) button {
|
||||||
|
background: var(--color-red-2); }
|
||||||
|
:host([color='red']) button:hover {
|
||||||
|
background: var(--color-red-1); }
|
||||||
|
:host([color='red']) button:active {
|
||||||
|
background: var(--color-red-3); }
|
||||||
|
|
||||||
|
:host([color='red'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-red-2); }
|
||||||
|
:host([color='red'][text]) button:hover {
|
||||||
|
color: var(--color-red-1); }
|
||||||
|
:host([color='red'][text]) button:active {
|
||||||
|
color: var(--color-red-3); }
|
||||||
|
|
||||||
|
:host([color='red'][loading]) button,
|
||||||
|
:host([color='red'][disabled]) button {
|
||||||
|
background: var(--color-red-1); }
|
||||||
|
|
||||||
|
:host([color='blue']) button {
|
||||||
|
background: var(--color-blue-2); }
|
||||||
|
:host([color='blue']) button:hover {
|
||||||
|
background: var(--color-blue-1); }
|
||||||
|
:host([color='blue']) button:active {
|
||||||
|
background: var(--color-blue-3); }
|
||||||
|
|
||||||
|
:host([color='blue'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-blue-2); }
|
||||||
|
:host([color='blue'][text]) button:hover {
|
||||||
|
color: var(--color-blue-1); }
|
||||||
|
:host([color='blue'][text]) button:active {
|
||||||
|
color: var(--color-blue-3); }
|
||||||
|
|
||||||
|
:host([color='blue'][loading]) button,
|
||||||
|
:host([color='blue'][disabled]) button {
|
||||||
|
background: var(--color-blue-1); }
|
||||||
|
|
||||||
|
:host([color='green']) button {
|
||||||
|
background: var(--color-green-2); }
|
||||||
|
:host([color='green']) button:hover {
|
||||||
|
background: var(--color-green-1); }
|
||||||
|
:host([color='green']) button:active {
|
||||||
|
background: var(--color-green-3); }
|
||||||
|
|
||||||
|
:host([color='green'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-green-2); }
|
||||||
|
:host([color='green'][text]) button:hover {
|
||||||
|
color: var(--color-green-1); }
|
||||||
|
:host([color='green'][text]) button:active {
|
||||||
|
color: var(--color-green-3); }
|
||||||
|
|
||||||
|
:host([color='green'][loading]) button,
|
||||||
|
:host([color='green'][disabled]) button {
|
||||||
|
background: var(--color-green-1); }
|
||||||
|
|
||||||
|
:host([color='teal']) button {
|
||||||
|
background: var(--color-teal-2); }
|
||||||
|
:host([color='teal']) button:hover {
|
||||||
|
background: var(--color-teal-1); }
|
||||||
|
:host([color='teal']) button:active {
|
||||||
|
background: var(--color-teal-3); }
|
||||||
|
|
||||||
|
:host([color='teal'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-teal-2); }
|
||||||
|
:host([color='teal'][text]) button:hover {
|
||||||
|
color: var(--color-teal-1); }
|
||||||
|
:host([color='teal'][text]) button:active {
|
||||||
|
color: var(--color-teal-3); }
|
||||||
|
|
||||||
|
:host([color='teal'][loading]) button,
|
||||||
|
:host([color='teal'][disabled]) button {
|
||||||
|
background: var(--color-teal-1); }
|
||||||
|
|
||||||
|
:host([color='orange']) button {
|
||||||
|
background: var(--color-orange-2); }
|
||||||
|
:host([color='orange']) button:hover {
|
||||||
|
background: var(--color-orange-1); }
|
||||||
|
:host([color='orange']) button:active {
|
||||||
|
background: var(--color-orange-3); }
|
||||||
|
|
||||||
|
:host([color='orange'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-orange-2); }
|
||||||
|
:host([color='orange'][text]) button:hover {
|
||||||
|
color: var(--color-orange-1); }
|
||||||
|
:host([color='orange'][text]) button:active {
|
||||||
|
color: var(--color-orange-3); }
|
||||||
|
|
||||||
|
:host([color='orange'][loading]) button,
|
||||||
|
:host([color='orange'][disabled]) button {
|
||||||
|
background: var(--color-orange-1); }
|
||||||
|
|
||||||
|
:host([color='dark']) button {
|
||||||
|
background: var(--color-dark-2); }
|
||||||
|
:host([color='dark']) button:hover {
|
||||||
|
background: var(--color-dark-1); }
|
||||||
|
:host([color='dark']) button:active {
|
||||||
|
background: var(--color-dark-3); }
|
||||||
|
|
||||||
|
:host([color='dark'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-dark-2); }
|
||||||
|
:host([color='dark'][text]) button:hover {
|
||||||
|
color: var(--color-dark-1); }
|
||||||
|
:host([color='dark'][text]) button:active {
|
||||||
|
color: var(--color-dark-3); }
|
||||||
|
|
||||||
|
:host([color='dark'][loading]) button,
|
||||||
|
:host([color='dark'][disabled]) button {
|
||||||
|
background: var(--color-dark-1); }
|
||||||
|
|
||||||
|
:host([color='purple']) button {
|
||||||
|
background: var(--color-purple-2); }
|
||||||
|
:host([color='purple']) button:hover {
|
||||||
|
background: var(--color-purple-1); }
|
||||||
|
:host([color='purple']) button:active {
|
||||||
|
background: var(--color-purple-3); }
|
||||||
|
|
||||||
|
:host([color='purple'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-purple-2); }
|
||||||
|
:host([color='purple'][text]) button:hover {
|
||||||
|
color: var(--color-purple-1); }
|
||||||
|
:host([color='purple'][text]) button:active {
|
||||||
|
color: var(--color-purple-3); }
|
||||||
|
|
||||||
|
:host([color='purple'][loading]) button,
|
||||||
|
:host([color='purple'][disabled]) button {
|
||||||
|
background: var(--color-purple-1); }
|
||||||
|
|
||||||
|
:host([color='grey']) button {
|
||||||
|
background: var(--color-grey-2); }
|
||||||
|
:host([color='grey']) button:hover {
|
||||||
|
background: var(--color-grey-1); }
|
||||||
|
:host([color='grey']) button:active {
|
||||||
|
background: var(--color-grey-3); }
|
||||||
|
|
||||||
|
:host([color='grey'][text]) button {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-grey-2); }
|
||||||
|
:host([color='grey'][text]) button:hover {
|
||||||
|
color: var(--color-grey-1); }
|
||||||
|
:host([color='grey'][text]) button:active {
|
||||||
|
color: var(--color-grey-3); }
|
||||||
|
|
||||||
|
:host([color='grey'][loading]) button,
|
||||||
|
:host([color='grey'][disabled]) button {
|
||||||
|
background: var(--color-grey-1); }
|
||||||
|
|
||||||
|
:host([no-border]) button {
|
||||||
|
border-color: transparent;
|
||||||
|
background: inherit; }
|
||||||
|
|
||||||
|
:host([text][loading]) button, :host([text][loading]) button:hover, :host([text][loading]) button:active,
|
||||||
|
:host([text][disabled]) button,
|
||||||
|
:host([text][disabled]) button:hover,
|
||||||
|
:host([text][disabled]) button:active {
|
||||||
|
text-decoration: none;
|
||||||
|
background: transparent; }
|
||||||
|
|
||||||
|
:host(:focus-within) {
|
||||||
|
box-shadow: 0 0 2px #88f7df; }
|
||||||
|
|
||||||
|
:host(:focus-within[disabled]),
|
||||||
|
:host(:focus-within[loading]) {
|
||||||
|
box-shadow: none; }
|
||||||
|
</style>
|
||||||
|
<button>
|
||||||
|
<wc-icon class="icon"></wc-icon>
|
||||||
|
<slot></slot>
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
// 圆形按钮不允许文字
|
||||||
|
if (this.hasAttribute('circle')) {
|
||||||
|
this.textContent = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__BTN__ = this.root.children[1]
|
||||||
|
this.__ICO__ = this.__BTN__.children[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
get loading() {
|
||||||
|
return this.props.loading
|
||||||
|
}
|
||||||
|
|
||||||
|
set loading(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.loading = true
|
||||||
|
this.__ICO__.setAttribute('is', 'loading')
|
||||||
|
this.setAttribute('loading', '')
|
||||||
|
} else {
|
||||||
|
this.props.loading = false
|
||||||
|
this.__ICO__.setAttribute('is', this.props.icon)
|
||||||
|
this.removeAttribute('loading')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.stamp = 0
|
||||||
|
|
||||||
|
// 阻止事件冒泡, 避免用户自己绑定click事件不受这2个值的限制
|
||||||
|
this._handleClick = $.bind(this.__BTN__, 'click', ev => {
|
||||||
|
var { loading, disabled, lazy } = this.props
|
||||||
|
var now = Date.now()
|
||||||
|
|
||||||
|
if (loading || disabled) {
|
||||||
|
return ev.stopPropagation()
|
||||||
|
}
|
||||||
|
// 并发拦截
|
||||||
|
if (lazy && now - this.stamp < lazy) {
|
||||||
|
return ev.stopPropagation()
|
||||||
|
}
|
||||||
|
this.stamp = now
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this.__BTN__, 'click', this._handleClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'icon':
|
||||||
|
this.props.icon = val
|
||||||
|
if (val) {
|
||||||
|
if (!this.props.loading) {
|
||||||
|
this.__ICO__.setAttribute('is', val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.removeAttribute('icon')
|
||||||
|
this.__ICO__.removeAttribute('is')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'autofocus':
|
||||||
|
this.__BTN__.setAttribute('autofocus', '')
|
||||||
|
// 辣鸡火狐, 要触发一下focus, 才能聚焦
|
||||||
|
if (IS_FIREFOX) {
|
||||||
|
setTimeout(_ => {
|
||||||
|
this.__BTN__.focus()
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'lazy':
|
||||||
|
this.props.lazy = val >> 0
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'loading':
|
||||||
|
case 'disabled':
|
||||||
|
this[name] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-button')){
|
||||||
|
customElements.define('wc-button', Button)
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import "../icon/index.js"
|
||||||
|
import $ from "../utils.js"
|
||||||
|
|
||||||
|
export default class CheckboxItem extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["color","value","checked","readonly","disabled"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
color: '',
|
||||||
|
value: '',
|
||||||
|
checked: false,
|
||||||
|
readonly: false,
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: inline-flex;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 14px; }
|
||||||
|
:host label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 5px;
|
||||||
|
line-height: 0;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: inherit;
|
||||||
|
color: var(--color-grey-3); }
|
||||||
|
:host .dot {
|
||||||
|
--size: 18px;
|
||||||
|
padding: 2px;
|
||||||
|
margin-right: 3px; }
|
||||||
|
|
||||||
|
:host([readonly]) {
|
||||||
|
opacity: 0.8; }
|
||||||
|
|
||||||
|
:host([disabled]) {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6; }
|
||||||
|
|
||||||
|
:host([size='large']) {
|
||||||
|
font-size: 16px; }
|
||||||
|
:host([size='large']) label {
|
||||||
|
height: 42px; }
|
||||||
|
:host([size='large']) .dot {
|
||||||
|
--size: 22px; }
|
||||||
|
|
||||||
|
:host([size='medium']) label {
|
||||||
|
height: 38px; }
|
||||||
|
|
||||||
|
:host([size='medium']) .dot {
|
||||||
|
--size: 20px; }
|
||||||
|
|
||||||
|
:host([size='mini']) {
|
||||||
|
font-size: 12px; }
|
||||||
|
:host([size='mini']) label {
|
||||||
|
height: 20px; }
|
||||||
|
:host([size='mini']) .dot {
|
||||||
|
--size: 14px; }
|
||||||
|
|
||||||
|
:host([color='red']) label.checked {
|
||||||
|
color: var(--color-red-1); }
|
||||||
|
:host([color='red']) label.checked .dot {
|
||||||
|
border-color: var(--color-red-1); }
|
||||||
|
:host([color='red']) label.checked .dot::after {
|
||||||
|
background: var(--color-red-1); }
|
||||||
|
|
||||||
|
:host([color='blue']) label.checked {
|
||||||
|
color: var(--color-blue-1); }
|
||||||
|
:host([color='blue']) label.checked .dot {
|
||||||
|
border-color: var(--color-blue-1); }
|
||||||
|
:host([color='blue']) label.checked .dot::after {
|
||||||
|
background: var(--color-blue-1); }
|
||||||
|
|
||||||
|
:host([color='green']) label.checked {
|
||||||
|
color: var(--color-green-1); }
|
||||||
|
:host([color='green']) label.checked .dot {
|
||||||
|
border-color: var(--color-green-1); }
|
||||||
|
:host([color='green']) label.checked .dot::after {
|
||||||
|
background: var(--color-green-1); }
|
||||||
|
|
||||||
|
:host([color='teal']) label.checked {
|
||||||
|
color: var(--color-teal-1); }
|
||||||
|
:host([color='teal']) label.checked .dot {
|
||||||
|
border-color: var(--color-teal-1); }
|
||||||
|
:host([color='teal']) label.checked .dot::after {
|
||||||
|
background: var(--color-teal-1); }
|
||||||
|
|
||||||
|
:host([color='orange']) label.checked {
|
||||||
|
color: var(--color-orange-1); }
|
||||||
|
:host([color='orange']) label.checked .dot {
|
||||||
|
border-color: var(--color-orange-1); }
|
||||||
|
:host([color='orange']) label.checked .dot::after {
|
||||||
|
background: var(--color-orange-1); }
|
||||||
|
|
||||||
|
:host([color='dark']) label.checked {
|
||||||
|
color: var(--color-dark-1); }
|
||||||
|
:host([color='dark']) label.checked .dot {
|
||||||
|
border-color: var(--color-dark-1); }
|
||||||
|
:host([color='dark']) label.checked .dot::after {
|
||||||
|
background: var(--color-dark-1); }
|
||||||
|
|
||||||
|
:host([color='purple']) label.checked {
|
||||||
|
color: var(--color-purple-1); }
|
||||||
|
:host([color='purple']) label.checked .dot {
|
||||||
|
border-color: var(--color-purple-1); }
|
||||||
|
:host([color='purple']) label.checked .dot::after {
|
||||||
|
background: var(--color-purple-1); }
|
||||||
|
</style>
|
||||||
|
<label>
|
||||||
|
<wc-icon class="dot" is="checkbox-off"></wc-icon>
|
||||||
|
<slot />
|
||||||
|
</label>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
this.__SWITCH__ = this.root.lastElementChild
|
||||||
|
this.__ICO__ = this.__SWITCH__.children[0]
|
||||||
|
|
||||||
|
this._isInGroup = false
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkGroup() {
|
||||||
|
this._isInGroup = this.parentNode.tagName === 'WC-CHECKBOX'
|
||||||
|
if (this._isInGroup && this.parentNode.root) {
|
||||||
|
if (this.parentNode.value.includes(this.value)) {
|
||||||
|
this.checked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
this.props.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
get checked() {
|
||||||
|
return this.props.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
set checked(val) {
|
||||||
|
this.props.checked = !!val
|
||||||
|
var { checked, color } = this.props
|
||||||
|
this.__SWITCH__.classList.toggle('checked', checked)
|
||||||
|
this.__ICO__.setAttribute('is', 'checkbox-' + (checked ? 'on' : 'off'))
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
this.__ICO__.setAttribute('color', color)
|
||||||
|
} else {
|
||||||
|
this.__ICO__.removeAttribute('color')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get readOnly() {
|
||||||
|
return this.props.readonly
|
||||||
|
}
|
||||||
|
|
||||||
|
set readOnly(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.readonly = true
|
||||||
|
this.setAttribute('readonly', '')
|
||||||
|
} else {
|
||||||
|
this.props.readonly = false
|
||||||
|
this.removeAttribute('readonly')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this._checkGroup()
|
||||||
|
|
||||||
|
this._handlClick = $.bind(this, 'click', ev => {
|
||||||
|
ev.preventDefault()
|
||||||
|
|
||||||
|
if (this.disabled || this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checked = !this.checked
|
||||||
|
|
||||||
|
if (this._isInGroup) {
|
||||||
|
this.parentNode.dispatchEvent(
|
||||||
|
new CustomEvent('child-picked', {
|
||||||
|
detail: { value: this.value, checked: this.checked }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this, 'click', this._handlClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'value':
|
||||||
|
case 'color':
|
||||||
|
this.props[name] = val
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'checked':
|
||||||
|
case 'readonly':
|
||||||
|
case 'disabled':
|
||||||
|
var k = name
|
||||||
|
if (k === 'readonly') {
|
||||||
|
k = 'readOnly'
|
||||||
|
}
|
||||||
|
this[k] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-checkbox-item')){
|
||||||
|
customElements.define('wc-checkbox-item', CheckboxItem)
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import $ from "../utils.js"
|
||||||
|
import "./checkbox-item.js"
|
||||||
|
|
||||||
|
export default class Checkbox extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["value"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
value: []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: inline-flex; }
|
||||||
|
</style>
|
||||||
|
<slot />
|
||||||
|
`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateChildrenStat() {
|
||||||
|
Array.from(this.children).forEach(it => {
|
||||||
|
if (it.tagName === 'WC-CHECKBOX-ITEM' && it.root) {
|
||||||
|
if (this.value.includes(it.value)) {
|
||||||
|
it.checked = true
|
||||||
|
} else {
|
||||||
|
it.checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
if (val === this.props.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.props.value = val
|
||||||
|
this._updateChildrenStat()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this._pickedFn = $.bind(this, 'child-picked', ev => {
|
||||||
|
var tmp = [...this.props.value]
|
||||||
|
var idx = tmp.indexOf(ev.detail.value)
|
||||||
|
if (ev.detail.checked) {
|
||||||
|
if (idx < 0) {
|
||||||
|
tmp.push(ev.detail.value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (~idx) {
|
||||||
|
tmp.splice(idx, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.props.value = tmp
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this, 'child-picked', this._pickedFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'value':
|
||||||
|
if (val) {
|
||||||
|
this.value = val.split(/,\s*?/)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-checkbox')){
|
||||||
|
customElements.define('wc-checkbox', Checkbox)
|
||||||
|
}
|
|
@ -0,0 +1,617 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import "../scroll/index.js"
|
||||||
|
import "../icon/index.js"
|
||||||
|
import $ from "../utils.js"
|
||||||
|
|
||||||
|
const TYPES = ['text', 'textarea', 'password']
|
||||||
|
const INPUTS = {
|
||||||
|
text: '<input spellcheck="false">',
|
||||||
|
textarea: '<textarea spellcheck="false"></textarea>'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Input extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["value","icon","type","placeholder","maxlength","minlength","autofocus","readonly","disabled"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
value: '',
|
||||||
|
icon: '',
|
||||||
|
type: 'text',
|
||||||
|
placeholder: '',
|
||||||
|
maxlength: null,
|
||||||
|
minlength: null,
|
||||||
|
autofocus: false,
|
||||||
|
readonly: false,
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
state = {
|
||||||
|
mvidx: null //下拉列表光标的索引ID
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
var type = this.getAttribute('type')
|
||||||
|
var input = ''
|
||||||
|
|
||||||
|
if (type !== 'textarea') {
|
||||||
|
type = 'text'
|
||||||
|
}
|
||||||
|
|
||||||
|
input = INPUTS[type]
|
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>@charset "UTF-8";
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
ul,
|
||||||
|
li {
|
||||||
|
list-style: none; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
color: var(--color-dark-1);
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: text; }
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 64px;
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: inherit;
|
||||||
|
background: var(--bg-color, #fff);
|
||||||
|
color: inherit;
|
||||||
|
cursor: inherit;
|
||||||
|
/* ----- */ }
|
||||||
|
.label input,
|
||||||
|
.label textarea {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 32px;
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 5px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
background: none;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
cursor: inherit; }
|
||||||
|
.label input::placeholder,
|
||||||
|
.label textarea::placeholder {
|
||||||
|
color: var(--color-grey-1); }
|
||||||
|
.label textarea {
|
||||||
|
padding: 5px 8px;
|
||||||
|
resize: none; }
|
||||||
|
.label .prepend,
|
||||||
|
.label .append {
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: auto;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 10px;
|
||||||
|
line-height: 0;
|
||||||
|
background: var(--bg-color, --color-plain-1);
|
||||||
|
white-space: nowrap; }
|
||||||
|
.label .prepend {
|
||||||
|
border-right: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: 2px 0 0 2px; }
|
||||||
|
.label .append {
|
||||||
|
border-left: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: 0 2px 2px 0; }
|
||||||
|
.label[prepend] .prepend,
|
||||||
|
.label[append] .append {
|
||||||
|
display: flex; }
|
||||||
|
.label[prepend] input,
|
||||||
|
.label[append] input {
|
||||||
|
min-width: 64px; }
|
||||||
|
.label .icon {
|
||||||
|
--size: 20px;
|
||||||
|
padding: 0 5px;
|
||||||
|
margin: 0 5px;
|
||||||
|
color: var(--color-grey-2); }
|
||||||
|
|
||||||
|
.suggestion {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10240;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 200px;
|
||||||
|
height: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
min-height: 46px;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.25); }
|
||||||
|
.suggestion .list {
|
||||||
|
width: 100%; }
|
||||||
|
.suggestion::after {
|
||||||
|
position: absolute;
|
||||||
|
left: 30px;
|
||||||
|
top: -4px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: -1px -1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
content: ''; }
|
||||||
|
.suggestion.show {
|
||||||
|
display: flex; }
|
||||||
|
.suggestion li {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding: 0 8px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer; }
|
||||||
|
.suggestion li:hover, .suggestion li[focus] {
|
||||||
|
background: var(--color-plain-1); }
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
:host([auto-border]) .label {
|
||||||
|
border-color: transparent; }
|
||||||
|
|
||||||
|
:host([disabled]) {
|
||||||
|
cursor: not-allowed; }
|
||||||
|
:host([disabled]) .label {
|
||||||
|
background: var(--color-plain-1);
|
||||||
|
opacity: 0.6; }
|
||||||
|
|
||||||
|
:host([readonly]) {
|
||||||
|
cursor: default; }
|
||||||
|
|
||||||
|
:host(:focus-within) {
|
||||||
|
box-shadow: 0 0 2px #88f7df; }
|
||||||
|
:host(:focus-within) .label {
|
||||||
|
border-color: var(--color-plain-3); }
|
||||||
|
|
||||||
|
:host(:focus-within[readonly]) {
|
||||||
|
box-shadow: 0 0 2px #f3be4d; }
|
||||||
|
|
||||||
|
:host([type='textarea']) {
|
||||||
|
display: flex;
|
||||||
|
height: 80px; }
|
||||||
|
:host([type='textarea']) .label {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%; }
|
||||||
|
:host([type='textarea']) .icon,
|
||||||
|
:host([type='textarea']) .suggestion {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
/* 额外样式 */
|
||||||
|
:host([round]) {
|
||||||
|
border-radius: 21px; }
|
||||||
|
:host([round]) .label input {
|
||||||
|
padding: 0 10px; }
|
||||||
|
:host([round]) .label[prepend] input,
|
||||||
|
:host([round]) .label[append] input {
|
||||||
|
padding: 0 5px; }
|
||||||
|
:host([round]) .prepend {
|
||||||
|
border-radius: 21px 0 0 21px; }
|
||||||
|
:host([round]) .append {
|
||||||
|
border-radius: 0 21px 21px 0; }
|
||||||
|
|
||||||
|
:host([size='large']) .label {
|
||||||
|
height: 42px;
|
||||||
|
font-size: 16px; }
|
||||||
|
|
||||||
|
:host([size='large']) .prepend,
|
||||||
|
:host([size='large']) .append {
|
||||||
|
height: 40px; }
|
||||||
|
|
||||||
|
:host([size='medium']) .label {
|
||||||
|
height: 36px; }
|
||||||
|
|
||||||
|
:host([size='medium']) .prepend,
|
||||||
|
:host([size='medium']) .append {
|
||||||
|
height: 34px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .label {
|
||||||
|
height: 24px;
|
||||||
|
font-size: 12px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .icon {
|
||||||
|
--size: 16px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .prepend,
|
||||||
|
:host([size='mini']) .append {
|
||||||
|
height: 18px; }
|
||||||
|
|
||||||
|
:host([no-border]),
|
||||||
|
:host(:focus-within[no-border]) {
|
||||||
|
box-shadow: none; }
|
||||||
|
:host([no-border]) .label,
|
||||||
|
:host(:focus-within[no-border]) .label {
|
||||||
|
border: 0; }
|
||||||
|
|
||||||
|
:host([transparent]) .label {
|
||||||
|
background: transparent; }
|
||||||
|
</style>
|
||||||
|
<div class="label">
|
||||||
|
<slot class="prepend" name="prepend"></slot>
|
||||||
|
${input}
|
||||||
|
<wc-icon class="icon"></wc-icon>
|
||||||
|
<slot class="append" name="append"></slot>
|
||||||
|
|
||||||
|
<div class="suggestion">
|
||||||
|
<wc-scroll>
|
||||||
|
<ul class="list"></ul>
|
||||||
|
</wc-scroll>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
this.props.type = type
|
||||||
|
|
||||||
|
this.__OUTER__ = this.root.children[1]
|
||||||
|
this.__PREPEND__ = this.__OUTER__.children[0]
|
||||||
|
this.__INPUT__ = this.__OUTER__.children[1]
|
||||||
|
this.__ICO__ = this.__OUTER__.children[2]
|
||||||
|
this.__APPEND__ = this.__OUTER__.children[3]
|
||||||
|
this.__LIST__ = this.__OUTER__.children[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
get readOnly() {
|
||||||
|
return this.props.readonly
|
||||||
|
}
|
||||||
|
|
||||||
|
set readOnly(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.readonly = true
|
||||||
|
this.setAttribute('readonly', '')
|
||||||
|
this.__INPUT__.setAttribute('readonly', '')
|
||||||
|
} else {
|
||||||
|
this.props.readonly = false
|
||||||
|
this.removeAttribute('readonly')
|
||||||
|
this.__INPUT__.removeAttribute('readonly')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
this.__INPUT__.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
this.__INPUT__.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.__INPUT__.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
this.__INPUT__.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() {
|
||||||
|
return this.__INPUT__.type
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(val) {
|
||||||
|
if (val !== 'textarea') {
|
||||||
|
this.__INPUT__.type = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动光标选择下拉选项
|
||||||
|
_moveSelect(ev) {
|
||||||
|
var { list } = this.props
|
||||||
|
if (list && list.length) {
|
||||||
|
ev.preventDefault()
|
||||||
|
var step = ev.keyCode === 38 ? -1 : 1
|
||||||
|
var items = Array.from(
|
||||||
|
this.__LIST__.firstElementChild.firstElementChild.children
|
||||||
|
)
|
||||||
|
if (this.state.mvidx === null) {
|
||||||
|
this.state.mvidx = 0
|
||||||
|
} else {
|
||||||
|
this.state.mvidx += step
|
||||||
|
}
|
||||||
|
if (this.state.mvidx < 0) {
|
||||||
|
this.state.mvidx = 0
|
||||||
|
} else if (this.state.mvidx > items.length - 1) {
|
||||||
|
this.state.mvidx = items.length - 1
|
||||||
|
}
|
||||||
|
items.forEach((it, i) => {
|
||||||
|
if (i === this.state.mvidx) {
|
||||||
|
this.__LIST__.firstElementChild.scrollTop = it.offsetTop - 150
|
||||||
|
it.setAttribute('focus', '')
|
||||||
|
} else {
|
||||||
|
it.removeAttribute('focus')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发列表选择
|
||||||
|
_fetchSelect(idx, ev) {
|
||||||
|
var item = this.props.list[idx]
|
||||||
|
this.value = item.value
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('select', {
|
||||||
|
detail: item
|
||||||
|
})
|
||||||
|
)
|
||||||
|
this._handleChange(ev)
|
||||||
|
this.__LIST__.classList.remove('show')
|
||||||
|
this.state.mvidx = null
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateAttr() {
|
||||||
|
var { maxlength, minlength } = this.props
|
||||||
|
|
||||||
|
if (maxlength && maxlength > 0) {
|
||||||
|
this.__INPUT__.setAttribute('maxlength', maxlength)
|
||||||
|
} else {
|
||||||
|
this.__INPUT__.removeAttribute('maxlength')
|
||||||
|
}
|
||||||
|
if (minlength && minlength > 0) {
|
||||||
|
this.__INPUT__.setAttribute('minlength', minlength)
|
||||||
|
} else {
|
||||||
|
this.__INPUT__.removeAttribute('minlength')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
var prepend = this.__PREPEND__.assignedNodes()
|
||||||
|
var append = this.__APPEND__.assignedNodes()
|
||||||
|
var { type } = this.props
|
||||||
|
|
||||||
|
// 相同插槽, 只允许1个
|
||||||
|
while (prepend.length > 1) {
|
||||||
|
this.removeChild(prepend.pop())
|
||||||
|
}
|
||||||
|
while (append.length > 1) {
|
||||||
|
this.removeChild(append.pop())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepend.length && type !== 'textarea') {
|
||||||
|
this.__OUTER__.setAttribute('prepend', '')
|
||||||
|
}
|
||||||
|
if (append.length && type !== 'textarea') {
|
||||||
|
this.__OUTER__.setAttribute('append', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateAttr()
|
||||||
|
|
||||||
|
// 键盘事件
|
||||||
|
this._handleSubmit = $.catch(this.__INPUT__, 'keydown', ev => {
|
||||||
|
if (this.disabled || this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// up: 38, down: 40
|
||||||
|
if (ev.keyCode === 38 || ev.keyCode === 40) {
|
||||||
|
// 仅普通文本表单, 密码和多行文本框不做响应
|
||||||
|
if (type === 'text') {
|
||||||
|
return this._moveSelect(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 回车触发submit事件
|
||||||
|
// textarea 要按Ctrl Or Cmd键, 才会触发
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
// 如果是输入建议存在,则第1次回车的时候, 不触发提交
|
||||||
|
if (type === 'text' && this.state.mvidx !== null) {
|
||||||
|
return this._fetchSelect(this.state.mvidx, ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
type === 'text' ||
|
||||||
|
(type === 'textarea' && (ev.ctrlKey || ev.metaKey))
|
||||||
|
) {
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('submit', {
|
||||||
|
detail: this.value
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 非textarea, 可做输入建议功能
|
||||||
|
if (type === 'text') {
|
||||||
|
// 输入状态事件
|
||||||
|
this._handleChange = $.bind(this.__INPUT__, 'input', ev => {
|
||||||
|
ev.preventDefault()
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('fetch-suggest', {
|
||||||
|
detail: {
|
||||||
|
value: this.value,
|
||||||
|
send: list => {
|
||||||
|
this.props.list = list
|
||||||
|
this._parseSuggestion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 渲染建议列表
|
||||||
|
this._parseSuggestion = $.bind(this.__INPUT__, 'click', ev => {
|
||||||
|
var { list } = this.props
|
||||||
|
let { x, y, width } = this.getBoundingClientRect()
|
||||||
|
if (list && list.length) {
|
||||||
|
var html = list
|
||||||
|
.map((it, i) => `<li data-idx="${i}">${it.value}</li>`)
|
||||||
|
.join('')
|
||||||
|
this.__LIST__.firstElementChild.firstElementChild.innerHTML = html
|
||||||
|
this.__LIST__.classList.toggle('show', true)
|
||||||
|
this.__LIST__.style.cssText = `left:${x}px;top:${y +
|
||||||
|
50}px;width:${width}px;`
|
||||||
|
} else {
|
||||||
|
this.__LIST__.classList.toggle('show', false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this._inactiveFn = $.outside(this, ev => {
|
||||||
|
this.__LIST__.classList.remove('show')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 选择建议
|
||||||
|
this._handleSelect = $.bind(this.__LIST__, 'click', ev => {
|
||||||
|
if (ev.target.tagName === 'LI') {
|
||||||
|
this._fetchSelect(ev.target.dataset.idx, ev)
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this._handleWheel = $.catch(this.__INPUT__, 'wheel')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this.__INPUT__, 'wheel', this._handleWheel)
|
||||||
|
$.unbind(this.__INPUT__, 'keydown', this._handleSubmit)
|
||||||
|
$.unbind(this.__INPUT__, 'input', this._handleChange)
|
||||||
|
$.unbind(this.__LIST__, 'click', this._handleSelect)
|
||||||
|
$.clearOutside(this._inactiveFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'icon':
|
||||||
|
this.props.icon = val
|
||||||
|
if (val) {
|
||||||
|
this.__ICO__.setAttribute('is', val)
|
||||||
|
} else {
|
||||||
|
this.removeAttribute('icon')
|
||||||
|
this.__ICO__.removeAttribute('is')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'autofocus':
|
||||||
|
this.__INPUT__.setAttribute('autofocus', '')
|
||||||
|
// 辣鸡火狐, 要触发一下focus, 才能聚焦
|
||||||
|
setTimeout(_ => {
|
||||||
|
this.__INPUT__.focus()
|
||||||
|
}, 10)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'placeholder':
|
||||||
|
this.__INPUT__.setAttribute('placeholder', val)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'type':
|
||||||
|
if (~TYPES.indexOf(val)) {
|
||||||
|
this.type = val
|
||||||
|
} else {
|
||||||
|
this.type = 'text'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'value':
|
||||||
|
this.value = val
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'maxlength':
|
||||||
|
case 'minlength':
|
||||||
|
this.props[name] = val
|
||||||
|
this._updateAttr()
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'readonly':
|
||||||
|
case 'disabled':
|
||||||
|
var k = name
|
||||||
|
if (k === 'readonly') {
|
||||||
|
k = 'readOnly'
|
||||||
|
}
|
||||||
|
this[k] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-input')){
|
||||||
|
customElements.define('wc-input', Input)
|
||||||
|
}
|
|
@ -0,0 +1,413 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import "../scroll/index.js"
|
||||||
|
import "../icon/index.js"
|
||||||
|
import $ from "../utils.js"
|
||||||
|
|
||||||
|
export default class Number extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["value","max","min","step","autofocus","readonly","disabled"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
value: 0,
|
||||||
|
max: null,
|
||||||
|
min: null,
|
||||||
|
step: 1,
|
||||||
|
autofocus: false,
|
||||||
|
readonly: false,
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>@charset "UTF-8";
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
width: 128px;
|
||||||
|
height: 32px;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
color: var(--color-dark-2);
|
||||||
|
border-radius: 2px; }
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
line-height: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: inherit;
|
||||||
|
background: var(--bg-color, #fff);
|
||||||
|
color: inherit;
|
||||||
|
cursor: text;
|
||||||
|
/* ----- */ }
|
||||||
|
.label span {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--bg-color, --color-plain-1);
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer; }
|
||||||
|
.label span:first-child {
|
||||||
|
border-radius: 2px 0 0 2px;
|
||||||
|
border-right: 1px solid var(--color-plain-3); }
|
||||||
|
.label span:last-child {
|
||||||
|
border-radius: 0 2px 2px 0;
|
||||||
|
border-left: 1px solid var(--color-plain-3); }
|
||||||
|
.label span.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6; }
|
||||||
|
.label input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 5px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
color: inherit;
|
||||||
|
text-align: center;
|
||||||
|
font-size: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
background: none;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
cursor: inherit; }
|
||||||
|
.label input::placeholder {
|
||||||
|
color: var(--color-grey-1); }
|
||||||
|
.label .icon {
|
||||||
|
padding: 0 5px;
|
||||||
|
--size: 20px; }
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
:host([readonly]) .label {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.8; }
|
||||||
|
:host([readonly]) .label span {
|
||||||
|
cursor: inherit; }
|
||||||
|
|
||||||
|
:host([disabled]) .label {
|
||||||
|
background: var(--color-plain-1);
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6; }
|
||||||
|
:host([disabled]) .label span {
|
||||||
|
cursor: inherit; }
|
||||||
|
|
||||||
|
:host(:focus-within) {
|
||||||
|
box-shadow: 0 0 2px #88f7df; }
|
||||||
|
|
||||||
|
:host(:focus-within[readonly]) {
|
||||||
|
box-shadow: 0 0 2px #f3be4d; }
|
||||||
|
|
||||||
|
/* 额外样式 */
|
||||||
|
:host([round]) {
|
||||||
|
border-radius: 21px; }
|
||||||
|
:host([round]) .label span:first-child {
|
||||||
|
border-radius: 21px 0 0 21px; }
|
||||||
|
:host([round]) .label span:last-child {
|
||||||
|
border-radius: 0 21px 21px 0; }
|
||||||
|
|
||||||
|
:host([size='large']) {
|
||||||
|
width: 192px;
|
||||||
|
height: 42px; }
|
||||||
|
:host([size='large']) .label {
|
||||||
|
font-size: 16px; }
|
||||||
|
:host([size='large']) .label span {
|
||||||
|
width: 48px; }
|
||||||
|
:host([size='large']) .prepend,
|
||||||
|
:host([size='large']) .append {
|
||||||
|
height: 40px; }
|
||||||
|
|
||||||
|
:host([size='medium']) {
|
||||||
|
width: 144px;
|
||||||
|
height: 36px; }
|
||||||
|
:host([size='medium']) .label span {
|
||||||
|
width: 36px; }
|
||||||
|
:host([size='medium']) .prepend,
|
||||||
|
:host([size='medium']) .append {
|
||||||
|
height: 34px; }
|
||||||
|
|
||||||
|
:host([size='mini']) {
|
||||||
|
width: 96px;
|
||||||
|
height: 24px; }
|
||||||
|
:host([size='mini']) .label {
|
||||||
|
font-size: 12px; }
|
||||||
|
:host([size='mini']) .label span {
|
||||||
|
width: 28px; }
|
||||||
|
:host([size='mini']) .icon {
|
||||||
|
--size: 16px; }
|
||||||
|
:host([size='mini']) .prepend,
|
||||||
|
:host([size='mini']) .append {
|
||||||
|
height: 18px; }
|
||||||
|
</style>
|
||||||
|
<div class="label">
|
||||||
|
<span data-act="-">-</span>
|
||||||
|
<!-- <wc-icon class="icon" is="minus"></wc-icon> -->
|
||||||
|
<input value="0" maxlength="9" />
|
||||||
|
<span data-act="+">+</span>
|
||||||
|
<!-- <wc-icon class="icon" is="plus"></wc-icon> -->
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
this.__OUTER__ = this.root.children[1]
|
||||||
|
this.__INPUT__ = this.__OUTER__.children[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
get readOnly() {
|
||||||
|
return this.props.readonly
|
||||||
|
}
|
||||||
|
|
||||||
|
set readOnly(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.readonly = true
|
||||||
|
this.setAttribute('readonly', '')
|
||||||
|
this.__INPUT__.setAttribute('readonly', '')
|
||||||
|
} else {
|
||||||
|
this.props.readonly = false
|
||||||
|
this.removeAttribute('readonly')
|
||||||
|
this.__INPUT__.removeAttribute('readonly')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
this.__INPUT__.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
this.__INPUT__.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
var n = +val
|
||||||
|
if (n === n) {
|
||||||
|
val = n
|
||||||
|
} else {
|
||||||
|
val = 0
|
||||||
|
}
|
||||||
|
this.props.value = val
|
||||||
|
this.__INPUT__.value = val
|
||||||
|
|
||||||
|
this._checkActionEnable()
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkActionEnable() {
|
||||||
|
var { max, min, value } = this.props
|
||||||
|
var n = value
|
||||||
|
|
||||||
|
if (min !== null) {
|
||||||
|
if (min > n) {
|
||||||
|
n = min
|
||||||
|
}
|
||||||
|
this.__OUTER__.children[0].classList.toggle('disabled', value <= min)
|
||||||
|
}
|
||||||
|
if (max !== null) {
|
||||||
|
if (max < n) {
|
||||||
|
n = max
|
||||||
|
}
|
||||||
|
this.__OUTER__.children[2].classList.toggle('disabled', value >= max)
|
||||||
|
}
|
||||||
|
if (n !== value) {
|
||||||
|
this.props.value = n
|
||||||
|
this.__INPUT__.value = n
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateValue(act) {
|
||||||
|
var { max, min, value, step } = this.props
|
||||||
|
if (act === '+') {
|
||||||
|
if (max !== null && max < value + step) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
value += step
|
||||||
|
} else {
|
||||||
|
if (min !== null && min > value - step) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
value -= step
|
||||||
|
}
|
||||||
|
this.props.value = +value.toFixed(2)
|
||||||
|
this.__INPUT__.value = this.props.value
|
||||||
|
this._checkActionEnable()
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
// 键盘事件
|
||||||
|
this._handleSubmit = $.catch(this.__INPUT__, 'keydown', ev => {
|
||||||
|
if (this.disabled || this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// up: 38, down: 40
|
||||||
|
if (ev.keyCode === 38 || ev.keyCode === 40) {
|
||||||
|
ev.preventDefault()
|
||||||
|
return this._updateValue(ev.keyCode === 38 ? '+' : '-')
|
||||||
|
}
|
||||||
|
// 回车触发submit事件
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
ev.preventDefault()
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('submit', {
|
||||||
|
detail: this.value
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this._handleChange = $.catch(this.__INPUT__, 'change', ev => {
|
||||||
|
if (isFinite(this.__INPUT__.value)) {
|
||||||
|
this.props.value = +this.__INPUT__.value
|
||||||
|
if (!this.__INPUT__.value.endsWith('.')) {
|
||||||
|
this.__INPUT__.value = this.props.value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.__INPUT__.value = this.props.value = 0
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
})
|
||||||
|
|
||||||
|
this._handleAction = $.bind(this.__OUTER__, 'click', ev => {
|
||||||
|
if (this.disabled || this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var target = ev.target
|
||||||
|
|
||||||
|
if (target.tagName === 'SPAN' || target.parentNode === 'SPAN') {
|
||||||
|
var act = target.dataset.act || target.parentNode.dataset.act
|
||||||
|
|
||||||
|
this._updateValue(act)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this.__INPUT__, 'keydown', this._handleSubmit)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'autofocus':
|
||||||
|
this.__INPUT__.setAttribute('autofocus', '')
|
||||||
|
// 辣鸡火狐, 要触发一下focus, 才能聚焦
|
||||||
|
setTimeout(_ => {
|
||||||
|
this.__INPUT__.focus()
|
||||||
|
}, 10)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'value':
|
||||||
|
this.value = val >> 0
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'step':
|
||||||
|
case 'max':
|
||||||
|
case 'min':
|
||||||
|
var n = +val
|
||||||
|
if (n === n) {
|
||||||
|
this.props[name] = n
|
||||||
|
}
|
||||||
|
this._checkActionEnable()
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'readonly':
|
||||||
|
case 'disabled':
|
||||||
|
var k = name
|
||||||
|
if (k === 'readonly') {
|
||||||
|
k = 'readOnly'
|
||||||
|
}
|
||||||
|
this[k] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-number')){
|
||||||
|
customElements.define('wc-number', Number)
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export default class Progress extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["value","max"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
value: 0,
|
||||||
|
max: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
align-items: center; }
|
||||||
|
:host label {
|
||||||
|
flex: 1;
|
||||||
|
height: var(--size, 10px);
|
||||||
|
border-radius: 9px;
|
||||||
|
background: var(--color-plain-2); }
|
||||||
|
:host label span {
|
||||||
|
display: block;
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 9px;
|
||||||
|
background: var(--color-teal-1); }
|
||||||
|
|
||||||
|
:host([size='large']) label {
|
||||||
|
height: 18px; }
|
||||||
|
|
||||||
|
:host([size='medium']) label {
|
||||||
|
height: 14px; }
|
||||||
|
|
||||||
|
:host([size='mini']) label {
|
||||||
|
height: 6px; }
|
||||||
|
|
||||||
|
:host([color='red']) label span {
|
||||||
|
background: var(--color-red-1); }
|
||||||
|
|
||||||
|
:host([color='blue']) label span {
|
||||||
|
background: var(--color-blue-1); }
|
||||||
|
|
||||||
|
:host([color='green']) label span {
|
||||||
|
background: var(--color-green-1); }
|
||||||
|
|
||||||
|
:host([color='orange']) label span {
|
||||||
|
background: var(--color-orange-1); }
|
||||||
|
|
||||||
|
:host([color='dark']) label span {
|
||||||
|
background: var(--color-dark-1); }
|
||||||
|
|
||||||
|
:host([color='purple']) label span {
|
||||||
|
background: var(--color-purple-1); }
|
||||||
|
</style>
|
||||||
|
<label><span></span></label>
|
||||||
|
`
|
||||||
|
|
||||||
|
this.__THUMB__ = this.root.children[1].lastElementChild
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
this.props.value = +val
|
||||||
|
this.calculate()
|
||||||
|
}
|
||||||
|
|
||||||
|
calculate() {
|
||||||
|
var { max, value } = this.props
|
||||||
|
this.__THUMB__.style.width = `${(100 * value) / max}%`
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.calculate()
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'max':
|
||||||
|
var max = +val
|
||||||
|
if (max !== max || max < 1) {
|
||||||
|
max = 1
|
||||||
|
}
|
||||||
|
this.props.max = max
|
||||||
|
this.calculate()
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'value':
|
||||||
|
var v = +val
|
||||||
|
if (v === v) {
|
||||||
|
this.props.value = v
|
||||||
|
this.calculate()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-progress')){
|
||||||
|
customElements.define('wc-progress', Progress)
|
||||||
|
}
|
|
@ -0,0 +1,308 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import $ from "../utils.js"
|
||||||
|
|
||||||
|
export default class RadioItem extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["value","checked","readonly","disabled"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
value: '',
|
||||||
|
checked: false,
|
||||||
|
readonly: false,
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: inline-flex;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 14px; }
|
||||||
|
:host label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 5px;
|
||||||
|
line-height: 1;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: inherit;
|
||||||
|
color: var(--color-grey-3); }
|
||||||
|
:host label.checked .dot::after {
|
||||||
|
visibility: visible; }
|
||||||
|
:host .dot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 3px;
|
||||||
|
border: 1px solid var(--color-grey-1);
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff; }
|
||||||
|
:host .dot::after {
|
||||||
|
display: block;
|
||||||
|
visibility: hidden;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--color-grey-1);
|
||||||
|
content: ''; }
|
||||||
|
|
||||||
|
:host([readonly]) {
|
||||||
|
opacity: 0.8; }
|
||||||
|
|
||||||
|
:host([disabled]) {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6; }
|
||||||
|
|
||||||
|
:host([size='large']) label {
|
||||||
|
min-width: 58px;
|
||||||
|
height: 32px; }
|
||||||
|
|
||||||
|
:host([size='large']) .dot {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px; }
|
||||||
|
:host([size='large']) .dot::after {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px; }
|
||||||
|
|
||||||
|
:host([size='medium']) label {
|
||||||
|
min-width: 50px;
|
||||||
|
height: 28px; }
|
||||||
|
|
||||||
|
:host([size='medium']) .dot {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px; }
|
||||||
|
:host([size='medium']) .dot::after {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px; }
|
||||||
|
|
||||||
|
:host([size='mini']) label {
|
||||||
|
height: 14px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .dot {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px; }
|
||||||
|
:host([size='mini']) .dot::after {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px; }
|
||||||
|
|
||||||
|
:host([color='red']) label.checked {
|
||||||
|
color: var(--color-red-1); }
|
||||||
|
:host([color='red']) label.checked .dot {
|
||||||
|
border-color: var(--color-red-1); }
|
||||||
|
:host([color='red']) label.checked .dot::after {
|
||||||
|
background: var(--color-red-1); }
|
||||||
|
|
||||||
|
:host([color='blue']) label.checked {
|
||||||
|
color: var(--color-blue-1); }
|
||||||
|
:host([color='blue']) label.checked .dot {
|
||||||
|
border-color: var(--color-blue-1); }
|
||||||
|
:host([color='blue']) label.checked .dot::after {
|
||||||
|
background: var(--color-blue-1); }
|
||||||
|
|
||||||
|
:host([color='green']) label.checked {
|
||||||
|
color: var(--color-green-1); }
|
||||||
|
:host([color='green']) label.checked .dot {
|
||||||
|
border-color: var(--color-green-1); }
|
||||||
|
:host([color='green']) label.checked .dot::after {
|
||||||
|
background: var(--color-green-1); }
|
||||||
|
|
||||||
|
:host([color='teal']) label.checked {
|
||||||
|
color: var(--color-teal-1); }
|
||||||
|
:host([color='teal']) label.checked .dot {
|
||||||
|
border-color: var(--color-teal-1); }
|
||||||
|
:host([color='teal']) label.checked .dot::after {
|
||||||
|
background: var(--color-teal-1); }
|
||||||
|
|
||||||
|
:host([color='orange']) label.checked {
|
||||||
|
color: var(--color-orange-1); }
|
||||||
|
:host([color='orange']) label.checked .dot {
|
||||||
|
border-color: var(--color-orange-1); }
|
||||||
|
:host([color='orange']) label.checked .dot::after {
|
||||||
|
background: var(--color-orange-1); }
|
||||||
|
|
||||||
|
:host([color='dark']) label.checked {
|
||||||
|
color: var(--color-dark-1); }
|
||||||
|
:host([color='dark']) label.checked .dot {
|
||||||
|
border-color: var(--color-dark-1); }
|
||||||
|
:host([color='dark']) label.checked .dot::after {
|
||||||
|
background: var(--color-dark-1); }
|
||||||
|
|
||||||
|
:host([color='purple']) label.checked {
|
||||||
|
color: var(--color-purple-1); }
|
||||||
|
:host([color='purple']) label.checked .dot {
|
||||||
|
border-color: var(--color-purple-1); }
|
||||||
|
:host([color='purple']) label.checked .dot::after {
|
||||||
|
background: var(--color-purple-1); }
|
||||||
|
</style>
|
||||||
|
<label>
|
||||||
|
<span class="dot"></span>
|
||||||
|
<slot />
|
||||||
|
</label>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
this.__SWITCH__ = this.root.lastElementChild
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
this.props.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
get checked() {
|
||||||
|
return this.props.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
set checked(val) {
|
||||||
|
this.props.checked = !!val
|
||||||
|
this.__SWITCH__.classList.toggle('checked', this.props.checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
get readOnly() {
|
||||||
|
return this.props.readonly
|
||||||
|
}
|
||||||
|
|
||||||
|
set readOnly(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.readonly = true
|
||||||
|
this.setAttribute('readonly', '')
|
||||||
|
} else {
|
||||||
|
this.props.readonly = false
|
||||||
|
this.removeAttribute('readonly')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
if (this.value === this.parentNode.value) {
|
||||||
|
this.checked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this._handleClick = $.catch(this, 'click', ev => {
|
||||||
|
if (this.disabled || this.readOnly || this.checked) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parentNode.dispatchEvent(
|
||||||
|
new CustomEvent('child-picked', { detail: this.value })
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this, 'click', this._handleClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'value':
|
||||||
|
this.value = val
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'checked':
|
||||||
|
case 'readonly':
|
||||||
|
case 'disabled':
|
||||||
|
var k = name
|
||||||
|
if (k === 'readonly') {
|
||||||
|
k = 'readOnly'
|
||||||
|
}
|
||||||
|
this[k] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-radio-item')){
|
||||||
|
customElements.define('wc-radio-item', RadioItem)
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import $ from "../utils.js"
|
||||||
|
import "./radio-item.js"
|
||||||
|
|
||||||
|
export default class Radio extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["value"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: inline-flex; }
|
||||||
|
</style>
|
||||||
|
<slot />
|
||||||
|
`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateChildrenStat() {
|
||||||
|
Array.from(this.children).forEach(it => {
|
||||||
|
if (it.tagName === 'WC-RADIO-ITEM' && it.root) {
|
||||||
|
if (it.value === this.props.value) {
|
||||||
|
it.checked = true
|
||||||
|
} else {
|
||||||
|
it.checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
if (val === this.props.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.props.value = val
|
||||||
|
this._updateChildrenStat()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this._pickedFn = $.bind(this, 'child-picked', ev => {
|
||||||
|
log('radio picked: ', ev.detail)
|
||||||
|
this.value = ev.detail
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this, 'child-picked', this._pickedFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'value':
|
||||||
|
this.value = val
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-radio')){
|
||||||
|
customElements.define('wc-radio', Radio)
|
||||||
|
}
|
|
@ -0,0 +1,559 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import "../scroll/index.js"
|
||||||
|
import "../icon/index.js"
|
||||||
|
import $ from "../utils.js"
|
||||||
|
|
||||||
|
function parseOptions(arr, props) {
|
||||||
|
let html = ''
|
||||||
|
for (let it of arr) {
|
||||||
|
if (it.list) {
|
||||||
|
html += `<dt>${it.name}</dt>`
|
||||||
|
for (let _ of it.list) {
|
||||||
|
props.DICT[_.value] = _
|
||||||
|
if (!_.disabled) {
|
||||||
|
props.LIST.push(_)
|
||||||
|
}
|
||||||
|
html += `<dd sub ${
|
||||||
|
_.disabled ? 'disabled' : `data-idx="${props.LIST.length - 1}"`
|
||||||
|
} ${_.value === props.value ? 'focus' : ''}>${_.label}</dd>`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!it.disabled) {
|
||||||
|
props.LIST.push(it)
|
||||||
|
}
|
||||||
|
props.DICT[it.value] = it
|
||||||
|
html += `<dd ${
|
||||||
|
it.disabled ? 'disabled' : `data-idx="${props.LIST.length - 1}"`
|
||||||
|
} ${it.value === props.value ? 'focus' : ''}>${it.label}</dd>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Select extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["label","placeholder","multi","value","options","mvidx","readonly","disabled"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
label: '',
|
||||||
|
placeholder: '',
|
||||||
|
multi: '',
|
||||||
|
value: '',
|
||||||
|
options: '',
|
||||||
|
mvidx: null, //下拉列表光标的索引ID
|
||||||
|
readonly: false,
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>@charset "UTF-8";
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
color: var(--color-dark-2);
|
||||||
|
border-radius: 2px; }
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 64px;
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: inherit;
|
||||||
|
background: #fff;
|
||||||
|
color: inherit;
|
||||||
|
cursor: default;
|
||||||
|
/* ----- */ }
|
||||||
|
.label input {
|
||||||
|
flex: 1;
|
||||||
|
width: 0;
|
||||||
|
min-width: 64px;
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 5px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
background: none;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
cursor: inherit; }
|
||||||
|
.label input::placeholder {
|
||||||
|
color: var(--color-grey-1); }
|
||||||
|
.label .prepend,
|
||||||
|
.label .append {
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: auto;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: var(--color-plain-1); }
|
||||||
|
.label .prepend {
|
||||||
|
border-right: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: 2px 0 0 2px; }
|
||||||
|
.label .append {
|
||||||
|
border-left: 1px solid var(--color-plain-3);
|
||||||
|
border-radius: 0 2px 2px 0; }
|
||||||
|
.label[prepend] .prepend,
|
||||||
|
.label[append] .append {
|
||||||
|
display: flex; }
|
||||||
|
.label .arrow {
|
||||||
|
padding: 0 5px;
|
||||||
|
--size: 14px;
|
||||||
|
color: #ddd;
|
||||||
|
transform: rotate(-90deg); }
|
||||||
|
|
||||||
|
.opt-box {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10260;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 200px;
|
||||||
|
height: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);
|
||||||
|
cursor: default; }
|
||||||
|
.opt-box .list {
|
||||||
|
width: 100%; }
|
||||||
|
.opt-box::after {
|
||||||
|
position: absolute;
|
||||||
|
left: 30px;
|
||||||
|
top: -4px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: -1px -1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
content: ''; }
|
||||||
|
.opt-box.show {
|
||||||
|
display: flex; }
|
||||||
|
.opt-box dt,
|
||||||
|
.opt-box dd {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding: 0 8px;
|
||||||
|
text-align: left;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap; }
|
||||||
|
.opt-box dt {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-grey-1); }
|
||||||
|
.opt-box dd {
|
||||||
|
cursor: pointer; }
|
||||||
|
.opt-box dd:hover, .opt-box dd[focus] {
|
||||||
|
background: var(--color-plain-1); }
|
||||||
|
.opt-box dd[focus] {
|
||||||
|
color: var(--color-teal-1); }
|
||||||
|
.opt-box dd[sub] {
|
||||||
|
text-indent: 1em; }
|
||||||
|
.opt-box dd[disabled] {
|
||||||
|
color: var(--color-grey-1);
|
||||||
|
cursor: not-allowed;
|
||||||
|
background: none; }
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
:host([disabled]) .label {
|
||||||
|
background: var(--color-plain-1);
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6; }
|
||||||
|
|
||||||
|
:host([readonly]) .label {
|
||||||
|
opacity: 0.8; }
|
||||||
|
|
||||||
|
:host(:focus-within) {
|
||||||
|
box-shadow: 0 0 2px #88f7df; }
|
||||||
|
|
||||||
|
:host(:focus-within[readonly]) {
|
||||||
|
box-shadow: 0 0 2px #f3be4d; }
|
||||||
|
|
||||||
|
/* 额外样式 */
|
||||||
|
:host([round]) {
|
||||||
|
border-radius: 21px; }
|
||||||
|
:host([round]) .label input {
|
||||||
|
padding: 0 10px; }
|
||||||
|
:host([round]) .prepend {
|
||||||
|
border-radius: 21px 0 0 21px; }
|
||||||
|
:host([round]) .append {
|
||||||
|
border-radius: 0 21px 21px 0; }
|
||||||
|
|
||||||
|
:host([size='large']) .label {
|
||||||
|
height: 42px;
|
||||||
|
font-size: 16px; }
|
||||||
|
|
||||||
|
:host([size='large']) .prepend,
|
||||||
|
:host([size='large']) .append {
|
||||||
|
height: 40px; }
|
||||||
|
|
||||||
|
:host([size='medium']) .label {
|
||||||
|
height: 36px; }
|
||||||
|
|
||||||
|
:host([size='medium']) .prepend,
|
||||||
|
:host([size='medium']) .append {
|
||||||
|
height: 34px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .label {
|
||||||
|
height: 24px;
|
||||||
|
font-size: 12px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .arrow {
|
||||||
|
--size: 12px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .prepend,
|
||||||
|
:host([size='mini']) .append {
|
||||||
|
height: 18px; }
|
||||||
|
</style>
|
||||||
|
<div class="label">
|
||||||
|
<slot class="prepend" name="prepend"></slot>
|
||||||
|
<input readonly />
|
||||||
|
<wc-icon class="arrow" is="left"></wc-icon>
|
||||||
|
<slot class="append" name="append"></slot>
|
||||||
|
<div class="opt-box">
|
||||||
|
<wc-scroll>
|
||||||
|
<dl class="list"></dl>
|
||||||
|
</wc-scroll>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
this.__OUTER__ = this.root.children[1]
|
||||||
|
this.__PREPEND__ = this.__OUTER__.children[0]
|
||||||
|
this.__INPUT__ = this.__OUTER__.children[1]
|
||||||
|
this.__APPEND__ = this.__OUTER__.children[3]
|
||||||
|
this.__OPTG__ = this.__OUTER__.children[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
get readOnly() {
|
||||||
|
return this.props.readonly
|
||||||
|
}
|
||||||
|
|
||||||
|
set readOnly(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.readonly = true
|
||||||
|
this.setAttribute('readonly', '')
|
||||||
|
} else {
|
||||||
|
this.props.readonly = false
|
||||||
|
this.removeAttribute('readonly')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
this.__INPUT__.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
this.__INPUT__.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
var { DICT, active } = this.props
|
||||||
|
this.props.value = val
|
||||||
|
this.__INPUT__.value = (DICT && DICT[val] && DICT[val].label) || val
|
||||||
|
if (!active) {
|
||||||
|
this._updateStyle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderOptions(options) {
|
||||||
|
this.props.DICT = {}
|
||||||
|
this.props.LIST = []
|
||||||
|
var elem = this.__OPTG__.firstElementChild.firstElementChild
|
||||||
|
|
||||||
|
elem.innerHTML = parseOptions(options, this.props)
|
||||||
|
this.props.ITEMS = Array.from(elem.children).filter(it => {
|
||||||
|
return it.tagName === 'DD' && !it.hasAttribute('disabled')
|
||||||
|
})
|
||||||
|
this.value = this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动光标选择下拉选项
|
||||||
|
_moveSelect(ev) {
|
||||||
|
var { LIST, DICT, ITEMS } = this.props
|
||||||
|
if (LIST && LIST.length) {
|
||||||
|
ev.preventDefault()
|
||||||
|
var step = ev.keyCode === 38 ? -1 : 1
|
||||||
|
|
||||||
|
if (this.props.mvidx === null) {
|
||||||
|
this.props.mvidx = 0
|
||||||
|
} else {
|
||||||
|
this.props.mvidx += step
|
||||||
|
}
|
||||||
|
if (this.props.mvidx < 0) {
|
||||||
|
this.props.mvidx = 0
|
||||||
|
} else if (this.props.mvidx > ITEMS.length - 1) {
|
||||||
|
this.props.mvidx = ITEMS.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ITEMS.forEach((it, i) => {
|
||||||
|
if (i === this.props.mvidx) {
|
||||||
|
this.__OPTG__.firstElementChild.scrollTop = it.offsetTop - 150
|
||||||
|
it.setAttribute('focus', '')
|
||||||
|
} else {
|
||||||
|
it.removeAttribute('focus')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateStyle(idx) {
|
||||||
|
var { LIST, ITEMS, value } = this.props
|
||||||
|
if (LIST && LIST.length) {
|
||||||
|
if (idx === undefined) {
|
||||||
|
for (let i = -1, it; (it = LIST[++i]); ) {
|
||||||
|
if (value === it.value) {
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.props.mvidx = idx
|
||||||
|
ITEMS.forEach((it, i) => {
|
||||||
|
if (i === idx) {
|
||||||
|
it.setAttribute('focus', '')
|
||||||
|
} else {
|
||||||
|
it.removeAttribute('focus')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发列表选择
|
||||||
|
_fetchSelect(idx, needUpdateStyle) {
|
||||||
|
var item = this.props.LIST[idx]
|
||||||
|
this.value = item.value
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('select', {
|
||||||
|
detail: item
|
||||||
|
})
|
||||||
|
)
|
||||||
|
if (needUpdateStyle) {
|
||||||
|
this._updateStyle(idx)
|
||||||
|
}
|
||||||
|
this.props.active = false
|
||||||
|
this.__OPTG__.classList.remove('show')
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
var prepend = this.__PREPEND__.assignedNodes()
|
||||||
|
var append = this.__APPEND__.assignedNodes()
|
||||||
|
|
||||||
|
// 相同插槽, 只允许1个
|
||||||
|
while (prepend.length > 1) {
|
||||||
|
this.removeChild(prepend.pop())
|
||||||
|
}
|
||||||
|
while (append.length > 1) {
|
||||||
|
this.removeChild(append.pop())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepend.length && this.props.type !== 'textarea') {
|
||||||
|
this.__OUTER__.setAttribute('prepend', '')
|
||||||
|
}
|
||||||
|
if (append.length && this.props.type !== 'textarea') {
|
||||||
|
this.__OUTER__.setAttribute('append', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function initPos() {
|
||||||
|
var { x, y, width } = this.getBoundingClientRect()
|
||||||
|
var size = this.getAttribute('size')
|
||||||
|
this.props.active = true
|
||||||
|
if (size && size === 'mini') {
|
||||||
|
y += 32
|
||||||
|
} else {
|
||||||
|
y += 50
|
||||||
|
}
|
||||||
|
this.__OPTG__.style.cssText = `left:${x}px;top:${y}px;width:${width}px;`
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------- */
|
||||||
|
/* ----------------- 各种事件 ------------------ */
|
||||||
|
/* ---------------------------------------------------- */
|
||||||
|
|
||||||
|
// 键盘事件
|
||||||
|
this._handleKeydown = $.catch(this.__INPUT__, 'keydown', ev => {
|
||||||
|
if (this.disabled || this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// up: 38, down: 40
|
||||||
|
if (ev.keyCode === 38 || ev.keyCode === 40) {
|
||||||
|
if (!this.props.active) {
|
||||||
|
initPos.call(this)
|
||||||
|
this.__OPTG__.classList.toggle('show', true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return this._moveSelect(ev)
|
||||||
|
}
|
||||||
|
// 回车触发select事件
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
if (this.props.mvidx !== null && this.props.active) {
|
||||||
|
return this._fetchSelect(this.props.mvidx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 渲染列表
|
||||||
|
this._activeFn = $.bind(this.__INPUT__, 'click', ev => {
|
||||||
|
var { options } = this.props
|
||||||
|
|
||||||
|
if (this.disabled || this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
initPos.call(this)
|
||||||
|
this.__OPTG__.classList.toggle('show')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 选择选项
|
||||||
|
this._handleSelect = $.bind(this.__OPTG__, 'click', ev => {
|
||||||
|
if (ev.target.tagName === 'DD' && !ev.target.hasAttribute('disabled')) {
|
||||||
|
this._fetchSelect(+ev.target.dataset.idx, true)
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this._inactiveFn = $.outside(this, ev => {
|
||||||
|
this.__OPTG__.classList.toggle('show', false)
|
||||||
|
this.props.active = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
// label和placeholder 功能相同
|
||||||
|
case 'label':
|
||||||
|
case 'placeholder':
|
||||||
|
this.__INPUT__.setAttribute('placeholder', val)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'options':
|
||||||
|
if (val) {
|
||||||
|
try {
|
||||||
|
this._renderOptions(JSON.parse(val))
|
||||||
|
} catch (err) {}
|
||||||
|
this.removeAttribute('options')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'value':
|
||||||
|
this.value = val
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'readonly':
|
||||||
|
case 'disabled':
|
||||||
|
var k = name
|
||||||
|
if (k === 'readonly') {
|
||||||
|
k = 'readOnly'
|
||||||
|
}
|
||||||
|
this[k] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this.__INPUT__, 'keydown', this._handleKeydown)
|
||||||
|
$.unbind(this.__INPUT__, 'click', this._activeFn)
|
||||||
|
$.unbind(this.__OPTG__, 'click', this._handleSelect)
|
||||||
|
$.clearOutside(this._inactiveFn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-select')){
|
||||||
|
customElements.define('wc-select', Select)
|
||||||
|
}
|
|
@ -0,0 +1,301 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import $ from "../utils.js"
|
||||||
|
|
||||||
|
export default class Star extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["value","text","size","color","'allow-half'","'show-value'","starSize","disabled"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
value: 0,
|
||||||
|
text: [],
|
||||||
|
size: '',
|
||||||
|
color: '',
|
||||||
|
'allow-half': false,
|
||||||
|
'show-value': false,
|
||||||
|
starSize: 32, // 星星的宽度, 用于实现半星
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
--size: 24px; }
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 0;
|
||||||
|
cursor: inherit; }
|
||||||
|
label wc-icon {
|
||||||
|
margin: 0 3px;
|
||||||
|
transition: transform 0.1s easein-out; }
|
||||||
|
label wc-icon:hover {
|
||||||
|
transform: scale(1.05); }
|
||||||
|
label span {
|
||||||
|
padding: 0 8px;
|
||||||
|
margin: 0 3px; }
|
||||||
|
|
||||||
|
:host([size='large']) {
|
||||||
|
font-size: 16px;
|
||||||
|
--size: 36px; }
|
||||||
|
|
||||||
|
:host([size='medium']) {
|
||||||
|
--size: 30px; }
|
||||||
|
|
||||||
|
:host([size='mini']) {
|
||||||
|
font-size: 12px;
|
||||||
|
--size: 20px; }
|
||||||
|
|
||||||
|
:host([color='red']) label span {
|
||||||
|
color: var(--color-red-1); }
|
||||||
|
|
||||||
|
:host([color='teal']) label span {
|
||||||
|
color: var(--color-teal-1); }
|
||||||
|
|
||||||
|
:host([color='green']) label span {
|
||||||
|
color: var(--color-green-1); }
|
||||||
|
|
||||||
|
:host([color='grey']) label span {
|
||||||
|
color: var(--color-grey-1); }
|
||||||
|
|
||||||
|
:host([color='blue']) label span {
|
||||||
|
color: var(--color-blue-1); }
|
||||||
|
|
||||||
|
:host([color='purple']) label span {
|
||||||
|
color: var(--color-purple-1); }
|
||||||
|
|
||||||
|
:host([color='orange']) label span {
|
||||||
|
color: var(--color-orange-1); }
|
||||||
|
|
||||||
|
:host([disabled]) {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.6; }
|
||||||
|
:host([disabled]) label wc-icon:hover {
|
||||||
|
transform: none; }
|
||||||
|
</style>
|
||||||
|
<label>
|
||||||
|
<wc-icon data-idx="0" is="star" color="grey"></wc-icon>
|
||||||
|
<wc-icon data-idx="1" is="star" color="grey"></wc-icon>
|
||||||
|
<wc-icon data-idx="2" is="star" color="grey"></wc-icon>
|
||||||
|
<wc-icon data-idx="3" is="star" color="grey"></wc-icon>
|
||||||
|
<wc-icon data-idx="4" is="star" color="grey"></wc-icon>
|
||||||
|
<span class="text"></span>
|
||||||
|
</label>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
this.__BOX__ = this.root.children[1]
|
||||||
|
this.__STARS__ = Array.from(this.__BOX__.children)
|
||||||
|
this.__TEXT__ = this.__STARS__.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
var v = +val
|
||||||
|
var tmp = val >> 0
|
||||||
|
if (v === v && v > 0) {
|
||||||
|
val = v
|
||||||
|
} else {
|
||||||
|
val = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val > 5) {
|
||||||
|
val = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.value = val
|
||||||
|
this._updateDraw(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新图标渲染
|
||||||
|
* i: int
|
||||||
|
* f: float
|
||||||
|
*/
|
||||||
|
_updateDraw(i, f = 0) {
|
||||||
|
var _last = 'star-half'
|
||||||
|
var { value, tmp = { i: 0, f: 0 } } = this.props
|
||||||
|
|
||||||
|
if (i === -1) {
|
||||||
|
i = Math.floor(value)
|
||||||
|
f = +(value % 1).toFixed(1)
|
||||||
|
if (i > 0 && i === value) {
|
||||||
|
i--
|
||||||
|
f = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.props['allow-half']) {
|
||||||
|
f = f > 0 ? 1 : 0
|
||||||
|
}
|
||||||
|
// 减少DOM操作
|
||||||
|
if (i === tmp.i && f === tmp.f) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f > 0.5) {
|
||||||
|
_last = 'star-full'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__STARS__.forEach((it, k) => {
|
||||||
|
it.setAttribute('is', k < i ? 'star-full' : 'star')
|
||||||
|
it.setAttribute('color', k < i ? this.props.color : 'grey')
|
||||||
|
})
|
||||||
|
|
||||||
|
if (f > 0) {
|
||||||
|
this.__STARS__[i].setAttribute('is', _last)
|
||||||
|
this.__STARS__[i].setAttribute('color', this.props.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存结果
|
||||||
|
this.props.tmp = { i, f }
|
||||||
|
|
||||||
|
if (i === 0 && f === 0) {
|
||||||
|
this.__TEXT__.textContent = ''
|
||||||
|
} else {
|
||||||
|
if (this.props.text.length === 5) {
|
||||||
|
this.__TEXT__.textContent = this.props.text[i]
|
||||||
|
} else {
|
||||||
|
if (this.props['show-value']) {
|
||||||
|
this.__TEXT__.textContent = i + f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
$.catch(this.__BOX__, 'mousemove', ev => {
|
||||||
|
if (this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ev.target.tagName === 'WC-ICON') {
|
||||||
|
let idx = +ev.target.dataset.idx
|
||||||
|
this._updateDraw(idx, +(ev.offsetX / this.props.starSize).toFixed(1))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$.catch(this.__BOX__, 'click', ev => {
|
||||||
|
var { tmp, disabled } = this.props
|
||||||
|
if (disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ev.target.tagName === 'WC-ICON') {
|
||||||
|
this.props.value = tmp.i + tmp.f
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$.catch(this.__BOX__, 'mouseleave', ev => {
|
||||||
|
if (this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._updateDraw(-1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'size':
|
||||||
|
this.props.starSize = this.__STARS__[0].clientWidth
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'allow-half':
|
||||||
|
case 'show-value':
|
||||||
|
case 'disabled':
|
||||||
|
this.props[name] = true
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'color':
|
||||||
|
if (val) {
|
||||||
|
this.props.color = val
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'text':
|
||||||
|
if (val) {
|
||||||
|
val = val.split('|')
|
||||||
|
if (val.length === 5) {
|
||||||
|
this.props.text = val.map(it => it.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'value':
|
||||||
|
this.value = val
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-star')){
|
||||||
|
customElements.define('wc-star', Star)
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent.io@gmail.com)
|
||||||
|
* @date 2020-12-08 11:30:52
|
||||||
|
* @version v1.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import $ from "../utils.js"
|
||||||
|
export default class Switch extends HTMLElement {
|
||||||
|
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["'active-text'","'inactive-text'","checked","disabled"]
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
'active-text': null,
|
||||||
|
'inactive-text': null,
|
||||||
|
checked: false,
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'root', {
|
||||||
|
value: this.attachShadow({ mode: 'open' }),
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.root.innerHTML = `<style>* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--color-teal-1: #4db6ac;
|
||||||
|
--color-teal-2: #26a69a;
|
||||||
|
--color-teal-3: #009688;
|
||||||
|
--color-green-1: #81c784;
|
||||||
|
--color-green-2: #66bb6a;
|
||||||
|
--color-green-3: #4caf50;
|
||||||
|
--color-purple-1: #9575cd;
|
||||||
|
--color-purple-2: #9575cd;
|
||||||
|
--color-purple-3: #673ab7;
|
||||||
|
--color-blue-1: #64b5f6;
|
||||||
|
--color-blue-2: #42a5f5;
|
||||||
|
--color-blue-3: #2196f3;
|
||||||
|
--color-red-1: #ff5061;
|
||||||
|
--color-red-2: #eb3b48;
|
||||||
|
--color-red-3: #ce3742;
|
||||||
|
--color-orange-1: #ffb618;
|
||||||
|
--color-orange-2: #f39c12;
|
||||||
|
--color-orange-3: #e67e22;
|
||||||
|
--color-plain-1: #f2f5fc;
|
||||||
|
--color-plain-2: #e8ebf4;
|
||||||
|
--color-plain-3: #dae1e9;
|
||||||
|
--color-grey-1: #bdbdbd;
|
||||||
|
--color-grey-2: #9e9e9e;
|
||||||
|
--color-grey-3: #757575;
|
||||||
|
--color-dark-1: #62778d;
|
||||||
|
--color-dark-2: #526273;
|
||||||
|
--color-dark-3: #425064; }
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: inline-block; }
|
||||||
|
:host section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 0;
|
||||||
|
white-space: nowrap; }
|
||||||
|
:host label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 3px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 21px;
|
||||||
|
background: var(--color-plain-3);
|
||||||
|
cursor: inherit; }
|
||||||
|
:host label.checked {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
background: var(--color-grey-3); }
|
||||||
|
:host .dot {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff; }
|
||||||
|
|
||||||
|
:host([disabled]) {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6; }
|
||||||
|
|
||||||
|
:host([size='large']) label {
|
||||||
|
width: 46px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 3px 5px; }
|
||||||
|
|
||||||
|
:host([size='large']) .dot {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px; }
|
||||||
|
|
||||||
|
:host([size='medium']) label {
|
||||||
|
width: 38px;
|
||||||
|
height: 22px;
|
||||||
|
padding: 3px 4px; }
|
||||||
|
|
||||||
|
:host([size='medium']) .dot {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px; }
|
||||||
|
|
||||||
|
:host([size='mini']) label {
|
||||||
|
width: 22px;
|
||||||
|
height: 14px;
|
||||||
|
padding: 2px; }
|
||||||
|
|
||||||
|
:host([size='mini']) .dot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px; }
|
||||||
|
|
||||||
|
:host([color='red']) label.checked {
|
||||||
|
background: var(--color-red-1); }
|
||||||
|
|
||||||
|
:host([color='blue']) label.checked {
|
||||||
|
background: var(--color-blue-1); }
|
||||||
|
|
||||||
|
:host([color='green']) label.checked {
|
||||||
|
background: var(--color-green-1); }
|
||||||
|
|
||||||
|
:host([color='teal']) label.checked {
|
||||||
|
background: var(--color-teal-1); }
|
||||||
|
|
||||||
|
:host([color='orange']) label.checked {
|
||||||
|
background: var(--color-orange-1); }
|
||||||
|
|
||||||
|
:host([color='dark']) label.checked {
|
||||||
|
background: var(--color-dark-1); }
|
||||||
|
|
||||||
|
:host([color='purple']) label.checked {
|
||||||
|
background: var(--color-purple-1); }
|
||||||
|
</style>
|
||||||
|
<section>
|
||||||
|
<label>
|
||||||
|
<span class="dot"></span>
|
||||||
|
</label>
|
||||||
|
<slot></slot>
|
||||||
|
</section>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
this.__SWITCH__ = this.root.lastElementChild.firstElementChild
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.props.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
this.checked = val
|
||||||
|
}
|
||||||
|
|
||||||
|
get checked() {
|
||||||
|
return this.props.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
set checked(val) {
|
||||||
|
this.props.checked = !!val
|
||||||
|
this.__SWITCH__.classList.toggle('checked', this.props.checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this._handleClick = $.bind(this, 'click', ev => {
|
||||||
|
if (this.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.checked = !this.checked
|
||||||
|
if (this.checked) {
|
||||||
|
if (this.props['active-text'] !== null) {
|
||||||
|
this.textContent = this.props['active-text']
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.props['inactive-text'] !== null) {
|
||||||
|
this.textContent = this.props['inactive-text']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent('input'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
$.unbind(this, 'click', this._handleClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, val) {
|
||||||
|
if (val === null || old === val) {return}
|
||||||
|
switch (name) {
|
||||||
|
case 'checked':
|
||||||
|
case 'disabled':
|
||||||
|
this[name] = true
|
||||||
|
break
|
||||||
|
case 'active-text':
|
||||||
|
case 'inactive-text':
|
||||||
|
this.props[name] = val + ''
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!customElements.get('wc-switch')){
|
||||||
|
customElements.define('wc-switch', Switch)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent@doui.cc)
|
||||||
|
* @date 2020-07-07 16:27:17
|
||||||
|
* @version v2.0.1
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
import SVG_DICT from"./svg.js";let dict=SVG_DICT;window.EXT_SVG_DICT&&Object.assign(dict,EXT_SVG_DICT);export default class Icon extends HTMLElement{static get observedAttributes(){return["is"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{is:""},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-block;color:inherit}:host(:not([is])){display:none}.icon{display:block;width:var(--size, 32px);height:var(--size, 32px);margin:var(--pad, auto);fill:currentColor}.icon.load{animation:load 1.5s linear infinite}.icon circle{stroke:currentColor;animation:circle 1.5s ease-in-out infinite}:host([size='large']) .icon{width:42px;height:42px}:host([size='medium']) .icon{width:38px;height:38px}:host([size='mini']) .icon{width:20px;height:20px}:host([color='red']){color:#ff5061}:host([color='blue']){color:#64b5f6}:host([color='green']){color:#81c784}:host([color='teal']){color:#4db6ac}:host([color='orange']){color:#ffb618}:host([color='dark']){color:#62778d}:host([color='purple']){color:#9575cd}:host([color='grey']){color:#bdbdbd}@keyframes circle{0%{stroke-dasharray:0, 3812px;stroke-dashoffset:0}50%{stroke-dasharray:1906px, 3812px;stroke-dashoffset:-287px}100%{stroke-dasharray:1906px, 3812px;stroke-dashoffset:-2393px}}@keyframes load{to{transform:rotate(360deg)}}</style> <svg class=\"icon\" viewBox=\"0 0 1024 1024\"></svg> ",this.__ICO__=this.root.lastElementChild,this.drawPath()}get is(){return this.props.is}set is(o){o&&this.setAttribute("is",o)}drawPath(){var{is:o}=this.props,t=dict[o];this.__ICO__&&o&&t&&(this.__ICO__.innerHTML="loading"===o?t:`<path d="${t}" />`,this.__ICO__.classList.toggle("load","loading"===o))}attributeChangedCallback(o,t,e){if(null!==e&&t!==e)switch(o){case"is":this.props.is=e,e?this.drawPath():this.removeAttribute("is")}}};
|
||||||
|
|
||||||
|
if(!customElements.get('wc-icon')){
|
||||||
|
customElements.define('wc-icon', Icon)
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* 与主进程的通讯
|
||||||
|
* @author yutent<yutent.io@gmail.com>
|
||||||
|
* @date 2020/07/14 11:42:02
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { ipcRenderer } = require('electron')
|
||||||
|
const EventEmitter = require('events')
|
||||||
|
const util = require('util')
|
||||||
|
|
||||||
|
class Socket {
|
||||||
|
constructor() {
|
||||||
|
ipcRenderer.on('app', (ev, conn) => {
|
||||||
|
this.emit(conn.type, conn.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(type = '', data = {}) {
|
||||||
|
return ipcRenderer.sendSync('app', { data, type })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(Socket, EventEmitter)
|
||||||
|
|
||||||
|
export default new Socket()
|
|
@ -0,0 +1 @@
|
||||||
|
function noop(){}export default{nextTick:function(){let t=[];let e=document.createTextNode("\x3c!-- --\x3e");new MutationObserver(function(){let e=t.length;for(let n=0;n<e;n++)t[n]();t=t.slice(e)}).observe(e,{characterData:!0});let n=!1;return function(i){t.push(i),n=!n,e.data=n}}(),each(t,e){if(t)if(Array.isArray(t))for(let n,i=0;(n=t[i++])&&!1!==e(n,i-1););else for(let n in t)if(t.hasOwnProperty(n)&&!1===e(t[n],n))break},bind(t,e,n=noop,i=!1){let o=e.split(",");return this.each(o,function(e){e=e.trim(),t.addEventListener(e,n,i)}),n},catch(t,e,n,i){return this.bind(t,e,function(t){t.stopPropagation(),n&&n(t)},i)},unbind(t,e,n=noop,i=!1){let o=e.split(",");this.each(o,function(e){e=e.trim(),t.removeEventListener(e,n,i)})},outside(t,e=noop){return this.bind(document,"mousedown",n=>{if(n)if(n.path){for(var i=n.path.concat();i.length>3;)if(i.shift()===t)return}else{var o=n.explicitOriginalTarget||n.target;if(t===o||t.contains(o)||t.root&&t.root.contains(o))return}e(n)})},clearOutside(t=noop){this.unbind(document,"mousedown",t)}};
|
|
@ -0,0 +1,153 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author yutent<yutent@doui.cc>
|
||||||
|
* @date 2019/09/16 20:51:19
|
||||||
|
*/
|
||||||
|
|
||||||
|
const {
|
||||||
|
app,
|
||||||
|
BrowserWindow,
|
||||||
|
protocol,
|
||||||
|
ipcMain,
|
||||||
|
net,
|
||||||
|
Notification
|
||||||
|
} = require('electron')
|
||||||
|
const path = require('path')
|
||||||
|
const fs = require('iofs')
|
||||||
|
|
||||||
|
const { createMainWindow, createFloatWindow } = require('./tools/window')
|
||||||
|
const createMenu = require('./tools/menu')
|
||||||
|
const createTay = require('./tools/tray')
|
||||||
|
|
||||||
|
const MIME_TYPES = {
|
||||||
|
'.js': 'text/javascript',
|
||||||
|
'.html': 'text/html',
|
||||||
|
'.htm': 'text/plain',
|
||||||
|
'.css': 'text/css',
|
||||||
|
'.jpg': 'image/jpg',
|
||||||
|
'.png': 'image/png',
|
||||||
|
'.gif': 'image/gif',
|
||||||
|
'.svg': 'image/svg+xml',
|
||||||
|
'.ico': 'image/ico'
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROOT = __dirname
|
||||||
|
|
||||||
|
var timer
|
||||||
|
|
||||||
|
function fetch(url) {
|
||||||
|
return new Promise((y, n) => {
|
||||||
|
var conn = net.request(url)
|
||||||
|
var r = []
|
||||||
|
|
||||||
|
conn.on('response', res => {
|
||||||
|
res.on('data', c => {
|
||||||
|
r.push(c)
|
||||||
|
})
|
||||||
|
|
||||||
|
res.on('end', _ => {
|
||||||
|
y(Buffer.concat(r).toString())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.on('error', e => {
|
||||||
|
n(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function ring() {
|
||||||
|
var n = 5
|
||||||
|
var t = setInterval(() => {
|
||||||
|
var notify = new Notification({
|
||||||
|
title: '搞基⏰',
|
||||||
|
subtitle: '神奇的2点半到啦',
|
||||||
|
body: '神奇的2点半到啦, 该加仓的加仓, 该卖的卖啦'
|
||||||
|
})
|
||||||
|
notify.show()
|
||||||
|
n--
|
||||||
|
if (n === 0) {
|
||||||
|
clearInterval(t)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------- */
|
||||||
|
app.commandLine.appendSwitch('--lang', 'zh-CN')
|
||||||
|
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
|
||||||
|
|
||||||
|
protocol.registerSchemesAsPrivileged([
|
||||||
|
{ scheme: 'app', privileges: { secure: true, standard: true } }
|
||||||
|
])
|
||||||
|
|
||||||
|
/* ----------------------------------------------------- */
|
||||||
|
|
||||||
|
app.dock.hide()
|
||||||
|
|
||||||
|
// 初始化应用
|
||||||
|
app.once('ready', () => {
|
||||||
|
// 注册协议
|
||||||
|
protocol.registerBufferProtocol('app', (req, cb) => {
|
||||||
|
let file = req.url.replace(/^app:\/\/local\//, '')
|
||||||
|
let ext = path.extname(req.url)
|
||||||
|
let buff = fs.cat(path.resolve(ROOT, file))
|
||||||
|
cb({ data: buff, mimeType: MIME_TYPES[ext] })
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建浏览器窗口
|
||||||
|
app.__main__ = createMainWindow(path.resolve(ROOT, './images/app.png'))
|
||||||
|
app.__float__ = createFloatWindow()
|
||||||
|
|
||||||
|
createMenu(app.__main__)
|
||||||
|
createTay(app.__float__, app.__main__)
|
||||||
|
|
||||||
|
app.__main__.on('closed', () => {
|
||||||
|
app.__main__ = null
|
||||||
|
app.__float__ = null
|
||||||
|
app.exit()
|
||||||
|
})
|
||||||
|
|
||||||
|
// mac专属事件,点击dock栏图标,可激活窗口
|
||||||
|
// app.on('activate', _ => {
|
||||||
|
// if (app.__main__) {
|
||||||
|
// app.__main__.restore()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('app', (ev, conn) => {
|
||||||
|
switch (conn.type) {
|
||||||
|
case 'fetch':
|
||||||
|
fetch(conn.data).then(r => {
|
||||||
|
ev.returnValue = r
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'notify':
|
||||||
|
clearTimeout(timer)
|
||||||
|
var t1 = Date.now()
|
||||||
|
var t2 = new Date()
|
||||||
|
t2.setHours(14)
|
||||||
|
t2.setMinutes(30)
|
||||||
|
t2.setSeconds(0)
|
||||||
|
|
||||||
|
if (t2.getTime() - t1 > 0) {
|
||||||
|
timer = setTimeout(ring, t2.getTime() - t1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.returnValue = true
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'data-reload':
|
||||||
|
app.__main__.webContents.send('app', { type: 'data-reload', data: null })
|
||||||
|
ev.returnValue = true
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'devtools':
|
||||||
|
app.__main__.openDevTools()
|
||||||
|
ev.returnValue = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* 菜单项
|
||||||
|
* @author yutent<yutent.io@gmail.com>
|
||||||
|
* @date 2020/12/10 19:30:02
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { Menu } = require('electron')
|
||||||
|
|
||||||
|
module.exports = function(win) {
|
||||||
|
var menuList = Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: '搞基数据',
|
||||||
|
submenu: [
|
||||||
|
{ role: 'about', label: '关于搞基数据' },
|
||||||
|
{ type: 'separator' },
|
||||||
|
{
|
||||||
|
label: '退出',
|
||||||
|
accelerator: 'Command+Q',
|
||||||
|
click(a, b, ev) {
|
||||||
|
win.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
submenu: [
|
||||||
|
{ role: 'cut', label: '剪切' },
|
||||||
|
{ role: 'copy', label: '复制' },
|
||||||
|
{ role: 'paste', label: '粘贴' },
|
||||||
|
{ role: 'selectall', label: '全选' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
Menu.setApplicationMenu(menuList)
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* 托盘
|
||||||
|
* @author yutent<yutent.io@gmail.com>
|
||||||
|
* @date 2020/12/10 19:30:20
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { Tray, Menu } = require('electron')
|
||||||
|
const path = require('path')
|
||||||
|
const ROOT = __dirname
|
||||||
|
|
||||||
|
module.exports = function(mini, main) {
|
||||||
|
var menuList = Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: '显示主窗口',
|
||||||
|
click(a, b, ev) {
|
||||||
|
main.restore()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '不搞基了',
|
||||||
|
accelerator: 'Command+Q',
|
||||||
|
click(a, b, ev) {
|
||||||
|
main.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
var tray = new Tray(path.join(ROOT, '../images/tray.png'))
|
||||||
|
|
||||||
|
tray.on('click', _ => {
|
||||||
|
var b = tray.getBounds()
|
||||||
|
mini.setBounds({ x: b.x - 120, y: b.y + b.height })
|
||||||
|
mini.show()
|
||||||
|
mini.focus()
|
||||||
|
mini.webContents.send('app', { type: 'float-visible', data: null })
|
||||||
|
})
|
||||||
|
|
||||||
|
tray.on('right-click', _ => {
|
||||||
|
tray.popUpContextMenu(menuList)
|
||||||
|
})
|
||||||
|
|
||||||
|
main.__tray__ = tray
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author yutent<yutent.io@gmail.com>
|
||||||
|
* @date 2020/12/10 14:57:49
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { BrowserWindow } = require('electron')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用主窗口
|
||||||
|
*/
|
||||||
|
exports.createMainWindow = function(icon) {
|
||||||
|
var win = new BrowserWindow({
|
||||||
|
title: '搞基数据',
|
||||||
|
width: 1024,
|
||||||
|
height: 540,
|
||||||
|
frame: false,
|
||||||
|
titleBarStyle: 'hiddenInset',
|
||||||
|
resizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
icon,
|
||||||
|
transparent: true,
|
||||||
|
vibrancy: 'hud',
|
||||||
|
visualEffectState: 'active',
|
||||||
|
webPreferences: {
|
||||||
|
// webSecurity: false,
|
||||||
|
experimentalFeatures: true,
|
||||||
|
nodeIntegration: true,
|
||||||
|
spellcheck: false
|
||||||
|
},
|
||||||
|
show: false
|
||||||
|
})
|
||||||
|
|
||||||
|
win.loadURL('app://local/index.html')
|
||||||
|
|
||||||
|
// win.on('ready-to-show', _ => {
|
||||||
|
// win.show()
|
||||||
|
// win.openDevTools()
|
||||||
|
// })
|
||||||
|
|
||||||
|
win.on('close', ev => {
|
||||||
|
ev.preventDefault()
|
||||||
|
win.hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
return win
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建悬浮窗口
|
||||||
|
exports.createFloatWindow = function() {
|
||||||
|
var win = new BrowserWindow({
|
||||||
|
width: 280,
|
||||||
|
height: 360,
|
||||||
|
resizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
frame: false,
|
||||||
|
show: false,
|
||||||
|
vibrancy: 'hud',
|
||||||
|
visualEffectState: 'active',
|
||||||
|
webPreferences: {
|
||||||
|
experimentalFeatures: true,
|
||||||
|
nodeIntegration: true,
|
||||||
|
spellcheck: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// win.openDevTools()
|
||||||
|
|
||||||
|
win.on('blur', ev => {
|
||||||
|
win.hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
win.loadURL('app://local/float.html')
|
||||||
|
|
||||||
|
return win
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
|
||||||
|
<title></title>
|
||||||
|
<link href="/lib/css/reset-basic.css" rel="stylesheet">
|
||||||
|
<link href="/css/float.css" rel="stylesheet">
|
||||||
|
<script src="/js/float.js" type="module"></script>
|
||||||
|
</head>
|
||||||
|
<body class="noselect">
|
||||||
|
|
||||||
|
<div class="app" anot="app">
|
||||||
|
<wc-scroll class="list">
|
||||||
|
<section class="item" :for="it in list">
|
||||||
|
<div class="info">
|
||||||
|
<h3 class="text-ell" :text="it.name"></h3>
|
||||||
|
<cite :text="it.code"></cite>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="today">
|
||||||
|
<span class="money" :text="'¥' + it.cm"></span>
|
||||||
|
<span
|
||||||
|
class="percent"
|
||||||
|
:class="{red: it.cp > 0, green: it.cp < 0}"
|
||||||
|
:text="it.cp + '%'">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section :if="list.length === 0" class="item">啥基都没有...</section>
|
||||||
|
</wc-scroll>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue