一大波更新

master
yutent 2023-09-01 18:39:40 +08:00
parent 4a80a18cc3
commit 74f4cd22d4
15 changed files with 888 additions and 600 deletions

View File

@ -1 +1,218 @@
html{width:100%;height:100vh}body{position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;font-size:14px;color:var(--color-dark-1)}.layout-left{display:flex;flex-direction:column;width:180px;height:100vh;background:var(--color-dark-2)}.layout-left wc-scroll{overflow:hidden;flex:1}.layout-left .domain-list{width:100%}.layout-left .domain-list .item{display:flex;justify-content:flex-end;align-items:center;height:40px;padding:0 20px 0 10px;color:var(--color-plain-1);cursor:pointer;transition:background .1s ease-in-out}.layout-left .domain-list .item wc-icon{--size: 12px;margin:auto -15px auto 5px;color:var(--color-grey-3)}.layout-left .domain-list .item:hover,.layout-left .domain-list .item.active{background:var(--color-dark-1)}.layout-left .domain-list .item.active{border-left:.3px solid var(--color-orange-1);color:var(--color-orange-1);font-weight:bold}.layout-left .domain-list .item.blank{justify-content:center;cursor:default}.layout-left .domain-list .item.blank:hover{background:none}.layout-left .action{display:flex;align-items:center;height:50px;padding:0 10px}.layout-right{flex:1;display:flex;flex-direction:column;background:#f7f8fb}.layout-right .toolbar{display:flex;align-items:center;justify-content:space-between;height:40px;padding:0 15px;background:var(--color-plain-2);box-shadow:0 2px 5px rgba(0,0,0,.1)}.layout-right .toolbar wc-input{width:200px}.layout-right .main{overflow:hidden;flex:1;display:flex;flex-direction:column;margin:20px 10px;padding:0 5px;background:#fff}.layout-right .thead{display:flex;align-items:center;justify-content:center;height:40px;margin-bottom:8px;border-bottom:1px solid var(--color-plain-2);text-align:center}.layout-right .thead span{flex:1}.layout-right .thead .long{flex:1.5}.layout-right wc-scroll{flex:1}.layout-right .record-list{width:100%;line-height:40px}.layout-right .record-list .item{display:flex;justify-content:center;align-items:center;height:40px;padding:0 10px;border-bottom:1px solid var(--color-plain-2);text-align:center}.layout-right .record-list .item wc-input,.layout-right .record-list .item span,.layout-right .record-list .item section{flex:1}.layout-right .record-list .item .long{flex:1.5}.layout-right .record-list .item section{display:flex;align-items:center;justify-content:center}.layout-right .record-list .item wc-button{margin-left:5px}.layout-right .record-list .item:last-child{border-bottom:none}.permission-error{position:fixed;left:0;top:0;z-index:102401;display:flex;flex-direction:column;align-items:center;width:100%;height:100%;padding:50px;background:rgba(255,233,233,.95);-webkit-backdrop-filter:blur(0.5rem);backdrop-filter:blur(0.5rem)}.permission-error pre{font-family:"Courier New",Courier,monospace;font-size:16px;color:var(--color-red-1)}.permission-error fieldset{width:600px;padding:0 30px 30px;border:1px solid var(--color-orange-1);border-radius:8px}.permission-error fieldset legend{padding:0 10px;font-size:16px}.permission-error fieldset dt{margin-top:20px;font-weight:bold}.permission-error fieldset code{display:block;padding:8px 10px;margin-top:8px;border-left:3px solid var(--color-plain-3);background:rgba(255,255,255,.3);font-family:"Courier New",Courier,monospace}.permission-error.show{display:flex}.context-menu{display:flex;flex-direction:column;width:100px;padding:5px 0;background:#fff}.context-menu .item{height:30px;line-height:30px;padding:0 15px;cursor:pointer}.context-menu .item:hover{background:#f2f5fc} html {
width: 100%;
height: 100vh;
}
body {
position: fixed;
left: 0;
top: 0;
display: flex;
width: 100%;
height: 100%;
line-height: 1.5;
font-size: 14px;
color: var(--color-dark-1);
}
.layout-left {
display: flex;
flex-direction: column;
width: 180px;
height: 100vh;
background: var(--color-dark-2);
}
.layout-left wc-scroll {
overflow: hidden;
flex: 1;
}
.layout-left .domain-list {
width: 100%;
}
.layout-left .domain-list .item {
display: flex;
justify-content: flex-end;
align-items: center;
height: 40px;
padding: 0 20px 0 10px;
color: var(--color-plain-1);
cursor: pointer;
transition: background 0.1s ease-in-out;
}
.layout-left .domain-list .item wc-icon {
--size: 12px;
margin: auto -15px auto 5px;
color: var(--color-grey-3);
}
.layout-left .domain-list .item:hover,
.layout-left .domain-list .item.active {
background: var(--color-dark-1);
}
.layout-left .domain-list .item.active {
border-left: 0.3px solid var(--color-orange-1);
color: var(--color-orange-1);
font-weight: bold;
}
.layout-left .domain-list .item.blank {
justify-content: center;
cursor: default;
}
.layout-left .domain-list .item.blank:hover {
background: none;
}
.layout-left .action {
display: flex;
align-items: center;
height: 50px;
padding: 0 10px;
}
.layout-right {
flex: 1;
display: flex;
flex-direction: column;
background: #f7f8fb;
}
.layout-right .toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 15px;
background: var(--color-plain-2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.layout-right .toolbar wc-input {
width: 200px;
}
.layout-right .main {
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
margin: 20px 10px;
padding: 0 5px;
background: #fff;
}
.layout-right .thead {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
margin-bottom: 8px;
border-bottom: 1px solid var(--color-plain-2);
text-align: center;
}
.layout-right .thead span {
flex: 1;
}
.layout-right .thead .long {
flex: 1.5;
}
.layout-right wc-scroll {
flex: 1;
}
.layout-right .record-list {
width: 100%;
line-height: 40px;
}
.layout-right .record-list .item {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
padding: 0 10px;
border-bottom: 1px solid var(--color-plain-2);
text-align: center;
}
.layout-right .record-list .item wc-input,
.layout-right .record-list .item span,
.layout-right .record-list .item section {
flex: 1;
}
.layout-right .record-list .item .long {
flex: 1.5;
}
.layout-right .record-list .item section {
display: flex;
align-items: center;
justify-content: center;
}
.layout-right .record-list .item wc-button {
margin-left: 5px;
}
.layout-right .record-list .item:last-child {
border-bottom: none;
}
.permission-error {
position: fixed;
left: 0;
top: 0;
z-index: 102401;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
padding: 50px;
background: rgba(255, 233, 233, 0.95);
-webkit-backdrop-filter: blur(0.5rem);
backdrop-filter: blur(0.5rem);
}
.permission-error pre {
font-family: 'Courier New', Courier, monospace;
font-size: 16px;
color: var(--color-red-1);
}
.permission-error fieldset {
width: 600px;
padding: 0 30px 30px;
border: 1px solid var(--color-orange-1);
border-radius: 8px;
}
.permission-error fieldset legend {
padding: 0 10px;
font-size: 16px;
}
.permission-error fieldset dt {
margin-top: 20px;
font-weight: bold;
}
.permission-error fieldset code {
display: block;
padding: 8px 10px;
margin-top: 8px;
border-left: 3px solid var(--color-plain-3);
background: rgba(255, 255, 255, 0.3);
font-family: 'Courier New', Courier, monospace;
}
.permission-error.show {
display: flex;
}
.context-menu {
display: flex;
flex-direction: column;
width: 100px;
padding: 5px 0;
background: #fff;
}
.context-menu .item {
height: 30px;
line-height: 30px;
padding: 0 15px;
cursor: pointer;
}
.context-menu .item:hover {
background: #f2f5fc;
}

View File

@ -40,6 +40,4 @@ createApp({
render() { render() {
return html` <wc-home></wc-home> ` return html` <wc-home></wc-home> `
} }
}) }).mount()
.use(router)
.mount()

View File

@ -3,64 +3,468 @@
* @author yutent<yutent.io@gmail.com> * @author yutent<yutent.io@gmail.com>
* @date 2023/08/08 18:19:17 * @date 2023/08/08 18:19:17
*/ */
import { html, css, Component } from 'wkit' import { html, css, Component, classMap, nextTick } from 'wkit'
import 'ui/icon/index.js' import 'ui/icon/index.js'
import 'ui/scroll/index.js'
import 'ui/layer/index.js'
import 'ui/space/index.js' import 'ui/space/index.js'
import 'ui/form/input.js' import 'ui/form/input.js'
import 'ui/form/switch.js' import 'ui/form/switch.js'
import 'ui/form/button.js' import 'ui/form/button.js'
import 'ui/form/link.js'
import './permission.js'
import { getHistory, saveHosts } from '../utils/index.js'
const HOST_DATA = await getHistory()
let tmp_records = Object.create(null)
function noop() {}
class Home extends Component { class Home extends Component {
static props = { static props = {
foo: '' filter: '',
activeDomain: localStorage.getItem('last_domain') || '', //当前选中的域名
editDomain: '', // 当前临时要编辑的域名, 即右键菜单选择到的
domains: Object.keys(HOST_DATA),
records: [],
permissionShow: false
} }
static styles = [ static styles = [
css` css`
:host { :host {
flex: 1; flex: 1;
} display: flex;
.main {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 32px; padding: 32px;
color: var(--color-dark-1); color: var(--color-dark-1);
background: #f0f0f0; background: #f0f0f0;
} }
.visible {
display: block;
}
wc-input { ul li {
list-style: none;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
`,
css`
.layout-left {
flex-shrink: 0;
display: flex;
flex-direction: column;
width: 180px;
height: 100vh;
background: var(--color-plain-2);
}
wc-scroll {
flex: 1; flex: 1;
} }
wc-button { .layout-left .domain-list {
width: 100%; width: 100%;
} }
.card { .layout-left .domain-list .item {
width: 100%; display: flex;
padding: 12px; justify-content: flex-end;
margin: 0 auto 24px; align-items: center;
border: 0; height: 40px;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.075); padding: 0 12px;
cursor: pointer;
transition: background 0.15s ease-in-out;
}
.layout-left .item wc-icon {
--wc-icon-size: 12px;
margin-left: 8px;
color: var(--color-grey-2);
}
.layout-left .item:hover,
.layout-left .item.active {
background: #fff; background: #fff;
} }
legend { .layout-left .item.active {
-webkit-touch-callout: none; border-right: 2px solid var(--color-orange-1);
user-select: none; color: var(--color-orange-1);
color: var(--color-dark-1); }
font-weight: bold; .layout-left .item.active wc-icon {
color: var(--color-orange-1);
}
.layout-left .item.blank {
justify-content: center;
cursor: default;
}
.layout-left .item.blank:hover {
background: none;
}
.action {
display: flex;
align-items: center;
height: 50px;
padding: 0 10px;
}
`,
css`
.layout-right {
flex: 1;
display: flex;
flex-direction: column;
}
.layout-right .toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 15px;
border-bottom: 1px solid var(--color-plain-3);
background: var(--color-plain-1);
}
`,
css`
.layout-right .main {
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
padding: 8px;
background: #fff;
}
.layout-right .item {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
padding: 0 4px;
border-bottom: 1px solid var(--color-plain-2);
text-align: center;
}
.layout-right .thead {
line-height: 40px;
}
.layout-right .item span {
flex: 1;
}
.layout-right .item .long {
flex: 1.5;
}
.layout-right wc-scroll {
overflow: hidden;
}
.layout-right .records {
flex: 1;
}
.layout-right .item wc-input {
flex: 1;
min-width: 0;
--wc-input-border-color: transparent;
}
.layout-right .record-list .item .long {
flex: 1.5;
}
.layout-right .record-list .item wc-button {
margin-left: 5px;
}
.layout-right .record-list .item:last-child {
border-bottom: none;
}
`,
css`
.context-menu {
display: flex;
flex-direction: column;
width: 100px;
padding: 5px 0;
background: #fff;
}
.context-menu .item {
height: 30px;
line-height: 30px;
padding: 0 15px;
cursor: pointer;
}
.context-menu .item :hover {
background: #f2f5fc;
} }
` `
] ]
mounted() {
window.foo = this
// let hosts = getHistory()
// console.log(hosts)
// this.domains = Object.keys(hosts)
this.toggleDomain(null, this.activeDomain)
}
addDomain() {
layer
.prompt('请输入根域名', function (val, done) {
if (
val === 'localhost' ||
val === 'local' ||
/^[\w.\-]+\.[a-z]+$/.test(val)
) {
done()
} else {
layer.toast('域名格式错误', 'error')
}
})
.then(val => {
this.domains.push(val)
HOST_DATA[val] = []
if (!this.activeDomain) {
this.toggleDomain(null, val)
}
this.save()
})
.catch(noop)
}
toggleDomain(ev, name) {
name = name || ev.currentTarget.dataset.name
this.activeDomain = name
this.records = (HOST_DATA[name] || []).sort((a, b) =>
a.record.localeCompare(b.record)
)
tmp_records = Object.create(null)
for (let it of this.records) {
if (tmp_records[it.record]) {
tmp_records[it.record].push(it)
} else {
tmp_records[it.record] = [it]
}
}
document.title = `伪域名解析 ${name} `
localStorage.setItem('last_domain', name)
nextTick(() => {
this.$refs.records.scrollTop = 0
})
}
showMenu(ev) {
this.$refs.context.close()
var { pageX, pageY } = ev
if (pageY + 70 > 600) {
pageY -= 70
}
var elem = ev.target
if (elem.tagName !== 'LI') {
elem = elem.parentNode
}
this.editDomain = elem.dataset.name
nextTick(_ => {
this.$refs.context.moveTo({ left: pageX + 'px', top: pageY + 'px' })
this.$refs.context.show()
})
}
confirmAction(ev) {
this.$refs.context.close()
if (ev.target.tagName === 'LI') {
var act = ev.target.dataset.act
if (act === 'del') {
layer
.confirm(`是否要删除域名「${this.editDomain}」?`)
.then(res => {
if (this.editDomain === this.activeDomain) {
if (this.records.length) {
return layer.toast(
'该域名下有主机记录, 请先删除主机记录后再删除域名',
'error'
)
}
} else {
if (HOST_DATA[this.editDomain].length > 0) {
return layer.toast(
'该域名下有主机记录, 请先删除主机记录后再删除域名',
'error'
)
}
}
delete HOST_DATA[this.editDomain]
this.domains.remove(this.editDomain)
this.activeDomain = ''
this.editDomain = ''
this.records.clear()
this.save()
this.toggleDomain(this.domains[0])
})
.catch(noop)
} else if (act === 'edit') {
layer
.prompt(`请输入新的名字「${this.editDomain}`, (val, done) => {
if (val === this.editDomain || HOST_DATA[val]) {
return
}
if (
val === 'localhost' ||
val === 'local' ||
/^[\w.]+\.[a-z]+$/.test(val)
) {
done()
} else {
layer.toast('域名格式错误', 'error')
}
})
.then(val => {
var idx = this.domains.indexOf(this.editDomain)
this.domains.set(idx, val)
HOST_DATA[val] = HOST_DATA[this.editDomain]
delete HOST_DATA[this.editDomain]
this.activeDomain = ''
this.editDomain = ''
this.save()
this.toggleDomain(val)
})
.catch(noop)
}
}
}
// 同一个记录, 允许一条被激活
recordChanges(item) {
if (item.enabled) {
if (tmp_records[item.record].length > 1) {
for (let it of tmp_records[item.record]) {
if (it.value !== item.value) {
it.enabled = false
}
}
}
}
}
save() {
if (this.activeDomain) {
HOST_DATA[this.activeDomain] = this.records
}
// electron.saveHosts(HOST_DATA)
layer.toast('保存成功', 'success')
}
render() { render() {
return html` return html`
<div class="layout-left noselect">
<wc-scroll>
<ul class="domain-list" @contextmenu.prevent=${this.showMenu}>
${this.domains.map(
it => html`
<li
class=${classMap({
item: true,
active: it === this.activeDomain
})}
data-name=${it}
@click=${this.toggleDomain}
>
<span>${it}</span>
<wc-icon name="right"></wc-icon>
</li>
`
)}
${this.domains.length < 1
? html`<li class="item blank">没有域名</li>`
: ''}
</ul>
</wc-scroll>
<section class="action">
<wc-button circle icon="plus" @click=${this.addDomain}> </wc-button>
</section>
</div>
<div class="layout-right noselect">
<header class="toolbar">
<wc-button size="m" icon="plus" type="primary" @click="addRecord"
>新增记录</wc-button
>
<wc-button size="m" icon="fly" type="info" @click="save"
>保存</wc-button
>
</header>
<main class="main"> <main class="main">
<fieldset class="card"> <header class="thead item">
<legend>镜像地址</legend> <span>主机记录</span>
</fieldset> <span>类型</span>
<span class="long">记录值</span>
<span>操作</span>
<span>备注</span>
</header>
<wc-scroll class="records" ref="records">
${this.records.map(
it => html`
<div class="item">
<wc-input
size="m"
autofocus
@change="updateCacheDict(it)"
:value=${it.record}
label="根域请填 @"
></wc-input>
<span>A</span>
<wc-input
size="m"
class="long"
:value=${it.value}
label="请填写IP"
></wc-input>
<wc-space gap="s">
<wc-link size="m" type="danger" @click="$remove"
>删除</wc-link
>
<wc-link size="m" type="info" @click="clone(it)"
>克隆</wc-link
>
<wc-switch
size="m"
:value=${it.enabled}
@change=${this.recordChanges(it)}
></wc-switch>
</wc-space>
<wc-input size="m" no-border :value=${it.remark}></wc-input>
</div>
`
)}
</wc-scroll>
</main> </main>
</div>
<wc-permission
class=${classMap({ visible: this.permissionShow })}
></wc-permission>
<wc-layer ref="context" left="100px" top="0" radius="0">
<ul class="context-menu" @click=${this.confirmAction}>
<li class="item" data-act="del">删除域名</li>
<li class="item" data-act="edit">修改域名</li>
</ul>
</wc-layer>
` `
} }
} }

View File

@ -0,0 +1,106 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/08/08 18:19:17
*/
import { html, css, Component } from 'wkit'
const tips_header = `/************************************************************/
* hosts文件没有写权限 *
/************************************************************/
`
class Permission extends Component {
static props = {
foo: ''
}
static styles = css`
:host {
display: none;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
.permission-error {
position: fixed;
left: 0;
top: 0;
z-index: 1024;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
padding: 24px 52px;
line-height: 1.5;
background: rgba(255, 233, 233, 0.95);
-webkit-backdrop-filter: blur(5px);
}
pre {
font-family: 'Courier New', Courier, monospace;
font-size: 14px;
color: var(--color-red-1);
}
fieldset {
width: 600px;
padding: 0 30px 30px;
border: 1px solid var(--color-orange-1);
border-radius: 8px;
}
legend {
padding: 0 10px;
font-size: 16px;
}
dt {
margin-top: 20px;
font-weight: bold;
}
code {
display: block;
padding: 8px 10px;
margin-top: 8px;
border-left: 3px solid var(--color-plain-3);
background: rgba(255, 255, 255, 0.8);
font-family: 'Courier New', Courier, monospace;
}
`
check() {
//
}
render() {
return html`
<div class="permission-error">
<pre class="noselect">${tips_header}</pre>
<fieldset>
<legend>操作指引</legend>
<dl>
<dt>MacOS用户</dt>
<dd>打开终端, 执行以下命令</dd>
<dd><code>sudo chown $USER:admin /etc/hosts</code></dd>
<dt>Linux用户</dt>
<dd>打开终端, 执行以下命令</dd>
<dd><code>sudo chown $USER: /etc/hosts</code></dd>
<dt>完成之后</dt>
<dd>点击下面的按钮重新检测.</dd>
<dd>
<wc-button type="danger" @click=${this.check}>权限检测</wc-button>
</dd>
</dl>
</fieldset>
</div>
`
}
}
Permission.reg('permission')

View File

@ -33,7 +33,7 @@ body {font-family: 'Helvetica Neue', Arial, 'WenQuanYi Micro Hei', 'PingFang SC'
code, pre, samp {font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;white-space:pre-wrap;} code, pre, samp {font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;white-space:pre-wrap;}
[anot],[\:repeat],[\:if] {visibility: hidden;} [anot],[\:repeat],[\:if] {visibility: hidden;}
.noselect {-webkit-touch-callout: none;-webkit-user-select: none;-moz-user-select: none;user-select: none;} .noselect {-webkit-touch-callout: none;-webkit-user-select: none;user-select: none;}
.noselect img, .noselect a {-webkit-user-drag: none;} .noselect img, .noselect a {-webkit-user-drag: none;}
.text-ell {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;} .text-ell {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
.text-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;} .text-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}

View File

@ -29,7 +29,7 @@
</script> </script>
<script type="module" src="/app.js"></script> <script type="module" src="/app.js"></script>
</head> </head>
<body class="noselect"> <body>
<wc-app></wc-app> <wc-app></wc-app>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

View File

@ -1,44 +1,4 @@
import { nextTick, css, html, Component, classMap, outsideClick } from "wkit"; import{nextTick as r,css as l,html as s,Component as a,classMap as h,outsideClick as c}from"wkit";import"../icon/index.js";const d={duration:100,custom:[{transform:"scaleY(0)",opacity:0},{transform:"scaleY(1)",opacity:1}]};class u extends a{static props={readOnly:!1,autofocus:!1,disabled:!1,clearable:!1,icon:"",placeholder:"",maxlength:{type:Number,default:null},minlength:{type:Number,default:null},value:"str!",lazy:"num!0"};#t=[];#e=-1;#i=!1;#s=0;static styles=[l`:host{position:relative;display:inline-flex;min-width:188px;height:32px;user-select:none;-moz-user-select:none;color:var(--color-dark-1);border-radius:3px;cursor:text;transition:box-shadow .15s linear}.label{flex:1;display:flex;justify-content:center;align-items:center;height:100%;font-size:14px;border:1px solid var(--wc-input-border-color, var(--color-grey-2));border-radius:inherit;background:var(--bg-color, #fff);color:inherit;cursor:inherit}.label input{flex:1;min-width:36px;width:0;height:100%;padding:0 8px;line-height:1;border:0;border-radius:inherit;font:inherit;color:inherit;background:none;outline:none;box-shadow:none;cursor:inherit}.label input::placeholder{color:var(--color-grey-1)}.label .close{--wc-icon-size: 18px;margin:0 8px 0 4px;padding:4px;border-radius:50%;color:var(--color-grey-2);cursor:pointer;transition:background .15s linear}.label .close:hover{background:var(--color-plain-1)}.label .icon{--wc-icon-size: 16px;margin:0 8px 0 4px;color:var(--color-grey-2)}.suggestion{overflow:hidden;position:absolute;z-index:1;left:0;top:calc(100% + 4px);width:100%;padding:4px 0;border-radius:4px;box-shadow:0 2px 5px rgba(0,0,0,.15);transform-origin:top}.suggestion .list{width:100%;background:#fff}.suggestion.hide{display:none}.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-2)}:host([round]){border-radius:26px}:host([round]) .label input{padding:0 10px}:host(:focus-within){box-shadow:0 0 0 2px var(--color-plain-a)}:host([disabled]){pointer-events:none;cursor:not-allowed}:host([disabled]) .label{border-color:var(--color-grey-1);background:var(--color-plain-1);opacity:.6}:host([readonly]){cursor:default}`,l`:host([size=m]){min-width:128px;height:24px;font-size:12px}:host([size=m]) .label{height:24px;font-size:12px}:host([size=m]) .icon{--wc-icon-size: 12px}:host([size=m][circle]){width:24px;height:24px}:host([size=xl]){min-width:224px;height:36px;font-size:14px}:host([size=xl]) .label{height:36px;font-size:14px}:host([size=xl]) .icon{--wc-icon-size: 14px}:host([size=xl][circle]){width:36px;height:36px}:host([size=xxl]){min-width:288px;height:44px;font-size:14px}:host([size=xxl]) .label{height:44px;font-size:14px}:host([size=xxl]) .icon{--wc-icon-size: 14px}:host([size=xxl][circle]){width:44px;height:44px}`];renderClear(){return s`<wc-icon class="close" name="close" @click=${this.clear} />`}render(){let e=h({suggestion:!0,hide:!this.#t.length});return s`
import "../icon/index.js";
const ANIMATION = {
duration: 100,
custom: [
{ transform: "scaleY(0)", opacity: 0 },
{ transform: "scaleY(1)", opacity: 1 }
]
};
class Input extends Component {
static props = {
readOnly: false,
autofocus: false,
disabled: false,
clearable: false,
icon: "",
placeholder: "",
maxlength: { type: Number, default: null },
minlength: { type: Number, default: null },
value: "str!",
lazy: "num!0"
// 并发拦截时间, 单位毫秒
};
#list = [];
#selectIndex = -1;
#listShowing = false;
#stamp = 0;
static styles = [
css`:host{position:relative;display:inline-flex;min-width:188px;height:32px;user-select:none;-moz-user-select:none;color:var(--color-dark-1);border-radius:3px;cursor:text;transition:box-shadow .15s linear}.label{flex:1;display:flex;justify-content:center;align-items:center;height:100%;font-size:14px;border:1px solid var(--wc-input-border-color, var(--color-grey-2));border-radius:inherit;background:var(--bg-color, #fff);color:inherit;cursor:inherit}.label input{flex:1;min-width:36px;width:0;height:100%;padding:0 8px;border:0;border-radius:inherit;font:inherit;color:inherit;background:none;outline:none;box-shadow:none;cursor:inherit}.label input::placeholder{color:var(--color-grey-1)}.label .close{--wc-icon-size: 18px;margin:0 8px 0 4px;padding:4px;border-radius:50%;color:var(--color-grey-2);cursor:pointer;transition:background .15s linear}.label .close:hover{background:var(--color-plain-1)}.label .icon{--wc-icon-size: 16px;margin:0 8px 0 4px;color:var(--color-grey-2)}.suggestion{overflow:hidden;position:absolute;z-index:1;left:0;top:calc(100% + 4px);width:100%;padding:4px 0;border-radius:4px;box-shadow:0 2px 5px rgba(0,0,0,.15);transform-origin:top}.suggestion .list{width:100%;background:#fff}.suggestion.hide{display:none}.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-2)}:host([round]){border-radius:26px}:host([round]) .label input{padding:0 10px}:host(:focus-within){box-shadow:0 0 0 2px var(--color-plain-a)}:host([disabled]){pointer-events:none;cursor:not-allowed}:host([disabled]) .label{border-color:var(--color-grey-1);background:var(--color-plain-1);opacity:.6}:host([readonly]){cursor:default}`,
//尺寸
css`:host([size=m]){min-width:128px;height:24px;font-size:12px}:host([size=m]) .label{height:24px;font-size:12px}:host([size=m]) .icon{--wc-icon-size: 12px}:host([size=m][circle]){width:24px;height:24px}:host([size=xl]){min-width:224px;height:36px;font-size:14px}:host([size=xl]) .label{height:36px;font-size:14px}:host([size=xl]) .icon{--wc-icon-size: 14px}:host([size=xl][circle]){width:36px;height:36px}:host([size=xxl]){min-width:288px;height:44px;font-size:14px}:host([size=xxl]) .label{height:44px;font-size:14px}:host([size=xxl]) .icon{--wc-icon-size: 14px}:host([size=xxl][circle]){width:44px;height:44px}`
];
renderClear() {
return html`<wc-icon class="close" name="close" @click=${this.clear} />`;
}
render() {
let classes = classMap({
suggestion: true,
hide: !this.#list.length
});
return html`
<div class="label"> <div class="label">
<slot name="prepend"> <slot name="prepend">
<wc-icon class="icon" name=${this.icon}></wc-icon> <wc-icon class="icon" name=${this.icon}></wc-icon>
@ -57,115 +17,19 @@ class Input extends Component {
autofocus=${this.autofocus} autofocus=${this.autofocus}
:value=${this.value} :value=${this.value}
/> />
${this.clearable && this.value ? this.renderClear() : ""} ${this.clearable&&this.value?this.renderClear():""}
<slot name="append"></slot> <slot name="append"></slot>
<div class=${classes} ref="suggestion" #animation=${ANIMATION}> <div class=${e} ref="suggestion" #animation=${d}>
<div ref="scroller" class="scroller"> <div ref="scroller" class="scroller">
<ul class="list" @click=${this.handleClickItem} ref="list"> <ul class="list" @click=${this.handleClickItem} ref="list">
${this.#list.map( ${this.#t.map((t,i)=>s`<li
(li, idx) => html`<li focus=${this.#e===i?!0:null}
focus=${this.#selectIndex === idx ? true : null} index=${i}
index=${idx}
> >
${li.value} ${t.value}
</li>` </li>`)}
)}
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
`; `}handleInput(e){let{lazy:t}=this;this.value=e.currentTarget.value,!(t&&Date.now()-this.#s<t)&&(this.#s=Date.now(),this.emitFetchSuggest())}handleClickItem(e){let t=e.target.getAttribute("index");this.#e=t,this.emitSelect()}clear(){this.$refs.input.value="",this.value="",this.$emit("change"),this.$emit("input")}handleChange(){this.$emit("change")}handleKeyDown(e){let{lazy:t,minlength:i,value:o}=this;if(e.keyCode===13)return e.preventDefault(),this.#e>-1&&this.#i?this.emitSelect():t&&Date.now()-this.#s<t||(this.#s=Date.now(),i&&o.length<i)?void 0:this.$emit("submit");if(e.keyCode===38||e.keyCode===40){e.preventDefault();let n=e.keyCode===38?-1:1;this.#e+=n,this.#e<0&&(this.#e=0),this.#e>this.#t.length-1&&(this.#e=this.#t.length-1),this.$requestUpdate()}}emitSelect(){let e=this.#t[this.#e];this.value=e.value,this.$refs.suggestion.$animate(!0),this.#i=!1,this.$requestUpdate(),this.$emit("change"),this.$emit("input"),this.$emit("select",{index:this.#e,value:e})}emitFetchSuggest(){this.$emit("fetch-suggest",{value:this.value,send:e=>{this.#t=e.slice(0,10),this.#e=-1,this.$requestUpdate()}})}handleFocus(){this.#i||(this.#i=!0,this.$refs.suggestion.$animate())}mounted(){this.autofocus&&r(e=>this.$refs.input.focus()),c(this,()=>{this.#i=!1,this.$refs.suggestion.$animate(!0)})}}u.reg("input");
}
handleInput(e) {
let { lazy } = this;
this.value = e.currentTarget.value;
if (lazy && Date.now() - this.#stamp < lazy) {
return;
}
this.#stamp = Date.now();
this.emitFetchSuggest();
}
handleClickItem(e) {
let index = e.target.getAttribute("index");
this.#selectIndex = index;
this.emitSelect();
}
clear() {
this.$refs.input.value = "";
this.value = "";
this.$emit("change");
this.$emit("input");
}
handleChange() {
this.$emit("change");
}
handleKeyDown(e) {
let { lazy, minlength, value } = this;
if (e.keyCode === 13) {
e.preventDefault();
if (this.#selectIndex > -1 && this.#listShowing) {
return this.emitSelect();
}
if (lazy && Date.now() - this.#stamp < lazy) {
return;
}
this.#stamp = Date.now();
if (minlength && value.length < minlength) {
return;
}
return this.$emit("submit");
}
if (e.keyCode === 38 || e.keyCode === 40) {
e.preventDefault();
let step = e.keyCode === 38 ? -1 : 1;
this.#selectIndex += step;
if (this.#selectIndex < 0) {
this.#selectIndex = 0;
}
if (this.#selectIndex > this.#list.length - 1) {
this.#selectIndex = this.#list.length - 1;
}
this.$requestUpdate();
}
}
// 触发列表选择
emitSelect() {
let item = this.#list[this.#selectIndex];
this.value = item.value;
this.$refs.suggestion.$animate(true);
this.#listShowing = false;
this.$requestUpdate();
this.$emit("change");
this.$emit("input");
this.$emit("select", {
index: this.#selectIndex,
value: item
});
}
emitFetchSuggest() {
this.$emit("fetch-suggest", {
value: this.value,
send: (list) => {
this.#list = list.slice(0, 10);
this.#selectIndex = -1;
this.$requestUpdate();
}
});
}
handleFocus() {
if (!this.#listShowing) {
this.#listShowing = true;
this.$refs.suggestion.$animate();
}
}
mounted() {
if (this.autofocus) {
nextTick((_) => this.$refs.input.focus());
}
outsideClick(this, () => {
this.#listShowing = false;
this.$refs.suggestion.$animate(true);
});
}
}
Input.reg("input");

View File

@ -0,0 +1,5 @@
import{css as r,html as s,Component as l,bind as i,nextTick as n}from"wkit";class c extends l{static props={type:"primary",to:"",autofocus:!1,disabled:!1,lazy:0};static styles=[r`:host{position:relative;display:inline-flex;border-radius:2px;user-select:none;-moz-user-select:none;font-size:inherit;cursor:pointer;transition:box-shadow .15s linear}:host .link{display:flex;justify-content:center;align-items:center;width:100%;padding:var(--padding, 0 2px);line-height:1;font-size:inherit;font-family:inherit;outline:none;color:inherit;cursor:inherit;text-decoration:none;transition:color .15s linear}:host .link::-moz-focus-inner{border:none}:host::after{position:absolute;bottom:-2px;left:0;width:100%;height:1px;border-bottom:1px dashed rgba(0,0,0,0);content:"";opacity:0;transition:opacity .15s linear}`,r`:host([type=primary]){color:var(--color-teal-2)}:host([type=primary])::after{border-color:var(--color-teal-1)}:host([type=primary]:not([disabled]):hover){color:var(--color-teal-1)}:host([type=primary]:not([disabled]):active){color:var(--color-teal-3)}:host([type=primary]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=info]){color:var(--color-blue-2)}:host([type=info])::after{border-color:var(--color-blue-1)}:host([type=info]:not([disabled]):hover){color:var(--color-blue-1)}:host([type=info]:not([disabled]):active){color:var(--color-blue-3)}:host([type=info]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]){color:var(--color-green-2)}:host([type=success])::after{border-color:var(--color-green-1)}:host([type=success]:not([disabled]):hover){color:var(--color-green-1)}:host([type=success]:not([disabled]):active){color:var(--color-green-3)}:host([type=success]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-green-a)}:host([type=warning]){color:var(--color-orange-2)}:host([type=warning])::after{border-color:var(--color-orange-1)}:host([type=warning]:not([disabled]):hover){color:var(--color-orange-1)}:host([type=warning]:not([disabled]):active){color:var(--color-orange-3)}:host([type=warning]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-orange-a)}:host([type=danger]){color:var(--color-red-2)}:host([type=danger])::after{border-color:var(--color-red-1)}:host([type=danger]:not([disabled]):hover){color:var(--color-red-1)}:host([type=danger]:not([disabled]):active){color:var(--color-red-3)}:host([type=danger]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-red-a)}:host([type=secondary]){color:var(--color-dark-2)}:host([type=secondary])::after{border-color:var(--color-dark-1)}:host([type=secondary]:not([disabled]):hover){color:var(--color-dark-1)}:host([type=secondary]:not([disabled]):active){color:var(--color-dark-3)}:host([type=secondary]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-dark-a)}:host([type=help]){color:var(--color-grey-2)}:host([type=help])::after{border-color:var(--color-grey-1)}:host([type=help]:not([disabled]):hover){color:var(--color-grey-1)}:host([type=help]:not([disabled]):active){color:var(--color-grey-3)}:host([type=help]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-grey-a)}`,r`:host(:not([disabled]):hover)::after,:host([underline])::after{opacity:1}:host([disabled]){cursor:not-allowed;opacity:.6}`];mounted(){this.stamp=0,this.autofocus&&n(o=>this.$refs.a.focus()),this._clickFn=i(this.$refs.a,"click",o=>{let{disabled:a,lazy:e}=this,t=Date.now();if(a){o.preventDefault(),o.stopPropagation();return}if(e>0&&t-this.stamp<e){o.preventDefault(),o.stopPropagation();return}this.stamp=t},!0)}render(){return s`
<a tabindex="0" ref="a" class="link" href=${this.to||"javascript:;"}>
<slot />
</a>
`}}c.reg("link");

View File

@ -1,61 +1,13 @@
import { nextTick, css, html, Component, classMap } from "wkit"; import{css as e,html as i,Component as s,classMap as a}from"wkit";class n extends s{static props={value:{type:Boolean,default:!1},inactiveText:"",activeText:"",inlineText:!1,disabled:!1,readonly:!1};static styles=[e`:host{display:inline-flex;align-items:center;font-size:14px;cursor:pointer}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;padding:0 8px 0 2px;line-height:1;-moz-user-select:none;user-select:none;white-space:nowrap;cursor:inherit;outline:none;color:var(--color-dark-1)}:host .dot{display:flex;align-items:center;justify-content:space-between;min-width:36px;height:18px;padding:0 4px;margin-right:5px;line-height:14px;border-radius:16px;background:var(--color-plain-3);transition:box-shadow .2s ease,background .2s ease}:host .dot::before{display:block;width:14px;height:14px;border-radius:50%;background:#fff;content:""}:host .dot::after{display:flex;padding:0 2px;font-size:12px;content:attr(st);color:#fff}:host .dot.open{flex-direction:row-reverse;background:var(--color-teal-1)}`,e`:host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-plain-a)}`,e`:host([size=m]){height:24px;font-size:12px}:host([size=m]) .dot{min-width:30px;height:16px;line-height:12px}:host([size=m]) .dot::before{width:12px;height:12px}:host([size=xl]){height:36px;font-size:14px}:host([size=xl]) .dot{min-width:35px;height:18px;line-height:14px}:host([size=xl]) .dot::before{width:14px;height:14px}:host([size=xxl]){height:44px;font-size:14px}:host([size=xxl]) .dot{min-width:35px;height:18px;line-height:14px}:host([size=xxl]) .dot::before{width:14px;height:14px}`,e`:host([type=primary]) .dot.open{background:var(--color-teal-1)}:host([type=primary]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=info]) .dot.open{background:var(--color-blue-1)}:host([type=info]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]) .dot.open{background:var(--color-green-1)}:host([type=success]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-green-a)}:host([type=warning]) .dot.open{background:var(--color-orange-1)}:host([type=warning]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-orange-a)}:host([type=danger]) .dot.open{background:var(--color-red-1)}:host([type=danger]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-red-a)}`,e`:host([readonly]),:host([disabled]){cursor:not-allowed;opacity:.6}:host([readonly]){cursor:default}`];toggleCheck(t){if(this.disabled||this.readOnly)return;t.stopPropagation(),this.value=!this.value;let o={value:this.value};this.$emit("input"),this.$emit("change",o)}handleClick(t){(t.type==="click"||t.keyCode===32)&&(t.preventDefault(),this.toggleCheck(t))}mounted(){}render(){let t=a({dot:!0,open:this.value});return i` <label
class Switch extends Component { tabindex=${this.disabled||this.readOnly?"none":0}
static props = {
value: {
type: Boolean,
default: false
},
inactiveText: "",
activeText: "",
inlineText: false,
disabled: false,
readonly: false
};
static styles = [
css`:host{display:inline-flex;align-items:center;font-size:14px;cursor:pointer}:host label{display:flex;justify-content:center;align-items:center;min-width: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 .dot{display:flex;align-items:center;justify-content:space-between;min-width:36px;height:18px;padding:0 4px;margin-right:5px;line-height:14px;border-radius:16px;background:var(--color-plain-3);transition:box-shadow .2s ease,background .2s ease}:host .dot::before{display:block;width:14px;height:14px;border-radius:50%;background:#fff;content:""}:host .dot::after{display:flex;padding:0 2px;font-size:12px;content:attr(st);color:#fff}:host .dot.open{flex-direction:row-reverse;background:var(--color-teal-1)}`,
css`:host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-plain-a)}`,
// 尺寸
css`:host([size=m]){height:24px;font-size:12px}:host([size=m]) .dot{min-width:30px;height:16px;line-height:12px}:host([size=m]) .dot::before{width:12px;height:12px}:host([size=xl]){height:36px;font-size:14px}:host([size=xl]) .dot{min-width:35px;height:18px;line-height:14px}:host([size=xl]) .dot::before{width:14px;height:14px}:host([size=xxl]){height:44px;font-size:14px}:host([size=xxl]) .dot{min-width:35px;height:18px;line-height:14px}:host([size=xxl]) .dot::before{width:14px;height:14px}`,
// 配色
css`:host([type=primary]) .dot.open{background:var(--color-teal-1)}:host([type=primary]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=info]) .dot.open{background:var(--color-blue-1)}:host([type=info]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]) .dot.open{background:var(--color-green-1)}:host([type=success]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-green-a)}:host([type=warning]) .dot.open{background:var(--color-orange-1)}:host([type=warning]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-orange-a)}:host([type=danger]) .dot.open{background:var(--color-red-1)}:host([type=danger]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-red-a)}`,
// 状态
css`:host([readonly]),:host([disabled]){cursor:not-allowed;opacity:.6}:host([readonly]){cursor:default}`
];
toggleCheck(ev) {
if (this.disabled || this.readOnly) {
return;
}
ev.stopPropagation();
this.value = !this.value;
let data = {
value: this.value
};
this.$emit("input");
this.$emit("change", data);
}
handleClick(ev) {
if (ev.type === "click" || ev.keyCode === 32) {
ev.preventDefault();
this.toggleCheck(ev);
}
}
mounted() {
}
render() {
let classes = classMap({ dot: true, open: this.value });
return html` <label
tabindex=${this.disabled || this.readOnly ? "none" : 0}
@click=${this.handleClick} @click=${this.handleClick}
@keydown=${this.handleClick} @keydown=${this.handleClick}
> >
<span <span
class=${classes} class=${t}
st=${this.inlineText ? this.value ? this.activeText : this.inactiveText : ""} st=${this.inlineText?this.value?this.activeText:this.inactiveText:""}
></span> ></span>
<slot <slot
>${!this.inlineText ? this.value ? this.activeText : this.inactiveText : ""}</slot >${this.inlineText?"":this.value?this.activeText:this.inactiveText}</slot
> >
</label>`; </label>`}}n.reg("switch");
}
}
Switch.reg("switch");

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
import{css as p,html as $,bind as a,unbind as x,Component as _}from"wkit";class z extends _{static props={axis:"xy",delay:1e3,distance:1};static styles=[p`:host{position:relative;display:block}:host .container{overflow:hidden;position:relative;width:100%;height:100%;max-height:inherit}:host .wrapper{overflow:auto;scrollbar-width:none;width:100%;height:100%;max-height:inherit;scrollbar-width:0}:host .wrapper::-webkit-scrollbar{display:none}:host .content{min-width:100%;width:fit-content;height:fit-content}`,p`.is-horizontal,.is-vertical{visibility:hidden;position:absolute;display:flex;justify-content:flex-end;opacity:0;user-select:none;transition:opacity .3s linear,visibility .3s linear}.is-horizontal .thumb,.is-vertical .thumb{display:block;border-radius:5px;background:rgba(44,47,53,.25);cursor:default;transition:width .1s linear,height .1s linear}.is-horizontal .thumb:hover,.is-vertical .thumb:hover{background:rgba(44,47,53,.5)}.is-horizontal{flex-direction:column;left:0;bottom:0;width:100%;height:10px}.is-horizontal .thumb{width:0;height:6px}.is-horizontal .thumb:hover{height:10px}.is-vertical{top:0;right:0;width:10px;height:100%}.is-vertical .thumb{width:6px;height:0}.is-vertical .thumb:hover{width:10px}`,p`:host(:hover) .is-horizontal,:host(:hover) .is-vertical{visibility:visible;opacity:1}:host([axis=x]) .wrapper{overflow-y:hidden}:host([axis=x]) .is-vertical{display:none}:host([axis=y]) .wrapper{overflow-x:hidden}:host([axis=y]) .is-horizontal{display:none}:host([disabled]) .wrapper{overflow:hidden}:host([disabled]) .is-vertical,:host([disabled]) .is-horizontal{display:none}`];stamp=0;cache={xBar:0,yBar:0,thumbX:0,thumbY:0};get scrollTop(){return this.$refs.box?.scrollTop||0}set scrollTop(t){t=+t,t===t&&(this.$refs.box.scrollTop=t)}get scrollLeft(){return this.$refs.box?.scrollLeft||0}set scrollLeft(t){t=+t,t===t&&(this.$refs.box.scrollLeft=t)}get scrollHeight(){return this.$refs.box?.scrollHeight}get scrollWidth(){return this.$refs.box?.scrollWidth}__init__(t){let e=this.offsetWidth,r=this.offsetHeight,s=this.scrollWidth,l=this.scrollHeight,h=50,i=50;h=r*(r/l)>>0,i=e*(e/s)>>0,h<50&&(h=50),i<50&&(i=50),i>=e&&(i=0),h>=r&&(h=0),this.cache.yBar=h,this.cache.xBar=i,i>0?(this.$refs.x.parentNode.style.display="flex",this.$refs.x.style.width=i+"px"):this.$refs.x.parentNode.style.display="none",h>0?(this.$refs.y.parentNode.style.display="flex",this.$refs.y.style.height=h+"px"):this.$refs.y.parentNode.style.display="none"}_fetchScrollX(t){let{xBar:e}=this.cache,{scrollWidth:r,offsetWidth:s}=this;return t<0?t=0:t>s-e&&(t=s-e),this.scrollLeft=(r-s)*(t/(s-e)),this.$refs.x.style.transform=`translateX(${t}px)`,t}_fetchScrollY(t){let{yBar:e}=this.cache,{scrollHeight:r,offsetHeight:s}=this;return t<0?t=0:t>s-e&&(t=s-e),this.scrollTop=(r-s)*(t/(s-e)),this.$refs.y.style.transform=`translateY(${t}px)`,t}_fireReachEnd(t="reach-bottom"){let e=this.delay,{scrollHeight:r,offsetHeight:s}=this,l=this.scrollTop,h=Date.now();if(h-this.stamp>e){if(t==="reach-bottom"){if(s+l<r)return}else if(l>0)return;this.stamp=h,this.$emit(t)}}mounted(){let t,e,r,s,l=i=>{let{thumbY:c,thumbX:n}=this.cache;t!==void 0&&(r=this._fetchScrollX(n+i.pageX-t)),e!==void 0&&(s=this._fetchScrollY(c+i.pageY-e))},h=i=>{Math.abs(i.pageY-e)>this.distance&&this._fireReachEnd(i.pageY>e?"reach-bottom":"reach-top"),t=void 0,e=void 0,this.cache.thumbX=r||0,this.cache.thumbY=s||0,delete this._active,x(document,"mousemove",l),x(document,"mouseup",h)};a(this.$refs.box,"scroll",i=>{if(i.stopPropagation(),this._active)return;let{xBar:c,yBar:n,thumbX:m,thumbY:f}=this.cache,{axis:d,scrollHeight:y,scrollWidth:g,offsetHeight:u,offsetWidth:b,scrollTop:v,scrollLeft:w}=this;if(!(c===0&&n===0)){if((d==="y"||d==="xy")&&n){let o=~~(v/(y-u)*(u-n));o!==f&&(this.cache.thumbY=o,this.$refs.y.style.transform=`translateY(${o}px)`,Math.abs(o-f)>this.distance&&this._fireReachEnd(o>f?"reach-bottom":"reach-top"))}if((d==="x"||d==="xy")&&c){let o=~~(w/(g-b)*(b-c));o!==m&&(this.cache.thumbX=o,this.$refs.x.style.transform=`translateX(${o}px)`)}this.$emit("scroll")}}),a(this.$refs.y,"mousedown",i=>{e=i.pageY,this._active=!0,a(document,"mousemove",l),a(document,"mouseup",h)}),a(this.$refs.x,"mousedown",i=>{t=i.pageX,this._active=!0,a(document,"mousemove",l),a(document,"mouseup",h)}),this.__observer=new ResizeObserver(this.__init__.bind(this)),this.__observer.observe(this.$refs.cont)}unmounted(){this.__observer?.disconnect()}render(){return $`
<div class="container">
<div class="wrapper" ref="box">
<div class="content" ref="cont"><slot></slot></div>
</div>
</div>
<div class="is-horizontal">
<span ref="x" class="thumb"></span>
</div>
<div class="is-vertical">
<span ref="y" class="thumb"></span>
</div>
`}}z.reg("scroll");

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,85 @@
const CONFIG_DIR = '/home/yutent/.config/hosts-switch'
const HOST_FILE = `${CONFIG_DIR}/host.cache`
const LOCK_FILE = `${CONFIG_DIR}/lock`
let timer
export function checkPermission() {
return writable('/etc/hosts')
}
export async function getHistory() {
if (await native.fs.isfile(LOCK_FILE)) {
var cache = await native.fs.read(HOST_FILE)
return JSON.parse(cache)
}
var cache = native.fs.read('/etc/hosts').toString()
var records = cache.split(/[\n\r]+/)
var list = []
var dict = {}
records.forEach(str => {
str = str.trim()
let matches = str.match(/^(#*?)\s*(\d+\.\d+\.\d+\.\d+)\s+(.*)/)
if (matches) {
let names = matches[3].split(/\s+/).map(it => it.trim())
let name
while ((name = names.pop())) {
list.push({ ip: matches[2], enabled: !matches[1], name })
}
}
})
records = null
list.forEach(it => {
it.name = it.name.split('.')
let domain = it.name.splice(-2, 2).join('.')
if (domain === 'com.cn' || domain === 'org.cn' || domain === 'net.cn') {
domain = it.name.pop() + '.' + domain
}
if (dict[domain]) {
dict[domain].push({
value: it.ip,
enabled: it.enabled,
record: it.name.join('.') || '@',
remark: ''
})
} else {
dict[domain] = [
{
value: it.ip,
enabled: it.enabled,
record: it.name.join('.') || '@',
remark: ''
}
]
}
})
list = null
native.fs.write(HOST_FILE, JSON.stringify(dict))
native.fs.write(LOCK_FILE, '')
return dict
}
export function saveHosts(dict) {
clearTimeout(timer)
timer = setTimeout(() => {
var txt = ''
for (let k in dict) {
for (let it of dict[k]) {
if (it.enabled) {
var name = it.record === '@' ? '' : it.record
if (name) {
name += '.'
}
txt += `${it.value.padEnd(15, ' ')} ${name + k}\n`
}
}
txt += '\n'
}
native.fs.write(HOST_FILE, JSON.stringify(dict))
native.fs.write('/etc/hosts', txt)
}, 1000)
}

View File

@ -7,7 +7,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
class Window(Gtk.Window): class Window(Gtk.Window):
def __init__(self, title = 'Untitled window', width = 760, height = 480): def __init__(self, title = 'Untitled window', width = 840, height = 520):
Gtk.Window.__init__(self, title = title) Gtk.Window.__init__(self, title = title)
self.set_default_size(width, height) self.set_default_size(width, height)
self.resize(width, height) self.resize(width, height)