init
parent
86e1baf89a
commit
ab165c0002
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/07/20 14:19:13
|
||||
*/
|
||||
|
||||
import 'es.shim'
|
||||
import { html, css, Component } from 'wkit'
|
||||
import { createApp, createRouter } from 'wkitd'
|
||||
|
||||
import store from './store.js'
|
||||
import './components/home.js'
|
||||
|
||||
import { noop } from './utils/index.js'
|
||||
|
||||
createApp({
|
||||
data: {
|
||||
input: '',
|
||||
img: '',
|
||||
content: ''
|
||||
},
|
||||
styles: [
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
`
|
||||
],
|
||||
methods: {
|
||||
quit() {
|
||||
native.quit()
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return html` <wc-home @contextmenu.prevent=${noop}></wc-home> `
|
||||
}
|
||||
})
|
||||
.use(store)
|
||||
.mount()
|
|
@ -0,0 +1,262 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/08/08 18:19:17
|
||||
*/
|
||||
import { html, css, Component, classMap, nextTick, outsideClick } from 'wkit'
|
||||
|
||||
import 'ui/icon/index.js'
|
||||
import 'ui/scroll/index.js'
|
||||
import 'ui/layer/index.js'
|
||||
import 'ui/space/index.js'
|
||||
import 'ui/form/input.js'
|
||||
import 'ui/form/switch.js'
|
||||
import 'ui/form/button.js'
|
||||
import 'ui/form/link.js'
|
||||
|
||||
import './sidebar.js'
|
||||
import './permission.js'
|
||||
import './records.js'
|
||||
|
||||
import { checkPermission, getHistory, saveHosts, noop } from '../utils/index.js'
|
||||
|
||||
const HOST_DATA = await getHistory()
|
||||
|
||||
class Home extends Component {
|
||||
static props = {
|
||||
editDomain: '', // 当前临时要编辑的域名, 即右键菜单选择到的
|
||||
permissionShow: false
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 32px;
|
||||
color: var(--color-dark-1);
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
`,
|
||||
|
||||
css`
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.main .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);
|
||||
}
|
||||
.main .list {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
}
|
||||
`,
|
||||
|
||||
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;
|
||||
}
|
||||
`
|
||||
]
|
||||
|
||||
async mounted() {
|
||||
let writable = await checkPermission()
|
||||
|
||||
if (writable) {
|
||||
this.$store.HOST_DATA = HOST_DATA
|
||||
this.$store.domains = Object.keys(HOST_DATA)
|
||||
this.$refs.domain.mounted()
|
||||
outsideClick(this.$refs.context, _ => this.$refs.context.close())
|
||||
} else {
|
||||
this.permissionShow = true
|
||||
}
|
||||
}
|
||||
|
||||
addRecord() {
|
||||
if (this.$store.activeDomain) {
|
||||
this.$store.records.push({
|
||||
record: '',
|
||||
value: '',
|
||||
enabled: true,
|
||||
remark: ''
|
||||
})
|
||||
nextTick(_ => (this.$refs.list.scrollTop = 1e6))
|
||||
} else {
|
||||
layer.toast('请先选择域名', 'warn')
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
setTimeout(_ => {
|
||||
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') {
|
||||
let act = ev.target.dataset.act
|
||||
let { HOST_DATA, records, domains } = this.$store
|
||||
let idx = domains.indexOf(this.editDomain)
|
||||
|
||||
if (act === 'del') {
|
||||
layer
|
||||
.confirm(`是否要删除域名「${this.editDomain}」?`)
|
||||
.then(res => {
|
||||
if (this.editDomain === this.$store.activeDomain) {
|
||||
if (records.length) {
|
||||
return layer.toast(
|
||||
'该域名下有主机记录, 请先删除主机记录后再删除域名',
|
||||
'error'
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (HOST_DATA[this.editDomain].length > 0) {
|
||||
return layer.toast(
|
||||
'该域名下有主机记录, 请先删除主机记录后再删除域名',
|
||||
'error'
|
||||
)
|
||||
}
|
||||
}
|
||||
delete HOST_DATA[this.editDomain]
|
||||
|
||||
domains.splice(idx, 1)
|
||||
|
||||
this.editDomain = ''
|
||||
this.$store.records = []
|
||||
this.$store.activeDomain = domains[0]
|
||||
this.$refs.domain.mounted()
|
||||
this.save()
|
||||
})
|
||||
.catch(noop)
|
||||
} else if (act === 'edit') {
|
||||
layer
|
||||
.prompt(
|
||||
`请输入新的名字「${this.editDomain}」`,
|
||||
this.editDomain,
|
||||
(val, done) => {
|
||||
if (val === this.editDomain || HOST_DATA[val]) {
|
||||
return layer.toast(`${val} 域名没有变化, 或已经存在`)
|
||||
}
|
||||
|
||||
if (
|
||||
val === 'localhost' ||
|
||||
val === 'local' ||
|
||||
/^[\w.]+\.[a-z]+$/.test(val)
|
||||
) {
|
||||
done()
|
||||
} else {
|
||||
layer.toast('域名格式错误', 'error')
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(val => {
|
||||
domains[idx] = val
|
||||
HOST_DATA[val] = HOST_DATA[this.editDomain]
|
||||
delete HOST_DATA[this.editDomain]
|
||||
|
||||
this.$store.activeDomain = val
|
||||
this.editDomain = ''
|
||||
this.$refs.domain.mounted()
|
||||
this.save()
|
||||
})
|
||||
.catch(noop)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
let { HOST_DATA, activeDomain, records } = this.$store
|
||||
if (activeDomain) {
|
||||
HOST_DATA[activeDomain] = records.filter(it => it.record && it.value)
|
||||
}
|
||||
saveHosts(HOST_DATA)
|
||||
layer.toast('保存成功', 'success')
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<wc-sidebar
|
||||
ref="domain"
|
||||
@toggle-domain=${_ => (this.$refs.list.scrollTop = 0)}
|
||||
@show-menu=${ev => this.showMenu(ev.event)}
|
||||
@save=${this.save}
|
||||
></wc-sidebar>
|
||||
|
||||
<main class="main noselect">
|
||||
<header class="toolbar">
|
||||
<wc-button size="m" icon="plus" @click=${this.addRecord}
|
||||
>新增记录</wc-button
|
||||
>
|
||||
<wc-button size="m" icon="fly" @click=${this.save}>保存</wc-button>
|
||||
</header>
|
||||
|
||||
<wc-records class="list" ref="list"></wc-records>
|
||||
</main>
|
||||
|
||||
<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>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
Home.reg('home')
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/08/08 18:19:17
|
||||
*/
|
||||
import { html, css, Component } from 'wkit'
|
||||
|
||||
import { checkPermission } from '../utils/index.js'
|
||||
|
||||
const tips_header = `/************************************************************/
|
||||
* hosts文件没有写权限 *
|
||||
/************************************************************/
|
||||
`
|
||||
|
||||
class Permission extends Component {
|
||||
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;
|
||||
}
|
||||
`
|
||||
|
||||
async check() {
|
||||
let writable = await checkPermission()
|
||||
if (writable) {
|
||||
location.reload()
|
||||
} else {
|
||||
layer.toast('hosts文件没有写权限, 请按提示修改', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
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')
|
|
@ -0,0 +1,158 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/08/08 18:19:17
|
||||
*/
|
||||
import { html, css, Component, classMap, nextTick } from 'wkit'
|
||||
|
||||
import { noop } from '../utils/index.js'
|
||||
|
||||
class Records extends Component {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.thead {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.records {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
.thead {
|
||||
line-height: 40px;
|
||||
}
|
||||
.item span {
|
||||
flex: 1;
|
||||
}
|
||||
.item .long {
|
||||
flex: 1.5;
|
||||
}
|
||||
|
||||
.item wc-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
--wc-input-border-color: transparent;
|
||||
}
|
||||
.item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
`
|
||||
|
||||
set scrollTop(val) {
|
||||
this.$refs.records.scrollTop = val
|
||||
}
|
||||
|
||||
// 同一个记录, 允许一条被激活
|
||||
recordChanges(ev, item) {
|
||||
let { tmp_records } = this.$store
|
||||
|
||||
item.enabled = ev.target.value
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateCacheDict(ev, item) {
|
||||
let { records } = this.$store
|
||||
let tmp_records = Object.create(null)
|
||||
|
||||
item.record = ev.target.value
|
||||
|
||||
for (let it of records) {
|
||||
if (tmp_records[it.record]) {
|
||||
tmp_records[it.record].push(it)
|
||||
} else {
|
||||
tmp_records[it.record] = [it]
|
||||
}
|
||||
}
|
||||
this.$store.tmp_records = tmp_records
|
||||
}
|
||||
|
||||
clone(item) {
|
||||
let { tmp_records, records } = this.$store
|
||||
var params = { ...item }
|
||||
params.enabled = false
|
||||
|
||||
records.push(params)
|
||||
tmp_records[params.record].push(records.at(-1))
|
||||
|
||||
nextTick(_ => (this.$refs.records.scrollTop = 1e6))
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<header class="thead item">
|
||||
<span>主机记录</span>
|
||||
<span>类型</span>
|
||||
<span class="long">记录值</span>
|
||||
<span>操作</span>
|
||||
<span>备注</span>
|
||||
</header>
|
||||
<wc-scroll class="records" ref="records">
|
||||
${this.$store.records.map(
|
||||
(it, i) => html`
|
||||
<div class="item">
|
||||
<wc-input
|
||||
size="m"
|
||||
autofocus
|
||||
@change=${ev => this.updateCacheDict(ev, it)}
|
||||
:value=${it.record}
|
||||
label="根域请填 @"
|
||||
></wc-input>
|
||||
<span>A</span>
|
||||
<wc-input
|
||||
size="m"
|
||||
class="long"
|
||||
:value=${it.value}
|
||||
@change=${ev => (it.value = ev.target.value)}
|
||||
label="请填写IP"
|
||||
></wc-input>
|
||||
<wc-space gap="s">
|
||||
<wc-link
|
||||
size="m"
|
||||
type="danger"
|
||||
@click=${ev => this.$store.records.splice(i, 1)}
|
||||
>删除</wc-link
|
||||
>
|
||||
<wc-link size="m" type="info" @click=${ev => this.clone(it)}
|
||||
>克隆</wc-link
|
||||
>
|
||||
<wc-switch
|
||||
size="m"
|
||||
:value=${it.enabled}
|
||||
@change=${ev => this.recordChanges(ev, it)}
|
||||
></wc-switch>
|
||||
</wc-space>
|
||||
<wc-input size="m" no-border :value=${it.remark}></wc-input>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</wc-scroll>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
Records.reg('records')
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/08/08 18:19:17
|
||||
*/
|
||||
import { html, css, Component, classMap, nextTick } from 'wkit'
|
||||
|
||||
import { noop } from '../utils/index.js'
|
||||
|
||||
class Sidebar extends Component {
|
||||
static styles = css`
|
||||
:host {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 180px;
|
||||
height: 100vh;
|
||||
background: var(--color-plain-2);
|
||||
}
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.domain-list {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.domain-list .item {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.item wc-icon {
|
||||
--wc-icon-size: 12px;
|
||||
margin-left: 8px;
|
||||
color: var(--color-grey-2);
|
||||
}
|
||||
|
||||
.item:hover,
|
||||
.item.active {
|
||||
background: #fff;
|
||||
}
|
||||
.item.active {
|
||||
border-right: 2px solid var(--color-orange-1);
|
||||
color: var(--color-orange-1);
|
||||
}
|
||||
.item.active wc-icon {
|
||||
color: var(--color-orange-1);
|
||||
}
|
||||
.item.blank {
|
||||
justify-content: center;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.item.blank:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.action {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
`
|
||||
|
||||
addDomain() {
|
||||
layer
|
||||
.prompt('请输入根域名', function (val, done) {
|
||||
if (
|
||||
val === 'localhost' ||
|
||||
val === 'local' ||
|
||||
/^[\w.\-]+\.[a-z]+$/.test(val)
|
||||
) {
|
||||
done()
|
||||
} else {
|
||||
layer.toast('域名格式错误', 'error')
|
||||
}
|
||||
})
|
||||
.then(val => {
|
||||
let { HOST_DATA, records } = this.$store
|
||||
this.$store.domains.push(val)
|
||||
HOST_DATA[val] = []
|
||||
if (!this.$store.activeDomain) {
|
||||
this.toggleDomain(null, val)
|
||||
}
|
||||
this.$emit('save')
|
||||
})
|
||||
.catch(noop)
|
||||
}
|
||||
|
||||
toggleDomain(ev, name) {
|
||||
let { HOST_DATA, records } = this.$store
|
||||
name = name ?? ev.currentTarget.dataset.name
|
||||
this.$store.activeDomain = name
|
||||
|
||||
this.$store.records = records = (HOST_DATA[name] || []).sort((a, b) =>
|
||||
a.record.localeCompare(b.record)
|
||||
)
|
||||
|
||||
let tmp_records = Object.create(null)
|
||||
for (let it of records) {
|
||||
if (tmp_records[it.record]) {
|
||||
tmp_records[it.record].push(it)
|
||||
} else {
|
||||
tmp_records[it.record] = [it]
|
||||
}
|
||||
}
|
||||
this.$store.tmp_records = tmp_records
|
||||
|
||||
document.title = `伪域名解析 ${name} `
|
||||
localStorage.setItem('last_domain', name)
|
||||
nextTick(() => {
|
||||
this.$emit('toggle-domain')
|
||||
})
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.toggleDomain(null, this.$store.activeDomain)
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<wc-scroll class="domain-list noselect">
|
||||
<ul
|
||||
@contextmenu.prevent=${ev => this.$emit('show-menu', { event: ev })}
|
||||
>
|
||||
${this.$store.domains.map(
|
||||
it => html`
|
||||
<li
|
||||
class=${classMap({
|
||||
item: true,
|
||||
active: it === this.$store.activeDomain
|
||||
})}
|
||||
data-name=${it}
|
||||
@click=${this.toggleDomain}
|
||||
>
|
||||
<span>${it}</span>
|
||||
<wc-icon name="right"></wc-icon>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
${this.$store.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>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
Sidebar.reg('sidebar')
|
|
@ -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;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;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
|
||||
<link rel="stylesheet" href="/css/reset.css">
|
||||
<style>
|
||||
html,body {
|
||||
width:100%;
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
font-size: 14px;
|
||||
cursor:default;
|
||||
}
|
||||
a {color:inherit;text-decoration: none;}
|
||||
</style>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports":{
|
||||
"es.shim":"app:///lib/es.shim.js",
|
||||
"wkit":"app:///lib/wkit.js",
|
||||
"wkitd":"app:///lib/wkitd.js",
|
||||
"fetch":"app:///lib/fetch.js",
|
||||
"crypto":"app:///lib/crypto.js",
|
||||
"ui/":"app:///lib/ui/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<wc-app></wc-app>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,16 @@
|
|||
import { css, html, Component } from "wkit";
|
||||
class Card extends Component {
|
||||
static props = {
|
||||
header: ""
|
||||
};
|
||||
static styles = css`:host{display:flex;border-radius:3px}.card-box{display:flex;flex-direction:column;position:relative;width:100%;border:1px solid var(--color-plain-2);border-radius:inherit;background:#fff;color:var(--color-dark-1);transition:box-shadow .2s ease-in-out;box-shadow:0 0 12px rgba(0,0,0,.12)}.card-box .header{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:52px;padding:var(--card-padding, 8px 16px);border-bottom:1px solid var(--color-plain-2);font-size:16px;user-select:none}.card-box .content{flex:1;min-height:64px;padding:var(--card-padding, 8px 16px);font-size:14px;color:var(--color-dark-1)}:host([shadow=never]) .card-box,:host([shadow=hover]) .card-box{box-shadow:none}:host([shadow=hover]:hover) .card-box{box-shadow:0 0 12px rgba(0,0,0,.12)}`;
|
||||
render() {
|
||||
return html`
|
||||
<div class="card-box">
|
||||
<div class="header"><slot name="header">${this.header}</slot></div>
|
||||
<div class="content"><slot></slot></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
Card.reg("card");
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,35 @@
|
|||
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`
|
||||
<div class="label">
|
||||
<slot name="prepend">
|
||||
<wc-icon class="icon" name=${this.icon}></wc-icon>
|
||||
</slot>
|
||||
<input
|
||||
ref="input"
|
||||
@input=${this.handleInput}
|
||||
@change=${this.handleChange}
|
||||
@keydown=${this.handleKeyDown}
|
||||
@focus=${this.handleFocus}
|
||||
placeholder=${this.placeholder}
|
||||
maxlength=${this.maxlength}
|
||||
minlength=${this.minlength}
|
||||
disabled=${this.disabled}
|
||||
readonly=${this.readOnly}
|
||||
autofocus=${this.autofocus}
|
||||
:value=${this.value}
|
||||
/>
|
||||
${this.clearable&&this.value?this.renderClear():""}
|
||||
<slot name="append"></slot>
|
||||
<div class=${e} ref="suggestion" #animation=${d}>
|
||||
<div ref="scroller" class="scroller">
|
||||
<ul class="list" @click=${this.handleClickItem} ref="list">
|
||||
${this.#t.map((t,i)=>s`<li
|
||||
focus=${this.#e===i?!0:null}
|
||||
index=${i}
|
||||
>
|
||||
${t.value}
|
||||
</li>`)}
|
||||
</ul>
|
||||
</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");
|
|
@ -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");
|
|
@ -0,0 +1,13 @@
|
|||
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
|
||||
tabindex=${this.disabled||this.readOnly?"none":0}
|
||||
@click=${this.handleClick}
|
||||
@keydown=${this.handleClick}
|
||||
>
|
||||
<span
|
||||
class=${t}
|
||||
st=${this.inlineText?this.value?this.activeText:this.inactiveText:""}
|
||||
></span>
|
||||
<slot
|
||||
>${this.inlineText?"":this.value?this.activeText:this.inactiveText}</slot
|
||||
>
|
||||
</label>`}}n.reg("switch");
|
|
@ -0,0 +1,31 @@
|
|||
import { css, svg, html, Component } from "wkit";
|
||||
import SVG_DICT from "./svg.js";
|
||||
let dict = SVG_DICT;
|
||||
if (window.EXT_SVG_DICT) {
|
||||
Object.assign(dict, EXT_SVG_DICT);
|
||||
}
|
||||
class Icon extends Component {
|
||||
static props = {
|
||||
name: {
|
||||
type: String,
|
||||
default: null,
|
||||
observer(val) {
|
||||
if (val === "") {
|
||||
this.name = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
static styles = css`:host{display:inline-flex;width:var(--wc-icon-size, 32px);height:var(--wc-icon-size, 32px)}:host(:not([name])){display:none}.icon{display:block;width:100%;height:100%;color:inherit;fill:currentColor}.icon.loading{animation:load 1.5s linear infinite}.icon circle{stroke:currentColor;animation:circle 1.5s ease-in-out infinite}:host([size=s]){width:20px;height:20px}:host([size=m]){width:24px;height:24px}:host([size=l]){width:32px;height:32px}:host([size=xl]){width:36px;height:36px}:host([size=xxl]){width:44px;height:44px}@keyframes circle{0%{stroke-dasharray:0,3812px;stroke-dashoffset:0}50%{stroke-dasharray:1906px,3812px;stroke-dashoffset:-287px}100%{stroke-dasharray:1906px,3812px;stroke-dashoffset:-2393px}}@keyframes load{to{transform:rotate(360deg)}}`;
|
||||
render() {
|
||||
return html`
|
||||
<svg
|
||||
class="icon ${this.name === "loading" ? "loading" : ""}"
|
||||
viewBox="0 0 1024 1024"
|
||||
>
|
||||
${this.name === "loading" ? svg`<circle class="circle" cx="512" cy="512" r="384" fill="none" stroke-width="80" />` : svg`<path d="${dict[this.name]}" />`}
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
}
|
||||
Icon.reg("icon");
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,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");
|
|
@ -0,0 +1,8 @@
|
|||
import { css, html, Component } from "wkit";
|
||||
class Space extends Component {
|
||||
static styles = css`:host{display:block}.container{display:flex;flex-wrap:wrap;align-items:center;width:100%;padding:6px 0;gap:12px}:host([vertical]) .container{flex-direction:column}:host([justify]) .container{justify-content:space-between}:host([gap=s]) .container{padding:2px 0;gap:4px}:host([gap=m]) .container{padding:4px 0;gap:8px}:host([gap=l]) .container{padding:6px 0;gap:12px}:host([gap=xl]) .container{padding:8px 0;gap:16px}:host([gap=xxl]) .container{padding:10px 0;gap:20px}:host([gap=xxxl]) .container{padding:12px 0;gap:24px}`;
|
||||
render() {
|
||||
return html`<div class="container"><slot /></div>`;
|
||||
}
|
||||
}
|
||||
Space.reg("space");
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,55 @@
|
|||
var ye=Object.defineProperty;var ge=(r,e,t)=>e in r?ye(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var w=(r,e,t)=>(ge(r,typeof e!="symbol"?e+"":e,t),t),F=(r,e,t)=>{if(!e.has(r))throw TypeError("Cannot "+t)};var n=(r,e,t)=>(F(r,e,"read from private field"),t?t.call(r):e.get(r)),p=(r,e,t)=>{if(e.has(r))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(r):e.set(r,t)},u=(r,e,t,o)=>(F(r,e,"write to private field"),o?o.call(r,t):e.set(r,t),t);var c=(r,e,t)=>(F(r,e,"access private method"),t);import"wkit";var d=Symbol("router"),y=Symbol("router-view"),C=Symbol("store"),M=new Set,k=new Map,R=new Set;var J=class extends WeakMap{broadcast(){for(let e of M){if(e.removed){this.deassign(e);continue}e.$requestUpdate()}}assign(e){M.add(e)}deassign(e){M.delete(e)}},_e=new J;Object.defineProperty(window,"wkitd",{get(){return _e},set(r){console.error("Can not set readonly property wkitd of window")},enumerable:!1});import{html as me,css as Se,Component as we}from"wkit";var $e=encodeURIComponent,oe=decodeURIComponent;function g(){}function ie(r,e,t){Object.defineProperty(r,e,{get(){return t}})}function V(r,e,t){var o;if(Array.isArray(e))e.forEach(function(i,s){o=r?`${r}[${Array.isArray(i)?s:""}]`:s,typeof i=="object"?V(o,i,t):t(o,i)});else for(let i in e)o=r?`${r}[${i}]`:i,typeof e[i]=="object"?V(o,e[i],t):t(o,e[i])}function Y(r=""){let e=new URLSearchParams(r),t=Object.create(null);for(let[o,i]of e.entries()){let s=oe(o),a=oe(i),h=0;if(/(\w+)\[(\w*?)\]/.test(s)){let S=RegExp.$1,m=RegExp.$2;s=S,!m||+m==+m?(a=[a],h|=2):(h|=1,a={[m]:a})}t[s]?h&2?t[s]=t[s].concat(a):h&1?Object.assign(t[s],a):(Array.isArray(t[a])||(t[s]=[t[s]]),t[s].push(a)):t[s]=a}return t}function T(r={}){if(r===null)return"";if(typeof r=="string"||typeof r=="number"||typeof r=="boolean")return r;let e=[];return typeof r=="object"&&V("",r,function(o,i){e.push(o+"="+$e(i))}),e.join("&")}import{Component as de}from"wkit";import{bind as Ee}from"wkit";var xe=/^(#!|#)[\/]+?/,Re=/(\/[^/]*)(:[\$@~\\!A-Za-z0-9_=\-]+)(\?)?/g,B="hash",Oe="history",_,U,$,f,E,O,v,j,N,se,P,Q,I,ee,x,q,W,te,D=class{constructor(e=B){p(this,v);p(this,N);p(this,P);p(this,I);p(this,x);p(this,W);w(this,"type",B);p(this,_,new Map);p(this,U,new Set);p(this,$,!1);p(this,f,Object.create(null));p(this,E,null);p(this,O,void 0);this.type=e,Ee(window,"popstate",c(this,v,j).bind(this))}get route(){return n(this,f)}get views(){return Array.from(n(this,U))}init(){u(this,$,!0),c(this,v,j).call(this)}beforeEach(e=g){u(this,O,e)}addRoute(e){Array.isArray(e)?e.forEach(t=>{c(this,P,Q).call(this,t)}):c(this,P,Q).call(this,e),n(this,$)&&c(this,v,j).call(this)}go(e=0){history.go(e)}back(){this.go(-1)}forward(){this.go(1)}push(e={path:"",query:{}},t=!1){let o="",i="";typeof e=="string"?o=e.trim():(i=T(e.query||""),o=e.path+(i?`?${i}`:"")),!(!o&&o===location.hash.slice(1))&&(u(this,E,e),this.type===B?t?location.replace(o.replace(/^\//,"#/")):location.hash=o:(t?window.history.replaceState({path:o},null,o+i):window.history.pushState({path:o},null,o+i),c(this,I,ee).call(this)))}replace(e={path:"",query:{}}){this.push(e,!0)}};_=new WeakMap,U=new WeakMap,$=new WeakMap,f=new WeakMap,E=new WeakMap,O=new WeakMap,v=new WeakSet,j=function(e){n(this,$)&&c(this,I,ee).call(this)},N=new WeakSet,se=function(e){if(e.path==="!")e.regexp=null;else{let t=[],o;if(e.path.includes("?")&&e.path.at(-1)!=="?")throw new SyntaxError(`The exp "?" can only be used in the last.
|
||||
|
||||
${JSON.stringify(e)}
|
||||
`);o=e.path.replace(Re,function(i,s,a,h=""){return t.push(a.slice(1)),s==="/"&&(s="/?"),s+"([\\$\\!@~A-Za-z0-9_=\\-]+)"+h}),o="^"+o+"$",e.regexp=new RegExp(o),e.vars=t}return e},P=new WeakSet,Q=function(e){if(e.path!=="!"&&e.path[0]!=="/"){console.error('route path must start with "/"');return}e.path=e.path.replace(/^[\/]+|[\/]+$|\s+/g,"/"),n(this,_).set(e.path,c(this,N,se).call(this,e)),n(this,U).add(e.name)},I=new WeakSet,ee=function(){let e=this.type===B,t=window.wkitd.get(y),o=location.hash||"#/",i=e?o:location.href.replace(location.origin,"").replace(o,""),s="";if(i.includes("?")&&([i,s]=i.split("?")),i=i.replace(xe,"/"),!t||i===n(this,f).path)return s!==T(n(this,f).query)?(n(this,f).query=Y(s),c(this,W,te).call(this)):void 0;for(let[a,h]of n(this,_)){let S=i.match(h.regexp);if(S){let m=Object.create(null);for(let K=1;K<S.length;K++)m[[h.vars[K-1]]]=S[K];let Z={path:i,name:h.name,params:m,query:Y(s)};return n(this,O)?n(this,O).call(this,this.route,Z,()=>{c(this,x,q).call(this,Z)}):c(this,x,q).call(this,Z)}}if(n(this,_).get("!")){let a=n(this,_).get("!");t.current=a.name,u(this,f,{path:i,name:a.name,params:{},query:{}}),c(this,x,q).call(this,n(this,f))}else n(this,E)&&(c(this,x,q).call(this,n(this,E)),u(this,E,null))},x=new WeakSet,q=function(e){let t=window.wkitd.get(y);t.current=e.name,u(this,f,e),c(this,W,te).call(this)},W=new WeakSet,te=function(){if(n(this,$))for(let e of R)e(this.route)};function ne(){return()=>new D}function ve(){return()=>new D(Oe)}import{Component as he,html as Ae,css as le,raw as ce}from"wkit";import{Component as ae}from"wkit";function pe(r,e=["$store"]){return r===null?r:new Proxy(r,{get(t,o,i){let s=Reflect.get(t,o,i);return typeof s=="object"?pe(s,e.concat(o)):s},set(t,o,i,s){let a=e.concat(o).join(".");return t[o]===i||(Reflect.set(t,o,i,s),k.get(a)&&k.get(a).forEach(h=>{h(i)}),window.wkitd.broadcast()),!0}})}function re(r,e=g){if(r.startsWith("$store.")){let t=k.get(r);t?t.add(e):(t=new Set,t.add(e),k.set(r,t))}else if(r.startsWith("$route"))R.add(e),e(window.wkitd.get(d).route);else return console.error("watch() only work on $store and $route")}function be(r={}){let e=!1;return function(){Object.defineProperty(ae.prototype,"$store",{get(){return window.wkitd.assign(this),window.wkitd.get(C)},set(t){if(e)return console.error("Can not set readonly property $store of Component");window.wkitd.set(C,pe(t)),e=!0}}),ae.prototype.$store=r}}var b,X,L=class extends he{constructor(){super(...arguments);p(this,b,"");p(this,X,[])}get current(){return n(this,b)}set current(t){let o=n(this,b);u(this,b,t),this.keepAlive?(o?this.$refs[o]?(this.$refs[o].removed=!0,this.$refs[o].deactivated(),this.$refs[o].remove()):this.$requestUpdate():this.$requestUpdate(),t&&(this.$refs[t]?(this.root.appendChild(this.$refs[t]),this.$refs[t].$requestUpdate(),this.transition&&this.$refs[t].$animate(),this.$refs[t].removed=!1,this.$refs[t].activated()):this.$requestUpdate())):this.$requestUpdate()}created(){window.wkitd.set(y,this)}sync(t){u(this,X,t)}render(){let t={immediate:!0,custom:[{transform:"translateX(-32px)",opacity:0},{transform:"translateX(0)",opacity:1}]};if(this.current)return this.transition?ce(`<${this.current} ref="${this.current}" ${this.keepAlive?"keep-alive":""} #animation="%s"></${this.current}>`,[t]):ce(`<${this.current} ref="${this.current}" ${this.keepAlive?"keep-alive":""}></${this.current}>`)}};b=new WeakMap,X=new WeakMap,w(L,"props",{transition:!1}),w(L,"styles",le`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
`);var A,z,ue,G,fe,H=class extends he{constructor(){super(...arguments);p(this,z);p(this,G);p(this,A,"")}activated(){this.mounted()}deactivated(){this.unmounted()}mounted(){re("$route",t=>{this.removed||this.classList.toggle("active",t.path===this.to.path)})}unmounted(){R.delete(this)}render(){return u(this,A,c(this,G,fe).call(this)),Ae`<a title=${n(this,A)} @click=${c(this,z,ue)}
|
||||
><slot></slot
|
||||
></a>`}};A=new WeakMap,z=new WeakSet,ue=function(){let t=this.$router.type,{path:o}=this.to;this.disabled||(t==="hash"?location.hash=n(this,A):this.$router.push(this.to))},G=new WeakSet,fe=function(){let t=this.$router.type,{path:o="",query:i={}}=this.to,s=typeof i=="string"?i.replaceAll("?",""):T(i);return o="/"+o.replace(/^\/+/,""),this.to={path:o,query:i},s&&(o+="?"+s),o},w(H,"props",{to:Object,disabled:!1}),w(H,"styles",le`
|
||||
:host {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--router-link-gap, 0);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:host([disabled]) a {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`);customElements.get("router-view")||customElements.define("router-view",L);customElements.get("router-link")||customElements.define("router-link",H);function et({history:r=ne(),routes:e=[]}={}){let t=r();window.wkitd.set(d,t),t.addRoute(e);function o(){Object.defineProperty(de.prototype,"$router",{get(){return t}}),Object.defineProperty(de.prototype,"$route",{get(){return t.route}})}return o.beforeEach=t.beforeEach.bind(t),o}var l=class extends we{};function nt({data:r={},styles:e=[],methods:t={},mounted:o=g,render:i}={}){return new function(){l.props=r,l.styles=e,Object.assign(l.prototype,t,{mounted:o,created(){ie(we.prototype,"$app",this)}}),this.use=function(s=g,...a){return s.apply(l.prototype,a),this},this.mount=function(){let s=window.wkitd.get(d);i?l.prototype.render=i:s?l.prototype.render=function(){return me`<router-view></router-view>`}:(l.styles=Se`
|
||||
:host {
|
||||
font-family: monospace;
|
||||
color: #647889;
|
||||
}
|
||||
.code {
|
||||
margin: 16px 0;
|
||||
background: #f7f8fb;
|
||||
}
|
||||
`,l.prototype.render=function(){return me`
|
||||
<h1>It works!!!</h1>
|
||||
<cite>
|
||||
If you don't use router, you may define the
|
||||
<b>render</b> property.
|
||||
</cite>
|
||||
<div class="code">
|
||||
<pre><code> createApp({</code></pre>
|
||||
<pre><code> render() {</code></pre>
|
||||
<pre><code> return html\`<wc-home></wc-home>\`</code></pre>
|
||||
<pre><code> }</code></pre>
|
||||
<pre><code> })</code></pre>
|
||||
<pre><code> .mount()</code></pre>
|
||||
</div>
|
||||
`}),s&&(l.prototype.mounted=function(...a){let h=window.wkitd.get(y);if(h)h.sync(s.views),s.init(),o.call(this,...a);else throw new Error('<router-view /> not found, "Router" works Unexpected.')}),l.reg("app")}}}function at(){return window.wkitd.get(C)}function pt(){return window.wkitd.get(d)}function ct(){var r;return(r=window.wkitd.get(d))==null?void 0:r.route}export{nt as createApp,et as createRouter,be as createStore,ne as createWebHashHistory,ve as createWebHistory,ct as getCurrentPage,pt as getRouter,at as getStore,re as watch};
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/09/04 11:11:51
|
||||
*/
|
||||
|
||||
import { createStore } from 'wkitd'
|
||||
|
||||
export default createStore({
|
||||
HOST_DATA: {},
|
||||
activeDomain: localStorage.getItem('last_domain') || '', //当前选中的域名
|
||||
domains: [],
|
||||
records: [],
|
||||
tmp_records: []
|
||||
})
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/09/04 19:18:28
|
||||
*/
|
||||
|
||||
import { nextTick } from 'wkit'
|
||||
|
||||
const APP_CONFIG_DIR = `${native.env.CONFIG_DIR}/hosts-switch`
|
||||
const HOST_FILE = `${APP_CONFIG_DIR}/host.cache`
|
||||
const LOCK_FILE = `${APP_CONFIG_DIR}/lock`
|
||||
|
||||
export function noop() {}
|
||||
|
||||
export function checkPermission() {
|
||||
return native.fs.access('/etc/hosts', 'a+')
|
||||
}
|
||||
|
||||
export async function getHistory() {
|
||||
if (await native.fs.isfile(LOCK_FILE)) {
|
||||
let cache = await native.fs.read(HOST_FILE)
|
||||
return JSON.parse(cache)
|
||||
}
|
||||
|
||||
let cache = await native.fs.read('/etc/hosts')
|
||||
let records = cache.split(/[\n\r]+/)
|
||||
let list = []
|
||||
let 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
|
||||
try {
|
||||
await native.fs.write(HOST_FILE, JSON.stringify(dict))
|
||||
await native.fs.write(LOCK_FILE, '')
|
||||
} catch (err) {}
|
||||
return dict
|
||||
}
|
||||
|
||||
export function saveHosts(dict) {
|
||||
nextTick(async () => {
|
||||
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'
|
||||
}
|
||||
|
||||
try {
|
||||
await native.fs.write(HOST_FILE, JSON.stringify(dict))
|
||||
await native.fs.write('/etc/hosts', txt)
|
||||
} catch (err) {
|
||||
layer.alert(err)
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue