This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
appcat
/
sonist
Archived
1
0
Fork 0

Compare commits

..

37 Commits

Author SHA1 Message Date
yutent 298dcd1ecb Update Readme.md 2023-08-25 15:37:06 +08:00
yutent c725ca63d8 update 2022-09-12 19:06:56 +08:00
yutent 55bdea11bb update 2022-08-29 00:43:21 +08:00
宇天 cabfa462a4 update 2020-12-25 14:20:19 +08:00
宇天 8348d50495 update 2020-12-14 19:26:26 +08:00
宇天 7a696c61b2 fixed 2020-12-13 22:08:25 +08:00
宇天 539a942906 update 2020-12-13 21:28:00 +08:00
宇天 82cc79a46f 增加tray控制按钮 2020-11-27 18:43:40 +08:00
宇天 991661d03c 更新图标, 更新tray 2020-11-27 17:48:30 +08:00
宇天 09b6b4c93c 更新图标 2020-11-27 11:30:59 +08:00
宇天 ea229c0a38 update 2020-11-23 22:39:19 +08:00
宇天 995a967312 修复播放切换,增加歌词搜索 2020-11-20 19:09:12 +08:00
宇天 bf84344f9c 优化歌曲切换 2020-11-20 18:35:03 +08:00
宇天 641199aa1f 完成播放功能 2020-11-20 18:23:53 +08:00
宇天 b21ec91170 重构音频播放 2020-11-19 20:49:53 +08:00
宇天 6bd72fcbac 优化封面 2020-11-18 19:53:07 +08:00
宇天 c8a7baf49d update 2020-11-18 19:33:09 +08:00
宇天 82b066a832 完成歌曲扫描 2020-11-18 19:24:49 +08:00
宇天 2f746df5fd 引入ID3读写模块 2020-11-18 18:32:20 +08:00
宇天 ea4bdddffa 重写进程通讯交互 2020-11-18 16:32:33 +08:00
宇天 cb66f9b287 update 2020-11-17 19:03:09 +08:00
宇天 db99d5c86b 一大波更新 2020-11-17 18:56:30 +08:00
宇天 df36541c70 大重构 2020-11-16 19:38:44 +08:00
宇天 6d5c72f235 2.x 2020-11-16 11:22:17 +08:00
宇天 039b70c4b6 update 2019-11-06 00:28:28 +08:00
宇天 faed0b59b3 Merge branch 'master' of github.com:yutent/sonist 2019-11-05 23:57:47 +08:00
宇天 19f429d163 更新组件库 2019-11-05 23:57:24 +08:00
宇天 38118ad788 一大波更新 2019-09-10 17:41:03 +08:00
宇天 dd2da3d4c7 一大波更新适配 2019-09-09 21:04:47 +08:00
宇天 691ef9f437 Merge branch 'master' of github.com:yutent/sonist 2019-09-09 20:21:52 +08:00
宇天 b72a8d4780 更新6.x的API 2019-09-09 00:56:32 +08:00
宇天 d1dcba7711 升级electron到6.0 2019-09-08 23:54:49 +08:00
宇天 a8f57fccba 更换组件库 2019-09-07 23:37:53 +08:00
宇天 a259a4693b UI更新 2019-09-02 00:05:51 +08:00
宇天 b1bcf6d14d 升级UI库 2019-09-01 23:20:34 +08:00
宇天 ba238b8aa2 update 2019-08-31 23:59:20 +08:00
宇天 8d9f8b9b26 u 2019-08-12 21:26:41 +08:00
155 changed files with 1709 additions and 2051 deletions

View File

@ -2,14 +2,15 @@
> 一个音乐播放器, 主打本地音乐播放。支持 自动歌词/自动封面/均衡器等常见功能。
>> 同时利用酷狗音乐的API(**来源于网络,仅供学习使用**), 获取实时的云音乐(**仅免费的那部分,付费部分无法提供**)。
# 此项目不再维护 (请关注Gtk版)
[Sonist-Gtk](https://github.com/app-cat/sonist-gtk
界面预览
![demo](./demo1.jpg)
![demo](./demo1.png)
![demo](./demo2.jpg)
![demo](./demo2.png)
![demo](./demo3.jpg)
## 开发计划 & 进度

BIN
demo1.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

BIN
demo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
demo2.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

BIN
demo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
demo3.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 67 KiB

BIN
icons/512x512.psd Normal file

Binary file not shown.

Binary file not shown.

BIN
icons/apps.icns Normal file

Binary file not shown.

BIN
icons/tray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
icons/tray.psd Normal file

Binary file not shown.

View File

@ -1,35 +1,40 @@
{
"name": "sonist",
"version": "1.1.0",
"version": "2.0.0-alpha-4",
"description": "Music Player",
"main": "src/main.js",
"scripts": {
"start": "electron .",
"pack": "electron-builder"
"start": "electron ."
},
"author": {
"name": "yutent",
"email": "yutent@doui.cc"
"email": "yutent.io@gmail.com"
},
"homepage": "https://yutent.me",
"homepage": "https://yutent.top",
"license": "MIT",
"dependencies": {
"crypto.js": "^1.2.0",
"iofs": "^1.1.0"
},
"devDependencies": {
"electron": "^4.0.2",
"electron-builder": "^20.38.5"
"crypto.js": "^2.0.2",
"iofs": "^1.5.1",
"music-metadata": "^7.5.0"
},
"build": {
"appId": "cc.doui.sonist",
"appId": "sonist",
"productName": "Sonist",
"copyright": "Copyright © 2019 ${author}",
"directories": {
"buildResources": "icons",
"output": "build"
},
"files": ["src/**/*", "node_modules/iofs/*", "node_modules/crypto.js/*"],
"electronDownload": {
"version": "10.1.5",
"mirror": "https://npm.taobao.org/mirrors/electron/"
},
"files": [
"src/**/*",
"node_modules/iofs/*",
"node_modules/crypto.js/*",
"node_modules/music-metadata/*"
],
"mac": {
"category": "public.app-category.music",
"target": "dmg",
@ -51,15 +56,7 @@
"icon": "./icons/"
},
"deb": {
"depends": [
"gconf2",
"gconf-service",
"libnotify4",
"libappindicator1",
"libxtst6",
"libnss3",
"ffmpeg"
]
"depends": []
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -5,313 +5,442 @@
* @date 2018/12/16 17:15:07
*/
@import './var.scss';
@import "./var.scss";
.app {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
#app {position:relative;display:flex;flex-flow:column wrap;width:100%;height:100%;background:#fff;
.album-cover {
overflow: hidden;
position: absolute;
z-index: -1;
left: 0;
top: 0;
width: 100%;
height: 394px;
img {
width: 100%;
height: 100%;
object-fit: cover;
filter: blur(35px) opacity(0.8);
}
}
//
.title-bar {position:relative;z-index:9;display:flex;flex:0 5rem;
.btn-box {position:absolute;left:1.2rem;top:0;width:auto;height:3rem;padding:.9rem 0;
.item {display:inline-block;width:1.2rem;height:1.2rem;margin:0 .2rem;background:url(/images/btn-grey.svg) no-repeat;background-size:cover;}
&.focus {
.quit {background-image:url(/images/btn-close.svg);}
.min {background-image:url(/images/btn-mini.svg);}
// .max {background-image:url(/images/btn-maxi.svg);}
}
&:hover {
.quit {background-image:url(/images/btn-close_a.svg);}
.min {background-image:url(/images/btn-mini_a.svg);}
// .max {background-image:url(/images/btn-maxi_a.svg);}
}
}
.btn-box-win {position:absolute;right:1.2rem;top:0;width:auto;height:4rem;padding:.9rem 0;line-height:1.8rem;
.item,
.opt {position:relative;display:inline-block;width:2.2rem;height:2.2rem;margin:0 .2rem;padding:.2rem;font-size:1.6rem;
}
.item {text-align:center;
&:hover {background:rgba(0, 0, 0, .05);}
&.quit:hover {color:nth($cr, 1);}
&.disabled {color:nth($cp, 3);background:none}
}
.opt i {font-size:1.8rem;}
.opt-list {position:absolute;z-index:100;right:0;top:2.2rem;width:13rem;height:auto;padding:.8rem 0;background:#fff;box-shadow:0 .5rem 2rem rgba(0, 0, 0, .1);font-size:1.4rem;
span {display:flex;align-items:center;height:3rem;padding:0 2rem;line-height:3rem;
i {padding-right:.8rem;}
&.pipe {height:.1rem;margin:.5rem 0;border-bottom:.1rem solid nth($cp, 1)}
&:hover {background:nth($cp, 1)}
}
}
}
//
.holder {flex:0 22rem;height:100%;background:nth($cp, 1);}
.tools {flex:1;padding:1rem;
.search {position:relative;display:inline-block;line-height:3rem;}
.icon {position:absolute;right:0;top:0;width:2.6rem;height:3rem;}
input {width:20rem;padding:0 1.3rem;border-radius:1.5rem;}
}
}
//
.main-body {flex:1;display:flex;
//
.sidebar {flex:0 22rem;position:relative;height:100%;background:nth($cp, 1);
//
.user-box {width:18rem;height:16.5rem;margin:0 2rem;text-align:center;
.avatar {overflow:hidden;width:12rem;height:12rem;margin:0 3rem;border:.6rem solid #fff;border-radius:50%;box-shadow:0 .5rem 1.5rem rgba(0, 0, 0, .15);}
img {width:100%;height:100%;}
.uname {line-height:2;font-weight:normal;}
}
//
.music-box {width:100%;height:auto;padding:0 1.5rem;
dt.title {line-height:4rem;color:nth($cgr, 1)}
dd.item {height:3rem;margin:.3rem 0;padding:0 .8rem;line-height:3rem;color:nth($cgr, 3);
.icon {float:left;width:3rem;height:3rem;padding:0 .5rem;font-size:2.4rem;}
&:hover {padding-left:.9rem;color:nth($ct, 1);}
&.active {border-radius:.3rem;background:nth($ct, 1);color:#fff;}
&.disabled {opacity:.25}
}
}
}
//
.module {position:relative;flex:1;display:flex;flex-flow:column wrap;}
}
.contrl-bar {position:relative;z-index:99;display:flex;flex:0 8rem;background:nth($cp, 1);
//
.play-box {flex:0 22rem;display:flex;justify-content:center;align-items:center;height:8rem;padding:1rem 2rem;text-align:center;
.item {flex:0 5rem;margin:0 .5rem;line-height:1;font-size:4.2rem;color:nth($ct, 2);@include ts();
&:hover {color:nth($co, 1)}
&:active {color:nth($ct, 1);transform:scale(1.1)}
}
.play {font-size:5rem;}
}
//
.stat-box {position:relative;flex:1;display:flex;justify-content:center;align-items:center;
.song-stat {flex:1;height:8rem;margin:0 2rem 0 0;
canvas {display:flex;width:100%;height:100%;}
}
.ctrl {position:relative;flex:0 1 3.5rem;height:3rem;line-height:3rem;text-align:center;color:nth($ct, 2);font-size:2rem;
&:hover {color:nth($ct, 1)}
&:active {color:nth($ct, 3)}
&.lrc {margin-right:2rem;font-size:1.6rem;}
.volume-ctrl {display:none;flex-direction:column;justify-content:flex-end;position:absolute;left:.5rem;bottom:3rem;width:2.4rem;height:12rem;padding:1rem .8rem;background:#fff;border-radius:.3rem;box-shadow:0 0 1rem rgba(0, 0, 0, .1);
em {flex:0 0;border-radius:.5rem;background:nth($ct, 1)}
}
&.volume:hover .volume-ctrl {display:flex}
}
}
}
//
&.blur {background:rgba(255, 255, 255, .85);backdrop-filter:blur(1rem);
.title-bar {
.holder {background:rgba(255, 255, 255, .3);}
.tools input {background:rgba(255, 255, 255, .8);}
position: relative;
display: flex;
align-items: center;
z-index: 9;
height: 32px;
background: rgba(32, 32, 32, 0.5);
.btns {
display: flex;
height: 16px;
padding: 0 4px;
}
.btn {
width: 16px;
height: 16px;
margin: 0 6px;
border-radius: 50%;
background: #86909b no-repeat;
background-size: 100%;
}
.btns.active {
.btn.close {
background: #ff5061;
}
.btn.mini {
background: #ffb618;
}
}
}
.main-body {
overflow: hidden;
flex: 1;
display: flex;
justify-content: space-between;
background: rgba(32, 32, 32, 0.5);
.sidebar {background:rgba(255, 255, 255, .3);}
.aside {
width: 180px;
height: 100%;
padding: 0 16px;
line-height: 2;
fieldset {
border: 0;
color: #ebebeb;
font-size: 12px;
legend {
font-size: 18px;
background: linear-gradient(to bottom, #58ffdf 50%, #459888);
background-clip: text;
color: transparent;
}
.contrl-bar {background:rgba(255, 255, 255, .35)}
.item {
padding-left: 12px;
line-height: 1.75;
}
// ktv
&.ktv {
.contrl-bar {background:rgba(233, 233, 233, .1);
.play-box .item,
.stat-box .ctrl {color:#fff;
&:hover {color:nth($cr, 1)}
button {
width: 42px;
height: 16px;
margin-left: 12px;
font-size: 10px;
border: 0;
border-radius: 9px;
background: nth($cb, 1);
color: #fff;
}
}
}
.song-box {
display: flex;
flex-direction: column;
width: 618px;
.ktv-box {overflow:hidden;position:absolute;z-index:80;left:0;top:0;width:100%;height:100%;background-color:nth($cd, 3);background-size:cover;background-repeat:no-repeat;color:#fff;
.preview {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 99px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(200, 200, 200, 0.1);
.inner-content {display:flex;flex-flow:column wrap;width:100%;height:100%;padding-bottom:8rem;background:rgba(29, 35, 44, 0.767);backdrop-filter:blur(1rem);
.info {flex:1;display:flex;justify-content:center;align-items:center;padding:0 10rem;line-height:2;
img {width:30rem;height:30rem;border:.5rem solid rgba(255, 255, 255, .5);border-radius:50%;}
.summary {flex:1; padding:0 5rem}
pre {overflow:auto;height:30rem}
h3 {line-height:3;font-size:1.8rem;}
}
.lrc-box {flex:0 10rem;display:flex;flex-flow:column wrap;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem;
section {flex:1;display:flex;
&.left {justify-content:flex-start;}
&.right {justify-content:flex-end}
span {background-clip:text!important;color:transparent;}
}
}
.tool-box {position:absolute;right:0;top:15rem;width:13rem;height:auto;padding:1.5rem 0;background:rgba(255, 255, 255, .1);border-radius:.3rem 0 0 .3rem;opacity:.3;transform:translateX(8.8rem);@include ts();
.item {height:3.4rem;padding:0 .8rem;line-height:3.4rem;
&:hover {background:rgba(255, 255, 255, .1);}
}
i {padding:0 1rem 0 .8rem}
&:hover {opacity:1;transform:translateX(0)}
}
.slide-down {position:absolute;right:1rem;top:1rem;width:3rem;height:2rem;line-height:1.8rem;border:.1rem solid nth($cgr, 1);border-radius:.3rem;text-align:center;
i {transform:rotate(90deg)}
}
.search-box {display:flex;justify-content:center;align-items:center;position:absolute;left:0;top:0;z-index:82;width:100%;height:56rem;background:rgba(29, 35, 44, 0.5);backdrop-filter:blur(.4rem);
.content {width:60rem;height:auto;padding:2rem;background:rgba(255, 255, 255, .8);color:nth($cd, 1);
.title {height:3rem;line-height:2rem;font-size:1.4rem;text-align:center;
i {float:right;font-size:2rem;color:nth($cr, 1);}
}
.section {height:3.5rem;
input {width:100%;}
}
.result {overflow-y:auto;width:100%;max-height:30rem;padding:1rem;background:rgba(255, 255, 255, .2);
.item {display:flex;justify-content:center;align-items:center;margin:.3rem 0;text-align:center;
&:nth-child(1) {line-height:2;border-bottom:.1rem solid nth($cgr, 1);}
span {flex:1;
&:nth-child(1) {flex:3}
.album {
width: 80px;
height: 80px;
img {
width: 100%;
height: 100%;
}
}
.info {
display: flex;
flex-direction: column;
justify-content: center;
width: 320px;
margin-left: 32px;
strong {
height: 36px;
font-size: 18px;
font-weight: normal;
}
cite {
font-size: 12px;
font-style: normal;
}
p {
font-size: 12px;
color: #bdbdbd;
}
}
.total {
position: absolute;
right: 32px;
bottom: 16px;
font-size: 16px;
font-weight: bold;
font-family: Raleway;
}
}
.scroll-box {
overflow: hidden;
flex: 1;
width: 100%;
padding: 16px 6px;
border-top: 1px solid rgba(32, 32, 32, 0.1);
}
.list {
height: 100%;
font-size: 12px;
.item {
display: flex;
align-items: center;
height: 26px;
padding: 0 12px;
border-radius: 13px;
.idx {
position: relative;
width: 64px;
padding-left: 16px;
font-size: 12px;
font-family: Raleway;
}
.loading {position:fixed;left:0;top:0;z-index:65536;display:flex;justify-content:center;align-items:center;width:100%;height:100%;
.box {position:relative;display:flex;justify-content:center;align-items:center;width:8rem;height:8rem;
i {position:absolute;width:8rem;height:8rem;border:3px solid nth($ct, 1);border-radius:50%;opacity:.5;
&:nth-child(1) {animation:load 2.5s ease-in-out infinite;}
&:nth-child(2) {animation:load 2.5s .5s ease-in-out infinite;}
&:nth-child(3) {animation:load 2.5s 1s ease-in-out infinite;}
&:nth-child(4) {animation:load 2.5s 1.5s ease-in-out infinite;}
&:nth-child(5) {animation:load 2.5s 2s ease-in-out infinite;}
.name {
flex: 1;
}
span {position:absolute;width:8rem;height:8rem;background:url(/images/load1.png) no-repeat center center;background-size:cover;
&:nth-child(6) {animation:play 1.5s linear infinite;}
&:nth-child(7) {background-image:url(/images/load2.png);animation:load2 2.5s linear infinite;}
.artist {
width: 128px;
margin-left: 12px;
}
cite {font-size:2.4rem;}
.duration {
width: 42px;
margin-left: 12px;
}
&.on {
color: #feac23;
font-size: 14px;
.idx::before {
position: absolute;
left: 0;
top: 3px;
font-size: 10px;
content: '';
}
}
&.active,
&:hover {
color: #58ffdf;
background: rgba(29, 77, 68, 0.15);
}
}
}
}
}
.play-bar {
height: 66px;
width: 100%;
background: rgba(24, 24, 24, 0.3);
color: #fff;
.stat-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 20px;
color: #ebebeb;
.time {
width: 42px;
margin: 0 6px;
text-align: center;
font-size: 12px;
}
.progress {
position: relative;
flex: 1;
display: flex;
align-items: flex-start;
height: 3px;
background: #b2cfe3;
input {
position: absolute;
left: 0;
top: 0;
display: block;
width: 100%;
height: 3px;
opacity: 0;
}
}
.thumb {
// width: 0;
height: 3px;
background: #58ffdf;
}
}
.ctrl-box {
display: flex;
align-items: center;
justify-content: space-between;
height: 42px;
padding: 0 16px;
.play-btn {
display: flex;
align-items: center;
justify-content: space-between;
width: 120px;
height: 42px;
.item {
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-size: cover;
transition: background 0.1s ease-in-out;
cursor: pointer;
&.prev {
background-image: url(/images/ctrl/prev.png);
&:hover,
&:active {
background-image: url(/images/ctrl/prev_a.png);
}
}
&.on,
&.off {
width: 42px;
height: 42px;
}
&.on {
background-image: url(/images/ctrl/play.png);
animation: round 2s linear infinite;
&:hover,
&:active {
background-image: url(/images/ctrl/play_a.png);
}
}
&.off {
background-image: url(/images/ctrl/pause.png);
&:hover,
&:active {
background-image: url(/images/ctrl/pause_a.png);
}
}
&.next {
background-image: url(/images/ctrl/next.png);
&:hover,
&:active {
background-image: url(/images/ctrl/next_a.png);
}
}
}
}
.play-action {
display: flex;
align-items: center;
justify-content: space-between;
width: 52px;
height: 22px;
.item {
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-size: cover;
transition: background 0.1s ease-in-out;
cursor: pointer;
&.volume,
&.mute {
position: relative;
a {
display: block;
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-size: cover;
transition: background 0.1s ease-in-out;
}
.range {
visibility: hidden;
position: absolute;
right: -5px;
bottom: 20px;
width: 32px;
height: 128px;
padding: 14px;
border-radius: 3px;
background: rgba(128, 128, 128, 0.8);
input {
display: block;
width: 4px;
height: 100px;
border-radius: 3px;
appearance: slider-vertical;
}
}
&:hover {
.range {
visibility: visible;
}
}
}
&.volume a {
background-image: url(/images/ctrl/volume.png);
&:hover,
&:active {
background-image: url(/images/ctrl/volume_a.png);
}
}
&.mute a {
background-image: url(/images/ctrl/mute.png);
&:hover,
&:active {
background-image: url(/images/ctrl/mute_a.png);
}
}
&.single {
background-image: url(/images/ctrl/single.png);
&:hover,
&:active {
background-image: url(/images/ctrl/single_a.png);
}
}
&.all {
background-image: url(/images/ctrl/all.png);
&:hover,
&:active {
background-image: url(/images/ctrl/all_a.png);
}
}
&.rand {
background-image: url(/images/ctrl/rand.png);
&:hover,
&:active {
background-image: url(/images/ctrl/rand_a.png);
}
}
}
}
}
}
}
.do-mod-contextmenu {width:145px;height:auto;padding:8px 0;line-height:35px;font-size:1.3rem;
li {overflow:hidden;width:100%;height:35px;padding:0 10px;@include ts(background);cursor:default;
&:hover {background:nth($cp, 1)}
i {padding:0 3px;font-size:1.6rem;vertical-align:bottom;}
@keyframes round {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
.do-layer .layer-box.do-mod-contextmenu__fixed {padding:0}
@keyframes load {
from {opacity: .5; transform: scale(1)}
to {opacity: 0; transform: scale(1.5)}
}
@keyframes load2 {
from {transform:rotate(360deg)}
to {transform:rotate(0deg)}
}
@keyframes play {
from {transform:rotate(0deg)}
to {transform:rotate(360deg)}
}

File diff suppressed because one or more lines are too long

View File

@ -7,48 +7,15 @@
@import "./var.scss";
@font-face {font-family: "sonist font";
src: url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff')
.app-drag {-webkit-app-region:drag;user-select: none;}
.app-nodrag {-webkit-app-region:no-drag;}
body {
position:fixed;left:0;top:0;
width:100%;height:100%;
line-height:1.5;
font-size:14px;
color:#fff;
}
[class^="s-icon-"], [class*=" s-icon-"] {display:inline-block;font-family:"sonist font" !important;font-style:normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
.s-icon-all:before { content: "\e714"; }
.s-icon-eq:before { content: "\e715"; }
.s-icon-heart:before { content: "\e716"; }
.s-icon-music:before { content: "\e717"; }
.s-icon-play-list:before { content: "\e719"; }
.s-icon-mv:before { content: "\e71d"; }
.s-icon-rank:before { content: "\e71e"; }
.s-icon-singer:before { content: "\e71f"; }
.s-icon-random:before { content: "\e720"; }
.s-icon-radio:before { content: "\e721"; }
.s-icon-single:before { content: "\e722"; }
.s-icon-next:before { content: "\e723"; }
.s-icon-prev:before { content: "\e724"; }
.s-icon-pause:before { content: "\e725"; }
.s-icon-play:before { content: "\e726"; }
.do-fn-drag {-webkit-app-region:drag;user-select: none;}
.do-fn-nodrag {-webkit-app-region:no-drag;}
html {font-size:62.5%}
body {position:fixed;left:0;top:0;width:100%;height:100%;line-height:1.5;background:#fff;font-size:1.4rem;color:nth($cd, 1);background-size:cover;background-repeat:no-repeat;}
table {overflow:auto;display:table;width:100%;line-height:2.5rem;
thead tr {height:4.5rem;border-bottom:.1rem solid rgba(200, 200, 200, .15)}
thead th {padding:1rem .8rem;border:0;}
tbody tr {height:auto;@include ts(background, .3s);
&:hover {background:rgba(29, 35, 44, .08);}
}
tbody td {padding:.9rem .8rem}
.ac {text-align:center}
.idx {color:nth($cp, 3);text-shadow:0 .1rem 0 rgba(255, 255, 255, 0.6)}
.active {color:nth($ct, 1);background:rgba(255, 255, 255, 0.6);font-weight:bold}
}
::-webkit-scrollbar {width:.6rem;height:.6rem;background:none;}
::-webkit-scrollbar:hover {background:rgba(255, 255, 255, .1);}
::-webkit-scrollbar-button {display:none;}
::-webkit-scrollbar-thumb {background:nth($cp, 3);}
::-webkit-scrollbar-thumb:hover {background:nth($ct, 1);}

File diff suppressed because one or more lines are too long

View File

@ -74,7 +74,10 @@
//
.do-mod-local {flex:1;display:flex;flex-flow:column wrap;
.tabbar {flex:0 1 3rem;padding:0 1rem;line-height:2.9rem;border-bottom:.1rem solid rgba(200, 200, 200, .3);
.tabbar {
flex:0 1 3rem;
padding:0 1rem;line-height:2.9rem;
border-bottom:.1rem solid rgba(200, 200, 200, .3);
.refresh {margin-left:1rem;color:nth($ct, 1);text-decoration:underline;
@ -83,10 +86,13 @@
}
}
.table {overflow:auto;flex:1;}
.table {flex:1;}
.edit-form {position:absolute;left:0;top:0;z-index:90;display:flex;justify-content:center;align-items:center;width:100%;height:100%;
.edit-form {
position:absolute;left:0;top:0;z-index:90;
display:flex;justify-content:center;align-items:center;
width:100%;height:100%;
.form {position:relative;display:flex;flex-flow:column wrap;flex:0 40rem;width:5rem;height:auto;padding:.5rem 4rem 2rem;background:#fff;box-shadow:0 .5rem 2rem rgba(0, 0, 0, .2);

82
src/css/reset-basic.css Normal file
View File

@ -0,0 +1,82 @@
@charset "UTF-8";
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2014-10-10 00:45:09
*
* CSS
*
* ,
*
*
* 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;white-space:pre-wrap;}
[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;}
.text-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
:root {
/* primary */
--color-teal-a: rgba(72, 201, 176, 0.35);
--color-teal-1: rgb(72, 201, 176);
--color-teal-2: rgb(26, 188, 156);
--color-teal-3: rgb(22, 160, 133);
/* success */
--color-green-a: rgba(70, 221, 126, 0.35);
--color-green-1: rgb(70, 221, 126);
--color-green-2: rgb(47, 208, 105);
--color-green-3: rgb(26, 196, 88);
/* info */
--color-blue-a: rgba(100, 181, 246, 0.35);
--color-blue-1: rgb(100, 181, 246);
--color-blue-2: rgb(66, 165, 245);
--color-blue-3: rgb(33, 150, 243);
/* danger */
--color-red-a: rgba(252, 118, 97, 0.35);
--color-red-1: #fc7661;
--color-red-2: #ff5f45;
--color-red-3: #f33e22;
/* warning */
--color-orange-a: rgba(254, 174, 117, 0.35);
--color-orange-1: #feae75;
--color-orange-2: #fd964b;
--color-orange-3: #f97316;
/* default1 */
--color-plain-a: rgba(150, 204, 248, 0.35);
--color-plain-1: rgb(242, 245, 252);
--color-plain-2: rgb(232, 235, 244);
--color-plain-3: rgb(218, 225, 233);
/* default2 */
--color-grey-a: rgba(206, 214, 224, 0.35);
--color-grey-1: rgb(206, 214, 224);
--color-grey-2: rgb(164, 176, 190);
--color-grey-3: rgb(134, 144, 155);
/* inverse */
--color-dark-a: rgba(100, 116, 139, 0.35);
--color-dark-1: #64748B;
--color-dark-2: #475569;
--color-dark-3: #2c3441;
}

View File

@ -1,14 +1,17 @@
$ct: #3fc2a7 #19b491 #16967a;
$cg: #58d68d #2ecc71 #27ae60;
$cpp: #ac61ce #9b59b6 #8e44ad;
$cb: #52a3de #2d8dd6 #2776b1;
$ct: #4db6ac #26a69a #009688;
$cg: #81c784 #66bb6a #4caf50;
$cpp: #9575cd #9575cd #673ab7;
$cb: #64b5f6 #42a5f5 #2196f3;
$cr: #ff5061 #eb3b48 #ce3742;
$co: #ffb618 #f39c12 #e67e22;
$cp: #f3f5fb #e8ebf4 #dae1e9;
$cgr: #98acae #8a9b9c #748182;
$cp: #f2f5fc #e8ebf4 #dae1e9;
$cgr: #bdbdbd #9e9e9e #757575;
$cd: #62778d #526273 #425064;
@mixin ts($c: all, $t: .2s, $m: ease-in-out){
transition:$c $t $m;
}
@function px($n: 1) {
@return ($n / 10) + rem;
}

View File

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link href="lib/css/reset-basic.css" rel="stylesheet">
<style>
.do-fn-drag {-webkit-app-region:drag;user-select: none;}
.do-fn-nodrag {-webkit-app-region:no-drag;}
html {font-size:62.5%}
body {position:fixed;left:0;top:0;width:100%;height:100%;padding:4rem 3rem;line-height:2;font-size:1.4rem;color:#62778d;}
span {color:#ff5061;font-weight:bold}
cite {color:#dae1e9;font-style:italic}
</style>
</head>
<body class="do-fn-drag">
<h1>欢迎使用Sonist!</h1>
<pre>
检测到你系统中没有安装<span>ffmpeg</span>
请先安装<span>ffmpeg</span>
如果你的系统是MacOS, 可以使用brew来安装:
brew install ffmpeg
如果你的系统是Linux, 可以使用包管理器"apt/yum/pacman等来安装":
sudo apt install ffmpeg <cite># 以debian系为例</cite>
</pre>
</body>
</html>

View File

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link href="lib/css/reset-basic.css" rel="stylesheet">
<link href="/css/common.css" rel="stylesheet">
<link href="/css/desktop-lrc.css" rel="stylesheet">
<script>window.LIBS_BASE_URL = location.origin + '/lib';</script>
<script type="module" src="js/desktop-lrc.js"></script>
</head>
<body class="do-fn-noselect do-fn-drag" anot="lrc">
<div class="lrc-box">
<section class="left">
<span class="shadow" :text="lrc.l.txt"></span>
</section>
<section class="right">
<span class="shadow" :text="lrc.r.txt"></span>
</section>
</div>
<div class="lrc-box">
<section class="left">
<span :text="lrc.l.txt" :css="{background: lrc.l.bg}"></span>
</section>
<section class="right">
<span :text="lrc.r.txt" :css="{background: lrc.r.bg}"></span>
</section>
</div>
<div class="touch-bar do-fn-nodrag">
<a class="do-icon-lock lock" :visible="isMac" :class="{active: isLock}" :click="lock"></a>
<a class="do-icon-close quit" :click="quit"></a>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #f55449;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_close_hover</title>
<g id="red">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #f55449;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_close_hover</title>
<g id="red">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<rect class="cls-3" x="192" y="476" width="640" height="72" rx="36" ry="36" transform="translate(512 1236.08) rotate(-135)"/>
<rect class="cls-3" x="191" y="475" width="640" height="72" rx="36" ry="36" transform="translate(1233.66 511) rotate(135)"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 899 B

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #dee1e3;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_grey</title>
<g id="grey">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #39ea49;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_max_hover</title>
<g id="green">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #39ea49;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_max_hover</title>
<g id="green">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<path class="cls-3" d="M395.93,267.8a31.32,31.32,0,0,1,22.86-9.48l319.33-2.55A31.25,31.25,0,0,1,770,287.64l-2.5,319.29A32,32,0,0,1,758,629.79a31.06,31.06,0,0,1-22.86,9.48,30.67,30.67,0,0,1-22.72-9.15L395.65,313.33c-6.22-6.13-9.24-13.67-9.15-22.72a31.28,31.28,0,0,1,9.43-22.82ZM629.84,758A31.32,31.32,0,0,1,607,767.45L287.64,770a31.25,31.25,0,0,1-31.87-31.87l2.5-319.29A32,32,0,0,1,267.75,396a31.06,31.06,0,0,1,22.86-9.48,30.67,30.67,0,0,1,22.72,9.15L630.12,712.44c6.22,6.13,9.24,13.67,9.15,22.72A31.28,31.28,0,0,1,629.84,758Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #fac536;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_min_hover</title>
<g id="yellow">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 595 B

View File

@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #fac536;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_min_hover</title>
<g id="yellow">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<rect class="cls-3" x="192" y="476" width="640" height="72" rx="36" ry="36"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 723 B

BIN
src/images/ctrl/all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/images/ctrl/all_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/images/ctrl/mute.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/images/ctrl/mute_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/images/ctrl/next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/images/ctrl/next_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/images/ctrl/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/images/ctrl/pause_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
src/images/ctrl/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/images/ctrl/play_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/images/ctrl/prev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
src/images/ctrl/prev_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/images/ctrl/rand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/images/ctrl/rand_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/images/ctrl/single.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/images/ctrl/volume.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/images/trays/tray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,176 +2,84 @@
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<link href="lib/css/reset-basic.css" rel="stylesheet">
<link href="lib/css/elem-ui.css" rel="stylesheet">
<link href="css/reset-basic.css" rel="stylesheet">
<link href="css/common.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet">
<link href="css/modules.css" rel="stylesheet">
<script>window.LIBS_BASE_URL = location.origin + '/lib';</script>
<script type="module" src="js/app.js"></script>
</head>
<body class="do-fn-noselect" anot="app" :css="{'background-image': coverBG}">
<body class="noselect">
<div id="app" :class="{blur: isPlaying && !ktvMode, ktv: ktvMode}">
<div class="app" anot="app">
<div class="title-bar do-fn-drag">
<div class="album-cover">
<img :src="preview.cover || defaultCover">
</div>
<nav class="btn-box do-fn-nodrag" :if="theme === 1" :class="{focus: winFocus}">
<i class="item quit" :click="quit(false)"></i>
<i class="item min" :click="minimize"></i>
<i class="item max"></i>
</nav>
<!-- 背景点位标签 -->
<div class="holder"></div>
<div class="tools do-fn-drag">
<div class="search do-fn-nodrag">
<input
class="do-ui-input"
placeholder="搜索 音乐/歌手/专辑"
:keyup="searchMusic"
:duplex="searchTxt">
<i class="icon do-icon-search"></i>
<div class="title-bar app-drag">
<div class="btns active">
<a class="btn close"></a>
<a class="btn mini"></a>
</div>
</div>
<main class="main-body">
<nav class="btn-box-win do-fn-nodrag">
<div class="opt">
<i class="do-icon-menu-right" :click="toggleOptBox"></i>
<section class="opt-list" :visible="optBoxShow">
<span :click="change2mini"><i class="do-icon-maximized"></i> 迷你模式</span>
<span :click="toggleModule('profile')">
<i class="do-icon-setting"></i> 首选项
</span>
<span class="pipe"></span>
<span :click="quit(true)"><i class="do-icon-logout"></i> 退出</span>
</main>
<div class="play-bar">
<section class="stat-bar">
<span class="time" :text="song.time | time"></span>
<div class="progress">
<span class="thumb" :css="{width: ((100 * song.time) / song.duration).toFixed(1) + '%'}"></span>
<input type="range" max="712" min="0" step="1" :duplex="progress">
</div>
<span class="time" :text="song.duration | time"></span>
</section>
</div>
<span :if="theme === 2">
<i class="item do-icon-minimize" :click="minimize"></i>
<i class="item do-icon-maximize disabled"></i>
<i class="item do-icon-close quit" :click="quit(false)"></i>
</span>
</nav>
<section class="ctrl-box">
<wc-icon is="menu-dot"></wc-icon>
<div class="play-btn">
<a class="item prev" @click="play(-1)"></a>
<a class="item"
:class="{on: isplaying, off: !isplaying}"
@click="play(0)">
</a>
<a class="item next" @click="play(1)"></a>
</div>
<div class="main-body">
<aside class="sidebar do-fn-drag">
<div class="user-box">
<div class="avatar">
<img src="images/avatar.jpg" alt="yutent">
</div>
<h2 class="uname">yutent</h2>
</div>
<dl class="music-box do-fn-nodrag">
<dt class="title">酷狗在线</dt>
<dd class="item"
:click="toggleModule('rank')"
:class="{active: mod === 'rank'}">
<i class="s-icon-rank"></i> 排行榜
</dd>
<dd class="item"
:click="toggleModule('artist')"
:class="{active: mod === 'artist'}">
<i class="s-icon-singer"></i> 歌手
</dd>
<dd class="item disabled"
:click="toggleModule('mv')"
:class="{active: mod === 'mv'}">
<i class="s-icon-mv"></i> MV
</dd>
<dt class="title">我的音乐</dt>
<dd class="item"
:click="toggleModule('search')"
:class="{active: mod === 'search'}">
<i class="s-icon-heart"></i> 试听列表
</dd>
<dd class="item"
:click="toggleModule('local')"
:class="{active: mod === 'local'}">
<i class="s-icon-play-list"></i> 本地音乐
</dd>
</dl>
</aside>
<content class="module" :include="views" data-cache="true"></content>
</div>
<div
class="ktv-box"
:if="ktvMode"
:include="'/views/ktv.htm'"
:css="{'background-image': coverBG}">
</div>
<div class="contrl-bar">
<div class="play-box" :click="handleCtrl">
<span class="item prev s-icon-prev" data-key="prev"></span>
<span
class="item play"
data-key="play"
:class="{'s-icon-play': !isPlaying, 's-icon-pause': isPlaying}">
</span>
<span class="item next s-icon-next" data-key="next"></span>
</div>
<div class="stat-box">
<div class="song-stat">
<canvas ref="player"></canvas>
</div>
<span
class="ctrl"
:class="{
's-icon-all': playMode === 0,
's-icon-single': playMode === 1,
's-icon-random': playMode === 2
<div class="play-action">
<item class="item" :class="{
all: playmode === 1,
single: playmode === 2,
rand: playmode === 3,
}"
:click="togglePlayMode">
</span>
<section class="ctrl volume">
<i
:class="{
'do-icon-unmute' : volume > 0 && !muted,
'do-icon-mute' : volume === 0 || muted
}">
</i>
<span
class="volume-ctrl"
:click="changeValume">
<em :css="{flex: '0 ' + volume + '%'}"></em>
</span>
@click="switchMode">
</item>
<item class="item" :class="{mute: mute || volume <= 0, volume: !mute && volume > 0}">
<div class="range">
<input type="range" max="100" min="0" step="1" :duplex="volume">
</div>
<a @click="toggleMute"></a>
</item>
</div>
</section>
<span class="ctrl s-icon-eq"></span>
<span class="ctrl lrc" :click="toggleDesktopLrc"></span>
</div>
<div class="scroll-box">
<wc-scroll class="list" ref="list">
<section
class="item"
@click="previewSong(it, i)"
@dblclick="playSong(i)"
:class="{on: curr === i}"
:for="i it in list">
<span class="idx" :text="i + 1"></span>
<span class="name text-ell" :text="it.name"></span>
<span class="artist text-ell" :text="it.artist || '未知歌手'"></span>
<span class="duration" :text="it.duration | time"></span>
</section>
</wc-scroll>
</div>
<div class="loading" :if="loading">
<div class="box">
<i></i><i></i><i></i><i></i><i></i>
<span></span>
<span></span>
<cite>{{progress}}%</cite>
</div>
</div>
</div>
</body>

View File

@ -1,139 +1,63 @@
/**
* 音乐APP接口
* @author yutent<yutent@doui.cc>
* @date 2018/12/24 16:02:00
* 部分远程接口
* @author yutent<yutent.io@gmail.com>
* @date 2020/11/20 19:07:32
*/
'use strict'
import fetch from './lib/fetch/index.js'
import request from '/lib/request/index.js'
const log = console.log
const BASE_API_URI = 'http://mobilecdnbj.kugou.com'
request.init({ dataType: 'json' })
const get = uri => {
return request.get(BASE_API_URI + uri)
}
const post = uri => {
return request.post(BASE_API_URI + uri)
function tojson(r) {
return r.json()
}
export default {
getLastHot100Artists() {
return get('/api/v5/singer/list', {
data: {
sort: 1,
showtype: 1,
sextype: 0,
musician: 0,
pagesize: 100,
plat: 2,
type: 0,
page: 1
}
}).then(res => {
if (res.status === 200) {
return res.body
}
})
},
getArtistList(sextype = 1, type = 1) {
return get('/api/v5/singer/list', {
data: {
showtype: 2,
musician: 0,
type,
sextype
}
}).then(res => {
if (res.status === 200) {
return res.body
}
})
},
getArtistInfo(singerid) {
return get('/api/v3/singer/info', { data: { singerid } }).then(res => {
if (res.status === 200) {
return res.body
}
})
},
getArtistInfo(singerid) {
return get('/api/v3/singer/info', { data: { singerid } }).then(res => {
if (res.status === 200) {
return res.body
}
})
},
getArtistSongs(singerid, page = 1) {
return get('/api/v3/singer/song', {
data: {
sorttype: 2,
pagesize: 50,
singerid,
area_code: 1,
page
}
}).then(res => {
if (res.status === 200) {
return res.body
}
})
},
getArtistAlbums(singerid, page = 1) {
return get('/api/v3/singer/album', {
data: {
pagesize: 50,
singerid,
area_code: 1,
page
}
}).then(res => {
if (res.status === 200) {
return res.body
}
})
},
search(keyword, page = 1, pagesize = 20) {
return request
.get('https://songsearch.kugou.com/song_search_v2', {
data: {
searchLrc(keyword, duration) {
return fetch('http://lyrics.kugou.com/search', {
body: {
client: 'pc',
duration: duration * 1000,
keyword,
platform: 'WebFilter',
tag: '',
page,
pagesize
man: 'no',
ver: 1
}
})
.then(res => {
if (res.status === 200) {
return res.body.data.lists
.then(tojson)
.then(r => {
if (r.candidates && r.candidates.length) {
return r.candidates.map(it => ({
id: it.id,
accesskey: it.accesskey,
duration: it.duration,
singer: it.singer,
song: it.song
}))
}
return []
})
},
getSongInfoByHash(hash, album_id = '') {
return request
.get('https://wwwapi.kugou.com/yy', {
data: {
r: 'play/getdata',
hash,
album_id
downloadLrc(id, accesskey) {
return fetch('http://lyrics.kugou.com/download', {
body: {
client: 'pc',
id,
accesskey,
ver: 1,
fmt: 'lrc',
charset: 'utf8'
}
})
.then(res => {
if (res.status === 200) {
return res.body.data
.then(tojson)
.then(r => {
if (r.status === 200 && r.content) {
var lrc
try {
lrc = Buffer.from(r.content, 'base64').toString()
lrc = lrc.split(/[\r\n]+/).join('\n')
} catch (e) {
console.error(e, r)
}
return lrc
}
})
}

View File

@ -1,533 +1,257 @@
/**
* {sonist app}
* @author yutent<yutent@doui.cc>
* @date 2018/12/16 17:15:57
*
* @author yutent<yutent.io@gmail.com>
* @date 2020/11/16 16:21:52
*/
import '/lib/anot.js'
import layer from '/lib/layer/index.js'
import store from '/lib/store/index.js'
import AudioPlayer from '/lib/audio/index.js'
import Lyrics from '/lib/lyrics/index.js'
import Anot from '/js/lib/anot.js'
import '/js/lib/scroll/index.js'
import '/js/lib/icon/index.js'
import Keyboard from '/js/lib/keyboard/index.js'
// import app from '/js/lib/socket.js'
import fetch from '/js/lib/fetch/index.js'
import Api from '/js/api.js'
import Artist from '/js/modules/artist.js'
import Local from '/js/modules/local.js'
import Profile from '/js/modules/profile.js'
import Search from '/js/modules/search.js'
import Player from '/js/lib/audio/index.js'
import KTV from '/js/modules/ktv.js'
import PLAYCTRL from '/js/modules/play-ctrl.js'
// const id3 = require('music-metadata')
const log = console.log
const MODE_DICT = { 1: 'all', 2: 'single', 3: 'random' }
const fs = require('iofs')
const path = require('path')
var kb = new Keyboard()
const { remote, ipcRenderer, screen } = require('electron')
const { createDesktopLrcWindow, createMiniWindow } = remote.require(
'./tools/windows'
)
const MAIN_SCREEN = screen.getPrimaryDisplay()
var player = new Player()
const WIN = remote.getCurrentWindow()
const __LRC__ = createDesktopLrcWindow(MAIN_SCREEN)
const __MINI__ = createMiniWindow(MAIN_SCREEN)
const PLAY_MODE = {
0: 'all',
1: 'single',
2: 'random'
}
// 本地音乐和试用音乐列表
window.LS = store.collection('local')
window.TS = store.collection('temp')
// 音乐播放器
window.SONIST = new AudioPlayer()
window.LYRICS = new Lyrics()
let appInit = ipcRenderer.sendSync('get-init')
Anot.ss('app-init', appInit)
SONIST.target = 'local' //播放目标是本地音乐
window.fetch = fetch
window.player = player
Anot({
$id: 'app',
state: {
theme: appInit.theme || 1, // 1:macos, 2: deepin
winFocus: false,
winShow: true,
mod: 'local',
searchTxt: '',
playMode: Anot.ls('play-mode') >>> 0, // 0:all | 1:single | 2:random
ktvMode: 0,
isPlaying: false,
optBoxShow: false,
volumeCtrlShow: false,
volume: Anot.ls('volume') || 70,
muted: false,
curr: {
id: '',
cover: '',
title: '',
artist: '',
defaultCover: '/images/disk.png',
isplaying: false,
playmode: +Anot.ls('app_mode') || 1,
mute: false,
volume: +Anot.ls('app_volume') || 50,
preview: {
name: '',
album: '',
artist: '',
cover: ''
},
song: {
name: '',
artist: '',
src: '',
time: 0,
duration: 0
},
ctrlLrc: '暂无歌词...',
...KTV.data,
loading: false,
progress: 0
progress: 0,
curr: -1,
list: []
},
skip: ['winShow'],
computed: {
views() {
if (!this.mod) {
return
async mounted() {
// var list = app.dispatch('get-all-songs')
// var list = app.dispatch('scan-dir', { dir: '/Volumes/ooc/music' })
// app.on('tray-play', ev => {
// this.play(0)
// })
// app.on('tray-prev', ev => {
// this.play(-1)
// })
// app.on('tray-next', ev => {
// this.play(1)
// })
kb.on(['left'], ev => {
var time = this.song.time - 5
if (time < 0) {
time = 0
}
return '/views/' + this.mod + '.htm'
},
coverBG() {
if (this.curr.cover) {
return `url(${this.curr.cover})`
} else {
return 'none'
this.song.time = time
})
kb.on(['right'], ev => {
var time = this.song.time + 5
if (time > this.song.duration) {
time = this.song.duration
}
this.song.time = time
})
kb.on(['down'], ev => {
var vol = +this.volume - 5
if (vol < 0) {
vol = 0
}
this.volume = vol
})
kb.on(['up'], ev => {
var vol = +this.volume + 5
if (vol > 100) {
vol = 100
}
this.volume = vol
})
// for (let it of list) {
// let { album, artist, title, duration } = await this.getID3(it.file_path)
// it.name = title || it.name
// it.artist = artist
// it.album = album
// it.duration = duration
// app.dispatch('add-song', {
// name: it.name,
// artist,
// album,
// duration,
// file_path: it.file_path
// })
// }
// console.log(list)
// this.list = list
player.volume = this.volume / 100
// player.load(list.map(it => `sonist://${it.file_path}`))
player.on('play', time => {
this.song.time = time
})
player.on('stop', _ => {
var idx = this.curr
var repeat = false
switch (this.playmode) {
case 1: // all
idx++
if (idx >= this.list.length) {
idx = 0
}
break
case 2: // single
repeat = true
break
case 3: // random
idx = ~~(Math.random() * this.list.length)
break
}
this.playSong(idx, repeat)
})
},
watch: {
mod(val) {
this.activeModule(val)
}
volume(v) {
Anot.ls('app_volume', v)
player.volume = v / 100
},
mounted() {
let canvas = this.$refs.player
// 画布放大4倍, 以解决模糊的问题
this.__WIDTH__ = canvas.clientWidth * 4
this.__HEIGHT__ = canvas.clientHeight * 4
canvas.width = this.__WIDTH__
canvas.height = this.__HEIGHT__
this.__CTX__ = canvas.getContext('2d')
this.draw(true)
// 修改歌曲进度
canvas.addEventListener(
'click',
ev => {
if (!this.curr.id) {
return
}
let rect = canvas.getBoundingClientRect()
let aw = rect.width
let ax = ev.pageX - rect.left
let ay = ev.pageY - rect.top
if (ax < 80) {
this.ktvMode = this.ktvMode ^ 1
if (!this.isPlaying) {
this.draw()
}
return
}
if (ax > 124 && ay > 55 && ay < 64) {
let pp = (ax - 124) / (aw - 124)
this.curr.time = pp * this.curr.duration
SONIST.seek(this.curr.time)
LYRICS.seek(this.curr.time)
if (!this.isPlaying) {
this.draw()
}
}
playmode(v) {
Anot.ls('app_mode', v)
},
false
)
// 设置循环模式
SONIST.mode = PLAY_MODE[this.playMode]
SONIST.volume = this.volume
SONIST.on('play', time => {
this.curr.time = time
LYRICS.update(time)
})
SONIST.on('end', time => {
this.nextSong(1)
})
// 控制条的单行歌词
LYRICS.on('ctrl-lrc', lrc => {
this.ctrlLrc = lrc
})
// ktv模式的歌词
LYRICS.on('ktv-lrc', lrc => {
this.lrc = lrc
__LRC__.emit('ktv-lrc', lrc)
})
// ktv模式的歌词
LYRICS.on('view-all', lrc => {
this.allLrc = lrc
})
this.activeModule(this.mod)
remote.app.on('browser-window-focus', _ => {
this.winFocus = true
})
remote.app.on('browser-window-blur', _ => {
this.winFocus = false
})
/**
* 响应mini窗口的事件
*/
WIN.on('mini-ctrl', ev => {
switch (ev) {
case 'prev':
this.nextSong(-1)
break
case 'play':
this.play()
break
case 'next':
this.nextSong(1)
break
case 'desktoplrc':
this.toggleDesktopLrc()
break
default:
if (ev.name === 'play-mode') {
this.playMode = ev.value
SONIST.mode = PLAY_MODE[ev.value]
Anot.ls('play-mode', ev.value)
progress(v) {
var t = +(this.song.duration * (v / 712)).toFixed(2)
player.seek(t)
}
}
})
/**
* 响应 全局快捷键的事件
*/
WIN.on('gs-ctrl', ev => {
switch (ev) {
case 'prev':
this.nextSong(-1)
break
case 'play':
this.play()
break
case 'next':
this.nextSong(1)
break
case 'stop':
this.isPlaying = false
this.curr.time = 0
SONIST.seek(0)
LYRICS.seek(0)
SONIST.pause()
this.draw()
break
case 'vu':
this.volume += 5
if (this.volume >= 100) {
this.volume = 100
}
SONIST.volume = this.volume
break
case 'vd':
this.volume -= 5
if (this.volume <= 0) {
this.volume = 0
}
SONIST.volume = this.volume
break
case 'lrc':
this.toggleDesktopLrc()
break
case 'mini':
this.change2mini()
break
default:
break
}
})
// 迷你模式开启时, 不响应托盘和dock栏的点击事件
ipcRenderer.on('dock-click', () => {
if (!__MINI__.isVisible()) {
WIN.show()
}
})
Anot(document).bind('keydown', ev => {
if (ev.target === document.body) {
switch (ev.keyCode) {
case 32: // 空格
this.play()
break
case 37: // 向左 (prev)
if (ev.metaKey) {
this.nextSong(-1)
}
break
case 39: // 向右 (next)
if (ev.metaKey) {
this.nextSong(1)
}
break
case 38: // 向上 (音量+
if (ev.metaKey) {
this.volume += 5
if (this.volume >= 100) {
this.volume = 100
}
SONIST.volume = this.volume
}
break
case 40: // 向下 (音量-
if (ev.metaKey) {
this.volume -= 5
if (this.volume <= 0) {
this.volume = 0
}
SONIST.volume = this.volume
}
break
case 77: // M (迷你模式)
if (ev.metaKey && ev.altKey) {
this.change2mini()
}
break
case 82: // R (桌面歌词)
if (ev.metaKey) {
this.toggleDesktopLrc()
}
break
}
}
})
WIN.on('show', ev => {
this.winShow = true
this.draw()
})
WIN.on('hide', ev => {
this.winShow = false
this.draw()
})
},
methods: {
quit(force) {
if (force) {
remote.app.exit()
} else {
if (appInit.allowPlayOnBack) {
WIN.hide()
} else {
remote.app.exit()
}
}
},
minimize() {
WIN.minimize()
},
change2mini() {
this.optBoxShow = false
WIN.hide()
__MINI__.show()
let song = this.curr.$model
if (!this.isPlaying) {
delete song.id
}
__MINI__.emit('mini-init', song)
},
play(act) {
var idx = this.curr
var repeat = false
activeModule(mod) {
switch (mod) {
case 'artist':
Artist.__init__()
break
case 'local':
Local.__init__()
break
case 'profile':
Profile.__init__()
break
case 'search':
Search.__init__()
switch (act) {
case 0:
if (idx > -1) {
player.play(-1)
this.isplaying = !this.isplaying
} else {
this.playSong(0)
}
break
default:
switch (this.playmode) {
case 1: // all
idx += act
if (idx < 0) {
idx = this.list.length - 1
}
if (idx >= this.list.length) {
idx = 0
}
break
case 2: // single
if (idx === -1) {
idx = ~~(Math.random() * this.list.length)
}
repeat = true
break
case 3: // random
idx = ~~(Math.random() * this.list.length)
break
}
this.playSong(idx, repeat)
break
}
},
toggleOptBox() {
this.optBoxShow = !this.optBoxShow
switchMode() {
var n = this.playmode + 1
if (n > 3) {
n = 1
}
this.playmode = n
},
toggleDesktopLrc() {
if (__LRC__.isVisible()) {
__LRC__.hide()
} else {
__LRC__.showInactive()
}
toggleMute() {
this.mute = !this.mute
},
toggleModule(mod) {
if ('mv' === mod) {
return
}
this.optBoxShow = false
this.__last__ = this.mod
this.mod = mod
},
// 设置保存 回调
onProfileSaved() {
this.toggleModule(this.__last__)
appInit = JSON.parse(Anot.ss('app-init'))
},
// 切换循环模式
togglePlayMode() {
let mod = this.playMode
mod++
if (mod > 2) {
mod = 0
}
this.playMode = mod
SONIST.mode = PLAY_MODE[mod]
Anot.ls('play-mode', mod)
},
// 修改音量
changeValume(ev) {
let volume = 575 - ev.pageY
if (volume < 0) {
volume = 0
}
if (volume > 100) {
volume = 100
}
this.volume = volume
SONIST.volume = volume
Anot.ls('volume', volume)
},
searchMusic(ev) {
if (ev.keyCode === 13) {
let txt = this.searchTxt.trim()
if (!txt) {
return
}
if (txt === ':debug:') {
log('----- 调试模式 -----')
this.searchTxt = ''
WIN.openDevTools()
return
}
if (this.mod !== 'search') {
this.toggleModule('search')
}
Search.search(txt)
this.searchTxt = ''
// layer.toast('搜索功能还未开放')
}
},
/**
* 播放按钮的事件
*/
handleCtrl(ev) {
let key = ev.target.dataset.key
switch (key) {
case 'prev':
this.nextSong(-1)
break
case 'play':
this.play()
break
case 'next':
this.nextSong(1)
break
default:
break
}
},
nextSong(step) {
let _p = null
if (step > 0) {
_p = SONIST.next()
} else {
_p = SONIST.prev()
}
this.isPlaying = false
_p.then(it => {
if (this.mod === 'local') {
Local.__updateSong__(it)
}
// 通知子模块歌曲已经改变
this.$fire('child!curr', it.id)
this.play(it)
getID3(file) {
return id3.parseFile(file).then(res => {
let {
common: { album, artist, title },
format: { duration }
} = res
return { album, artist, title, duration: ~~duration }
})
},
updateCurr(obj) {
let old = this.curr.$model
this.curr = Object.assign(old, obj)
__MINI__.emit('mini-init', this.curr.$model)
previewSong(it) {
var { album, artist, name, cover } = it
Object.assign(this.preview, { album, artist, name, cover })
},
playSong(i, repeat) {
//
var it = this.list[i]
play(song) {
// 有参数的,说明是播放回调通知
// 此时仅更新播放控制条的信息即可
if (song) {
song.time = 0
this.ctrlLrc = '暂无歌词...'
this.updateCurr(song)
this.isPlaying = true
this.draw(true)
LYRICS.__init__(song.id)
} else {
if (SONIST.stat === 'ready') {
let played = this.isPlaying
this.isPlaying = !this.isPlaying
if (this.curr.id) {
if (played) {
SONIST.pause()
} else {
SONIST.play()
if (this.curr === i) {
if (repeat) {
this.song.time = 0
player.play(-1, repeat)
}
this.draw()
} else {
let lastPlay = Anot.ls('last-play') || 0
SONIST.play(lastPlay).then(it => {
it.time = 0
this.ctrlLrc = '暂无歌词...'
this.updateCurr(it)
this.draw(true)
// this.ktvMode = 1
return
}
// player.stop()
this.curr = i
this.song.name = it.name
this.song.artist = it.artist
this.song.duration = it.duration
this.song.src = `file://${it.file_path}`
this.song.time = 0
this.isplaying = true
this.previewSong(it)
player.play(i)
this.$refs.list.scrollTop = (i - 3) * 26
LYRICS.__init__(it.id)
})
app.dispatch('update-lrc', { lrc: '正在播放: ' + it.name })
// if (!it.lrc) {
// Api.searchLrc(it.name, it.duration).then(list => {
// var { id, accesskey } = list[0]
// Api.downloadLrc(id, accesskey).then(lrc => {
// console.log(lrc)
// })
// })
// }
}
if (!this.winShow) {
__MINI__.emit('mini-ctrl', this.isPlaying ? 'play' : 'pause')
}
}
}
},
...PLAYCTRL.methods,
...KTV.methods
}
})

8
src/js/lib/anot.js Normal file

File diff suppressed because one or more lines are too long

177
src/js/lib/audio/index.js Normal file
View File

@ -0,0 +1,177 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2020/11/19 17:32:19
*/
import $ from '../utils.js'
import fetch from '../fetch/index.js'
import Events from '../events.js'
const AC = new AudioContext()
function hide(target, key, value) {
Object.defineProperty(target, key, {
value,
writable: true,
enumerable: false,
configurable: true
})
}
class AudioTrack extends Events {
constructor(elem) {
super()
this._el = elem
this.gain = AC.createGain()
this._track = AC.createMediaElementSource(elem)
this._track.connect(this.gain)
this.gain.connect(AC.destination)
this.__playFn = $.bind(elem, 'timeupdate', _ => {
this.$emit('play', elem.currentTime)
})
this.__stopFn = $.bind(elem, 'ended', _ => {
this.$emit('stop')
})
}
set volume(val) {
if (this.gain) {
this.gain.gain.value = val
}
}
destroy() {
$.unbind(this._el, 'timeupdate', this.__playFn)
$.unbind(this._el, 'ended', this.__stopFn)
this.removeAllListeners()
this._track.disconnect()
// this._track.disconnect(AC.destination)
this._el.src = ''
this._el.currentTime = 0
this._el.pause()
delete this.__playFn
delete this.__stopFn
delete this._el
delete this.gain
delete this._track
}
}
export default class Player extends Events {
constructor() {
super()
hide(this, '__LIST__', [])
hide(this, 'props', {
curr: '',
stat: 'ready',
volume: 0.5,
duration: 0
})
hide(this, 'track', null)
}
load(list) {
this.stop()
this.__LIST__ = list
}
async _getTrack(file) {
hide(this, '__AUDIO__', new Audio())
this.__AUDIO__.src = URL.createObjectURL(await fetch(file).then(r => r.blob()))
this.track = new AudioTrack(this.__AUDIO__)
this.track.volume = this.props.volume
this.track.$on('play', t => this.$emit('play', t))
this.track.$on('stop', _ => this.$emit('stop'))
}
get volume() {
return this.props.volume
}
set volume(val) {
val = +val
if (val < 0) {
val = 0
}
if (val > 1) {
val = 1
}
this.props.volume = val
if (this.track) {
this.track.volume = val
}
}
get time() {
return this.__AUDIO__.currentTime
}
get stat() {
return this.props.stat
}
/**
* id: 歌曲序号
* force: 强制重新播放
*/
async play(id, force = false) {
if (id === -1) {
if (this.track) {
if (force) {
this.seek(0)
this.props.stat = 'paused'
}
if (this.stat === 'playing') {
this.__AUDIO__.pause()
this.props.stat = 'paused'
} else if (this.stat === 'paused') {
this.__AUDIO__.play()
this.props.stat = 'playing'
}
}
} else {
var url = this.__LIST__[id]
if (!url || this.props.curr === url) {
return
}
if (this.track) {
this.stop()
}
this.props.curr = url
await this._getTrack(url)
this.__AUDIO__.play()
this.props.stat = 'playing'
}
}
seek(time) {
if (this.track) {
this.__AUDIO__.currentTime = time
}
}
stop() {
if (this.track) {
this.track.destroy()
this.props.stat = 'stoped'
}
}
}

1
src/js/lib/drag/core.js Normal file
View File

@ -0,0 +1 @@
import e from"../utils.js";const c={axis:"",limit:!1,overflow:!0};class g{constructor(t){this.$elem=t,this._init()}_init(){this.$elem.style.transform="";var{x:t,y:n}=this.$elem.getBoundingClientRect();this.pos={x:t,y:n,_x:0,_y:0}}by(t,n={}){return this.$drag=t,this.opt=Object.assign(Object.create(null),c,n),this.opt.limit!==!1&&(this.opt.overflow=!1),t.style.cursor="move",this._handleResize=e.bind(window,"resize",this._init.bind(this)),this._handleMousedown=e.bind(t,"mousedown",p=>{if(this.disabled)return;var l=this.$elem.getBoundingClientRect();l.x-this.pos._x!==this.pos.x&&(this.pos.x=l.x-this.pos._x),l.y-this.pos._y!==this.pos.y&&(this.pos.y=l.y-this.pos._y);let r=p.pageX,a=p.pageY,u=document.documentElement.clientWidth,f=document.documentElement.clientHeight,d=l.width,m=l.height,s=[0,u-d,f-m,0];if(this.opt.limit==="parent"){let i=this.$elem.parentNode.getBoundingClientRect();s=[i.top,i.right-d,i.bottom-m,i.left]}let x=e.bind(document,"mousemove",i=>{i.preventDefault();let o=i.pageX-r+(l.x-this.pos.x),h=i.pageY-a+(l.y-this.pos.y);this.opt.axis==="x"&&(h=0),this.opt.axis==="y"&&(o=0),this.opt.overflow===!1&&(o<s[3]-this.pos.x?o=s[3]-this.pos.x:o>s[1]-this.pos.x&&(o=s[1]-this.pos.x),h<s[0]-this.pos.y?h=s[0]-this.pos.y:h>s[2]-this.pos.y&&(h=s[2]-this.pos.y)),this.pos._x=o,this.pos._y=h,this.$elem.dispatchEvent(new CustomEvent("dragging",{detail:{offset:{x:this.pos.x+o,y:this.pos.y+h},move:{x:o,y:h}}})),this.$elem.style.transform=`translate(${o}px, ${h}px)`}),y=e.bind(document,"mouseup",i=>{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}}})),e.unbind(document,"mousemove",x),e.unbind(document,"mouseup",y)})}),this}on(t,n){if(!(!t||typeof n!="function"))return e.bind(this,t,n)}off(t,n){e.unbind(this,t,n)}destroy(){e.unbind(window,"resize",this._handleResize),e.unbind(this.$drag,"mousedown",this._handleMousedown),delete this.$elem,delete this.$drag}}export{g as default};

1
src/js/lib/drag/index.js Normal file
View File

@ -0,0 +1 @@
import a 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}, \u89E3\u6790\u5F02\u5E38[\u5143\u7D20\u4E0D\u5B58\u5728]`),t.tagName==="WC-LAYER"&&e==="layer"){t=t.root.children[1];break}if(t.classList.contains(e)||t.id===e)break;t=t.parentNode}new a(t).by(this.element,{limit:this.limit,axis:this.axis,overflow:this.overflow})}});

38
src/js/lib/events.js Normal file
View File

@ -0,0 +1,38 @@
export default class EventEmitter {
//
#events = Object.create(null)
$on(name, fn) {
if (this.#events[name]) {
this.#events[name].push(fn)
} else {
this.#events[name] = [fn]
}
}
$off(name, fn) {
if (this.#events[name]) {
if (fn) {
this.#events[name] = this.#events[name].filter(it => it !== fn)
} else {
this.#events[name] = []
}
}
}
$emit(name, ...args) {
if (this.#events[name]) {
for (let fn of this.#events[name]) {
try {
fn.apply(this, args)
} catch (e) {
console.error(e)
}
}
}
}
$destroy() {
this.#events = Object.create(null)
}
}

View File

@ -0,0 +1 @@
import{Format,toS}from"./lib/format.js";const noop=function(e,t){this.defer.resolve(t)},NOBODY_METHODS=["GET","HEAD"],FORM_TYPES={form:"application/x-www-form-urlencoded; charset=UTF-8",json:"application/json; charset=UTF-8",text:"text/plain; charset=UTF-8"},ERRORS={10001:"Argument url is required",10012:"Parse error",10100:"Request canceled",10104:"Request pending...",10200:"Ok",10204:"No content",10304:"Not modified",10500:"Internal Server Error",10504:"Connected timeout"};Promise.defer=function(){var e={};return e.promise=new Promise(function(t,s){e.resolve=t,e.reject=s}),e};class _Request{constructor(e="",t={},{BASE_URL:s,__INIT__:r}){if(!e)throw new Error(ERRORS[10001]);if(e=e.replace(/#.*$/,""),s&&(/^([a-z]+:|\/\/)/.test(e)||(e=s+e)),t.method=(t.method||"get").toUpperCase(),this.xhr=new XMLHttpRequest,this.defer=Promise.defer(),this.options={headers:{"X-Requested-With":"XMLHttpRequest","content-type":FORM_TYPES.form},body:null,cache:"default",credentials:!1,signal:null,timeout:3e4},!t.signal){var o=new AbortController;t.signal=o.signal}this.defer.promise.abort=function(){o.abort()};var i=this.options.headers;return r.headers&&Object.assign(i,r.headers),t.headers&&(Object.assign(i,t.headers),delete t.headers),Object.assign(this.options,r,t,{url:e,headers:i}),this.__next__(),this.defer.promise}__next__(){var e=this.options,t=null,s=!1,r=!1,o=NOBODY_METHODS.includes(e.method);if(e.signal.onabort=(e=>{this.cancel=!0,this.xhr.abort()}),e.body)switch(typeof e.body){case"number":case"string":this.__type__("text"),t=e.body;break;case"object":if("FORM"===e.body.nodeName)e.method=e.body.method.toUpperCase()||"POST",s=(t=Format.parseForm(e.body)).constructor===FormData;else if(e.body.constructor===FormData)s=!0,o&&(e.method="POST"),t=e.body;else{for(let t in e.body)if("[object File]"===toS.call(e.body[t])){s=!0;break}s?(o&&(e.method="POST"),t=Format.mkFormData(e.body)):t=e.body}}s&&delete e.headers["content-type"];try{let t=document.createElement("a");t.href=e.url,r=location.protocol!==t.protocol||location.host!==t.host}catch(e){}r&&(e.credentials?this.xhr.withCredentials=!0:delete e.headers["X-Requested-With"]),o?((t=Format.param(t))&&(e.url+=(~e.url.indexOf("?")?"&":"?")+t),"no-store"===e.cache&&(e.url+=(~e.url.indexOf("?")?"&":"?")+"_t_="+Date.now())):s||(t=~e.headers["content-type"].indexOf("json")?JSON.stringify(t):Format.param(t)),this.xhr.responseType="blob",this.xhr.onreadystatechange=(t=>{e.timeout>0&&(e["time"+this.xhr.readyState]=t.timeStamp,4===this.xhr.readyState&&(e.isTimeout=e.time4-e.time1>e.timeout)),4===this.xhr.readyState&&this.__dispatch__(e.isTimeout)}),this.xhr.open(e.method,e.url);for(let t in e.headers)this.xhr.setRequestHeader(t,e.headers[t]);this.xhr.send(t),e.timeout&&e.timeout>0&&(this.xhr.timeout=e.timeout)}__type__(e){this.options.headers["content-type"]=FORM_TYPES[e]}__dispatch__(e){let t={status:200,statusText:"ok",body:"",headers:Object.create(null)};if(this.cancel)return this.__cancel__();if(e)return this.__timeout__();let s=this.xhr.status>=200&&this.xhr.status<400,r=this.xhr.getAllResponseHeaders().split("\n")||[];for(let e of r)if(e=e.trim()){let s=(e=e.split(":")).shift().toLowerCase();e=e.join(":").trim(),t.headers[s]=e}s?(t.status=this.xhr.status,204===t.status?t.statusText=ERRORS[10204]:304===t.status&&(t.statusText=ERRORS[10304])):(t.status=this.xhr.status||500,t.statusText=this.xhr.statusText||ERRORS[10500]),t.body=this.xhr.response,this.__success__(s,t)}__success__(e,t){var s=new _Response(t.status,t.statusText,t.body,t.headers);e?this.defer.resolve(s):this.defer.reject(s),delete this.xhr,delete this.options,delete this.defer}__cancel__(e){var t=new _Response(0,ERRORS[10100],Object.create(null));this.defer.reject(t),delete this.xhr,delete this.options,delete this.defer}__timeout__(e){var t=new _Response(504,ERRORS[10504],Object.create(null));this.defer.reject(t),delete this.xhr,delete this.options,delete this.defer}}class _Response{constructor(e=200,t="OK",s=null,r={}){this.status=e,this.statusText=t,this.ok=e>=200&&e<400,this.headers=r,Object.defineProperty(this,"__R__",{value:s,writable:!0,enumerable:!1,configurable:!0})}text(){return this.__R__.text()}json(){return this.__R__.text().then(e=>JSON.parse(e))}blob(){return this.__R__}arrayBuffer(){return this.__R__.arrayBuffer()}}const _fetch=function(e,t){return new _Request(e,t,{BASE_URL:_fetch.BASE_URL,__INIT__:_fetch.__INIT__||Object.create(null)})};_fetch.create=function(e,t=Object.create(null)){return function(s,r){return new _Request(s,r,{BASE_URL:e,__INIT__:t})}};export default _fetch;

View File

@ -0,0 +1 @@
export const toS=Object.prototype.toString;export const encode=encodeURIComponent;export const decode=decodeURIComponent;function serialize(e,t,o){var a;if(Array.isArray(t))t.forEach(function(t,r){a=e?`${e}[${Array.isArray(t)?r:""}]`:r,"object"==typeof t?serialize(a,t,o):o(a,t)});else for(let r in t)a=e?`${e}[${r}]`:r,"object"==typeof t[r]?serialize(a,t[r],o):o(a,t[r])}export const Format={parseForm(e){let t={},o=!1;for(let a,r=0;a=e.elements[r++];)switch(a.type){case"select-one":case"select-multiple":if(a.name.length&&!a.disabled)for(let e,o=0;e=a.options[o++];)e.selected&&(t[a.name]=e.value||e.text);break;case"file":a.name.length&&!a.disabled&&(t[a.name]=a.files[0],o=!0);break;case void 0:case"submit":case"reset":case"button":break;case"radio":case"checkbox":if(!a.checked)break;default:a.name.length&&!a.disabled&&(t[a.name]=a.value)}return o?this.mkFormData(t):t},mkFormData(e){let t=new FormData;for(let o in e){let a=e[o];Array.isArray(a)?a.forEach(function(e){t.append(o+"[]",e)}):t.append(o,e[o])}return t},param(e){if(!e||"string"==typeof e||"number"==typeof e)return e;let t=[];return"object"==typeof e&&serialize("",e,function(e,o){/native code/.test(o)||(o="function"==typeof o?o():o,o="[object File]"===toS.call(o)?o:encode(o),t.push(encode(e)+"="+o))}),t.join("&")}};

1
src/js/lib/fetch/next.js Normal file
View File

@ -0,0 +1 @@
import{Format,toS}from"./lib/format.js";const noop=function(e,t){this.defer.resolve(t)},NOBODY_METHODS=["GET","HEAD"],FORM_TYPES={form:"application/x-www-form-urlencoded; charset=UTF-8",json:"application/json; charset=UTF-8",text:"text/plain; charset=UTF-8"};class _Request{constructor(e="",t={},{BASE_URL:o,__INIT__:r}){if(!e)throw new Error("Argument url is required");e=e.replace(/#.*$/,""),o&&(/^([a-z]+:|\/\/)/.test(e)||(e=o+e)),t.method=(t.method||"get").toUpperCase(),this.options={headers:{"X-Requested-With":"XMLHttpRequest","content-type":FORM_TYPES.form},body:null,cache:"default",signal:null,timeout:3e4},t.signal||(this.control=new AbortController,t.signal=this.control.signal);var s=this.options.headers;return r.headers&&Object.assign(s,r.headers),t.headers&&(Object.assign(s,t.headers),delete t.headers),Object.assign(this.options,r,t,{url:e,headers:s}),this.__next__()}__next__(){var e=this.options,t=null,o=!1,r=!1,s=NOBODY_METHODS.includes(e.method);if(e.body)switch(typeof e.body){case"number":case"string":this.__type__("text"),t=e.body;break;case"object":if("FORM"===e.body.nodeName)e.method=e.body.method.toUpperCase()||"POST",o=(t=Format.parseForm(e.body)).constructor===FormData;else if(e.body.constructor===FormData)o=!0,s&&(e.method="POST"),t=e.body;else{for(let t in e.body)if("[object File]"===toS.call(e.body[t])){o=!0;break}o?(s&&(e.method="POST"),t=Format.mkFormData(e.body)):t=e.body}}o&&delete e.headers["content-type"];try{let t=document.createElement("a");t.href=e.url,r=location.protocol!==t.protocol||location.host!==t.host}catch(e){}r&&"omit"===e.credentials&&delete e.headers["X-Requested-With"],s?((t=Format.param(t))&&(e.url+=(~e.url.indexOf("?")?"&":"?")+t),"no-store"===e.cache&&(e.url+=(~e.url.indexOf("?")?"&":"?")+"_t_="+Date.now())):o||(t=~e.headers["content-type"].indexOf("json")?JSON.stringify(t):Format.param(t)),e.timeout&&e.timeout>0&&(this.timer=setTimeout(e=>{this.abort()},e.timeout),delete e.timeout);var n=e.url;delete e.url;for(let t in e)null!==e[t]&&void 0!==e[t]&&""!==e[t]||delete e[t];return window.fetch(n,e).then(e=>{return clearTimeout(this.timer),e.status>=200&&e.status<400?e:Promise.reject(e)}).catch(e=>(clearTimeout(this.timer),Promise.reject(e)))}abort(){this.control.abort()}__type__(e){this.options.headers["content-type"]=FORM_TYPES[e]}}const _fetch=function(e,t){return new _Request(e,t,{BASE_URL:_fetch.BASE_URL,__INIT__:_fetch.__INIT__||Object.create(null)})};_fetch.create=function(e,t=Object.create(null)){return function(o,r){return new _Request(o,r,{BASE_URL:e,__INIT__:t})}};export default _fetch;

10
src/js/lib/form/button.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-09-12 18:13:11
* @version v1.0.6
*
*/
customElements.get("wc-")||customElements.define("wc-");

View File

@ -0,0 +1,10 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-09-12 18:13:11
* @version v1.0.6
*
*/
var d=Object.defineProperty;var n=(r,o,e)=>o in r?d(r,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[o]=e;var l=(r,o,e)=>(n(r,typeof o!="symbol"?o+"":o,e),e);import"../icon/index.js";import s from"../utils.js";class c extends HTMLElement{constructor(){super();l(this,"props",{value:"",checked:!1,readonly:!1,disabled:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),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-flex;align-items:center;line-height:1;font-size:14px;cursor:pointer}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;height:32px;padding-right:16px;line-height:1;-moz-user-select:none;user-select:none;white-space:nowrap;cursor:inherit;outline:none;color:var(--color-dark-1)}:host label.checked .dot{background:var(--color-dark-1)}:host label.checked .dot wc-icon{--size: 14px}:host .dot{display:flex;justify-content:center;align-items:center;width:16px;height:16px;margin-right:4px;border:1px solid var(--color-dark-1);border-radius:4px;background:#fff;color:#fff;transition:box-shadow .15s linear,background .15s linear}:host .dot wc-icon{--size: 0px;transition:width .15s linear}:host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-plain-a)}:host([type=danger]) label{color:var(--color-red-1)}:host([type=danger]) label .dot{border-color:var(--color-red-1)}:host([type=danger]) label.checked .dot{background:var(--color-red-1)}:host([type=danger]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-red-a)}:host([type=info]) label{color:var(--color-blue-1)}:host([type=info]) label .dot{border-color:var(--color-blue-1)}:host([type=info]) label.checked .dot{background:var(--color-blue-1)}:host([type=info]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]) label{color:var(--color-green-1)}:host([type=success]) label .dot{border-color:var(--color-green-1)}:host([type=success]) label.checked .dot{background:var(--color-green-1)}:host([type=success]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-green-a)}:host([type=primary]) label{color:var(--color-teal-1)}:host([type=primary]) label .dot{border-color:var(--color-teal-1)}:host([type=primary]) label.checked .dot{background:var(--color-teal-1)}:host([type=primary]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=warning]) label{color:var(--color-orange-1)}:host([type=warning]) label .dot{border-color:var(--color-orange-1)}:host([type=warning]) label.checked .dot{background:var(--color-orange-1)}:host([type=warning]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-orange-a)}:host([readonly]){cursor:default;opacity:.8}:host([disabled]){cursor:not-allowed;opacity:.6}:host([disabled]) label{color:var(--color-grey-2)}</style> <label tabindex="0"> <span class="dot"><wc-icon is="get"></wc-icon></span> <slot /> </label> ',this.__SWITCH__=this.root.lastElementChild,this.__ICO__=this.__SWITCH__.children[0],this._isInGroup=!1}static get observedAttributes(){return["value","checked","readonly","disabled"]}_checkGroup(){this._isInGroup=this.parentNode.tagName==="WC-CHECKBOX-GROUP",this._isInGroup&&this.parentNode.root&&this.parentNode.value.includes(this.value)&&(this.checked=!0)}get value(){return this.props.value}set value(e){this.props.value=e}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e,this.__SWITCH__.classList.toggle("checked",this.props.checked)}get readOnly(){return this.props.readonly}set readOnly(e){var t=typeof e;e!==this.props.readonly&&(t==="boolean"&&e||t!=="boolean"?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&(t==="boolean"&&e||t!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled",""),this.__SWITCH__.removeAttribute("tabindex")):(this.props.disabled=!1,this.removeAttribute("disabled")))}_toggleCheck(){this.disabled||this.readOnly||(this.checked=!this.checked,this._isInGroup?this.parentNode.dispatchEvent(new CustomEvent("child-picked",{detail:{value:this.value,checked:this.checked}})):this.dispatchEvent(new CustomEvent("input")))}connectedCallback(){this._checkGroup(),this._handlClick=s.bind(this,"click",e=>{e.stopPropagation(),this._toggleCheck()}),this._handlKeydown=s.bind(this,"keydown",e=>{e.keyCode===32&&this._toggleCheck()})}disconnectedCallback(){s.unbind(this,"click",this._handlClick),s.unbind(this,"keydown",this._handlKeydown)}attributeChangedCallback(e,t,a){if(t!==a)switch(e){case"value":this.props[e]=a;break;case"checked":case"readonly":case"disabled":var i=e;i==="readonly"&&(i="readOnly"),this[i]=a!==null;break}}}customElements.get("wc-checkbox")||customElements.define("wc-checkbox",c);export{c as default};

View File

@ -0,0 +1,10 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-09-12 18:13:11
* @version v1.0.6
*
*/
var u=Object.defineProperty;var h=(i,r,e)=>r in i?u(i,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[r]=e;var l=(i,r,e)=>(h(i,typeof r!="symbol"?r+"":r,e),e);import d from"../utils.js";import"./checkbox-item.js";class o extends HTMLElement{constructor(){super();l(this,"props",{value:"",disabled:!1,readonly:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),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-flex;flex-wrap:wrap}</style> <slot /> "}static get observedAttributes(){return["value","disabled","readonly"]}_updateChildrenStat(e){Array.from(this.children).forEach(t=>{t.tagName==="WC-CHECKBOX"?t.root&&(e&&(t.disabled=this.disabled,t.readOnly=this.readOnly),this.value.includes(t.value)?t.checked=!0:t.checked=!1):t.remove()})}get value(){return this.props.value}set value(e){e!==this.props.value&&(this.props.value=[...e],this._updateChildrenStat())}get readOnly(){return this.props.readonly}set readOnly(e){var t=typeof e;e!==this.props.readonly&&(t==="boolean"&&e||t!=="boolean"?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")),this._updateChildrenStat(!0))}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&(t==="boolean"&&e||t!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")),this._updateChildrenStat(!0))}connectedCallback(){this._pickedFn=d.bind(this,"child-picked",e=>{var t=[...this.props.value],s=t.indexOf(e.detail.value);e.detail.checked?s<0&&t.push(e.detail.value):~s&&t.splice(s,1),this.value=t,this.dispatchEvent(new CustomEvent("input"))}),this.__observer=new MutationObserver(e=>{this._updateChildrenStat(!0)}),this.__observer.observe(this,{childList:!0,subtree:!0})}disconnectedCallback(){d.unbind(this,"child-picked",this._pickedFn),this.__observer.disconnect()}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"value":s&&(this.value=s.split(",").map(n=>n.trim()));break;case"readonly":case"disabled":var a=e;a==="readonly"&&(a="readOnly"),this[a]=s!==null;break}}}customElements.get("wc-checkbox-group")||customElements.define("wc-checkbox-group",o);export{o as default};

Some files were not shown because too many files have changed in this diff Show More