feat: add js base build function

v3
veypi 1 month ago
parent 3cadef35be
commit 7a2ef864a3

@ -7,12 +7,21 @@
@import "./animate.scss"; @import "./animate.scss";
:root {
--bg-base: #efefef; div[voa] {
--fg-base: #090909 box-sizing: border-box;
--oabg-base: var(--bg-base, #efefef);
--oafg-base: var(--fg-base, #090909);
--oaheader-bg: linear-gradient(90deg, #6849E1, #b09ef4);
div {
box-sizing: border-box;
}
} }
.voa { .voa {
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
height: inherit; height: inherit;
@ -102,7 +111,7 @@
position: relative; position: relative;
width: 100%; width: 100%;
height: 4rem; height: 4rem;
background: linear-gradient(90deg, #6849E1, #b09ef4); background: var(--oaheader-bg);
z-index: 1; z-index: 1;
} }
@ -110,13 +119,22 @@
position: relative; position: relative;
width: 100%; width: 100%;
height: calc(100% - 4rem); height: calc(100% - 4rem);
background: var(--bg-base); background: var(--oabg-base);
color: var(--fg-base); color: var(--oafg-base);
transform: translateY(-140%); transform: translateY(-140%);
.voa-slide-main { .voa-slide-main {
height: calc(100% - 3rem); height: calc(100% - 3rem);
overflow: auto; overflow: auto;
padding: 1rem;
.voa-sm-separate {
height: 1px;
margin: 1rem 0;
background: var(--oafg-base);
width: 100%;
opacity: 0.4;
}
} }
.voa-slide-footer { .voa-slide-footer {
@ -137,6 +155,43 @@
} }
} }
.voa-account {
padding: 0 1rem;
.voa-account-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 3rem;
line-height: 3rem;
font-size: 1.25rem;
}
.voa-account-body {
width: 100%;
height: 7rem;
display: flex;
align-items: center;
gap: 2rem;
.voa-ab-ico {
display: contents;
img {
width: 4rem;
height: 4rem;
vertical-align: middle;
border-radius: 50%;
}
}
.voa-ab-info {
display: flex;
flex-direction: column;
}
}
}
.voa-logo { .voa-logo {
background-image: url("../favicon.svg"); background-image: url("../favicon.svg");

@ -12,6 +12,13 @@ let cfg = {
app_data: {}, app_data: {},
user: {
username: 'asd',
nickname: '',
email: '',
phone: '',
icon: 'https://public.veypi.com/img/avatar/0001.jpg'
},
ready: false, ready: false,
local_user: {}, local_user: {},
@ -20,7 +27,7 @@ let cfg = {
nats_pub_key: '', nats_pub_key: '',
prefix: '/api', prefix: '/api',
BaseUrl() { BaseUrl() {
return this.host.value + this.prefix return this.host + this.prefix
}, },
NatsHost() { NatsHost() {
if (this._host_nats.startsWith('ws')) { if (this._host_nats.startsWith('ws')) {
@ -29,7 +36,7 @@ let cfg = {
return 'ws://' + this._host_nats return 'ws://' + this._host_nats
}, },
media(u: string) { media(u: string) {
return this.host.value + u return this.host + u
}, },
goto(url: string) { goto(url: string) {
if (url.startsWith('http')) { if (url.startsWith('http')) {
@ -39,16 +46,16 @@ let cfg = {
if (!url.startsWith('/')) { if (!url.startsWith('/')) {
url = '/' + url url = '/' + url
} }
window.location.href = this.host.value + url window.location.href = this.host + url
}, },
Host() { Host() {
return this.host.value || window.location.host return this.host || window.location.host
}, },
userFileUrl() { userFileUrl() {
return '/fs/u/' return '/fs/u/'
}, },
appFileUrl() { appFileUrl() {
return `/fs/a/${this.uuid.value}/` return `/fs/a/${this.uuid}/`
}, },
} }

@ -4,36 +4,23 @@
* 2024-10-22 22:07 * 2024-10-22 22:07
* Distributed under terms of the GPL license. * Distributed under terms of the GPL license.
*/ */
import Base from './base' import b from './build'
import cfg from '../cfg'
export default class extends Base { export default class {
doms: { [key: string]: HTMLElement }
main: HTMLElement main: HTMLElement
constructor() { constructor() {
super() this.main = b('voa-account', [
this.main = this.build({ b('voa-account-header', [b('voa-ah-1', '我的账户'), b('voa-ah-2')]),
class: 'voa-account' b('voa-account-body', [
}) b('voa-ab-ico', [b('img', '', (d) => { d.setAttribute('src', cfg.user.icon) })]),
let u = { b('voa-ab-info', [
username: 'asd', b('voa-abi-1', [b('span', '昵称:'), b('span', '', (d) => { d.innerHTML = cfg.user.nickname })]),
icon: 'https://public.veypi.com/img/avatar/0001.jpg' b('voa-abi-2', [b('span', '账户:'), b('span', '', (d) => { d.innerHTML = cfg.user.username })]),
} b('voa-abi-3', [b('span', '邮箱:'), b('span', '', (d) => { d.innerHTML = cfg.user.email })]),
this.main.innerHTML = ` b('voa-abi-4', [b('span', '手机:'), b('span', '', (d) => { d.innerHTML = cfg.user.phone })]),
<div class="voa-account-header"> ])
<div class="vah-1">my account</div> ])
<div class="vah-2">account center</div> ])
</div>
<div class="voa-account-body">
<div class="vab-ico">
<img style="" class="" src="${u.icon}" />
</div>
<div class="vab-info">
<div class="vabi-1"><span></span> <span>${u.username}</span> </div>
<div class="vabi-2"><span></span> <span>${u.username}</span> </div>
<div class="vabi-3"><span></span> <span>${u.username}</span> </div>
<div class="vabi-4"><span></span> <span>${u.username}</span> </div>
</div>
</div>
`
} }
} }

@ -0,0 +1,24 @@
/*
* app.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-10-23 15:35
* Distributed under terms of the GPL license.
*/
import b from "./build";
export default class {
main: HTMLElement
body: HTMLElement
constructor() {
this.body = b('voa-apps-body')
this.main = b({
class: 'voa-apps',
children: [
b({ class: 'voa-apps-header', children: [b('voa-apps-title')] }),
this.body
]
})
}
}

@ -1,73 +0,0 @@
/*
* base.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-10-22 18:21
* Distributed under terms of the GPL license.
*/
interface buildOpts {
id?: string
typ?: 'div'
class?: string
style?: string
innerHtml?: string
onclick?: any
children?: HTMLElement[]
}
export default class {
class_prefix: string
constructor(p?: string) {
this.class_prefix = p || 'voa-'
}
build(opts: buildOpts) {
let dom = document.createElement(opts.typ || 'div')
if (opts.id) {
dom.id = opts.id
}
if (opts.class) {
this.addClass(dom, opts.class)
}
if (opts.innerHtml) {
dom.innerHTML = opts.innerHtml
}
if (opts.onclick) {
dom.onclick = opts.onclick
}
if (opts.children) {
for (let c in opts.children) {
dom.appendChild(opts.children[c])
}
}
if (opts.style) {
const regex = /([a-zA-Z-]+)\s*:\s*([^;]+);?/g;
let match;
while ((match = regex.exec(opts.style)) !== null) {
const key = match[1].trim();
const value = match[2].trim();
console.log([key, value])
dom.style.setProperty(key, value)
}
}
return dom
}
addClass(dom: HTMLElement, c: string) {
let items = c.split(' ')
for (let i of items) {
if (i.startsWith(this.class_prefix)) {
dom.classList.add(i)
} else {
dom.classList.add(this.class_prefix + i)
}
}
}
removeClass(dom: HTMLElement, c: string) {
let items = c.split(' ')
for (let i of items) {
if (i.startsWith(this.class_prefix)) {
dom.classList.remove(i)
} else {
dom.classList.remove(this.class_prefix + i)
}
}
}
}

@ -0,0 +1,84 @@
/*
* build.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-10-23 15:54
* Distributed under terms of the GPL license.
*/
interface buildOpts {
id?: string
typ?: string
class?: string
style?: string
innerHtml?: string
onclick?: any
children?: HTMLElement[]
updator?: (p: HTMLElement) => void
}
const typs = ['div', 'img', 'span', 'p', 'a']
const caches: (() => void)[] = []
function update() {
for (let c of caches) {
c()
}
}
export { update }
export default (opts: buildOpts | string, inner?: string | HTMLElement[], updator?: (p: HTMLElement) => void) => {
if (typeof opts === 'string') {
if (typs.indexOf(opts) >= 0) {
opts = { typ: opts }
} else {
opts = { class: opts }
}
}
if (inner) {
if (typeof inner == 'string') {
opts.innerHtml = inner
} else {
opts.children = inner
}
}
if (opts.updator) {
updator = opts.updator
}
let dom = document.createElement(opts.typ || 'div')
if (opts.id) {
dom.id = opts.id
}
if (opts.class) {
dom.classList.add(...opts.class.split(' '))
}
if (opts.innerHtml) {
dom.innerHTML = opts.innerHtml
}
if (opts.onclick) {
dom.onclick = opts.onclick
}
if (opts.children) {
for (let c in opts.children) {
dom.appendChild(opts.children[c])
}
}
if (opts.style) {
const regex = /([a-zA-Z-]+)\s*:\s*([^;]+);?/g;
let match: any
while ((match = regex.exec(opts.style)) !== null) {
const key = match[1].trim();
const value = match[2].trim();
console.log([key, value])
dom.style.setProperty(key, value)
}
}
dom.setAttribute('voa', '1')
if (updator) {
caches.push(() => {
updator(dom)
})
updator(dom)
}
return dom
}

@ -6,15 +6,14 @@
*/ */
import slide from './slide' import slide from './slide'
import Base from './base' import b from './build'
import bus from '../bus' import bus from '../bus'
export default class extends Base { export default class {
slide: slide slide: slide
frame: HTMLDivElement frame: HTMLElement
frame_login?: HTMLDivElement frame_login?: HTMLElement
frame_user?: HTMLDivElement frame_user?: HTMLElement
constructor(frame: HTMLDivElement) { constructor(frame: HTMLElement) {
super()
this.frame = frame this.frame = frame
this.frame.classList.add('voa') this.frame.classList.add('voa')
this.slide = new slide() this.slide = new slide()
@ -25,12 +24,12 @@ export default class extends Base {
}) })
} }
mount_login() { mount_login() {
this.frame_login = this.build({ this.frame_login = b({
class: 'off hover-line-b scale-in', class: 'voa-off voa-hover-line-b voa-scale-in',
innerHtml: '登录', innerHtml: '登录',
onclick: () => { onclick: () => {
console.log('click login') console.log('click login')
this.addClass(this.frame_login!, 'scale-off') this.frame_login?.classList.add('voa-scale-off')
this.mount_user() this.mount_user()
} }
}) })
@ -41,10 +40,10 @@ export default class extends Base {
} }
mount_user() { mount_user() {
let icon = 'https://public.veypi.com/img/avatar/0001.jpg' let icon = 'https://public.veypi.com/img/avatar/0001.jpg'
this.frame_user = this.build({ this.frame_user = b({
class: 'on scale-in', class: 'voa-on voa-scale-in',
innerHtml: ` innerHtml: `
<img style="" class="" src="${icon}" /> <img src="${icon}" />
`, `,
onclick: () => { onclick: () => {
this.slide.show() this.slide.show()

@ -4,7 +4,7 @@
* 2024-10-22 17:57 * 2024-10-22 17:57
* Distributed under terms of the GPL license. * Distributed under terms of the GPL license.
*/ */
import Base from './base' import b from './build'
import bus from '../bus' import bus from '../bus'
import account from './account' import account from './account'
@ -17,41 +17,43 @@ mask
footer footer
* *
* */ * */
export default class extends Base { export default class {
mask: HTMLDivElement mask: HTMLElement
slide: HTMLDivElement slide: HTMLElement
header: HTMLDivElement header: HTMLElement
body: HTMLElement body: HTMLElement
main: HTMLElement main: HTMLElement
footer: HTMLElement footer: HTMLElement
constructor() { constructor() {
super() this.header = b({
this.header = this.build({ class: 'voa-slide-header voa-animate-slow',
class: 'slide-header animate-slow',
}) })
this.footer = this.build({ this.footer = b({
class: 'slide-footer', class: 'voa-slide-footer',
innerHtml: 'logout', innerHtml: 'logout',
onclick: () => { onclick: () => {
bus.emit('logout') bus.emit('logout')
} }
}) })
this.main = this.build({ this.main = b({
class: 'slide-main', class: 'voa-slide-main',
children: [new account().main] children: [
new account().main,
b({ class: 'voa-sm-separate' })
]
}) })
this.body = this.build({ this.body = b({
class: 'slide-body animate-slow', class: 'voa-slide-body voa-animate-slow',
style: 'animation-delay: 300ms', style: 'animation-delay: 300ms',
children: [this.main, this.footer] children: [this.main, this.footer]
}) })
this.slide = this.build({ this.slide = b({
id: 'voa-slide', id: 'voa-slide',
class: 'slide', class: 'voa-slide',
children: [this.header, this.body] children: [this.header, this.body]
}) })
this.mask = this.build({ this.mask = b({
class: 'slide-mask', class: 'voa-slide-mask',
style: 'visibility: hidden', style: 'visibility: hidden',
children: [this.slide], children: [this.slide],
onclick: (e: MouseEvent) => { onclick: (e: MouseEvent) => {
@ -64,12 +66,12 @@ export default class extends Base {
} }
show() { show() {
this.mask.style.visibility = 'visible' this.mask.style.visibility = 'visible'
this.addClass(this.header, 'slidein-right') this.header.classList.add('voa-slidein-right')
this.addClass(this.body, 'slidein-up') this.body.classList.add('voa-slidein-up')
} }
hide() { hide() {
this.mask.style.visibility = 'hidden' this.mask.style.visibility = 'hidden'
this.removeClass(this.header, 'slidein-right') this.header.classList.remove('voa-slidein-right')
this.removeClass(this.body, 'slidein-up') this.body.classList.remove('voa-slidein-up')
} }
} }

Loading…
Cancel
Save