diff --git a/compiled/alipay/demo/pages/Calendar/collapse-container/cn-day/cn-day.js b/compiled/alipay/demo/pages/Calendar/collapse-container/cn-day/cn-day.js index a68de7a51..b5675af23 100644 --- a/compiled/alipay/demo/pages/Calendar/collapse-container/cn-day/cn-day.js +++ b/compiled/alipay/demo/pages/Calendar/collapse-container/cn-day/cn-day.js @@ -1,21 +1,38 @@ import dayjs from 'dayjs'; +import equal from 'fast-deep-equal'; import Converter from './js-calendar-converter'; -import { mountComponent } from '../../../../../src/_util/component'; -const CollapseContainer = (props) => { - var _a, _b, _c; - const time = dayjs((_a = props.cell) === null || _a === void 0 ? void 0 : _a.time); - const vs = Converter.solar2lunar(time.get('year'), time.get('month') + 1, time.get('date')); - if (vs === -1) { - return { - cnday: '', - }; - } - return { - cnday: vs.lunarFestival || vs.festival || vs.IDayCn, - festival: !!vs.festival || !!vs.lunarFestival, - unset: ((_b = props.cell) === null || _b === void 0 ? void 0 : _b.isSelectedBegin) || ((_c = props.cell) === null || _c === void 0 ? void 0 : _c.isSelectedEnd), - }; -}; -mountComponent(CollapseContainer, { +import { Component, getValueFromProps } from '../../../../../src/_util/simply'; +Component({ cell: null, +}, { + updateData() { + const cell = getValueFromProps(this, 'cell'); + const time = dayjs(cell === null || cell === void 0 ? void 0 : cell.time); + const vs = Converter.solar2lunar(time.get('year'), time.get('month') + 1, time.get('date')); + if (vs === -1) { + this.setData({ + cnday: '', + }); + return; + } + this.setData({ + cnday: vs.lunarFestival || vs.festival || vs.IDayCn, + festival: !!vs.festival || !!vs.lunarFestival, + unset: (cell === null || cell === void 0 ? void 0 : cell.isSelectedBegin) || (cell === null || cell === void 0 ? void 0 : cell.isSelectedEnd), + }); + }, +}, { + cnday: '', + festival: '', + unset: '', +}, null, { + onInit() { + this.updateData(); + }, + didUpdate(prevProps) { + const cell = getValueFromProps(this, 'cell'); + if (!equal(prevProps.cell, cell)) { + this.updateData(); + } + }, }); diff --git a/compiled/alipay/demo/pages/Calendar/collapse-container/collapse-container.js b/compiled/alipay/demo/pages/Calendar/collapse-container/collapse-container.js index eb2ee114b..c430dba14 100644 --- a/compiled/alipay/demo/pages/Calendar/collapse-container/collapse-container.js +++ b/compiled/alipay/demo/pages/Calendar/collapse-container/collapse-container.js @@ -1,19 +1,30 @@ -import { useEvent, useState } from 'functional-mini/component'; -import { mountComponent } from '../../../../src/_util/component'; -const CollapseContainer = (props) => { - var _a; - const [collapse, setCollapse] = useState((_a = props.defaultCollapse) !== null && _a !== void 0 ? _a : true); - useEvent('handleToggle', () => { - setCollapse((v) => !v); - }); - return { - collapse, - internalHide: props.hide, - containerTitle: props.title, - }; -}; -mountComponent(CollapseContainer, { +import { Component, getValueFromProps } from '../../../../src/_util/simply'; +Component({ hide: false, defaultCollapse: null, title: '', +}, { + handleToggle() { + const { collapse } = this.data; + this.setData({ + collapse: !collapse, + }); + }, +}, { + collapse: true, + internalHide: false, + containerTitle: '', +}, null, { + onInit() { + const [defaultCollapse, hide, title] = getValueFromProps(this, [ + 'defaultCollapse', + 'hide', + 'title', + ]); + this.setData({ + collapse: defaultCollapse !== null && defaultCollapse !== void 0 ? defaultCollapse : true, + internalHide: hide, + containerTitle: title, + }); + }, }); diff --git a/compiled/alipay/src/Calendar/index.ts b/compiled/alipay/src/Calendar/index.ts index 5e12a4af9..738161432 100644 --- a/compiled/alipay/src/Calendar/index.ts +++ b/compiled/alipay/src/Calendar/index.ts @@ -1,244 +1,223 @@ import dayjs from 'dayjs'; -import { - useComponent, - useEvent, - useReady, - useState, - useEffect, -} from 'functional-mini/component'; -import { mountComponent } from '../_util/component'; -import { useComponentEvent } from '../_util/hooks/useComponentEvent'; -import { triggerRefEvent } from '../_util/hooks/useReportRef'; -import { hasValue, useMergedState } from '../_util/hooks/useMergedState'; +import equal from 'fast-deep-equal'; +import { Component, triggerEvent, getValueFromProps } from '../_util/simply'; import { CalendarValue, CellState, defaultLocaleText, - ICalendarProps, + CalendarDefaultProps, } from './props'; import { - defaultMonthRange, getMonthListFromRange, getSelectionModeFromValue, renderCells, getScrollIntoViewId, } from './utils'; - -function getBoundingClientRect(instance: any, selector: string) { - return new Promise((resolve, reject) => { - instance - .createSelectorQuery() - .select(selector) - .boundingClientRect() - .exec((ret) => { - if (ret && ret[0]) { - resolve(ret[0]); - } else { - reject(); - } - }); - }); -} - -const Calendar = (props: ICalendarProps) => { - const localeText = Object.assign({}, defaultLocaleText, props.localeText); - - const markItems = [...localeText.weekdayNames]; - const weekStartsOn = props.weekStartsOn; - if (weekStartsOn === 'Sunday') { - const item = markItems.pop(); - if (item) markItems.unshift(item); - } - - const [value, setValue] = useMergedState(props.defaultValue, { - value: props.value, - }); - - const [scrollIntoViewId, setScrollIntoViewId] = useState(''); - - useEvent('scrollIntoView', (value) => { - updateScrollIntoViewId(getScrollIntoViewId(value)); - }); - - triggerRefEvent(); - - // scroll 触发滚动之后需要重置 scrollIntoViewId - function updateScrollIntoViewId(id) { - setScrollIntoViewId(id); - - const timer = setTimeout(() => { - setScrollIntoViewId(''); - clearTimeout(timer); - }); - } - - const selectionModeFromValue = getSelectionModeFromValue(value); - const selectionMode = - props.selectionMode ?? selectionModeFromValue ?? 'range'; - - const { triggerEvent } = useComponentEvent(props); - function updateValue(newValue: CalendarValue) { - const isControl = hasValue(props.value); - triggerEvent('change', newValue); - if (!isControl) { - setValue(newValue); - } - } - - useEvent('clickCell', (e) => { - const time = e.currentTarget.dataset.time; - const clickDate = dayjs(time.time); - if (time.disabled) { - return; - } - if (selectionMode === 'range') { - if (Array.isArray(value)) { - if (value.length === 1) { - const current = value[0]; - if (dayjs(clickDate.toDate().getTime()).isBefore(dayjs(current))) { - updateValue([clickDate.toDate().getTime()]); +import mixinValue from '../mixins/value'; +import { getInstanceBoundingClientRect } from '../_util/jsapi/get-instance-bounding-client-rect'; + +Component( + CalendarDefaultProps, + { + getInstance() { + if (this.$id) { + return my; + } + return this; + }, + async getBoundingClientRect(query: string) { + return await getInstanceBoundingClientRect(this.getInstance(), query); + }, + scrollIntoView(value) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); + }, + clickCell(e) { + const time = e.currentTarget.dataset.time; + const clickDate = dayjs(time.time); + if (time.disabled) { + return; + } + const value = this.getValue(); + const selectionModeFromValue = getSelectionModeFromValue(value); + const selectionMode = + getValueFromProps(this, 'selectionMode') ?? + selectionModeFromValue ?? + 'range'; + if (selectionMode === 'range') { + if (Array.isArray(value)) { + if (value.length === 1) { + const current = value[0]; + if (dayjs(clickDate.toDate().getTime()).isBefore(dayjs(current))) { + this.updateValue([clickDate.toDate().getTime()]); + } else { + this.updateValue([value[0], clickDate.toDate().getTime()]); + } } else { - updateValue([value[0], clickDate.toDate().getTime()]); + this.updateValue([clickDate.toDate().getTime()]); } } else { - updateValue([clickDate.toDate().getTime()]); + this.updateValue([clickDate.toDate().getTime()]); } - } else { - updateValue([clickDate.toDate().getTime()]); + } else if (selectionMode === 'single') { + this.updateValue(clickDate.toDate().getTime()); } - } else if (selectionMode === 'single') { - updateValue(clickDate.toDate().getTime()); - } - }); + }, + setCurrentMonth(e) { + this.setData({ headerState: e.month }); + }, + measurement() { + const { elementSize } = this.data; + // 组件如果内嵌在 slot 里, 一定会被渲染出来, 但是此时 cellHight 为 0 + // 此时需要重新计算 + if (!elementSize || elementSize.cellHight === 0) { + this.measurementFn(); + } + }, + measurementFn() { + Promise.all([ + this.getBoundingClientRect('.ant-calendar-body-container'), + this.getBoundingClientRect('.ant-calendar-cells'), + this.getBoundingClientRect('.ant-calendar-title-container'), + ]) + .then(([bodyContainer, cellContainer, Title]) => { + // 滚动的时候 top 和 bottom 等尺寸会变 + // 所以只能依赖 height 来计算 + const paddingHeight = + bodyContainer.height - cellContainer.height - Title.height; + const monthTitleHeight = Title.height + paddingHeight; + const cellHight = + cellContainer.height / (this.data.monthList[0].cells.length / 7); + this.setData({ + elementSize: { + monthTitleHeight, + cellHight, + paddingHeight, + }, + }); + }) + .catch(() => { + this.setData({ elementSize: null }); + }); + }, + + // scroll 触发滚动之后需要重置 scrollIntoViewId + updateScrollIntoViewId(id) { + this.setData({ scrollIntoViewId: id }); + const timer = setTimeout(() => { + this.setData({ scrollIntoViewId: '' }); + clearTimeout(timer); + }); + }, - const monthList = getMonthListFromRange( - dayjs(props.monthRange[0]), - dayjs(props.monthRange[1]) - ).map((p) => { - let cells = renderCells(p, weekStartsOn, value, localeText); - if (props.onFormatter && typeof props.onFormatter === 'function') { - cells = cells.map((o): CellState => { - const { - time, - top, - bottom, - disabled, - isSelectedBegin, - isSelectedEnd, - isSelected, - } = o; - const newState = - props.onFormatter( - { + updateValue(newValue: CalendarValue) { + triggerEvent(this, 'change', newValue); + if (!this.isControlled()) { + this.update(newValue); + } + }, + updateData() { + const [monthRange, plocaleText, pweekStartsOn, onFormatter] = + getValueFromProps(this, [ + 'monthRange', + 'localeText', + 'weekStartsOn', + 'onFormatter', + ]); + const localeText = Object.assign({}, defaultLocaleText, plocaleText); + const markItems = [...localeText.weekdayNames]; + const weekStartsOn = pweekStartsOn; + if (weekStartsOn === 'Sunday') { + const item = markItems.pop(); + if (item) markItems.unshift(item); + } + const value = this.getValue(); + const monthList = getMonthListFromRange( + dayjs(monthRange?.[0]), + dayjs(monthRange?.[1]) + ).map((p) => { + let cells = renderCells(p, weekStartsOn, value, localeText); + if (onFormatter && typeof onFormatter === 'function') { + cells = cells.map((o): CellState => { + const { time, - top: top ? { ...top } : undefined, - bottom: bottom ? { ...bottom } : undefined, + top, + bottom, disabled, isSelectedBegin, isSelectedEnd, isSelected, - }, - value - ) ?? {}; - const result = { ...o }; - if (typeof newState === 'object') { - // 只允许修改三个字段 - ['top', 'bottom', 'disabled'].forEach((key) => { - if (key in newState) { - result[key] = newState[key]; + } = o; + const newState = + onFormatter( + { + time, + top: top ? { ...top } : undefined, + bottom: bottom ? { ...bottom } : undefined, + disabled, + isSelectedBegin, + isSelectedEnd, + isSelected, + }, + value + ) ?? {}; + const result = { ...o }; + if (typeof newState === 'object') { + // 只允许修改三个字段 + ['top', 'bottom', 'disabled'].forEach((key) => { + if (key in newState) { + result[key] = newState[key]; + } + }); } + return result; }); } - return result; + return { + title: p.format(localeText.title), + cells, + }; }); - } - return { - title: p.format(localeText.title), - cells, - }; - }); - - const [headerState, setHeaderState] = useState(0); - - useEvent('setCurrentMonth', (e) => { - setHeaderState(e.month); - }); - - const [elementSize, setElementSize] = useState<{ - monthTitleHeight: number; - cellHight: number; - paddingHeight: number; - }>(null); - - const componentInstance = useComponent(); - function measurement() { - Promise.all([ - getBoundingClientRect(componentInstance, '.ant-calendar-body-container'), - getBoundingClientRect(componentInstance, '.ant-calendar-cells'), - getBoundingClientRect(componentInstance, '.ant-calendar-title-container'), - ]) - .then(([bodyContainer, cellContainer, Title]) => { - // 滚动的时候 top 和 bottom 等尺寸会变 - // 所以只能依赖 height 来计算 - const paddingHeight = - bodyContainer.height - cellContainer.height - Title.height; - const monthTitleHeight = Title.height + paddingHeight; - const cellHight = - cellContainer.height / (monthList[0].cells.length / 7); - setElementSize({ - monthTitleHeight, - cellHight, - paddingHeight, - }); - }) - .catch(() => { - setElementSize(null); - }); + this.setData({ markItems, monthList }); + }, + }, + { + elementSize: null, + markItems: [], + monthList: [], + headerState: 0, + scrollIntoViewId: '', + }, + [mixinValue()], + { + didMount() { + this.updateData(); + this.measurementFn(); + // 初始化默认值时,滚动到选中位置 + const [value, defaultValue] = getValueFromProps(this, [ + 'value', + 'defaultValue', + ]); + if (this.isControlled()) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); + } else { + defaultValue && + this.updateScrollIntoViewId(getScrollIntoViewId(defaultValue)); + } + }, + didUpdate(prevProps, prevData) { + if (!this.isEqualValue(prevData)) { + // 滚动到已选的位置 + const changedScrollIntoView = getValueFromProps( + this, + 'changedScrollIntoView' + ); + changedScrollIntoView && + this.updateScrollIntoViewId(getScrollIntoViewId(this.getValue())); + } + if (!equal(prevProps, this.props) || !this.isEqualValue(prevData)) { + this.updateData(); + } + }, } - - useEffect(() => { - // 滚动到已选的位置 - props.changedScrollIntoView && - updateScrollIntoViewId(getScrollIntoViewId(value)); - }, [value]); - - useReady(() => { - measurement(); - // 初始化默认值时,滚动到选中位置 - const isControl = hasValue(props.value); - if (isControl) { - updateScrollIntoViewId(getScrollIntoViewId(props.value)); - } else { - props.defaultValue && - updateScrollIntoViewId(getScrollIntoViewId(props.defaultValue)); - } - }, []); - - useEvent('measurement', () => { - // 组件如果内嵌在 slot 里, 一定会被渲染出来, 但是此时 cellHight 为 0 - // 此时需要重新计算 - if (!elementSize || elementSize.cellHight === 0) { - measurement(); - } - }); - - return { - elementSize, - markItems, - monthList, - headerState, - scrollIntoViewId, - }; -}; - -mountComponent(Calendar, { - defaultValue: null, - value: null, - selectionMode: 'range', - monthRange: defaultMonthRange(), - weekStartsOn: 'Sunday', - localeText: defaultLocaleText, - onFormatter: null, - changedScrollIntoView: null, -}); +); diff --git a/compiled/alipay/src/Calendar/props.ts b/compiled/alipay/src/Calendar/props.ts index 54891430c..e06386972 100644 --- a/compiled/alipay/src/Calendar/props.ts +++ b/compiled/alipay/src/Calendar/props.ts @@ -1,4 +1,5 @@ import { IBaseProps } from '../_util/base'; +import { defaultMonthRange } from './utils'; export interface CalendarDate { year: number; @@ -142,3 +143,14 @@ export interface ICalendarProps extends IBaseProps { currentValue: CalendarValue ) => Pick; } + +export const CalendarDefaultProps = { + defaultValue: null, + value: null, + selectionMode: 'range', + monthRange: defaultMonthRange(), + weekStartsOn: 'Sunday', + localeText: defaultLocaleText, + onFormatter: null, + changedScrollIntoView: null, +}; diff --git a/compiled/alipay/src/_util/component.ts b/compiled/alipay/src/_util/component.ts deleted file mode 100644 index 05c423a77..000000000 --- a/compiled/alipay/src/_util/component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { alipayComponent, wechatComponent } from 'functional-mini/component'; - -function removeNullProps(props) { - const newProps = {}; - for (const key in props) { - if (props[key] !== null) { - newProps[key] = props[key]; - } - } - return newProps; -} - -export function mountComponent( - Hooks: (props: T) => unknown, - defaultProps: T -) { - - Component( - alipayComponent(Hooks, removeNullProps(mergeDefaultProps(defaultProps))) - ); -} - -function mergeDefaultProps(defaultProps: Record = {}) { - return { - ...defaultProps, - }; -} diff --git a/compiled/alipay/src/_util/hooks/useComponentEvent.ts b/compiled/alipay/src/_util/hooks/useComponentEvent.ts deleted file mode 100644 index 1e2d7d31b..000000000 --- a/compiled/alipay/src/_util/hooks/useComponentEvent.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { useComponent } from 'functional-mini/component'; -import fmtEvent from '../fmtEvent'; -import { useEvent } from './useEvent'; - -export function useComponentEvent(props: T) { - const component = useComponent(); - const triggerEvent = useEvent( - (eventName: string, value: unknown, e?: any) => { - // 首字母大写,然后加上 on - - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](value, fmtEvent(props, e)); - } - - } - ); - - const triggerEventValues = useEvent( - (eventName: string, values: any[], e?: any) => { - // 首字母大写,然后加上 on - - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](...values, fmtEvent(props, e)); - } - - } - ); - const triggerEventOnly = useEvent((eventName: string, e?: any) => { - // 首字母大写,然后加上 on - - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](fmtEvent(props, e)); - } - - }); - - const alipayForwardCatchEvent = useEvent((eventName: string, e: any) => { - // 首字母大写,然后加上 catch - - const alipayCallbackName = - 'catch' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](fmtEvent(props, e)); - } - }); - - const alipayForwardEvent = useEvent((eventName: string, e: any) => { - // 首字母大写,然后加上 on - - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](fmtEvent(props, e)); - } - }); - - return { - triggerEvent, - triggerEventValues, - triggerEventOnly, - // 转发 catch 事件 - alipayForwardCatchEvent, - // 转发事件 - alipayForwardEvent, - }; -} diff --git a/compiled/alipay/src/_util/hooks/useEvent.ts b/compiled/alipay/src/_util/hooks/useEvent.ts deleted file mode 100644 index 665c6e811..000000000 --- a/compiled/alipay/src/_util/hooks/useEvent.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useEvent.ts - */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as React from 'functional-mini/compat'; - -// eslint-disable-next-line @typescript-eslint/ban-types -export function useEvent(callback: T): T { - const fnRef = React.useRef(); - fnRef.current = callback; - - const memoFn = React.useCallback( - ((...args: any) => fnRef.current?.(...args)) as any, - [] - ); - - return memoFn; -} diff --git a/compiled/alipay/src/_util/hooks/useHandleCustomEvent.ts b/compiled/alipay/src/_util/hooks/useHandleCustomEvent.ts deleted file mode 100644 index e66d98d27..000000000 --- a/compiled/alipay/src/_util/hooks/useHandleCustomEvent.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useEvent } from 'functional-mini/component'; -import { platform } from '../platform'; - -export type EventHandler = (value: T, e: any) => void; - -export const useHandleCustomEvent = ( - eventName: string, - handler: EventHandler -) => { - useEvent( - eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (eventOrValue: any, alipayEvent?: any) => { - if (platform() === 'alipay') { - return handler(eventOrValue, alipayEvent); - } - - } - ); -}; - -export type MultipleValueEventHandler = (...args: any[]) => void; - -export const useMultipleValueHandleCustomEvent = ( - eventName: string, - handler: MultipleValueEventHandler -) => { - useEvent( - eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (...args: any[]) => { - if (platform() === 'alipay') { - return handler(...args); - } - - } - ); -}; - -export type EventOnlyHandler = (e: any) => void; - -export const useHandleCustomEventOnly = ( - eventName: string, - handler: EventOnlyHandler -) => { - useEvent( - eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (event) => { - if (platform() === 'alipay') { - return handler(event); - } - - } - ); -}; diff --git a/compiled/alipay/src/_util/hooks/useInstanceBoundingClientRect.ts b/compiled/alipay/src/_util/hooks/useInstanceBoundingClientRect.ts deleted file mode 100644 index 467a457bb..000000000 --- a/compiled/alipay/src/_util/hooks/useInstanceBoundingClientRect.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useComponent } from 'functional-mini/component'; -import { getInstanceBoundingClientRect } from '../jsapi/get-instance-bounding-client-rect'; - -export const useInstanceBoundingClientRect = () => { - const instance = useComponent(); - function getInstance() { - if (instance.$id) { - return my; - } - return instance; - } - - async function getBoundingClientRectWithId(prefix: string) { - return await getInstanceBoundingClientRect( - getInstance(), - `${prefix}${instance.$id ? `-${instance.$id}` : ''}` - ); - } - async function getBoundingClientRect(query: string) { - return await getInstanceBoundingClientRect(getInstance(), query); - } - - async function getBoundingClientRectWithBuilder( - builder: (id: string) => string - ) { - return await getInstanceBoundingClientRect( - getInstance(), - builder(instance.$id ? `-${instance.$id}` : '') - ); - } - - return { - getBoundingClientRect, - getBoundingClientRectWithId, - getBoundingClientRectWithBuilder, - }; -}; diff --git a/compiled/alipay/src/_util/hooks/useLayoutEffect.ts b/compiled/alipay/src/_util/hooks/useLayoutEffect.ts deleted file mode 100644 index 896935268..000000000 --- a/compiled/alipay/src/_util/hooks/useLayoutEffect.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useLayoutEffect.ts - */ - -import * as React from 'functional-mini/compat'; - -/** - * Wrap `React.useLayoutEffect` which will not throw warning message in test env - */ -const useInternalLayoutEffect = React.useEffect; - -const useLayoutEffect = (callback: (mount: boolean) => void, deps?: any) => { - const firstMountRef = React.useRef(true); - - useInternalLayoutEffect(() => { - return callback(firstMountRef.current); - }, deps); - - // We tell react that first mount has passed - useInternalLayoutEffect(() => { - firstMountRef.current = false; - return () => { - firstMountRef.current = true; - }; - }, []); -}; - -export const useComponentUpdateEffect: typeof React.useEffect = ( - callback, - deps -) => { - useLayoutEffect((firstMount) => { - if (!firstMount) { - return callback(); - } - }, deps); -}; - -export default useLayoutEffect; diff --git a/compiled/alipay/src/_util/hooks/useMergedState.ts b/compiled/alipay/src/_util/hooks/useMergedState.ts deleted file mode 100644 index 37148ac85..000000000 --- a/compiled/alipay/src/_util/hooks/useMergedState.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useMergedState.ts - */ -import { useEvent } from './useEvent'; -import { useComponentUpdateEffect } from './useLayoutEffect'; -import { useSafeState as useState } from './useState'; -import { supportUndefinedProperty } from '../platform'; - -type Updater = ( - updater: T | ((origin: T) => T), - ignoreDestroy?: boolean -) => void; - -/** We only think `undefined` is empty */ -export function hasValue(value: any) { - if (supportUndefinedProperty()) { - return value !== undefined; - } - return value !== null && value !== undefined; -} - -/** - * Similar to `useState` but will use props value if provided. - * Note that internal use rc-util `useState` hook. - */ -export function useMergedState( - defaultStateValue: T | (() => T), - option?: { - defaultValue?: T | (() => T); - value?: T; - onChange?: (value: T, prevValue: T) => void; - postState?: (value: T) => T; - } -): [R, Updater] { - const { defaultValue, value, onChange, postState } = option || {}; - - // ======================= Init ======================= - const [innerValue, setInnerValue] = useState(() => { - if (hasValue(value)) { - return value; - } else if (hasValue(defaultValue)) { - return typeof defaultValue === 'function' - ? (defaultValue as any)() - : defaultValue; - } else { - return typeof defaultStateValue === 'function' - ? (defaultStateValue as any)() - : defaultStateValue; - } - }); - - const mergedValue = hasValue(value) ? value : innerValue; - const postMergedValue = postState ? postState(mergedValue) : mergedValue; - - // ====================== Change ====================== - const onChangeFn = useEvent(onChange); - - const [prevValue, setPrevValue] = useState<[T]>([mergedValue]); - - useComponentUpdateEffect(() => { - const prev = prevValue[0]; - if (innerValue !== prev) { - onChangeFn(innerValue, prev); - } - }, [prevValue]); - - // Sync value back to `undefined` when it from control to un-control - useComponentUpdateEffect(() => { - if (!hasValue(value)) { - setInnerValue(value); - } - }, [value]); - - // ====================== Update ====================== - const triggerChange: Updater = useEvent((updater, ignoreDestroy) => { - setInnerValue(updater, ignoreDestroy); - setPrevValue([mergedValue], ignoreDestroy); - }); - - return [postMergedValue as unknown as R, triggerChange]; -} diff --git a/compiled/alipay/src/_util/hooks/useMixState.ts b/compiled/alipay/src/_util/hooks/useMixState.ts deleted file mode 100644 index d558a71fd..000000000 --- a/compiled/alipay/src/_util/hooks/useMixState.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { useEvent } from './useEvent'; -import { useComponentUpdateEffect } from './useLayoutEffect'; -import { hasValue } from './useMergedState'; -import { useSafeState as useState } from './useState'; - -type Updater = ( - updater: T | ((old: T) => T), - ignoreDestroy?: boolean -) => void; - -export function useMixState( - defaultStateValue: T | (() => T), - option?: { - defaultValue?: T | (() => T); - value?: T; - postState?: ( - value: T, - option?: O - ) => { valid: true; value: T } | { valid: false }; - } -): [ - R, - { - isControlled: boolean; - triggerUpdater: (value: (old: T) => T, option?: O) => void; - update( - value: T, - option?: O - ): { changed: true; newValue: T } | { changed: false }; - } -] { - const { - defaultValue, - value, - postState = (v) => ({ valid: true, value: v }), - } = option || {}; - - // ======================= Init ======================= - const [innerValue, setInnerValue] = useState(() => { - let v; - if (hasValue(value)) { - v = value; - } else if (hasValue(defaultValue)) { - v = - typeof defaultValue === 'function' - ? (defaultValue as any)() - : defaultValue; - } else { - v = - typeof defaultStateValue === 'function' - ? (defaultStateValue as any)() - : defaultStateValue; - } - const state = postState(v); - if (state.valid) { - return state.value; - } - }); - - const state = postState(value); - const merge = hasValue(value) && state.valid ? state.value : innerValue; - - useComponentUpdateEffect(() => { - const state = postState(value); - if (state.valid) { - setInnerValue(state.value); - } - }, [value]); - - const isControlled = hasValue(value); - const triggerChange: Updater = useEvent((newState, ignoreDestroy) => { - setInnerValue(newState, ignoreDestroy); - }); - - const triggerUpdate = useEvent((value, option) => { - const state = postState(value, option); - if (state.valid && state.value !== innerValue) { - triggerChange(state.value); - return { changed: true, newValue: state.value }; - } - return { changed: false }; - }); - - const triggerUpdater: (value: (old: T) => T, option?: O) => void = useEvent( - (getValue, option) => { - if (isControlled) { - getValue(merge); - } else { - triggerChange((old: T): T => { - const newValue = getValue(old); - const state = postState(newValue, option); - if (state.valid && state.value !== innerValue) { - return state.value; - } - return old; - }); - } - } - ); - - return [ - merge as unknown as R, - { - isControlled, - update: triggerUpdate as any, - triggerUpdater, - }, - ]; -} diff --git a/compiled/alipay/src/_util/hooks/useReportRef.ts b/compiled/alipay/src/_util/hooks/useReportRef.ts deleted file mode 100644 index 553fc85c4..000000000 --- a/compiled/alipay/src/_util/hooks/useReportRef.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { useComponent, useEffect } from 'functional-mini/component'; - -export const triggerRefEvent = () => { -}; diff --git a/compiled/alipay/src/_util/hooks/useState.ts b/compiled/alipay/src/_util/hooks/useState.ts deleted file mode 100644 index 2c80b46b7..000000000 --- a/compiled/alipay/src/_util/hooks/useState.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useState.ts - */ - -import * as React from 'functional-mini/compat'; - -type Updater = T | ((prevValue: T) => T); - -export type SetState = ( - nextValue: Updater, - /** - * Will not update state when destroyed. - * Developer should make sure this is safe to ignore. - */ - ignoreDestroy?: boolean -) => void; - -/** - * Same as React.useState but `setState` accept `ignoreDestroy` param to not to setState after destroyed. - * We do not make this auto is to avoid real memory leak. - * Developer should confirm it's safe to ignore themselves. - */ -export function useSafeState( - defaultValue?: T | (() => T) -): [T, SetState] { - const destroyRef = React.useRef(false); - const [value, setValue] = React.useState(defaultValue); - - React.useEffect(() => { - destroyRef.current = false; - - return () => { - destroyRef.current = true; - }; - }, []); - - function safeSetState(updater: Updater, ignoreDestroy?: boolean) { - if (ignoreDestroy && destroyRef.current) { - return; - } - - setValue(updater); - } - - return [value, safeSetState]; -} diff --git a/compiled/alipay/src/_util/triggerComonentEvent.ts b/compiled/alipay/src/_util/triggerComonentEvent.ts deleted file mode 100644 index c01678048..000000000 --- a/compiled/alipay/src/_util/triggerComonentEvent.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function triggerComponentEvent(instance, propsName, event) { - - if (instance.props[propsName]) { - instance.props[propsName](event); - } -} diff --git a/compiled/wechat/demo/pages/Calendar/collapse-container/cn-day/cn-day.js b/compiled/wechat/demo/pages/Calendar/collapse-container/cn-day/cn-day.js index a68de7a51..3f3e09a87 100644 --- a/compiled/wechat/demo/pages/Calendar/collapse-container/cn-day/cn-day.js +++ b/compiled/wechat/demo/pages/Calendar/collapse-container/cn-day/cn-day.js @@ -1,21 +1,41 @@ import dayjs from 'dayjs'; +import equal from 'fast-deep-equal'; import Converter from './js-calendar-converter'; -import { mountComponent } from '../../../../../src/_util/component'; -const CollapseContainer = (props) => { - var _a, _b, _c; - const time = dayjs((_a = props.cell) === null || _a === void 0 ? void 0 : _a.time); - const vs = Converter.solar2lunar(time.get('year'), time.get('month') + 1, time.get('date')); - if (vs === -1) { - return { - cnday: '', - }; - } - return { - cnday: vs.lunarFestival || vs.festival || vs.IDayCn, - festival: !!vs.festival || !!vs.lunarFestival, - unset: ((_b = props.cell) === null || _b === void 0 ? void 0 : _b.isSelectedBegin) || ((_c = props.cell) === null || _c === void 0 ? void 0 : _c.isSelectedEnd), - }; -}; -mountComponent(CollapseContainer, { +import { Component, getValueFromProps } from '../../../../../src/_util/simply'; +Component({ cell: null, +}, { + updateData() { + const cell = getValueFromProps(this, 'cell'); + const time = dayjs(cell === null || cell === void 0 ? void 0 : cell.time); + const vs = Converter.solar2lunar(time.get('year'), time.get('month') + 1, time.get('date')); + if (vs === -1) { + this.setData({ + cnday: '', + }); + return; + } + this.setData({ + cnday: vs.lunarFestival || vs.festival || vs.IDayCn, + festival: !!vs.festival || !!vs.lunarFestival, + unset: (cell === null || cell === void 0 ? void 0 : cell.isSelectedBegin) || (cell === null || cell === void 0 ? void 0 : cell.isSelectedEnd), + }); + }, +}, { + cnday: '', + festival: '', + unset: '', +}, null, { + attached() { + this.updateData(); + }, + observers: { + '**': function (data) { + const prevData = this._prevData || this.data; + this._prevData = { ...data }; + if (!equal(prevData.cell, data.cell)) { + this.updateData(); + } + }, + }, }); diff --git a/compiled/wechat/demo/pages/Calendar/collapse-container/collapse-container.js b/compiled/wechat/demo/pages/Calendar/collapse-container/collapse-container.js index eb2ee114b..b2f8e043a 100644 --- a/compiled/wechat/demo/pages/Calendar/collapse-container/collapse-container.js +++ b/compiled/wechat/demo/pages/Calendar/collapse-container/collapse-container.js @@ -1,19 +1,30 @@ -import { useEvent, useState } from 'functional-mini/component'; -import { mountComponent } from '../../../../src/_util/component'; -const CollapseContainer = (props) => { - var _a; - const [collapse, setCollapse] = useState((_a = props.defaultCollapse) !== null && _a !== void 0 ? _a : true); - useEvent('handleToggle', () => { - setCollapse((v) => !v); - }); - return { - collapse, - internalHide: props.hide, - containerTitle: props.title, - }; -}; -mountComponent(CollapseContainer, { +import { Component, getValueFromProps } from '../../../../src/_util/simply'; +Component({ hide: false, defaultCollapse: null, title: '', +}, { + handleToggle() { + const { collapse } = this.data; + this.setData({ + collapse: !collapse, + }); + }, +}, { + collapse: true, + internalHide: false, + containerTitle: '', +}, null, { + attached() { + const [defaultCollapse, hide, title] = getValueFromProps(this, [ + 'defaultCollapse', + 'hide', + 'title', + ]); + this.setData({ + collapse: defaultCollapse !== null && defaultCollapse !== void 0 ? defaultCollapse : true, + internalHide: hide, + containerTitle: title, + }); + }, }); diff --git a/compiled/wechat/package.json b/compiled/wechat/package.json index fb85b12e2..3ff979971 100644 --- a/compiled/wechat/package.json +++ b/compiled/wechat/package.json @@ -4,7 +4,6 @@ "async-validator": "^4.0.7", "dayjs": "^1.11.10", "fast-deep-equal": "3.1.3", - "functional-mini": "^0.17.0", "tslib": "2.5.0" }, "repository": "git@github.com:ant-design/ant-design-mini.git" diff --git a/compiled/wechat/src/Calendar/index.js b/compiled/wechat/src/Calendar/index.js index 9885518f6..3b584f9fd 100644 --- a/compiled/wechat/src/Calendar/index.js +++ b/compiled/wechat/src/Calendar/index.js @@ -9,6 +9,42 @@ var __assign = (this && this.__assign) || function () { }; return __assign.apply(this, arguments); }; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { @@ -19,137 +55,82 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { return to.concat(ar || Array.prototype.slice.call(from)); }; import dayjs from 'dayjs'; -import { useComponent, useEvent, useReady, useState, useEffect, } from 'functional-mini/component'; -import { mountComponent } from '../_util/component'; -import { useComponentEvent } from '../_util/hooks/useComponentEvent'; -import { triggerRefEvent } from '../_util/hooks/useReportRef'; -import { hasValue, useMergedState } from '../_util/hooks/useMergedState'; -import { defaultLocaleText, } from './props'; -import { defaultMonthRange, getMonthListFromRange, getSelectionModeFromValue, renderCells, getScrollIntoViewId, } from './utils'; -function getBoundingClientRect(instance, selector) { - return new Promise(function (resolve, reject) { - instance - .createSelectorQuery() - .select(selector) - .boundingClientRect() - .exec(function (ret) { - if (ret && ret[0]) { - resolve(ret[0]); - } - else { - reject(); - } - }); - }); -} -var Calendar = function (props) { - var _a, _b; - var localeText = Object.assign({}, defaultLocaleText, props.localeText); - var markItems = __spreadArray([], localeText.weekdayNames, true); - var weekStartsOn = props.weekStartsOn; - if (weekStartsOn === 'Sunday') { - var item = markItems.pop(); - if (item) - markItems.unshift(item); - } - var _c = useMergedState(props.defaultValue, { - value: props.value, - }), value = _c[0], setValue = _c[1]; - var _d = useState(''), scrollIntoViewId = _d[0], setScrollIntoViewId = _d[1]; - useEvent('scrollIntoView', function (value) { - updateScrollIntoViewId(getScrollIntoViewId(value)); - }); - triggerRefEvent(); - // scroll 触发滚动之后需要重置 scrollIntoViewId - function updateScrollIntoViewId(id) { - setScrollIntoViewId(id); - var timer = setTimeout(function () { - setScrollIntoViewId(''); - clearTimeout(timer); - }); - } - var selectionModeFromValue = getSelectionModeFromValue(value); - var selectionMode = (_b = (_a = props.selectionMode) !== null && _a !== void 0 ? _a : selectionModeFromValue) !== null && _b !== void 0 ? _b : 'range'; - var triggerEvent = useComponentEvent(props).triggerEvent; - function updateValue(newValue) { - var isControl = hasValue(props.value); - triggerEvent('change', newValue); - if (!isControl) { - setValue(newValue); +import equal from 'fast-deep-equal'; +import { Component, triggerEvent, getValueFromProps } from '../_util/simply'; +import { defaultLocaleText, CalendarDefaultProps, } from './props'; +import { getMonthListFromRange, getSelectionModeFromValue, renderCells, getScrollIntoViewId, } from './utils'; +import mixinValue from '../mixins/value'; +import { getInstanceBoundingClientRect } from '../_util/jsapi/get-instance-bounding-client-rect'; +Component(CalendarDefaultProps, { + getInstance: function () { + if (this.$id) { + return my; } - } - useEvent('clickCell', function (e) { + return this; + }, + getBoundingClientRect: function (query) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, getInstanceBoundingClientRect(this.getInstance(), query)]; + case 1: return [2 /*return*/, _a.sent()]; + } + }); + }); + }, + scrollIntoView: function (value) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); + }, + clickCell: function (e) { + var _a, _b; var time = e.currentTarget.dataset.time; var clickDate = dayjs(time.time); if (time.disabled) { return; } + var value = this.getValue(); + var selectionModeFromValue = getSelectionModeFromValue(value); + var selectionMode = (_b = (_a = getValueFromProps(this, 'selectionMode')) !== null && _a !== void 0 ? _a : selectionModeFromValue) !== null && _b !== void 0 ? _b : 'range'; if (selectionMode === 'range') { if (Array.isArray(value)) { if (value.length === 1) { var current = value[0]; if (dayjs(clickDate.toDate().getTime()).isBefore(dayjs(current))) { - updateValue([clickDate.toDate().getTime()]); + this.updateValue([clickDate.toDate().getTime()]); } else { - updateValue([value[0], clickDate.toDate().getTime()]); + this.updateValue([value[0], clickDate.toDate().getTime()]); } } else { - updateValue([clickDate.toDate().getTime()]); + this.updateValue([clickDate.toDate().getTime()]); } } else { - updateValue([clickDate.toDate().getTime()]); + this.updateValue([clickDate.toDate().getTime()]); } } else if (selectionMode === 'single') { - updateValue(clickDate.toDate().getTime()); + this.updateValue(clickDate.toDate().getTime()); } - }); - var monthList = getMonthListFromRange(dayjs(props.monthRange[0]), dayjs(props.monthRange[1])).map(function (p) { - var cells = renderCells(p, weekStartsOn, value, localeText); - if (props.onFormatter && typeof props.onFormatter === 'function') { - cells = cells.map(function (o) { - var _a; - var time = o.time, top = o.top, bottom = o.bottom, disabled = o.disabled, isSelectedBegin = o.isSelectedBegin, isSelectedEnd = o.isSelectedEnd, isSelected = o.isSelected; - var newState = (_a = props.onFormatter({ - time: time, - top: top ? __assign({}, top) : undefined, - bottom: bottom ? __assign({}, bottom) : undefined, - disabled: disabled, - isSelectedBegin: isSelectedBegin, - isSelectedEnd: isSelectedEnd, - isSelected: isSelected, - }, value)) !== null && _a !== void 0 ? _a : {}; - var result = __assign({}, o); - if (typeof newState === 'object') { - // 只允许修改三个字段 - ['top', 'bottom', 'disabled'].forEach(function (key) { - if (key in newState) { - result[key] = newState[key]; - } - }); - } - return result; - }); + }, + setCurrentMonth: function (e) { + this.setData({ headerState: e.month }); + }, + measurement: function () { + var elementSize = this.data.elementSize; + // 组件如果内嵌在 slot 里, 一定会被渲染出来, 但是此时 cellHight 为 0 + // 此时需要重新计算 + if (!elementSize || elementSize.cellHight === 0) { + this.measurementFn(); } - return { - title: p.format(localeText.title), - cells: cells, - }; - }); - var _e = useState(0), headerState = _e[0], setHeaderState = _e[1]; - useEvent('setCurrentMonth', function (e) { - setHeaderState(e.month); - }); - var _f = useState(null), elementSize = _f[0], setElementSize = _f[1]; - var componentInstance = useComponent(); - function measurement() { + }, + measurementFn: function () { + var _this = this; Promise.all([ - getBoundingClientRect(componentInstance, '.ant-calendar-body-container'), - getBoundingClientRect(componentInstance, '.ant-calendar-cells'), - getBoundingClientRect(componentInstance, '.ant-calendar-title-container'), + this.getBoundingClientRect('.ant-calendar-body-container'), + this.getBoundingClientRect('.ant-calendar-cells'), + this.getBoundingClientRect('.ant-calendar-title-container'), ]) .then(function (_a) { var bodyContainer = _a[0], cellContainer = _a[1], Title = _a[2]; @@ -157,56 +138,148 @@ var Calendar = function (props) { // 所以只能依赖 height 来计算 var paddingHeight = bodyContainer.height - cellContainer.height - Title.height; var monthTitleHeight = Title.height + paddingHeight; - var cellHight = cellContainer.height / (monthList[0].cells.length / 7); - setElementSize({ - monthTitleHeight: monthTitleHeight, - cellHight: cellHight, - paddingHeight: paddingHeight, + var cellHight = cellContainer.height / (_this.data.monthList[0].cells.length / 7); + _this.setData({ + elementSize: { + monthTitleHeight: monthTitleHeight, + cellHight: cellHight, + paddingHeight: paddingHeight, + }, }); }) .catch(function () { - setElementSize(null); + _this.setData({ elementSize: null }); }); - } - useEffect(function () { - // 滚动到已选的位置 - props.changedScrollIntoView && - updateScrollIntoViewId(getScrollIntoViewId(value)); - }, [value]); - useReady(function () { - measurement(); + }, + // scroll 触发滚动之后需要重置 scrollIntoViewId + updateScrollIntoViewId: function (id) { + var _this = this; + this.setData({ scrollIntoViewId: id }); + var timer = setTimeout(function () { + _this.setData({ scrollIntoViewId: '' }); + clearTimeout(timer); + }); + }, + updateValue: function (newValue) { + triggerEvent(this, 'change', newValue); + if (!this.isControlled()) { + this.update(newValue); + } + }, + updateData: function () { + var _a = getValueFromProps(this, [ + 'monthRange', + 'localeText', + 'weekStartsOn', + 'onFormatter', + ]), monthRange = _a[0], plocaleText = _a[1], pweekStartsOn = _a[2], onFormatter = _a[3]; + var localeText = Object.assign({}, defaultLocaleText, plocaleText); + var markItems = __spreadArray([], localeText.weekdayNames, true); + var weekStartsOn = pweekStartsOn; + if (weekStartsOn === 'Sunday') { + var item = markItems.pop(); + if (item) + markItems.unshift(item); + } + var value = this.getValue(); + var monthList = getMonthListFromRange(dayjs(monthRange === null || monthRange === void 0 ? void 0 : monthRange[0]), dayjs(monthRange === null || monthRange === void 0 ? void 0 : monthRange[1])).map(function (p) { + var cells = renderCells(p, weekStartsOn, value, localeText); + if (onFormatter && typeof onFormatter === 'function') { + cells = cells.map(function (o) { + var _a; + var time = o.time, top = o.top, bottom = o.bottom, disabled = o.disabled, isSelectedBegin = o.isSelectedBegin, isSelectedEnd = o.isSelectedEnd, isSelected = o.isSelected; + var newState = (_a = onFormatter({ + time: time, + top: top ? __assign({}, top) : undefined, + bottom: bottom ? __assign({}, bottom) : undefined, + disabled: disabled, + isSelectedBegin: isSelectedBegin, + isSelectedEnd: isSelectedEnd, + isSelected: isSelected, + }, value)) !== null && _a !== void 0 ? _a : {}; + var result = __assign({}, o); + if (typeof newState === 'object') { + // 只允许修改三个字段 + ['top', 'bottom', 'disabled'].forEach(function (key) { + if (key in newState) { + result[key] = newState[key]; + } + }); + } + return result; + }); + } + return { + title: p.format(localeText.title), + cells: cells, + }; + }); + this.setData({ markItems: markItems, monthList: monthList }); + }, +}, { + elementSize: null, + markItems: [], + monthList: [], + headerState: 0, + scrollIntoViewId: '', +}, [mixinValue()], { + didMount: function () { + this.updateData(); + this.measurementFn(); // 初始化默认值时,滚动到选中位置 - var isControl = hasValue(props.value); - if (isControl) { - updateScrollIntoViewId(getScrollIntoViewId(props.value)); + var _a = getValueFromProps(this, [ + 'value', + 'defaultValue', + ]), value = _a[0], defaultValue = _a[1]; + if (this.isControlled()) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); } else { - props.defaultValue && - updateScrollIntoViewId(getScrollIntoViewId(props.defaultValue)); + defaultValue && + this.updateScrollIntoViewId(getScrollIntoViewId(defaultValue)); } - }, []); - useEvent('measurement', function () { - // 组件如果内嵌在 slot 里, 一定会被渲染出来, 但是此时 cellHight 为 0 - // 此时需要重新计算 - if (!elementSize || elementSize.cellHight === 0) { - measurement(); + }, + didUpdate: function (prevProps, prevData) { + if (!this.isEqualValue(prevData)) { + // 滚动到已选的位置 + var changedScrollIntoView = getValueFromProps(this, 'changedScrollIntoView'); + changedScrollIntoView && + this.updateScrollIntoViewId(getScrollIntoViewId(this.getValue())); } - }); - return { - elementSize: elementSize, - markItems: markItems, - monthList: monthList, - headerState: headerState, - scrollIntoViewId: scrollIntoViewId, - }; -}; -mountComponent(Calendar, { - defaultValue: null, - value: null, - selectionMode: 'range', - monthRange: defaultMonthRange(), - weekStartsOn: 'Sunday', - localeText: defaultLocaleText, - onFormatter: null, - changedScrollIntoView: null, + if (!equal(prevProps, this.props) || !this.isEqualValue(prevData)) { + this.updateData(); + } + }, + attached: function () { + this.updateData(); + this.measurementFn(); + // 初始化默认值时,滚动到选中位置 + var _a = getValueFromProps(this, [ + 'value', + 'defaultValue', + ]), value = _a[0], defaultValue = _a[1]; + if (this.isControlled()) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); + } + else { + defaultValue && + this.updateScrollIntoViewId(getScrollIntoViewId(defaultValue)); + } + this.triggerEvent('ref', this); + }, + observers: { + '**': function (data) { + var prevData = this._prevData || this.data; + this._prevData = __assign({}, data); + if (!equal(prevData, data)) { + this.updateData(); + } + }, + 'mixin.value': function () { + // 滚动到已选的位置 + var changedScrollIntoView = getValueFromProps(this, 'changedScrollIntoView'); + changedScrollIntoView && + this.updateScrollIntoViewId(getScrollIntoViewId(this.getValue())); + }, + }, }); diff --git a/compiled/wechat/src/Calendar/props.js b/compiled/wechat/src/Calendar/props.js index 20e66c29a..bdf434103 100644 --- a/compiled/wechat/src/Calendar/props.js +++ b/compiled/wechat/src/Calendar/props.js @@ -1,3 +1,4 @@ +import { defaultMonthRange } from './utils'; export var defaultLocaleText = { weekdayNames: ['一', '二', '三', '四', '五', '六', '日'], title: 'YYYY年MM月', @@ -6,3 +7,13 @@ export var defaultLocaleText = { end: '结束', startAndEnd: '开始/结束', }; +export var CalendarDefaultProps = { + defaultValue: null, + value: null, + selectionMode: 'range', + monthRange: defaultMonthRange(), + weekStartsOn: 'Sunday', + localeText: defaultLocaleText, + onFormatter: null, + changedScrollIntoView: null, +}; diff --git a/compiled/wechat/src/_util/component.js b/compiled/wechat/src/_util/component.js deleted file mode 100644 index 6a4d6808d..000000000 --- a/compiled/wechat/src/_util/component.js +++ /dev/null @@ -1,34 +0,0 @@ -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -import { wechatComponent } from 'functional-mini/component'; -function removeNullProps(props) { - var newProps = {}; - for (var key in props) { - if (props[key] !== null) { - newProps[key] = props[key]; - } - } - return newProps; -} -export function mountComponent(Hooks, defaultProps) { - Component(wechatComponent(Hooks, mergeDefaultProps(defaultProps), { - options: { - styleIsolation: 'shared', - multipleSlots: true, - virtualHost: true, - }, - })); -} -function mergeDefaultProps(defaultProps) { - if (defaultProps === void 0) { defaultProps = {}; } - return __assign({ className: '', style: '' }, defaultProps); -} diff --git a/compiled/wechat/src/_util/hooks/useComponentEvent.js b/compiled/wechat/src/_util/hooks/useComponentEvent.js deleted file mode 100644 index a3ff760c4..000000000 --- a/compiled/wechat/src/_util/hooks/useComponentEvent.js +++ /dev/null @@ -1,32 +0,0 @@ -import { useComponent } from 'functional-mini/component'; -import { useEvent } from './useEvent'; -export function useComponentEvent(props) { - var component = useComponent(); - var triggerEvent = useEvent(function (eventName, value, e) { - // 首字母大写,然后加上 on - component.triggerEvent(eventName.toLocaleLowerCase(), value); - }); - var triggerEventValues = useEvent(function (eventName, values, e) { - // 首字母大写,然后加上 on - component.triggerEvent(eventName.toLocaleLowerCase(), values); - }); - var triggerEventOnly = useEvent(function (eventName, e) { - // 首字母大写,然后加上 on - component.triggerEvent(eventName.toLocaleLowerCase()); - }); - var alipayForwardCatchEvent = useEvent(function (eventName, e) { - // 首字母大写,然后加上 catch - }); - var alipayForwardEvent = useEvent(function (eventName, e) { - // 首字母大写,然后加上 on - }); - return { - triggerEvent: triggerEvent, - triggerEventValues: triggerEventValues, - triggerEventOnly: triggerEventOnly, - // 转发 catch 事件 - alipayForwardCatchEvent: alipayForwardCatchEvent, - // 转发事件 - alipayForwardEvent: alipayForwardEvent, - }; -} diff --git a/compiled/wechat/src/_util/hooks/useEvent.js b/compiled/wechat/src/_util/hooks/useEvent.js deleted file mode 100644 index fe06a3759..000000000 --- a/compiled/wechat/src/_util/hooks/useEvent.js +++ /dev/null @@ -1,28 +0,0 @@ -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useEvent.ts - */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as React from 'functional-mini/compat'; -// eslint-disable-next-line @typescript-eslint/ban-types -export function useEvent(callback) { - var fnRef = React.useRef(); - fnRef.current = callback; - var memoFn = React.useCallback((function () { - var _a; - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return (_a = fnRef.current) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArray([fnRef], args, false)); - }), []); - return memoFn; -} diff --git a/compiled/wechat/src/_util/hooks/useHandleCustomEvent.js b/compiled/wechat/src/_util/hooks/useHandleCustomEvent.js deleted file mode 100644 index bcce0902e..000000000 --- a/compiled/wechat/src/_util/hooks/useHandleCustomEvent.js +++ /dev/null @@ -1,33 +0,0 @@ -import { useEvent } from 'functional-mini/component'; -export var useHandleCustomEvent = function (eventName, handler) { - useEvent(eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - function (eventOrValue, alipayEvent) { - return handler(eventOrValue.detail, eventOrValue); - }); -}; -export var useMultipleValueHandleCustomEvent = function (eventName, handler) { - useEvent(eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - var firstArgs = args[0]; - if (Array.isArray(firstArgs.detail)) { - var wechatArgs = firstArgs.detail.concat(firstArgs); - return handler.apply(void 0, wechatArgs); - } - else { - return handler([firstArgs.detail, firstArgs]); - } - }); -}; -export var useHandleCustomEventOnly = function (eventName, handler) { - useEvent(eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - function (event) { - return handler(event); - }); -}; diff --git a/compiled/wechat/src/_util/hooks/useInstanceBoundingClientRect.js b/compiled/wechat/src/_util/hooks/useInstanceBoundingClientRect.js deleted file mode 100644 index 13d90c699..000000000 --- a/compiled/wechat/src/_util/hooks/useInstanceBoundingClientRect.js +++ /dev/null @@ -1,82 +0,0 @@ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -import { useComponent } from 'functional-mini/component'; -import { getInstanceBoundingClientRect } from '../jsapi/get-instance-bounding-client-rect'; -export var useInstanceBoundingClientRect = function () { - var instance = useComponent(); - function getInstance() { - if (instance.$id) { - return my; - } - return instance; - } - function getBoundingClientRectWithId(prefix) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, getInstanceBoundingClientRect(getInstance(), "".concat(prefix).concat(instance.$id ? "-".concat(instance.$id) : ''))]; - case 1: return [2 /*return*/, _a.sent()]; - } - }); - }); - } - function getBoundingClientRect(query) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, getInstanceBoundingClientRect(getInstance(), query)]; - case 1: return [2 /*return*/, _a.sent()]; - } - }); - }); - } - function getBoundingClientRectWithBuilder(builder) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, getInstanceBoundingClientRect(getInstance(), builder(instance.$id ? "-".concat(instance.$id) : ''))]; - case 1: return [2 /*return*/, _a.sent()]; - } - }); - }); - } - return { - getBoundingClientRect: getBoundingClientRect, - getBoundingClientRectWithId: getBoundingClientRectWithId, - getBoundingClientRectWithBuilder: getBoundingClientRectWithBuilder, - }; -}; diff --git a/compiled/wechat/src/_util/hooks/useLayoutEffect.js b/compiled/wechat/src/_util/hooks/useLayoutEffect.js deleted file mode 100644 index ca2b92bfc..000000000 --- a/compiled/wechat/src/_util/hooks/useLayoutEffect.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useLayoutEffect.ts - */ -import * as React from 'functional-mini/compat'; -/** - * Wrap `React.useLayoutEffect` which will not throw warning message in test env - */ -var useInternalLayoutEffect = React.useEffect; -var useLayoutEffect = function (callback, deps) { - var firstMountRef = React.useRef(true); - useInternalLayoutEffect(function () { - return callback(firstMountRef.current); - }, deps); - // We tell react that first mount has passed - useInternalLayoutEffect(function () { - firstMountRef.current = false; - return function () { - firstMountRef.current = true; - }; - }, []); -}; -export var useComponentUpdateEffect = function (callback, deps) { - useLayoutEffect(function (firstMount) { - if (!firstMount) { - return callback(); - } - }, deps); -}; -export default useLayoutEffect; diff --git a/compiled/wechat/src/_util/hooks/useMergedState.js b/compiled/wechat/src/_util/hooks/useMergedState.js deleted file mode 100644 index 3d4af9027..000000000 --- a/compiled/wechat/src/_util/hooks/useMergedState.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useMergedState.ts - */ -import { useEvent } from './useEvent'; -import { useComponentUpdateEffect } from './useLayoutEffect'; -import { useSafeState as useState } from './useState'; -import { supportUndefinedProperty } from '../platform'; -/** We only think `undefined` is empty */ -export function hasValue(value) { - if (supportUndefinedProperty()) { - return value !== undefined; - } - return value !== null && value !== undefined; -} -/** - * Similar to `useState` but will use props value if provided. - * Note that internal use rc-util `useState` hook. - */ -export function useMergedState(defaultStateValue, option) { - var _a = option || {}, defaultValue = _a.defaultValue, value = _a.value, onChange = _a.onChange, postState = _a.postState; - // ======================= Init ======================= - var _b = useState(function () { - if (hasValue(value)) { - return value; - } - else if (hasValue(defaultValue)) { - return typeof defaultValue === 'function' - ? defaultValue() - : defaultValue; - } - else { - return typeof defaultStateValue === 'function' - ? defaultStateValue() - : defaultStateValue; - } - }), innerValue = _b[0], setInnerValue = _b[1]; - var mergedValue = hasValue(value) ? value : innerValue; - var postMergedValue = postState ? postState(mergedValue) : mergedValue; - // ====================== Change ====================== - var onChangeFn = useEvent(onChange); - var _c = useState([mergedValue]), prevValue = _c[0], setPrevValue = _c[1]; - useComponentUpdateEffect(function () { - var prev = prevValue[0]; - if (innerValue !== prev) { - onChangeFn(innerValue, prev); - } - }, [prevValue]); - // Sync value back to `undefined` when it from control to un-control - useComponentUpdateEffect(function () { - if (!hasValue(value)) { - setInnerValue(value); - } - }, [value]); - // ====================== Update ====================== - var triggerChange = useEvent(function (updater, ignoreDestroy) { - setInnerValue(updater, ignoreDestroy); - setPrevValue([mergedValue], ignoreDestroy); - }); - return [postMergedValue, triggerChange]; -} diff --git a/compiled/wechat/src/_util/hooks/useMixState.js b/compiled/wechat/src/_util/hooks/useMixState.js deleted file mode 100644 index 10f8c6908..000000000 --- a/compiled/wechat/src/_util/hooks/useMixState.js +++ /dev/null @@ -1,73 +0,0 @@ -import { useEvent } from './useEvent'; -import { useComponentUpdateEffect } from './useLayoutEffect'; -import { hasValue } from './useMergedState'; -import { useSafeState as useState } from './useState'; -export function useMixState(defaultStateValue, option) { - var _a = option || {}, defaultValue = _a.defaultValue, value = _a.value, _b = _a.postState, postState = _b === void 0 ? function (v) { return ({ valid: true, value: v }); } : _b; - // ======================= Init ======================= - var _c = useState(function () { - var v; - if (hasValue(value)) { - v = value; - } - else if (hasValue(defaultValue)) { - v = - typeof defaultValue === 'function' - ? defaultValue() - : defaultValue; - } - else { - v = - typeof defaultStateValue === 'function' - ? defaultStateValue() - : defaultStateValue; - } - var state = postState(v); - if (state.valid) { - return state.value; - } - }), innerValue = _c[0], setInnerValue = _c[1]; - var state = postState(value); - var merge = hasValue(value) && state.valid ? state.value : innerValue; - useComponentUpdateEffect(function () { - var state = postState(value); - if (state.valid) { - setInnerValue(state.value); - } - }, [value]); - var isControlled = hasValue(value); - var triggerChange = useEvent(function (newState, ignoreDestroy) { - setInnerValue(newState, ignoreDestroy); - }); - var triggerUpdate = useEvent(function (value, option) { - var state = postState(value, option); - if (state.valid && state.value !== innerValue) { - triggerChange(state.value); - return { changed: true, newValue: state.value }; - } - return { changed: false }; - }); - var triggerUpdater = useEvent(function (getValue, option) { - if (isControlled) { - getValue(merge); - } - else { - triggerChange(function (old) { - var newValue = getValue(old); - var state = postState(newValue, option); - if (state.valid && state.value !== innerValue) { - return state.value; - } - return old; - }); - } - }); - return [ - merge, - { - isControlled: isControlled, - update: triggerUpdate, - triggerUpdater: triggerUpdater, - }, - ]; -} diff --git a/compiled/wechat/src/_util/hooks/useReportRef.js b/compiled/wechat/src/_util/hooks/useReportRef.js deleted file mode 100644 index 71b766ed5..000000000 --- a/compiled/wechat/src/_util/hooks/useReportRef.js +++ /dev/null @@ -1,7 +0,0 @@ -import { useComponent, useEffect } from 'functional-mini/component'; -export var triggerRefEvent = function () { - var component = useComponent(); - useEffect(function () { - component.triggerEvent('ref', component); - }, []); -}; diff --git a/compiled/wechat/src/_util/hooks/useState.js b/compiled/wechat/src/_util/hooks/useState.js deleted file mode 100644 index 9effb3b72..000000000 --- a/compiled/wechat/src/_util/hooks/useState.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useState.ts - */ -import * as React from 'functional-mini/compat'; -/** - * Same as React.useState but `setState` accept `ignoreDestroy` param to not to setState after destroyed. - * We do not make this auto is to avoid real memory leak. - * Developer should confirm it's safe to ignore themselves. - */ -export function useSafeState(defaultValue) { - var destroyRef = React.useRef(false); - var _a = React.useState(defaultValue), value = _a[0], setValue = _a[1]; - React.useEffect(function () { - destroyRef.current = false; - return function () { - destroyRef.current = true; - }; - }, []); - function safeSetState(updater, ignoreDestroy) { - if (ignoreDestroy && destroyRef.current) { - return; - } - setValue(updater); - } - return [value, safeSetState]; -} diff --git a/compiled/wechat/src/_util/triggerComonentEvent.js b/compiled/wechat/src/_util/triggerComonentEvent.js deleted file mode 100644 index 68b4898f6..000000000 --- a/compiled/wechat/src/_util/triggerComonentEvent.js +++ /dev/null @@ -1,3 +0,0 @@ -export function triggerComponentEvent(instance, propsName, event) { - instance.triggerEvent(propsName, event.detail); -} diff --git a/demo/pages/Calendar/collapse-container/cn-day/cn-day.ts b/demo/pages/Calendar/collapse-container/cn-day/cn-day.ts index f3668babf..c32f3c0e1 100644 --- a/demo/pages/Calendar/collapse-container/cn-day/cn-day.ts +++ b/demo/pages/Calendar/collapse-container/cn-day/cn-day.ts @@ -1,31 +1,69 @@ import dayjs from 'dayjs'; +import equal from 'fast-deep-equal'; import Converter from './js-calendar-converter'; -import { mountComponent } from '../../../../../src/_util/component'; +import { Component, getValueFromProps } from '../../../../../src/_util/simply'; interface Props { cell: any; } -const CollapseContainer = (props: Props) => { - const time = dayjs(props.cell?.time); - const vs = Converter.solar2lunar( - time.get('year'), - time.get('month') + 1, - time.get('date') - ); - if (vs === -1) { - return { - cnday: '', - }; +Component( + { + cell: null, + } as Props, + { + updateData() { + const cell = getValueFromProps(this, 'cell'); + const time = dayjs(cell?.time); + const vs = Converter.solar2lunar( + time.get('year'), + time.get('month') + 1, + time.get('date') + ); + if (vs === -1) { + this.setData({ + cnday: '', + }); + return; + } + this.setData({ + cnday: vs.lunarFestival || vs.festival || vs.IDayCn, + festival: !!vs.festival || !!vs.lunarFestival, + unset: cell?.isSelectedBegin || cell?.isSelectedEnd, + }); + }, + }, + { + cnday: '', + festival: '', + unset: '', + }, + null, + { + /// #if ALIPAY + onInit() { + this.updateData(); + }, + didUpdate(prevProps) { + const cell = getValueFromProps(this, 'cell'); + if (!equal(prevProps.cell, cell)) { + this.updateData(); + } + }, + /// #endif + /// #if WECHAT + attached() { + this.updateData(); + }, + observers: { + '**': function (data) { + const prevData = this._prevData || this.data; + this._prevData = { ...data }; + if (!equal(prevData.cell, data.cell)) { + this.updateData(); + } + }, + }, + /// #endif } - - return { - cnday: vs.lunarFestival || vs.festival || vs.IDayCn, - festival: !!vs.festival || !!vs.lunarFestival, - unset: props.cell?.isSelectedBegin || props.cell?.isSelectedEnd, - }; -}; - -mountComponent(CollapseContainer, { - cell: null, -}); +); diff --git a/demo/pages/Calendar/collapse-container/collapse-container.ts b/demo/pages/Calendar/collapse-container/collapse-container.ts index fd7e57d14..0afb9ada0 100644 --- a/demo/pages/Calendar/collapse-container/collapse-container.ts +++ b/demo/pages/Calendar/collapse-container/collapse-container.ts @@ -1,5 +1,4 @@ -import { useEvent, useState } from 'functional-mini/component'; -import { mountComponent } from '../../../../src/_util/component'; +import { Component, getValueFromProps } from '../../../../src/_util/simply'; export interface Props { hide?: boolean; @@ -8,22 +7,55 @@ export interface Props { handleClick?(id: string): void; } -const CollapseContainer = (props: Props) => { - const [collapse, setCollapse] = useState(props.defaultCollapse ?? true); +Component( + { + hide: false, + defaultCollapse: null, + title: '', + } as Props, + { + handleToggle() { + const { collapse } = this.data; + this.setData({ + collapse: !collapse, + }); + }, + }, + { + collapse: true, + internalHide: false, + containerTitle: '', + }, + null, + { + /// #if ALIPAY + onInit() { + const [defaultCollapse, hide, title] = getValueFromProps(this, [ + 'defaultCollapse', + 'hide', + 'title', + ]); + this.setData({ + collapse: defaultCollapse ?? true, + internalHide: hide, + containerTitle: title, + }); + }, + /// #endif + /// #if WECHAT + attached() { + const [defaultCollapse, hide, title] = getValueFromProps(this, [ + 'defaultCollapse', + 'hide', + 'title', + ]); + this.setData({ + collapse: defaultCollapse ?? true, + internalHide: hide, + containerTitle: title, + }); + }, - useEvent('handleToggle', () => { - setCollapse((v) => !v); - }); - - return { - collapse, - internalHide: props.hide, - containerTitle: props.title, - }; -}; - -mountComponent(CollapseContainer, { - hide: false, - defaultCollapse: null, - title: '', -}); + /// #endif + } +); diff --git a/docs/guide/contribute.md b/docs/guide/contribute.md index 92ee63b8b..6bafebe52 100644 --- a/docs/guide/contribute.md +++ b/docs/guide/contribute.md @@ -64,14 +64,6 @@ $ npm run dev:doc ## Ant Design Mini 的工程方案 -### 函数式组件 - -从 v2 版本开始,我们逐步采用“React 函数式组件”开发模式来开发小程序自定义组件,背后依托 [functional-mini](https://github.com/ant-design/functional-mini) 这个 SDK。如日历组件(参见 [Calendar/index.ts](https://github.com/ant-design/ant-design-mini/blob/master/src/Calendar/index.ts))。 - -[functional-mini](https://github.com/ant-design/functional-mini) 作为运行时 SDK,接管小程序的逻辑层代码,但并不影响视图层,为我们在项目架构复杂度和编码习惯上带来平衡。借此,函数式组件的基本特性得以运用,提升代码可维护性,如数据加工逻辑组装、hooks 逻辑复用等。 - -欢迎你一同参与 Ant Design Mini 函数式组件开发,探索更佳的小程序工程形态。 - ### 使用 tsx 语法编写视图层 我们使用 tsx 语法编写视图层。编译器解析 tsx 语法后,生成小程序视图层代码。这意味着: diff --git a/package.json b/package.json index 0c297fe96..2e1e9515e 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "async-validator": "^4.0.7", "dayjs": "^1.11.3", "fast-deep-equal": "3.1.3", - "functional-mini": "^0.17.0", "tslib": "2.5.0" }, "overrides": { diff --git a/src/Calendar/index.ts b/src/Calendar/index.ts index 5e12a4af9..f5867d2b3 100644 --- a/src/Calendar/index.ts +++ b/src/Calendar/index.ts @@ -1,244 +1,260 @@ import dayjs from 'dayjs'; -import { - useComponent, - useEvent, - useReady, - useState, - useEffect, -} from 'functional-mini/component'; -import { mountComponent } from '../_util/component'; -import { useComponentEvent } from '../_util/hooks/useComponentEvent'; -import { triggerRefEvent } from '../_util/hooks/useReportRef'; -import { hasValue, useMergedState } from '../_util/hooks/useMergedState'; +import equal from 'fast-deep-equal'; +import { Component, triggerEvent, getValueFromProps } from '../_util/simply'; import { CalendarValue, CellState, defaultLocaleText, - ICalendarProps, + CalendarDefaultProps, } from './props'; import { - defaultMonthRange, getMonthListFromRange, getSelectionModeFromValue, renderCells, getScrollIntoViewId, } from './utils'; +import mixinValue from '../mixins/value'; +import { getInstanceBoundingClientRect } from '../_util/jsapi/get-instance-bounding-client-rect'; -function getBoundingClientRect(instance: any, selector: string) { - return new Promise((resolve, reject) => { - instance - .createSelectorQuery() - .select(selector) - .boundingClientRect() - .exec((ret) => { - if (ret && ret[0]) { - resolve(ret[0]); - } else { - reject(); - } - }); - }); -} - -const Calendar = (props: ICalendarProps) => { - const localeText = Object.assign({}, defaultLocaleText, props.localeText); - - const markItems = [...localeText.weekdayNames]; - const weekStartsOn = props.weekStartsOn; - if (weekStartsOn === 'Sunday') { - const item = markItems.pop(); - if (item) markItems.unshift(item); - } - - const [value, setValue] = useMergedState(props.defaultValue, { - value: props.value, - }); - - const [scrollIntoViewId, setScrollIntoViewId] = useState(''); - - useEvent('scrollIntoView', (value) => { - updateScrollIntoViewId(getScrollIntoViewId(value)); - }); - - triggerRefEvent(); - - // scroll 触发滚动之后需要重置 scrollIntoViewId - function updateScrollIntoViewId(id) { - setScrollIntoViewId(id); - - const timer = setTimeout(() => { - setScrollIntoViewId(''); - clearTimeout(timer); - }); - } - - const selectionModeFromValue = getSelectionModeFromValue(value); - const selectionMode = - props.selectionMode ?? selectionModeFromValue ?? 'range'; - - const { triggerEvent } = useComponentEvent(props); - function updateValue(newValue: CalendarValue) { - const isControl = hasValue(props.value); - triggerEvent('change', newValue); - if (!isControl) { - setValue(newValue); - } - } - - useEvent('clickCell', (e) => { - const time = e.currentTarget.dataset.time; - const clickDate = dayjs(time.time); - if (time.disabled) { - return; - } - if (selectionMode === 'range') { - if (Array.isArray(value)) { - if (value.length === 1) { - const current = value[0]; - if (dayjs(clickDate.toDate().getTime()).isBefore(dayjs(current))) { - updateValue([clickDate.toDate().getTime()]); +Component( + CalendarDefaultProps, + { + getInstance() { + if (this.$id) { + return my; + } + return this; + }, + async getBoundingClientRect(query: string) { + return await getInstanceBoundingClientRect(this.getInstance(), query); + }, + scrollIntoView(value) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); + }, + clickCell(e) { + const time = e.currentTarget.dataset.time; + const clickDate = dayjs(time.time); + if (time.disabled) { + return; + } + const value = this.getValue(); + const selectionModeFromValue = getSelectionModeFromValue(value); + const selectionMode = + getValueFromProps(this, 'selectionMode') ?? + selectionModeFromValue ?? + 'range'; + if (selectionMode === 'range') { + if (Array.isArray(value)) { + if (value.length === 1) { + const current = value[0]; + if (dayjs(clickDate.toDate().getTime()).isBefore(dayjs(current))) { + this.updateValue([clickDate.toDate().getTime()]); + } else { + this.updateValue([value[0], clickDate.toDate().getTime()]); + } } else { - updateValue([value[0], clickDate.toDate().getTime()]); + this.updateValue([clickDate.toDate().getTime()]); } } else { - updateValue([clickDate.toDate().getTime()]); + this.updateValue([clickDate.toDate().getTime()]); } - } else { - updateValue([clickDate.toDate().getTime()]); + } else if (selectionMode === 'single') { + this.updateValue(clickDate.toDate().getTime()); + } + }, + setCurrentMonth(e) { + this.setData({ headerState: e.month }); + }, + measurement() { + const { elementSize } = this.data; + // 组件如果内嵌在 slot 里, 一定会被渲染出来, 但是此时 cellHight 为 0 + // 此时需要重新计算 + if (!elementSize || elementSize.cellHight === 0) { + this.measurementFn(); } - } else if (selectionMode === 'single') { - updateValue(clickDate.toDate().getTime()); - } - }); + }, + measurementFn() { + Promise.all([ + this.getBoundingClientRect('.ant-calendar-body-container'), + this.getBoundingClientRect('.ant-calendar-cells'), + this.getBoundingClientRect('.ant-calendar-title-container'), + ]) + .then(([bodyContainer, cellContainer, Title]) => { + // 滚动的时候 top 和 bottom 等尺寸会变 + // 所以只能依赖 height 来计算 + const paddingHeight = + bodyContainer.height - cellContainer.height - Title.height; + const monthTitleHeight = Title.height + paddingHeight; + const cellHight = + cellContainer.height / (this.data.monthList[0].cells.length / 7); + this.setData({ + elementSize: { + monthTitleHeight, + cellHight, + paddingHeight, + }, + }); + }) + .catch(() => { + this.setData({ elementSize: null }); + }); + }, + + // scroll 触发滚动之后需要重置 scrollIntoViewId + updateScrollIntoViewId(id) { + this.setData({ scrollIntoViewId: id }); + const timer = setTimeout(() => { + this.setData({ scrollIntoViewId: '' }); + clearTimeout(timer); + }); + }, - const monthList = getMonthListFromRange( - dayjs(props.monthRange[0]), - dayjs(props.monthRange[1]) - ).map((p) => { - let cells = renderCells(p, weekStartsOn, value, localeText); - if (props.onFormatter && typeof props.onFormatter === 'function') { - cells = cells.map((o): CellState => { - const { - time, - top, - bottom, - disabled, - isSelectedBegin, - isSelectedEnd, - isSelected, - } = o; - const newState = - props.onFormatter( - { + updateValue(newValue: CalendarValue) { + triggerEvent(this, 'change', newValue); + if (!this.isControlled()) { + this.update(newValue); + } + }, + updateData() { + const [monthRange, plocaleText, pweekStartsOn, onFormatter] = + getValueFromProps(this, [ + 'monthRange', + 'localeText', + 'weekStartsOn', + 'onFormatter', + ]); + const localeText = Object.assign({}, defaultLocaleText, plocaleText); + const markItems = [...localeText.weekdayNames]; + const weekStartsOn = pweekStartsOn; + if (weekStartsOn === 'Sunday') { + const item = markItems.pop(); + if (item) markItems.unshift(item); + } + const value = this.getValue(); + const monthList = getMonthListFromRange( + dayjs(monthRange?.[0]), + dayjs(monthRange?.[1]) + ).map((p) => { + let cells = renderCells(p, weekStartsOn, value, localeText); + if (onFormatter && typeof onFormatter === 'function') { + cells = cells.map((o): CellState => { + const { time, - top: top ? { ...top } : undefined, - bottom: bottom ? { ...bottom } : undefined, + top, + bottom, disabled, isSelectedBegin, isSelectedEnd, isSelected, - }, - value - ) ?? {}; - const result = { ...o }; - if (typeof newState === 'object') { - // 只允许修改三个字段 - ['top', 'bottom', 'disabled'].forEach((key) => { - if (key in newState) { - result[key] = newState[key]; + } = o; + const newState = + onFormatter( + { + time, + top: top ? { ...top } : undefined, + bottom: bottom ? { ...bottom } : undefined, + disabled, + isSelectedBegin, + isSelectedEnd, + isSelected, + }, + value + ) ?? {}; + const result = { ...o }; + if (typeof newState === 'object') { + // 只允许修改三个字段 + ['top', 'bottom', 'disabled'].forEach((key) => { + if (key in newState) { + result[key] = newState[key]; + } + }); } + return result; }); } - return result; + return { + title: p.format(localeText.title), + cells, + }; }); - } - return { - title: p.format(localeText.title), - cells, - }; - }); - - const [headerState, setHeaderState] = useState(0); - - useEvent('setCurrentMonth', (e) => { - setHeaderState(e.month); - }); - const [elementSize, setElementSize] = useState<{ - monthTitleHeight: number; - cellHight: number; - paddingHeight: number; - }>(null); - - const componentInstance = useComponent(); + this.setData({ markItems, monthList }); + }, + }, + { + elementSize: null, + markItems: [], + monthList: [], + headerState: 0, + scrollIntoViewId: '', + }, + [mixinValue()], + { + didMount() { + this.updateData(); + this.measurementFn(); + // 初始化默认值时,滚动到选中位置 + const [value, defaultValue] = getValueFromProps(this, [ + 'value', + 'defaultValue', + ]); + if (this.isControlled()) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); + } else { + defaultValue && + this.updateScrollIntoViewId(getScrollIntoViewId(defaultValue)); + } + }, + didUpdate(prevProps, prevData) { + if (!this.isEqualValue(prevData)) { + // 滚动到已选的位置 + const changedScrollIntoView = getValueFromProps( + this, + 'changedScrollIntoView' + ); + changedScrollIntoView && + this.updateScrollIntoViewId(getScrollIntoViewId(this.getValue())); + } + if (!equal(prevProps, this.props) || !this.isEqualValue(prevData)) { + this.updateData(); + } + }, + /// #if WECHAT + attached() { + this.updateData(); + this.measurementFn(); + // 初始化默认值时,滚动到选中位置 + const [value, defaultValue] = getValueFromProps(this, [ + 'value', + 'defaultValue', + ]); + if (this.isControlled()) { + this.updateScrollIntoViewId(getScrollIntoViewId(value)); + } else { + defaultValue && + this.updateScrollIntoViewId(getScrollIntoViewId(defaultValue)); + } + this.triggerEvent('ref', this); + }, - function measurement() { - Promise.all([ - getBoundingClientRect(componentInstance, '.ant-calendar-body-container'), - getBoundingClientRect(componentInstance, '.ant-calendar-cells'), - getBoundingClientRect(componentInstance, '.ant-calendar-title-container'), - ]) - .then(([bodyContainer, cellContainer, Title]) => { - // 滚动的时候 top 和 bottom 等尺寸会变 - // 所以只能依赖 height 来计算 - const paddingHeight = - bodyContainer.height - cellContainer.height - Title.height; - const monthTitleHeight = Title.height + paddingHeight; - const cellHight = - cellContainer.height / (monthList[0].cells.length / 7); - setElementSize({ - monthTitleHeight, - cellHight, - paddingHeight, - }); - }) - .catch(() => { - setElementSize(null); - }); + observers: { + '**': function (data) { + const prevData = this._prevData || this.data; + this._prevData = { ...data }; + if (!equal(prevData, data)) { + this.updateData(); + } + }, + 'mixin.value': function () { + // 滚动到已选的位置 + const changedScrollIntoView = getValueFromProps( + this, + 'changedScrollIntoView' + ); + changedScrollIntoView && + this.updateScrollIntoViewId(getScrollIntoViewId(this.getValue())); + }, + }, + /// #endif } - - useEffect(() => { - // 滚动到已选的位置 - props.changedScrollIntoView && - updateScrollIntoViewId(getScrollIntoViewId(value)); - }, [value]); - - useReady(() => { - measurement(); - // 初始化默认值时,滚动到选中位置 - const isControl = hasValue(props.value); - if (isControl) { - updateScrollIntoViewId(getScrollIntoViewId(props.value)); - } else { - props.defaultValue && - updateScrollIntoViewId(getScrollIntoViewId(props.defaultValue)); - } - }, []); - - useEvent('measurement', () => { - // 组件如果内嵌在 slot 里, 一定会被渲染出来, 但是此时 cellHight 为 0 - // 此时需要重新计算 - if (!elementSize || elementSize.cellHight === 0) { - measurement(); - } - }); - - return { - elementSize, - markItems, - monthList, - headerState, - scrollIntoViewId, - }; -}; - -mountComponent(Calendar, { - defaultValue: null, - value: null, - selectionMode: 'range', - monthRange: defaultMonthRange(), - weekStartsOn: 'Sunday', - localeText: defaultLocaleText, - onFormatter: null, - changedScrollIntoView: null, -}); +); diff --git a/src/Calendar/props.ts b/src/Calendar/props.ts index 54891430c..e06386972 100644 --- a/src/Calendar/props.ts +++ b/src/Calendar/props.ts @@ -1,4 +1,5 @@ import { IBaseProps } from '../_util/base'; +import { defaultMonthRange } from './utils'; export interface CalendarDate { year: number; @@ -142,3 +143,14 @@ export interface ICalendarProps extends IBaseProps { currentValue: CalendarValue ) => Pick; } + +export const CalendarDefaultProps = { + defaultValue: null, + value: null, + selectionMode: 'range', + monthRange: defaultMonthRange(), + weekStartsOn: 'Sunday', + localeText: defaultLocaleText, + onFormatter: null, + changedScrollIntoView: null, +}; diff --git a/src/_util/component.ts b/src/_util/component.ts deleted file mode 100644 index 0983dce4c..000000000 --- a/src/_util/component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { alipayComponent, wechatComponent } from 'functional-mini/component'; - -function removeNullProps(props) { - const newProps = {}; - for (const key in props) { - if (props[key] !== null) { - newProps[key] = props[key]; - } - } - return newProps; -} - -export function mountComponent( - Hooks: (props: T) => unknown, - defaultProps: T -) { - /// #if WECHAT - Component( - wechatComponent(Hooks, mergeDefaultProps(defaultProps) as unknown as T, { - options: { - styleIsolation: 'shared', - multipleSlots: true, - virtualHost: true, - }, - }) - ); - /// #endif - - /// #if ALIPAY - Component( - alipayComponent(Hooks, removeNullProps(mergeDefaultProps(defaultProps))) - ); - /// #endif -} - -function mergeDefaultProps(defaultProps: Record = {}) { - return { - /// #if WECHAT - className: '', - style: '', - /// #endif - ...defaultProps, - }; -} diff --git a/src/_util/hooks/useComponentEvent.ts b/src/_util/hooks/useComponentEvent.ts deleted file mode 100644 index cdfb318fa..000000000 --- a/src/_util/hooks/useComponentEvent.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { useComponent } from 'functional-mini/component'; -import fmtEvent from '../fmtEvent'; -import { useEvent } from './useEvent'; - -export function useComponentEvent(props: T) { - const component = useComponent(); - const triggerEvent = useEvent( - (eventName: string, value: unknown, e?: any) => { - // 首字母大写,然后加上 on - - /// #if ALIPAY - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](value, fmtEvent(props, e)); - } - /// #endif - - /// #if WECHAT - component.triggerEvent(eventName.toLocaleLowerCase(), value); - /// #endif - } - ); - - const triggerEventValues = useEvent( - (eventName: string, values: any[], e?: any) => { - // 首字母大写,然后加上 on - - /// #if ALIPAY - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](...values, fmtEvent(props, e)); - } - /// #endif - - /// #if WECHAT - component.triggerEvent(eventName.toLocaleLowerCase(), values); - /// #endif - } - ); - const triggerEventOnly = useEvent((eventName: string, e?: any) => { - // 首字母大写,然后加上 on - - /// #if ALIPAY - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](fmtEvent(props, e)); - } - /// #endif - - /// #if WECHAT - component.triggerEvent(eventName.toLocaleLowerCase()); - /// #endif - }); - - const alipayForwardCatchEvent = useEvent((eventName: string, e: any) => { - // 首字母大写,然后加上 catch - - /// #if ALIPAY - const alipayCallbackName = - 'catch' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](fmtEvent(props, e)); - } - /// #endif - }); - - const alipayForwardEvent = useEvent((eventName: string, e: any) => { - // 首字母大写,然后加上 on - - /// #if ALIPAY - const alipayCallbackName = - 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); - - if (props[alipayCallbackName]) { - props[alipayCallbackName](fmtEvent(props, e)); - } - /// #endif - }); - - return { - triggerEvent, - triggerEventValues, - triggerEventOnly, - // 转发 catch 事件 - alipayForwardCatchEvent, - // 转发事件 - alipayForwardEvent, - }; -} diff --git a/src/_util/hooks/useEvent.ts b/src/_util/hooks/useEvent.ts deleted file mode 100644 index 665c6e811..000000000 --- a/src/_util/hooks/useEvent.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useEvent.ts - */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as React from 'functional-mini/compat'; - -// eslint-disable-next-line @typescript-eslint/ban-types -export function useEvent(callback: T): T { - const fnRef = React.useRef(); - fnRef.current = callback; - - const memoFn = React.useCallback( - ((...args: any) => fnRef.current?.(...args)) as any, - [] - ); - - return memoFn; -} diff --git a/src/_util/hooks/useHandleCustomEvent.ts b/src/_util/hooks/useHandleCustomEvent.ts deleted file mode 100644 index 6a2319ba7..000000000 --- a/src/_util/hooks/useHandleCustomEvent.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { useEvent } from 'functional-mini/component'; -import { platform } from '../platform'; - -export type EventHandler = (value: T, e: any) => void; - -export const useHandleCustomEvent = ( - eventName: string, - handler: EventHandler -) => { - useEvent( - eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (eventOrValue: any, alipayEvent?: any) => { - /// #if ALIPAY - if (platform() === 'alipay') { - return handler(eventOrValue, alipayEvent); - } - /// #endif - - /// #if WECHAT - return handler(eventOrValue.detail, eventOrValue); - /// #endif - } - ); -}; - -export type MultipleValueEventHandler = (...args: any[]) => void; - -export const useMultipleValueHandleCustomEvent = ( - eventName: string, - handler: MultipleValueEventHandler -) => { - useEvent( - eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (...args: any[]) => { - /// #if ALIPAY - if (platform() === 'alipay') { - return handler(...args); - } - /// #endif - - /// #if WECHAT - const firstArgs = args[0]; - if (Array.isArray(firstArgs.detail)) { - const wechatArgs = firstArgs.detail.concat(firstArgs); - return handler(...wechatArgs); - } else { - return handler([firstArgs.detail, firstArgs]); - } - /// #endif - } - ); -}; - -export type EventOnlyHandler = (e: any) => void; - -export const useHandleCustomEventOnly = ( - eventName: string, - handler: EventOnlyHandler -) => { - useEvent( - eventName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (event) => { - /// #if ALIPAY - if (platform() === 'alipay') { - return handler(event); - } - /// #endif - - /// #if WECHAT - return handler(event); - /// #endif - } - ); -}; diff --git a/src/_util/hooks/useInstanceBoundingClientRect.ts b/src/_util/hooks/useInstanceBoundingClientRect.ts deleted file mode 100644 index 467a457bb..000000000 --- a/src/_util/hooks/useInstanceBoundingClientRect.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useComponent } from 'functional-mini/component'; -import { getInstanceBoundingClientRect } from '../jsapi/get-instance-bounding-client-rect'; - -export const useInstanceBoundingClientRect = () => { - const instance = useComponent(); - function getInstance() { - if (instance.$id) { - return my; - } - return instance; - } - - async function getBoundingClientRectWithId(prefix: string) { - return await getInstanceBoundingClientRect( - getInstance(), - `${prefix}${instance.$id ? `-${instance.$id}` : ''}` - ); - } - async function getBoundingClientRect(query: string) { - return await getInstanceBoundingClientRect(getInstance(), query); - } - - async function getBoundingClientRectWithBuilder( - builder: (id: string) => string - ) { - return await getInstanceBoundingClientRect( - getInstance(), - builder(instance.$id ? `-${instance.$id}` : '') - ); - } - - return { - getBoundingClientRect, - getBoundingClientRectWithId, - getBoundingClientRectWithBuilder, - }; -}; diff --git a/src/_util/hooks/useLayoutEffect.ts b/src/_util/hooks/useLayoutEffect.ts deleted file mode 100644 index 896935268..000000000 --- a/src/_util/hooks/useLayoutEffect.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useLayoutEffect.ts - */ - -import * as React from 'functional-mini/compat'; - -/** - * Wrap `React.useLayoutEffect` which will not throw warning message in test env - */ -const useInternalLayoutEffect = React.useEffect; - -const useLayoutEffect = (callback: (mount: boolean) => void, deps?: any) => { - const firstMountRef = React.useRef(true); - - useInternalLayoutEffect(() => { - return callback(firstMountRef.current); - }, deps); - - // We tell react that first mount has passed - useInternalLayoutEffect(() => { - firstMountRef.current = false; - return () => { - firstMountRef.current = true; - }; - }, []); -}; - -export const useComponentUpdateEffect: typeof React.useEffect = ( - callback, - deps -) => { - useLayoutEffect((firstMount) => { - if (!firstMount) { - return callback(); - } - }, deps); -}; - -export default useLayoutEffect; diff --git a/src/_util/hooks/useMergedState.ts b/src/_util/hooks/useMergedState.ts deleted file mode 100644 index 37148ac85..000000000 --- a/src/_util/hooks/useMergedState.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useMergedState.ts - */ -import { useEvent } from './useEvent'; -import { useComponentUpdateEffect } from './useLayoutEffect'; -import { useSafeState as useState } from './useState'; -import { supportUndefinedProperty } from '../platform'; - -type Updater = ( - updater: T | ((origin: T) => T), - ignoreDestroy?: boolean -) => void; - -/** We only think `undefined` is empty */ -export function hasValue(value: any) { - if (supportUndefinedProperty()) { - return value !== undefined; - } - return value !== null && value !== undefined; -} - -/** - * Similar to `useState` but will use props value if provided. - * Note that internal use rc-util `useState` hook. - */ -export function useMergedState( - defaultStateValue: T | (() => T), - option?: { - defaultValue?: T | (() => T); - value?: T; - onChange?: (value: T, prevValue: T) => void; - postState?: (value: T) => T; - } -): [R, Updater] { - const { defaultValue, value, onChange, postState } = option || {}; - - // ======================= Init ======================= - const [innerValue, setInnerValue] = useState(() => { - if (hasValue(value)) { - return value; - } else if (hasValue(defaultValue)) { - return typeof defaultValue === 'function' - ? (defaultValue as any)() - : defaultValue; - } else { - return typeof defaultStateValue === 'function' - ? (defaultStateValue as any)() - : defaultStateValue; - } - }); - - const mergedValue = hasValue(value) ? value : innerValue; - const postMergedValue = postState ? postState(mergedValue) : mergedValue; - - // ====================== Change ====================== - const onChangeFn = useEvent(onChange); - - const [prevValue, setPrevValue] = useState<[T]>([mergedValue]); - - useComponentUpdateEffect(() => { - const prev = prevValue[0]; - if (innerValue !== prev) { - onChangeFn(innerValue, prev); - } - }, [prevValue]); - - // Sync value back to `undefined` when it from control to un-control - useComponentUpdateEffect(() => { - if (!hasValue(value)) { - setInnerValue(value); - } - }, [value]); - - // ====================== Update ====================== - const triggerChange: Updater = useEvent((updater, ignoreDestroy) => { - setInnerValue(updater, ignoreDestroy); - setPrevValue([mergedValue], ignoreDestroy); - }); - - return [postMergedValue as unknown as R, triggerChange]; -} diff --git a/src/_util/hooks/useMixState.ts b/src/_util/hooks/useMixState.ts deleted file mode 100644 index d558a71fd..000000000 --- a/src/_util/hooks/useMixState.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { useEvent } from './useEvent'; -import { useComponentUpdateEffect } from './useLayoutEffect'; -import { hasValue } from './useMergedState'; -import { useSafeState as useState } from './useState'; - -type Updater = ( - updater: T | ((old: T) => T), - ignoreDestroy?: boolean -) => void; - -export function useMixState( - defaultStateValue: T | (() => T), - option?: { - defaultValue?: T | (() => T); - value?: T; - postState?: ( - value: T, - option?: O - ) => { valid: true; value: T } | { valid: false }; - } -): [ - R, - { - isControlled: boolean; - triggerUpdater: (value: (old: T) => T, option?: O) => void; - update( - value: T, - option?: O - ): { changed: true; newValue: T } | { changed: false }; - } -] { - const { - defaultValue, - value, - postState = (v) => ({ valid: true, value: v }), - } = option || {}; - - // ======================= Init ======================= - const [innerValue, setInnerValue] = useState(() => { - let v; - if (hasValue(value)) { - v = value; - } else if (hasValue(defaultValue)) { - v = - typeof defaultValue === 'function' - ? (defaultValue as any)() - : defaultValue; - } else { - v = - typeof defaultStateValue === 'function' - ? (defaultStateValue as any)() - : defaultStateValue; - } - const state = postState(v); - if (state.valid) { - return state.value; - } - }); - - const state = postState(value); - const merge = hasValue(value) && state.valid ? state.value : innerValue; - - useComponentUpdateEffect(() => { - const state = postState(value); - if (state.valid) { - setInnerValue(state.value); - } - }, [value]); - - const isControlled = hasValue(value); - const triggerChange: Updater = useEvent((newState, ignoreDestroy) => { - setInnerValue(newState, ignoreDestroy); - }); - - const triggerUpdate = useEvent((value, option) => { - const state = postState(value, option); - if (state.valid && state.value !== innerValue) { - triggerChange(state.value); - return { changed: true, newValue: state.value }; - } - return { changed: false }; - }); - - const triggerUpdater: (value: (old: T) => T, option?: O) => void = useEvent( - (getValue, option) => { - if (isControlled) { - getValue(merge); - } else { - triggerChange((old: T): T => { - const newValue = getValue(old); - const state = postState(newValue, option); - if (state.valid && state.value !== innerValue) { - return state.value; - } - return old; - }); - } - } - ); - - return [ - merge as unknown as R, - { - isControlled, - update: triggerUpdate as any, - triggerUpdater, - }, - ]; -} diff --git a/src/_util/hooks/useReportRef.ts b/src/_util/hooks/useReportRef.ts deleted file mode 100644 index 1cb16e980..000000000 --- a/src/_util/hooks/useReportRef.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useComponent, useEffect } from 'functional-mini/component'; - -export const triggerRefEvent = () => { - /// #if WECHAT - const component = useComponent(); - useEffect(() => { - component.triggerEvent('ref', component); - }, []); - /// #endif -}; diff --git a/src/_util/hooks/useState.ts b/src/_util/hooks/useState.ts deleted file mode 100644 index 2c80b46b7..000000000 --- a/src/_util/hooks/useState.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * copy from https://github.com/react-component/util/blob/9d5cb8946da29e690bead78b2c251da6f7cbd0eb/src/hooks/useState.ts - */ - -import * as React from 'functional-mini/compat'; - -type Updater = T | ((prevValue: T) => T); - -export type SetState = ( - nextValue: Updater, - /** - * Will not update state when destroyed. - * Developer should make sure this is safe to ignore. - */ - ignoreDestroy?: boolean -) => void; - -/** - * Same as React.useState but `setState` accept `ignoreDestroy` param to not to setState after destroyed. - * We do not make this auto is to avoid real memory leak. - * Developer should confirm it's safe to ignore themselves. - */ -export function useSafeState( - defaultValue?: T | (() => T) -): [T, SetState] { - const destroyRef = React.useRef(false); - const [value, setValue] = React.useState(defaultValue); - - React.useEffect(() => { - destroyRef.current = false; - - return () => { - destroyRef.current = true; - }; - }, []); - - function safeSetState(updater: Updater, ignoreDestroy?: boolean) { - if (ignoreDestroy && destroyRef.current) { - return; - } - - setValue(updater); - } - - return [value, safeSetState]; -} diff --git a/src/_util/triggerComonentEvent.ts b/src/_util/triggerComonentEvent.ts deleted file mode 100644 index a2c169cbe..000000000 --- a/src/_util/triggerComonentEvent.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function triggerComponentEvent(instance, propsName, event) { - /// #if WECHAT - instance.triggerEvent(propsName, event.detail); - /// #endif - - /// #if ALIPAY - if (instance.props[propsName]) { - instance.props[propsName](event); - } - /// #endif -} diff --git a/tests/alipay/_util/__tests__/useHandleCustomEvent.spec.ts b/tests/alipay/_util/__tests__/useHandleCustomEvent.spec.ts deleted file mode 100644 index 535d5e94c..000000000 --- a/tests/alipay/_util/__tests__/useHandleCustomEvent.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { useEvent, useState, alipayComponent } from 'functional-mini/component'; -import { sleep, createInstance } from '../../../utils'; -import { expect, it } from 'vitest'; -import { - useHandleCustomEvent, - useMultipleValueHandleCustomEvent, - useHandleCustomEventOnly, -} from 'compiled-alipay/_util/hooks/useHandleCustomEvent'; -import fmtEvent from 'compiled-alipay/_util/fmtEvent'; - -const testMy = { - canIUse() { - return true; - }, -}; - -it('test useHandleCustomEvent', async () => { - const Test = () => { - const [count, setCount] = useState(1); - useHandleCustomEvent('handleEvent', (value, event) => { - return { - value, - event, - count, - }; - }); - useEvent('updateCount', function (v) { - setCount(v); - return 'ok'; - }); - return { - count, - }; - }; - - const componentOptions = alipayComponent(Test, {}); - const instance = createInstance(componentOptions as any, {}, testMy); - expect( - instance.callMethod('handleEvent', 2, fmtEvent({ 'data-a': 1 })).count - ).toBe(1); - expect(instance.callMethod('updateCount', 10)).toEqual('ok'); - await sleep(20); - expect(instance.getData().count).toEqual(10); - expect( - instance.callMethod('handleEvent', 2, fmtEvent({ 'data-a': 1 })) - ).toEqual({ - value: 2, - event: { - currentTarget: { dataset: { 'a': 1 } }, - target: { dataset: { 'a': 1 }, targetDataset: { 'a': 1 } }, - }, - count: 10, - }); - expect(instance.callMethod('updateCount', 30)).toEqual('ok'); - await sleep(20); - expect(instance.getData().count).toEqual(30); -}); - -it('test useMultipleValueHandleCustomEvent', async () => { - const Test = () => { - useMultipleValueHandleCustomEvent('handleEvent', (...args) => { - return { - args, - }; - }); - return {}; - }; - - const componentOptions = alipayComponent(Test, {}); - const instance = createInstance(componentOptions as any, {}, testMy); - expect( - instance.callMethod('handleEvent', 1, 2, fmtEvent({ 'data-a': 1 })) - ).toEqual({ - args: [ - 1, - 2, - { - currentTarget: { dataset: { 'a': 1 } }, - target: { dataset: { 'a': 1 }, targetDataset: { 'a': 1 } }, - }, - ], - }); - - expect(instance.callMethod('handleEvent')).toEqual({ - args: [], - }); - - expect(instance.callMethod('handleEvent', fmtEvent({ 'data-a': 1 }))).toEqual( - { - args: [ - { - currentTarget: { dataset: { 'a': 1 } }, - target: { dataset: { 'a': 1 }, targetDataset: { 'a': 1 } }, - }, - ], - } - ); -}); - -it('test useHandleCustomEventOnly', async () => { - const Test = () => { - useHandleCustomEventOnly('handleEvent', (...args) => { - return { - args, - }; - }); - return {}; - }; - - const componentOptions = alipayComponent(Test, {}) as any; - const instance = createInstance(componentOptions, {}, testMy); - expect(instance.callMethod('handleEvent', fmtEvent({ 'data-a': 1 }))).toEqual( - { - args: [ - { - currentTarget: { dataset: { 'a': 1 } }, - target: { dataset: { 'a': 1 }, targetDataset: { 'a': 1 } }, - }, - ], - } - ); -}); diff --git a/vite.config.ts b/vite.config.ts index 726f1078d..1174da00c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,11 +10,6 @@ export default defineConfig({ }, }, test: { - server: { - deps: { - inline: [/functional-mini/], - }, - }, watch: true, globals: true, setupFiles: ['./tests/setup.ts'],