TASK:#105404-StarWebComponents开发-picker
This commit is contained in:
parent
ebd34e96f0
commit
bbfc298696
|
@ -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 |
|
@ -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 |
|
@ -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>
|
||||
```
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
`
|
Loading…
Reference in New Issue