feat: 1. SelectControl 开发完成

This commit is contained in:
yanxiong 2022-08-16 16:47:37 +08:00
parent f8bfe8b953
commit 3840d9850b
11 changed files with 392 additions and 6 deletions

View File

@ -0,0 +1,6 @@
---
title: 选择框实例
order: 9
---
<code src="./select.tsx" compact defaultShowCode></code>

View File

@ -0,0 +1,80 @@
import { GaodeMap, RasterLayer, Scene, SelectControl } from '@antv/l7';
import { FunctionComponent, useEffect } from 'react';
class Select extends SelectControl {}
const Demo: FunctionComponent = () => {
useEffect(() => {
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'dark',
center: [120, 30],
pitch: 0,
zoom: 6.45,
}),
// logoVisible: false,
});
scene.on('loaded', () => {
// const layer = new RasterLayer({}).source(
// 'http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
// {
// parser: {
// type: 'rasterTile',
// tileSize: 256,
//
// zoomOffset: 0,
// updateStrategy: 'overlap',
// },
// },
// );
//
// scene.addLayer(layer);
scene.addControl(
new Select({
position: 'topcenter',
options: [
{
img:
'https://gw.alipayobjects.com/mdn/rms_58ab56/afts/img/A*vky9QKrWjlwAAAAAAAAAAAAAARQnAQ',
value: 'normal',
text: 'normal',
},
{
img:
'https://gw.alipayobjects.com/mdn/rms_58ab56/afts/img/A*UlP0Qp9Zwx0AAAAAAAAAAAAAARQnAQ',
value: 'light',
text: 'light',
},
{
img:
'https://gw.alipayobjects.com/mdn/rms_58ab56/afts/img/A*UitzS5mxpSwAAAAAAAAAAAAAARQnAQ',
value: 'dark',
text: 'dark',
},
{
img:
'https://gw.alipayobjects.com/mdn/rms_58ab56/afts/img/A*UitzS5mxpSwAAAAAAAAAAAAAARQnAQ',
value: 'dark1',
text: 'dark1',
},
],
defaultValue: '1',
}),
);
});
}, []);
return (
<div
id="map"
style={{
height: '500px',
position: 'relative',
}}
/>
);
};
export default Demo;

View File

@ -1,3 +1,4 @@
export * from './control';
export * from './buttonControl';
export * from './popperControl';
export * from './selectControl';

View File

@ -0,0 +1,217 @@
import { DOM } from '@antv/l7-utils';
import { IPopperControlOption, PopperControl } from './popperControl';
type BaseOptionItem = {
value: string;
text: string;
};
type NormalOptionItem = BaseOptionItem & {
icon?: HTMLElement;
};
type ImageOptionItem = BaseOptionItem & {
img: string;
};
type OptionItem = ImageOptionItem | NormalOptionItem;
export interface ISelectControlOption extends IPopperControlOption {
options: OptionItem[];
defaultValue?: string | string[];
}
export { SelectControl };
enum SelectControlConstant {
ActiveOptionClassName = 'l7-select-control-item-active',
OptionValueAttrKey = 'data-option-value',
OptionIndexAttrKey = 'data-option-index',
}
export default abstract class SelectControl<
O extends ISelectControlOption = ISelectControlOption
> extends PopperControl<O> {
/**
*
* @protected
*/
protected selectValue: string[] = [];
/**
*
* @protected
*/
protected abstract isMultiple: boolean;
/**
* Popper
* @protected
*/
protected abstract closeAfterSelect: boolean;
/**
* DOM
* @protected
*/
protected optionDOMList: HTMLElement[];
constructor(option: Partial<O>) {
super(option);
const { defaultValue } = option;
if (defaultValue) {
this.selectValue = this.transSelectValue(defaultValue);
}
}
public onAdd() {
const button = super.onAdd();
this.popper.setContent(this.getPopperContent());
setTimeout(() => {
this.popper.show();
}, 100);
return button;
}
public getSelectValue() {
return this.isMultiple ? this.selectValue : this.selectValue[0];
}
public setSelectValue(value: string | string[]) {
const finalValue = this.transSelectValue(value);
this.optionDOMList.forEach((optionDOM) => {
const optionValue = optionDOM.getAttribute(
SelectControlConstant.OptionValueAttrKey,
)!;
const checkboxDOM = this.isMultiple
? optionDOM.querySelector('input[type=checkbox]')
: undefined;
if (finalValue.includes(optionValue)) {
DOM.addClass(optionDOM, SelectControlConstant.ActiveOptionClassName);
if (checkboxDOM) {
// @ts-ignore
DOM.setChecked(checkboxDOM, true);
}
} else {
DOM.removeClass(optionDOM, SelectControlConstant.ActiveOptionClassName);
if (checkboxDOM) {
// @ts-ignore
DOM.setChecked(checkboxDOM, false);
}
}
});
this.selectValue = finalValue;
}
protected getPopperContent(): HTMLElement {
const options = this.controlOption.options;
const isImageOptions = this.isImageOptions();
const content = DOM.create(
'div',
isImageOptions ? 'l7-select-control-image' : 'l7-select-control-normal',
) as HTMLElement;
const optionsDOMList = options.map((option, optionIndex) => {
const optionDOM = isImageOptions
? // @ts-ignore
this.createImageOption(option)
: this.createNormalOption(option);
optionDOM.setAttribute(
SelectControlConstant.OptionValueAttrKey,
option.value,
);
optionDOM.setAttribute(
SelectControlConstant.OptionIndexAttrKey,
window.String(optionIndex),
);
optionDOM.addEventListener('click', this.onItemClick.bind(this, option));
return optionDOM;
});
content.append(...optionsDOMList);
this.optionDOMList = optionsDOMList;
return content;
}
protected createNormalOption = (option: NormalOptionItem) => {
const isSelect = this.selectValue.includes(option.value);
const optionDOM = DOM.create(
'div',
`l7-select-control-item ${
isSelect ? SelectControlConstant.ActiveOptionClassName : ''
}`,
) as HTMLElement;
if (this.isMultiple) {
optionDOM.appendChild(this.createCheckbox(isSelect));
}
if (option.icon) {
optionDOM.appendChild(option.icon);
}
const textDOM = DOM.create('span');
textDOM.innerText = option.text;
optionDOM.appendChild(textDOM);
return optionDOM;
};
protected createImageOption(option: ImageOptionItem): HTMLElement {
const isSelect = this.selectValue.includes(option.value);
const optionDOM = DOM.create(
'div',
`l7-select-control-item ${
isSelect ? SelectControlConstant.ActiveOptionClassName : ''
}`,
) as HTMLElement;
const imgDOM = DOM.create('img') as HTMLElement;
imgDOM.setAttribute('src', option.img);
optionDOM.appendChild(imgDOM);
const rowDOM = DOM.create(
'div',
'l7-select-control-item-row',
) as HTMLElement;
if (this.isMultiple) {
optionDOM.appendChild(this.createCheckbox(isSelect));
}
const textDOM = DOM.create('span');
textDOM.innerText = option.text;
rowDOM.appendChild(textDOM);
optionDOM.appendChild(rowDOM);
return optionDOM;
}
protected createCheckbox(isSelect: boolean) {
const checkboxDOM = DOM.create('input') as HTMLElement;
checkboxDOM.setAttribute('type', 'checkbox');
if (isSelect) {
DOM.setChecked(checkboxDOM, true);
}
return checkboxDOM;
}
protected onItemClick = (item: OptionItem) => {
if (this.isMultiple) {
const targetIndex = this.selectValue.findIndex(
(value) => value === item.value,
);
if (targetIndex > -1) {
this.selectValue.splice(targetIndex, 1);
} else {
this.selectValue = [...this.selectValue, item.value];
}
} else {
this.selectValue = [item.value];
if (this.closeAfterSelect) {
this.popper.hide();
}
}
this.setSelectValue(this.selectValue);
};
protected isImageOptions() {
// @ts-ignore
return !!this.controlOption.options.find((item) => item.img);
}
protected transSelectValue(value: string | string[]) {
return Array.isArray(value) ? value : [value];
}
}

View File

@ -1,6 +1,7 @@
@import 'variables';
@import 'l7';
@import 'control';
@import 'buttonControl';
@import 'button';
@import 'popper';
@import 'select';
@import '../assets/iconfont.css';

View File

@ -12,7 +12,7 @@
.l7-popper-content {
min-height: @l7-btn-control-size;
background: @l7-popper-bg-color;
border-radius: 2px;
border-radius: @l7-btn-control-border-radius;
}
.l7-popper-arrow {
width: 0;

View File

@ -0,0 +1,64 @@
@import 'variables';
.l7-select-control-normal {
.l7-select-control-item {
height: @l7-btn-control-size;
display: flex;
align-items: center;
padding: 0 10px;
> * + * {
margin-left: 6px;
}
&:hover {
background-color: @l7-btn-control-bg-hover-color;
}
&.l7-select-control-item-active {
color: @l7-select-active-color;
}
}
}
.l7-select-control-image {
padding: 12px 12px 0 12px;
width: @l7-select-image-popper-width;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
box-sizing: border-box;
.l7-select-control-item {
border-radius: @l7-btn-control-border-radius;
border: 2px solid @l7-popper-bg-color;
box-sizing: content-box;
width: calc((100% - 36px) / 3);
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 12px;
position: relative;
input[type='checkbox'] {
position: absolute;
right: 0;
top: 0;
}
.l7-select-control-item-row {
display: flex;
justify-content: center;
align-items: center;
margin: 4px 0;
> * + * {
margin-left: 6px;
}
}
&.l7-select-control-item-active {
border-color: @l7-select-active-color;
}
}
}
.l7-select-control-item {
cursor: pointer;
input[type='checkbox'] {
margin: 0;
cursor: pointer;
}
}

View File

@ -11,11 +11,16 @@
@l7-btn-control-shadow-color: rgba(0, 0, 0, 0.3);
@l7-btn-control-size: 30px;
@l7-btn-icon-size: 16px;
@l7-btn-control-border-radius: 2px;
@l7-btn-control-border-radius: 4px;
@l7-btn-control-disabled-bg-color: @l7-btn-control-bg-hover-color;
@l7-btn-control-disabled-font-color: lighten(@l7-control-font-color, 10%);
// PopperControl
@l7-popper-bg-color: @l7-btn-control-bg-color;
@l7-popper-arrow-size: 6px;
// SelectControl
@l7-select-active-color: #0370fe;
@l7-select-image-popper-width: 600px;
@position-list: top, right, bottom, left;

View File

@ -38,10 +38,12 @@ export interface IPopperOption {
closeOther?: boolean;
}
export class Popper extends EventEmitter {
export class Popper extends EventEmitter<'show' | 'hide'> {
protected get buttonRect() {
return this.button.getBoundingClientRect();
}
protected static conflictPopperList: Popper[] = [];
/**
*
* @protected
@ -64,8 +66,6 @@ export class Popper extends EventEmitter {
protected timeout: number | null = null;
protected static conflictPopperList: Popper[] = [];
constructor(button: HTMLElement, option: IPopperOption) {
super();
this.button = button;
@ -100,6 +100,7 @@ export class Popper extends EventEmitter {
}
});
}
this.emit('show');
}
public hide() {
@ -108,6 +109,7 @@ export class Popper extends EventEmitter {
}
DOM.addClass(this.popper, 'l7-popper-hide');
this.isShow = false;
this.emit('hide');
}
protected onBtnMouseLeave = () => {

View File

@ -204,3 +204,13 @@ export function getDiffRect(dom1Rect: any, dom2Rect: any) {
bottom: dom2Rect.top + dom2Rect.height - dom1Rect.top - dom1Rect.height,
};
}
export function setChecked(el: ELType, value: boolean) {
// @ts-ignore
el.checked = value;
if (value) {
el.setAttribute('checked', 'true');
} else {
el.removeAttribute('checked');
}
}