Merge branch 'master' into feature-component-dock

This commit is contained in:
luojiahao 2022-09-19 10:21:36 +08:00
commit f4a65c3a7e
78 changed files with 5290 additions and 818 deletions

View File

@ -17,6 +17,7 @@
- optimize strategy of container
- add delay exchange feature
- fix bugs of container exchange stradegy
- add confirm
- add dock
- add function for dragging icon into dock
- add function for dragging icon into container

View File

@ -0,0 +1,37 @@
import {LitElement, ReactiveElement} from 'lit'
import GestureDector, {GestureEvents} from '../../lib/gesture/gesture-detector'
export interface StarInterface {
hello(): void
}
type Constructor<T = Record<string, unknown>> = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new (...args: any[]): T
prototype: T
}
export function StarMixin<T extends Constructor<ReactiveElement>>(
constructor: T
): T & Constructor<StarInterface> {
return class SlotTextObservingElement extends constructor {
hello() {}
}
}
export type StarElementEventMap = HTMLElementEventMap & GestureEvents
export class StarBaseElement extends StarMixin(LitElement) {
/**
*
*/
startGestureDetector() {
GestureDector.embedded(this)
}
/**
*
*/
stopGestureDetector() {
GestureDector.disembedded(this)
}
}

View File

@ -7,7 +7,7 @@ export class StarBlur extends LitElement {
@property({type: Boolean}) openblur = false
@property({type: String}) imagesrc = ''
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
attributeChangedCallback(name: string, _old: string, value: string): void {
super.attributeChangedCallback(name, _old, value)
if (name === 'imagesrc') {

View File

@ -1,11 +1,12 @@
# star-button
星光 Web 组件——按钮组件本组件样式及代码风格依然在完善阶段现阶段介绍如下8月27日
星光 Web 组件——按钮组件本组件样式及代码风格依然在完善阶段现阶段介绍如下8 27 日)
## 介绍
star-button属性
1. type按钮类型包括文本按钮base、图标文本按钮iconlabel和图标按钮icononly。
star-button 属性:
1. type按钮类型包括文本按钮 base、图标文本按钮 iconlabel 和图标按钮 icononly。
```html demo
<star-button type="base" label="文本按钮"></star-button>
@ -13,18 +14,28 @@ star-button属性
<star-button type="icononly" label="图标按钮"></star-button>
```
2. variant按钮样式包括accent、primary、secondary、negative、black和white默认为accent。
2. variant按钮样式包括 accent、primary、secondary、negative、black white默认为 accent。
```html demo
<star-button type="base" variant="accent" label="accent"></star-button>
<star-button type="base" variant="primary" label="primary"></star-button>
```
3. size控制按钮大小包括small、medium、large和extralarge四种默认为medium。
3. size控制按钮大小包括 small、medium、large extralarge 四种,默认为 medium。
```html demo
<star-button type="base" variant="accent" label="accent" size="small"></star-button>
<star-button type="base" variant="primary" label="primary" size="large"></star-button>
<star-button
type="base"
variant="accent"
label="accent"
size="small"
></star-button>
<star-button
type="base"
variant="primary"
label="primary"
size="large"
></star-button>
```
4. label显示按钮名如省略则显示为“默认”。
@ -39,16 +50,31 @@ star-button属性
<star-button type="base" variant="accent" label="禁用" disabled></star-button>
```
6. treatment控制按钮填充状态包括fill和outline默认为fill。
6. treatment控制按钮填充状态包括 fill outline默认为fill。
```html demo
<star-button type="base" variant="accent" label="fill" treatment="fill"></star-button>
<star-button type="base" variant="accent" label="outline" treatment="outline"></star-button>
<star-button
type="base"
variant="accent"
label="fill"
treatment="fill"
></star-button>
<star-button
type="base"
variant="accent"
label="outline"
treatment="outline"
></star-button>
```
7. icon和iconcolor控制图标样式和其颜色。
7. icon iconcolor控制图标样式和其颜色。
```html demo
<star-button type="base" variant="accent" label="图标按钮" icon="alarm" iconcolor="white"></star-button>
<star-button
type="base"
variant="accent"
label="图标按钮"
icon="alarm"
iconcolor="white"
></star-button>
```

View File

@ -10,7 +10,6 @@ export const sharedStyles: CSSResult = css`
color: #606266;
text-align: center;
box-sizing: border-box;
margin: 20px;
transition: 0.1s;
font-weight: 500;
font-size: 14px;
@ -57,46 +56,42 @@ export const sharedStyles: CSSResult = css`
.accent {
background-color: #0469e3;
color: #FFFFFF;
color: #ffffff;
}
.accent:hover {
background-color: #015BC7;
background-color: #015bc7;
box-shadow: none;
}
.primary {
background-color: #222222;
color: #EBEBEB;
color: #ebebeb;
}
.primary:hover {
background-color: #000000;
color: #FFFFFF;
color: #ffffff;
}
.secondary {
background-color: #E6E6E6;
background-color: #e6e6e6;
color: #222222;
}
.secondary:hover {
background-color: #D5D5D5;
background-color: #d5d5d5;
color: #d42222;
transition-property: color;
transition-duration: 2s;
transition-timing-function: linear;
transition-delay: 0.2s;
}
.negative {
background-color: #B30202;
color: #EBEBEB;
background-color: #b30202;
color: #ebebeb;
}
.negative:hover {
background-color: #A20101;
color: #EBEBEB;
background-color: #a20101;
color: #ebebeb;
}
.black {
@ -105,18 +100,18 @@ export const sharedStyles: CSSResult = css`
}
.white {
background-color: #FFFFFF;
color: #EBEBEB;
background-color: #ffffff;
color: #ebebeb;
}
.disabled {
background-color: #E6E6E6;
background-color: #e6e6e6;
cursor: default;
pointer-events: none;
}
.fill.primary {
color: #FFFFFF;
color: #ffffff;
}
.fill.primary:hover {
@ -130,7 +125,7 @@ export const sharedStyles: CSSResult = css`
}
.outline.primary:hover {
background-color: #D5D5D5;
background-color: #d5d5d5;
border-style: solid;
color: #000000;
}
@ -158,4 +153,7 @@ export const sharedStyles: CSSResult = css`
min-width: 32.2px;
padding: 0px;
}
.start-button-confirm {
display: inline-block;
}
`

View File

@ -12,6 +12,7 @@ export enum ButtonType {
BASE = 'base',
ICONONLY = 'icononly',
ICONLABEL = 'iconlabel',
Confirm = 'confirm',
}
export enum ButtonSize {
@ -47,10 +48,7 @@ export class StarButton extends LitElement {
getBaseButton(): HTMLTemplateResult {
if (this.hasAttribute('disabled')) {
return html`
<button
class="disabled ${this.variant} ${this.size} ${this.treatment}"
size=${this.size}
>
<button class="disabled ${this.variant} ${this.size} ${this.treatment}">
${this.label}
</button>
`
@ -126,7 +124,11 @@ export class StarButton extends LitElement {
`
}
}
getConfirmButton(): HTMLTemplateResult {
return html`
<span class="start-button-confirm"><slot></slot></span>
`
}
render() {
switch (this.type) {
case ButtonType.BASE:
@ -135,6 +137,8 @@ export class StarButton extends LitElement {
return this.getIconOnlyButton()
case ButtonType.ICONLABEL:
return this.getIconLabelButton()
case ButtonType.Confirm:
return this.getConfirmButton()
default:
console.error('unhanled 【star-button】 type')
return nothing

View File

@ -7,17 +7,17 @@ star-card 的用途:
star-card 类型:
1、base
具有图片、标题、内容以及页脚几个模块同时删除base类型对应模块可以转变成其他类型无图卡片、无页脚卡片等。
具有图片、标题、内容以及页脚几个模块,同时删除 base 类型对应模块可以转变成其他类型:无图卡片、无页脚卡片等。
2、linkcard
该类型相比base类型多出点击后跳转相应链接的功能。
该类型相比 base 类型多出点击后跳转相应链接的功能。
3、tupianonly
该类型只展现一张正方形图片,用于陈列图片组。
star-card 其他属性:
1、image
通过填写图片URL来讲图片展示在卡片上。
通过填写图片 URL 来讲图片展示在卡片上。
2、heading
填写卡片标题以表明该卡片的用途。
@ -29,4 +29,4 @@ star-card 其他属性:
卡片页脚,一般用来填写卡片内容的时间、作者等信息。
5、link
用来填写链接卡片的跳转网址。
用来填写链接卡片的跳转网址。

View File

@ -2,16 +2,16 @@ import {css, CSSResult} from 'lit'
export const sharedStyles: CSSResult = css`
:host {
--background-image-url: ''
--background-image-url: '';
}
div {
width:200px;
width: 200px;
}
.card {
background: #FFFFFF;
border-color: #E6E6E6;
background: #ffffff;
border-color: #e6e6e6;
border-radius: 4px;
border-style: solid;
border-width: 1px;
@ -28,8 +28,8 @@ export const sharedStyles: CSSResult = css`
}
.base:hover {
background: #E6E6E6;
border-color: #B1B1B1;
background: #e6e6e6;
border-color: #b1b1b1;
}
.cardhead {
@ -79,7 +79,7 @@ export const sharedStyles: CSSResult = css`
}
.cardfooter {
border-color: #E6E6E6;
border-color: #e6e6e6;
border-top-style: solid;
border-top-width: 1px;
display: block;
@ -91,7 +91,7 @@ export const sharedStyles: CSSResult = css`
}
.imageonly {
background: #B1B1B1;
background: #b1b1b1;
border-color: #000000;
border: 10px;
border-style: solid;
@ -108,6 +108,6 @@ export const sharedStyles: CSSResult = css`
}
.imageonly:hover {
border-color: #E6E6E6;
border-color: #e6e6e6;
}
`
`

View File

@ -1,134 +1,134 @@
import {
html,
LitElement,
CSSResultArray,
HTMLTemplateResult,
nothing,
} from "lit"
import {customElement, property} from "lit/decorators.js"
import {sharedStyles} from "./card-styles"
export enum CardType {
BASE = "base",
LINKCARD = "linkcard",
IMAGEONLY = "imageonly",
LABELONLY = "labelonly",
FOOTERDELETED = 'footerdeleted',
}
// export enum CardSize {
// SMALL = "small",
// MEDIUM = "medium",
// }
@customElement("star-card")
export class StarCard extends LitElement {
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
@property({type: String}) type = "base"
// @property({type: String}) size = "medium"
@property({type: String}) heading = ""
@property({type: String}) subheading = ""
@property({type: String}) footer = ""
@property({type: String}) image = ""
@property({type: String}) link = ""
getBaseCard(): HTMLTemplateResult {
return html`
<div class="card base">
<div class="cardhead">
<img class="base-image" src="${this.image}">
</div>
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
<div class="cardfooter">
<p class="foooter">${this.footer}</p>
</div>
</div>
`
}
html,
LitElement,
CSSResultArray,
HTMLTemplateResult,
nothing,
} from 'lit'
import {customElement, property} from 'lit/decorators.js'
import {sharedStyles} from './card-styles'
getLinkCard(): HTMLTemplateResult {
return html`
<a href=${this.link} target="_blank" style="text-decoration:none;">
<div class="card base">
<div class="cardhead">
<img class="base-image" src="${this.image}">
</div>
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
<div class="cardfooter">
<p class="foooter">${this.footer}</p>
</div>
</div>
</a>
`
export enum CardType {
BASE = 'base',
LINKCARD = 'linkcard',
IMAGEONLY = 'imageonly',
LABELONLY = 'labelonly',
FOOTERDELETED = 'footerdeleted',
}
// export enum CardSize {
// SMALL = "small",
// MEDIUM = "medium",
// }
@customElement('star-card')
export class StarCard extends LitElement {
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
getLabelOnlyCard(): HTMLTemplateResult {
return html`
<div class="card base">
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
<div class="cardfooter">
<p class="foooter">${this.footer}</p>
</div>
@property({type: String}) type = 'base'
// @property({type: String}) size = "medium"
@property({type: String}) heading = ''
@property({type: String}) subheading = ''
@property({type: String}) footer = ''
@property({type: String}) image = ''
@property({type: String}) link = ''
getBaseCard(): HTMLTemplateResult {
return html`
<div class="card base">
<div class="cardhead">
<img class="base-image" src="${this.image}" />
</div>
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
<div class="cardfooter">
<p class="foooter">${this.footer}</p>
</div>
</div>
`
}
getLinkCard(): HTMLTemplateResult {
return html`
<a href=${this.link} target="_blank" style="text-decoration:none;">
<div class="card base">
<div class="cardhead">
<img class="base-image" src="${this.image}" />
</div>
`
}
getImageOnlyCard(): HTMLTemplateResult {
return html`
<div class="card imageonly">
<div class="cardhead">
<img class="imageonly-image" src="${this.image}">
</div>
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
`
}
getFooterDeletedCard(): HTMLTemplateResult {
return html `
<div class="card base">
<div class="cardhead">
<img class="base-image" src="${this.image}">
</div>
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
<div class="cardfooter">
<p class="foooter">${this.footer}</p>
</div>
`
}
render() {
switch (this.type) {
case CardType.BASE:
return this.getBaseCard()
case CardType.LINKCARD:
return this.getLinkCard()
case CardType.IMAGEONLY:
return this.getImageOnlyCard()
case CardType.LABELONLY:
return this.getLabelOnlyCard()
case CardType.FOOTERDELETED:
return this.getFooterDeletedCard()
default:
console.error("unhanled 【star-card】 type")
return nothing
}
</div>
</a>
`
}
getLabelOnlyCard(): HTMLTemplateResult {
return html`
<div class="card base">
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
<div class="cardfooter">
<p class="foooter">${this.footer}</p>
</div>
</div>
`
}
getImageOnlyCard(): HTMLTemplateResult {
return html`
<div class="card imageonly">
<div class="cardhead">
<img class="imageonly-image" src="${this.image}" />
</div>
</div>
`
}
getFooterDeletedCard(): HTMLTemplateResult {
return html`
<div class="card base">
<div class="cardhead">
<img class="base-image" src="${this.image}" />
</div>
<div class="cardbody">
<h3 class="heading">${this.heading}</h3>
<p class="subheading">${this.subheading}</p>
</div>
</div>
`
}
render() {
switch (this.type) {
case CardType.BASE:
return this.getBaseCard()
case CardType.LINKCARD:
return this.getLinkCard()
case CardType.IMAGEONLY:
return this.getImageOnlyCard()
case CardType.LABELONLY:
return this.getLabelOnlyCard()
case CardType.FOOTERDELETED:
return this.getFooterDeletedCard()
default:
console.error('unhanled 【star-card】 type')
return nothing
}
}
declare global {
interface HTMLElementTagNameMap {
"star-card": StarCard
}
}
}
declare global {
interface HTMLElementTagNameMap {
'star-card': StarCard
}
}

View File

@ -1 +1 @@
Checkbox 复选框
Checkbox 复选框

View File

@ -1,6 +1,18 @@
# 确认弹窗-Confirm
## 星光 Web 组件——确认弹窗组件confirm 组件介绍9 月 13 日)
参考UI设计
## 介绍
star-confirm 组件为确认弹窗组件,主要是操作确认框和消息确认框两种类型。
## 1、操作确认框
使用场景:一般用于删除、卸载、二次确认等。
## 2、消息确认框
使用场景:一般用于确认权限、安全提示、功能开启提示等。
参考UI 设计
## 内容排布
@ -13,14 +25,39 @@
标题
子标题
按钮1(按钮2)
(复选框)(复选框文本)
```
特点:
- 支持模态模式
- 支持非模态模式
- 支持静态资源插槽(此处为图片)
- 支持确认回调
- 支持取消回调
## 关联组件
- button
- 静态资源容器
- 静态资源容器
- checkbox
## 基本用法
通过 click 事件调用 Confirm 函数,根据传入属性的不同显示不同的弹出框类型
```
Confirm({
type: '', 支持模态和非模态,默认是模态模式,用户只能操作该弹出框,不能点击其他
model'', 支持深色模式和浅色模式,默认背景是深色模式
image: '', 图标slot插槽
headline: '', 标题
subtitle: '', 子标题
checkBoxText: '', 复选框文本(checkbox插槽)
confirmButtonText: '', 操作按钮()
cancelButtonText: '', 取消
location: '', 弹出框位置,默认是在屏幕中心弹出
onConfirm: () => {}, 确认回调函数
onCancel: () => {}, 取消回调函数
})
```

View File

@ -0,0 +1,124 @@
import {css, CSSResult} from 'lit'
export const sharedStyles: CSSResult = css`
.star-confirm-background {
position: fixed;
width: 100vw;
height: 100vh;
background-color: #00000033;
display: flex;
justify-content: center;
align-items: center;
}
.top-center-popup {
align-items: flex-start;
}
.bottom-center-popup {
align-items: flex-end;
}
/*非模态模式*/
.non-modal-mode {
background-color: transparent !important;
pointer-events: none !important;
}
.star-confirm-subject {
width: 370px;
background: linear-gradient(101.98deg, #4e5161 1.12%, #363a47 96.75%);
border-radius: 20px;
overflow: hidden;
pointer-events: auto;
}
.star-confirm-main {
padding: 30px 20px 10px 20px;
color: #2c0404;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
::slotted(.star-confirm-image) {
height: 48px;
width: 48px;
margin-bottom: 10px;
}
.star-confirm-title {
margin-top: 10px;
margin-bottom: 10px;
color: #f0f0f0;
font-size: 18px;
font-weight: bold;
text-align: center;
line-height: 20px;
}
.star-confirm-subtitle {
margin-top: 10px;
margin-bottom: 10px;
font-size: 16px;
color: #e0e0e0;
margin-left: 5px;
margin-right: 5px;
line-height: 20px;
max-height: 60px;
overflow: auto;
}
::slotted(.star-confirm-checkbox) {
align-self: start;
margin-top: 5px;
margin-bottom: 5px;
font-size: 16px;
color: #e0e0e0;
}
.star-confirm-button {
display: flex;
font-size: 18px;
align-items: center;
}
.star-confirm-button1 {
flex: 1;
color: #f0f0f0;
text-align: center;
cursor: pointer;
font-weight: bold;
line-height: 60px;
}
.star-split-line {
height: 25px;
border: 1px solid #585a66;
}
.star-confirm-button2 {
flex: 1;
color: #1d98f0;
cursor: pointer;
text-align: center;
font-weight: bold;
line-height: 60px;
}
.red {
color: #ec4949;
}
/*浅色模式*/
.star-confirm-subject.light {
background: linear-gradient(
134.78deg,
#f7f5f7 2.34%,
#fafafa 34.11%,
#e1e4f2 100%
);
border-radius: 20px;
}
.star-confirm-subject.light .star-confirm-title {
color: #292929;
}
.star-confirm-subject.light .star-confirm-subtitle {
color: #4d4d4d;
}
.star-confirm-subject.light .star-confirm-button1 {
color: #292929;
}
.star-confirm-subject.light .star-split-line {
height: 25px;
border: 1px solid #cfd8e8;
}
.star-confirm-subject.light ::slotted(.star-confirm-checkbox) {
color: #4d4d4d;
}
`

View File

@ -1,27 +1,157 @@
import { html, css, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import {html, LitElement, CSSResultArray} from 'lit'
import {customElement, property} from 'lit/decorators.js'
import {sharedStyles} from './confirm-style'
@customElement('star-confirm')
export class StarConfirm extends LitElement {
@property()
foo = ''
@state()
bar = ''
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
@property({type: String}) model = '' //支持深色模式和浅色模式,默认背景是深色模式
@property({type: String}) headline = ''
@property({type: String}) subtitle = ''
@property({type: String}) confirmButtonText = ''
@property({type: String}) cancelButtonText = ''
@property({type: String}) buttonColor = ''
@property({type: String}) type = '' //支持模态和非模态,默认是模态模式,用户只能操作该弹出框,不能点击其他
@property({type: String}) location = '' //弹出框位置,默认是在屏幕中心弹出
render() {
return html`
<div
id="star-confirm"
class="star-confirm-background ${this.type} ${this.location}"
>
<div class="star-confirm-subject ${this.model}">
<div class="star-confirm-main">
<slot name="image"></slot>
<span class="star-confirm-title">${this.headline}</span>
<span class="star-confirm-subtitle">${this.subtitle}</span>
<slot name="checkBox"></slot>
</div>
<div class="star-confirm-button">
${this.cancelButtonText
? html`
<star-button
type="confirm"
class="star-confirm-button1"
@click="${this.cancelTo}"
>
${this.cancelButtonText}
</star-button>
<span class="star-split-line"></span>
`
: ''}
${this.confirmButtonText
? html`
<star-button
type="confirm"
class="star-confirm-button2 ${this.buttonColor}"
@click="${this.confirmTo}"
>
${this.confirmButtonText}
</star-button>
`
: ''}
</div>
</div>
</div>
`
}
static styles = css`
`
protected cancelTo() {
this.dispatchEvent(
new Event('cancel-to', {
bubbles: true,
composed: true,
})
)
}
protected confirmTo() {
this.dispatchEvent(
new Event('confirm-to', {
bubbles: true,
composed: true,
})
)
}
}
export const Confirm = ({
type,
model,
image,
headline,
subtitle,
checkBoxText,
confirmButtonText,
cancelButtonText,
buttonColor,
location,
onConfirm,
onCancel,
}: {
type?: string
model?: string
image?: string
headline: string
subtitle?: string
checkBoxText?: string
confirmButtonText?: string
cancelButtonText?: string
buttonColor?: string
location?: string
onConfirm: (obj: {checked?: undefined}) => void
onCancel: () => void
}) => {
let bodyHtml = document.querySelector('body')
let confirmHtml = document.createElement('div')
confirmHtml.innerHTML = `<star-confirm
type="${type}"
model="${model}"
headline="${headline}"
subtitle="${subtitle}"
location="${location}"
buttonColor="${buttonColor}"
confirmButtonText="${confirmButtonText}"
cancelButtonText="${cancelButtonText}">
${
image
? `<img slot="image" src="${image}" class="star-confirm-image" alt=""/>`
: ''
}
${
checkBoxText
? `<div slot="checkBox" class="star-confirm-checkbox">
<label>
<input id="checkbox-start-checked" type="checkbox" class="star-confirm-label"/>
${checkBoxText}
</label>
</div>`
: ''
}
</star-confirm>`
bodyHtml && bodyHtml.appendChild(confirmHtml)
confirmHtml.addEventListener('cancel-to', () => {
onCancel() // 取消回调函数
bodyHtml && bodyHtml.removeChild(confirmHtml)
})
confirmHtml.addEventListener('confirm-to', () => {
let params = {
checked: undefined,
}
if (checkBoxText) {
let checkedInput = document.querySelector('#checkbox-start-checked')
// @ts-ignore
checkedInput = checkedInput && checkedInput.checked
console.log(checkedInput) // 判断复选框是否被勾选, 后面要换成StarCheckBox
// @ts-ignore
params.checked = checkedInput
}
onConfirm(params) // 确认回调函数 并传参
bodyHtml && bodyHtml.removeChild(confirmHtml)
})
}
declare global {
interface HTMLElementTagNameMap {
'star-confirm': StarConfirm
}
}
}

View File

@ -16,3 +16,16 @@
|-Dock容器
|-图标容器
```
## 每层的任务
- root-container
1. 负责主屏,次屏等页容器
2. 负责 dock 栏容器
3. 负责主屏页面指示器
4. 负责传导编辑状态
- child-container
1. 根据不同场景类型,渲染图标,文件夹,小组件等容器
2. 负责传导编辑功能

View File

@ -1,57 +1,144 @@
import {html, css, LitElement, TemplateResult, nothing} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import {customElement, state} from 'lit/decorators.js'
import {map} from 'lit/directives/map.js'
import {styleMap} from 'lit/directives/style-map.js'
import {StyleInfo} from 'lit/directives/style-map.js'
import {range} from 'lit/directives/range.js'
import './icon-container'
import './folder-container'
import './small-component-container'
import {
ChildContainerNode,
ChildData,
FolderContainerNode,
SmallComponentContainerNode,
} from './data-type'
@customElement('child-container')
export class ChildContainer extends LitElement {
@property({type: String}) size = '6,4'
@state() node!: ChildContainerNode<ChildData>
@state() row = 6
@state() col = 4
@state() dynmaicCssVar: TemplateResult | typeof nothing = nothing
attributeChangedCallback(
name: string,
_old: string | null,
value: string | null
) {
super.attributeChangedCallback(name, _old, value)
if (name === 'size') {
const result = value?.split(',')
const row = result?.[0]
const col = result?.[1]
protected willUpdate() {
if (this.node?.size) {
const result = this.node.size?.split(',')
this.row = Number(result?.[0])
this.col = Number(result?.[1])
this.dynmaicCssVar = html`
<style>
:host {
--base-container-width: calc(
100% / ${row} - 2 * var(--icon-container-margin)
);
--base-container-height: calc(
100% / ${col} - 2 * var(--icon-container-margin)
);
--base-grid-template-rows: ${'1fr '.repeat(this.row).trim()};
--base-grid-template-columns: ${'1fr '.repeat(this.col).trim()};
}
</style>
`
}
}
parsedata(data: ChildData): TemplateResult | typeof nothing {
switch (data.node) {
case 'icon-container':
return html`
<icon-container></icon-container>
`
case 'folder-container':
return html`
<folder-container
.node=${data as FolderContainerNode}
></folder-container>
`
case 'small-component-container':
return html`
<small-component-container
.node=${data as SmallComponentContainerNode}
></small-component-container>
`
default:
console.error('unhandled type: ', data.node)
return nothing
}
}
/**
* 1 2 3 4 5
* 2 ......
* 3 ..
* 4 . .
* 5 . .
* 6 . .
* 7 . .
*
*
* - (1x1)
* - (1x1)
* - (1x1,1x2,2x1,2x2,2x3,3x2)
*
* (w,h),(x,y),x[1,6],y[1,4],w[1,3],h[1,3]
* gridArea(x,y,x+w,y+h)
*
*
* @returns
*/
render() {
const childs = this.node?.childs
const divs = map(range(this.row * this.col), (_, index) => {
const x = Math.floor(index / this.col) + 1
const y = (index % this.col) + 1
const w = 1
const h = 1
const gridRowStart = x
const gridColumnStart = y
const gridRowEnd = x + w
const gridColumnEnd = y + h
const styles: StyleInfo = {
gridArea: `${gridRowStart}/${gridColumnStart}/${gridRowEnd}/${gridColumnEnd}`,
}
const child = childs?.[index]
let childNode: TemplateResult | typeof nothing = nothing
if (child !== undefined) {
childNode = this.parsedata(child)
}
return html`
<div style=${styleMap(styles)}>${childNode}</div>
`
})
return html`
${this.dynmaicCssVar}
<slot></slot>
${this.dynmaicCssVar} ${divs}
`
}
static styles = css`
:host {
display: inline-flex;
--base-component-width: span 1;
--base-component-height: span 1;
background-color: purple;
width: auto;
height: 100%;
margin: 10px;
flex-wrap: wrap;
width: calc(100% - 10px);
height: calc(100% - 10px);
margin: 5px;
align-content: start;
/* 改为Grid布局 */
display: grid;
grid-template-columns: var(--base-grid-template-columns);
grid-template-rows: var(--base-grid-template-rows);
}
icon-container,
folder-container,
small-component-container {
margin: var(--icon-container-margin);
}
div {
display: grid;
background: gray;
border: 1px #fff solid;
}
`
}

View File

@ -0,0 +1,29 @@
export interface ChildContainerNode<T> {
node: string
type: string
size?: string
childs: Array<T>
}
export interface IconContainerNode {
node: string
childs?: {}
}
export interface SmallComponentContainerNode {
node: string
size: string
childs?: {}
}
export interface FolderContainerNode {
node: string
name?: string
size?: string
childs: Array<ChildContainerNode<IconContainerNode>>
}
export type ChildData =
| IconContainerNode
| FolderContainerNode
| SmallComponentContainerNode

View File

@ -1,5 +1,5 @@
import { html, css, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import {html, css, LitElement} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import './icon-container'
@customElement('dock-container')
@ -16,23 +16,23 @@ export class DockContainer extends LitElement {
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
`
}
static styles = css`
:host {
--base-container-height: calc(100% - 6px);
display: inline-flex;
display: grid;
grid-template-columns: var(--base-grid-template-columns);
grid-template-rows: 1fr;
background-color: purple;
width: 100%;
margin: 10px;
flex-wrap: wrap;
align-content: start;
}
icon-container, folder-container, small-component-container {
icon-container,
folder-container,
small-component-container {
margin: var(--icon-container-margin);
}
`
@ -42,4 +42,4 @@ declare global {
interface HTMLElementTagNameMap {
'dock-container': DockContainer
}
}
}

View File

@ -1,45 +1,68 @@
import {html, css, LitElement, nothing} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import {
html,
css,
LitElement,
nothing,
TemplateResult,
PropertyValueMap,
} from 'lit'
import {customElement, state} from 'lit/decorators.js'
import {classMap} from 'lit/directives/class-map.js'
import {map} from 'lit/directives/map.js'
import {range} from 'lit/directives/range.js'
import './child-container'
import {FolderContainerNode} from './data-type'
import './icon-container'
@customElement('folder-container')
export class FolderContainer extends LitElement {
@property({type: Number}) total = 8
@property({type: String}) name = '未命名'
@state() node!: FolderContainerNode
/* index 从1开始计算 */
@state() index = 1
@state() pages = -1
@state() isopening = false
@state() name = '未命名'
renderHeader(): TemplateResult | typeof nothing {
return this.isopening
? html`
<header>${this.name}</header>
`
: nothing
}
renderIndicator(): TemplateResult | typeof nothing {
return this.isopening && this.pages > 1
? html`
<indicator-page-point
total=${this.pages}
index=${this.index}
></indicator-page-point>
`
: nothing
}
protected willUpdate(_changedProperties: PropertyValueMap<any>) {
this.pages = this.node.childs.length
if (this.node?.name) {
this.name = this.node.name
}
}
/**
*
* @returns
*/
render() {
const classes = {
hidden: !this.isopening,
popup: this.isopening,
}
return html`
<section class=${classMap(classes)} @click=${this}>
${this.isopening
? html`
<header>${this.name}</header>
`
: nothing}
<child-container size="4,4">
${map(range(this.total), (_, index) => {
return html`
<icon-container ?hidden=${index >= 9}></icon-container>
`
})}
</child-container>
${this.isopening && this.total > 8
? html`
<indicator-page-point total="2" index="1"></indicator-page-point>
`
: nothing}
${this.renderHeader()}
<child-container .node=${this.node.childs[0]}></child-container>
${this.renderIndicator()}
</section>
`
}
@ -68,17 +91,13 @@ export class FolderContainer extends LitElement {
static styles = css`
:host {
--folder-margin: 300px;
display: block;
width: var(--base-container-width);
height: var(--base-container-height);
--folder-margin-width: 30vw;
--folder-margin-height: 20vh;
background-color: lightblue;
}
child-container {
width: calc(100% - 20px);
height: calc(100% - 20px);
width: calc(100% - 10px);
height: calc(100% - 10px);
}
icon-container {
margin: var(--icon-container-margin);
@ -90,15 +109,22 @@ export class FolderContainer extends LitElement {
width: 100%;
height: 100%;
}
section.hidden {
animation-name: scale-fade-out;
animation-duration: 260ms;
animation-fill-mode: forwards;
}
section.popup {
position: absolute;
display: flex;
flex-direction: column;
padding: var(--folder-margin);
width: calc(100vw - calc(2 * var(--folder-margin)));
height: calc(100vh - calc(2 * var(--folder-margin)));
padding: calc(var(--folder-margin-height))
calc(var(--folder-margin-width) / 2);
width: calc(100vw - var(--folder-margin-width));
height: calc(100vh - calc(2 * var(--folder-margin-height)));
top: 0;
left: 0;
z-index: 1;
background-color: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(8px); /* firefox 103 support it */
animation-name: scale-fade-in;
@ -112,7 +138,16 @@ export class FolderContainer extends LitElement {
scale: 0.5;
}
}
@keyframes scale-fade-out {
0% {
opacity: 1;
scale: 1.2;
}
50% {
opacity: 0.5;
scale: 0.8;
}
}
`
}

View File

@ -1,31 +1,21 @@
import { html, css, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import {html, css, LitElement} from 'lit'
import {customElement} from 'lit/decorators.js'
@customElement('icon-container')
export class IconContainer extends LitElement {
@property()
foo = ''
@state()
bar = ''
render() {
return html`
<!-- <span></span>
<span></span>
<span></span>
<span></span> -->
`
/**
* |
* ()
*
*/
return html``
}
static styles = css`
:host {
display: block;
width: var(--base-container-width);
height: var(--base-container-height);
background-color: lightgreen;
}
:host([hidden='']) {
display: none;
}
@ -36,4 +26,4 @@ declare global {
interface HTMLElementTagNameMap {
'icon-container': IconContainer
}
}
}

View File

@ -1,35 +1,23 @@
import {html, css, LitElement} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import {customElement, state} from 'lit/decorators.js'
import {map} from 'lit/directives/map.js'
import './child-container'
import './dock-container'
import '../indicator/indicator-page-point'
import {ChildContainerNode, ChildData} from './data-type'
@customElement('root-container')
export class RootContainer extends LitElement {
@property()
foo = ''
@state()
bar = ''
@state() data!: ChildContainerNode<ChildData>[]
render() {
return html`
<section>
<child-container>
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
<icon-container></icon-container>
<folder-container total=4 name='文件夹1'></folder-container>
<folder-container total=7 name='文件夹2'></folder-container>
<small-component-container></small-component-container>
<icon-container></icon-container>
<folder-container total=9 name='文件夹3'></folder-container>
<folder-container total=12 name='文件夹4'></folder-container>
</child-container>
${map(this.data, (node) => {
return html`
<child-container .node=${node}></child-container>
`
})}
</section>
<indicator-page-point total="3" index="1"></indicator-page-point>
<dock-container></dock-container>
@ -39,7 +27,8 @@ export class RootContainer extends LitElement {
static styles = css`
:host {
--icon-container-margin: 3px;
--base-container-width: calc(100% / 6 - 2 * var(--icon-container-margin));
--base-grid-template-columns: 1fr 1fr 1fr 1fr;
--base-grid-template-rows: 1fr 1fr 1fr 1fr 1fr 1fr;
--base-container-height: calc(
100% / 6 - 2 * var(--icon-container-margin)
);
@ -52,10 +41,10 @@ export class RootContainer extends LitElement {
section {
display: flex;
flex-direction: column;
width: 80%;
width: calc(100% - 10px);
height: 80%;
background-color: green;
margin: 10px auto 5px auto;
margin: 5px;
}
indicator-page-point {
width: 80%;
@ -65,15 +54,9 @@ export class RootContainer extends LitElement {
dock-container {
width: 80%;
height: calc(15% - 30px);
margin: 5px auto 10px auto;
margin: 5px auto;
background-color: yellow;
}
icon-container,
folder-container,
small-component-container {
margin: var(--icon-container-margin);
}
`
}

View File

@ -1,26 +1,44 @@
import { html, css, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import {html, css, LitElement, TemplateResult, nothing} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import {SmallComponentContainerNode} from './data-type'
@customElement('small-component-container')
export class SmallComponentContainer extends LitElement {
@property()
foo = ''
@property({type: Object}) node!: SmallComponentContainerNode
@state()
bar = ''
@state() dynmaicCssVar: TemplateResult | typeof nothing = nothing
protected willUpdate(): void {
console.log(this.node)
if (this.node?.size) {
const size = this.node.size
const result = size?.split(',')
const row = Number(result?.[0])
const col = Number(result?.[1])
this.dynmaicCssVar = html`
<style>
:host {
--base-component-width: span ${row};
--base-component-height: span ${col};
}
</style>
`
console.log(this.dynmaicCssVar)
}
}
render() {
return html`
${this.dynmaicCssVar}
`
}
static styles = css`
:host {
display: block;
width: calc((var(--base-container-width) + var(--icon-container-margin)) * 2);
height: calc(var(--base-container-height) * 2);
background-color: lightcoral;
grid-column: var(--base-component-width);
grid-row: var(--base-component-height);
}
`
}
@ -29,4 +47,4 @@ declare global {
interface HTMLElementTagNameMap {
'small-component-container': SmallComponentContainer
}
}
}

View File

@ -1,3 +1,3 @@
# 表单-form
参考iPhone-VPN-添加配置
参考iPhone-VPN-添加配置

View File

@ -1,7 +1,34 @@
# 抓握指示器-Grabber
下分:
- Multitasking Indicator (多任务指示器)(TODO)
- App Divider (App分割条)(TODO)
- App Divider (App 分割条)(TODO)
- Resizable Indicator (可调指示器)
- Home Indicator (主屏指示器)
- Home Indicator (主屏指示器)
## 主屏指示器(Home Indicator)
主屏指示器应具备的功能:
- 锁屏处,向上拨动底部可抓取的主屏指示器随手指拖动向上推动锁屏页,进入主屏页
- 应用内,向上拨动且不减速,进入主屏页
- 应用内,向上拨动且减速至 0进入任务管理页
- 主屏内,向右滑动,打开最近的后台应用
- 应用内,向右滑动,打开最近的后台应用
- 应用内,向左滑动,打开之前切换的后台应用
设计时应具备的功能:
- 手指在抓取器上的状态
- 向各个方向移动的事件指向
- 移动的二维尺度(x,y)
### 主屏指示器与父容器的通讯
自身发送事件
向上拨动 y 轴超出一定距离,则发出 'go-home'
向上拨动 y 轴超出一定距离,则发出 'go-task'
向左拨动 x 轴超出一定距离,则发出 'go-prev'
向右拨动 x 轴超出一定距离,则发出 'go-next'

View File

@ -0,0 +1,870 @@
/**
* 手势检测库(支持单指和双指) - GestureDetector.js
* -- 针对单指或双指手势生成事件
*
* GestureDetector 对象监听指定元素上的触摸事件,并生成描述元素上的一个和两个手指手势的更高层级的事件。
*
* 支持的事件:
* - 轻触(tap) 类似单击(click)事件
* - 双指轻触(dbltap) 类似双击(dbclick)事件
* - 平移(pan) 单指平移
* - 轻扫(swipe) 在平移事件后释放手指时进行滑动
* - 长按(holdstart) 保持触摸.必须要设置一个选项才能获取这些.
* - 长按后移动(holdmove) holdstart事件后的holdmove运动
* - 长按结束(holdend) 长按/长按后移动松开手指时
* - 转换(transform) 双指捏合手势, 用于放缩和旋转
*
* 需支持的事件(同步ios):
* - Tap------------轻触--------
* - Pinch----------捏合--------
* - Rotation-------旋转--------
* - Swipe----------轻扫--------
* - Pan------------平移--------
* - ScreenEdgePan--边缘平移-----
* - LongPress------长按--------
* - Hover----------悬停--------
*
* 这些事件中的每一个都是一个冒泡的CustomEvent事件中有重要的细节。
* 详细信息字段。事件详情尚不稳定尚未记录。有关详细信息请参阅对emitEvent的调用。
*
* 使用方法:
* 1. 创建一个GestureDetector对象;
* 2. 将元素传递给 GestureDetector() 构造函数,然后对其调用 startDetecting();
* 3. 该元素将是所有发射的手势事件的目标。还可以将可选对象作为第二个构造函数参数传递。
*
* 如果您对 holdstart/holdmove/holdend 事件感兴趣,请将 {holdEvents:true} 作为第二个参数传递。
* 否则它们将不会生成。
* 如果要自定义平移阈值,请在选项参数中传递 {panThreshold:X}X和Y以像素为单位
*
* 实现说明:
* 事件处理使用简单的有限状态机完成。
* 这意味着,一般来说各种手势是相互排斥的。
* 例如,在手指移动超过最小阈值之前,您不会获得平移事件,
* 但如果移动超过最小值FSM将进入一个新状态在该状态下
* 它可以发出平移和滑动事件,但不能发出保持事件。
* 类似地,如果您启动了单手指平移/滑动手势,并意外地用第二根手指触摸,
* 则将继续获取平移事件,而不会突然开始获取双手指变换事件。
*
* 该库从未对其处理的任何事件调用 preventDefault() 或 stopPropagation
* 因此原始触摸事件仍应可供其他代码处理。我不清楚这是一个特性还是一个bug。
*/
interface detectOptions {
panThreshold: number
holdEvents?: Boolean
}
interface TapPoint {
readonly screenX: number
readonly screenY: number
readonly clientX: number
readonly clientY: number
readonly timeStamp: number
}
interface FSMGestureState {
readonly name: string
readonly init: (d: GestureDetector, e: TouchEvent, t: Touch) => void
readonly touchstart: (d: GestureDetector, e: TouchEvent, t: Touch) => void
readonly touchmove: (d: GestureDetector, e: TouchEvent, t: Touch) => void
readonly touchend: (d: GestureDetector, e: TouchEvent, t: Touch) => void
readonly holdtimeout?: (d: GestureDetector) => void
}
type TouchEventType = 'touchstart' | 'touchmove' | 'touchend'
type PickUP<T, K extends keyof T> = T[K]
type TouchHandler = PickUP<FSMGestureState, TouchEventType> | any
type TimerType = 'holdtimeout'
interface PanEvent {
readonly absolute: {
readonly dx: number
readonly dy: number
}
readonly relative: {
readonly dx: number
readonly dy: number
}
readonly position: TapPoint
}
interface HoldendEvent {
readonly start: TapPoint
readonly end: TapPoint
readonly dx: number
readonly dy: number
}
interface SwipeEvent {
readonly start: TapPoint
readonly end: TapPoint
readonly dx: number
readonly dy: number
readonly dt: number
readonly vx: number
readonly vy: number
readonly direction: string
readonly angle: number
}
interface TransformEvent {
readonly absolute: {
readonly scale: number
readonly rotate: number
}
readonly relative: {
readonly scale: number
readonly rotate: number
}
readonly midpoint: TapPoint
}
type TapEvent = TapPoint
type DbltapEvent = TapPoint
type HoldstartEvent = PanEvent
type HoldmoveEvent = PanEvent
type TransformEndEvent = TransformEvent
// type PascalCase<T> = T extends string
// ? T extends `${infer A}${infer B}`
// ? `${Uppercase<A>}${B}Event`
// : T
// : T;
interface EmitEvents {
tap: TapEvent
dbltap: DbltapEvent
transform: TransformEvent
transformend: TransformEndEvent
pan: PanEvent
swipe: SwipeEvent
holdstart: HoldstartEvent
holdmove: HoldmoveEvent
holdend: HoldendEvent
}
type EmitEventType = keyof EmitEvents
type EmitEventDetail<T extends keyof EmitEvents> = EmitEvents[T]
export default class GestureDetector {
/**
* 可调参数
*/
// hold 长按事件阈值:
static HOLD_INTERVAL = 1000
// pan 平移事件抖动阈值:
static PAN_THRESHOLD = 20
static DOUBLE_TAP_DISTANCE = 50
static DOUBLE_TAP_TIME = 500
static VELOCITY_SMOOTHING = 0.5
// transform 转换事件抖动阈值:
// 分为 scale 使用的像素单位和 rotate
static SCALE_THRESHOLD = 20
static ROTATE_THRESHOLD = 22.5
// 对于平移(pans)和缩放(zooms), 我们计算初始事件和超过阈值的事件之间的新起始坐标,
// 这样我们在发送第一个事件时不会引起大的倾斜.
// 该常数区间为[0,1], 代表初始值和新选值之间的距离.
static THRESHOLD_SMOOTHING = 0.9
// 需要注册的原生事件组
eventtypes: TouchEventType[] = ['touchstart', 'touchmove', 'touchend']
private timers: any = {}
private state: FSMGestureState | any
private element: HTMLElement
emptyV = -1
emptyTouchId = -1
emptyDistance = -1
emptyDirection = -1
emptyTarget = new EventTarget()
emptyTapPoint: TapPoint = {
clientX: 0,
clientY: 0,
screenX: 0,
screenY: 0,
timeStamp: 0,
}
options: detectOptions
target!: EventTarget
start: TapPoint = this.emptyTapPoint
last: TapPoint = this.emptyTapPoint
lastTap: TapPoint = this.emptyTapPoint
lastMidpoint: TapPoint = this.emptyTapPoint
// 标识touch id
touch1!: number
touch2!: number
vx!: number
vy!: number
startDistance!: number
lastDistance!: number
startDirection!: number
lastDirection!: number
scaled!: boolean
rotated!: boolean
constructor(element: HTMLElement, options?: any) {
this.element = element
this.options = options || {}
this.options.panThreshold =
options?.panThreshold || GestureDetector.PAN_THRESHOLD
this.state = this.initialState
}
/**
* FSM(Finite State Machine, 有限状态机)
*
* 以下对象是 FSM 的状态:
* initialState----------初始状态
* touchStartedState-----开始触摸状态
* panStartedState-------开始平移状态
* holdState-------------长按状态
* transformState--------变换状态
* afterTransformState---结束变换状态
*/
/**
* 初始状态 - initialState
*
* 此状态下, 不处理任何手势, 仅等待一个开始手势的事件
*/
private initialState: FSMGestureState = {
name: 'initialState',
init: (d, e, t) => {
// 当进入或返回初始状态时, 清除跟踪手势的检测器属性
// 不要在这里清除 d.lastTap, 需用其处理 dbltap 事件
d.target = this.emptyTarget
d.start = d.last = this.emptyTapPoint
d.touch1 = d.touch2 = this.emptyTouchId
d.vx = d.vy = this.emptyV
d.startDistance = d.lastDistance = this.emptyDistance
d.startDirection = d.lastDirection = this.emptyDirection
d.lastMidpoint = this.emptyTapPoint
d.scaled = d.rotated = false
// 如果调用了 touch 事件, 就开始处理它.
if (e && t && e.type === 'touchstart') {
console.info('initialState::init')
this.initialState.touchstart(d, e, t)
}
},
// 切换到 touchstarted 状态并在此处理触摸事件
touchstart: (d, e, t) => {
console.info('initialState::touchstart')
d.switchTo(this.touchStartedState, e, t)
},
touchmove: () => {},
touchend: () => {},
}
/**
* 开始触摸状态 - touchStartedState
*
* 此时已有一根手指向下, 但我们还没有生成任何事件
* 等待手指的动作:
* 1. 如果手指很快抬起, 是 tap 动作;
* 2. 如果手指静止不动, 是 hold 动作;
* 3. 如果手指移动, 是 pan/swipe 动作;
* 4. 如果第二根手指向下, 是 transform 动作;
*/
private touchStartedState: FSMGestureState = {
name: 'touchStartedState',
init: (d, e, t) => {
// 记住 event target
d.target = e.target as EventTarget
// 记住开始时的 touch id
d.touch1 = t.identifier
// 获取触摸坐标
d.start = d.last = this.coordinates(e, t)
// 针对"长按"事件启动一个计时器
// 如果我们正在处理长按事件, 则为它们启动计时器.
if (d.options.holdEvents === true) {
console.info('touchStartedState::holdEvents')
d.startTimer('holdtimeout', GestureDetector.HOLD_INTERVAL)
}
console.info('touchStartedState::init')
},
touchstart: (d, e, t) => {
console.info('touchStartedState::touchstart')
console.trace()
// 如果另一只手指在本状态下落下, 就转换状态以启动双指手势.
d.clearTimer('holdtimeout')
if (e.touches.length > 1) {
// 验证我们确实有双指触摸屏幕
d.switchTo(this.transformState, e, t)
} else {
// 如果 Gecko 未能向我们发送 touchend 事件, (例如, bug 1162771)
// 然后我们可能会连续获得两个 touchstart 事件, 并在当前屏幕上只有一个手指时结束.
// 在这种情况下, 我们切换回初始状态并从该状态处理该触摸.
// (我们不直接切换到 touchStartedState, 因为我们需要重置
// initialState init() 中的内容.)
console.warn('Ignoring missing touchend event. See bug 1162771.')
d.switchTo(this.initialState, e, t)
}
},
touchmove: (d, e, t) => {
// 忽略除初始触摸之外的任何触摸
// e.g.: 如果在先前的双指手势结束后仍有手指向下, 则可能发生这种情况.
if (t.identifier !== d.touch1) return
if (
Math.abs(t.screenX - d.start.screenX) > d.options.panThreshold ||
Math.abs(t.screenY - d.start.screenY) > d.options.panThreshold
) {
d.clearTimer('holdtimeout')
d.switchTo(this.panStartedState, e, t)
}
},
touchend: (d, e, t) => {
// 忽略除初始触摸之外的任何触摸
if (t.identifier !== d.touch1) return
// 如果存在一个之前的tap, 它在时间和空间上足够接近, 则发出 dbltap 事件
// console.log(d.lastTap, this.emptyTapPoint)
if (
d.lastTap !== this.emptyTapPoint &&
this.isDoubleTap(d.lastTap, d.start) === true
) {
d.emitEvent('tap', d.start)
d.emitEvent('dbltap', d.start)
// 清除 lastTap 属性, 这样我们就不会得到另一个
d.lastTap = this.emptyTapPoint
} else {
// 使用起始坐标作为 event details 发出 tap 事件
d.emitEvent('tap', d.start)
// 记住这个 tap 的坐标, 这样我们就可以检测到 double taps(dbltap)
d.lastTap = this.coordinates(e, t)
}
// 在任何情况下, 清除计时器并转换至初始状态
d.clearTimer('holdtimeout')
d.switchTo(this.initialState)
},
holdtimeout: (d) => {
d.switchTo(this.holdState)
},
}
/**
* 变换状态 - transformState
*
* 何时进入变换状态?
* 在开始重新记录任何其他手势之前, 开始了第二次触摸.
*
* 当触摸移动时, 我们跟踪它们之间的距离和角度, 并报告变换事件(transform)中的缩放和旋转值.
*/
private transformState: FSMGestureState = {
name: 'transformState',
init: (d, e, t) => {
// 记住第2个触摸的 id
d.touch2 = t.identifier
// 获取两个 Touch 对象
const t1 = Array.prototype.some.call(
e.touches,
(i: Touch) => i.identifier === d.touch1
)
const t2 = t
// 计算并记住初始距离和角度
d.startDistance = d.lastDistance = this.touchDistance(t1, t2)
d.startDirection = d.lastDirection = this.touchDirection(t1, t2)
// 在超过阈值之前不要开始发出事件
d.scaled = d.rotated = false
},
touchstart: () => {},
touchmove: (d, e, t) => {
// 忽略未追踪的 Touches
if (t.identifier !== d.touch1 && t.identifier !== d.touch2) return
const t1: Touch = Array.prototype.some.call(
e.touches,
(i: Touch) => i.identifier === d.touch1
)
const t2: Touch = Array.prototype.some.call(
e.touches,
(i: Touch) => i.identifier === d.touch2
)
// 计算新的中点、距离和方向
const midpoint = this.midpoints(e, t1, t2)
let distance = this.touchDistance(t1, t2)
let direction = this.touchDirection(t1, t2)
const rotation = this.touchRotation(d.startDirection, direction)
// 对照阈值校验所有这些数字.
// 否则, 即使您试图保持手指不动, transform 也会过于紧张.
if (d.scaled === false) {
if (
Math.abs(distance - d.startDistance) > GestureDetector.SCALE_THRESHOLD
) {
d.scaled = true
d.startDistance = d.lastDistance = Math.floor(
d.startDistance +
GestureDetector.THRESHOLD_SMOOTHING * (distance - d.startDistance)
)
} else {
distance = d.startDistance
}
}
if (d.rotated === false) {
if (Math.abs(rotation) > GestureDetector.ROTATE_THRESHOLD) {
d.rotated = true
} else {
direction = d.startDirection
}
}
// 如果未超过阈值, 则不必触发事件.
if (d.scaled === true || d.rotated === true) {
// The detail field for the transform gesture event includes
// 'absolute' transformations against the initial values and
// 'relative' transformations against the values from the last
// transformgesture event.
// transform 手势事件的 detail 字段包括:
// 1. 针对初始值的 'absolute' 变换
// 2. 针对上一个变换手势事件值的 'relative' 变换.
d.emitEvent('transform', {
absolute: {
// transform details since gesture start
scale: distance / d.startDistance,
rotate: this.touchRotation(d.startDirection, direction),
},
relative: {
// transform since last gesture change
scale: distance / d.lastDistance,
rotate: this.touchRotation(d.lastDirection, direction),
},
midpoint: midpoint,
})
d.lastDistance = distance
d.lastDirection = direction
d.lastMidpoint = midpoint
}
},
touchend: (d, _, t) => {
// 如果任何一个手指向上, 我们的手势就结束了.
// 用户可能会移动手指并再次将其放回原位, 以开始另一个双指手势,
// 因此当其中一个手指保持竖起时, 我们无法返回到初始状态.
// 另一方面, 我们无法返回到 touchStartedState,
// 因为这意味着手指向下可能会导致点击或平移事件.
// 因此, 我们需要一个 afterTransform 状态, 以等待一个手指下降或另一个手指上升.
if (t.identifier !== d.touch1 && t.identifier !== d.touch2) return
if (t.identifier === d.touch2) {
d.touch2 = this.emptyTouchId
} else if (t.identifier === d.touch1) {
d.touch1 = d.touch2
d.touch2 = this.emptyTouchId
}
// 如果我们发出了任何 transform 事件, 那现在我们需要发出一个 transformend 事件来结束这一系列.
// 此事件的 details 使用了上次触摸移动的值, 相对数量将为1和0,
// 仅仅为了完整性而包括它们, 即使它们没有用处.
if (d.scaled || d.rotated) {
d.emitEvent('transformend', {
absolute: {
// 手势开始时的 transform details
scale: d.lastDistance / d.startDistance,
rotate: this.touchRotation(d.startDirection, d.lastDirection),
},
relative: {
// 与上次 touchmove 相比, 没有任何变化
scale: 1,
rotate: 0,
},
midpoint: d.lastMidpoint,
})
}
d.switchTo(this.afterTransformState)
},
}
/**
* 开始平移状态 - panStartedState
*
* 此时, 单个 touch 已经移动到足以超过 pan 的阈值,
* 现在我们将在每次移动后生成 pan 事件, 并在触摸结束时生成 swipe 事件.
*
* 注意:在进行 pan/swipe 手势处理时, 忽略同时发生的任何其他触摸.
*/
private panStartedState: FSMGestureState = {
name: 'panStartedState',
init: (d, e, t) => {
// 直到 touch 移动超过某个阈值, 才开始平移.
// 但我们不希望第一个事件距离太远时, pan 突然启动.
// 因此继续, 因为平移实际上开始于沿着第一次触摸和当前触摸之间的路径的点.
d.start = d.last = this.between(d.start, this.coordinates(e, t))
// 如果我们使用 touchmove 事件转换到这种状态, 则使用 handler 处理它.
// 如果我们不这样做, 那么我们最终会出现不知道其速度的滑动事件.
if (e.type === 'touchmove') {
this.panStartedState.touchmove(d, e, t)
}
},
touchstart: () => {},
touchmove: (d, e, t) => {
// 忽略未追踪的 fingers
if (t.identifier !== d.touch1) return
// 每次 touch 移动时, 发出 pan 事件, 但仍保持此状态
const current = this.coordinates(e, t)
d.emitEvent('pan', {
absolute: {
dx: current.screenX - d.start.screenX,
dy: current.screenY - d.start.screenY,
},
relative: {
dx: current.screenX - d.last.screenX,
dy: current.screenY - d.last.screenY,
},
position: current,
})
// 跟踪 pan 速度, 以便我们可以通过 swipe 报告
// 使用一个指数移动平均值对速度进行一点平滑
const dt = current.timeStamp - d.last.timeStamp
const vx = (current.screenX - d.last.screenX) / dt
const vy = (current.screenY - d.last.screenY) / dt
if (d.vx == this.emptyV) {
// 第一次, 没有平均值
d.vx = vx
d.vy = vy
} else {
d.vx =
d.vx * GestureDetector.VELOCITY_SMOOTHING +
vx * (1 - GestureDetector.VELOCITY_SMOOTHING)
d.vy =
d.vy * GestureDetector.VELOCITY_SMOOTHING +
vy * (1 - GestureDetector.VELOCITY_SMOOTHING)
}
d.last = current
},
touchend: (d, e, t) => {
// 忽略未追踪的 fingers
if (t.identifier !== d.touch1) return
// 当手指抬起时发出 swipe 事件.
// 报告起点和终点、dx、dy、dt、速度和方向
const current = this.coordinates(e, t)
const dx = current.screenX - d.start.screenX
const dy = current.screenY - d.start.screenY
// angle(角度)是一个正的度数, 从正x轴上的0开始, 顺时针递增.
let angle = (Math.atan2(dy, dx) * 180) / Math.PI
if (angle < 0) angle += 360
// 方向枚举: 'right', 'down', 'left' or 'up'
let direction
if (angle >= 315 || angle < 45) {
direction = 'right'
} else if (angle >= 45 && angle < 135) {
direction = 'down'
} else if (angle >= 135 && angle < 225) {
direction = 'left'
} else if (angle >= 225 && angle < 315) {
direction = 'up'
}
d.emitEvent('swipe', {
start: d.start,
end: current,
dx: dx,
dy: dy,
dt: e.timeStamp - d.start.timeStamp,
vx: d.vx,
vy: d.vy,
direction: direction,
angle: angle,
})
d.switchTo(this.initialState)
},
}
/**
* 长按状态 - holdState
*
* 何时进入长按状态?
* 用户 touch & hold 足够长的时间, 且只有较少的移动.
*
* 进入状态时的动作: 发出一个 holdstart 事件.
*
* holdstart 之后的运动生成 holdmove 事件。
* 并且当触摸结束时, 我们生成保持结束事件。
* holdmove 和 holdend 事件的使用类似于使用鼠标的UI中的拖放事件。
* 目前, 这些事件仅报告了触摸的坐标。是否还需其他细节?
*/
private holdState: FSMGestureState = {
name: 'holdState',
init: function (d) {
d.emitEvent('holdstart', d.start)
},
touchstart: () => {},
touchmove: (d, e, t) => {
var current = this.coordinates(e, t)
d.emitEvent('holdmove', {
absolute: {
dx: current.screenX - d.start.screenX,
dy: current.screenY - d.start.screenY,
},
relative: {
dx: current.screenX - d.last.screenX,
dy: current.screenY - d.last.screenY,
},
position: current,
})
d.last = current
},
touchend: (d, e, t) => {
const current = this.coordinates(e, t)
d.emitEvent('holdend', {
start: d.start,
end: current,
dx: current.screenX - d.start.screenX,
dy: current.screenY - d.start.screenY,
})
d.switchTo(this.initialState)
},
}
/**
* 结束变换状态 - afterTransformState
*
* 我们做一个 transform, 一个手指抬起.
* 等待那根手指落下, 或者另一根手指也向上.
*/
private afterTransformState: FSMGestureState = {
name: 'afterTransformState',
init: () => {},
touchstart: (d, e, t) => {
d.switchTo(this.transformState, e, t)
},
touchend: (d, _, t) => {
if (t.identifier === d.touch1) {
d.switchTo(this.initialState)
}
},
touchmove: () => {},
}
/**
* 触摸点坐标生成器
*
* 返回一个包含触摸事件空间和时间坐标的对象.
* 我们冻结对象以使其不可变, 用于在事件中传递它, 而不用担心值被更改.
*/
private coordinates(e: TouchEvent, t: Touch): TapPoint {
return Object.freeze({
screenX: t.screenX,
screenY: t.screenY,
clientX: t.clientX,
clientY: t.clientY,
timeStamp: this.eventTime(e),
})
}
/**
* 检查两个 tap 在时间和空间上是否足够接近, 以触发 dbltap 事件.
*/
private isDoubleTap(lastTap: TapPoint, thisTap: TapPoint): boolean {
// console.log(lastTap, thisTap)
const dx = Math.abs(thisTap.screenX - lastTap.screenX)
const dy = Math.abs(thisTap.screenY - lastTap.screenY)
const dt = thisTap.timeStamp - lastTap.timeStamp
return (
dx < GestureDetector.DOUBLE_TAP_DISTANCE &&
dy < GestureDetector.DOUBLE_TAP_DISTANCE &&
dt < GestureDetector.DOUBLE_TAP_TIME
)
}
/**
* 计算两次 touch 之间的距离
*/
private touchDistance(t1: Touch, t2: Touch): number {
const dx = t2.screenX - t1.screenX
const dy = t2.screenY - t1.screenY
return Math.sqrt(dx * dx + dy * dy)
}
/**
* 计算两次 touch 间直线的方向(单位为角度)
*
* 返回一个数字 d, 区间为 (-180, 180]
*/
private touchDirection(t1: Touch, t2: Touch): number {
return (
(Math.atan2(t2.screenY - t1.screenY, t2.screenX - t1.screenX) * 180) /
Math.PI
)
}
/**
* 获取中间点函数
*
* 类似于 coordinates(), 但返回的是两次触摸之间的中点
*/
private midpoints(e: TouchEvent, t1: Touch, t2: Touch): TapPoint {
return Object.freeze({
screenX: Math.floor((t1.screenX + t2.screenX) / 2),
screenY: Math.floor((t1.screenY + t2.screenY) / 2),
clientX: Math.floor((t1.clientX + t2.clientX) / 2),
clientY: Math.floor((t1.clientY + t2.clientY) / 2),
timeStamp: this.eventTime(e),
})
}
/**
* 返回事件的时间戳(毫秒)
*
* 在Gecko中, 合成事件似乎以微秒而不是毫秒为单位.
* 因此, 如果时间戳比当前时间大得多, 假设它以微秒为单位, 除以1000.
*/
private eventTime(e: TouchEvent): number {
const ts = e.timeStamp
if (ts > 2 * Date.now()) return Math.floor(ts / 1000)
else return ts
}
/**
* 角度计算函数
*
* 计算方向d1和方向d2之间的顺时针角度.
* 返回角度 angle 所属区间 (-180, 180]
*/
private touchRotation(d1: number, d2: number): number {
let angle = d2 - d1
if (angle > 180) angle -= 360
else if (angle <= -180) angle += 360
return angle
}
/**
* 生成中间点函数
*
* 给定坐标对象 c1 和 c2, 返回表示沿着这些点之间的线的点和时间的新坐标对象。
* 点的位置由阈值平滑常数(THRESHOLD_SMOOTHING)控制
*/
private between(c1: TapPoint, c2: TapPoint): TapPoint {
const r = GestureDetector.THRESHOLD_SMOOTHING
return Object.freeze({
screenX: Math.floor(c1.screenX + r * (c2.screenX - c1.screenX)),
screenY: Math.floor(c1.screenY + r * (c2.screenY - c1.screenY)),
clientX: Math.floor(c1.clientX + r * (c2.clientX - c1.clientX)),
clientY: Math.floor(c1.clientY + r * (c2.clientY - c1.clientY)),
timeStamp: Math.floor(c1.timeStamp + r * (c2.timeStamp - c1.timeStamp)),
})
}
public startDetecting(): void {
this.eventtypes.forEach((t) => {
this.element.addEventListener(t, this)
})
}
public stopDetecting(): void {
this.eventtypes.forEach((t) => {
this.element.removeEventListener(t, this)
})
}
public handleEvent(e: TouchEvent): void {
let handler: TouchHandler = this.state[e.type]
if (!handler) return
// 如果这是 touch 事件, 请分别处理每个更改的 touch
if (e.changedTouches) {
for (let i = 0; i < e.changedTouches.length; i++) {
console.log(this.state.name)
handler(this, e, e.changedTouches[i])
// 第一次改变的 touch 可能改变了 FSM 的状态.
handler = this.state[e.type]
if (!handler) return
}
} else {
// 否则, 直接将事件派分给 handler
handler(this, e)
}
}
private startTimer(type: TimerType, time: number): void {
this.clearTimer(type)
this.timers[type] = setTimeout(() => {
this.timers[type] = null
// 以下即为 this.state.holdtimeout(this)
this.state[type]?.(this)
}, time)
}
private clearTimer(type: TimerType): void {
if (this.timers[type]) {
clearTimeout(this.timers[type])
this.timers[type] = null
}
}
/**
* FSM状态切换函数
*
* 切换到一个新的 FSM State, 并调用该状态对应的 init() 方法(如果存在的话).
* event 和 touch 都是可选的, 仅传递给状态初始化函数。
*/
private switchTo(
state: FSMGestureState,
event?: TouchEvent,
touch?: Touch
): void {
this.state = state
state?.init(this, event as TouchEvent, touch as Touch)
}
private emitEvent(
type: EmitEventType,
detail: EmitEventDetail<EmitEventType>
): void {
if (this.target === this.emptyTarget) {
console.error('Attempt to emit event with no target')
return
}
// const event = this.element.ownerDocument.createEvent('CustomEvent');
// event.initCustomEvent(type, true, true, detail);
// this.target.dispatchEvent(event);
const event = new CustomEvent(type, {
bubbles: true,
cancelable: true,
detail: detail,
})
console.log(event)
this.target.dispatchEvent(event)
}
}

View File

@ -0,0 +1,88 @@
import {html, css} from 'lit'
import {customElement} from 'lit/decorators.js'
import GestureDector from '../../lib/gesture/gesture-detector'
import {StarBaseElement} from '../base/star-base-element'
@customElement('home-bar-indicator')
export class HomeBarIndicator extends StarBaseElement {
constructor() {
super()
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)
})
}
render() {
return html``
}
static styles = css`
:host {
position: absolute;
bottom: 0;
width: 100vw;
height: 100vh;
background-color: yellow;
user-select: none;
}
a {
position: absolute;
width: 100%;
height: 100%;
background-color: yellow;
}
`
}
declare global {
interface HTMLElementTagNameMap {
'home-bar-indicator': HomeBarIndicator
}
}

View File

@ -658,7 +658,6 @@ class GaiaContainer extends LitElement {
realAppendChild(pagination = 0, ...args: HTMLElement[]) {
if (pagination < 0) return
let page = this.pages[pagination]
if (!page) {
page = this.addPage()
}

View File

@ -380,6 +380,14 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
if (this._status && evt.target === this.container) {
this.close()
}
if (
this._status &&
(evt.target as HTMLElement).tagName !== 'SITE-ICON'
) {
evt.preventDefault()
evt.stopImmediatePropagation()
}
break
case 'touchstart':
case 'touchmove':
if (

View File

@ -1 +1 @@
# 九宫格-Grid
# 九宫格-Grid

View File

@ -1 +1 @@
# 标题栏-header
# 标题栏-header

View File

@ -2,4 +2,4 @@
## indicator-page-point 页面圆点指示器
用于指示页面所在位置
用于指示页面所在位置

View File

@ -11,9 +11,7 @@ export class IndicatorPagePoint extends LitElement {
#firstRender = true
protected shouldUpdate(
_changedProperties: PropertyValueMap<this>
): boolean {
protected shouldUpdate(_changedProperties: PropertyValueMap<this>): boolean {
let isShouldUpdate = true
if (this.total < 1) {

View File

@ -1,16 +1,17 @@
# 输入框-Input
工作职责:
- 输入信息
## 特点
拥有设置焦点的可配置属性如进到一个页面立即聚焦于该input弹起输入法
拥有设置焦点的可配置属性,如进到一个页面立即聚焦于该 input弹起输入法
删除图标在 input 中文字为空时应不显示
input应拥有可配置的默认值(对于一些总要类型,需强制配置)
input 应拥有可配置的默认值(对于一些总要类型,需强制配置)
## 类型包括
- input输入|删除图标
- input 输入|删除图标
## 特殊说明
## 特殊说明

View File

@ -1,15 +1,17 @@
# 行-Line
工作职责:
- `组合排列``有限种`内容填充物
- ` 组合排列``有限种 `内容填充物
## 特点
行宽 - 100%填充父容器给的宽
行高 - 存在一个min-height实际行高由行内填充物决定
行高 - 存在一个 min-height实际行高由行内填充物决定
行中独立嵌套其他内容时,使用 slot
## 说明:
- | 用途:偏左侧 | 偏右侧
- () 用途:可选内容
- 主文字在上,辅助文字在下
@ -30,10 +32,10 @@
- 右侧图标,通常包括:
- 下一级图标
- 勾选类型图标
- Loading图标
- Loading 图标
- 云下载图标
- 图标组中的单个图标或多个图标
- 图标组加密锁图标、wifi信号强度图标、其他状态图标
- 图标组加密锁图标、wifi 信号强度图标、其他状态图标
- 选择器,通常包括:
- 时间选择器
- 日期选择器
@ -54,9 +56,9 @@
- 一排复选框
- 滑块控件
- 步进器控件
- input输入
- input 输入
- 纯文字
- 组合的SVG
- 组合的 SVG
- (左侧占位) 主文字(辅助信息) | (右侧图标)
- (左侧占位) (图标) 主文字(辅助信息) | (右侧占位)
- (左侧占位) (图标) 主文字(辅助信息) | 开关 (右侧图标)
@ -70,9 +72,9 @@
iPhone-通用-关于本机-SEID
### 组合的SVG
### 组合的 SVG
iPhone-通用-iPhone储存空间
iPhone-通用-iPhone 储存空间
### 一排复选框
@ -87,8 +89,8 @@
### (大图标) 主文字(辅助信息) | 按钮
iPhone-通用-关于本机-支持范围-Apple支持
iPhone-通用-iPhone储存空间-任意应用内
iPhone-通用-关于本机-支持范围-Apple 支持
iPhone-通用-iPhone 储存空间-任意应用内
### (左侧占位) (图标) 主文字(辅助信息) | (右侧占位)

View File

@ -0,0 +1,30 @@
# starwebcomponents——overflowmenu
## 介绍
### overflowmenu溢出菜单。
## 新需求(主页面要求——罗 9.5
1. 外部颜色控制(思路:使用自定义 css 样式,如: --test-colorXXX p{color --test-color},通过修改自定义 css 样式的值达到从外部修改组件颜色)
2. 弹出菜单时的越界判断,包括主、副屏切换时的图标定位以及旋转屏幕时的定位
思路:
1首先获取 button 在屏幕显示的 left、right、top 和 bottom 值以及 menu 的 width 和 height
2对于右侧边界right >= width ? true 则 menu 的 left = button 的 left : false 则 menu 的 right = button 的 right
3对于下边界bottom >= height ? true 则 menu 的 top = button 的 bottom false 则 menu 的 bottom = button 的 top
3. 外部控制接口,事件还是属性(暂定)
4. 弹出的菜单绑定在父节点上以供调用,减少重复使用(思路:后续通过 overlay 组件实现)
## 问题(9.6)
1. 首次点击最右侧的按钮是获取到的菜单宽度和高度与实际不符:(该问题已消失,但不知道为何消失)
2. 点击空白处无法关闭菜单栏(解决方法:将点击事件绑定在父容器中)
## 新要求:(9.7)
1将不需要修改的“var”变量声明变成“const”(已修改)
2变量命名要直观且有解释已修改变量命名规范并添加对应注释
3点击一个按钮后其余按钮应关闭方法同1
4可以将 slot 增加名称从而将 div 以删除
5定位方式修改为相对定位将将 fixed 改为 relative达到适应效果
6控制菜单栏宽度菜单栏中 star-ul 中的 ul 标签负责扩充大小,修改其 width 值

View File

@ -0,0 +1,110 @@
import {LitElement, html, HTMLTemplateResult, CSSResultArray} from 'lit'
import {customElement, property, queryAssignedElements} from 'lit/decorators.js'
import '../button/button'
import {sharedStyles} from './overflowmenustyle'
@customElement('star-overflowmenu')
export class StarOverflowMenu extends LitElement {
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
@property({type: String}) type = 'base'
@property({type: String}) size = 'medium'
@property({type: String}) icon = ''
@property({type: String}) label = ''
@property({type: Boolean}) disabled = false
@property({type: Boolean, reflect: true}) open = false
//获取slot元素
@queryAssignedElements({flatten: true})
_evenEl: any
_getElement() {
// 获取网页宽度用于判断菜单显示位置是否越界
const bodywidth = document.documentElement.clientWidth
const bodyheight = document.documentElement.clientHeight
// 获取菜单所在div,用于控制menu显示或隐藏ts默认使用Element需转换为HTMLElement
const mu = this.renderRoot.querySelector('#menuitem') as HTMLElement
// 获取star-button相对屏幕的位置
const buttonposition = this.renderRoot
.querySelector('star-button')
?.getBoundingClientRect()
// star-button的top、bottom、left及right值
const buttontop = Number(buttonposition?.top)
const buttonbottom = Number(buttonposition?.bottom)
const buttonleft = Number(buttonposition?.left)
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 {
for (var i = 0; i < this._evenEl.length; i++) {
mu.style.display = 'none'
this.open = true
}
}
}
protected firstUpdated(): void {
this._getElement()
}
getBaseMenu(): HTMLTemplateResult {
return html`
<star-button
type="icononly"
icon=${this.icon}
@click=${this._getElement}
></star-button>
<div id="menuitem">
<slot></slot>
</div>
`
}
render() {
return this.getBaseMenu()
}
}
declare global {
interface HTMLElementTagNameMap {
'star-overflowmenu': StarOverflowMenu
}
}

View File

@ -0,0 +1,34 @@
import {css, CSSResult} from 'lit'
export const sharedStyles: CSSResult = css`
:host {
width: auto;
display: block;
text-align: left;
}
#menuitem {
width: auto;
position: inherit;
}
star-button {
display: block;
width: 35px;
}
::slotted(star-ul) {
margin: 0;
z-index: 2;
}
html {
scrollbar-width: none;
}
body {
margin: 0;
padding: 0;
background: #f0f0f0;
}
`

View File

@ -1,5 +1,5 @@
# 全局overlay
# 全局 overlay
## 类型包括:
- 置于底部的overlay内可填充ul
- 置于底部的 overlay内可填充 ul

View File

@ -1,11 +1,10 @@
# star-picker
星光组件--picker组件(8月30日)
星光组件--picker 组件(8 30 日)
# 介绍
1. type实现了用type属性控制不同大小的picker选择框包括small、medium、large、extralarge
1. type实现了用 type 属性控制不同大小的 picker 选择框,包括 small、medium、large、extralarge
```html demo
<star-picker type="small" hide-value>
@ -15,7 +14,7 @@
</star-picker>
<star-picker type="medium" hide-value>
<star-picker-option value="1" >red</star-picker-option>
<star-picker-option value="1">red</star-picker-option>
<star-picker-option value="2">yellow</star-picker-option>
<star-picker-option value="3">blue</star-picker-option>
</star-picker>
@ -34,6 +33,7 @@
```
2. hide-value实现了隐藏选择项的索引值
```html demo
<star-picker hide-value>
<star-picker-option value="1">red</star-picker-option>
@ -42,11 +42,12 @@
</star-picker>
```
3. disable实现了picker选择框的禁用状态
3. disable实现了 picker 选择框的禁用状态
```html demo
<star-picker disable>
<star-picker-option value="1">red</star-picker-option>
<star-picker-option value="2">yellow</star-picker-option>
<star-picker-option value="3">blue</star-picker-option>
</star-picker>
```
```

View File

@ -1,129 +1,129 @@
import {css, CSSResult} from 'lit'
export const sharedStyles: CSSResult = css`
* {
box-sizing: border-box;
}
* {
box-sizing: border-box;
}
:host {
display: block;
border-radius: 4px;
}
:host {
display: block;
border-radius: 4px;
}
:host,
.input {
font-family: sans-serif;
font-size: 15px;
outline: none;
}
:host,
.input {
font-family: sans-serif;
font-size: 15px;
outline: none;
}
.input,
.value-container {
background: white;
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px 10px;
width: 100%;
}
.input,
.value-container {
background: white;
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px 10px;
width: 100%;
}
.input:focus {
background: #FFFFFF;
border: 1px solid #2DA7E0;
box-shadow: inset 0 0 0 1000px #FFFFFF!important;
}
.input:focus {
background: #ffffff;
border: 1px solid #2da7e0;
box-shadow: inset 0 0 0 1000px #ffffff !important;
}
.main-options-container {
position: relative;
}
.main-options-container {
position: relative;
}
.options-container {
background: white;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.15);
position: absolute;
top: 38px;
width: 100%;
}
.options-container {
background: white;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.15);
position: absolute;
top: 38px;
width: 100%;
}
.option,
.value-container {
display: flex;
}
.option,
.value-container {
display: flex;
}
.option {
padding: 10px 8px;
cursor: pointer;
}
.option {
padding: 10px 8px;
cursor: pointer;
}
.option:hover {
background: #f3f3f3;
}
.option:hover {
background: #f3f3f3;
}
.option-value,
.value,
.magnifying-glass {
flex-shrink: 0;
width: 15px;
}
.option-value,
.value,
.magnifying-glass {
flex-shrink: 0;
width: 15px;
}
.option-label,
.label,
.placeholder {
flex-grow: 1;
}
.option-label,
.label,
.placeholder {
flex-grow: 1;
}
.placeholder-text {
color: #777;
font-style: italic;
}
.placeholder-text {
color: #777;
font-style: italic;
}
.label,
.option-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.label,
.option-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.no-results {
color: #777;
font-style: italic;
padding: 8px 10px;
}
.no-results {
color: #777;
font-style: italic;
padding: 8px 10px;
}
:host([hide-value]) .value,
:host([hide-value]) .option-value {
display: none;
}
:host([hide-value]) .value,
:host([hide-value]) .option-value {
display: none;
}
:host([disable]){
pointer-events: none;
color: #c0c4cc;
background-image: none;
background-color: #fff;
border-color: #ebeef5;
}
:host([disable]) {
pointer-events: none;
color: #c0c4cc;
background-image: none;
background-color: #fff;
border-color: #ebeef5;
}
.small {
padding: 4px 6px;
font-size: 10px;
border-radius: 3px;
}
.small {
padding: 4px 6px;
font-size: 10px;
border-radius: 3px;
}
.medium {
padding: 5px 10px;
font-size: 14px;
border-radius: 4px;
}
.medium {
padding: 5px 10px;
font-size: 14px;
border-radius: 4px;
}
.large {
padding: 8px 15px;
font-size: 18px;
border-radius: 5px;
}
.large {
padding: 8px 15px;
font-size: 18px;
border-radius: 5px;
}
.extralarge {
padding: 10px 20px;
font-size: 20px;
border-radius: 5px;
}
`
.extralarge {
padding: 10px 20px;
font-size: 20px;
border-radius: 5px;
}
`

View File

@ -1,21 +1,19 @@
/* eslint-disable lit-a11y/click-events-have-key-events */
import { html,LitElement, CSSResultArray } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import {html, LitElement, CSSResultArray} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import {styleMap} from 'lit/directives/style-map.js'
import {sharedStyles} from './picker-styles'
interface StarPickerOption {
value: string;
label: string;
value: string
label: string
}
export enum PickerType {
SMALL = 'small',
MEDIUM = 'medium',
LARGE = 'large',
EXTRALARGE = 'extralarge'
EXTRALARGE = 'extralarge',
}
@customElement('star-picker')
@ -24,78 +22,83 @@ export class StarPicker extends LitElement {
return [sharedStyles]
}
@property({ type: PickerType}) type = '';
@property({type: PickerType}) type = ''
@property({ type: String }) tabindex = '0';
@property({type: String}) tabindex = '0'
@property({ type: String }) placeholder = '';
@property({type: String}) placeholder = ''
@property({ type: String }) value = '';
@property({type: String}) value = ''
@property({ type: Boolean, attribute: 'hide-value' }) hideValue = false;
@property({type: Boolean, attribute: 'hide-value'}) hideValue = false
@property({ type: Boolean, attribute: 'disable'}) disAble = false;
@property({type: Boolean, attribute: 'disable'}) disAble = false
@property({ type: Boolean, attribute: 'small'}) small = false;
@property({type: Boolean, attribute: 'small'}) small = false
@property({ type: Boolean, attribute: 'medium'}) medium = false;
@property({type: Boolean, attribute: 'medium'}) medium = false
@property({ type: Boolean, attribute: 'large'}) large = false;
@property({type: Boolean, attribute: 'large'}) large = false
@property({ type: Boolean, attribute: 'extralarge'}) extralarge = false;
@property({type: Boolean, attribute: 'extralarge'}) extralarge = false
@property({ type: String, attribute: 'value-pattern' }) valuePattern =
'\\d{1,5}';
@property({type: String, attribute: 'value-pattern'}) valuePattern =
'\\d{1,5}'
@state() private _searchState: Boolean = false;
@state() private _searchState: Boolean = false
@state() private _options: StarPickerOption[] = [];
@state() private _options: StarPickerOption[] = []
@state() private _query: String = '';
@state() private _query: String = ''
firstUpdated() {
this.setAttribute('tabindex', this.tabindex);
this.setAttribute('tabindex', this.tabindex)
this.addEventListener('keydown', e => {
this.addEventListener('keydown', (e) => {
if (this._searchState === false) {
this._onKeyDownValueContainer(e);
this._onKeyDownValueContainer(e)
}
});
})
this.addEventListener('blur', () => { //聚焦事件
this._searchState = false;
this.addEventListener('blur', () => {
//聚焦事件
this._searchState = false
const pattern = new RegExp(this.valuePattern); //正则表达式匹配字符串
if (pattern.test(this._query as string)) { //返回是否匹配
this.value = this._query as string;//类型断言
const pattern = new RegExp(this.valuePattern) //正则表达式匹配字符串
if (pattern.test(this._query as string)) {
//返回是否匹配
this.value = this._query as string //类型断言
}
});
})
this._updateOptionsList();
this._updateOptionsList()
const observer = new MutationObserver(() => { //js监听dom元素的属性变化
this._updateOptionsList();
});
const observer = new MutationObserver(() => {
//js监听dom元素的属性变化
this._updateOptionsList()
})
//开始观察目标节点的DOM元素的属性变化
observer.observe(this as Node, { //将this作为节点
observer.observe(this as Node, {
//将this作为节点
attributes: true,
characterData: true,
childList: true,
subtree: true,
});
})
}
render() {
const selectedOption = this._options.find( //查找数组中符合条件的第一个元素
option => option.value === this.value //比较是不是严格相等
);
const label = selectedOption ? selectedOption.label : null; //判断返回值
const selectedOption = this._options.find(
//查找数组中符合条件的第一个元素
(option) => option.value === this.value //比较是不是严格相等
)
const label = selectedOption ? selectedOption.label : null //判断返回值
return html`
<div
class="main-options-container"
style=${styleMap({ display: this._searchState ? 'block' : 'none' })}
style=${styleMap({display: this._searchState ? 'block' : 'none'})}
>
<input
class="input ${this.type}"
@ -106,9 +109,9 @@ export class StarPicker extends LitElement {
/>
<div class="options-container">
${this._options
.filter(option => this._doesOptionMatchQuery(option))
.filter((option) => this._doesOptionMatchQuery(option))
.map(
option => html`
(option) => html`
<div
class="option"
@click="${() => this._onClickOption(option)}"
@ -117,15 +120,17 @@ export class StarPicker extends LitElement {
<div class="option-label">${option.label}</div>
</div>
`
)}
${this._options.filter(option => this._doesOptionMatchQuery(option)) //过滤元素
)}
${this._options.filter((option) => this._doesOptionMatchQuery(option)) //过滤元素
.length === 0 && this._query.length > 0
? html` <div class="no-results">No results</div> `
? html`
<div class="no-results">No results</div>
`
: ''}
</div>
</div>
<div
style=${styleMap({ display: this._searchState ? 'none' : 'flex' })}
style=${styleMap({display: this._searchState ? 'none' : 'flex'})}
@click="${this._onClickValueContainer}"
class="value-container ${this.type}"
>
@ -134,47 +139,52 @@ export class StarPicker extends LitElement {
<div class="value">${this.value}</div>
<div class="label">
${label ||
html`<span class="placeholder-text">Loading...</span>`}
html`
<span class="placeholder-text">Loading...</span>
`}
</div>
<div @click="${this._onClickClearButton}"><img src="src/assets/close.svg" class="magnifying-glass">
<div @click="${this._onClickClearButton}">
<img src="src/assets/close.svg" class="magnifying-glass" />
</div>
`
: html`
<div class="placeholder">
<span class="placeholder-text">${this.placeholder}</span>
</div>
<div class="magnifying-glass"><img src="src/assets/down.svg"></div>
<div class="magnifying-glass">
<img src="src/assets/down.svg" />
</div>
`}
</div>
`;
`
}
private _updateOptionsList() {
const options = this.querySelectorAll('star-picker-option');
this._options = Array.from(options).map(option => ({
const options = this.querySelectorAll('star-picker-option')
this._options = Array.from(options).map((option) => ({
value: option.getAttribute('value'),
label: option.textContent,
})) as StarPickerOption[];
})) as StarPickerOption[]
}
private _onChangeQueryInput(e: KeyboardEvent) {
const queryInput = e.target! as HTMLInputElement;
this._query = queryInput.value;
const queryInput = e.target! as HTMLInputElement
this._query = queryInput.value
}
private _onKeyDownQueryInput(e: KeyboardEvent) {
if (e.key === 'Tab') {
e.preventDefault();
this._searchState = false;
e.preventDefault()
this._searchState = false
const pattern = new RegExp(this.valuePattern);
const pattern = new RegExp(this.valuePattern)
if (pattern.test(this._query as string)) {
this.value = this._query as string;
this.value = this._query as string
}
this.updateComplete.then(() => {
this.focus();
});
this.focus()
})
}
}
@ -182,49 +192,50 @@ export class StarPicker extends LitElement {
return (
option.label.toLowerCase().includes(this._query.toLowerCase()) || //转换位小写后判断是否包含
option.value.toLocaleLowerCase() === this._query.toLocaleLowerCase()
);
)
}
private _onClickOption(option: StarPickerOption) {
this.value = option.value;
this._searchState = false;
this.updateComplete.then(() => { //更新完成
this.focus(); //聚焦元素
});
this.value = option.value
this._searchState = false
this.updateComplete.then(() => {
//更新完成
this.focus() //聚焦元素
})
}
private _onClickValueContainer() {
this._openOptionsContainer();
this._openOptionsContainer()
}
private _onKeyDownValueContainer(e: KeyboardEvent) {
if (e.code === 'Space') {
this._openOptionsContainer();
e.preventDefault();
this._openOptionsContainer()
e.preventDefault()
} else if (e.key === 'Delete' || e.key === 'Backspace') {
this.value = '';
this.value = ''
}
}
private _onClickClearButton(e: MouseEvent) {
e.stopPropagation();
this.value = '';
e.stopPropagation()
this.value = ''
this.updateComplete.then(() => {
this.focus();
});
this.focus()
})
}
private _openOptionsContainer() {
this._searchState = true;
this._searchState = true
if (this.value !== '' && this.hideValue === false) {
this._query = this.value;
this._query = this.value
} else {
this._query = '';
this._query = ''
}
this.updateComplete.then(() => {
this.shadowRoot?.querySelector('input')?.select();
});
this.shadowRoot?.querySelector('input')?.select()
})
}
}
@ -233,5 +244,3 @@ declare global {
'star-picker': StarPicker
}
}

View File

@ -1,6 +1,6 @@
# 提示输入弹窗-Prompt
参考UI设计
参考UI 设计
## 内容排布
@ -17,6 +17,7 @@
```
特点:
- 支持模态模式
- 支持非模态模式
@ -24,4 +25,4 @@
- button
- input
- 外部链接slot
- 外部链接 slot

View File

@ -1,5 +1,5 @@
import { html, css, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import {html, css, LitElement} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
@customElement('star-prompt')
export class StarPrompt extends LitElement {
@ -10,18 +10,14 @@ export class StarPrompt extends LitElement {
bar = ''
render() {
return html`
`
return html``
}
static styles = css`
`
static styles = css``
}
declare global {
interface HTMLElementTagNameMap {
'star-prompt': StarPrompt
}
}
}

View File

@ -1,71 +1,48 @@
# star-radio and star-radio group
星光 Web 组件——单选框组件radio组件介绍8月31日)
星光 Web 组件——单选框组件radio 组件介绍8 月 31 日)
## 介绍
star-radio和star-radio-group为单选框按钮和单选框按钮组一般在使用过程中都是同时使用。
star-radio属性
### 1、checkedType属性
单选框风格样式类型,checkedType="symbol"为默认类型(√),checkedType="round"为圆圈类型,后续根据具体要求可扩展定义
```html demo
<star-radio-group checkedType="round">
<star-radio></star-radio>
<star-radio-group>
```
### 2、checked属性
Booloean类型checked=true,单选框选中状态checked=false,未选中状态。默认为false不选中状态。
```html demo
<star-radio-group>
<star-radio checked></star-radio>
<star-radio></star-radio>
<star-radio-group>
```
### 3、checkedLocation属性
单选框选中图标位置状态checkedLocation="left" 选中图标在左侧栏checkedLocation="right" 选中图标在右侧栏,默认在左侧栏
```html demo
<star-radio-group checkedLocation="right">
<star-radio checked></star-radio>
<star-radio></star-radio>
<star-radio-group>
```
### 4、label属性
UI界面中的正文主体文本内容
```html demo
<star-radio-group checkedLocation="right">
<star-radio label="content1" checked></star-radio>
<star-radio> label="content2"</star-radio>
<star-radio-group>
```
### 5、node属性
UI界面中的正文主体文本内容下方补充扩展文本内容
``` html demo
<star-radio-group checkedLocation="right">
<star-radio label="content1" label2="expand1" checked></star-radio>
<star-radio> label="content2" label2="expand2" </star-radio>
<star-radio-group>
```
### 6、icon属性
控制ui界面中的图标字体样式属性
```html demo
<star-radio-group checkedLocation="right" checkedType="round">
<star-radio icon="add" label="content1" label2="expand1" checked></star-radio>
<star-radio> icon="keyboard" label="content2" label2="expand2" </star-radio>
<star-radio-group>
```
### 7、iconcolor属性
控制图标字体颜色样式属性和icon属性同时使用时起作用
```html demo
<star-radio-group checkedLocation="right" checkedType="round">
<star-radio icon="add" iconcolor="#226dee" label="content1" label2="expand1" checked></star-radio>
<star-radio> icon="keyboard" iconcolor="#226dee" label="content2" label2="expand2" </star-radio>
<star-radio-group>
```
### 8、disabled属性
控制单选框按钮禁用状态默认为false。
```html demo
<star-radio-group checkedLocation="right" checkedType="round">
<star-radio icon="add" iconcolor="#226dee" label="content1" label2="expand1" disabled></star-radio>
<star-radio> icon="keyboard" iconcolor="#226dee" label="content2" label2="expand2" disabled</star-radio>
<star-radio-group>
```
star-radio 和 star-radio-group 为单选框按钮和单选框按钮组,一般在使用过程中都是同时使用。
star-radio 属性:
### 1、checkedType 属性
单选框风格样式类型,checkedType="symbol"为默认类型(√),checkedType="round"为圆圈类型,后续根据具体要求可扩展定义
`html demo <star-radio-group checkedType="round"> <star-radio></star-radio> <star-radio-group> `
### 2、checked 属性
Booloean 类型checked=true,单选框选中状态checked=false,未选中状态。默认为 false不选中状态。
`html demo <star-radio-group> <star-radio checked></star-radio> <star-radio></star-radio> <star-radio-group> `
### 3、checkedLocation 属性
单选框选中图标位置状态checkedLocation="left" 选中图标在左侧栏checkedLocation="right" 选中图标在右侧栏,默认在左侧栏
`html demo <star-radio-group checkedLocation="right"> <star-radio checked></star-radio> <star-radio></star-radio> <star-radio-group> `
### 4、label 属性
UI 界面中的正文主体文本内容
`html demo <star-radio-group checkedLocation="right"> <star-radio label="content1" checked></star-radio> <star-radio> label="content2"</star-radio> <star-radio-group> `
### 5、node 属性
UI 界面中的正文主体文本内容下方补充扩展文本内容
`html demo <star-radio-group checkedLocation="right"> <star-radio label="content1" label2="expand1" checked></star-radio> <star-radio> label="content2" label2="expand2" </star-radio> <star-radio-group> `
### 6、icon 属性
控制 ui 界面中的图标字体样式属性
`html demo <star-radio-group checkedLocation="right" checkedType="round"> <star-radio icon="add" label="content1" label2="expand1" checked></star-radio> <star-radio> icon="keyboard" label="content2" label2="expand2" </star-radio> <star-radio-group> `
### 7、iconcolor 属性
控制图标字体颜色样式属性,和 icon 属性同时使用时起作用
`html demo <star-radio-group checkedLocation="right" checkedType="round"> <star-radio icon="add" iconcolor="#226dee" label="content1" label2="expand1" checked></star-radio> <star-radio> icon="keyboard" iconcolor="#226dee" label="content2" label2="expand2" </star-radio> <star-radio-group> `
### 8、disabled 属性
控制单选框按钮禁用状态默认为false。
`html demo <star-radio-group checkedLocation="right" checkedType="round"> <star-radio icon="add" iconcolor="#226dee" label="content1" label2="expand1" disabled></star-radio> <star-radio> icon="keyboard" iconcolor="#226dee" label="content2" label2="expand2" disabled</star-radio> <star-radio-group> `

View File

@ -64,4 +64,4 @@ declare global {
interface HTMLElementTagNameMap {
'star-radio-group': StarRadioGroup
}
}
}

View File

@ -11,7 +11,7 @@ export const sharedStyles: CSSResult = css`
cursor: default;
pointer-events: none;
}
/*按钮选中状态*/
/*按钮选中状态*/
.star-radio.checked {
display: flex;
cursor: pointer;
@ -73,4 +73,4 @@ export const sharedStyles: CSSResult = css`
.star-radio.checked .label {
color: #1561f0;
}
`
`

View File

@ -84,4 +84,4 @@ declare global {
interface HTMLElementTagNameMap {
'star-radio': StarRadio
}
}
}

View File

@ -33,14 +33,14 @@ export class StarAnimateSection extends LitElement {
position: absolute;
width: 100%; /* 100vw会有x轴溢出 */
height: 100vh;
overflow: hidden;
/* overflow: hidden; */
/* height: calc(100vh + 1px); */ /* 手动制造溢出 */
animation-duration: 0.3s;
background-color: #f0f0f0;
}
section {
/* overflow: auto; */
overflow: auto;
}
`
}

View File

@ -4,4 +4,4 @@
付费|免费|畅销排行
过去24小时|过去4天
过去 24 小时|过去 4

View File

@ -5,4 +5,4 @@
## 特点
- 支持多选
- 强制多选类型`的情况下,选择只有一个时,将其置灰
- 强制多选类型`的情况下,选择只有一个时,将其置灰

View File

@ -1,12 +1,68 @@
# 滑块-Slider
工作职责:
- 滑块空间
- 滑块拖动后返回 value 值
说明:
- | 用途:分割位置
## 类型包括:
1. 默认滑块
类型包括:
```
<star-slider></star-slider>
```
- 左侧图标|滑块|右侧图标
2. `coverWidth` --- 初始填充
```
<star-slider coverWidth="150px"></star-slider>
<star-slider coverWidth="50%"></star-slider>
```
3. `disabled` --- 禁用滑块
```
<star-slider disabled></star-slider>
<star-slider disabled coverWidth="150px"></star-slider>
```
4. `unfilled` --- 滑块中小球左侧不进行填充
```
<star-slider unfilled></star-slider>
<star-slider unfilled coverWidth="30%"></star-slider>
<star-slider unfilled coverWidth="30%" disabled></star-slider>
```
5. `Tick` --- 分格滑块(默认是 unfilled 属性)
min=0,mix=100按照需求填写 step(每一格)的大小<br>
example : <br>
step="25" 表示把 slider 分为 4 块<br>
coverWidth="40%" 表示初始小球落在第二格上
```
<star-slider tick step="10"></star-slider>
<star-slider tick step="20" coverWidth="40%" disabled></star-slider>
```
6. `vertical` --- 垂直 slider
```
<star-slider vertical></star-slider>
<star-slider vertical disabled></star-slider>
<star-slider vertical coverWidth="50%"></star-slider>
```
7. 左侧图标|滑块|右侧图标
```
<star-slider>
<p class="left" data-icon="brightness"></p>
</star-slider>
```
## 后续需解决的问题:
- tick 属性中小球不能完全覆盖 step
- vertical 属性变化太快

View File

@ -0,0 +1,132 @@
import {css, CSSResult} from 'lit'
export const sharedStyles: CSSResult = css`
:host {
--cover-width: 0px;
--dot-move: 0px;
--vWidth: 8px;
}
.content {
height: 2px;
margin: 50px;
position: relative;
padding: 8px;
/*border: 1px solid skyblue;*/
border-radius: 5px;
}
.v-content {
width: 2px;
margin: 0px 2px;
position: relative;
padding: 200px 10px;
/*border: 1px solid skyblue;*/
border-radius: 5px;
}
.v-sliderBar {
position: absolute;
width: var(--vWidth);
height: 80%;
left: calc(50% - var(--vWidth) / 2);
top: 0px;
background: rgba(0, 0, 0, 0.08);
border-radius: 20px;
overflow: hidden;
}
.sliderBar {
position: absolute;
width: 100%;
height: 6px;
left: 0px;
right: 0px;
top: calc(50% - 6px / 2);
background: rgba(0, 0, 0, 0.08);
border-radius: 8px;
}
.v-progress {
position: absolute;
width: var(--vWidth);
height: var(--cover-width);
left: 0px;
bottom: 0px;
border-radius: 0 0 20px 20px;
background: #404040;
}
.progress {
position: absolute;
width: var(--cover-width);
height: 6px;
left: 0px;
right: 0px;
top: calc(50% - 6px / 2);
background: #4d4d4d;
border-radius: 5px 0 0 5px;
}
.dot {
position: absolute;
left: var(--dot-move);
width: 20px;
height: 20px;
top: calc(50% - 20px / 2);
background: #544f4f;
border-radius: 50%;
z-index: 1;
}
p {
position: absolute;
right: 5px;
top: 1px;
}
:host([disabled]) .progress {
background: #c0c0c0;
}
:host([disabled]) .v-progress {
background: #c0c0c0;
}
:host([disabled]) .step {
background: #c0c0c0;
}
:host([tick]) .step {
background: #c0c0c0;
}
:host([tick]) .sliderBar {
background: #c0c0c0;
border-radius: 0 0 0 0;
height: 4px;
}
:host([disabled]) .dot {
background: #c0c0c0;
}
:host([unfilled]) .progress {
background: none;
}
:host([tick]) .progress {
background: none;
}
.step {
position: absolute;
left: calc(var(--i) * 99.3%);
top: calc(50% - 10px / 2);
width: 3px;
height: 10px;
background-color: #d5d5d5;
border-radius: 3px;
}
::slotted(span)::before {
font-size: 27px;
font-family: 'gaia-icons';
content: attr(data-icon);
text-align: center;
position: relative;
top: 357px;
}
::slotted(p)::before {
font-size: 27px;
font-family: 'gaia-icons';
content: attr(data-icon);
text-align: center;
position: relative;
top: 74px;
left: 5px;
}
`

View File

@ -0,0 +1,244 @@
import {html, LitElement, CSSResultArray, PropertyValueMap} from 'lit'
import {customElement, property, query} from 'lit/decorators.js'
import {sharedStyles} from './slider-styles'
@customElement('star-slider')
export class StarSlider extends LitElement {
_coverWidth: string = ''
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
@query('.content') content!: HTMLDivElement
@query('.sliderBar') sliderBar!: HTMLDivElement
@query('.progress') progress!: HTMLDivElement
@query('.v-sliderBar') vSliderBar!: HTMLDivElement
@query('.v-progress') vProgress!: HTMLDivElement
@query('.dot') dot!: HTMLDivElement
@query('p') p!: HTMLParagraphElement
@property({type: Boolean}) disabled = false
@property({type: Boolean}) tick = false
@property({type: Boolean}) vertical = false
@property({type: Number}) startX = 0
@property({type: Number}) startY = 0
@property({type: Number}) touchX = 0
@property({type: Number}) touchY = 0
@property({type: Number}) moveX = 0
@property({type: Number}) moveY = 0
@property({type: Number}) newX = 0
@property({type: Number}) newY = 0
@property({type: Number}) barWidth = 0
@property({type: Number}) dotL = 0
@property({type: Number}) proportion = 0
@property({type: Number}) barX = 0
@property({type: String}) endValue = ''
@property({type: String}) step = ''
@property({type: String})
get coverWidth() {
return this._coverWidth
}
set coverWidth(value: string) {
this.style.setProperty('--cover-width', value)
this._coverWidth = value
this.style.setProperty('--dot-move', this._coverWidth)
}
render() {
if (!this.vertical) {
return html`
<slot></slot>
<div
class="content"
id="content"
@touchstart=${this.touchStart}
@touchend=${this.touchEnd}
@touchmove=${this.touchMove}
>
<div class="sliderBar">
<div class="progress"></div>
<div class="dot"></div>
</div>
</div>
`
} else {
return html`
<slot></slot>
<div
class="v-content"
@touchstart=${this.touchStartVertical}
@touchend=${this.touchEndVertical}
@touchmove=${this.touchMoveVertical}
>
<div class="v-sliderBar">
<div class="v-progress"></div>
</div>
</div>
`
}
}
protected firstUpdated(
_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>
): void {
if (this.vertical) {
this.touchStart = this.touchStartVertical
this.touchEnd = this.touchEndVertical
this.touchMove = this.touchMoveVertical
}
if (this.tick) {
var tickStep = 100 / parseInt(this.step)
for (let i = 0; i <= tickStep; i++) {
const stepTick = document.createElement('div')
stepTick.style.setProperty('--i', String(i / tickStep))
stepTick.classList.add('step')
this.sliderBar.appendChild(stepTick)
}
}
}
private touchStart(evt: TouchEvent) {
if (!this.disabled) {
this.touchX = evt.touches[0].clientX //手指触摸的 x 坐标
// 黑色覆盖部分
this.barX =
this.touchX -
this.sliderBar.getBoundingClientRect().left -
this.dot.offsetWidth * 0.5
this.barWidth = this.sliderBar.offsetWidth - this.dot.offsetWidth
if (this.barX < 0) {
this.barX = 0
}
if (this.barX >= this.barWidth) {
this.barX = this.barWidth
}
this.proportion = (this.barX / this.barWidth) * 100
this.endValue = Math.ceil(this.proportion) + ''
if (this.tick) {
// tick跳格滑动
var tickStep = 100 / parseInt(this.step) //分为几格
for (let i = 0; i <= tickStep; i++) {
if (this.barX > (this.barWidth * (i + 0.5)) / tickStep) {
this.style.setProperty(
'--dot-move',
(this.barWidth * (i + 1)) / tickStep + 'px'
)
}
if (this.barX == 0) {
this.style.setProperty('--dot-move', 0 + 'px')
}
}
} else {
this.style.setProperty('--dot-move', this.barX + 'px')
this.progress.style.setProperty(
'width',
(this.barWidth * Math.ceil(this.proportion)) / 100 + 'px'
)
}
// 重新初始化防止滑块增量
this.startX = evt.touches[0].clientX //手指点下的初始 X&Y 坐标
this.barWidth = this.sliderBar.offsetWidth - this.dot.offsetWidth //总长度减去小球覆盖的部分
this.dotL = this.dot.offsetLeft //小球左侧相对于父元素的左边距
}
}
private touchMove(evt: TouchEvent) {
if (!this.disabled) {
//阻止默认行为
evt.preventDefault()
this.touchX = evt.touches[0].clientX //整个屏幕实时触摸的 X 坐标
this.moveX = this.touchX - this.startX //手指移动的距离
//判断最大值和最小值
this.newX = this.dotL + this.moveX
if (this.newX < 0) {
this.newX = 0
}
if (this.newX >= this.barWidth) {
this.newX = this.barWidth
}
//计算比例
this.proportion = (this.newX / this.barWidth) * 100
//取整
this.endValue = Math.ceil(this.proportion) + ''
if (!this.tick) {
this.style.setProperty('--dot-move', this.newX + 'px')
this.progress.style.setProperty(
'width',
(this.barWidth * Math.ceil(this.proportion)) / 100 + 'px'
)
} else {
// tick跳格滑动
var tickStep = 100 / parseInt(this.step) //分为几格
for (let i = 0; i <= tickStep; i++) {
if (this.newX > (this.barWidth * (i + 0.5)) / tickStep) {
this.style.setProperty(
'--dot-move',
(this.barWidth * (i + 1)) / tickStep + 'px'
)
this.proportion =
((this.barWidth * (i + 1)) / tickStep / this.barWidth) * 100
//取整
this.endValue = Math.ceil(this.proportion) + ''
}
if (this.newX == 0) {
this.style.setProperty('--dot-move', 0 + 'px')
this.endValue = 0 + ''
}
}
}
}
}
private touchEnd(_: TouchEvent) {
if (this.tick) {
return console.log(10 * Math.round(parseInt(this.endValue) / 10) + '')
} else {
return console.log(this.endValue)
}
}
private touchStartVertical(evt: TouchEvent) {
if (!this.disabled) {
this.startY = evt.touches[0].clientY //手指点下的初始 Y 坐标
this.barWidth = this.vSliderBar.offsetHeight //总长度
this.dotL = this.barWidth - this.vProgress.offsetTop //黑条长度
//按压音量条变宽
this.vSliderBar.style.setProperty('--vWidth', '16px')
this.vProgress.style.setProperty('--vWidth', '16px')
}
}
private touchMoveVertical(evt: TouchEvent) {
if (!this.disabled) {
//阻止默认行为
evt.preventDefault()
this.touchY = evt.touches[0].clientY //整个屏幕实时触摸的 Y 坐标
this.moveY = this.startY - this.touchY //手指移动的距离
//判断最大值和最小值
this.newY = this.dotL + this.moveY
if (this.newY < 0) {
// console.log('到底了')
this.newY = 0
}
if (this.newY >= this.barWidth) {
// console.log('超了')
this.newY = this.barWidth
}
//计算比例
this.proportion = (this.newY / this.barWidth) * 100
//取整
this.endValue = Math.ceil(this.proportion) + ''
this.vProgress.style.setProperty('height', this.newY + 'px')
}
}
private touchEndVertical(_: TouchEvent) {
if (!this.disabled) {
this.vProgress.style.setProperty('--vWidth', '8px')
this.vSliderBar.style.setProperty('--vWidth', '8px')
return console.log(this.endValue)
}
}
}
declare global {
interface HTMLElementTagNameMap {
'star-slider': StarSlider
}
}

View File

@ -8,4 +8,4 @@
## 类型包括
- 数字|单位|加减步进器
- 数字|单位|加减步进器

View File

@ -1,17 +1,20 @@
# SVG图标容器-SVG Icon
# SVG 图标容器-SVG Icon
参照物:
参照物:
- https://github.com/apple/swift-docc-render.git
- src/components/SVGIcon.vue
- src/components/Icons
使用 <svg-icon> 元素可以通过拷贝SVG的主体内容(如一个绘制路径path)到标签内,来实现SVG图标的显示。
使用 <svg-icon> 元素,可以通过拷贝 SVG 的主体内容(如一个绘制路径 path)到标签内,来实现 SVG 图标的显示。
好处:
- 内联加载SVG矢量图标
- 可动态操作SVG通过修改扩展的svg-icon的属性值如颜色、填充等
- 内联加载 SVG 矢量图标
- 可动态操作 SVG通过修改扩展的 svg-icon 的属性值(如颜色、填充等)
目标物:
1. SVG内容填充
2. 普通的SVG图标
3. 动态的SVG应用图标(时钟、日历、设置、安装应用过程中)
1. SVG 内容填充
2. 普通的 SVG 图标
3. 动态的 SVG 应用图标(时钟、日历、设置、安装应用过程中)

View File

@ -1,5 +1,5 @@
import { html, css, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import {html, css, LitElement} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import '../svgicon'
@customElement('example-icon')
@ -16,13 +16,11 @@ export class ExampleIcon extends LitElement {
`
}
static styles = css`
`
static styles = css``
}
declare global {
interface HTMLElementTagNameMap {
'example-icon': ExampleIcon
}
}
}

View File

@ -1,5 +1,5 @@
import { html, css, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import {html, css, LitElement} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
@customElement('svg-icon')
export class SvgIcon extends LitElement {
@ -10,18 +10,14 @@ export class SvgIcon extends LitElement {
bar = ''
render() {
return html`
`
return html``
}
static styles = css`
`
static styles = css``
}
declare global {
interface HTMLElementTagNameMap {
'svg-icon': SvgIcon
}
}
}

View File

@ -2,17 +2,28 @@
星光 Web 组件 --- Switch 组件8 月 29 日)
`star-switch` 组件包含**种**互相独立的属性,介绍如下:
`star-switch` 组件包含**种**互相独立的属性,介绍如下:
1. switchcolor : 控制 switch 的选中背景颜色,默认为蓝色`#0265dc`。
```
<star-switch></star-switch>
<hr />
<star-switch switchcolor="#4cd964"></star-switch>
<star-switch switchcolor="#4cd964">ios绿</star-switch>
<star-switch switchcolor="#ff3b30">ios红</star-switch>
```
2. checked : 用于选择对应 switch 首次加载时是否被选中,默认为`false`。
2. switchicon : 用于控制内含文本,默认为`false`。
```
<star-switch></star-switch>
<hr />
<star-switch switchicon></star-switch>
<hr />
<star-switch checked switchicon></star-switch>
```
3. checked : 用于选择对应 switch 首次加载时是否被选中,默认为`false`。
```
<star-switch></star-switch>
@ -22,7 +33,7 @@
<star-switch checked switchcolor="#4cd964"></star-switch>
```
3. disabled : 控制 switch 是否**禁用**状态,默认为`false`。
4. disabled : 控制 switch 是否**禁用**状态,默认为`false`。
```
<star-switch></star-switch>
@ -32,7 +43,7 @@
<star-switch disabled checked></star-switch>
```
4. size : 控制 switch 的大小,包括 small、medium、large 和 extralarge默认为 `medium`
5. size : 控制 switch 的大小,包括 small、medium、large 和 extralarge默认为 `medium`
```
<star-switch size="small" switchcolor="#4cd964"></star-switch>
@ -45,3 +56,7 @@
<hr />
<star-switch size="extralarge" disabled checked></star-switch>
```
该组件支持焦点选中,且可以在焦点选中后任意拖动。
拖动后可间接触发 checked 开关。

View File

@ -13,6 +13,10 @@ export class StarSwitch extends LitElement {
@property({type: Number}) right = 0
@property({type: Number}) left = 0
@property({type: Number}) switchx = 0
@property({type: Number}) startx = 0
@property({type: Number}) _x = 0
@property({type: Number}) rightx = 0
@property({type: Number}) leftx = 10000
@property({type: Number}) x = 0
@property({type: Boolean}) disabled = false
@property({type: Boolean}) checked = false
@ -40,27 +44,87 @@ export class StarSwitch extends LitElement {
id="base"
switchcolor="#0265dc"
/>
<label id="switchBall" for="base" @touchmove=${this.selectTouchMove}>
<label
id="switchBall"
for="base"
@touchstart=${this.touchStart}
@touchend=${this.touchEnd}
@touchmove=${this.selectTouchMove}
>
<div class="${this.checked ? 'iconTrue' : 'iconFalse '}"></div>
</label>
`
}
private touchStart(evt: TouchEvent) {
console.log('##')
this.startx = evt.touches[0].clientX
this._x = this.startx
}
private touchEnd(_: TouchEvent) {
this.rightx = 0
this.leftx = 10000
this.startx = 0
}
private selectTouchMove(evt: TouchEvent) {
evt.preventDefault()
// disabled不允许拖动
if (!this.disabled) {
let right = this.switchBall.getBoundingClientRect().right
let left = this.switchBall.getBoundingClientRect().left
let switchx = (right - left) / 2 + left
let switchx = (right - left) / 2
let x = evt.touches[0].clientX
if (x >= switchx) {
this.base.checked = true
// 解决touchmove监测不到checked变化
this.checked = true
} else {
this.base.checked = false
// 解决touchmove监测不到checked变化
this.checked = false
// 向左滑
if (x < this._x) {
// console.log('往左滑', x, this._x)
this.leftx = this.leftx > this._x ? this._x : this.leftx
//到最左端后向右走 switchx 变true
if (x > this.leftx + this.switchx) {
this.base.checked = true
// 解决touchmove监测不到checked变化
this.checked = true
this.leftx = 10000
}
//初始的开关阈值调节
if (x > left && x < right) {
if (this.startx - this.leftx > switchx) {
this.base.checked = false
this.checked = false
}
}
//有rightx的情况下向左移动 switch 变false
if (x < this.rightx - switchx) {
this.base.checked = false
this.checked = false
}
} else if (x > this._x) {
//向右滑
// console.log('往右滑', x, this._x)
this.rightx = this.rightx > this._x ? this.rightx : this._x
//到最右端后向左移动 switch 变false
if (x < this.rightx - this.switchx) {
this.base.checked = false
this.checked = false
this.rightx = 0
}
//初始的开关阈值调节
if (x > left && x < right) {
if (this.rightx - this.startx > switchx) {
this.base.checked = true
this.checked = true
}
}
//到最左端开始向右走 switchx 变true
if (x - this.leftx > switchx) {
this.base.checked = true
this.checked = true
this.leftx = 10000
}
}
//更新_x的值保持_x在x后面
this._x = x
}
}
}

View File

@ -1,9 +1,11 @@
# 块-无序列表-Unordered List
工作职责:
- `顺序罗列``有限种`内容填充
- ` 顺序罗列``有限种 `内容填充
说明:
- | 用途:分割上下
- TabBar 用于切换内容栏
@ -17,4 +19,4 @@
### 左侧行|右侧行
用于突出对等显示的信息
用于突出对等显示的信息

View File

@ -10,8 +10,12 @@ import './test/panels/root'
import './components/button/button'
import './components/radio/radio-group'
import './components/radio/radio'
import './components/confirm/confirm'
import './components/toast/toast'
import './components/picker/picker'
import './components/overflowmenu/overflowmenu'
import './components/slider/slider'
@customElement('settings-app')
export class SettingsApp extends LitElement {
@query('star-animate-section#root') private rootSection!: StarAnimateSection

File diff suppressed because it is too large Load Diff

149
src/lib/gesture/gesture.md Normal file
View File

@ -0,0 +1,149 @@
# 手势检测框架
## Gesture Detector Finite State Machine
<center><h2>FSM状态转移表</h2></center>
| 当前状态 →<br>条件 ↓ | initialState | touchStartedState | touchesStartedState | holdState | panState | swipeState | pinchState | rotateState |
| ---------------------------------- | ----------------- | ------------------- | ------------------- | ------------ | ---------- | ------------ | ------------ | ------------ |
| 收到 touchstart | touchStartedState |
| 收到 touchstart 且触摸点=1 | | initialState |
| 收到 touchstart 且触摸点>1 | | touchesStartedState |
| 收到 touchstart 且判定进行捏 | | | pinchState |
| 收到 touchstart 且判定进行旋转 | | | rotateState |
| 收到 touchstart 且判定进行平移 | | | panState |
| 收到 touchmove 且移动差值>平移阈值 | | panState |
| 收到 touchend | | initialState | | initialState | swipeState | initialState | initialState | initialState |
| hold 超时完成 | | holdState |
## Gesture Events
### TapEvent
- definition: one or 2 fingers press, lift
- Usage: Select / Zoom in
- Event: tap
- Interface
```idl
Interface TapEvent {
readonly attribute TouchList touches;
readonly attribute long fingers; // only support 1 and 2
}
```
### PanEvent
- Definition: One or 2 fingers press, move, lift
- Usage: Dismiss, scroll, tilt / select multiple items, pan, tilt
- Event: panstart/panmove/panend
- Interface
```idl
Interface PanEvent {
readonly attribute TouchList startTouches;
readonly attribute TouchList endTouches;
readonly attribute TouchList movingTouches;
readonly attribute long fingers; // only support 1 and 2
}
```
### SwipeEvent
- Definition: One fingers press, move quickly, lift
- Usage: Dismiss, scroll, tilt / select multiple items, pan, tilt
- Event: swipestart/swipemove/swipeend
- Interface
```idl
Interface SwipeEvent {
readonly attribute Touch startTouch;
readonly attribute Touch endTouch;
readonly attribute double velocity;
Readonly attribute double direction;
}
```
### PinchEvent
- Definition: Two fingers press, move in/outwards, lift
- Usage: Zoom in/out
- Event: pinchstart/pinchmove/pinchend
- Interface
```idl
Interface Enum PinchZoom {
IN,
OUT
}
Interface PinchEvent {
readonly attribute TouchList startTouches;
readonly attribute TouchList endTouches;
readonly attribute double scale;
readonly attribute enum PinchZoom zoom;
}
```
### LongPressEvent
- Definition: One or 2 fingers press, wait, lift
- Usage: Select an element,
- Event: longpress
- Interface
```idl
Interface LongPressEvent {
readonly attribute TouchList pressTouches;
readonly attribute long fingers; // only support 1 and 2
}
```
### DoubleTapEvent
- Definition: One or 2 fingers press, lift, one or 2 fingers press, lift
- Usage: Selection / Context menu
- Event: doubletap
- Interface
```idl
Interface DoubleTapEvent {
readonly attribute TouchList firstTapTouches;
readonly attribute TouchList secondTapTouches;
readonly attribute long fingers; // only support 1 and 2
}
```
### RotateEvent
- Definition: 2 fingers press, simultaneously orbit both fingers around the center point, lift.
- Usage: Rotate content, a picture or a map.
- Event: rotatestart/rotatemove/rotateend
- Interface
```ts
Interface RotateEvent {
readonly attribute TouchList startTouches;
readonly attribute TouchList endTouches;
readonly attribute TouchList rotatingTouches;
readonly attribute double degree;
}
```
```
/* Using the this keyword to access the element property of the current object. */
// this.element.addEventListener = new Proxy(this.element.addEventListener, {
// set(target: any, _: any, val: any) {
// debug(target, _, val)
// // if (typeof val == 'object') {
// // target.push(val)
// // if (target.length > 10) target.shift()
// // }
// return true
// },
// get(target, prop: any, _) {
// debug(target, _, prop)
// return target[prop]
// },
// })
```

View File

@ -1,85 +1,84 @@
import {html, LitElement, CSSResultArray} from "lit"
import {customElement} from "lit/decorators.js"
import {sharedStyles} from "../shared-styles"
import "../../../components/card/card"
import {html, LitElement, CSSResultArray} from 'lit'
import {customElement} from 'lit/decorators.js'
import {sharedStyles} from '../shared-styles'
import '../../../components/card/card'
@customElement("panel-card")
@customElement('panel-card')
export class PanelCard extends LitElement {
render() {
return html `
<div>
<h4></h4>
<star-card
type="base"
image="./src/test/panels/card/image/1.png"
heading="基础卡片"
subheading="详情叙述"
footer="footer"
></star-card>
</div>
<br>
<br>
<br>
<br>
<div>
<h4></h4>
<star-card
type="linkcard"
link="https://www.kylinos.cn/"
image="./src/test/panels/card/image/1.png"
heading="连接卡片"
subheading="详情叙述"
footer="footer"
></star-card>
</div>
<br>
<br>
<br>
<br>
<div>
<h4></h4>
<star-card
type="labelonly"
heading="无图卡片"
subheading="内容展示"
footer="footer"
></star-card>
</div>
<br>
<br>
<br>
<br>
<div>
<h4></h4>
<star-card
type="footerdeleted"
image="./src/test/panels/card/image/2.jpg"
heading="无页脚卡片"
subheading="内容展示"
></star-card>
</div>
<br>
<br>
<br>
<br>
<div>
<h4></h4>
<star-card
type="imageonly"
image="./src/test/panels/card/image/2.jpg"
></star-card>
</div>
`
}
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
render() {
return html`
<div>
<h4></h4>
<star-card
type="base"
image="./src/test/panels/card/image/1.png"
heading="基础卡片"
subheading="详情叙述"
footer="footer"
></star-card>
</div>
<br />
<br />
<br />
<br />
<div>
<h4></h4>
<star-card
type="linkcard"
link="https://www.kylinos.cn/"
image="./src/test/panels/card/image/1.png"
heading="连接卡片"
subheading="详情叙述"
footer="footer"
></star-card>
</div>
<br />
<br />
<br />
<br />
<div>
<h4></h4>
<star-card
type="labelonly"
heading="无图卡片"
subheading="内容展示"
footer="footer"
></star-card>
</div>
<br />
<br />
<br />
<br />
<div>
<h4></h4>
<star-card
type="footerdeleted"
image="./src/test/panels/card/image/2.jpg"
heading="无页脚卡片"
subheading="内容展示"
></star-card>
</div>
<br />
<br />
<br />
<br />
<div>
<h4></h4>
<star-card
type="imageonly"
image="./src/test/panels/card/image/2.jpg"
></star-card>
</div>
`
}
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
}
declare global {
interface HTMLElementTagNameMap {
"panel-card": PanelCard
}
}
interface HTMLElementTagNameMap {
'panel-card': PanelCard
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,308 @@
import {html, LitElement} from 'lit'
import {customElement, property, state} from 'lit/decorators.js'
import {Confirm} from '../../../components/confirm/confirm'
// import icon1 from '../../panels/confirm/asserts/icon.png'
@customElement('panel-confirm')
export class PanelConfirm extends LitElement {
@property()
@state()
confirmDelLig() {
Confirm({
model: 'light',
image: '/src/test/panels/confirm/asserts/icon1.png',
headline: '是否卸载“影音”?',
subtitle: '卸载应用将会同步清除其所有数据',
confirmButtonText: '卸载',
cancelButtonText: '取消',
buttonColor: 'red',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmDel(event: Event) {
console.log(event, 'event')
Confirm({
// image:icon1,
image: '/src/test/panels/confirm/asserts/icon1.png',
headline: '是否卸载“影音”?',
subtitle: '卸载应用将会同步清除其所有数据',
confirmButtonText: '卸载',
cancelButtonText: '取消',
buttonColor: 'red',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmDetLight(event: Event) {
console.log(event, 'event')
Confirm({
type: 'non-modal-mode', // 非模态模式,默认代表支持模态模式,用户只能操作该对话框,背景不可以滑动点击
model: 'light', //浅色模式
image: '',
headline: '是否移除“天气”组件?',
subtitle: '长按屏幕空白处进入组件编辑界面,可重新添加该组件',
confirmButtonText: '移除',
cancelButtonText: '取消',
onConfirm: () => {
console.log('点击的移除======')
},
onCancel: () => {
console.log('点击的取消===')
},
})
}
confirmDetDark(event: Event) {
console.log(event, 'event')
Confirm({
type: 'non-modal-mode',
image: '',
headline: '是否移除“天气”组件?',
subtitle: '长按屏幕空白处进入组件编辑界面,可重新添加该组件',
confirmButtonText: '移除',
cancelButtonText: '取消',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmDetDarkLink() {
Confirm({
type: 'non-modal-mode', // 非模态模式,默认代表支持模态模式,用户只能操作该对话框,背景不可以滑动点击
image: '',
headline: '是否删除联系人?',
subtitle: '',
confirmButtonText: '删除',
cancelButtonText: '取消',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmProLig() {
Confirm({
model: 'light',
headline: '标题',
subtitle:
'超过一句话的长文本内容,文字居左对齐超过一句话的长文本内容,文字居左对齐',
confirmButtonText: '好的',
cancelButtonText: '',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmProLigCheckBox() {
Confirm({
headline: '标题',
model: 'light',
subtitle:
'超过一句话的长文本内容,文字居左对齐超过一句话的长文本内容,文字居左对齐',
confirmButtonText: '好的',
cancelButtonText: '',
checkBoxText: '不再提示',
onConfirm: (params) => {
console.log(params)
},
onCancel: () => {},
})
}
confirmProCheckBox() {
Confirm({
headline: '标题',
subtitle:
'超过一句话的长文本内容,文字居左对齐超过一句话的长文本内容,文字居左对齐',
confirmButtonText: '好的',
cancelButtonText: '',
checkBoxText: '不再提示',
onConfirm: (params) => {
console.log(params)
},
onCancel: () => {},
})
}
confirmProCheckBoxTest() {
Confirm({
headline: '标题',
subtitle:
'超过一句话的长文本内容,文字居左对齐,超过一句话的长文本内容,文字居左对齐,超过一句话的长文本内容,文字居左对齐,超过一句话的长文本内容,文字居左对齐,超过一句话的长文本内容,文字居左对齐超过一句话的长文本内容,文字居左对齐',
confirmButtonText: '好的',
cancelButtonText: '',
checkBoxText: '不再提示',
onConfirm: (params) => {
console.log(params)
},
onCancel: () => {},
})
}
confirmProLigNo() {
Confirm({
model: 'light',
headline: '标题',
subtitle: '长按屏幕空白处进入组件编辑页面,可重新添加该组件',
cancelButtonText: '',
confirmButtonText: '好的',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmProNo() {
Confirm({
model: 'light',
headline: '此处是一句话文本内容',
subtitle: '',
cancelButtonText: '',
confirmButtonText: '好的',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmTopCenter() {
Confirm({
image: '/src/test/panels/confirm/asserts/icon1.png',
headline: '是否卸载“影音”?',
subtitle: '卸载应用将会同步清除其所有数据',
location: 'top-center-popup',
confirmButtonText: '卸载',
cancelButtonText: '取消',
onConfirm: () => {},
onCancel: () => {},
})
}
confirmBottomCenter() {
Confirm({
image: '/src/test/panels/confirm/asserts/icon1.png',
headline: '是否卸载“影音”?',
subtitle: '卸载应用将会同步清除其所有数据',
location: 'bottom-center-popup',
confirmButtonText: '卸载',
cancelButtonText: '取消',
onConfirm: () => {},
onCancel: () => {},
})
}
render() {
return html`
<div>
<h3>//</h3>
<div>
<star-button
type="iconlabel"
label="浅色卸载"
icon="delete"
iconcolor="white"
variant="secondary"
@click="${this.confirmDelLig}"
></star-button>
<star-button
type="iconlabel"
label="深色卸载"
icon="delete"
iconcolor="black"
variant="secondary"
@click="${this.confirmDel}"
></star-button>
<star-button
type="iconlabel"
label="浅色移除"
icon="delete"
iconcolor="white"
variant="secondary"
@click="${this.confirmDetLight}"
></star-button>
<star-button
type="iconlabel"
label="深色移除"
icon="delete"
iconcolor="black"
variant="secondary"
@click="${this.confirmDetDark}"
></star-button>
<star-button
type="iconlabel"
label="深色删除联系人"
icon="delete"
iconcolor="black"
variant="secondary"
@click="${this.confirmDetDarkLink}"
></star-button>
</div>
<h3>//</h3>
<div>
<h4></h4>
<star-button
type="iconlabel"
label="浅色提示"
icon="help"
iconcolor="white"
variant="secondary"
@click="${this.confirmProLig}"
></star-button>
<star-button
type="iconlabel"
label="带复选框的浅色提示"
icon="help"
iconcolor="white"
variant="secondary"
@click="${this.confirmProLigCheckBox}"
></star-button>
<star-button
type="iconlabel"
label="带复选框的深色提示"
icon="help"
iconcolor="green"
variant="primary"
@click="${this.confirmProCheckBox}"
></star-button>
<star-button
type="iconlabel"
label="边界值测试"
icon="help"
iconcolor="white"
variant="secondary"
@click="${this.confirmProCheckBoxTest}"
></star-button>
</div>
<div>
<h4></h4>
<star-button
type="iconlabel"
label="浅色有标题提示"
icon="help"
iconcolor="white"
variant="secondary"
@click="${this.confirmProLigNo}"
></star-button>
<star-button
type="iconlabel"
label="浅色无标题提示"
icon="help"
iconcolor="white"
variant="secondary"
@click="${this.confirmProNo}"
></star-button>
</div>
</div>
<div>
<h3></h3>
<star-button
type="iconlabel"
label="顶部居中弹窗"
icon="help"
iconcolor="white"
variant="secondary"
@click="${this.confirmTopCenter}"
></star-button>
<star-button
type="iconlabel"
label="顶部居中弹窗"
icon="help"
iconcolor="white"
variant="secondary"
@click="${this.confirmBottomCenter}"
></star-button>
</div>
`
}
}
declare global {
interface HTMLElementTagNameMap {
'panel-confirm': PanelConfirm
}
}

View File

@ -1,12 +1,111 @@
import { html, css, LitElement } from 'lit'
import { customElement } from 'lit/decorators.js'
import {html, css, LitElement} from 'lit'
import {customElement} from 'lit/decorators.js'
import '../../../components/container/root-container'
import {
ChildContainerNode,
ChildData,
} from '../../../components/container/data-type'
@customElement('panel-container')
export class PanelContainer extends LitElement {
/* 测试用数据 */
data: ChildContainerNode<ChildData>[] = [
{
node: 'child-container',
type: 'main',
childs: [
{node: 'icon-container'},
{node: 'small-component-container'},
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'small-component-container', size: '2,2'},
{
node: 'folder-container',
name: '文件夹1',
childs: [
{
node: 'child-container',
type: 'main',
size: '3,3',
childs: [{node: 'icon-container'}, {node: 'icon-container'}],
},
],
},
{
node: 'folder-container',
name: '文件夹2',
childs: [
{
node: 'child-container',
type: 'main',
size: '4,4',
childs: [{node: 'icon-container'}, {node: 'icon-container'}],
},
],
},
{
node: 'folder-container',
name: '文件夹3',
childs: [
{
node: 'child-container',
type: 'main',
size: '4,3',
childs: [{node: 'icon-container'}, {node: 'icon-container'}],
},
],
},
{
node: 'folder-container',
name: '文件夹4',
childs: [
{
node: 'child-container',
type: 'main',
size: '6,6',
childs: [{node: 'icon-container'}, {node: 'icon-container'}],
},
],
},
{
node: 'folder-container',
name: '文件夹5',
childs: [
{
node: 'child-container',
type: 'main',
size: '3,3',
childs: [
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
],
},
{
node: 'child-container',
type: 'main',
size: '3,3',
childs: [
{node: 'icon-container'},
{node: 'icon-container'},
{node: 'icon-container'},
],
},
],
},
],
},
]
render() {
return html`
<root-container></root-container>
<root-container .data=${this.data}></root-container>
`
}
@ -19,8 +118,8 @@ export class PanelContainer extends LitElement {
background-color: black;
}
root-container {
width: 90vw;
height: 90vh;
width: calc(100% - 10px);
height: calc(100% - 10px);
margin: auto;
}
`
@ -30,4 +129,4 @@ declare global {
interface HTMLElementTagNameMap {
'panel-container': PanelContainer
}
}
}

View File

@ -349,6 +349,7 @@ export class PanelContainer extends LitElement {
flex-direction: column;
justify-content: space-around;
height: 100vh;
overflow: hidden;
/** 控制组件容器的位置 */
--container-margin-top: 30px;

View File

@ -0,0 +1,40 @@
import {html, css, LitElement} from 'lit'
import {customElement, query, state} from 'lit/decorators.js'
import '../../../components/grabber/home-bar-indicator'
import {HomeBarIndicator} from '../../../components/grabber/home-bar-indicator'
@customElement('panel-home-indicator')
export class PanelHomeIndicator extends LitElement {
@query('home-indicator', true) homeIndicator!: HomeBarIndicator
// @property({type: String}) status = this.homeIndicator?.status
foo = ''
@state()
bar = ''
attributeChangedCallback(name: string, _old: string, value: string): void {
super.attributeChangedCallback(name, _old, value)
console.log(name)
}
constructor() {
super()
// setInterval(() => {
// console.log(this.homeIndicator.movePoint)
// }, 30)
}
render() {
return html`
<home-bar-indicator></home-bar-indicator>
`
}
static styles = css``
}
declare global {
interface HTMLElementTagNameMap {
'panel-home-indicator': PanelHomeIndicator
}
}

View File

@ -1,8 +1,7 @@
import {html, css, LitElement, CSSResultArray} from 'lit'
import {customElement, queryAll, state} from 'lit/decorators.js'
import {customElement, state} from 'lit/decorators.js'
import {sharedStyles} from '../shared-styles'
import '../../../components/indicator/indicator-page-point'
import {IndicatorPagePoint} from '../../../components/indicator/indicator-page-point'
@customElement('panel-indicators')
export class PanelIndicators extends LitElement {
@ -16,10 +15,7 @@ export class PanelIndicators extends LitElement {
this.index < 1 ? 1 : this.index > this.total ? this.total : this.index
}
@queryAll('.myindicator') private myindicators!: IndicatorPagePoint
handleEvent(evt: Event) {
this.myindicators
switch (evt.type) {
case 'click':
evt.preventDefault() // iOS上不禁止按钮默认行为将会双击按钮强制放缩屏幕

View File

@ -0,0 +1,149 @@
import {html, LitElement, css} from 'lit'
import {customElement, property} from 'lit/decorators.js'
import '../../../components/button/button'
import '../../../components/ul/ul'
import '../../../components/li//li'
import {UlType} from '../../../components/ul/ul'
import {LiType} from '../../../components/li//li'
@customElement('panel-overflowmenu')
export class PanelOverflowMenu extends LitElement {
constructor() {
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() {
return html`
<div>
<star-overflowmenu icon="more">
<star-ul type=${UlType.BASE}>
<star-li
type=${LiType.ONLY_EDIT}
label="星光麒麟"
default="星光麒麟"
></star-li>
</star-ul>
</star-overflowmenu>
<star-overflowmenu
icon="more"
style="position: fixed; top: 50%; left: 50%;"
>
<star-ul type=${UlType.BASE}>
<star-li
type=${LiType.ONLY_EDIT}
label="星光麒麟"
default="星光麒麟"
></star-li>
</star-ul>
</star-overflowmenu>
<star-overflowmenu
icon="more"
style="position: fixed; top: 0; right: 0;"
>
<star-ul type=${UlType.BASE}>
<star-li
type=${LiType.ONLY_EDIT}
label="星光麒麟"
default="星光麒麟"
></star-li>
<star-li
type=${LiType.ONLY_EDIT}
label="星光麒麟"
default="星光麒麟"
></star-li>
</star-ul>
</star-overflowmenu>
<star-overflowmenu
icon="more"
style="position: fixed; bottom: 0; left: 0;"
>
<star-ul
type=${UlType.ONLY_HEADER}
title="头部有文字"
text="尾部有文字"
>
<star-li type=${LiType.ONLY_LABEL} label="素条目"></star-li>
</star-ul>
</star-overflowmenu>
<star-overflowmenu
icon="more"
style="position: fixed; bottom: 0; right: 0;"
>
<star-ul
type=${UlType.ONLY_HEADER}
title="头部有文字"
text="尾部有文字"
>
<star-li type=${LiType.ONLY_LABEL} label="素条目"></star-li>
</star-ul>
</star-overflowmenu>
</div>
`
}
static styles = css`
:host {
display: block;
width: 100vw;
height: 100vh;
}
div {
display: block;
width: 100vw;
height: 100vh;
}
`
}
declare global {
interface HTMLElementTagNameMap {
'panel-overflowmenu': PanelOverflowMenu
}
}

View File

@ -37,7 +37,7 @@ export class PanelPicker extends LitElement {
<label>
<span>medium</span>
<star-picker type="medium" hide-value>
<star-picker-option value="1" >red</star-picker-option>
<star-picker-option value="1">red</star-picker-option>
<star-picker-option value="2">yellow</star-picker-option>
<star-picker-option value="3">blue</star-picker-option>
</star-picker>

View File

@ -150,4 +150,4 @@ declare global {
interface HTMLElementTagNameMap {
'panel-radio': PanelRadio
}
}
}

View File

@ -11,14 +11,19 @@ import './icon/icon'
import './general/general'
import './card/card'
import './indicators/indicators'
import './indicators/home-indicator'
import './blur/use-blur'
import './button/button'
import './container/container'
import './radio/radio'
import './confirm/confirm'
import './overflowmenu/overflowmenu'
import './switch/switch'
import './slider/slider'
import './container/homescreen-container'
import './toast/toast'
import './picker/picker'
type SEID = string
@customElement('panel-root')
@ -115,6 +120,14 @@ export class PanelRoot extends LitElement {
href="#switch"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="SliderTest"
icon="scene"
iconcolor="brown"
href="#slider"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="查看所有按钮"
@ -131,6 +144,14 @@ export class PanelRoot extends LitElement {
href="#radio"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="溢出菜单"
icon="menu"
iconcolor="blue"
href="#overflowmenu"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="关于"
@ -139,6 +160,14 @@ export class PanelRoot extends LitElement {
href="#about"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="AllConfirm"
icon="messages"
iconcolor="green"
href="#confirm"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="页面圆点指示器"
@ -147,6 +176,14 @@ export class PanelRoot extends LitElement {
href="#indicators"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="home指示器"
icon="accessibility"
iconcolor="blue"
href="#home-indicator"
></star-li>
<hr />
<star-li
type=${LiType.ICON_LABEL}
label="毛玻璃"

View File

@ -1,28 +1,28 @@
import {css, CSSResult} from 'lit'
export const sharedPickerStyles: CSSResult = css`
* {
box-sizing: border-box;
}
* {
box-sizing: border-box;
}
body {
background: #fafafa;
font-family: sans-serif;
margin: 0;
padding: 20px;
}
body {
background: #fafafa;
font-family: sans-serif;
margin: 0;
padding: 20px;
}
label {
display: block;
margin-bottom: 50px;
width: inherit;
}
label {
display: block;
margin-bottom: 50px;
width: inherit;
}
span {
display: block;
}
span {
display: block;
}
#demo {
margin:20px;
}
#demo {
margin: 20px;
}
`

View File

@ -0,0 +1,74 @@
import {html, LitElement, css} from 'lit'
import {customElement, property} from 'lit/decorators.js'
import '../icon/icon'
@customElement('panel-slider')
export class PanelSlider extends LitElement {
@property({type: String}) icon = ''
static styles = css`
div {
position: relative;
margin: 50px 50px;
border-radius: 5px;
}
#a {
position: absolute;
left: 100px;
top: 45px;
}
#b {
position: absolute;
left: 200px;
top: 45px;
}
`
render() {
return html`
<div>
<h4>vertical</h4>
<star-slider vertical>
<span data-icon="alarm"></span>
</star-slider>
<star-slider id="a" vertical disabled coverWidth="30%">
<span data-icon="bluetooth-a2dp"></span>
</star-slider>
<star-slider id="b" vertical coverWidth="70%">
<span data-icon="alarm-clock"></span>
</star-slider>
<h4>default - </h4>
<star-slider>
<p class="left" data-icon="brightness"></p>
</star-slider>
<h4>coverWidth - </h4>
<star-slider coverWidth="150px">
<p data-icon="brightness"></p>
</star-slider>
<star-slider coverWidth="50%">
<p data-icon="brightness"></p>
</star-slider>
<h4>disabled</h4>
<star-slider disabled coverWidth="30%"></star-slider>
<star-slider disabled coverWidth="70%"></star-slider>
<h4>unfilled - </h4>
<star-slider unfilled coverWidth="20%"></star-slider>
<star-slider unfilled coverWidth="40%"></star-slider>
<h4>tick</h4>
<h5>step="10"</h5>
<star-slider tick step="10"></star-slider>
<h5>step="20"</h5>
<star-slider tick step="20" coverWidth="20%"></star-slider>
<h5>step="50"</h5>
<star-slider tick step="50" coverWidth="48%"></star-slider>
<h5>step="25"</h5>
<star-slider tick step="25" disabled coverWidth="23%"></star-slider>
</div>
`
}
}
declare global {
interface HTMLElementTagNameMap {
'panel-slider': PanelSlider
}
}

View File

@ -1,25 +1,25 @@
import { css, CSSResult } from 'lit'
import {css, CSSResult} from 'lit'
export const switchStyles: CSSResult = css`
div {
padding: 10px 40px;
}
padding: 10px 40px;
}
html {
scrollbar-width: none;
}
html {
scrollbar-width: none;
}
body {
margin: 0;
padding: 0;
background: #f0f0f0;
}
body {
margin: 0;
padding: 0;
background: #f0f0f0;
}
:root {
--left-transform: -100vw;
--right-transform: +100vw;
--li-base-height: 43px;
--li-left-padding: 10px;
--li-right-padding: 10px;
}
`
:root {
--left-transform: -100vw;
--right-transform: +100vw;
--li-base-height: 43px;
--li-left-padding: 10px;
--li-right-padding: 10px;
}
`

View File

@ -27,6 +27,6 @@
}
]
},
"include": ["src/**/*.ts", "temp/gaia-container-child.ts"],
"include": ["src/**/*.ts"],
"references": [{"path": "./tsconfig.node.json"}]
}

View File

@ -7,7 +7,7 @@ export default defineConfig({
entry: 'src/index.ts',
formats: ['es'],
},
outDir: 'dist/'
outDir: 'dist/',
// rollupOptions: {
// external: /^lit/,
// },