tsp-component
Version:
提供多端和react版本的UI组件
460 lines (419 loc) • 13.2 kB
text/typescript
/// <reference path="./definition.d.ts" />
import * as React from 'react';
import { popupOpen, popupClose } from '../popup';
import { setTranslate } from '../util/animation';
import FormCore from '../form/core';
const prefix = 'tsp-component-Picker';
const coefficient = 102;
class Picker {
constructor(params: TspComponentPickerCoreParams) {
this.id = params.id;
this.cascade = params.cascade || false;
this.value = params.defaultValue;
this.data = params.data;
this.onOk = params.onOk;
this.sliderElem = params.sliderElem;
this.containerElem = params.containerElem;
this.labelElem = params.labelElem;
this.formatLabel = params.formatLabel;
this.required = params.required;
this.panStart = this.panStart.bind(this);
this.pan = this.pan.bind(this);
this.panEnd = this.panEnd.bind(this);
this.ok = this.ok.bind(this);
this.cancel = this.cancel.bind(this);
this.open = this.open.bind(this);
this.transitionEnd = this.transitionEnd.bind(this);
this.labelElem.innerText = params.defaultLabel || '请选择';
}
private id: string;
/**
* FormCore 实例
*/
private formCore: FormCore;
/**
* 该字段必选提示文字
*/
private required: string;
/**
* 是否级联
*/
private cascade: boolean;
/**
* 值, 格式[value1, value2, value3], 对应数据源的N级value或索引
*/
private value: TspComponentPickerValues[];
/**
* 改变之后触发
*/
private onOk: (selected: TspComponentPickerValues[]) => void;
/**
* 记录当前拖动的元素translateY的值
*/
private translateY: number[] = [];
/**
* 移动开始之前的索引
*/
private moveStartBeforeValueIndex: number;
/**
* 移动中上一个选中的索引
*/
private moveingPrevValueIndex: number;
/**
* 格式化渲染文本
*/
private formatLabel: (selected: TspComponentPickerValues[]) => string;
/**
* 拖动是否结束
*/
public isPanEnd: boolean = true;
/**
* 触摸事件在哪个列上响应
*/
public touchElementCol: number = 0;
/**
* 滑块元素
*/
public sliderElem: HTMLElement;
/**
* 主容器Element
*/
public containerElem: HTMLElement;
/**
* 文本Element
*/
public labelElem: HTMLElement;
/**
* 被选中的项
*/
public selected: TspComponentPickerValues[] = [];
/**
* 数据源
*/
public data: TspComponentPickerDataSource[][];
/**
* 是否已出发onTransitionEnd
*/
public isTransitionEnd: boolean = false;
/**
* 初始化
*/
public init(): void {
for (let i = 0; i < this.data.length; i++) {
if (this.value) {
this.selected.push(this.value[i]);
} else {
this.selected.push({
index: 0
});
}
this.renderCol(this.data[i], i, 'init');
}
this.formCore = new FormCore({
elem: this.containerElem,
required: this.required,
defaultValue: this.value.map((value) => value.value).join()
});
this.setLabel();
this.setElemValue();
}
/**
* 开始拖动
*/
public panStart(evt: TspComponentSliderEvt): void {
this.touchElementCol = this.getTouchElementCol(evt);
if (this.touchElementCol !== undefined) {
this.isPanEnd = false;
this.moveStartBeforeValueIndex = this.getValueIndex(this.touchElementCol, this.selected);
this.moveingPrevValueIndex = this.moveStartBeforeValueIndex;
this.isTransitionEnd = false;
this.removePanEndClass(this.touchElementCol);
}
}
/**
* 拖动中
*/
public pan(evt: TspComponentSliderEvt): void {
if (!this.isPanEnd) {
const position = this.getMoveBeforePosition(this.touchElementCol, this.moveStartBeforeValueIndex);
const moveY = this.getMoveDistance(this.touchElementCol, position, evt.deltaY);
const nowValueIndex = this.getSelectedIndexByMoveY(this.touchElementCol, moveY);
this.moveingPrevValueIndex = this.setCurrentStyle(this.touchElementCol, this.moveingPrevValueIndex, nowValueIndex);
this.sliderMove(this.touchElementCol , moveY);
}
}
/**
* 拖动结束后
*/
public panEnd(evt: TspComponentSliderEvt): void {
if (!this.isPanEnd) {
const position = this.getMoveBeforePosition(this.touchElementCol, this.moveingPrevValueIndex);
this.isPanEnd = true;
// this.containerElem.classList.add('tsp-compoent-slider-disabled');
this.setSelected(this.touchElementCol, this.moveingPrevValueIndex);
this.addPanEndClass(this.touchElementCol);
this.sliderMove(this.touchElementCol, position);
}
}
/**
* 监听translate完成事件
*/
public transitionEnd(timeoutInstance: number): void {
this.isTransitionEnd = true;
this.removePanEndClass(this.touchElementCol);
// this.containerElem.classList.remove('tsp-compoent-slider-disabled');
clearTimeout(timeoutInstance);
}
/**
* 确定
*/
public ok(): void {
popupClose(this.id);
this.setLabel();
this.setElemValue();
if (this.onOk) {
this.onOk(this.selected);
}
}
/**
* 取消
*/
public cancel(): void {
const children = this.sliderElem.children;
for (let i = 0; i < this.data.length; i++) {
children[i].children[this.getValueIndex(i, this.selected)].classList.remove(`${prefix}-body-col-item-current`);
children[i].children[this.getValueIndex(i, this.selected)].classList.add(`${prefix}-body-col-item-current`);
if (this.value) {
this.selected[i] = this.value[i];
}
}
popupClose(this.id);
}
/**
* 打开
*/
public open(): void {
for (let i = 0; i < this.data.length; i++) {
this.pickerTranslateInitial(i, this.getValueIndex(i, this.selected));
}
popupOpen(this.id);
}
/**
* 设置显示的文本
*/
public setLabel(): void {
if (this.formatLabel) {
this.labelElem.textContent = this.formatLabel(this.selected);
} else {
let label = '';
for (let i = 0; i < this.selected.length; i++) {
if (this.selected[i].value !== undefined) {
label += this.selected[i].label;
}
}
if (label !== '' && label !== 'undefined') {
this.labelElem.textContent = label;
}
}
}
/**
* 设置元素element dataset.value
*/
public setElemValue(): void {
const value = [];
for (let i = 0; i < this.selected.length; i++) {
value.push(this.selected[i].value);
}
this.formCore.elem.dataset.value = value.join();
}
/**
* 将待分配的children移动到排列的位置
*/
public pickerTranslateInitial(col: number, selectedIndex: number): void {
const elem = this.sliderElem.children[col];
const position = this.getMoveBeforePosition(col, selectedIndex);
// const oldIndex = this.getValueIndex(col, this.selected);
const length = elem.children.length;
this.translateY[col] = position;
for (let i = 0; i < length; i++) {
elem.children[i]['classList'].remove(`${prefix}-body-col-item-current`);
}
this.setSelected(col, selectedIndex);
elem.children[selectedIndex]['classList'].add(`${prefix}-body-col-item-current`);
setTranslate(elem as HTMLElement, 0, position);
}
/**
* 递归获取value的children
*/
// public getValueChildren(value: TspComponentPickerValues[], col: number, index: number = 0): TspComponentPickerValues[] {
// if (index >= col) {
// return value;
// } else {
// return this.getValueChildren(value[this.selected[index].index].children, col, index + 1);
// }
// }
/**
* 创建每列子项的元素
*/
public createColItemElement(data: TspComponentPickerDataSource[]): HTMLElement[] {
const elem = document.createElement('div');
const tempArray = [];
const length = data.length;
let i = data.length;
elem.classList.add(`${prefix}-body-col-item`);
for (i = 0; i < length; i++) {
const temp = elem.cloneNode(false);
temp.textContent = data[i].label.toString();
tempArray.push(temp);
}
return tempArray;
}
/**
* 获取value在data中的索引
*/
public getValueIndex(col: number, value: TspComponentPickerValues[] = this.value): number {
let selectedIndex = 0;
if (!value) {
return;
}
if (value[col].index !== undefined) {
selectedIndex = value[col].index;
} else if (value[col].value !== undefined) {
const length = this.data[col].length;
let i;
for (i = 0; i < length; i++) {
if (value[col].value === this.data[col][i].value) {
selectedIndex = i;
break;
}
}
}
return selectedIndex;
}
/**
* 获取触摸事件在哪个列上响应
*/
public getTouchElementCol(evt: TspComponentSliderEvt): number {
const col = evt.target.parentNode['dataset'].col;
if (col) {
return parseInt(col);
} else {
return undefined;
}
}
/**
* 添加panend样式
*/
public addPanEndClass(col: number): void {
this.sliderElem.children[col].classList.add('tsp-component-slider-panend');
}
/**
* 移除panend样式
*/
public removePanEndClass(col: number): void {
if (this.sliderElem.children[col]) {
this.sliderElem.children[col].classList.remove('tsp-component-slider-panend');
}
}
/**
* 获取移动前的初始化位置
*/
public getMoveBeforePosition(col: number, selectedIndex: number): number {
return selectedIndex * -34 + coefficient;
}
/**
* 获取移动的距离
*/
public getMoveDistance(col: number, position: number, deltaY: number): number {
const length = (this.data[col].length - 1) * -34 + coefficient;
const distance = position + deltaY;
const symbol = deltaY < 0 ? -1 : 1;
if (distance > coefficient || distance < length) {
return this.translateY[col] + symbol * 0.25;
}
return distance;
}
/**
* 移动滑块容器
*/
public sliderMove(col: number, y: number): void {
this.translateY[col] = y;
setTranslate(this.sliderElem.children[col] as HTMLElement, 0, y);
}
/**
* 根据移动距离计算索引
*/
public getSelectedIndexByMoveY(col: number, moveY: number): number {
const nowValueIndex = -Math.round((moveY - coefficient) / 34);
const count = this.data[col].length - 1;
if (nowValueIndex >= count) {
return count;
} else if (nowValueIndex <= 0) {
return 0;
}
return nowValueIndex;
}
/**
* 设置选中的item样式
*/
public setCurrentStyle(col: number, oldIndex: number, nowIndex: number): number {
if (oldIndex !== nowIndex) {
const elem = this.sliderElem.children[col].children;
elem[oldIndex].classList.remove(`${prefix}-body-col-item-current`);
elem[nowIndex].classList.add(`${prefix}-body-col-item-current`);
}
return nowIndex;
}
/**
* 设置选中的值
*/
public setSelected(col: number, selectedIndex: number): void {
const data = this.data[col];
this.selected[col] = {
value: data[selectedIndex].value,
index: selectedIndex,
label: data[selectedIndex].label
};
}
/**
* 渲染列
*/
public renderCol(data: TspComponentPickerDataSource[], col: number, type: string): void {
const fragment = document.createDocumentFragment();
const elems = this.createColItemElement(data);
const wrap = document.createElement('div');
const length = elems.length;
let i;
for (i = 0; i < length; i++) {
fragment.appendChild(elems[i]);
}
wrap.classList.add(`${prefix}-body-col`);
wrap.dataset.col = col.toString();
wrap.appendChild(fragment);
if (type === 'init') {
this.sliderElem.appendChild(wrap);
} else {
this.sliderElem.replaceChild(wrap, this.sliderElem.children[col]);
}
}
public componentWillReceiveProps(props: TspComponentDatePickerProps, nextProps: TspComponentDatePickerProps): void {
if (props.updateId !== nextProps.updateId || props.defaultLabel !== nextProps.defaultLabel) {
if (nextProps['data']) {
this.data = nextProps['data'];
}
this.sliderElem.innerHTML = '';
for (let i = 0; i < nextProps.defaultValue.length; i++) {
const defaultValue = nextProps.defaultValue[i].value;
this.selected[i] = {
value: defaultValue === undefined || defaultValue === null ? undefined : defaultValue.toString(),
index: undefined,
label: props.changeLabel && nextProps.defaultValue[i].value !== undefined ? nextProps.defaultValue[i].label : undefined,
};
this.renderCol(this.data[i], i, 'init');
}
this.setLabel();
this.setElemValue();
}
}
}
export default Picker;