diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..9d05015 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "bashmish.es6-string-css", + "ecmel.vscode-html-css", + "bierner.lit-html" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 9c62168..00034d3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,28 @@ -# StarElement +# Star-Web-Components -星光WebUI组件 \ No newline at end of file +星光Web组件 + +## 拉取 + +```sh +git clone ssh://git@172.20.184.160:7999/yr/star-web-components.git +``` + +## 提交 + +从主线分支切出对应的 feature|bugfix|optimize 等关键词的分支。 + +```sh +git checkout -b feature-component-button master +``` + +## 示例运行 + +pnpm 是npm包管理领域一个新的、领先的包管理器。 + +若无 pnpm,请先进行全局安装及切换npm源至[国内镜像源](https://www.npmmirror.com/)。 + +```sh +pnpm install # 安装开发调试依赖包 +pnpm vite # 使用vite运行示例 +``` diff --git a/fonts/gaia-icons.ttf b/fonts/gaia-icons.ttf new file mode 100644 index 0000000..76c2dc4 Binary files /dev/null and b/fonts/gaia-icons.ttf differ diff --git a/index.html b/index.html index fdee573..5a648a5 100644 --- a/index.html +++ b/index.html @@ -5,12 +5,40 @@ Vite + Lit + TS - - + - -

Vite + Lit

-
+ + + diff --git a/src/components/bubble/bubble.ts b/src/components/bubble/bubble.ts new file mode 100644 index 0000000..6a69058 --- /dev/null +++ b/src/components/bubble/bubble.ts @@ -0,0 +1,70 @@ +import { html, css, LitElement, nothing } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +@customElement('star-bubble') +export class StarBubble extends LitElement { + @property({ type: Number }) number = 0 + + render() { + /** + * 窄方盒子里放圆球,圆球数字区间 [1, 99], 超过99显示99+。 + * 数字可弹性伸缩。 + */ + if (this.number <= 0) return nothing + else if (this.number > 99) return html` +
+ 99+ +
+ ` + else return html` +
+ ${this.number} +
+ ` + } + + static styles = css` + :host { + display: inline-block; + width: calc(var(--li-base-height) - 16px); /* 尽量节省宽度 */ + height: var(--li-base-height); + } + + div { + display: flex; + width: 100%; + height: calc(100% - 16px); + top: 8px; + position: relative; + border-radius: 50%; + background-color: red; + } + + span { + width: 100%; + height: 100%; + color: #fff; + line-height: 190%; + text-align: center; + font-weight: bold; + font-size: 14px; + } + + span.small { + font-size: 12px; + line-height: 170%; + } + + sup { + font-size: 10px; + max-width: 6px; + display: inline-block; + } + ` +} + +declare global { + interface HTMLElementTagNameMap { + 'star-bubble': StarBubble + } +} \ No newline at end of file diff --git a/src/components/button/button-styles.ts b/src/components/button/button-styles.ts new file mode 100644 index 0000000..1b23def --- /dev/null +++ b/src/components/button/button-styles.ts @@ -0,0 +1,23 @@ +import {css, CSSResult} from 'lit' + +export const sharedStyles: CSSResult = css` + .test { + color: darkred; + font-weight: bold; + background-color: #ffffff; + } + + button { + color: blue; + padding: 0; + width: 100%; + border: unset; + display: block; + font-size: 16px; + text-align: left; + background: unset; + padding-inline-start: var(--li-left-padding); + min-height: var(--li-base-height); + line-height: var(--li-base-height); + } +` \ No newline at end of file diff --git a/src/components/button/button.ts b/src/components/button/button.ts new file mode 100644 index 0000000..fd67114 --- /dev/null +++ b/src/components/button/button.ts @@ -0,0 +1,40 @@ +import { html, LitElement, CSSResultArray, HTMLTemplateResult, nothing } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { sharedStyles } from './button-styles'; + +export enum ButtonType { + BASE = 'base' +} + +@customElement('star-button') +export class StarButton extends LitElement { + public static override get styles(): CSSResultArray { + return [ + sharedStyles + ]; + } + + @property({ type: ButtonType }) type = '' + @property({ type: String }) label = '' + + getBaseButton(): HTMLTemplateResult { + return html` + + ` + } + + render() { + switch(this.type) { + case ButtonType.BASE: return this.getBaseButton() + default: + console.error('unhanled 【star-button】 type') + return nothing + } + } +} + +declare global { + interface HTMLElementTagNameMap { + 'star-button': StarButton + } +} \ No newline at end of file diff --git a/src/components/li/li.ts b/src/components/li/li.ts new file mode 100644 index 0000000..b8807cd --- /dev/null +++ b/src/components/li/li.ts @@ -0,0 +1,287 @@ +import { html, css, LitElement, HTMLTemplateResult, nothing } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { classMap } from 'lit/directives/class-map.js'; +import '../bubble/bubble' + +export enum LiType { + BASE = 'base', + BUBBLE_LABEL = 'bubble-label', + ICON_LABEL = 'icon-label', + INFO_LABEL = 'info-label', + ONLY_EDIT = 'only-edit', + ONLY_LABEL = 'only-label', + ONLY_READ = 'only-read', +} + +@customElement('star-li') +export class StarLi extends LitElement { + @property({ type: LiType }) type = '' + @property({ type: String }) label = '' + @property({ type: String }) value = '' + @property({ type: String }) default = '' + @property({ type: String }) href = '' + @property({ type: String }) icon = '' + @property({ type: String }) iconcolor = '' + @property({ type: Number }) bubble = 0 + + getbase(): HTMLTemplateResult { + return html`
  • ` + } + + getbubblelabel(): HTMLTemplateResult | typeof nothing { + if (!this.label) { + console.error('【star-ul】【bubblelabel】缺少 label 参数') + return nothing + } + if (!this.bubble) { + console.error('【star-ul】【bubblelabel】缺少 bubble 参数') + return nothing + } + + const classes = `${this.href ? 'hashref' : ''} ${this.icon ? 'hasicon' : ''}` + const colorstyle = this.iconcolor + ? html`` + : nothing + + return html` +
  • + ${this.href ? html` + + ${colorstyle} + ${this.label} + + + ` : html` + + ${colorstyle} + ${this.label} + + + `} +
  • + ` + } + + geticonlabel(): HTMLTemplateResult | typeof nothing { + if (!this.label) { + console.error('【star-ul】【iconlabel】缺少 label 参数') + return nothing + } + if (!this.icon) { + console.error('【star-ul】【iconlabel】缺少 icon 参数') + return nothing + } + + const classes = this.href ? 'hasicon hashref' : 'hasicon' + const colorstyle = this.iconcolor + ? html`` + : nothing + + return html` +
  • + ${this.href ? html` + + ${colorstyle} + ${this.label} + + ` : html` + + ${colorstyle} + ${this.label} + + `} +
  • + ` + } + + getinfolabel(): HTMLTemplateResult | typeof nothing { + if (!this.label) { + console.error('【star-ul】【info】缺少 label 参数') + return nothing + } + if (!this.value) { + console.error('【star-li】【info】缺少 value 参数') + return nothing + } + + if (!this.href) { + return html` +
  • + ${this.label} + ${this.value} +
  • + ` + } + else { + return html` +
  • + + ${this.label} + ${this.value} + +
  • + ` + } + } + + getonlylabel(): HTMLTemplateResult | typeof nothing { + if (!this.label) { + console.error('【star-ul】【labeliconhref】缺少 label 参数') + return nothing + } + + return html` +
  • + ${this.href ? html` + + ${this.label} + + ` : html` + + ${this.label} + + `} +
  • + ` + } + + getonlyread(): HTMLTemplateResult | typeof nothing { + if (!this.label) { + console.error('【star-ul】【onlyread】缺少 label 参数') + return nothing + } + + return html` +
  • + ${this.label} +
  • + ` + } + + getonlyedit(): HTMLTemplateResult | typeof nothing { + if (!this.label) { + console.error('【star-ul】【onlyedit】缺少 label 参数') + return nothing + } + if (!this.default) { + console.error('【star-ul】【onlyedit】缺少 default 参数') + return nothing + } + + return html` +
  • + +
  • + ` + } + + render() { + switch (this.type) { + case LiType.BASE: return this.getbase() + case LiType.BUBBLE_LABEL: return this.getbubblelabel() + case LiType.ICON_LABEL: return this.geticonlabel() + case LiType.INFO_LABEL: return this.getinfolabel() + case LiType.ONLY_EDIT: return this.getonlyedit() + case LiType.ONLY_LABEL: return this.getonlylabel() + case LiType.ONLY_READ: return this.getonlyread() + default: + console.error('unhanled 【star-li】 type') + return nothing + } + } + + static styles = css` + :host { + width: inherit; + } + li { + width: inherit; + display: flex; + min-height: var(--li-base-height); + line-height: var(--li-base-height); + padding-inline-start: var(--li-left-padding); + padding-inline-end: var(--li-right-padding); /* right-arrow须与最右侧文字对齐 */ + } + li.hashref { + /* padding-inline-end: 0; */ /* right-arrow须与最右侧文字对齐 */ + } + a { + display: flex; + text-decoration: none; + color: #000; + width: 100%; + } + a.hasicon::before, a.hashref::after { + flex: 1; + font-size: 25px; + min-width: 25px; + max-width: 25px; + font-family: "gaia-icons"; + } + a.hasicon::before { + color: #657073; + text-align: left; + content: attr(data-icon); + padding-inline-end: var(--li-right-padding); + } + a.hashref::after { + color: #a5acae; + text-align: right; + content: "right-light"; + } + input { + width: 100vw; + max-width: 500px; + padding: 0; + border: 0; + height: inherit; + outline: none; + box-sizing: border-box; + vertical-align: top; + border-radius: 6px; + background-color: transparent; + font-size: 16px; + } + span.infokey, span.label { + flex: 1; + text-align: left; + } + span.infovalue { + /* flex: 1; */ /* 利用自动挤压 */ + text-align: right; + color: #aaa; + } + span.onlyread { + color: #aaa; + font-size: clamp(0.55rem, 2.3vw, 1.2rem); + } + span.text { + flex: 8; + text-align: left; + } + span.bubble { + flex: 1; + height: inherit; + background-color: red; + } + span.next { + flex: 1; + text-align: right; + color: #aaa; + } + ` +} + +declare global { + interface HTMLElementTagNameMap { + 'star-li': StarLi + } +} \ No newline at end of file diff --git a/src/components/section/section.ts b/src/components/section/section.ts new file mode 100644 index 0000000..92fbaf6 --- /dev/null +++ b/src/components/section/section.ts @@ -0,0 +1,50 @@ +import { html, css, LitElement, nothing } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' + +@customElement('star-animate-section') +export class StarAnimateSection extends LitElement { + @property() + transform = '' + + @state() + bar = '' + + render() { + const withTransform = this.transform ? html` + + ` : nothing + + return html` + ${withTransform} +
    + +
    + ` + } + + static styles = css` + :host { + position: absolute; + width: 100%; /* 100vw会有x轴溢出 */ + height: 100vh; + overflow: auto; + /* height: calc(100vh + 1px); */ /* 手动制造溢出 */ + animation-duration: .3s; + background-color: #f0f0f0; + } + + section { + /* overflow: auto; */ + } + ` +} + +declare global { + interface HTMLElementTagNameMap { + 'star-animate-section': StarAnimateSection + } +} \ No newline at end of file diff --git a/src/components/ul/ul.ts b/src/components/ul/ul.ts new file mode 100644 index 0000000..457f3e8 --- /dev/null +++ b/src/components/ul/ul.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement, HTMLTemplateResult, nothing } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { unsafeHTML } from 'lit/directives/unsafe-html.js'; + +export enum UlType { + BASE = 'base', + ONLY_HEADER = 'onlyheader', + ONLY_FOOTER = 'onlyfooter', + HEADER_FOOTER = 'headerfooter', +} + +@customElement('star-ul') +export class StarUl extends LitElement { + @property({ type: UlType }) type = '' + @property({ type: String }) title = '' + @property({ type: String }) text = '' + + getbase(): HTMLTemplateResult { + return html` + + ` + } + + getonlyheader(): HTMLTemplateResult | typeof nothing { + if (!this.title) { + console.error('【star-ul】【onlyheader】缺少 title 参数') + return nothing + } + + return html` +
    ${this.title}
    + + ` + } + + getonlyfooter(): HTMLTemplateResult | typeof nothing { + if (!this.text) { + console.error('【star-ul】【onlyfooter】缺少 text 参数') + return nothing + } + + return html` + + + ` + } + + getheaderfooter(): HTMLTemplateResult | typeof nothing { + if (!this.title) { + console.error('【star-ul】【headerfooter】缺少 title 参数') + return nothing + } + + if (!this.text) { + console.error('【star-ul】【headerfooter】缺少 text 参数') + return nothing + } + + return html` +
    ${this.title}
    + + + ` + } + + render() { + if (!this.type) { + console.error('使用【star-ul】前请先写明使用的类型(type)') + return nothing + } + + switch (this.type) { + case UlType.BASE: return this.getbase() + case UlType.HEADER_FOOTER: return this.getheaderfooter() + case UlType.ONLY_HEADER: return this.getonlyheader() + case UlType.ONLY_FOOTER: return this.getonlyfooter() + default: + console.error('unhanled 【star-ul】 type') + return nothing + } + } + + static styles = css` + :host { + width: inherit; + display: block; + margin: 20px auto; + max-width: 88vw; + } + + ul { + list-style: none; + flex-direction: column; + display: flex; + width: 88vw; + padding: 0; + margin: 10px auto; + max-width: 88vw; + background: #fff; + box-shadow: rgb(64 60 67 / 16%) 1px 1px 5px 1px; + border-radius: 10px; + } + + header, footer { + color: #888; + margin-left: 10px; + font-size: 12px; + } + + star-ul-base { + margin: 8px 0 12px 0; /* override child(star-ul-base) */ + } + + footer a { + text-decoration: unset; + } + + footer a:visited { + color: blue; + } + ` +} + +declare global { + interface HTMLElementTagNameMap { + 'star-ul': StarUl + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3d232ba --- /dev/null +++ b/src/index.ts @@ -0,0 +1,197 @@ +import { html, css, LitElement } from 'lit' +import { customElement, query, state } from 'lit/decorators.js' +import './components/ul/ul' +import './components/li/li' +import './components/section/section' +import { StarAnimateSection } from './components/section/section' + +import './test/panels/root' + +@customElement('settings-app') +export class SettingsApp extends LitElement { + + @query('star-animate-section#root') private rootSection!: StarAnimateSection; + + @state() sectionsMap = new Map() + + @state() prevSection: StarAnimateSection | null = null; + @state() nextSection: StarAnimateSection | null = null; + @state() currentSection: StarAnimateSection | null = null; + + + handleEvent(evt: Event) { + switch(evt.type) { + case 'hashchange': + // console.log(evt) + const newURL = (evt as HashChangeEvent).newURL; + // const oldURL = (evt as HashChangeEvent).oldURL; + const goPanelHash = new URL(newURL).hash; + this.navigate(goPanelHash) + break; + } + } + + /** + * + * @param panelhash + */ + dynamicLoadSection(panelhash: String): StarAnimateSection | null { + if (this.sectionsMap.has(panelhash)) return this.sectionsMap.get(panelhash) + + const panelname = panelhash.slice(1) + const section = document.createElement('star-animate-section') + section.setAttribute('id', panelname) + section.setAttribute('class', 'inactive r') + + const panel = document.createElement('panel-' + panelname) + if (!panel) return null + + section.appendChild(panel) + this.shadowRoot?.appendChild(section) + this.sectionsMap.set(panelhash, section) + + return section + } + + navigate(panelhash: String) { + console.log('goto', panelhash) + if (this.currentSection === null) this.currentSection = this.rootSection + this.prevSection = this.currentSection + + if (panelhash === '') { + panelhash = 'root' + this.nextSection = this.rootSection + } else { + const wantSection: StarAnimateSection | null = + document.querySelector('star-animate-section' + panelhash); + if (wantSection === null) { + const loadsection = this.dynamicLoadSection(panelhash) + if (loadsection === null) return console.error('该panel不存在', panelhash) + else this.nextSection = loadsection + } else { + this.nextSection = wantSection + } + } + this.currentSection = this.nextSection + console.log(this.prevSection, this.currentSection) + + this.nextSection.className = 'active r2m' + this.prevSection.className = 'm2l' + this.prevSection.addEventListener('animationend', (e) => { + console.log(e) + this.nextSection?.classList.remove('r2m') + this.prevSection?.classList.remove('m2l') + this.prevSection?.classList.add('inactive', 'l') + }, { once: true }) + return + + // switch (panelhash) { + // case '#icon': + // this.nextSection.className = 'active r2m' + // this.prevSection.className = 'm2l' + // this.prevSection.addEventListener('animationend', (e) => { + // console.log(e) + // this.nextSection?.classList.remove('r2m') + // this.prevSection?.classList.remove('m2l') + // this.prevSection?.classList.add('inactive', 'l') + // }, { once: true }) + // break + // case '#root': + // default: + // this.nextSection.className = 'active l2m' + // this.prevSection.className = 'm2r' + // this.prevSection.addEventListener('animationend', (e) => { + // console.log(e) + // this.nextSection?.classList.remove('l2m'); + // this.prevSection?.classList.remove('m2r'); + // this.prevSection?.classList.add('inactive', 'r') + // }, { once: true }) + // break; + // } + } + + render() { + window.addEventListener('hashchange', this) + + return html` + + + + ` + } + + static styles = css` + :host { + position: absolute; + width: 100vw; + } + .active { + display: block; + } + .inactive { + display: none; + } + .r2m { + animation-name: right2middle; + } + .m2l { + animation-name: middle2left; + } + .l2m { + animation-name: left2middle; + } + .m2r { + animation-name: middle2right; + } + + .r { + transform: translateX(var(--right-transform)); + } + .l { + transform: translateX(var(--left-transform)); + } + + @keyframes right2middle { + from { + transform: translateX(var(--right-transform)); + } + to { + transform: translateX(0); + } + } + @keyframes middle2left { + from { + transform: translateX(0); + background-iconcolor: #f0f0f0; + } + to { + transform: translateX(var(--left-transform)); + background-iconcolor: #e0e0e0; + } + } + @keyframes left2middle { + from { + transform: translateX(var(--left-transform)); + background-iconcolor: #e0e0e0; + } + to { + transform: translateX(0); + background-iconcolor: #f0f0f0; + } + } + @keyframes middle2right { + from { + transform: translateX(0); + } + to { + transform: translateX(var(--right-transform)); + } + } + ` +} + +declare global { + interface HTMLElementTagNameMap { + 'settings-app': SettingsApp + } +} \ No newline at end of file diff --git a/src/my-element.ts b/src/my-element.ts deleted file mode 100644 index da680eb..0000000 --- a/src/my-element.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { html, css, LitElement } from 'lit' -import { customElement, property } from 'lit/decorators.js' -import litLogo from './assets/lit.svg' - -/** - * An example element. - * - * @slot - This element has a slot - * @csspart button - The button - */ -@customElement('my-element') -export class MyElement extends LitElement { - /** - * Copy for the read the docs hint. - */ - @property() - docsHint = 'Click on the Vite and Lit logos to learn more' - - /** - * The number of times the button has been clicked. - */ - @property({ type: Number }) - count = 0 - - render() { - return html` -
    - - - - - - -
    - -
    - -
    -

    ${this.docsHint}

    - ` - } - - private _onClick() { - this.count++ - } - - static styles = css` - :host { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; - } - - .logo { - height: 6em; - padding: 1.5em; - will-change: filter; - } - .logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); - } - .logo.lit:hover { - filter: drop-shadow(0 0 2em #325cffaa); - } - - .card { - padding: 2em; - } - - .read-the-docs { - color: #888; - } - - h1 { - font-size: 3.2em; - line-height: 1.1; - } - - a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; - } - a:hover { - color: #535bf2; - } - - button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; - } - button:hover { - border-color: #646cff; - } - button:focus, - button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; - } - - @media (prefers-color-scheme: light) { - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } - } - ` -} - -declare global { - interface HTMLElementTagNameMap { - 'my-element': MyElement - } -} diff --git a/src/test/panels/about/about.ts b/src/test/panels/about/about.ts new file mode 100644 index 0000000..af5ee67 --- /dev/null +++ b/src/test/panels/about/about.ts @@ -0,0 +1,44 @@ +import { html, LitElement, CSSResultArray } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { LiType } from '../../../components/li/li' +import { UlType } from '../../../components/ul/ul' +import { sharedStyles } from '../shared-styles'; + +@customElement('panel-about') +export class PanelAbout extends LitElement { + @property() + foo = '' + + @state() + bar = '' + + render() { + return html` + + FXJT +
    + ZXXZ +
    + + + 自动加入热点 + + + + + + ` + } + + public static override get styles(): CSSResultArray { + return [ + sharedStyles + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'panel-about': PanelAbout + } +} \ No newline at end of file diff --git a/src/test/panels/general/about_machine/about_machine.ts b/src/test/panels/general/about_machine/about_machine.ts new file mode 100644 index 0000000..e0c49fe --- /dev/null +++ b/src/test/panels/general/about_machine/about_machine.ts @@ -0,0 +1,290 @@ +import { html, LitElement, CSSResultArray } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { repeat } from 'lit/directives/repeat.js' +import { UlType } from '../../../../components/ul/ul' +import { LiType } from '../../../../components/li/li' +import { sharedStyles } from '../../shared-styles'; +import { nothing } from 'lit' + +type LINENODE = { + node: String, + nodetype: LiType, + label: String, + value?: String, + href?: String, +} + +type BLOCKNODE = { + node: String, + nodetype: UlType, + nodes: Array, + nodetitle?: String, + nodevalue?: String, +} + +@customElement('panel-about-machine') +export class PanelAboutMachine extends LitElement { + @property() + foo = '' + + @state() paneljson = [ + { + node: '', + nodetype: UlType.BASE, + nodes: [ + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '名称', + value: 'StarElement', + href: '#icon' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '软件版本', + value: '15.5' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '型号名称', + value: 'iPhone 13 Pro' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '型号号码', + value: 'MDNFAF3H/A' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '序列号', + value: 'RFAFAFF12432GM' + }, + ] + }, + { + node: 'star-ul', + nodetype: UlType.BASE, + nodes: [ + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '有限保修', + value: '到期日期: 2025/5/25', + href: '#icon' + }, + ] + }, + { + node: '', + nodetype: UlType.BASE, + nodes: [ + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '歌曲', + value: '0' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '视频', + value: '430' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '照片', + value: '6050' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '应用程序', + value: '154' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '总容量', + value: '256 GB' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '可用容量', + value: '56.42 GB' + } + ] + }, + { + node: '', + nodetype: UlType.BASE, + nodes: [ + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '无线局域网地址', + value: '30:E2:D3:4A:C3:5D' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '蓝牙', + value: '30:E2:D3:4A:C3:5D' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '调制解调器固件', + value: '1.61.00' + }, + { + node: 'star-li', + nodetype: LiType.ONLY_LABEL, + label: 'SEID', + href: '#icon' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '运营商锁', + value: '无 SIM 卡限制' + } + ] + }, + { + node: '', + nodetype: UlType.ONLY_HEADER, + nodetitle: '主号', + nodes: [ + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '网络', + value: '中国联通' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '运营商', + value: '中国联通 50.0' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: 'IMEI', + value: '35 717429 442903 2' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: 'ICCID', + value: '89860118802136995387' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: 'MEID', + value: '35717429442903' + } + ] + }, + { + node: '', + nodetype: UlType.ONLY_HEADER, + nodetitle: '副号', + nodes: [ + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '网络', + value: '中国移动' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: '运营商', + value: '中国移动 50.0' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: 'IMEI', + value: '35 717429 442903 2' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: 'ICCID', + value: '89860118802136995387' + }, + { + node: 'star-li', + nodetype: LiType.INFO_LABEL, + label: 'MEID', + value: '35717429442903' + } + ] + }, + { + node: 'star-ul', + nodetype: UlType.BASE, + nodes: [ + { + node: 'star-li', + nodetype: LiType.ONLY_LABEL, + label: '证书信任设置', + href: '#icon' + }, + ] + }, + ] + + render() { + return html` + ${repeat(this.paneljson, (block: BLOCKNODE) => html` + + ${repeat(block.nodes, (node: LINENODE, index) => html` + ${node.href + ? html` + + ` + : html` + + ` + } + ${index < block.nodes.length - 1 + ? html`
    ` // 分隔线 + : nothing + } + `)} +
    + `)} + ` + } + + public static override get styles(): CSSResultArray { + return [ + sharedStyles + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'panel-about-machine': PanelAboutMachine + } +} \ No newline at end of file diff --git a/src/test/panels/general/general.ts b/src/test/panels/general/general.ts new file mode 100644 index 0000000..7b79d42 --- /dev/null +++ b/src/test/panels/general/general.ts @@ -0,0 +1,90 @@ +import { html, LitElement, CSSResultArray } from 'lit' +import { customElement } from 'lit/decorators.js' +import { eventOptions } from 'lit/decorators.js'; +import { UlType } from '../../../components/ul/ul' +import { LiType } from '../../../components/li/li' +import { ButtonType, StarButton } from '../../../components/button/button' +import { sharedStyles } from '../shared-styles'; +import './about_machine/about_machine' +import './update/auto_update' +import './update/update_system' + +@customElement('panel-general') +export class PanelGeneral extends LitElement { + @eventOptions({passive: false}) + handleEvent(evt: Event) { + switch (evt.type) { + case 'click': + if ((evt.target as StarButton).label === '关机') { + alert('关机') + } + break; + } + } + + render() { + return html` + + +
    + +
    + + + +
    + +
    + +
    + +
    + +
    + + + +
    + +
    + + + +
    + +
    + +
    + +
    + +
    + + + + + + + + + + + +
    + +
    + ` + } + + public static override get styles(): CSSResultArray { + return [ + sharedStyles + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'panel-general': PanelGeneral + } +} \ No newline at end of file diff --git a/src/test/panels/general/update/auto_update.ts b/src/test/panels/general/update/auto_update.ts new file mode 100644 index 0000000..637e130 --- /dev/null +++ b/src/test/panels/general/update/auto_update.ts @@ -0,0 +1,31 @@ +import { html, css, LitElement } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { LiType } from '../../../../components/li/li' +import { UlType } from '../../../../components/ul/ul' + +@customElement('panel-auto-update') +export class PanelAutoUpdate extends LitElement { + @property() + foo = '' + + @state() + bar = '' + + render() { + return html` + + + + ` + } + + static styles = css` + + ` +} + +declare global { + interface HTMLElementTagNameMap { + 'panel-auto-update': PanelAutoUpdate + } +} \ No newline at end of file diff --git a/src/test/panels/general/update/update_system.ts b/src/test/panels/general/update/update_system.ts new file mode 100644 index 0000000..152696d --- /dev/null +++ b/src/test/panels/general/update/update_system.ts @@ -0,0 +1,35 @@ +import { html, LitElement, CSSResultArray } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { LiType } from '../../../../components/li/li' +import { UlType } from '../../../../components/ul/ul' + +@customElement('panel-update-system') +export class PanelUpdateSystem extends LitElement { + @property() + foo = '' + + @state() + bar = '' + + render() { + return html` + + + + + + + + ` + } + + public static override get styles(): CSSResultArray { + return []; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'panel-update-system': PanelUpdateSystem + } +} \ No newline at end of file diff --git a/src/test/panels/icon/icon.ts b/src/test/panels/icon/icon.ts new file mode 100644 index 0000000..38910ba --- /dev/null +++ b/src/test/panels/icon/icon.ts @@ -0,0 +1,286 @@ +import { html, LitElement, CSSResultArray } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { repeat } from 'lit/directives/repeat.js'; +import { UlType } from '../../../components/ul/ul' +import { LiType } from '../../../components/li/li'; +import { sharedStyles } from '../shared-styles'; + +@customElement('panel-icon') +export class PanelIcon extends LitElement { + @property() foo = '' + + @state() icons = [ + "2g", + "3g", + "4g", + + "accessibility", + "achievement", + "add-contact", + "add", + "addons", + + "airplane", + "alarm-clock", + "alarm-stop", + "alarm", + + "album", + "all-day", + "arrow-left", + "arrow-right", + "artist", + "attachment", + "battery-0", + "battery-1", + "battery-2", + "battery-3", + "battery-4", + "battery-5", + "battery-6", + "battery-7", + "battery-8", + "battery-9", + "battery-10", + "battery-charging", + "battery-unknown", + + "bluetooth-a2dp", + "bluetooth-circle", + "bluetooth-transfer-circle", + "bluetooth-transfer", + "bluetooth", + + "brightness", + "browsing", + "bug", + + "calendar", + "call-bluetooth", + "call-emergency", + "call-forwarding", + "call-hang-up", + "call-hold", + "call-incoming", + "call-merge", + "call-missed", + "call-outgoing", + "call-reversed", + "call-ringing", + "call", + "callback-emergency", + + "camera", + "change-wallpaper", + "clear-input-left", + "clear-input-right", + + "close", + "compose", + "contact-find", + "contacts", + "crashed", + "crop", + + "data", + "delete", + "developer", + "device-info", + "dismiss-keyboard", + "do-not-track", + "download-circle", + "download", + + "edge", + "edit-contact", + "edit-image", + "edit", + "effects", + "email-forward", + "email-mark-read", + "email-mark-unread", + "email-move", + "email-reply", + "email", + + "exclamation", + "expand-left", + "expand-right", + "facebook", + "feedback", + "find", + "firefox", + "flag", + "flash-auto", + "flash-off", + "flash-on", + "focus-locked", + "focus-locking", + + "gesture", + "gmail", + "grid-circular", + "grid", + "gsm", + + "hdr-boxed", + "hdr", + "headphones", + "help", + "homescreen", + "hspa-plus", + "hspa", + + "import-memorycard-circle", + "incoming-sms", + "info", + + "keyboard-circle", + "keyboard", + + "languages", + "left-light", + "left", + "link", + "location", + "lock", + + "media-storage", + "menu", + "messages", + "mic", + "minus", + "moon", + "more", + "mute", + + "nfc", + "no-sim", + "notifications", + + "o", + "outgoing-sms", + "outlook", + + "pause", + "picture-size", + "play", + "play-circle", + "playlist", + "privacy", + + "recent-calls", + "reload", + "repeat-once", + "repeat", + "reply-all", + "right-light", + "right", + "rocket", + "rotate", + + "scene", + "sd-card", + "search", + "seek-back", + "seek-forward", + "select", + "self-timer", + "send-left", + "send-right", + "settings", + "share", + "shuffle", + + "signal-0", + "signal-1", + "signal-2", + "signal-3", + "signal-4", + "signal-5", + "signal-roaming", + + "sim-toolkit", + "sim", + + "skip-back", + "skip-forward", + + "songs", + "sound-max", + "sound-min", + "star-empty", + "star-full", + + "stop", + "storage-circle", + "storage", + "switch", + "sync", + + "tethering", + "themes", + "tick-circle", + "tick", + "time", + "toggle-camera-front", + "toggle-camera-rear", + "topup-with-code", + "topup", + "twitter", + + "undo-circular", + "undo", + "unlock", + "update-balance", + "usb", + "user", + + "vibrate", + "video-mic", + "video-size", + "video", + "voicemail", + + "wallpaper", + "wifi-1", + "wifi-2", + "wifi-3", + "wifi-4", + "wifi-permissions" + ] + + getRangeColor() { + const r = ((255 * Math.random())>>0).toString(16).padStart(2, '0'); + const g = ((255 * Math.random())>>0).toString(16).padStart(2, '0'); + const b = ((255 * Math.random())>>0).toString(16).padStart(2, '0'); + return "#"+r+g+b; + } + + render() { + return html` + + ${repeat(this.icons, (iconname, index) => html` + + ${index < this.icons.length - 1 ? html`
    ` : null} + `)} +
    + ` + } + + public static override get styles(): CSSResultArray { + return [ + sharedStyles + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'panel-icon': PanelIcon + } +} \ No newline at end of file diff --git a/src/test/panels/root.ts b/src/test/panels/root.ts new file mode 100644 index 0000000..75a848b --- /dev/null +++ b/src/test/panels/root.ts @@ -0,0 +1,105 @@ +import { html, LitElement, CSSResultArray } from 'lit' +import { customElement, state } from 'lit/decorators.js' +import { LiType } from '../../components/li/li' +import { UlType } from '../../components/ul/ul' +import { sharedStyles } from './shared-styles'; +import './about/about' +import './icon/icon' +import './general/general' + +type SEID = String + +@customElement('panel-root') +export class PanelRoot extends LitElement { + @state() wlanMacAddress = "31:D2:3F:4E:D2:5B" + @state() availableCapability = "50.26GB" + @state() seid = this.genMockSeid() + @state() otherdevice = '若要将手表与手机配对,请前往 https://help.kylin.cn' + + genMockSeid(): SEID { + return Array(48) + .fill(0) + .map(() => "0123456789ABCDEF"[Math.floor(Math.random()*16)]) + .join('') + } + + rootPanel = html` + + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + + +
    + +
    + +
    + +
    + +
    + +
    + + + + + + + +
    + +
    + + + +
    + +
    + + + 自动加入热点 + + + + +
    + +
    + ` + + render() { + return html` + ${this.rootPanel} + ` + } + + public static override get styles(): CSSResultArray { + return [ + sharedStyles + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'panel-root': PanelRoot + } +} \ No newline at end of file diff --git a/src/test/panels/shared-styles.ts b/src/test/panels/shared-styles.ts new file mode 100644 index 0000000..70b4b0d --- /dev/null +++ b/src/test/panels/shared-styles.ts @@ -0,0 +1,11 @@ +import { css, CSSResult } from 'lit' + +export const sharedStyles: CSSResult = css` + hr { + margin: 0; + padding: 0; + border-width: 0 0 1px 0; + border-style: solid; + border-color: #ccc; + } +` diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/tsconfig.json b/tsconfig.json index b080b2b..be3a862 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "module": "ESNext", + "target": "ES2022", "lib": ["ES2020", "DOM", "DOM.Iterable"], "declaration": true, "emitDeclarationOnly": true,