UNPKG

tsp-component

Version:

提供多端和react版本的UI组件

460 lines (419 loc) 13.2 kB
/// <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;