diff --git a/packages/mini-demo/order.json b/packages/mini-demo/order.json index 8f23290e8..146e93683 100644 --- a/packages/mini-demo/order.json +++ b/packages/mini-demo/order.json @@ -1 +1 @@ -{"input":{"AutoHeight":"6","Basic":"1","Clearable":"2","Disabled":"5","Native":"3","ReadOnly":"4","ShowLength":"7","Vertical":"8","Readonly":"4","Disable":0},"swipe-action":{"Basic":0}} +{"input":{"AutoHeight":"6","Basic":"1","Clearable":"2","Disabled":"5","Native":"3","ReadOnly":"4","ShowLength":"7","Vertical":"8","Readonly":"4","Disable":0},"swipe-action":{"Basic":0},"modal":{"Basic":"1","Button":"2","Alert":"3","Confrim":"3"},"loading":{"Basic":0,"Spinner":0}} diff --git a/packages/mini-demo/src/app.config.ts b/packages/mini-demo/src/app.config.ts index df9ea8bc8..0800d7875 100644 --- a/packages/mini-demo/src/app.config.ts +++ b/packages/mini-demo/src/app.config.ts @@ -8,6 +8,8 @@ export default defineAppConfig({ 'pages/tabs/index', 'pages/input/index', 'pages/swipe-action/index', + 'pages/modal/index', + 'pages/loading/index', ], window: { backgroundTextStyle: 'light', diff --git a/packages/mini-demo/src/app.scss b/packages/mini-demo/src/app.scss index c2d08aa0e..9e84a282b 100644 --- a/packages/mini-demo/src/app.scss +++ b/packages/mini-demo/src/app.scss @@ -1,3 +1,7 @@ page { height: 100%; } + +view { + box-sizing: border-box; +} diff --git a/packages/mini-demo/src/pages/loading/component/basic.tsx b/packages/mini-demo/src/pages/loading/component/basic.tsx new file mode 100644 index 000000000..9b84bee8d --- /dev/null +++ b/packages/mini-demo/src/pages/loading/component/basic.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { List, Loading } from 'zarm/mini'; + +const Demo =() => { + return ( + + } /> + } /> + + ); +} + +export default Demo; \ No newline at end of file diff --git a/packages/mini-demo/src/pages/loading/component/spinner.tsx b/packages/mini-demo/src/pages/loading/component/spinner.tsx new file mode 100644 index 000000000..f47cd713b --- /dev/null +++ b/packages/mini-demo/src/pages/loading/component/spinner.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { List, Loading } from 'zarm/mini'; + +const Demo =() => { + return ( + + } /> + } /> + + ); +} + +export default Demo; \ No newline at end of file diff --git a/packages/mini-demo/src/pages/loading/index.config.ts b/packages/mini-demo/src/pages/loading/index.config.ts new file mode 100644 index 000000000..f740c6423 --- /dev/null +++ b/packages/mini-demo/src/pages/loading/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: 'Loading' +}) \ No newline at end of file diff --git a/packages/mini-demo/src/pages/loading/index.scss b/packages/mini-demo/src/pages/loading/index.scss new file mode 100644 index 000000000..c2d08aa0e --- /dev/null +++ b/packages/mini-demo/src/pages/loading/index.scss @@ -0,0 +1,3 @@ +page { + height: 100%; +} diff --git a/packages/mini-demo/src/pages/loading/index.tsx b/packages/mini-demo/src/pages/loading/index.tsx new file mode 100644 index 000000000..d554ee087 --- /dev/null +++ b/packages/mini-demo/src/pages/loading/index.tsx @@ -0,0 +1,15 @@ + +import * as React from 'react'; +import Basic from './component/basic'; +import Spinner from './component/spinner'; + +import './index.scss'; + +export default () => { + return ( + <> + + + + ) +} \ No newline at end of file diff --git a/packages/mini-demo/src/pages/modal/component/alert.tsx b/packages/mini-demo/src/pages/modal/component/alert.tsx new file mode 100644 index 000000000..f32e5807d --- /dev/null +++ b/packages/mini-demo/src/pages/modal/component/alert.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { List, Button, Modal, Panel } from 'zarm/mini'; + +/* order: 3 */ + +const Demo = () => { + return ( + + + { + Modal.alert({ + id: 'alert', + className: 'test', + title: '警告框标题', + content: '这里是警告框的内容部分', + onConfirm: () => { + console.log('点击确认'); + }, + }); + }} + > + 开启 + + } + /> + { + Modal.alert({ + id: 'alert', + title: '警告框标题', + content: '这里是警告框的内容部分,点击关闭按钮,将触发 Promise 关闭警告框', + onConfirm: async () => { + await new Promise((resolve) => setTimeout(resolve, 3000)); + // Toast.show({ content: '提交成功' }); + }, + }); + }} + > + 开启 + + } + /> + + + + ); +}; + +export default Demo; \ No newline at end of file diff --git a/packages/mini-demo/src/pages/modal/component/basic.tsx b/packages/mini-demo/src/pages/modal/component/basic.tsx new file mode 100644 index 000000000..2f7718c33 --- /dev/null +++ b/packages/mini-demo/src/pages/modal/component/basic.tsx @@ -0,0 +1,220 @@ +import React, { useReducer } from 'react'; +import { Modal, List, Button, Panel } from 'zarm/mini'; +import { View } from '@tarojs/components'; + +/* order: 1 */ + +const initState = { + normal: { + visible: false, + }, + hasFooter: { + visible: false, + }, + closable: { + visible: false, + }, + onlyBody: { + visible: false, + }, + animation: { + visible: false, + animationType: 'fade', + }, + customContainer: { + visible: false, + }, + overlength: { + visible: false, + }, +}; + +const reducer = (state, action) => { + const { type, key, animationType } = action; + + switch (type) { + case 'visible': + return { + ...state, + [key]: { + ...state[key], + visible: !state[key].visible, + }, + }; + + case 'animation': + return { + ...state, + [key]: { + ...state[key], + animationType, + }, + }; + + default: + } +}; + +const Demo = () => { + const [state, dispatch] = useReducer(reducer, initState); + + const toggle = (key) => dispatch({ type: 'visible', key }); + + return ( + + + toggle('normal')}> + 开启 + + } + /> + toggle('hasFooter')}> + 开启 + + } + /> + toggle('closable')}> + 开启 + + } + /> + toggle('onlyBody')}> + 开启 + + } + /> + {/* toggle('animation')}> + 开启 + + } + > */} + {/* data && `${data.label}(${data.value})`} + displayRender={(selected) => selected.map((item) => item && item.label)} + onConfirm={(selected) => { + dispatch({ + type: 'animation', + key: 'animation', + animationType: selected[0], + }); + }} + /> + */} + {/* toggle('customContainer')}> + 开启 + + } + > + 挂载到指定 DOM 节点 + */} + toggle('overlength')}> + 开启 + + } + > + 超长内容 + + + + toggle('normal')}> + 模态框内容 + + + toggle('hasFooter')}> + 确定 + + } + > + 模态框内容 + + + toggle('closable')} + > + 点击遮罩层关闭 + + + toggle('onlyBody')}> + 无头部,无底部 + + + toggle('animation')} + > + + 当前使用的动画类型animationType:{state.animation.animationType} + + + + {/* toggle('customContainer')} + mountContainer={() => myRef.current} + > + 挂载到指定dom节点 + */} + + toggle('overlength')} + maskClosable + > + {Array.from(Array(100).fill(0)).map((_, index) => ( + + 模态框内容 + + ))} + + + ); +}; + +export default Demo; \ No newline at end of file diff --git a/packages/zarm/src/modal/demo/button.mini.tsx b/packages/zarm/src/modal/demo/button.mini.tsx new file mode 100644 index 000000000..dbd31370d --- /dev/null +++ b/packages/zarm/src/modal/demo/button.mini.tsx @@ -0,0 +1,69 @@ +import React, { useState } from 'react'; +import { Modal, List, Button, Panel } from 'zarm/mini'; + +/* order: 2 */ + +const Demo = () => { + const [visible, setVisible] = useState(false); + + return ( + + + setVisible(true)}> + 开启 + + } + /> + + + { + switch (action.key) { + case 'cancel': + setVisible(false); + break; + default: + // 模拟异步操作 + await new Promise((resolve) => setTimeout(resolve, 3000)); + setVisible(false); + } + console.log(action); + }} + > + 模态框内容 + + + ); +}; + +export default Demo; \ No newline at end of file diff --git a/packages/zarm/src/modal/demo/confrim.mini.tsx b/packages/zarm/src/modal/demo/confrim.mini.tsx new file mode 100644 index 000000000..91b69fed3 --- /dev/null +++ b/packages/zarm/src/modal/demo/confrim.mini.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { List, Button, Modal, Panel } from 'zarm/mini'; + +/* order: 3 */ + +const Demo = () => { + return ( + + + { + Modal.confirm({ + id: 'confirm', + title: '确认信息', + content: '这里是确认框的内容部分', + onCancel: () => { + console.log('点击cancel'); + }, + onConfirm: () => { + console.log('点击ok'); + }, + }); + }} + > + 开启 + + } + /> + { + Modal.confirm({ + id: 'confirm', + title: '确定要删除吗?', + content: '这里是确认框的内容部分,点击确定按钮,将触发 Promise 关闭确认框', + onConfirm: async () => { + await new Promise((resolve) => setTimeout(resolve, 3000)); + // Toast.show({ content: '提交成功' }); + }, + }); + }} + > + 开启 + + } + /> + + + + ); +}; + +export default Demo; \ No newline at end of file diff --git a/packages/zarm/src/modal/index.mini.ts b/packages/zarm/src/modal/index.mini.ts new file mode 100644 index 000000000..2287a49e5 --- /dev/null +++ b/packages/zarm/src/modal/index.mini.ts @@ -0,0 +1,18 @@ +import attachPropertiesToComponent from '../utils/attachPropertiesToComponent'; +import { alert } from './Alert.mini'; +import { confirm } from './Confirm.mini'; +import { clear, show } from './methods.mini'; +import Modal from './Modal.mini'; + +export type { ModalAlertProps } from './Alert.mini'; +export type { ModalConfirmProps } from './Confirm.mini'; +export type { ModalShowProps } from './methods'; +export type { ModalProps } from './Modal.mini'; +export type { ModalCssVars } from './interface'; + +export default attachPropertiesToComponent(Modal, { + show, + clear, + alert, + confirm, +}); \ No newline at end of file diff --git a/packages/zarm/src/modal/index.ts b/packages/zarm/src/modal/index.ts index 7037c5ef0..1576e9bdf 100644 --- a/packages/zarm/src/modal/index.ts +++ b/packages/zarm/src/modal/index.ts @@ -7,7 +7,8 @@ import Modal from './Modal'; export type { ModalAlertProps } from './Alert'; export type { ModalConfirmProps } from './Confirm'; export type { ModalShowProps } from './methods'; -export type { ModalCssVars, ModalProps } from './Modal'; +export type { ModalProps } from './Modal'; +export type { ModalCssVars } from './interface'; export default attachPropertiesToComponent(Modal, { show, diff --git a/packages/zarm/src/modal/interface.ts b/packages/zarm/src/modal/interface.ts index c1a09ac41..3940d3c88 100644 --- a/packages/zarm/src/modal/interface.ts +++ b/packages/zarm/src/modal/interface.ts @@ -19,3 +19,25 @@ export interface BaseModalActionProps { bold?: boolean; onClick?: () => void; } + +export interface ModalCssVars { + '--background'?: React.CSSProperties['background']; + '--border-radius'?: React.CSSProperties['borderRadius']; + '--shadow'?: React.CSSProperties['boxShadow']; + '--title-font-size'?: React.CSSProperties['fontSize']; + '--title-font-weight'?: React.CSSProperties['fontWeight']; + '--title-text-color'?: React.CSSProperties['color']; + '--close-size'?: React.CSSProperties['fontSize']; + '--close-color'?: React.CSSProperties['color']; + '--close-active-color'?: React.CSSProperties['color']; + '--body-font-size'?: React.CSSProperties['fontSize']; + '--body-text-color'?: React.CSSProperties['color']; + '--body-padding'?: React.CSSProperties['padding']; + '--button-height'?: React.CSSProperties['height']; + '--button-font-size'?: React.CSSProperties['fontSize']; + '--button-font-weight'?: React.CSSProperties['fontWeight']; + '--button-text-color'?: React.CSSProperties['color']; + '--button-background'?: React.CSSProperties['background']; + '--button-active-background'?: React.CSSProperties['background']; + '--button-disabled-opacity'?: React.CSSProperties['opacity']; +} \ No newline at end of file diff --git a/packages/zarm/src/modal/methods.mini.tsx b/packages/zarm/src/modal/methods.mini.tsx new file mode 100644 index 000000000..9e9a56c07 --- /dev/null +++ b/packages/zarm/src/modal/methods.mini.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { ImperativeHandler } from '../utils/dom'; +import { ModalProps } from './Modal.mini'; +import { getCustomEventsPath, customEvents } from '../utils/dom/dom.mini'; + +export interface ModalShowProps + extends Omit { + content?: React.ReactNode; + id: string; +} + +export type ModalShowHandler = Pick; + +const closeFn = new Set<() => void>(); + +export const show = (props: ModalShowProps) => { + const path = getCustomEventsPath(props.id); + + customEvents.trigger(path, { + ...props, + visible: true, + }); + + const close = () => { + customEvents.trigger(path, { + ...props, + visible: false, + }); + } + closeFn.add(close); + return { + close, + }; +}; + +export const clear = () => { + closeFn.forEach((close) => close()); +}; diff --git a/packages/zarm/src/modal/style/index.mini.scss b/packages/zarm/src/modal/style/index.mini.scss new file mode 100644 index 000000000..f7e6e5afd --- /dev/null +++ b/packages/zarm/src/modal/style/index.mini.scss @@ -0,0 +1,2 @@ +@import '../../style/core/index'; +@import 'component'; diff --git a/packages/zarm/src/modal/style/index.mini.ts b/packages/zarm/src/modal/style/index.mini.ts new file mode 100644 index 000000000..240bd4650 --- /dev/null +++ b/packages/zarm/src/modal/style/index.mini.ts @@ -0,0 +1,4 @@ +import '../../loading/style/index.mini'; +import '../../popup/style'; +import '../../style'; +import './index.scss'; \ No newline at end of file diff --git a/packages/zarm/src/popup/Popup.tsx b/packages/zarm/src/popup/Popup.tsx index dd127dc8b..f3f0cfc1b 100644 --- a/packages/zarm/src/popup/Popup.tsx +++ b/packages/zarm/src/popup/Popup.tsx @@ -55,10 +55,10 @@ const Popup = React.forwardRef((props, ref) => { }, []); const handleClick = (event: React.MouseEvent) => { - if (nodeRef.current !== event.target && nodeRef.current.contains(event.target as HTMLElement)) { + if (nodeRef.current !== event.target && nodeRef?.current?.contains?.(event.target as HTMLElement)) { return; } - maskRef.current?.click(); + onMaskClick?.(); }; const transitionName = animationType ?? TRANSITION_NAMES[direction!]; diff --git a/packages/zarm/src/utils/dom/dom.mini.ts b/packages/zarm/src/utils/dom/dom.mini.ts index c7e26d137..06bd768c2 100644 --- a/packages/zarm/src/utils/dom/dom.mini.ts +++ b/packages/zarm/src/utils/dom/dom.mini.ts @@ -1,8 +1,9 @@ -import Taro from '@tarojs/taro'; +import { useEffect } from 'react' +import { Events, getCurrentInstance, createSelectorQuery } from '@tarojs/taro' export const getRect = (id): Promise => { return new Promise((resolve) => { - Taro.createSelectorQuery() + createSelectorQuery() .select(`#${id}`) .boundingClientRect() .exec(([rect]) => { @@ -15,7 +16,7 @@ export const getRects = ( query: string, ): Promise => { return new Promise((resolve) => { - Taro.createSelectorQuery() + createSelectorQuery() .selectAll(query) .boundingClientRect() .exec(([rect]) => { @@ -23,3 +24,31 @@ export const getRects = ( }); }); }; + +export const customEvents = new Events(); + +export function getCustomEventsPath(selector?: string) { + selector = selector || '' + const path = getCurrentInstance().router?.path; + return path ? `${path}_${selector}` : selector; +} + +export function useCustomEvent(selector: string, callback: any) { + const path = getCustomEventsPath(selector); + useEffect(() => { + customEvents.on(path, callback); + return () => { + customEvents.off(path); + } + }, []); + + const trigger = (args: T) => { + customEvents.trigger(path, args); + } + + const off = () => { + customEvents.off(path); + } + + return [trigger, off]; +} diff --git a/site/.dumirc.ts b/site/.dumirc.ts index 13a0c6d1d..4d50f1f33 100644 --- a/site/.dumirc.ts +++ b/site/.dumirc.ts @@ -16,6 +16,7 @@ export default defineConfig({ 'zarm/lib': path.resolve(__dirname, '../packages/zarm/src'), 'zarm/es': path.resolve(__dirname, '../packages/zarm/src'), zarm: require.resolve('../packages/zarm/src/index.ts'), + '@tarojs/taro': '@tarojs/taro-h5', ['@tarojs/components$']: '@tarojs/components/lib/react', }, extraBabelPlugins: [ @@ -48,4 +49,15 @@ export default defineConfig({ autoAlias: {}, prefersColor: { default: 'auto' }, }, + define: { + 'process.env.TARO_ENV': JSON.stringify('h5'), + ENABLE_INNER_HTML: JSON.stringify(false), + ENABLE_ADJACENT_HTML: JSON.stringify(false), + ENABLE_SIZE_APIS: JSON.stringify(false), + ENABLE_TEMPLATE_CONTENT: JSON.stringify(false), + ENABLE_CLONE_NODE: JSON.stringify(false), + ENABLE_CONTAINS: JSON.stringify(false), + ENABLE_MUTATION_OBSERVER: JSON.stringify(false), + DEPRECATED_ADAPTER_COMPONENT: JSON.stringify(false), + } });