Merge branch 'master' of ssh://172.20.184.160:7999/yr/star-web-components into featurn-component-locked
This commit is contained in:
commit
1731b05a1e
|
@ -0,0 +1,192 @@
|
||||||
|
import {html, css, LitElement, HTMLTemplateResult} from 'lit'
|
||||||
|
import {customElement, property, query} from 'lit/decorators.js'
|
||||||
|
@customElement('brightness-slider')
|
||||||
|
export class BrightnessSlider extends LitElement {
|
||||||
|
@query('.progress') progress!: HTMLDivElement
|
||||||
|
@query('.sliderBar') sliderBar!: HTMLDivElement
|
||||||
|
@property({type: String}) id = ''
|
||||||
|
@property({type: String}) _coverLen = ''
|
||||||
|
@property({type: Number}) innerWidth = 0
|
||||||
|
@property({type: Number}) distance = 0
|
||||||
|
@property({type: Number}) currentDistance = 0
|
||||||
|
@property({type: Number}) barWidth = 0
|
||||||
|
@property({type: Number}) max = 100
|
||||||
|
@property({type: Number}) value = 1
|
||||||
|
@property() touchAction = {
|
||||||
|
// 触摸开始落点
|
||||||
|
start: {
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: 0,
|
||||||
|
clientX: 0,
|
||||||
|
clientY: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 触摸结束落点
|
||||||
|
last: {
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: 0,
|
||||||
|
clientX: 0,
|
||||||
|
clientY: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({type: String})
|
||||||
|
get coverLen() {
|
||||||
|
return this._coverLen
|
||||||
|
}
|
||||||
|
|
||||||
|
set coverLen(value: string) {
|
||||||
|
this.style.setProperty('--cover-length', value)
|
||||||
|
this._coverLen = value
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): HTMLTemplateResult {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="sliderBar"
|
||||||
|
data-icon="brightness"
|
||||||
|
value=${this.value}
|
||||||
|
max=${this.max}
|
||||||
|
@touchstart=${this}
|
||||||
|
@touchmove=${this}
|
||||||
|
@touchend=${this}
|
||||||
|
>
|
||||||
|
<div class="progress"></div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
dislay: block;
|
||||||
|
--cover-length: 3.78px; // 亮度为1
|
||||||
|
--background-lm: rgba(255, 255, 255, 0.5);
|
||||||
|
--background-dm: rgba(0, 0, 0, 0.15);
|
||||||
|
--progress-background-lm: rgba(255, 255, 255, 0.8);
|
||||||
|
--progress-background-dm: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderBar {
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
background: var(--background-lm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
width: var(--cover-length);
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
background: var(--progress-background-lm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderBar::before {
|
||||||
|
position: absolute;
|
||||||
|
content: attr(data-icon);
|
||||||
|
font-family: gaia-icons;
|
||||||
|
font-style: normal;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
.sliderBar {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderBar::before {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
left: 29px;
|
||||||
|
top: 29px;
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
.sliderBar {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderBar::before {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
left: 14px;
|
||||||
|
top: 14px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
if (this.value) {
|
||||||
|
let len = Math.floor((this.value * this.sliderBar.offsetWidth) / this.max)
|
||||||
|
this.coverLen = len.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(event: TouchEvent) {
|
||||||
|
switch (event.type) {
|
||||||
|
case 'touchstart':
|
||||||
|
this.touchAction.start.clientX = event.touches[0].clientX
|
||||||
|
this.barWidth = this.sliderBar.offsetWidth
|
||||||
|
break
|
||||||
|
case 'touchmove':
|
||||||
|
this.touchAction.last.clientX = event.touches[0].clientX
|
||||||
|
this.distance =
|
||||||
|
this.touchAction.last.clientX - this.touchAction.start.clientX
|
||||||
|
this.currentDistance = this.distance + this.progress.offsetWidth
|
||||||
|
|
||||||
|
if (this.currentDistance < 0) {
|
||||||
|
// this.currentDistance = 0;
|
||||||
|
this.currentDistance = this.barWidth / this.max
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentDistance > this.barWidth) {
|
||||||
|
this.currentDistance = this.barWidth
|
||||||
|
}
|
||||||
|
this.progress.style.setProperty('width', this.currentDistance + 'px')
|
||||||
|
break
|
||||||
|
case 'touchend':
|
||||||
|
this.value = Math.floor(
|
||||||
|
(this.currentDistance * this.max) / this.barWidth
|
||||||
|
)
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('brightness-slider-change', {
|
||||||
|
detail: {
|
||||||
|
value: this.value,
|
||||||
|
},
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(value: number) {
|
||||||
|
this.value = value
|
||||||
|
let len = Math.floor((value * this.sliderBar.offsetWidth) / this.max)
|
||||||
|
this.coverLen = len.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'brightness-slider': BrightnessSlider
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './brightness-slider.js'
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "@star-web-components/brightness-slider",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./index.js",
|
||||||
|
"module": "./index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./index": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./brightness-slider.js": {
|
||||||
|
"default": "./brightness-slider.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"rootDir": "../../"
|
||||||
|
},
|
||||||
|
"include": ["*.ts"]
|
||||||
|
}
|
|
@ -102,4 +102,10 @@ export default css`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* menubutton */
|
||||||
|
:host(#menuitem) button {
|
||||||
|
font-size: 24px;
|
||||||
|
height: 76px;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
import {html, css, LitElement, HTMLTemplateResult, nothing} from 'lit'
|
||||||
|
import {customElement, property, queryAssignedElements} from 'lit/decorators.js'
|
||||||
|
import {HeaderBarType} from '../header-bar/header-bar.js'
|
||||||
|
import {
|
||||||
|
IconControlBar,
|
||||||
|
IconControlBarType,
|
||||||
|
} from '../icon-control-bar/icon-control-bar.js'
|
||||||
|
import {WebActivity} from './interface.js'
|
||||||
|
import '../icon-control-bar/icon-control-bar.js'
|
||||||
|
import '../header-bar/header-bar.js'
|
||||||
|
export enum DropDownMenuType {
|
||||||
|
HEADER_WITH_TIME = 'header-with-time',
|
||||||
|
HEADER_WITH_DATE_TIME = 'header-with-date-time',
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('drop-down-menu')
|
||||||
|
export class DropDownMenu extends LitElement {
|
||||||
|
@property({type: DropDownMenuType}) type = ''
|
||||||
|
@queryAssignedElements({flatten: true}) slotElements!: HTMLSlotElement[]
|
||||||
|
getOnlyTime(): HTMLTemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="inner">
|
||||||
|
<header-bar type=${HeaderBarType.ONLY_TIME}>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="compose"
|
||||||
|
@click=${this}
|
||||||
|
></icon-control-bar>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="settings"
|
||||||
|
@click=${this}
|
||||||
|
></icon-control-bar>
|
||||||
|
</header-bar>
|
||||||
|
<div class="all-quick-icons">
|
||||||
|
<slot @slotchange=${this}></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeAndDate(): HTMLTemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="inner">
|
||||||
|
<header-bar type=${HeaderBarType.DATE_TIME}>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="settings"
|
||||||
|
@click=${this}
|
||||||
|
></icon-control-bar>
|
||||||
|
</header-bar>
|
||||||
|
<div class="others">
|
||||||
|
<slot @slotchange=${this}></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): HTMLTemplateResult | typeof nothing {
|
||||||
|
switch (this.type) {
|
||||||
|
case DropDownMenuType.HEADER_WITH_TIME:
|
||||||
|
return this.getOnlyTime()
|
||||||
|
case DropDownMenuType.HEADER_WITH_DATE_TIME:
|
||||||
|
return this.getTimeAndDate()
|
||||||
|
default:
|
||||||
|
console.error('unhandled 【pull-down-menu】 type')
|
||||||
|
return nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([open]) {
|
||||||
|
transform: translateY(0);
|
||||||
|
transition: transform 300ms ease, visibility 300ms;
|
||||||
|
will-change: transform;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
header-bar[type='only-time'] {
|
||||||
|
dispaly: flex;
|
||||||
|
width: 512px;
|
||||||
|
height: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header-bar[type='date-time'] {
|
||||||
|
dispaly: flex;
|
||||||
|
width: 860px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-quick-icons {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 52px);
|
||||||
|
position: relative;
|
||||||
|
top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.others {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
top: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
header-bar[type='only-time'] {
|
||||||
|
dispaly: flex;
|
||||||
|
width: 256px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header-bar[type='date-time'] {
|
||||||
|
dispaly: flex;
|
||||||
|
width: 430px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-quick-icons {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 26px);
|
||||||
|
position: relative;
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.others {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
handleEvent(event: Event) {
|
||||||
|
switch (event.type) {
|
||||||
|
case 'click':
|
||||||
|
switch ((event.target as IconControlBar).icon) {
|
||||||
|
case 'compose':
|
||||||
|
window.dispatchEvent(new CustomEvent('editor-action'))
|
||||||
|
break
|
||||||
|
case 'settings':
|
||||||
|
let activity = new WebActivity('configure', {})
|
||||||
|
activity.start().then(
|
||||||
|
() => {},
|
||||||
|
() => {}
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'drop-down-menu': DropDownMenu
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './drop-down-menu.js'
|
|
@ -0,0 +1,9 @@
|
||||||
|
export interface WebActivity {
|
||||||
|
start(): Promise<any>
|
||||||
|
cancel(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export var WebActivity: {
|
||||||
|
prototype: WebActivity
|
||||||
|
new (name: string, data?: any): WebActivity
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "@star-web-components/drop-down-menu",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./index.js",
|
||||||
|
"module": "./index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./index": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./drop-down-menu.js": {
|
||||||
|
"default": "./drop-down-menu.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"rootDir": "../../"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"*.ts",
|
||||||
|
"../icon-control-bar/icon-control-bar.js",
|
||||||
|
"../header-bar/header-bar.js",
|
||||||
|
"./interface.js"
|
||||||
|
]
|
||||||
|
}
|
|
@ -9,6 +9,12 @@
|
||||||
|
|
||||||
## 主屏指示器(Home Indicator)
|
## 主屏指示器(Home Indicator)
|
||||||
|
|
||||||
|
定义:Home Indicator,为了达成全面屏,苹果移除了实体 Home 键,取而代之的是一条 134 x 5 pt 的虚拟 Home 指示条。参照 Macbook Pro 的 Touch Bar,感觉库克对可以滑动的 Bar 真是有迷之热爱。
|
||||||
|
|
||||||
|
1px \* 3/4 = 1pt
|
||||||
|
|
||||||
|
苹果全面屏底部多出了高度为 34 的 Home Indicator 区域。
|
||||||
|
|
||||||
主屏指示器应具备的功能:
|
主屏指示器应具备的功能:
|
||||||
|
|
||||||
- 锁屏处,向上拨动底部可抓取的主屏指示器随手指拖动向上推动锁屏页,进入主屏页
|
- 锁屏处,向上拨动底部可抓取的主屏指示器随手指拖动向上推动锁屏页,进入主屏页
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {html, css} from 'lit'
|
import {html, css, CSSResultArray} from 'lit'
|
||||||
import {customElement} from 'lit/decorators.js'
|
import {customElement} from 'lit/decorators.js'
|
||||||
import GestureDector from '../../lib/gesture/gesture-detector'
|
|
||||||
import {StarBaseElement} from '../base/star-base-element'
|
import {StarBaseElement} from '../base/star-base-element'
|
||||||
|
|
||||||
@customElement('home-bar-indicator')
|
@customElement('home-bar-indicator')
|
||||||
|
@ -8,77 +7,30 @@ export class HomeBarIndicator extends StarBaseElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.startGestureDetector()
|
this.startGestureDetector()
|
||||||
this.updateComplete.then(() => {
|
|
||||||
console.log(this)
|
|
||||||
const log = (e: any) => {
|
|
||||||
console.log(e.type, e.target)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chrome 上调用 e.preventDedault() 可阻止右键菜单弹出,同时不会影响
|
|
||||||
* 正常 touch 事件的分发。
|
|
||||||
*
|
|
||||||
* Firefox 上调用 e.preventDedault() 可阻止右键菜单弹出,但会中断 touch 事件,
|
|
||||||
* 表现为:长按(touchstart),中断,手抬起再按(touchmove)
|
|
||||||
*
|
|
||||||
* 测试长按事件时,请使用终端设备或Chrome
|
|
||||||
*/
|
|
||||||
this.addEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
(e) => {
|
|
||||||
e.stopImmediatePropagation()
|
|
||||||
if (navigator.vendor === 'Google Inc.') {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 同时监听 tap 和 doubletap 事件时,注意设置可选参数的次序
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// this.addEventListener('tap', log, {once:true});
|
|
||||||
// this.addEventListener('doubletap', log);
|
|
||||||
// this.addEventListener('panstart', log);
|
|
||||||
// this.addEventListener('panmove', log);
|
|
||||||
// this.addEventListener('panend', log);
|
|
||||||
// this.addEventListener('swipe', log);
|
|
||||||
// this.addEventListener('holdstart', log);
|
|
||||||
|
|
||||||
let myElement = document.createElement('button')
|
|
||||||
myElement.setAttribute('style', 'height:200px;width:200px;')
|
|
||||||
myElement = GestureDector.embedded(myElement)
|
|
||||||
myElement.addEventListener('tap', log)
|
|
||||||
myElement.addEventListener('doubletap', log, {once: true})
|
|
||||||
myElement.addEventListener('holdstart', log)
|
|
||||||
// console.log(GestureDector)
|
|
||||||
// GestureDector.disembedded(myElement)
|
|
||||||
this.shadowRoot?.appendChild(myElement)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一块等宽屏幕的区域
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
render() {
|
render() {
|
||||||
return html``
|
return html``
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
public static override get styles(): CSSResultArray {
|
||||||
:host {
|
return [
|
||||||
position: absolute;
|
css`
|
||||||
bottom: 0;
|
:host {
|
||||||
width: 100vw;
|
position: absolute;
|
||||||
height: 100vh;
|
bottom: 0;
|
||||||
background-color: yellow;
|
width: 100%;
|
||||||
user-select: none;
|
height: 34px; /* 1px * 3/4 = 1pt */
|
||||||
}
|
background-color: yellow;
|
||||||
a {
|
user-select: none;
|
||||||
position: absolute;
|
}
|
||||||
width: 100%;
|
`,
|
||||||
height: 100%;
|
]
|
||||||
background-color: yellow;
|
}
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
import {html, css, LitElement, HTMLTemplateResult, nothing} from 'lit'
|
||||||
|
import {customElement, property, query, state} from 'lit/decorators.js'
|
||||||
|
|
||||||
|
export enum HeaderBarType {
|
||||||
|
ONLY_TIME = 'only-time',
|
||||||
|
DATE_TIME = 'date-time',
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('header-bar')
|
||||||
|
export class HeaderBar extends LitElement {
|
||||||
|
@state() updateTimeTimeout!: number
|
||||||
|
@state() updateDateTimeout!: number
|
||||||
|
@property({type: HeaderBarType}) type = ''
|
||||||
|
|
||||||
|
@query('#time') time!: HTMLDivElement
|
||||||
|
@query('#date') date!: HTMLDivElement
|
||||||
|
|
||||||
|
getTime(): HTMLTemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="time-outer">
|
||||||
|
<div id="time"></div>
|
||||||
|
<div class="time-icons">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeAndDate(): HTMLTemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="time-date-outer">
|
||||||
|
<div class="time-date">
|
||||||
|
<div id="time"></div>
|
||||||
|
<div id="date"></div>
|
||||||
|
</div>
|
||||||
|
<div class="time-date-icons">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
switch (this.type) {
|
||||||
|
case HeaderBarType.ONLY_TIME:
|
||||||
|
return this.getTime()
|
||||||
|
case HeaderBarType.DATE_TIME:
|
||||||
|
return this.getTimeAndDate()
|
||||||
|
default:
|
||||||
|
console.error('unhandled 【header-bar】 type')
|
||||||
|
return nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
--only-time-color-lm: #292929;
|
||||||
|
--only-time-color-dm: #f0f0f0;
|
||||||
|
--time-date-time-color-lm: #404040;
|
||||||
|
--time-date-date-color-lm: #000000;
|
||||||
|
--time-date-time-color-dm: #e0e0e0;
|
||||||
|
--time-date-date-color-dm: #e0e0e0;
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-outer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-outer > #time {
|
||||||
|
font-family: OPPOSans;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--only-time-color-lm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-icons > ::slotted(*) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date-outer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-family: 'OPPOSans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date > #time {
|
||||||
|
color: var(--time-date-time-color-lm);
|
||||||
|
}
|
||||||
|
|
||||||
|
#date {
|
||||||
|
position: relative;
|
||||||
|
opacity: 0.6;
|
||||||
|
color: var(--time-date-date-color-lm);
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.host([deep-mode]) .time-outer > #time {
|
||||||
|
color: var(--only-time-color-dm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.host([deep-mode]) #date {
|
||||||
|
color: var(--time-date-date-color-dm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
.time-outer > #time {
|
||||||
|
height: 52px;
|
||||||
|
line-height: 52px;
|
||||||
|
font-size: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-icons {
|
||||||
|
display: flex;
|
||||||
|
width: 136px;
|
||||||
|
position: relative;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-icons > ::slotted(:last-child) {
|
||||||
|
position: relative;
|
||||||
|
left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date {
|
||||||
|
line-height: 64px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date > #time {
|
||||||
|
font-size: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#date {
|
||||||
|
height: 34px;
|
||||||
|
line-height: 34px;
|
||||||
|
left: 22px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date-icons {
|
||||||
|
position: relative;
|
||||||
|
right: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
.time-outer > #time {
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-icons {
|
||||||
|
display: flex;
|
||||||
|
width: 68px;
|
||||||
|
position: relative;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-icons > ::slotted(:last-child) {
|
||||||
|
position: relative;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date {
|
||||||
|
line-height: 32px;
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date > #time {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#date {
|
||||||
|
height: 17px;
|
||||||
|
line-height: 17px;
|
||||||
|
left: 11px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-date-icons {
|
||||||
|
position: relative;
|
||||||
|
right: 5.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.autoUpdateDateTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdateDateTime() {
|
||||||
|
switch (this.type) {
|
||||||
|
case HeaderBarType.ONLY_TIME:
|
||||||
|
window.clearTimeout(this.updateTimeTimeout)
|
||||||
|
this.autoUpdateTime()
|
||||||
|
break
|
||||||
|
case HeaderBarType.DATE_TIME:
|
||||||
|
window.clearTimeout(this.updateDateTimeout)
|
||||||
|
window.clearTimeout(this.updateTimeTimeout)
|
||||||
|
this.autoUpdateDate()
|
||||||
|
this.autoUpdateTime()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdateTime() {
|
||||||
|
var d = new Date()
|
||||||
|
this.time.innerHTML = this.formatTime(d)
|
||||||
|
let self = this
|
||||||
|
var remainMillisecond = (60 - d.getSeconds()) * 1000
|
||||||
|
this.updateTimeTimeout = window.setTimeout(() => {
|
||||||
|
self.autoUpdateTime()
|
||||||
|
}, remainMillisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdateDate() {
|
||||||
|
var d = new Date()
|
||||||
|
this.date.innerHTML = this.formatDate(d) + ' ' + this.getWeekDay()
|
||||||
|
let remainMillisecond =
|
||||||
|
(24 - d.getHours()) * 3600 * 1000 -
|
||||||
|
d.getMinutes() * 60 * 1000 -
|
||||||
|
d.getMilliseconds()
|
||||||
|
let self = this
|
||||||
|
this.updateDateTimeout = window.setTimeout(function updateDateTimeout() {
|
||||||
|
self.autoUpdateDate()
|
||||||
|
}, remainMillisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
getWeekDay() {
|
||||||
|
var d = new Date()
|
||||||
|
let daynumber = d.getDay()
|
||||||
|
let day = ''
|
||||||
|
switch (daynumber) {
|
||||||
|
case 0:
|
||||||
|
day = '周日'
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
day = '周一'
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
day = '周二'
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
day = '周三'
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
day = '周四'
|
||||||
|
break
|
||||||
|
case 5:
|
||||||
|
day = '周五'
|
||||||
|
break
|
||||||
|
case 6:
|
||||||
|
day = '周六'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return day
|
||||||
|
}
|
||||||
|
|
||||||
|
formatDate(d: Date | string, iso?: boolean) {
|
||||||
|
if (d instanceof Date) {
|
||||||
|
if (iso) {
|
||||||
|
let date = d.toISOString() // Thu Oct 07 2021 07:45:18 GMT+0800
|
||||||
|
date = date.split('T')[0]
|
||||||
|
return date
|
||||||
|
} else {
|
||||||
|
return d.toLocaleString(navigator.languages as string[], {
|
||||||
|
// year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: '2-digit',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatTime(d: Date | string, iso?: boolean) {
|
||||||
|
if (d instanceof Date) {
|
||||||
|
if (iso) {
|
||||||
|
let time = d.toLocaleTimeString()
|
||||||
|
time = time.split(' ')[0]
|
||||||
|
if (time.indexOf(':') === 1) {
|
||||||
|
time = `0${time}`
|
||||||
|
}
|
||||||
|
return time
|
||||||
|
} else {
|
||||||
|
return d.toLocaleString(navigator.languages as string[], {
|
||||||
|
// hour12: (navigator as any).mozHour12,
|
||||||
|
hour12: false,
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (d.indexOf(':') == 1) {
|
||||||
|
d = '0' + d
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'header-bar': HeaderBar
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './header-bar.js'
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "@star-web-components/header-bar",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./index.js",
|
||||||
|
"module": "./index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./index": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./header-bar.js": {
|
||||||
|
"default": "./header-bar.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"rootDir": "../../"
|
||||||
|
},
|
||||||
|
"include": ["*.ts"]
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
import {html, css, LitElement, HTMLTemplateResult, nothing} from 'lit'
|
||||||
|
import {customElement, property, queryAssignedElements} from 'lit/decorators.js'
|
||||||
|
|
||||||
|
export enum IconControlBarGroupType {
|
||||||
|
BASE = 'base',
|
||||||
|
WITH_STATE = 'with-state',
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('icon-control-bar-group')
|
||||||
|
export class IconControlBarGroup extends LitElement {
|
||||||
|
@property({type: IconControlBarGroupType}) type = ''
|
||||||
|
@property({type: Number}) count = 2
|
||||||
|
@queryAssignedElements({flatten: true}) slotElements!: HTMLSlotElement[]
|
||||||
|
|
||||||
|
getbase(): HTMLTemplateResult {
|
||||||
|
const colorstyle =
|
||||||
|
this.count !== 4
|
||||||
|
? html`
|
||||||
|
<style>
|
||||||
|
.icon-only > div > ::slotted(*) {
|
||||||
|
width: calc(100% / ${this.count}) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`
|
||||||
|
: nothing
|
||||||
|
return html`
|
||||||
|
<div class="icon-only">
|
||||||
|
<div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
${colorstyle}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
getIconWithState(): HTMLTemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="icon-with-state">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.count = this.slotElements.length
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
switch (this.type) {
|
||||||
|
case IconControlBarGroupType.BASE:
|
||||||
|
return this.getbase()
|
||||||
|
case IconControlBarGroupType.WITH_STATE:
|
||||||
|
return this.getIconWithState()
|
||||||
|
default:
|
||||||
|
console.error('unhandled 【icon-control-bar-group】 type')
|
||||||
|
return nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
--background-lm: #ffffff;
|
||||||
|
--background-dm: #000000;
|
||||||
|
--opacity-lm: 0.75;
|
||||||
|
--opacity-dm: 0.25;
|
||||||
|
--line-border-lm: rgba(0, 0, 0, 0.07);
|
||||||
|
--line-border-dm: rgba(255, 255, 255, 0.07);
|
||||||
|
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
background: var(--background-lm);
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
opacity: var(--opacity-lm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-with-state {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: space-around;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-only {
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
background: inherit;
|
||||||
|
border-radius: inherit;
|
||||||
|
opacity: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-only > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-only > div > ::slotted(*) {
|
||||||
|
width: 25%;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-only > div > ::slotted(:first-child) {
|
||||||
|
border-left-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([deep-mode]) {
|
||||||
|
background: var(--background-dm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
:host {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-only > div > ::slotted(*) {
|
||||||
|
height: 30px;
|
||||||
|
border-left: 2px solid var(--line-border-lm);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([deep-mode]) .icon-only > div > ::slotted(*) {
|
||||||
|
border-left: 2px solid var(--line-border-dm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
:host {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-only > div > ::slotted(*) {
|
||||||
|
height: 15px;
|
||||||
|
border-left: 1px solid var(--line-border-lm);
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([deep-mode]) .icon-only > div > ::slotted(*) {
|
||||||
|
border-left: 1px solid var(--line-border-dm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'icon-control-bar-group': IconControlBarGroup
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './icon-control-bar-group.js'
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "@star-web-components/icon-control-bar-group",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./index.js",
|
||||||
|
"module": "./index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./index": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./icon-control-bar-group.js": {
|
||||||
|
"default": "./icon-control-bar-group.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"rootDir": "../../"
|
||||||
|
},
|
||||||
|
"include": ["*.ts"]
|
||||||
|
}
|
|
@ -0,0 +1,415 @@
|
||||||
|
import {html, css, LitElement, HTMLTemplateResult, nothing} from 'lit'
|
||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
eventOptions,
|
||||||
|
query,
|
||||||
|
state,
|
||||||
|
} from 'lit/decorators.js'
|
||||||
|
import {WebActivity} from './interface.js'
|
||||||
|
|
||||||
|
export enum IconControlBarType {
|
||||||
|
BASE = 'base',
|
||||||
|
BASE_WITHOUT_BORDER = 'base-without-border',
|
||||||
|
WITH_STATE = 'with-state',
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('icon-control-bar')
|
||||||
|
export class IconControlBar extends LitElement {
|
||||||
|
@property({type: IconControlBarType}) type = ''
|
||||||
|
@property({type: String}) icon = ''
|
||||||
|
@property({type: String}) stateDesc = ''
|
||||||
|
@property({type: String}) settingsKey = ''
|
||||||
|
@property({type: Boolean}) bgchange = false
|
||||||
|
@property({type: String}) id = ''
|
||||||
|
@property({type: Boolean}) active = false
|
||||||
|
@state({}) timer!: NodeJS.Timeout
|
||||||
|
|
||||||
|
@query('.more-info-icon') moreInfoIcon!: HTMLDivElement
|
||||||
|
@query('.icon-button') iconBtn!: HTMLDivElement
|
||||||
|
|
||||||
|
getbase(): HTMLTemplateResult | typeof nothing {
|
||||||
|
if (!this.icon) {
|
||||||
|
console.error('【icon-control-bar】缺少 icon 参数')
|
||||||
|
return nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconstyle = html`
|
||||||
|
<style>
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
:host {
|
||||||
|
width: 104px;
|
||||||
|
height: 104px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
:host {
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="icon-button icon-base with-border"
|
||||||
|
data-icon=${this.icon}
|
||||||
|
@click=${this.handleClick}
|
||||||
|
@touchstart=${this.handlePress}
|
||||||
|
@touchend=${this.handlePressEnd}
|
||||||
|
>
|
||||||
|
${iconstyle}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
getbaseWithOutBorder(): HTMLTemplateResult | typeof nothing {
|
||||||
|
if (!this.icon) {
|
||||||
|
console.error('【icon-control-bar】缺少 icon 参数')
|
||||||
|
return nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="icon-button icon-base without-border"
|
||||||
|
data-icon=${this.icon}
|
||||||
|
@click=${this.handleClick}
|
||||||
|
@touchstart=${this.handlePress}
|
||||||
|
@touchend=${this.handlePressEnd}
|
||||||
|
></div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
getwithstate(): HTMLTemplateResult | typeof nothing {
|
||||||
|
if (!this.icon) {
|
||||||
|
console.error('【icon-control-bar】缺少 icon 参数')
|
||||||
|
return nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconstyle = html`
|
||||||
|
<style>
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
:host {
|
||||||
|
width: 240px;
|
||||||
|
height: 108px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
:host {
|
||||||
|
width: 120px;
|
||||||
|
height: 54px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="icon-button icon-with-state with-border config-activity"
|
||||||
|
data-icon=${this.icon}
|
||||||
|
@click=${this.handleClick}
|
||||||
|
@touchstart=${this.handlePress}
|
||||||
|
@touchend=${this.handlePressEnd}
|
||||||
|
>
|
||||||
|
<p>${this.stateDesc}</p>
|
||||||
|
<div
|
||||||
|
class="more-info-icon"
|
||||||
|
@click=${this.handleInfo}
|
||||||
|
data-icon="expand-left"
|
||||||
|
></div>
|
||||||
|
${iconstyle}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
@eventOptions({passive: false})
|
||||||
|
handleClick(event: Event) {
|
||||||
|
let isActive = true // 闹铃
|
||||||
|
if (this.bgchange) {
|
||||||
|
let target = event.target as HTMLElement
|
||||||
|
if (target.nodeName == 'P') {
|
||||||
|
target = target.parentElement as HTMLElement
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.className == 'more-info-icon') {
|
||||||
|
target = target.parentElement as HTMLElement
|
||||||
|
}
|
||||||
|
|
||||||
|
isActive = target.classList.contains('active')
|
||||||
|
this.activeOrInactive(isActive, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (this.type == IconControlBarType.WITH_STATE) {
|
||||||
|
// if (target.className == "more-info-icon") {
|
||||||
|
// target = target.parentElement as HTMLElement;
|
||||||
|
// }
|
||||||
|
// if (target.classList.contains("active")) {
|
||||||
|
// this.moreInfoIcon.dataset.icon = "expand-left";
|
||||||
|
// } else {
|
||||||
|
// this.moreInfoIcon.dataset.icon = "";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
let self = this
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent('icon-control-bar-click', {
|
||||||
|
detail: {
|
||||||
|
id: self.id ? self.id : self.icon,
|
||||||
|
isActive: isActive,
|
||||||
|
target: self,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInfo() {
|
||||||
|
let self = this
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent('more-info-action', {detail: self.icon})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePress(event: Event) {
|
||||||
|
let target = event.target as HTMLElement
|
||||||
|
if (target.nodeName == 'P') {
|
||||||
|
target = target.parentElement as HTMLElement
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getAttribute('disabled') === 'true') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let section = this.icon
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
let activity = new WebActivity('moz_configure_window', {
|
||||||
|
data: {
|
||||||
|
target: 'device',
|
||||||
|
section: section,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
activity.start()
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
let self = this
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent('touch-start', {
|
||||||
|
detail: {
|
||||||
|
id: self.id ? self.id : self.icon,
|
||||||
|
target: self,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePressEnd() {
|
||||||
|
clearTimeout(this.timer)
|
||||||
|
}
|
||||||
|
|
||||||
|
activeOrInactive(isActive: boolean, element?: HTMLElement) {
|
||||||
|
if (element == null) {
|
||||||
|
isActive
|
||||||
|
? this.iconBtn.classList.remove('active')
|
||||||
|
: this.iconBtn.classList.add('active')
|
||||||
|
this.active = isActive
|
||||||
|
} else {
|
||||||
|
isActive
|
||||||
|
? element.classList.remove('active')
|
||||||
|
: element.classList.add('active')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): HTMLTemplateResult | typeof nothing {
|
||||||
|
switch (this.type) {
|
||||||
|
case IconControlBarType.BASE:
|
||||||
|
return this.getbase()
|
||||||
|
case IconControlBarType.BASE_WITHOUT_BORDER:
|
||||||
|
return this.getbaseWithOutBorder()
|
||||||
|
case IconControlBarType.WITH_STATE:
|
||||||
|
return this.getwithstate()
|
||||||
|
default:
|
||||||
|
console.error('unhandled 【icon-control-bar】 type')
|
||||||
|
return nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addClass(className: string[]) {
|
||||||
|
this.iconBtn.classList.add(...className)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeClass(className: string) {
|
||||||
|
this.iconBtn.classList.remove(className)
|
||||||
|
}
|
||||||
|
|
||||||
|
getClassList() {
|
||||||
|
return this.iconBtn.classList
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
--background-active: #1d98f5;
|
||||||
|
--background-lm: rgba(255, 255, 255, 0.35);
|
||||||
|
--background-dm: rgba(0, 0, 0, 0.15);
|
||||||
|
--text-color-lm: #4d4d4d;
|
||||||
|
--text-color-dm: #d1d1d1;
|
||||||
|
--text-color-active: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: var(--background-active) !important;
|
||||||
|
color: var(--text-color-active) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.with-border {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--background-lm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button::before {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
content: attr(data-icon);
|
||||||
|
font-family: gaia-icons;
|
||||||
|
font-style: normal;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-base {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-info-icon::after {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
content: attr(data-icon);
|
||||||
|
font-family: gaia-icons;
|
||||||
|
font-style: normal;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
position: relative;
|
||||||
|
color: var(--text-color-lm);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: OPPOSans;
|
||||||
|
font-style: normal;
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active > p {
|
||||||
|
color: var(--text-color-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([deep-mode]) .with-border {
|
||||||
|
background: var(--background-dm);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([deep-mode]) p {
|
||||||
|
position: relative;
|
||||||
|
color: var(--text-color-dm);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: OPPOSans;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
.with-border {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button::before {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-with-state::before {
|
||||||
|
position: relative;
|
||||||
|
left: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-info-icon {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
position: relative;
|
||||||
|
left: 29px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-info-icon::after {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
line-height: 8px;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
width: 50px;
|
||||||
|
height: 10px;
|
||||||
|
left: 17px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 10.5px;
|
||||||
|
line-height: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
.with-border {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button::before {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-with-state::before {
|
||||||
|
position: relative;
|
||||||
|
left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-info-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
position: relative;
|
||||||
|
left: 58px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-info-icon::after {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
width: 100px;
|
||||||
|
height: 20px;
|
||||||
|
left: 34px;
|
||||||
|
font-size: 21px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'icon-control-bar': IconControlBar
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './icon-control-bar.js'
|
|
@ -0,0 +1,9 @@
|
||||||
|
export interface WebActivity {
|
||||||
|
start(): Promise<any>
|
||||||
|
cancel(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export var WebActivity: {
|
||||||
|
prototype: WebActivity
|
||||||
|
new (name: string, data?: any): WebActivity
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "@star-web-components/icon-control-bar",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./index.js",
|
||||||
|
"module": "./index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./index": {
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./icon-control-bar.js": {
|
||||||
|
"default": "./icon-control-bar.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"rootDir": "../../"
|
||||||
|
},
|
||||||
|
"include": ["*.ts", "./interface.js"]
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import {LitElement, html, HTMLTemplateResult, CSSResultArray} from 'lit'
|
import {LitElement, html, HTMLTemplateResult, CSSResultArray} from 'lit'
|
||||||
import {customElement, property, queryAssignedElements} from 'lit/decorators.js'
|
import {customElement, property, queryAssignedElements} from 'lit/decorators.js'
|
||||||
import '../button/button.js'
|
import '../button/button.js'
|
||||||
|
import {OverlayStack} from '../overlay/overlay-stack'
|
||||||
import {sharedStyles} from './overflowmenustyle.js'
|
import {sharedStyles} from './overflowmenustyle.js'
|
||||||
|
|
||||||
@customElement('star-overflowmenu')
|
@customElement('star-overflowmenu')
|
||||||
|
@ -20,81 +21,36 @@ export class StarOverflowMenu extends LitElement {
|
||||||
@queryAssignedElements({flatten: true})
|
@queryAssignedElements({flatten: true})
|
||||||
_evenEl: any
|
_evenEl: any
|
||||||
|
|
||||||
_getElement() {
|
public overlayStack = new OverlayStack()
|
||||||
// 获取网页宽度用于判断菜单显示位置是否越界
|
|
||||||
const bodywidth = document.documentElement.clientWidth
|
public showmenu(e: Event) {
|
||||||
const bodyheight = document.documentElement.clientHeight
|
if (this.overlayStack.isOpen == false) {
|
||||||
// 获取菜单所在div,用于控制menu显示或隐藏,ts默认使用Element,需转换为HTMLElement
|
// 获取响应事件节点
|
||||||
const mu = this.renderRoot.querySelector('#menuitem') as HTMLElement
|
const targetNode = e.target as HTMLElement
|
||||||
// 获取star-button相对屏幕的位置
|
// 获取溢出菜单
|
||||||
const buttonposition = this.renderRoot
|
const originalNode = this
|
||||||
.querySelector('star-button')
|
// 获取overlay显示内容
|
||||||
?.getBoundingClientRect()
|
const overlaycontent = this.querySelector('#menu') as HTMLElement
|
||||||
// star-button的top、bottom、left及right值
|
// overlaycontent为空则退出
|
||||||
const buttontop = Number(buttonposition?.top)
|
if (!overlaycontent) return
|
||||||
const buttonbottom = Number(buttonposition?.bottom)
|
// 开启overlay
|
||||||
const buttonleft = Number(buttonposition?.left)
|
this.overlayStack.openOverlay(originalNode, targetNode, overlaycontent)
|
||||||
const buttonright = Number(buttonposition?.right)
|
|
||||||
// 通过“open”判断是否显示menu
|
|
||||||
if (this.open == true) {
|
|
||||||
for (var i = 0; i < this._evenEl.length; i++) {
|
|
||||||
const slotelement = this._evenEl[i]
|
|
||||||
// 设置div显示display状态
|
|
||||||
mu.style.display = 'block'
|
|
||||||
// 设置显示位置类型
|
|
||||||
// this._evenEl[i].style.position = 'fixed'
|
|
||||||
slotelement.style.position = 'relative'
|
|
||||||
this.open = false
|
|
||||||
// 获取溢出菜单width及height
|
|
||||||
const menuwidth = slotelement.getBoundingClientRect().width
|
|
||||||
const menuheight = slotelement.getBoundingClientRect().height
|
|
||||||
// 弹出菜单边界,rightline和bottomline分别为是否超过右侧和下侧显示区域
|
|
||||||
const rightline = buttonright + menuwidth > bodywidth ? true : false
|
|
||||||
const bottomline = buttonbottom + menuheight > bodyheight ? true : false
|
|
||||||
// 右下角边界
|
|
||||||
if (rightline && bottomline) {
|
|
||||||
slotelement.style.left =
|
|
||||||
-(menuwidth - (buttonright - buttonleft)) + 'px'
|
|
||||||
slotelement.style.bottom =
|
|
||||||
menuheight + (buttonbottom - buttontop) + 'px'
|
|
||||||
return
|
|
||||||
} else if (rightline) {
|
|
||||||
// 右侧边界
|
|
||||||
slotelement.style.right =
|
|
||||||
menuwidth - (buttonright - buttonleft) + 'px'
|
|
||||||
return
|
|
||||||
} else if (bottomline) {
|
|
||||||
// 下侧边界
|
|
||||||
slotelement.style.bottom =
|
|
||||||
menuheight + (buttonbottom - buttontop) + 'px'
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
// 正常情况
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < this._evenEl.length; i++) {
|
this.overlayStack.closeOverlay()
|
||||||
mu.style.display = 'none'
|
|
||||||
this.open = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(): void {
|
|
||||||
this._getElement()
|
|
||||||
}
|
|
||||||
|
|
||||||
getBaseMenu(): HTMLTemplateResult {
|
getBaseMenu(): HTMLTemplateResult {
|
||||||
|
// 为OverflowMenu绑定监听
|
||||||
|
this.addEventListener('test', () => {
|
||||||
|
this.overlayStack.closeOverlay()
|
||||||
|
})
|
||||||
return html`
|
return html`
|
||||||
<star-button
|
<star-button
|
||||||
type="icononly"
|
type="icononly"
|
||||||
icon=${this.icon}
|
id="openmenu"
|
||||||
@click=${this._getElement}
|
@click=${this.showmenu}
|
||||||
></star-button>
|
></star-button>
|
||||||
<div id="menuitem">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,19 @@ import {css, CSSResult} from 'lit'
|
||||||
|
|
||||||
export const sharedStyles: CSSResult = css`
|
export const sharedStyles: CSSResult = css`
|
||||||
:host {
|
:host {
|
||||||
width: auto;
|
width: inherit;
|
||||||
|
max-width: 300px;
|
||||||
display: block;
|
display: block;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menuitem {
|
|
||||||
width: auto;
|
|
||||||
position: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
star-button {
|
star-button {
|
||||||
display: block;
|
display: block;
|
||||||
width: 35px;
|
width: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::slotted(star-ul) {
|
::slotted(star-ul) {
|
||||||
|
display: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import {LitElement, html, TemplateResult} from 'lit'
|
import {LitElement, html, TemplateResult, CSSResultArray} from 'lit'
|
||||||
import {customElement, property} from 'lit/decorators.js'
|
import {customElement, property} from 'lit/decorators.js'
|
||||||
import {OverlayOpenDetail} from './overlay-types'
|
import {OverlayOpenDetail} from './overlay-types'
|
||||||
|
import {sharedStyles} from './overlaystyle'
|
||||||
|
|
||||||
@customElement('star-activeoverlay')
|
@customElement('star-activeoverlay')
|
||||||
export class ActiveOverlay extends LitElement {
|
export class ActiveOverlay extends LitElement {
|
||||||
|
public static override get styles(): CSSResultArray {
|
||||||
|
return [sharedStyles]
|
||||||
|
}
|
||||||
|
|
||||||
// 根节点(占位符所表示的元素的父节点)
|
// 根节点(占位符所表示的元素的父节点)
|
||||||
public root?: HTMLElement
|
public root?: HTMLElement
|
||||||
// 被点击的节点
|
// 被点击的节点
|
||||||
|
@ -14,9 +19,6 @@ export class ActiveOverlay extends LitElement {
|
||||||
@property({reflect: true})
|
@property({reflect: true})
|
||||||
public placement?: String
|
public placement?: String
|
||||||
|
|
||||||
offset!: number
|
|
||||||
skidding!: number
|
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
@ -28,12 +30,9 @@ export class ActiveOverlay extends LitElement {
|
||||||
// 初始化函数
|
// 初始化函数
|
||||||
private extractDetail(detail: OverlayOpenDetail): void {
|
private extractDetail(detail: OverlayOpenDetail): void {
|
||||||
this.placement = detail.placement
|
this.placement = detail.placement
|
||||||
this.offset = detail.offset
|
|
||||||
this.skidding = detail.skidding || 0
|
|
||||||
this.root = detail.root
|
this.root = detail.root
|
||||||
}
|
}
|
||||||
|
|
||||||
public stealOverlayContent(_: HTMLElement) {}
|
|
||||||
// 位置更新函数
|
// 位置更新函数
|
||||||
public updatePosition = () => {
|
public updatePosition = () => {
|
||||||
// 设置最大显示宽度和高度
|
// 设置最大显示宽度和高度
|
||||||
|
@ -47,44 +46,41 @@ export class ActiveOverlay extends LitElement {
|
||||||
const bodyheight = document.documentElement.clientHeight
|
const bodyheight = document.documentElement.clientHeight
|
||||||
// 被点击的节点
|
// 被点击的节点
|
||||||
const targetnode = this.targetNode as HTMLElement
|
const targetnode = this.targetNode as HTMLElement
|
||||||
// 获取被点击节点位置——用于越界判断和定位
|
// 获取被点击节点位置——用于越界判断和定位:分别表示上下左右四个方位
|
||||||
const targetNodePosition = targetnode.getBoundingClientRect()
|
const targetNodePosition = targetnode.getBoundingClientRect()
|
||||||
const targettop = Number(targetNodePosition?.top)
|
const targettop = Number(targetNodePosition?.top)
|
||||||
const targetbottom = Number(targetNodePosition?.bottom)
|
const targetbottom = Number(targetNodePosition?.bottom)
|
||||||
const targetleft = Number(targetNodePosition?.left)
|
const targetleft = Number(targetNodePosition?.left)
|
||||||
const targetright = Number(targetNodePosition?.right)
|
|
||||||
// 设置样式
|
// 设置样式
|
||||||
this.style.position = 'relative'
|
this.style.position = 'relative'
|
||||||
this.style.zIndex = '1'
|
this.style.zIndex = '10'
|
||||||
this.style.maxWidth = availableWidth + 'px'
|
this.style.maxWidth = availableWidth + 'px'
|
||||||
this.style.maxHeight = availableHeight + 'px'
|
this.style.maxHeight = availableHeight + 'px'
|
||||||
// 边界判断
|
// 边界判断:targetright指被点击的节点右边界,contentwidth表示将要显示的内容的宽度
|
||||||
const rightline = targetright + contentwidth > bodywidth ? true : false // 右侧越界条件
|
const rightline = targetleft + contentwidth > bodywidth ? true : false // 右侧越界条件
|
||||||
const bottomline =
|
const bottomline =
|
||||||
targetbottom + availableHeight > bodyheight ? true : false //下方越界条件
|
targetbottom + availableHeight > bodyheight ? true : false //下方越界条件
|
||||||
|
let left: number
|
||||||
|
let top: number
|
||||||
// 右下角边界
|
// 右下角边界
|
||||||
if (rightline && bottomline) {
|
if (rightline && bottomline) {
|
||||||
this.style.left =
|
left = targetleft - (contentwidth - targetnode.offsetWidth)
|
||||||
targetleft - (contentwidth - targetnode.offsetWidth) + 'px'
|
top = targettop - contentheight - targetnode.offsetHeight
|
||||||
this.style.top = targettop - targetnode.offsetHeight + 'px'
|
|
||||||
return
|
|
||||||
} else if (rightline) {
|
} else if (rightline) {
|
||||||
// 右侧边界
|
// 右侧边界
|
||||||
this.style.left =
|
left = targetleft - (contentwidth - targetnode.offsetWidth)
|
||||||
targetleft - (contentwidth - targetnode.offsetWidth) + 'px'
|
top = targettop + targetnode.offsetHeight
|
||||||
this.style.top = targettop + targetnode.offsetHeight + 'px'
|
|
||||||
return
|
|
||||||
} else if (bottomline) {
|
} else if (bottomline) {
|
||||||
// 下侧边界
|
// 下侧边界
|
||||||
this.style.left = targetleft + 'px'
|
left = targetleft
|
||||||
this.style.top = targettop - contentheight + 'px'
|
top = targettop - contentheight - targetnode.offsetHeight
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
// 正常情况
|
// 正常情况
|
||||||
this.style.left = targetleft + 'px'
|
left = targetleft
|
||||||
this.style.top = targettop + targetnode.offsetHeight + 'px'
|
top = targettop + targetnode.offsetHeight
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
this.style.left = left + 'px'
|
||||||
|
this.style.top = top + 'px'
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSlotChange(): void {
|
private onSlotChange(): void {
|
||||||
|
|
|
@ -13,18 +13,46 @@ export class OverlayStack {
|
||||||
const activeOverlay = ActiveOverlay.create(root, targetnode, content)
|
const activeOverlay = ActiveOverlay.create(root, targetnode, content)
|
||||||
// 开启状态
|
// 开启状态
|
||||||
this.isOpen = true
|
this.isOpen = true
|
||||||
// 为overlay添加显示内容
|
|
||||||
activeOverlay.appendChild(content as HTMLElement)
|
|
||||||
// 创建注释节点模板——用于替换要展示在overlay中的元素
|
// 创建注释节点模板——用于替换要展示在overlay中的元素
|
||||||
const placeholderTemplate: Comment = document.createComment(
|
const placeholderTemplate: Comment = document.createComment(
|
||||||
'placeholder for reparented element'
|
'placeholder for reparented element'
|
||||||
)
|
)
|
||||||
|
// 为overlay添加显示内容
|
||||||
|
activeOverlay.appendChild(content as HTMLElement)
|
||||||
// 占位
|
// 占位
|
||||||
activeOverlay.root?.appendChild(placeholderTemplate)
|
activeOverlay.root?.appendChild(placeholderTemplate)
|
||||||
// 将activeoverlay添加到body底部
|
// // 将activeoverlay添加到body底部显示div中
|
||||||
document.body.append(activeOverlay)
|
let showmenu = document.querySelector('#showmenu') as HTMLElement //后续关联模态时
|
||||||
|
if (showmenu == null) {
|
||||||
|
showmenu = document.createElement('div')
|
||||||
|
showmenu.setAttribute('id', 'showmenu')
|
||||||
|
Object.assign(showmenu.style, {
|
||||||
|
width: '100vw',
|
||||||
|
height: '100vh',
|
||||||
|
position: 'fixed',
|
||||||
|
backgroundColor: '#00000033',
|
||||||
|
justifycontent: 'center',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
showmenu.style.display = 'fixed'
|
||||||
|
}
|
||||||
|
// 为显示div添加overlay并将div添加到body中
|
||||||
|
showmenu.append(activeOverlay)
|
||||||
|
document.body.appendChild(showmenu)
|
||||||
// 将activeoverlay添加到已打开overlay数组中
|
// 将activeoverlay添加到已打开overlay数组中
|
||||||
this.overlays.push(activeOverlay)
|
this.overlays.push(activeOverlay)
|
||||||
|
// 为showmenu显示层添加监听
|
||||||
|
showmenu.addEventListener('click', function (e: Event) {
|
||||||
|
console.log(e.target)
|
||||||
|
if (e.target == this) {
|
||||||
|
root!.dispatchEvent(
|
||||||
|
new Event('test', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,8 +69,11 @@ export class OverlayStack {
|
||||||
const content = closeactiveoverlay.restoreContent as HTMLElement
|
const content = closeactiveoverlay.restoreContent as HTMLElement
|
||||||
// 替换子节点
|
// 替换子节点
|
||||||
srcroot?.replaceChild(content, placeholder)
|
srcroot?.replaceChild(content, placeholder)
|
||||||
|
const showmenu = document.querySelector('#showmenu') as HTMLElement
|
||||||
// 从body中移除activeoverlay节点
|
// 从body中移除activeoverlay节点
|
||||||
document.body.removeChild(closeactiveoverlay)
|
showmenu.removeChild(closeactiveoverlay)
|
||||||
|
// showmenu.style.display = 'none'
|
||||||
|
document.body.removeChild(showmenu)
|
||||||
this.isOpen = false
|
this.isOpen = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import {css, CSSResult} from 'lit'
|
||||||
|
export const sharedStyles: CSSResult = css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
`
|
|
@ -1,32 +1,36 @@
|
||||||
# 图案密码 pattern-view
|
# 图案密码 pattern-view
|
||||||
|
|
||||||
|
|
||||||
工作职责:
|
工作职责:
|
||||||
|
|
||||||
- 由九宫圆圈组成的图案密码
|
- 由九宫圆圈组成的图案密码
|
||||||
- 默认密码为 `012543` (滑动如下图)
|
- 默认密码为 `012543` (滑动如下图)
|
||||||
```
|
|
||||||
--- --- ---
|
|
||||||
| * | **** | * | **** | * |
|
|
||||||
--- --- ---
|
|
||||||
*
|
|
||||||
--- --- ---
|
|
||||||
| * | **** | * | **** | * |
|
|
||||||
--- --- ---
|
|
||||||
|
|
||||||
--- --- ---
|
|
||||||
| | | | | |
|
|
||||||
--- --- ---
|
|
||||||
```
|
```
|
||||||
|
--- --- ---
|
||||||
|
| * | **** | * | **** | * |
|
||||||
|
--- --- ---
|
||||||
|
*
|
||||||
|
--- --- ---
|
||||||
|
| * | **** | * | **** | * |
|
||||||
|
--- --- ---
|
||||||
|
|
||||||
|
--- --- ---
|
||||||
|
| | | | | |
|
||||||
|
--- --- ---
|
||||||
|
```
|
||||||
|
|
||||||
- 点击数字反馈,输入成功上滑,输入错误抖动反馈
|
- 点击数字反馈,输入成功上滑,输入错误抖动反馈
|
||||||
|
|
||||||
### 默认
|
### 默认
|
||||||
|
|
||||||
```
|
```
|
||||||
<star-pattern-view></star-pattern-view>
|
<star-pattern-view></star-pattern-view>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 距离顶部的位置 `topDir` 默认`217.5px`
|
### 距离顶部的位置 `topDir` 默认`217.5px`
|
||||||
|
|
||||||
```
|
```
|
||||||
<star-pattern-view topDir="300px"></star-pattern-view>
|
<star-pattern-view topDir="300px"></star-pattern-view>
|
||||||
<star-pattern-view topDir="-100px"></star-pattern-view>
|
<star-pattern-view topDir="-100px"></star-pattern-view>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,43 +2,49 @@ import {css} from 'lit'
|
||||||
|
|
||||||
export default css`
|
export default css`
|
||||||
:host {
|
:host {
|
||||||
:host {
|
width: inherit;
|
||||||
width: inherit;
|
display: block;
|
||||||
display: block;
|
margin: 20px auto;
|
||||||
margin: 20px auto;
|
max-width: 88vw;
|
||||||
max-width: 88vw;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
/* menu ul */
|
||||||
list-style: none;
|
:host(#iconmenu) {
|
||||||
flex-direction: column;
|
width: 320px;
|
||||||
display: flex;
|
display: block;
|
||||||
width: 100%;
|
margin: 0;
|
||||||
padding: 0;
|
border-radius: 16px;
|
||||||
margin: 10px auto;
|
}
|
||||||
max-width: 88vw;
|
|
||||||
background: var(--bg-ul, var(--pure-white));
|
|
||||||
box-shadow: rgb(64 60 67 / 16%) 1px 1px 5px 1px;
|
|
||||||
border-radius: inherit; /* 外部传入 */
|
|
||||||
}
|
|
||||||
|
|
||||||
header,
|
ul {
|
||||||
footer {
|
list-style: none;
|
||||||
color: #888;
|
flex-direction: column;
|
||||||
margin-left: 10px;
|
display: flex;
|
||||||
font-size: 12px;
|
width: 100%;
|
||||||
}
|
padding: 0;
|
||||||
|
margin: 10px auto;
|
||||||
|
max-width: 88vw;
|
||||||
|
background: var(--bg-ul, var(--pure-white));
|
||||||
|
box-shadow: rgb(64 60 67 / 16%) 1px 1px 5px 1px;
|
||||||
|
border-radius: inherit; /* 外部传入 */
|
||||||
|
}
|
||||||
|
|
||||||
star-ul-base {
|
header,
|
||||||
margin: 8px 0 12px 0; /* override child(star-ul-base) */
|
footer {
|
||||||
}
|
color: #888;
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
footer a {
|
star-ul-base {
|
||||||
text-decoration: unset;
|
margin: 8px 0 12px 0; /* override child(star-ul-base) */
|
||||||
}
|
}
|
||||||
|
|
||||||
footer a:visited {
|
footer a {
|
||||||
color: blue;
|
text-decoration: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer a:visited {
|
||||||
|
color: blue;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -16,6 +16,8 @@ import './components/toast/toast'
|
||||||
import './components/picker/picker'
|
import './components/picker/picker'
|
||||||
import './components/overflowmenu/overflowmenu'
|
import './components/overflowmenu/overflowmenu'
|
||||||
import './components/slider/slider'
|
import './components/slider/slider'
|
||||||
|
import './components/icon-control-bar/icon-control-bar'
|
||||||
|
import './components/icon-control-bar-group/icon-control-bar-group'
|
||||||
import './components/notification/notification'
|
import './components/notification/notification'
|
||||||
import './components/prompt/prompt'
|
import './components/prompt/prompt'
|
||||||
import './components/digicipher/digicipher'
|
import './components/digicipher/digicipher'
|
||||||
|
|
|
@ -81,16 +81,16 @@ class MyElement extends StarBaseElement {
|
||||||
|
|
||||||
<center><h2>FSM状态转移表</h2></center>
|
<center><h2>FSM状态转移表</h2></center>
|
||||||
|
|
||||||
| 当前状态 →<br>条件 ↓ | initialState | touchStartedState | touchesStartedState | holdState | panState | swipeState | pinchState | rotateState |
|
| 当前状态 →<br>条件 ↓ | initialState | touchStartedState | touchesStartedState | holdState | panState | swipeState | transformstate |
|
||||||
| ---------------------------------- | ----------------- | ------------------- | ------------------- | ------------ | ---------- | ------------ | ------------ | ------------ |
|
| ---------------------------------- | ----------------- | ------------------- | ---------------------- | ------------ | ---------- | ------------ | -------------- |
|
||||||
| 收到 touchstart | touchStartedState |
|
| 收到 touchstart | touchStartedState |
|
||||||
| 收到 touchstart 且触摸点=1 | | initialState |
|
| 收到 touchstart 且触摸点=1 | | initialState |
|
||||||
| 收到 touchstart 且触摸点>1 | | touchesStartedState |
|
| 收到 touchstart 且触摸点>1 | | touchesStartedState |
|
||||||
| 收到 touchstart 且判定进行捏 | | | pinchState |
|
| 收到 touchstart 且判定进行捏 | | | transformstate(pinch) |
|
||||||
| 收到 touchstart 且判定进行旋转 | | | rotateState |
|
| 收到 touchstart 且判定进行旋转 | | | transformstate(rotate) |
|
||||||
| 收到 touchstart 且判定进行平移 | | | panState |
|
| 收到 touchstart 且判定进行平移 | | | panState |
|
||||||
| 收到 touchmove 且移动差值>平移阈值 | | panState |
|
| 收到 touchmove 且移动差值>平移阈值 | | panState |
|
||||||
| 收到 touchend | | initialState | | initialState | swipeState | initialState | initialState | initialState |
|
| 收到 touchend | | initialState | | initialState | swipeState | initialState | initialState |
|
||||||
| hold 超时完成 | | holdState |
|
| hold 超时完成 | | holdState |
|
||||||
|
|
||||||
## 手势事件(Gesture Events)
|
## 手势事件(Gesture Events)
|
||||||
|
@ -224,3 +224,27 @@ Interface RotateEvent {
|
||||||
// },
|
// },
|
||||||
// })
|
// })
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
|
||||||
|
```js
|
||||||
|
/**
|
||||||
|
* Chrome 上调用 e.preventDedault() 可阻止右键菜单弹出,同时不会影响
|
||||||
|
* 正常 touch 事件的分发。
|
||||||
|
*
|
||||||
|
* Firefox 上调用 e.preventDedault() 可阻止右键菜单弹出,但会中断 touch 事件,
|
||||||
|
* 表现为:长按(touchstart),中断,手抬起再按(touchmove)
|
||||||
|
*****
|
||||||
|
* 测试长按事件时,请使用终端设备或Chrome
|
||||||
|
*/
|
||||||
|
this.addEventListener(
|
||||||
|
'contextmenu',
|
||||||
|
(e) => {
|
||||||
|
e.stopImmediatePropagation()
|
||||||
|
if (navigator.vendor === 'Google Inc.') {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
1. 单击位移阈值 10px 或热区以内
|
||||||
|
2. 双击间隔时间小于 300ms,两次点击的手指位移阈值 10px
|
||||||
|
3. 长按的位移阈值 10px
|
||||||
|
4. 拖放判定条件:手指进入长按状态后,手指位移阈值 10px
|
||||||
|
5. 滑动、轻扫判定条件:手指位移阈值 20px
|
|
@ -99,8 +99,7 @@ export interface SwipeEvent {
|
||||||
readonly startTouch?: Touch
|
readonly startTouch?: Touch
|
||||||
readonly endTouch?: Touch
|
readonly endTouch?: Touch
|
||||||
readonly velocity: number
|
readonly velocity: number
|
||||||
// readonly direction: number
|
readonly direction?: PanOrSwipeDirection // number
|
||||||
readonly direction?: PanOrSwipeDirection
|
|
||||||
readonly angle?: number
|
readonly angle?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,8 +221,6 @@ export type GestureState =
|
||||||
| 'panState'
|
| 'panState'
|
||||||
| 'holdState'
|
| 'holdState'
|
||||||
| 'swipeState'
|
| 'swipeState'
|
||||||
| 'rotateState'
|
|
||||||
| 'pinchState'
|
|
||||||
| 'transformState'
|
| 'transformState'
|
||||||
|
|
||||||
type PickUP<T, K extends keyof T> = T[K]
|
type PickUP<T, K extends keyof T> = T[K]
|
||||||
|
@ -733,31 +730,11 @@ export default class GestureDector {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* * Pinch:(pointers:2,threshold:0)
|
* 变换状态
|
||||||
* - pinchstart
|
|
||||||
* - pinchmove
|
|
||||||
* - pinchend
|
|
||||||
* - pinchcancel
|
|
||||||
* - pinch(include below all)
|
|
||||||
* - pinchin
|
|
||||||
* - pinchout
|
|
||||||
*/
|
|
||||||
private pinchState: FSMGestureState = {
|
|
||||||
name: 'pinchState',
|
|
||||||
init: (evt, touch) => {
|
|
||||||
this.pinchState.touchstart?.(evt, touch)
|
|
||||||
},
|
|
||||||
touchstart: (_, __) => {
|
|
||||||
this.emitEvent('pinchstart')
|
|
||||||
},
|
|
||||||
touchmove: null,
|
|
||||||
touchend: null,
|
|
||||||
touchcancel: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 针对双指手势的变换状态
|
|
||||||
*
|
*
|
||||||
|
* 针对双指手势,专用于处理双指捏合和旋转动作
|
||||||
|
*
|
||||||
|
* 囊括:rotate 和 pinch
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private transformState: FSMGestureState = {
|
private transformState: FSMGestureState = {
|
||||||
|
@ -835,8 +812,9 @@ export default class GestureDector {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.scaled === true) {
|
if (this.scaled === true) {
|
||||||
this.switchTo(this.pinchState, evt, touch)
|
// ..
|
||||||
} else if (this.rotated === true) {
|
} else if (this.rotated === true) {
|
||||||
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.scaled === true || this.rotated === true) {
|
if (this.scaled === true || this.rotated === true) {
|
||||||
|
@ -1124,6 +1102,7 @@ export default class GestureDector {
|
||||||
// 清理资源
|
// 清理资源
|
||||||
this.lastPanTouchID = -1
|
this.lastPanTouchID = -1
|
||||||
|
|
||||||
|
// 如果检测到是第2指,提前退出
|
||||||
if (isSecondFinger) {
|
if (isSecondFinger) {
|
||||||
debugInfo('swipe::isSecondFinger', this.vc)
|
debugInfo('swipe::isSecondFinger', this.vc)
|
||||||
if (
|
if (
|
||||||
|
@ -1201,15 +1180,6 @@ export default class GestureDector {
|
||||||
touchcancel: null,
|
touchcancel: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
// private rotateState: FSMGestureState = {
|
|
||||||
// name: 'rotateState',
|
|
||||||
// init: () => {},
|
|
||||||
// touchstart: null,
|
|
||||||
// touchmove: null,
|
|
||||||
// touchend: null,
|
|
||||||
// touchcancel: null,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: 优化调用处,添加缓存处理
|
// TODO: 优化调用处,添加缓存处理
|
||||||
private getSeriesEvent<T extends SeriesOfEventsType>(
|
private getSeriesEvent<T extends SeriesOfEventsType>(
|
||||||
type: T
|
type: T
|
||||||
|
|
|
@ -13,6 +13,9 @@ export class PanelActiveOverlay extends LitElement {
|
||||||
public overlayStack = new OverlayStack()
|
public overlayStack = new OverlayStack()
|
||||||
|
|
||||||
public test(e: Event) {
|
public test(e: Event) {
|
||||||
|
this.addEventListener('test', () => {
|
||||||
|
this.overlayStack.closeOverlay()
|
||||||
|
})
|
||||||
if (this.overlayStack.isOpen == false) {
|
if (this.overlayStack.isOpen == false) {
|
||||||
// 获取被点击节点
|
// 获取被点击节点
|
||||||
const targetNode = e.target as HTMLElement
|
const targetNode = e.target as HTMLElement
|
||||||
|
@ -40,7 +43,7 @@ export class PanelActiveOverlay extends LitElement {
|
||||||
<button>这是要显示的内容</button>
|
<button>这是要显示的内容</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="position: fixed; top: 98%;">
|
<div style="position: fixed; top: 20%;">
|
||||||
<button id="open" @click="${this.test}">点击显示overlay层</button>
|
<button id="open" @click="${this.test}">点击显示overlay层</button>
|
||||||
<button>这是要显示的内容</button>
|
<button>这是要显示的内容</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,7 +53,7 @@ export class PanelActiveOverlay extends LitElement {
|
||||||
<button>这是要显示的内容</button>
|
<button>这是要显示的内容</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="position: fixed; top: 98%; left: 90%;">
|
<div style="position: fixed; top: 80%; left: 90%;">
|
||||||
<button id="open" @click="${this.test}">点击显示overlay层</button>
|
<button id="open" @click="${this.test}">点击显示overlay层</button>
|
||||||
<button>这是要显示的内容</button>
|
<button>这是要显示的内容</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
import {html, LitElement} from 'lit'
|
||||||
|
import {customElement, query} from 'lit/decorators.js'
|
||||||
|
import {IconControlBarType} from '../../../components/icon-control-bar/icon-control-bar'
|
||||||
|
import '../../../components/icon-control-bar/icon-control-bar'
|
||||||
|
import {IconControlBarGroupType} from '../../../components/icon-control-bar-group/icon-control-bar-group'
|
||||||
|
import {UlType} from '../../../components/ul/ul'
|
||||||
|
import {HeaderBarType} from '../../../components/header-bar/header-bar'
|
||||||
|
import '../../../components/icon-control-bar-group/icon-control-bar-group'
|
||||||
|
import '../../../components/ul/ul'
|
||||||
|
import '../../../components/header-bar/header-bar'
|
||||||
|
@customElement('panel-control-center')
|
||||||
|
export class PanelControlCenter extends LitElement {
|
||||||
|
@query('icon-control-bar[icon="wifi-4"]') wifiIcon!: LitElement
|
||||||
|
@query('icon-control-bar[icon="crop"]') cropBtn!: LitElement
|
||||||
|
@query('icon-control-bar[icon="brightness"]') brightnessBtn!: LitElement
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<star-ul type=${UlType.ONLY_HEADER} title="icon-control-bar">
|
||||||
|
<p>带状态的图标,点击有背景色变化,绑定settingsObserver监测值</p>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.WITH_STATE}
|
||||||
|
icon="wifi-4"
|
||||||
|
bgchange="true"
|
||||||
|
settingsKey="wifi.enabled"
|
||||||
|
stateDesc="fgfgfhhhh"
|
||||||
|
@click=${this}
|
||||||
|
></icon-control-bar>
|
||||||
|
<br />
|
||||||
|
<p>基本图标</p>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE}
|
||||||
|
icon="crop"
|
||||||
|
@click=${this}
|
||||||
|
></icon-control-bar>
|
||||||
|
<br />
|
||||||
|
<p>基本图标,点击有背景色变化</p>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE}
|
||||||
|
icon="brightness"
|
||||||
|
bgchange="true"
|
||||||
|
settingsKey="screen.automatic-brightness"
|
||||||
|
@click=${this}
|
||||||
|
></icon-control-bar>
|
||||||
|
<br />
|
||||||
|
<p>基本图标,不带边框</p>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="settings"
|
||||||
|
></icon-control-bar>
|
||||||
|
</star-ul>
|
||||||
|
|
||||||
|
<star-ul type=${UlType.ONLY_HEADER} title="icon-control-bar-group">
|
||||||
|
<p>一行带状态的图标</p>
|
||||||
|
<icon-control-bar-group type=${IconControlBarGroupType.WITH_STATE}>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.WITH_STATE}
|
||||||
|
icon="wifi-4"
|
||||||
|
bgchange="true"
|
||||||
|
settingsKey="wifi.enabled"
|
||||||
|
stateDesc=""
|
||||||
|
></icon-control-bar>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.WITH_STATE}
|
||||||
|
icon="bluetooth"
|
||||||
|
bgchange="true"
|
||||||
|
settingsKey="bluetooth.enabled"
|
||||||
|
></icon-control-bar>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.WITH_STATE}
|
||||||
|
icon="data"
|
||||||
|
bgchange="true"
|
||||||
|
settingsKey="a.enabled"
|
||||||
|
></icon-control-bar>
|
||||||
|
</icon-control-bar-group>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<p>一个box框,框内是由短竖线分隔开的基本图标</p>
|
||||||
|
<icon-control-bar-group type=${IconControlBarGroupType.BASE} count="4">
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="crop"
|
||||||
|
></icon-control-bar>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="video"
|
||||||
|
></icon-control-bar>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="camera"
|
||||||
|
></icon-control-bar>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="o"
|
||||||
|
></icon-control-bar>
|
||||||
|
</icon-control-bar-group>
|
||||||
|
<br />
|
||||||
|
</star-ul>
|
||||||
|
|
||||||
|
<star-ul type=${UlType.ONLY_HEADER} title="header-bar">
|
||||||
|
<p>header带时间</p>
|
||||||
|
<header-bar type=${HeaderBarType.ONLY_TIME}>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="compose"
|
||||||
|
></icon-control-bar>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="settings"
|
||||||
|
></icon-control-bar>
|
||||||
|
</header-bar>
|
||||||
|
<br />
|
||||||
|
<p>header带时间/日期/星期</p>
|
||||||
|
<header-bar type=${HeaderBarType.DATE_TIME}>
|
||||||
|
<icon-control-bar
|
||||||
|
type=${IconControlBarType.BASE_WITHOUT_BORDER}
|
||||||
|
icon="settings"
|
||||||
|
></icon-control-bar>
|
||||||
|
</header-bar>
|
||||||
|
<br />
|
||||||
|
</star-ul>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'panel-control-center': PanelControlCenter
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +1,35 @@
|
||||||
import {html, css, LitElement} from 'lit'
|
import {html, css, LitElement} from 'lit'
|
||||||
import {customElement, query, state} from 'lit/decorators.js'
|
import {customElement, query} from 'lit/decorators.js'
|
||||||
import '../../../components/grabber/home-bar-indicator'
|
import '../../../components/grabber/home-bar-indicator'
|
||||||
import {HomeBarIndicator} from '../../../components/grabber/home-bar-indicator'
|
import {HomeBarIndicator} from '../../../components/grabber/home-bar-indicator'
|
||||||
|
|
||||||
@customElement('panel-home-indicator')
|
@customElement('panel-home-indicator')
|
||||||
export class PanelHomeIndicator extends LitElement {
|
export class PanelHomeIndicator extends LitElement {
|
||||||
@query('home-indicator', true) homeIndicator!: HomeBarIndicator
|
@query('home-indicator', true) homeIndicator!: HomeBarIndicator
|
||||||
// @property({type: String}) status = this.homeIndicator?.status
|
|
||||||
foo = ''
|
|
||||||
|
|
||||||
@state()
|
handleEvent(e: Event) {
|
||||||
bar = ''
|
switch (e.type) {
|
||||||
|
case 'panmove':
|
||||||
attributeChangedCallback(name: string, _old: string, value: string): void {
|
console.log(e.type)
|
||||||
super.attributeChangedCallback(name, _old, value)
|
break
|
||||||
console.log(name)
|
default:
|
||||||
}
|
console.log(e.type)
|
||||||
|
}
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
// setInterval(() => {
|
|
||||||
// console.log(this.homeIndicator.movePoint)
|
|
||||||
// }, 30)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<home-bar-indicator></home-bar-indicator>
|
<home-bar-indicator
|
||||||
|
@swipeup=${this}
|
||||||
|
@swipedown=${this}
|
||||||
|
@swiperight=${this}
|
||||||
|
@swipeleft=${this}
|
||||||
|
@panup=${this}
|
||||||
|
@pandown=${this}
|
||||||
|
@panright=${this}
|
||||||
|
@panleft=${this}
|
||||||
|
@panmove=${this}
|
||||||
|
></home-bar-indicator>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import {html, LitElement, css} from 'lit'
|
import {html, LitElement, css, CSSResultArray} from 'lit'
|
||||||
import {customElement, property} from 'lit/decorators.js'
|
import {customElement, property} from 'lit/decorators.js'
|
||||||
import '../../../components/button/button'
|
import '../../../components/button/button'
|
||||||
import '../../../components/ul/ul'
|
import '../../../components/ul/ul'
|
||||||
import '../../../components/li//li'
|
import '../../../components/li//li'
|
||||||
import {UlType} from '../../../components/ul/ul'
|
import {baseStyles} from '../../../components/base/base-style'
|
||||||
import {LiType} from '../../../components/li//li'
|
|
||||||
|
|
||||||
@customElement('panel-overflowmenu')
|
@customElement('panel-overflowmenu')
|
||||||
export class PanelOverflowMenu extends LitElement {
|
export class PanelOverflowMenu extends LitElement {
|
||||||
|
@ -12,134 +11,214 @@ export class PanelOverflowMenu extends LitElement {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
// state用于记录展开的菜单数量,用以菜单展开状态互斥的判断
|
|
||||||
@property({type: Number}) state = 0
|
|
||||||
|
|
||||||
// 关闭菜单
|
|
||||||
closeoverflowmenu(e: any) {
|
|
||||||
// 获取点击事件所在的标签名字
|
|
||||||
const tagName = e.target.tagName.toLowerCase()
|
|
||||||
// 判断是否点击的star-overflowmenu标签
|
|
||||||
if (tagName == 'star-overflowmenu') {
|
|
||||||
this.state++
|
|
||||||
}
|
|
||||||
// 如果点在空白处则关闭菜单
|
|
||||||
if (tagName != 'star-overflowmenu') {
|
|
||||||
// 获取所有的star-overflowmenu
|
|
||||||
var menulist = this.shadowRoot!.querySelectorAll('star-overflowmenu')
|
|
||||||
for (var i = 0; i < menulist.length; i++) {
|
|
||||||
menulist[i].open = true
|
|
||||||
var menu = menulist[i].renderRoot.querySelector(
|
|
||||||
'#menuitem'
|
|
||||||
) as HTMLElement
|
|
||||||
menu.style.display = 'none'
|
|
||||||
this.state = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 通过state判断是否已有展开的菜单,若已有则关闭菜单
|
|
||||||
if (this.state > 1) {
|
|
||||||
var menulist = this.shadowRoot!.querySelectorAll('star-overflowmenu')
|
|
||||||
for (var i = 0; i < menulist.length; i++) {
|
|
||||||
menulist[i].open = true
|
|
||||||
var menu = menulist[i].renderRoot.querySelector(
|
|
||||||
'#menuitem'
|
|
||||||
) as HTMLElement
|
|
||||||
menu.style.display = 'none'
|
|
||||||
this.state = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback(): void {
|
|
||||||
super.connectedCallback()
|
|
||||||
// 添加click事件
|
|
||||||
this.shadowRoot?.addEventListener('click', (e) => this.closeoverflowmenu(e))
|
|
||||||
}
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<star-overflowmenu icon="more">
|
||||||
<star-overflowmenu icon="more">
|
<div id="menu">
|
||||||
<star-ul type=${UlType.BASE}>
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
<star-li
|
<star-button
|
||||||
type=${LiType.ONLY_EDIT}
|
type="iconlabel"
|
||||||
label="星光麒麟"
|
icon="info"
|
||||||
default="星光麒麟"
|
label="应用详情"
|
||||||
></star-li>
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="delete"
|
||||||
|
label="卸载"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="download-circle"
|
||||||
|
label="固定至Dock"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
</star-ul>
|
</star-ul>
|
||||||
</star-overflowmenu>
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="keyboard-circle"
|
||||||
|
label="编辑屏幕"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
</star-ul>
|
||||||
|
</div>
|
||||||
|
</star-overflowmenu>
|
||||||
|
|
||||||
<star-overflowmenu
|
<star-overflowmenu
|
||||||
icon="more"
|
icon="more"
|
||||||
style="position: fixed; top: 50%; left: 50%;"
|
style="position: fixed; top: 50%; left: 50%;"
|
||||||
>
|
>
|
||||||
<star-ul type=${UlType.BASE}>
|
<div id="menu">
|
||||||
<star-li
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
type=${LiType.ONLY_EDIT}
|
<star-button
|
||||||
label="星光麒麟"
|
type="iconlabel"
|
||||||
default="星光麒麟"
|
icon="info"
|
||||||
></star-li>
|
label="应用详情"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="delete"
|
||||||
|
label="卸载"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="download-circle"
|
||||||
|
label="固定至Dock"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
</star-ul>
|
</star-ul>
|
||||||
</star-overflowmenu>
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="keyboard-circle"
|
||||||
|
label="编辑屏幕"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
</star-ul>
|
||||||
|
</div>
|
||||||
|
</star-overflowmenu>
|
||||||
|
|
||||||
<star-overflowmenu
|
<star-overflowmenu
|
||||||
icon="more"
|
icon="more"
|
||||||
style="position: fixed; top: 0; right: 0;"
|
style="position: fixed; top: 0; left: 90%;"
|
||||||
>
|
>
|
||||||
<star-ul type=${UlType.BASE}>
|
<div id="menu">
|
||||||
<star-li
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
type=${LiType.ONLY_EDIT}
|
<star-button
|
||||||
label="星光麒麟"
|
type="iconlabel"
|
||||||
default="星光麒麟"
|
icon="info"
|
||||||
></star-li>
|
label="应用详情"
|
||||||
<star-li
|
id="menuitem"
|
||||||
type=${LiType.ONLY_EDIT}
|
></star-button>
|
||||||
label="星光麒麟"
|
<star-button
|
||||||
default="星光麒麟"
|
type="iconlabel"
|
||||||
></star-li>
|
icon="delete"
|
||||||
|
label="卸载"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="download-circle"
|
||||||
|
label="固定至Dock"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
</star-ul>
|
</star-ul>
|
||||||
</star-overflowmenu>
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="keyboard-circle"
|
||||||
|
label="编辑屏幕"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
</star-ul>
|
||||||
|
</div>
|
||||||
|
</star-overflowmenu>
|
||||||
|
|
||||||
<star-overflowmenu
|
<star-overflowmenu
|
||||||
icon="more"
|
icon="more"
|
||||||
style="position: fixed; bottom: 0; left: 0;"
|
style="position: fixed; top: 90%; left: 0;"
|
||||||
>
|
>
|
||||||
<star-ul
|
<div id="menu">
|
||||||
type=${UlType.ONLY_HEADER}
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
title="头部有文字"
|
<star-button
|
||||||
text="尾部有文字"
|
type="iconlabel"
|
||||||
>
|
icon="info"
|
||||||
<star-li type=${LiType.ONLY_LABEL} label="素条目"></star-li>
|
label="应用详情"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="delete"
|
||||||
|
label="卸载"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="download-circle"
|
||||||
|
label="固定至Dock"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
</star-ul>
|
</star-ul>
|
||||||
</star-overflowmenu>
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="keyboard-circle"
|
||||||
|
label="编辑屏幕"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
</star-ul>
|
||||||
|
</div>
|
||||||
|
</star-overflowmenu>
|
||||||
|
|
||||||
<star-overflowmenu
|
<star-overflowmenu
|
||||||
icon="more"
|
icon="more"
|
||||||
style="position: fixed; bottom: 0; right: 0;"
|
style="position: fixed; top: 90%; left: 90%;"
|
||||||
>
|
>
|
||||||
<star-ul
|
<div id="menu">
|
||||||
type=${UlType.ONLY_HEADER}
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
title="头部有文字"
|
<star-button
|
||||||
text="尾部有文字"
|
type="iconlabel"
|
||||||
>
|
icon="info"
|
||||||
<star-li type=${LiType.ONLY_LABEL} label="素条目"></star-li>
|
label="应用详情"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="delete"
|
||||||
|
label="卸载"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="download-circle"
|
||||||
|
label="固定至Dock"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
</star-ul>
|
</star-ul>
|
||||||
</star-overflowmenu>
|
<star-ul type="base" id="iconmenu" background="var(--bg-dialog)">
|
||||||
</div>
|
<star-button
|
||||||
|
type="iconlabel"
|
||||||
|
icon="keyboard-circle"
|
||||||
|
label="编辑屏幕"
|
||||||
|
id="menuitem"
|
||||||
|
></star-button>
|
||||||
|
</star-ul>
|
||||||
|
</div>
|
||||||
|
</star-overflowmenu>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
static get styles(): CSSResultArray {
|
||||||
:host {
|
return [
|
||||||
display: block;
|
baseStyles,
|
||||||
width: 100vw;
|
css`
|
||||||
height: 100vh;
|
star-ul#dialog {
|
||||||
}
|
border-radius: var(--base-dialog-radius);
|
||||||
div {
|
}
|
||||||
display: block;
|
star-ul#menu {
|
||||||
width: 100vw;
|
max-width: 200px;
|
||||||
height: 100vh;
|
border-radius: var(--base-menu-radius);
|
||||||
}
|
}
|
||||||
`
|
star-ul#iconmenu {
|
||||||
|
max-width: 150px;
|
||||||
|
border-radius: var(--base-menu-radius);
|
||||||
|
}
|
||||||
|
star-ul#dialog {
|
||||||
|
border-radius: var(--base-menu-radius);
|
||||||
|
}
|
||||||
|
star-li span.split {
|
||||||
|
color: var(--split-line);
|
||||||
|
}
|
||||||
|
::slotted(div) {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import './pattern-view/pattern-view'
|
||||||
import './container/homescreen-container'
|
import './container/homescreen-container'
|
||||||
import './toast/toast'
|
import './toast/toast'
|
||||||
import './picker/picker'
|
import './picker/picker'
|
||||||
|
import './control-center/control-center'
|
||||||
import './notification/notification'
|
import './notification/notification'
|
||||||
|
|
||||||
import './switch/switch'
|
import './switch/switch'
|
||||||
|
@ -201,6 +202,14 @@ export class PanelRoot extends LitElement {
|
||||||
href="#overflowmenu"
|
href="#overflowmenu"
|
||||||
></star-li>
|
></star-li>
|
||||||
<hr />
|
<hr />
|
||||||
|
<star-li
|
||||||
|
type=${LiType.ICON_LABEL}
|
||||||
|
label="control-center"
|
||||||
|
icon="play-circle"
|
||||||
|
iconcolor="blue"
|
||||||
|
href="#control-center"
|
||||||
|
></star-li>
|
||||||
|
<hr />
|
||||||
<star-li
|
<star-li
|
||||||
type=${LiType.ICON_LABEL}
|
type=${LiType.ICON_LABEL}
|
||||||
label="notification"
|
label="notification"
|
||||||
|
@ -241,14 +250,6 @@ export class PanelRoot extends LitElement {
|
||||||
href="#indicators"
|
href="#indicators"
|
||||||
></star-li>
|
></star-li>
|
||||||
<hr />
|
<hr />
|
||||||
<star-li
|
|
||||||
type=${LiType.ICON_LABEL}
|
|
||||||
label="home指示器"
|
|
||||||
icon="accessibility"
|
|
||||||
iconcolor="blue"
|
|
||||||
href="#home-indicator"
|
|
||||||
></star-li>
|
|
||||||
<hr />
|
|
||||||
<star-li
|
<star-li
|
||||||
type=${LiType.ICON_LABEL}
|
type=${LiType.ICON_LABEL}
|
||||||
label="毛玻璃"
|
label="毛玻璃"
|
||||||
|
@ -299,7 +300,7 @@ export class PanelRoot extends LitElement {
|
||||||
></star-li>
|
></star-li>
|
||||||
</star-ul>
|
</star-ul>
|
||||||
|
|
||||||
<star-ul type=${UlType.ONLY_HEADER} title="手势框架">
|
<star-ul type=${UlType.ONLY_HEADER} title="手势">
|
||||||
<star-li
|
<star-li
|
||||||
type=${LiType.ICON_LABEL}
|
type=${LiType.ICON_LABEL}
|
||||||
label="手势框架"
|
label="手势框架"
|
||||||
|
@ -307,6 +308,14 @@ export class PanelRoot extends LitElement {
|
||||||
iconcolor="red"
|
iconcolor="red"
|
||||||
href="#gesture"
|
href="#gesture"
|
||||||
></star-li>
|
></star-li>
|
||||||
|
<hr />
|
||||||
|
<star-li
|
||||||
|
type=${LiType.ICON_LABEL}
|
||||||
|
label="home指示器"
|
||||||
|
icon="accessibility"
|
||||||
|
iconcolor="blue"
|
||||||
|
href="#home-indicator"
|
||||||
|
></star-li>
|
||||||
</star-ul>
|
</star-ul>
|
||||||
|
|
||||||
<star-ul type=${UlType.BASE}>
|
<star-ul type=${UlType.BASE}>
|
||||||
|
|
Loading…
Reference in New Issue