feat: add js base build function

v3
veypi 1 month ago
parent 3cadef35be
commit 7a2ef864a3

@ -7,12 +7,21 @@
@import "./animate.scss";
:root {
--bg-base: #efefef;
--fg-base: #090909
div[voa] {
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 {
user-select: none;
cursor: pointer;
height: inherit;
@ -102,7 +111,7 @@
position: relative;
width: 100%;
height: 4rem;
background: linear-gradient(90deg, #6849E1, #b09ef4);
background: var(--oaheader-bg);
z-index: 1;
}
@ -110,13 +119,22 @@
position: relative;
width: 100%;
height: calc(100% - 4rem);
background: var(--bg-base);
color: var(--fg-base);
background: var(--oabg-base);
color: var(--oafg-base);
transform: translateY(-140%);
.voa-slide-main {
height: calc(100% - 3rem);
overflow: auto;
padding: 1rem;
.voa-sm-separate {
height: 1px;
margin: 1rem 0;
background: var(--oafg-base);
width: 100%;
opacity: 0.4;
}
}
.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 {
background-image: url("../favicon.svg");

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

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

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

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

Loading…
Cancel
Save