UNPKG

@tarojs/components

Version:
624 lines (623 loc) 19.5 kB
import { Host, h } from '@stencil/core'; import classNames from 'classnames'; import { hoursRange, minutesRange, compareTime, verifyValue, verifyTime, verifyDate, getYearRange, getMonthRange, getDayRange, formatValue } from './utils'; import { TOP, LINE_HEIGHT } from './constant'; export class Picker { constructor() { this.index = []; // 展示 Picker this.showPicker = () => { if (this.disabled) return; this.height = this.getHeightByIndex(); this.hidden = false; }; this.getHeightByIndex = () => { const height = this.index.map(i => { let factor = 0; if (this.mode === 'time') { factor = LINE_HEIGHT * 4; } return TOP - LINE_HEIGHT * i - factor; }); return height; }; // 隐藏 picker this.hidePicker = () => { this.fadeOut = true; setTimeout(() => { this.hidden = true; this.fadeOut = false; }, 350); }; // 点击确定按钮 this.handleChange = () => { this.hidePicker(); this.index = this.height.map(h => (TOP - h) / LINE_HEIGHT); let value = this.index.length && this.mode !== 'selector' ? this.index : this.index[0]; if (this.mode === 'time') { const range = [hoursRange.slice(), minutesRange.slice()]; const timeArr = this.index.map((n, i) => range[i][n]); this.index = timeArr.map(item => parseInt(item)); value = timeArr.join(':'); } if (this.mode === 'date') { const { _start, _end, _updateValue } = this.pickerDate; const currentYear = _updateValue[0]; const currentMonth = _updateValue[1]; const yearRange = getYearRange(_start.getFullYear(), _end.getFullYear()); const monthRange = getMonthRange(_start, _end, currentYear); const dayRange = getDayRange(_start, _end, currentYear, currentMonth); const year = yearRange[this.index[0]]; const month = monthRange[this.index[1]]; const day = dayRange[this.index[2]]; if (this.fields === 'year') { value = [year]; } else if (this.fields === 'month') { value = [year, month]; } else { value = [year, month, day]; } value = value .map(item => { return item < 10 ? `0${item}` : item; }) .join('-'); } this.value = value; this.pickerValue = this.value; this.onChange.emit({ value }); }; this.handleColumnChange = (e) => { const { columnId, height } = e.detail; this.onColumnChange.emit({ column: Number(columnId), value: (TOP - height) / LINE_HEIGHT }); }; // 点击取消按钮或蒙层 this.handleCancel = () => { this.hidePicker(); this.onCancel.emit(); }; this.updateHeight = (height, columnId, needRevise = false) => { const temp = [...this.height]; temp[columnId] = height; this.height = temp; // time picker 必须在规定时间范围内,因此需要在 touchEnd 做修正 if (needRevise) { let { start, end } = this; if (!verifyTime(start)) start = '00:00'; if (!verifyTime(end)) end = '23:59'; if (!compareTime(start, end)) return; const range = [hoursRange.slice(), minutesRange.slice()]; const timeList = this.height.map(h => (TOP - h) / LINE_HEIGHT); const timeStr = timeList.map((n, i) => range[i][n]).join(':'); if (!compareTime(start, timeStr)) { // 修正到 start const height = start .split(':') .map(i => TOP - LINE_HEIGHT * (+i + 4)); requestAnimationFrame(() => (this.height = height)); } else if (!compareTime(timeStr, end)) { // 修正到 end const height = end .split(':') .map(i => TOP - LINE_HEIGHT * (+i + 4)); requestAnimationFrame(() => (this.height = height)); } } }; this.updateDay = (value, fields) => { const { _start, _end, _updateValue } = this.pickerDate; _updateValue[fields] = value; const currentYear = _updateValue[0]; const currentMonth = _updateValue[1]; const currentDay = _updateValue[2]; // 滚动年份 if (fields === 0) { const monthRange = getMonthRange(_start, _end, currentYear); const max = monthRange[monthRange.length - 1]; const min = monthRange[0]; if (currentMonth > max) _updateValue[1] = max; if (currentMonth < min) _updateValue[1] = min; const index = monthRange.indexOf(_updateValue[1]); const height = TOP - LINE_HEIGHT * index; this.updateDay(_updateValue[1], 1); this.updateHeight(height, '1'); } else if (fields === 1) { const dayRange = getDayRange(_start, _end, currentYear, currentMonth); const max = dayRange[dayRange.length - 1]; const min = dayRange[0]; if (currentDay > max) _updateValue[2] = max; if (currentDay < min) _updateValue[2] = min; const index = dayRange.indexOf(_updateValue[2]); const height = TOP - LINE_HEIGHT * index; this.updateDay(_updateValue[2], 2); this.updateHeight(height, '2'); } }; // 单列选择器 this.getSelector = () => { return (h("taro-picker-group", { range: this.range, rangeKey: this.rangeKey, height: this.height[0], updateHeight: this.updateHeight, columnId: '0' })); }; // 多列选择器 this.getMultiSelector = () => { return this.range.map((range, index) => { return (h("taro-picker-group", { range: range, rangeKey: this.rangeKey, height: this.height[index], updateHeight: this.updateHeight, onColumnChange: this.handleColumnChange, columnId: String(index) })); }); }; // 时间选择器 this.getTimeSelector = () => { const hourRange = hoursRange.slice(); const minRange = minutesRange.slice(); return [ h("taro-picker-group", { mode: 'time', range: hourRange, height: this.height[0], updateHeight: this.updateHeight, columnId: '0' }), h("taro-picker-group", { mode: 'time', range: minRange, height: this.height[1], updateHeight: this.updateHeight, columnId: '1' }) ]; }; // 日期选择器 this.getDateSelector = () => { const { fields, height } = this; const { _start, _end, _updateValue } = this.pickerDate; const currentYear = _updateValue[0]; const currentMonth = _updateValue[1]; const yearRange = getYearRange(_start.getFullYear(), _end.getFullYear()) .map(item => `${item}年`); const monthRange = getMonthRange(_start, _end, currentYear) .map(item => `${item < 10 ? `0${item}` : item}月`); const dayRange = getDayRange(_start, _end, currentYear, currentMonth) .map(item => `${item < 10 ? `0${item}` : item}日`); const renderView = [ h("taro-picker-group", { mode: 'date', range: yearRange, height: height[0], updateDay: this.updateDay, updateHeight: this.updateHeight, columnId: '0' }) ]; if (fields === 'month' || fields === 'day') { renderView.push(h("taro-picker-group", { mode: 'date', range: monthRange, height: height[1], updateDay: this.updateDay, updateHeight: this.updateHeight, columnId: '1' })); } if (fields === 'day') { renderView.push(h("taro-picker-group", { mode: 'date', range: dayRange, height: height[2], updateDay: this.updateDay, updateHeight: this.updateHeight, columnId: '2' })); } return renderView; }; this.mode = 'selector'; this.disabled = false; this.range = []; this.rangeKey = undefined; this.value = undefined; this.start = ''; this.end = ''; this.fields = 'day'; this.name = ''; this.textProps = {}; this.pickerValue = []; this.height = []; this.hidden = true; this.fadeOut = false; this.isWillLoadCalled = false; } componentWillLoad() { this.isWillLoadCalled = true; this.handleProps(); } componentDidLoad() { if (this.overlay) { document.body.appendChild(this.overlay); } } disconnectedCallback() { var _a; if (this.overlay) { (_a = this.overlay.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.overlay); } } onPropsChange() { if (!this.isWillLoadCalled) return; this.handleProps(); } handleProps() { const { mode, start, end } = this; if (mode === 'selector') { const value = this.value; this.index = [verifyValue(value, this.range) ? Math.floor(value) : 0]; } else if (mode === 'multiSelector') { const value = this.value; this.index = []; this.range.forEach((range, index) => { const val = value === null || value === void 0 ? void 0 : value[index]; const item = verifyValue(val, range) ? Math.floor(val) : 0; this.index.push(item); }); } else if (mode === 'time') { let value = this.value; // check value... if (!verifyTime(value)) { console.warn('time picker value illegal'); value = '0:0'; } const time = value.split(':').map(n => +n); this.index = time; } else if (mode === 'date') { const value = this.value; let _value = verifyDate(value) || new Date(new Date().setHours(0, 0, 0, 0)); // 没传值或值的合法性错误默认今天时间 const _start = verifyDate(start) || new Date('1970/01/01'); const _end = verifyDate(end) || new Date('2999/01/01'); // 时间区间有效性 if (!(_start <= _end)) { throw new Error(`Picker start time must be less than end time.`); } if (!(_value >= _start && _value <= _end)) { _value = _start; } const currentYear = _value.getFullYear(); const currentMonth = _value.getMonth() + 1; const currentDay = _value.getDate(); const yearRange = getYearRange(_start.getFullYear(), _end.getFullYear()); const monthRange = getMonthRange(_start, _end, currentYear); const dayRange = getDayRange(_start, _end, currentYear, currentMonth); this.index = [ yearRange.indexOf(currentYear), monthRange.indexOf(currentMonth), dayRange.indexOf(currentDay) ]; if (!this.pickerDate || this.pickerDate._value.getTime() !== _value.getTime() || this.pickerDate._start.getTime() !== _start.getTime() || this.pickerDate._end.getTime() !== _end.getTime()) { this.pickerDate = { _value, _start, _end, _updateValue: [ currentYear, currentMonth, currentDay ] }; } } else { throw new Error(`Picker not support "${mode}" mode.`); } // Prop 变化时,无论是否正在显示弹层,都更新 height 值 this.height = this.getHeightByIndex(); // 同步表单 value 值,用于 form submit this.pickerValue = this.value; if (mode === 'date') { const val = this.pickerValue; if (this.fields === 'month') { this.pickerValue = val.split('-').slice(0, 2).join('-'); } else if (this.fields === 'year') { this.pickerValue = val.split('-')[0]; } } } render() { var _a, _b; const { name, mode, fadeOut, hidden } = this; // 选项条 let pickerGroup; switch (mode) { case 'multiSelector': pickerGroup = this.getMultiSelector(); break; case 'time': pickerGroup = this.getTimeSelector(); break; case 'date': pickerGroup = this.getDateSelector(); break; default: pickerGroup = this.getSelector(); } // 动画类名控制逻辑 const clsMask = classNames('weui-mask', 'weui-animate-fade-in', { 'weui-animate-fade-out': fadeOut }); const clsSlider = classNames('weui-picker', 'weui-animate-slide-up', { 'weui-animate-slide-down': fadeOut }); const shouldDivHidden = hidden ? { display: 'none' } : {}; return (h(Host, null, h("div", { onClick: this.showPicker }, h("slot", null), h("input", { type: 'hidden', name: name, value: formatValue(this.pickerValue) })), h("div", { class: 'weui-picker__overlay', style: shouldDivHidden, ref: el => { this.overlay = el; } }, h("div", { class: clsMask, onClick: this.handleCancel }), h("div", { class: clsSlider }, h("div", { class: 'weui-picker__hd' }, h("div", { class: 'weui-picker__action', onClick: this.handleCancel }, (_a = this.textProps.cancelText) !== null && _a !== void 0 ? _a : '取消'), h("div", { class: 'weui-picker__action', onClick: this.handleChange }, (_b = this.textProps.okText) !== null && _b !== void 0 ? _b : '确定')), h("div", { class: 'weui-picker__bd' }, pickerGroup), h("input", { type: 'hidden', name: name, value: formatValue(this.pickerValue) }))))); } static get is() { return "taro-picker-core"; } static get originalStyleUrls() { return { "$": ["./style/index.scss"] }; } static get styleUrls() { return { "$": ["./style/index.css"] }; } static get properties() { return { "mode": { "type": "string", "mutable": false, "complexType": { "original": "Mode", "resolved": "\"date\" | \"multiSelector\" | \"selector\" | \"time\"", "references": { "Mode": { "location": "local" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "mode", "reflect": false, "defaultValue": "'selector'" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "disabled", "reflect": false, "defaultValue": "false" }, "range": { "type": "unknown", "mutable": false, "complexType": { "original": "any[]", "resolved": "any[]", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "defaultValue": "[]" }, "rangeKey": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "range-key", "reflect": false }, "value": { "type": "any", "mutable": true, "complexType": { "original": "PickerValue", "resolved": "number | number[] | string", "references": { "PickerValue": { "location": "local" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "value", "reflect": false }, "start": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "start", "reflect": false, "defaultValue": "''" }, "end": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "end", "reflect": false, "defaultValue": "''" }, "fields": { "type": "string", "mutable": false, "complexType": { "original": "Fields", "resolved": "\"day\" | \"month\" | \"year\"", "references": { "Fields": { "location": "local" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "fields", "reflect": false, "defaultValue": "'day'" }, "name": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "name", "reflect": false, "defaultValue": "''" }, "textProps": { "type": "unknown", "mutable": false, "complexType": { "original": "PickerText", "resolved": "PickerText", "references": { "PickerText": { "location": "local" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "defaultValue": "{}" } }; } static get states() { return { "pickerValue": {}, "height": {}, "hidden": {}, "fadeOut": {}, "isWillLoadCalled": {} }; } static get events() { return [{ "method": "onChange", "name": "change", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "any", "resolved": "any", "references": {} } }, { "method": "onColumnChange", "name": "columnchange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "any", "resolved": "any", "references": {} } }, { "method": "onCancel", "name": "cancel", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "any", "resolved": "any", "references": {} } }]; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "mode", "methodName": "onPropsChange" }, { "propName": "value", "methodName": "onPropsChange" }, { "propName": "range", "methodName": "onPropsChange" }, { "propName": "start", "methodName": "onPropsChange" }, { "propName": "end", "methodName": "onPropsChange" }]; } }