TASK:#105404-StarWebComponents开发-picker

This commit is contained in:
wangguoqing 2022-08-31 20:49:08 +08:00
parent ebd34e96f0
commit bbfc298696
9 changed files with 544 additions and 2 deletions

1
src/assets/close.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1661754021578" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1613" width="16.03125" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M603.52 512l274.56-274.56a64 64 0 0 0 16.64-64 64 64 0 0 0-45.44-45.44 64 64 0 0 0-64 16.64L512 421.76l-273.28-275.2a64 64 0 0 0-90.88 90.88L423.04 512l-275.2 274.56a64 64 0 0 0 0 90.24A64 64 0 0 0 192 896a64 64 0 0 0 45.44-19.2L512 602.24l275.2 274.56a64 64 0 0 0 44.8 19.2 64 64 0 0 0 45.44-109.44L603.52 512z" fill="#445365" p-id="1614"></path></svg>

After

Width:  |  Height:  |  Size: 690 B

1
src/assets/down.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1661752864101" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2155" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M185.884 327.55 146.3 367.133 512.021 732.779 877.7 367.133 838.117 327.55 511.997 653.676Z" p-id="2156"></path></svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@ -0,0 +1,52 @@
# star-picker
星光组件--picker组件(8月30日)
# 介绍
1. type实现了用type属性控制不同大小的picker选择框包括small、medium、large、extralarge
```html demo
<star-picker type="small" hide-value>
<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>
<star-picker type="medium" hide-value>
<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>
<star-picker type="large" hide-value>
<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>
<star-picker type="extralarge" hide-value>
<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>
```
2. hide-value实现了隐藏选择项的索引值
```html demo
<star-picker hide-value>
<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>
```
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

@ -0,0 +1,129 @@
import {css, CSSResult} from 'lit'
export const sharedStyles: CSSResult = css`
* {
box-sizing: border-box;
}
:host {
display: block;
border-radius: 4px;
}
: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:focus {
background: #FFFFFF;
border: 1px solid #2DA7E0;
box-shadow: inset 0 0 0 1000px #FFFFFF!important;
}
.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%;
}
.option,
.value-container {
display: flex;
}
.option {
padding: 10px 8px;
cursor: pointer;
}
.option:hover {
background: #f3f3f3;
}
.option-value,
.value,
.magnifying-glass {
flex-shrink: 0;
width: 15px;
}
.option-label,
.label,
.placeholder {
flex-grow: 1;
}
.placeholder-text {
color: #777;
font-style: italic;
}
.label,
.option-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.no-results {
color: #777;
font-style: italic;
padding: 8px 10px;
}
: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;
}
.small {
padding: 4px 6px;
font-size: 10px;
border-radius: 3px;
}
.medium {
padding: 5px 10px;
font-size: 14px;
border-radius: 4px;
}
.large {
padding: 8px 15px;
font-size: 18px;
border-radius: 5px;
}
.extralarge {
padding: 10px 20px;
font-size: 20px;
border-radius: 5px;
}
`

View File

@ -0,0 +1,237 @@
/* 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 {sharedStyles} from './picker-styles'
interface StarPickerOption {
value: string;
label: string;
}
export enum PickerType {
SMALL = 'small',
MEDIUM = 'medium',
LARGE = 'large',
EXTRALARGE = 'extralarge'
}
@customElement('star-picker')
export class StarPicker extends LitElement {
public static override get styles(): CSSResultArray {
return [sharedStyles]
}
@property({ type: PickerType}) type = '';
@property({ type: String }) tabindex = '0';
@property({ type: String }) placeholder = '';
@property({ type: String }) value = '';
@property({ type: Boolean, attribute: 'hide-value' }) hideValue = false;
@property({ type: Boolean, attribute: 'disable'}) disAble = false;
@property({ type: Boolean, attribute: 'small'}) small = false;
@property({ type: Boolean, attribute: 'medium'}) medium = false;
@property({ type: Boolean, attribute: 'large'}) large = false;
@property({ type: Boolean, attribute: 'extralarge'}) extralarge = false;
@property({ type: String, attribute: 'value-pattern' }) valuePattern =
'\\d{1,5}';
@state() private _searchState: Boolean = false;
@state() private _options: StarPickerOption[] = [];
@state() private _query: String = '';
firstUpdated() {
this.setAttribute('tabindex', this.tabindex);
this.addEventListener('keydown', e => {
if (this._searchState === false) {
this._onKeyDownValueContainer(e);
}
});
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;//类型断言
}
});
this._updateOptionsList();
const observer = new MutationObserver(() => { //js监听dom元素的属性变化
this._updateOptionsList();
});
//开始观察目标节点的DOM元素的属性变化
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; //判断返回值
return html`
<div
class="main-options-container"
style=${styleMap({ display: this._searchState ? 'block' : 'none' })}
>
<input
class="input ${this.type}"
type="text"
.value=${this.value}
@input="${this._onChangeQueryInput}"
@keydown="${this._onKeyDownQueryInput}"
/>
<div class="options-container">
${this._options
.filter(option => this._doesOptionMatchQuery(option))
.map(
option => html`
<div
class="option"
@click="${() => this._onClickOption(option)}"
>
<div class="option-value">${option.value}</div>
<div class="option-label">${option.label}</div>
</div>
`
)}
${this._options.filter(option => this._doesOptionMatchQuery(option)) //过滤元素
.length === 0 && this._query.length > 0
? html` <div class="no-results">No results</div> `
: ''}
</div>
</div>
<div
style=${styleMap({ display: this._searchState ? 'none' : 'flex' })}
@click="${this._onClickValueContainer}"
class="value-container ${this.type}"
>
${this.value !== ''
? html`
<div class="value">${this.value}</div>
<div class="label">
${label ||
html`<span class="placeholder-text">Loading...</span>`}
</div>
<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>
`;
}
private _updateOptionsList() {
const options = this.querySelectorAll('star-picker-option');
this._options = Array.from(options).map(option => ({
value: option.getAttribute('value'),
label: option.textContent,
})) as StarPickerOption[];
}
private _onChangeQueryInput(e: KeyboardEvent) {
const queryInput = e.target! as HTMLInputElement;
this._query = queryInput.value;
}
private _onKeyDownQueryInput(e: KeyboardEvent) {
if (e.key === 'Tab') {
e.preventDefault();
this._searchState = false;
const pattern = new RegExp(this.valuePattern);
if (pattern.test(this._query as string)) {
this.value = this._query as string;
}
this.updateComplete.then(() => {
this.focus();
});
}
}
private _doesOptionMatchQuery(option: StarPickerOption) {
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(); //聚焦元素
});
}
private _onClickValueContainer() {
this._openOptionsContainer();
}
private _onKeyDownValueContainer(e: KeyboardEvent) {
if (e.code === 'Space') {
this._openOptionsContainer();
e.preventDefault();
} else if (e.key === 'Delete' || e.key === 'Backspace') {
this.value = '';
}
}
private _onClickClearButton(e: MouseEvent) {
e.stopPropagation();
this.value = '';
this.updateComplete.then(() => {
this.focus();
});
}
private _openOptionsContainer() {
this._searchState = true;
if (this.value !== '' && this.hideValue === false) {
this._query = this.value;
} else {
this._query = '';
}
this.updateComplete.then(() => {
this.shadowRoot?.querySelector('input')?.select();
});
}
}
declare global {
interface HTMLElementTagNameMap {
'star-picker': StarPicker
}
}

View File

@ -8,7 +8,7 @@ import {StarAnimateSection} from './components/section/section'
import './components/section/section'
import './test/panels/root'
import './components/button/button'
import './components/picker/picker'
@customElement('settings-app')
export class SettingsApp extends LitElement {
@query('star-animate-section#root') private rootSection!: StarAnimateSection

View File

@ -0,0 +1,85 @@
import {html, LitElement, CSSResultArray} from 'lit'
import {customElement} from 'lit/decorators.js'
import {sharedPickerStyles} from '../shared-picker-styles'
@customElement('panel-picker')
export class PanelPicker extends LitElement {
render() {
return html`
<div id="demo">
<label>
<span>standard</span>
<star-picker>
<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>
</label>
<label>
<span>hide value</span>
<star-picker hide-value>
<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>
</label>
<label>
<span>small</span>
<star-picker type="small" hide-value>
<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>
</label>
<label>
<span>medium</span>
<star-picker type="medium" hide-value>
<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>
</label>
<label>
<span>large</span>
<star-picker type="large" hide-value>
<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>
</label>
<label>
<span>extralarge</span>
<star-picker type="extralarge" hide-value>
<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>
</label>
<label>
<span>disable</span>
<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>
</label>
</div>
`
}
public static override get styles(): CSSResultArray {
return [sharedPickerStyles]
}
}
declare global {
interface HTMLElementTagNameMap {
'panel-picker': PanelPicker
}
}

View File

@ -13,7 +13,7 @@ import './indicators/indicators'
import './blur/use-blur'
import './button/button'
import './container/container'
import './picker/picker'
type SEID = string
@customElement('panel-root')
@ -117,6 +117,15 @@ export class PanelRoot extends LitElement {
iconcolor="red"
href="#button"
></star-li>
<hr/>
<star-li
type=${LiType.ICON_LABEL}
label="picker"
icon="select"
iconcolor="blue"
href="#picker"
></star-li>
<hr />
<hr />
<star-li
type=${LiType.ICON_LABEL}

View File

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