diff --git a/src/app.tsx b/src/app.tsx
index 3024735..6d2e71d 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -7,14 +7,14 @@ class App extends Taro.Component {
pages: [
'pages/Home',
// @index('./pages/*[!Home].tsx', (pp, cc) => `'${pp.path.replace(/^\.\//, '')}',`)
- // 'pages/DatePicker',
- // 'pages/ECharts',
+ 'pages/DatePicker',
+ 'pages/ECharts',
'pages/Picker',
'pages/PickerView',
'pages/Popup',
- // 'pages/SinglePicker',
+ 'pages/SinglePicker',
'pages/Sticky',
- // 'pages/TimePicker',
+ 'pages/TimePicker',
// 'pages/Transition',
// @endindex
],
diff --git a/src/components/DatePicker/index.tsx b/src/components/DatePicker/index.tsx
index ca0d998..ca8b66e 100644
--- a/src/components/DatePicker/index.tsx
+++ b/src/components/DatePicker/index.tsx
@@ -1,10 +1,10 @@
import dayjs from 'dayjs'
-import MPicker from '../Picker'
-import Taro from '@tarojs/taro'
-import { CascadedData } from '../PickerView'
-import { component } from '../component'
-import { MDatePickerProps } from './props'
+import MPicker, { MPickerProps } from '../Picker'
+import Taro, { useEffect, useState } from '@tarojs/taro'
+import { functionalComponent } from '../component'
+import { MDatePickerDefaultProps, MDatePickerProps } from './props'
import { memoize } from 'vtils'
+import { MPickerCascadedData } from '../Picker/types'
const getDaysInMonth = memoize(
(month: number, year: number) => {
@@ -16,162 +16,130 @@ const getDaysInMonth = memoize(
},
)
-/**
- * 日期选择器组件。
- *
- * @example
- *
- * ```jsx
- * this.setState({ selectedDate })}>
- * 选择日期
- *
- * ```
- */
-class MDatePicker extends component({
- props: MDatePickerProps,
- state: {
- localData: [] as CascadedData,
- localSelectedIndexes: [] as number[],
- },
-}) {
- lastUpdateAt: number = 0
-
- lastSelectedIndexes: number[] = []
+function MDatePicker(props: MDatePickerProps) {
+ const [data, setData] = useState([])
+ const [selectedIndexes, setSelectedIndexes] = useState([])
- componentDidMount() {
- this.updateLocalState(this.props)
- }
+ useEffect(
+ () => {
+ let reject: boolean | void = false
- componentWillReceiveProps(nextProps: MDatePicker['props']) {
- // perf: 极短时间内的行为应是由子组件触发的父组件更新
- if (this.lastUpdateAt && (Date.now() - this.lastUpdateAt < 60)) {
- this.setState({
- localSelectedIndexes: this.lastSelectedIndexes,
- })
- } else {
- this.updateLocalState(nextProps)
- }
- }
+ const startDate = dayjs(props.startDate)
+ const startYear = startDate.year()
+ const startMonth = startDate.month() + 1
+ const startDay = startDate.date()
- updateLocalState(props: MDatePicker['props']) {
- let reject: boolean | void = false
+ const endDate = dayjs(props.endDate)
+ const endYear = endDate.year()
+ const endMonth = endDate.month() + 1
+ const endDay = endDate.date()
- const startDate = dayjs(props.startDate)
- const startYear = startDate.year()
- const startMonth = startDate.month() + 1
- const startDay = startDate.date()
+ const useRawYearValue = props.formatYear == null
+ const useRawMonthValue = props.formatMonth == null
+ const useRawDayValue = props.formatDay == null
- const endDate = dayjs(props.endDate)
- const endYear = endDate.year()
- const endMonth = endDate.month() + 1
- const endDay = endDate.date()
-
- const useRawYearValue = props.formatYear == null
- const useRawMonthValue = props.formatMonth == null
- const useRawDayValue = props.formatDay == null
-
- const yearList: CascadedData = []
- const selectedIndexes: number[] = []
- for (let year = startYear; year <= endYear; year++) {
- reject = props.filterYear && props.filterYear({ year: year })
- if (reject !== true) {
- if (year === props.selectedDate[0]) {
- selectedIndexes[0] = yearList.length
- }
- const monthList: CascadedData = []
- yearList.push({
- label: String(useRawYearValue ? year : props.formatYear({ year })),
- value: year,
- children: monthList,
- })
- const months = year === endYear ? endMonth : 12
- for (let month = (year === startYear ? startMonth : 1); month <= months; month++) {
- reject = props.filterMonth && props.filterMonth({
- year: year,
- month: month,
+ const yearList: MPickerCascadedData = []
+ const selectedIndexes: number[] = []
+ for (let year = startYear; year <= endYear; year++) {
+ reject = props.filterYear && props.filterYear({ year: year })
+ if (reject !== true) {
+ if (year === props.selectedDate[0]) {
+ selectedIndexes[0] = yearList.length
+ }
+ const monthList: MPickerCascadedData = []
+ yearList.push({
+ label: String(useRawYearValue ? year : props.formatYear({ year })),
+ value: year,
+ children: monthList,
})
- if (reject !== true) {
- if (month === props.selectedDate[1]) {
- selectedIndexes[1] = monthList.length
- }
- const dayList: CascadedData = []
- monthList.push({
- label: String(useRawMonthValue ? month : props.formatMonth({ year, month })),
- value: month,
- children: dayList,
+ const months = year === endYear ? endMonth : 12
+ for (let month = (year === startYear ? startMonth : 1); month <= months; month++) {
+ reject = props.filterMonth && props.filterMonth({
+ year: year,
+ month: month,
})
- const days = year === endYear && month === endMonth ? endDay : getDaysInMonth(month, year)
- for (let day = (year === startYear && month === startMonth ? startDay : 1); day <= days; day++) {
- reject = props.filterDay && props.filterDay({
- year: year,
- month: month,
- day: day,
+ if (reject !== true) {
+ if (month === props.selectedDate[1]) {
+ selectedIndexes[1] = monthList.length
+ }
+ const dayList: MPickerCascadedData = []
+ monthList.push({
+ label: String(useRawMonthValue ? month : props.formatMonth({ year, month })),
+ value: month,
+ children: dayList,
})
- if (reject !== true) {
- if (day === props.selectedDate[2]) {
- selectedIndexes[2] = dayList.length
- }
- dayList.push({
- label: String(useRawDayValue ? day : props.formatDay({ year, month, day })),
- value: day,
+ const days = year === endYear && month === endMonth ? endDay : getDaysInMonth(month, year)
+ for (let day = (year === startYear && month === startMonth ? startDay : 1); day <= days; day++) {
+ reject = props.filterDay && props.filterDay({
+ year: year,
+ month: month,
+ day: day,
})
- } else {
- reject = false
+ if (reject !== true) {
+ if (day === props.selectedDate[2]) {
+ selectedIndexes[2] = dayList.length
+ }
+ dayList.push({
+ label: String(useRawDayValue ? day : props.formatDay({ year, month, day })),
+ value: day,
+ })
+ } else {
+ reject = false
+ }
}
+ selectedIndexes[2] = selectedIndexes[2] == null ? 0 : selectedIndexes[2]
+ } else {
+ reject = false
}
- selectedIndexes[2] = selectedIndexes[2] == null ? 0 : selectedIndexes[2]
- } else {
- reject = false
}
+ selectedIndexes[1] = selectedIndexes[1] == null ? 0 : selectedIndexes[1]
+ } else {
+ reject = false
}
- selectedIndexes[1] = selectedIndexes[1] == null ? 0 : selectedIndexes[1]
- } else {
- reject = false
}
- }
- selectedIndexes[0] = selectedIndexes[0] == null ? 0 : selectedIndexes[0]
+ selectedIndexes[0] = selectedIndexes[0] == null ? 0 : selectedIndexes[0]
- this.setState({
- localData: yearList,
- localSelectedIndexes: selectedIndexes,
- })
- }
+ setData(yearList)
+ setSelectedIndexes(selectedIndexes)
+ },
+ [
+ props.startDate,
+ props.endDate,
+ props.filterYear,
+ props.filterMonth,
+ props.filterDay,
+ props.formatYear,
+ props.formatMonth,
+ props.formatDay,
+ ],
+ )
- handleConfirm: MPicker['props']['onConfirm'] = selectedIndexes => {
- const { localData } = this.state
+ const handleConfirm: MPickerProps['onConfirm'] = selectedIndexes => {
const selectedDate: number[] = []
- let list = localData
- const n = Math.min(selectedIndexes.length, 3)
- for (let i = 0; i < n; i++) {
+ for (
+ let i = 0,
+ n = Math.min(selectedIndexes.length, 3),
+ list = data;
+ i < n;
+ i++
+ ) {
if (!list[selectedIndexes[i]]) break
selectedDate.push(list[selectedIndexes[i]].value)
list = list[selectedIndexes[i]].children
if (!list) break
}
- this.lastUpdateAt = Date.now()
- this.lastSelectedIndexes = selectedIndexes
- this.props.onConfirm(selectedDate)
+ props.onConfirm(selectedDate)
}
- render() {
- const {
- localData,
- localSelectedIndexes,
- } = this.state
-
- return !localData.length ? this.props.children : (
-
- {this.props.children}
-
- )
- }
+ return !data.length ? props.children : (
+
+ {props.children}
+
+ )
}
-export default MDatePicker
+export default functionalComponent(MDatePickerDefaultProps)(MDatePicker)
diff --git a/src/components/DatePicker/props.ts b/src/components/DatePicker/props.ts
index ddc7271..2145e03 100644
--- a/src/components/DatePicker/props.ts
+++ b/src/components/DatePicker/props.ts
@@ -1,109 +1,116 @@
-import { MPickerProps } from '../Picker/props'
+import { createProps, RequiredProp } from '../component'
+import { MPickerDefaultProps } from '../Picker/props'
import { noop, omit } from 'vtils'
-import { RequiredProp } from '../component'
const currentYear = new Date().getFullYear()
-export const MDatePickerProps = {
- ...omit(MPickerProps, ['data', 'selectedIndexes', 'onConfirm']),
+export const MDatePickerDefaultProps = {
+ ...omit(
+ MPickerDefaultProps,
+ ['data', 'selectedIndexes', 'onConfirm'],
+ ),
- /**
- * 开始日期。可以是:
- *
- * - 字符串: `2019-2-03`
- * - ISO 8601 字符串:`2019-07-10T08:30:19.710Z`
- * - Unix 时间戳: `1554916837`
- *
- * @default `${currentYear - 10}-1-1`
- */
- startDate: `${currentYear - 10}-1-1` as string | number,
+ ...createProps({
+ /**
+ * 开始日期。可以是:
+ *
+ * - 字符串: `2019-2-03`
+ * - ISO 8601 字符串:`2019-07-10T08:30:19.710Z`
+ * - Unix 时间戳: `1554916837`
+ *
+ * @default `${currentYear - 10}-1-1`
+ */
+ startDate: `${currentYear - 10}-1-1` as string | number,
- /**
- * 结束日期。可以是:
- *
- * - 字符串: `2019-2-03`
- * - ISO 8601 字符串:`2019-07-10T08:30:19.710Z`
- * - Unix 时间戳: `1554916837`
- *
- * @default `${currentYear + 10}-12-31`
- */
- endDate: `${currentYear + 10}-12-31` as string | number,
+ /**
+ * 结束日期。可以是:
+ *
+ * - 字符串: `2019-2-03`
+ * - ISO 8601 字符串:`2019-07-10T08:30:19.710Z`
+ * - Unix 时间戳: `1554916837`
+ *
+ * @default `${currentYear + 10}-12-31`
+ */
+ endDate: `${currentYear + 10}-12-31` as string | number,
- /**
- * 格式化年份。
- *
- * @default params => params.year
- */
- formatYear: undefined as (params: {
- /** 年 */
- year: number,
- }) => string | number,
+ /**
+ * 格式化年份。
+ *
+ * @default params => params.year
+ */
+ formatYear: undefined as (params: {
+ /** 年 */
+ year: number,
+ }) => string | number,
- /**
- * 格式化月份。
- *
- * @default params => params.month
- */
- formatMonth: undefined as (params: {
- /** 年 */
- year: number,
- /** 月 */
- month: number,
- }) => string | number,
+ /**
+ * 格式化月份。
+ *
+ * @default params => params.month
+ */
+ formatMonth: undefined as (params: {
+ /** 年 */
+ year: number,
+ /** 月 */
+ month: number,
+ }) => string | number,
- /**
- * 格式化天数。
- *
- * @default params => params.day
- */
- formatDay: undefined as (params: {
- /** 年 */
- year: number,
- /** 月 */
- month: number,
- /** 日 */
- day: number,
- }) => string | number,
+ /**
+ * 格式化天数。
+ *
+ * @default params => params.day
+ */
+ formatDay: undefined as (params: {
+ /** 年 */
+ year: number,
+ /** 月 */
+ month: number,
+ /** 日 */
+ day: number,
+ }) => string | number,
- /**
- * 年份过滤器,返回 `true` 可过滤掉传入的年份。
- */
- filterYear: undefined as (params: {
- /** 年 */
- year: number,
- }) => boolean | void,
+ /**
+ * 年份过滤器,返回 `true` 可过滤掉传入的年份。
+ */
+ filterYear: undefined as (params: {
+ /** 年 */
+ year: number,
+ }) => boolean | void,
- /**
- * 月份过滤器,返回 `true` 可过滤掉传入的月份。
- */
- filterMonth: undefined as (params: {
- /** 年 */
- year: number,
- /** 月 */
- month: number,
- }) => boolean | void,
+ /**
+ * 月份过滤器,返回 `true` 可过滤掉传入的月份。
+ */
+ filterMonth: undefined as (params: {
+ /** 年 */
+ year: number,
+ /** 月 */
+ month: number,
+ }) => boolean | void,
- /**
- * 日期过滤器,返回 `true` 可过滤掉传入的日期。
- */
- filterDay: undefined as (params: {
- /** 年 */
- year: number,
- /** 月 */
- month: number,
- /** 日 */
- day: number,
- }) => boolean | void,
+ /**
+ * 日期过滤器,返回 `true` 可过滤掉传入的日期。
+ */
+ filterDay: undefined as (params: {
+ /** 年 */
+ year: number,
+ /** 月 */
+ month: number,
+ /** 日 */
+ day: number,
+ }) => boolean | void,
- /**
- * 选中的日期。
- */
- selectedDate: [] as any as RequiredProp,
+ /**
+ * 选中的日期。
+ */
+ selectedDate: [] as any as RequiredProp,
- /**
- * 点击确定事件。
- *
- * @default () => {}
- */
- onConfirm: noop as any as RequiredProp<(selectedDate: number[]) => void>,
+ /**
+ * 点击确定事件。
+ *
+ * @default () => {}
+ */
+ onConfirm: noop as any as RequiredProp<(selectedDate: number[]) => void>,
+ }),
}
+
+export type MDatePickerProps = typeof MDatePickerDefaultProps
diff --git a/src/components/ECharts/index.tsx b/src/components/ECharts/index.tsx
index bf59e5d..606c32a 100644
--- a/src/components/ECharts/index.tsx
+++ b/src/components/ECharts/index.tsx
@@ -1,38 +1,39 @@
-import Taro from '@tarojs/taro'
+import Taro, { useEffect, useRef } from '@tarojs/taro'
import { Canvas } from '@tarojs/components'
import { CanvasAdapter, wrapTouch } from './utils'
-import { component } from '../component'
-import { MEChartsProps } from './props'
+import { functionalComponent } from '../component'
+import { MEChartsDefaultProps, MEChartsProps } from './props'
const canvasId = 'canvas'
-export default class MECharts extends component({
- props: MEChartsProps,
-}) {
- chart: any
+function MECharts(props: MEChartsProps) {
+ const chart = useRef(null)
- componentDidMount() {
- const { getECharts, options } = this.props
- const echarts = getECharts()
- const ctx = Taro.createCanvasContext(canvasId, this.$scope)
- const canvas = new CanvasAdapter(ctx)
- ;(echarts as any).setCanvasCreator(() => canvas)
- Taro.createSelectorQuery()
- .in(this.$scope)
- .select('.m-echarts__canvas')
- .boundingClientRect(res => {
- this.chart = (echarts as any).init(canvas, null, res)
- this.chart.setOption(options)
- canvas.setChart(this.chart)
- })
- .exec()
- }
+ useEffect(
+ () => {
+ const { getECharts, options } = props
+ const echarts = getECharts()
+ const ctx = Taro.createCanvasContext(canvasId, this.$scope)
+ const canvas = new CanvasAdapter(ctx)
+ ;(echarts as any).setCanvasCreator(() => canvas)
+ Taro.createSelectorQuery()
+ .in(this.$scope)
+ .select('.m-echarts__canvas')
+ .boundingClientRect(res => {
+ chart.current = (echarts as any).init(canvas, null, res)
+ chart.current.setOption(options)
+ canvas.setChart(chart.current)
+ })
+ .exec()
+ },
+ [],
+ )
- handleTouchStart(e: any) {
- if (this.props.disableTouch) return
- if (this.chart && e.touches.length > 0) {
+ function handleTouchStart(e: any) {
+ if (props.disableTouch) return
+ if (chart.current && e.touches.length > 0) {
const touch = e.touches[0]
- const handler = this.chart.getZr().handler
+ const handler = chart.current.getZr().handler
handler.dispatch('mousedown', {
zrX: touch.x,
zrY: touch.y,
@@ -45,11 +46,11 @@ export default class MECharts extends component({
}
}
- handleTouchMove(e: any) {
- if (this.props.disableTouch) return
- if (this.chart && e.touches.length > 0) {
+ function handleTouchMove(e: any) {
+ if (props.disableTouch) return
+ if (chart.current && e.touches.length > 0) {
const touch = e.touches[0]
- const handler = this.chart.getZr().handler
+ const handler = chart.current.getZr().handler
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y,
@@ -58,11 +59,11 @@ export default class MECharts extends component({
}
}
- handleTouchEnd(e: any) {
- if (this.props.disableTouch) return
- if (this.chart) {
+ function handleTouchEnd(e: any) {
+ if (props.disableTouch) return
+ if (chart.current) {
const touch = e.changedTouches ? e.changedTouches[0] : {}
- const handler = this.chart.getZr().handler
+ const handler = chart.current.getZr().handler
handler.dispatch('mouseup', {
zrX: touch.x,
zrY: touch.y,
@@ -75,18 +76,18 @@ export default class MECharts extends component({
}
}
- render() {
- const { className } = this.props
-
- return (
-
- )
- }
+ return (
+
+ )
}
+
+export { MEChartsProps }
+
+export default functionalComponent(MEChartsDefaultProps)(MECharts)
diff --git a/src/components/ECharts/props.ts b/src/components/ECharts/props.ts
index 096a953..c2cd2bc 100644
--- a/src/components/ECharts/props.ts
+++ b/src/components/ECharts/props.ts
@@ -1,8 +1,8 @@
import * as ECharts from 'echarts'
+import { createProps, RequiredProp } from '../component'
import { noop } from 'vtils'
-import { RequiredProp } from '../component'
-export const MEChartsProps = {
+export const MEChartsDefaultProps = createProps({
/**
* 获取 `ECharts` 对象的函数。
*
@@ -27,4 +27,6 @@ export const MEChartsProps = {
* @default false
*/
disableTouch: false as boolean,
-}
+})
+
+export type MEChartsProps = typeof MEChartsDefaultProps
diff --git a/src/components/NavigationBar/index.tsx b/src/components/NavigationBar/index.tsx
index 782834c..3e491a8 100644
--- a/src/components/NavigationBar/index.tsx
+++ b/src/components/NavigationBar/index.tsx
@@ -2,7 +2,7 @@ import './index.scss'
import Taro, { useState } from '@tarojs/taro'
import { functionalComponent } from '../component'
import { last } from 'vtils'
-import { NavigationBarDefaultProps, NavigationBarProps } from './props'
+import { MNavigationBarDefaultProps, MNavigationBarProps } from './props'
import { useCustomNavigationBarFullHeight, useDidEnter, useDidLeave } from '../../hooks'
import { View } from '@tarojs/components'
@@ -10,7 +10,7 @@ function onlyPath(url: string) {
return url ? url.split('?')[0].replace(/^\/+/, '') : ''
}
-function NavigationBar(props: NavigationBarProps) {
+function MNavigationBar(props: MNavigationBarProps) {
const { setCustomNavigationBarFullHeight, resetCustomNavigationBarFullHeight } = useCustomNavigationBarFullHeight()
const [state, setState] = useState({
verticalPadding: 0 as number,
@@ -115,6 +115,6 @@ function NavigationBar(props: NavigationBarProps) {
)
}
-export { NavigationBarProps }
+export { MNavigationBarProps }
-export default functionalComponent(NavigationBarDefaultProps)(NavigationBar)
+export default functionalComponent(MNavigationBarDefaultProps)(MNavigationBar)
diff --git a/src/components/NavigationBar/props.ts b/src/components/NavigationBar/props.ts
index 4bdde5c..98707a9 100644
--- a/src/components/NavigationBar/props.ts
+++ b/src/components/NavigationBar/props.ts
@@ -1,6 +1,6 @@
import { createProps, RequiredProp } from '../component'
-export const NavigationBarDefaultProps = createProps({
+export const MNavigationBarDefaultProps = createProps({
/**
* 小程序主页的绝对路径,可带参数。
*
@@ -25,4 +25,4 @@ export const NavigationBarDefaultProps = createProps({
backgroundColor: 'auto' as string,
})
-export type NavigationBarProps = typeof NavigationBarDefaultProps
+export type MNavigationBarProps = typeof MNavigationBarDefaultProps
diff --git a/src/components/Picker/index.tsx b/src/components/Picker/index.tsx
index d785d4c..0321646 100644
--- a/src/components/Picker/index.tsx
+++ b/src/components/Picker/index.tsx
@@ -1,137 +1,101 @@
import MPickerView from '../PickerView'
import MPopup from '../Popup'
-import Taro from '@tarojs/taro'
-import { component } from '../component'
-import { MPickerProps } from './props'
+import Taro, { useEffect, useRef, useState } from '@tarojs/taro'
+import { functionalComponent } from '../component'
+import { MPickerDefaultProps, MPickerProps } from './props'
import { View } from '@tarojs/components'
-/**
- * 选择器组件。
- */
-class MPicker extends component({
- props: MPickerProps,
- state: {
- localVisible: false as boolean,
- localSelectedIndexes: [],
- },
-}) {
- canClose: boolean = true
+function MPicker(props: MPickerProps) {
+ const [visible, setVisible] = useState(false)
+ const [selectedIndexes, setSelectedIndexes] = useState([])
+ const canClose = useRef(false)
- componentWillMount() {
- const { selectedIndexes } = this.props
- this.setState({
- localSelectedIndexes: selectedIndexes,
- })
- }
-
- componentWillReceiveProps({ selectedIndexes }: MPicker['props']) {
- this.setState({
- localSelectedIndexes: selectedIndexes,
- })
- }
+ useEffect(
+ () => {
+ setSelectedIndexes(props.selectedIndexes)
+ },
+ [props.selectedIndexes],
+ )
- handleTriggerClick = () => {
- this.setState(_ => ({
- localVisible: !_.localVisible,
- }))
+ function handleTriggerClick() {
+ setVisible(!visible)
}
- handleVisibleChange: MPopup['props']['onVisibleChange'] = visible => {
- const { selectedIndexes } = this.props
- this.setState({
- localVisible: visible,
- ...(visible ? {} as any : { localSelectedIndexes: selectedIndexes }),
- })
+ function handleVisibleChange(visible: boolean) {
+ setVisible(visible)
+ if (!visible) {
+ setSelectedIndexes(props.selectedIndexes)
+ }
}
- handlePickStart = () => {
- this.canClose = false
+ function handlePickStart() {
+ canClose.current = false
}
- handlePickEnd = () => {
- this.canClose = true
+ function handlePickEnd() {
+ canClose.current = true
}
- handlePickChange: MPickerView['props']['onChange'] = selectedIndexes => {
- this.setState({
- localSelectedIndexes: selectedIndexes,
- })
+ function handlePickChange(selectedIndexes: number[]) {
+ setSelectedIndexes(selectedIndexes)
}
- handleCancelClick = () => {
- if (!this.canClose) return
- this.setState({
- localSelectedIndexes: this.props.selectedIndexes,
- localVisible: false,
- }, () => {
- this.props.onCancel()
- })
+ function handleCancelClick() {
+ if (canClose.current) {
+ setSelectedIndexes(props.selectedIndexes)
+ setVisible(false)
+ props.onCancel()
+ }
}
- handleConfirmClick = () => {
- if (!this.canClose) return
- this.setState({
- localVisible: false,
- }, () => {
- this.props.onConfirm(this.state.localSelectedIndexes.slice())
- })
+ function handleConfirmClick() {
+ if (canClose.current) {
+ setVisible(false)
+ props.onConfirm(selectedIndexes.slice())
+ }
}
- render() {
- const {
- maskClosable,
- noCancel,
- cancelText,
- confirmText,
- title,
- disabled,
- className,
- // ...props // TODO: 尚不可用
- } = this.props
-
- const {
- localVisible,
- localSelectedIndexes,
- } = this.state
-
- return disabled ? this.props.children : (
-
-
- {this.props.children}
-
-
-
-
-
- {cancelText}
-
-
- {title}
-
-
- {confirmText}
-
+ return props.disabled ? props.children : (
+
+
+ {props.children}
+
+
+
+
+
+ {props.cancelText}
+
+
+ {props.title}
+
+
+ {props.confirmText}
-
-
-
- )
- }
+
+
+
+
+ )
}
-export default MPicker
+export * from './types'
+
+export { MPickerProps }
+
+export default functionalComponent(MPickerDefaultProps)(MPicker)
diff --git a/src/components/Picker/props.ts b/src/components/Picker/props.ts
index 4967193..e6b9bdc 100644
--- a/src/components/Picker/props.ts
+++ b/src/components/Picker/props.ts
@@ -1,56 +1,62 @@
-import { MPickerViewProps } from '../PickerView/props'
+import { createProps, RequiredProp } from '../component'
+import { MPickerViewDefaultProps } from '../PickerView/props'
import { noop, omit } from 'vtils'
-import { RequiredProp } from '../component'
-
-export const MPickerProps = {
- ...omit(MPickerViewProps, ['onChange', 'onPickEnd', 'onPickStart']),
-
- /**
- * 是否可点击遮罩关闭。
- *
- * @default true
- */
- maskClosable: true as boolean,
-
- /**
- * 标题。
- *
- * @default ''
- */
- title: '' as string,
-
- /**
- * 是否无取消按钮。
- *
- * @default false
- */
- noCancel: false as boolean,
-
- /**
- * 取消文字。
- *
- * @default '取消'
- */
- cancelText: '取消' as string,
-
- /**
- * 确定文字。
- *
- * @default '确定'
- */
- confirmText: '确定' as string,
-
- /**
- * 点击取消事件。
- *
- * @default () => {}
- */
- onCancel: noop as () => void,
-
- /**
- * 点击确定事件。
- *
- * @default () => {}
- */
- onConfirm: noop as any as RequiredProp<(selectedIndexes: number[]) => void>,
+
+export const MPickerDefaultProps = {
+ ...omit(
+ MPickerViewDefaultProps,
+ ['onChange', 'onPickEnd', 'onPickStart'],
+ ),
+ ...createProps({
+ /**
+ * 是否可点击遮罩关闭。
+ *
+ * @default true
+ */
+ maskClosable: true as boolean,
+
+ /**
+ * 标题。
+ *
+ * @default ''
+ */
+ title: '' as string,
+
+ /**
+ * 是否无取消按钮。
+ *
+ * @default false
+ */
+ noCancel: false as boolean,
+
+ /**
+ * 取消文字。
+ *
+ * @default '取消'
+ */
+ cancelText: '取消' as string,
+
+ /**
+ * 确定文字。
+ *
+ * @default '确定'
+ */
+ confirmText: '确定' as string,
+
+ /**
+ * 点击取消事件。
+ *
+ * @default () => {}
+ */
+ onCancel: noop as () => void,
+
+ /**
+ * 点击确定事件。
+ *
+ * @default () => {}
+ */
+ onConfirm: noop as any as RequiredProp<(selectedIndexes: number[]) => void>,
+ }),
}
+
+export type MPickerProps = typeof MPickerDefaultProps
diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts
new file mode 100644
index 0000000..3be2f0c
--- /dev/null
+++ b/src/components/Picker/types.ts
@@ -0,0 +1,28 @@
+import { MPickerViewCascadedData, MPickerViewCascadedItem, MPickerViewCascadedList, MPickerViewData, MPickerViewItem, MPickerViewList, MPickerViewNormalData, MPickerViewNormalItem, MPickerViewNormalList } from '../PickerView/types'
+
+/** 普通条目 */
+export type MPickerNormalItem = MPickerViewNormalItem
+
+/** 普通列表 */
+export type MPickerNormalList = MPickerViewNormalList
+
+/** 普通数据 */
+export type MPickerNormalData = MPickerViewNormalData
+
+/** 级联条目 */
+export type MPickerCascadedItem = MPickerViewCascadedItem
+
+/** 级联列表 */
+export type MPickerCascadedList = MPickerViewCascadedList
+
+/** 级联数据 */
+export type MPickerCascadedData = MPickerViewCascadedData
+
+/** 条目 */
+export type MPickerItem = MPickerViewItem
+
+/** 列表 */
+export type MPickerList = MPickerViewList
+
+/** 数据 */
+export type MPickerData = MPickerViewData
diff --git a/src/components/SinglePicker/index.tsx b/src/components/SinglePicker/index.tsx
index 02242ca..d0698f6 100644
--- a/src/components/SinglePicker/index.tsx
+++ b/src/components/SinglePicker/index.tsx
@@ -1,56 +1,45 @@
import MPicker from '../Picker'
-import Taro from '@tarojs/taro'
-import { component } from '../component'
-import { Data, Item, MSinglePickerProps } from './props'
-import { NormalData } from '../PickerView'
-
-export { Item, Data }
-
-/**
- * 单项选择器组件。
- */
-class MSinglePicker extends component({
- props: MSinglePickerProps,
- state: {
- localData: [] as NormalData,
- localSelectedIndexes: [] as number[],
- },
-}) {
- componentWillMount() {
- const { data, selectedIndex } = this.props
- this.setState({
- localData: [data],
- localSelectedIndexes: [selectedIndex],
- })
- }
+import Taro, { useEffect, useState } from '@tarojs/taro'
+import { functionalComponent } from '../component'
+import { MPickerData } from '../Picker/types'
+import { MPickerProps } from '../Picker/props'
+import { MSinglePickerDefaultProps, MSinglePickerProps } from './props'
- componentWillReceiveProps({ data, selectedIndex }: MSinglePicker['props']) {
- this.setState({
- localData: [data],
- localSelectedIndexes: [selectedIndex],
- })
- }
+function MSinglePicker(props: MSinglePickerProps) {
+ const [data, setData] = useState([])
+ const [selectedIndexes, setSelectedIndexes] = useState([])
- handleConfirm: MPicker['props']['onConfirm'] = selectedIndexes => {
- this.props.onConfirm(selectedIndexes[0])
- }
+ useEffect(
+ () => {
+ setData([props.data])
+ },
+ [props.data],
+ )
+
+ useEffect(
+ () => {
+ setSelectedIndexes([props.selectedIndex])
+ },
+ [props.selectedIndex],
+ )
- render() {
- const {
- localData,
- localSelectedIndexes,
- } = this.state
-
- return (
-
- {this.props.children}
-
- )
+ const handleConfirm: MPickerProps['onConfirm'] = selectedIndexes => {
+ props.onConfirm(selectedIndexes[0])
}
+
+ return (
+
+ {props.children}
+
+ )
}
-export default MSinglePicker
+export * from './types'
+
+export { MSinglePickerProps }
+
+export default functionalComponent(MSinglePickerDefaultProps)(MSinglePicker)
diff --git a/src/components/SinglePicker/props.ts b/src/components/SinglePicker/props.ts
index bd54b41..3eeae28 100644
--- a/src/components/SinglePicker/props.ts
+++ b/src/components/SinglePicker/props.ts
@@ -1,28 +1,32 @@
-import { MPickerProps } from '../Picker/props'
+import { createProps, RequiredProp } from '../component'
+import { MPickerDefaultProps } from '../Picker/props'
+import { MSinglePickerData } from './types'
import { noop, omit } from 'vtils'
-import { NormalItem } from '../PickerView'
-import { RequiredProp } from '../component'
-export type Item = NormalItem
-export type Data = Item[]
+export const MSinglePickerDefaultProps = {
+ ...omit(
+ MPickerDefaultProps,
+ ['data', 'selectedIndexes', 'onConfirm'],
+ ),
-export const MSinglePickerProps = {
- ...omit(MPickerProps, ['data', 'selectedIndexes', 'onConfirm']),
+ ...createProps({
+ /**
+ * 选项数据。
+ */
+ data: [] as any as RequiredProp,
- /**
- * 选项数据。
- */
- data: [] as any as RequiredProp,
+ /**
+ * 选中条目的索引。
+ */
+ selectedIndex: 0 as any as RequiredProp,
- /**
- * 选中条目的索引。
- */
- selectedIndex: 0 as any as RequiredProp,
-
- /**
- * 点击确定事件。
- *
- * @default () => {}
- */
- onConfirm: noop as any as RequiredProp<(selectedIndex: number) => void>,
+ /**
+ * 点击确定事件。
+ *
+ * @default () => {}
+ */
+ onConfirm: noop as any as RequiredProp<(selectedIndex: number) => void>,
+ }),
}
+
+export type MSinglePickerProps = typeof MSinglePickerDefaultProps
diff --git a/src/components/SinglePicker/types.ts b/src/components/SinglePicker/types.ts
new file mode 100644
index 0000000..cfcd843
--- /dev/null
+++ b/src/components/SinglePicker/types.ts
@@ -0,0 +1,7 @@
+import { MPickerNormalItem } from '../Picker/types'
+
+/** 条目 */
+export type MSinglePickerItem = MPickerNormalItem
+
+/** 数据 */
+export type MSinglePickerData = MPickerNormalItem[]
diff --git a/src/components/TimePicker/index.tsx b/src/components/TimePicker/index.tsx
index 947c6fd..57a445f 100644
--- a/src/components/TimePicker/index.tsx
+++ b/src/components/TimePicker/index.tsx
@@ -1,142 +1,109 @@
-import MPicker from '../Picker'
-import Taro from '@tarojs/taro'
-import { CascadedData } from '../PickerView'
-import { component } from '../component'
-import { MTimePickerProps } from './props'
+import MPicker, { MPickerCascadedData, MPickerProps } from '../Picker'
+import Taro, { useEffect, useState } from '@tarojs/taro'
+import { functionalComponent } from '../component'
+import { MTimePickerDefaultProps, MTimePickerProps } from './props'
-/**
- * 时间选择器组件。
- *
- * @example
- *
- * ```jsx
- * this.setState({ selectedTime })}>
- * 选择时间
- *
- * ```
- */
-class MTimePicker extends component({
- props: MTimePickerProps,
- state: {
- localData: [] as CascadedData,
- localSelectedIndexes: [] as number[],
- },
-}) {
- lastUpdateAt: number = 0
+function MTimePicker(props: MTimePickerProps) {
+ const [data, setData] = useState([])
+ const [selectedIndexes, setSelectedIndexes] = useState([])
- lastSelectedIndexes: number[] = []
+ useEffect(
+ () => {
+ let reject: boolean | void = false
- componentDidMount() {
- this.updateLocalState(this.props)
- }
-
- componentWillReceiveProps(nextProps: MTimePicker['props']) {
- // perf: 极短时间内的行为应是由子组件触发的父组件更新
- if (this.lastUpdateAt && (Date.now() - this.lastUpdateAt < 60)) {
- this.setState({
- localSelectedIndexes: this.lastSelectedIndexes,
- })
- } else {
- this.updateLocalState(nextProps)
- }
- }
+ const startTime = props.startTime.split(':')
+ const startHour = parseInt(startTime[0]) || 0
+ const startMinute = parseInt(startTime[1]) || 0
- updateLocalState(props: MTimePicker['props']) {
- let reject: boolean | void = false
+ const endTime = props.endTime.split(':')
+ const endHour = parseInt(endTime[0]) || 23
+ const endMinute = parseInt(endTime[1]) || 59
- const startTime = props.startTime.split(':')
- const startHour = parseInt(startTime[0]) || 0
- const startMinute = parseInt(startTime[1]) || 0
+ const useRawHourValue = props.formatHour == null
+ const useRawMinuteValue = props.formatMinute == null
- const endTime = props.endTime.split(':')
- const endHour = parseInt(endTime[0]) || 23
- const endMinute = parseInt(endTime[1]) || 59
-
- const useRawHourValue = props.formatHour == null
- const useRawMinuteValue = props.formatMinute == null
-
- const hourList: CascadedData = []
- const selectedIndexes: number[] = []
- for (let hour = startHour; hour <= endHour; hour++) {
- reject = props.filterHour && props.filterHour({
- hour: hour,
- })
- if (reject !== true) {
- if (hour === props.selectedTime[0]) {
- selectedIndexes[0] = hourList.length
- }
- const minuteList: CascadedData = []
- hourList.push({
- label: String(useRawHourValue ? hour.toString() : props.formatHour({ hour })),
- value: hour,
- children: minuteList,
+ const hourList: MPickerCascadedData = []
+ const selectedIndexes: number[] = []
+ for (let hour = startHour; hour <= endHour; hour++) {
+ reject = props.filterHour && props.filterHour({
+ hour: hour,
})
- const minutes = hour === endHour ? endMinute : 59
- for (let minute = (hour === startHour ? startMinute : 0); minute <= minutes; minute++) {
- reject = props.filterMinute && props.filterMinute({
- hour: hour,
- minute: minute,
+ if (reject !== true) {
+ if (hour === props.selectedTime[0]) {
+ selectedIndexes[0] = hourList.length
+ }
+ const minuteList: MPickerCascadedData = []
+ hourList.push({
+ label: String(useRawHourValue ? hour.toString() : props.formatHour({ hour })),
+ value: hour,
+ children: minuteList,
})
- if (reject !== true) {
- if (minute === props.selectedTime[1]) {
- selectedIndexes[1] = minuteList.length
- }
- minuteList.push({
- label: String(useRawMinuteValue ? minute.toString() : props.formatMinute({ hour, minute })),
- value: minute,
+ const minutes = hour === endHour ? endMinute : 59
+ for (let minute = (hour === startHour ? startMinute : 0); minute <= minutes; minute++) {
+ reject = props.filterMinute && props.filterMinute({
+ hour: hour,
+ minute: minute,
})
- } else {
- reject = false
+ if (reject !== true) {
+ if (minute === props.selectedTime[1]) {
+ selectedIndexes[1] = minuteList.length
+ }
+ minuteList.push({
+ label: String(useRawMinuteValue ? minute.toString() : props.formatMinute({ hour, minute })),
+ value: minute,
+ })
+ } else {
+ reject = false
+ }
}
+ selectedIndexes[1] = selectedIndexes[1] == null ? 0 : selectedIndexes[1]
+ } else {
+ reject = false
}
- selectedIndexes[1] = selectedIndexes[1] == null ? 0 : selectedIndexes[1]
- } else {
- reject = false
}
- }
- selectedIndexes[0] = selectedIndexes[0] == null ? 0 : selectedIndexes[0]
+ selectedIndexes[0] = selectedIndexes[0] == null ? 0 : selectedIndexes[0]
- this.setState({
- localData: hourList,
- localSelectedIndexes: selectedIndexes,
- })
- }
+ setData(hourList)
+ setSelectedIndexes(selectedIndexes)
+ },
+ [
+ props.startTime,
+ props.endTime,
+ props.filterHour,
+ props.filterMinute,
+ props.formatHour,
+ props.formatMinute,
+ ],
+ )
- handleConfirm: MPicker['props']['onConfirm'] = selectedIndexes => {
- const { localData } = this.state
+ const handleConfirm: MPickerProps['onConfirm'] = selectedIndexes => {
const selectedDate: number[] = []
- let list = localData
- const n = Math.min(selectedIndexes.length, 2)
- for (let i = 0; i < n; i++) {
+ for (
+ let i = 0,
+ n = Math.min(selectedIndexes.length, 2),
+ list = data;
+ i < n;
+ i++
+ ) {
if (!list[selectedIndexes[i]]) break
selectedDate.push(list[selectedIndexes[i]].value)
list = list[selectedIndexes[i]].children
if (!list) break
}
- this.lastUpdateAt = Date.now()
- this.lastSelectedIndexes = selectedIndexes
- this.props.onConfirm(selectedDate)
+ props.onConfirm(selectedDate)
}
- render() {
- const {
- localData,
- localSelectedIndexes,
- } = this.state
-
- return !localData.length ? this.props.children : (
-
- {this.props.children}
-
- )
- }
+ return !data.length ? props.children : (
+
+ {props.children}
+
+ )
}
-export default MTimePicker
+export { MTimePickerProps }
+
+export default functionalComponent(MTimePickerDefaultProps)(MTimePicker)
diff --git a/src/components/TimePicker/props.ts b/src/components/TimePicker/props.ts
index 47991ac..37f80b1 100644
--- a/src/components/TimePicker/props.ts
+++ b/src/components/TimePicker/props.ts
@@ -1,78 +1,85 @@
-import { MPickerProps } from '../Picker/props'
+import { createProps, RequiredProp } from '../component'
+import { MPickerDefaultProps } from '../Picker/props'
import { noop, omit } from 'vtils'
-import { RequiredProp } from '../component'
-export const MTimePickerProps = {
- ...omit(MPickerProps, ['data', 'selectedIndexes', 'onConfirm']),
+export const MTimePickerDefaultProps = {
+ ...omit(
+ MPickerDefaultProps,
+ ['data', 'selectedIndexes', 'onConfirm'],
+ ),
- /**
- * 开始时间。
- *
- * @default '00:00'
- */
- startTime: '00:00' as string,
+ ...createProps({
+ /**
+ * 开始时间。
+ *
+ * @default '00:00'
+ */
+ startTime: '00:00' as string,
- /**
- * 结束时间。
- *
- * @default '23:59'
- */
- endTime: `23:59` as string,
+ /**
+ * 结束时间。
+ *
+ * @default '23:59'
+ */
+ endTime: `23:59` as string,
- /**
- * 格式化小时。
- *
- * @default params => params.hour
- */
- formatHour: undefined as (params: {
- /** 时 */
- hour: number,
- }) => string | number,
+ /**
+ * 格式化小时。
+ *
+ * @default params => params.hour
+ */
+ formatHour: undefined as (params: {
+ /** 时 */
+ hour: number,
+ }) => string | number,
- /**
- * 格式化分钟。
- *
- * @default params => params.minute
- */
- formatMinute: undefined as (params: {
- /** 时 */
- hour: number,
- /** 分 */
- minute: number,
- }) => string | number,
+ /**
+ * 格式化分钟。
+ *
+ * @default params => params.minute
+ */
+ formatMinute: undefined as (params: {
+ /** 时 */
+ hour: number,
+ /** 分 */
+ minute: number,
+ }) => string | number,
- /**
- * 小时过滤器,返回 `true` 可过滤掉传入的小时。
- */
- filterHour: undefined as (params: {
- /** 时 */
- hour: number,
- }) => boolean | void,
+ /**
+ * 小时过滤器,返回 `true` 可过滤掉传入的小时。
+ */
+ filterHour: undefined as (params: {
+ /** 时 */
+ hour: number,
+ }) => boolean | void,
- /**
- * 分钟过滤器,返回 `true` 可过滤掉传入的分钟。
- */
- filterMinute: undefined as (params: {
- /** 时 */
- hour: number,
- /** 分 */
- minute: number,
- }) => boolean | void,
+ /**
+ * 分钟过滤器,返回 `true` 可过滤掉传入的分钟。
+ */
+ filterMinute: undefined as (params: {
+ /** 时 */
+ hour: number,
+ /** 分 */
+ minute: number,
+ }) => boolean | void,
- /**
- * 选中的时间。
- *
- * @example
- *
- * [20, 5] // => 20 时 5 分
- * [0, 20] // => 0 时 20 分
- */
- selectedTime: [] as any as RequiredProp,
+ /**
+ * 选中的时间。
+ *
+ * @example
+ *
+ * [20, 5] // => 20 时 5 分
+ * [0, 20] // => 0 时 20 分
+ */
+ selectedTime: [] as any as RequiredProp,
- /**
- * 点击确定事件。
- *
- * @default () => {}
- */
- onConfirm: noop as any as RequiredProp<(selectedTime: number[]) => void>,
+ /**
+ * 点击确定事件。
+ *
+ * @default () => {}
+ */
+ onConfirm: noop as any as RequiredProp<(selectedTime: number[]) => void>,
+ }),
}
+
+export type MTimePickerProps = typeof MTimePickerDefaultProps