UNPKG

antd-mini

Version:

antd-mini 是支付宝小程序 UI 组件库,遵循 Ant Design 规范。

283 lines (244 loc) 6.77 kB
import fmtEvent from './fmtEvent'; function removeNullProps(props) { const newProps = {}; for (const key in props) { if (props[key] !== null) { newProps[key] = props[key]; } } return newProps; } function buildProperties(props) { const newProperties = {}; for (const key in props) { let type = null; switch (typeof props[key]) { case 'string': type = String; break; case 'number': type = Number; break; case 'boolean': type = Boolean; break; } newProperties[key] = { type, value: props[key], }; } return newProperties; } function mergeDefaultProps(defaultProps: Record<string, any> = {}) { return { ...defaultProps, }; } type ComponentInstance<Props, Methods, Data, Mixins, InstanceMethods> = unknown; // 增加store使用的版本 interface IComponentOptions { // TODO: 需要考虑没有启用 Component2 的情况 onInit?: () => void; didUnmount?: () => void; } type ExtendedInstanceMethods = Partial<IComponentOptions> & Record<string, any>; export const ComponentWithSignalStoreImpl = < S, M extends Record<string, (o: { store: S }) => unknown>, Props, Methods = unknown, Data = unknown, Mixins = unknown, InstanceMethods extends ExtendedInstanceMethods = ExtendedInstanceMethods >({ storeOptions, props: defaultProps, methods, data, mixins, ...instanceMethods }: { storeOptions?: TStoreOptions<S, M>; props?: Props; methods?: Methods; data?: Data; mixins?: Mixins & any; } & InstanceMethods) => { const storeBinder = new StoreBinder(storeOptions); const defaultOnInit = function () { storeBinder.init(this as unknown as TStoreInitOptions<S>); }; const instanceMethodsCopy: ExtendedInstanceMethods = { ...instanceMethods }; // 确保 instanceMethods 存在 // 备份原有的 onInit 和 didUnmount 方法 const onInitBackup = instanceMethodsCopy.onInit || (() => {}); const onDidUnmountBackup = instanceMethodsCopy.didUnmount || (() => {}); instanceMethodsCopy.onInit = function () { defaultOnInit.call(this); if (onInitBackup) { onInitBackup.call(this); } }; instanceMethodsCopy.didUnmount = function () { if (onDidUnmountBackup) { onDidUnmountBackup.call(this); } storeBinder.dispose(); }; // 这里确保 instanceMethodsCopy.onInit 被正确执行 if (!instanceMethodsCopy.onInit) { instanceMethodsCopy.onInit = defaultOnInit; } Component({ props: removeNullProps(mergeDefaultProps(defaultProps)), methods, mixins: mixins || [], data, ...(instanceMethodsCopy || {}), }); }; type TMapState<S> = Record<string, (o: { store: S }) => unknown>; // 这里类型直接抄了 PageWithAnyStore,但其实对于 antd-mini 的场景,store 和 updateHook 都可以写死而不需要使用时自定义 export type TStoreOptions<S, M extends TMapState<S>> = { /** * store 的创建器,因为页面会有多实例,所以 store 必须每个页面实例单独创建一次 * 如果你非要多个实例共用一个 store,那你可以 const store = new Store(); store: => store; */ store: () => S; /** * store 数据更新后的 listener,通过它来触发向页面数据的同步,返回值是一个 dispose 函数。 * 在 mobx 是 autorun、redux 是 subscribe、你要不在意性能,setInterval 也可以 */ updateHook: (fn: () => void) => () => void; /** * store 数据到页面 data 的映射关系 */ mapState: M; }; export type TStoreInitOptions<S> = { setData: (o: Record<string, unknown>, callback?: () => void) => void; $store?: S; }; export class StoreBinder<S, M extends TMapState<S>> { private disposeStore?: () => void = undefined; constructor(private storeOptions: TStoreOptions<S, M>) {} /** * 绑定和 store 的关系 */ init(theThis: TStoreInitOptions<S>) { const store = this.storeOptions.store(); const disposes = Object.keys(this.storeOptions.mapState).map((key) => { return this.storeOptions.updateHook(() => { theThis.setData({ [key]: this.storeOptions.mapState[key]({ store }), }); }); }); theThis.$store = store; this.disposeStore = () => disposes.forEach((d) => d()); } /** * 释放和 store 的关系 */ dispose() { if (this.disposeStore) { this.disposeStore(); } } } function ComponentImpl< Props, Data = unknown, Methods = unknown, Mixins = unknown, InstanceMethods = unknown >({ props: defaultProps, data, methods, mixins, ...instanceMethods }: { props?: Props; data?: Data; methods?: Methods; mixins?: Mixins & any; } & InstanceMethods) { Component({ props: removeNullProps(mergeDefaultProps(defaultProps)), methods, mixins: mixins || [], data, ...instanceMethods, }); } export interface IPlatformEvent { currentTarget: { dataset: Record<string, unknown>; }; target: { dataset: Record<string, unknown>; }; } export function triggerEvent( instance: any, eventName: string, value: unknown, e?: any ) { // 首字母大写,然后加上 on const alipayCallbackName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); const props = instance.props; if (props[alipayCallbackName]) { props[alipayCallbackName](value, fmtEvent(props, e)); } } export function triggerEventOnly(instance: any, eventName: string, e?: any) { // 首字母大写,然后加上 on const alipayCallbackName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); const props = instance.props; if (props[alipayCallbackName]) { props[alipayCallbackName](fmtEvent(props, e)); } } export function triggerEventValues( instance: any, eventName: string, values: any[], e?: any ) { // 首字母大写,然后加上 on const alipayCallbackName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); const props = instance.props; if (props[alipayCallbackName]) { props[alipayCallbackName](...values, fmtEvent(props, e)); } } export function triggerCatchEvent(instance: any, eventName: string, e?: any) { const props = instance.props; if (props[eventName]) { props[eventName](fmtEvent(props, e)); } } export function getValueFromProps(instance: any, propName?: string | string[]) { let value; const props = instance.props; if (!propName) { return props; } if (typeof propName === 'string') { value = props[propName]; } if (Array.isArray(propName)) { value = propName.map((name) => props[name]); } return value; } export { ComponentWithSignalStoreImpl as ComponentWithSignalStore, ComponentImpl as Component, };