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')}>
+ 开启
+
+ }
+ > */}
+ {/* */}
+ {/* 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/mini-demo/src/pages/modal/component/button.tsx b/packages/mini-demo/src/pages/modal/component/button.tsx
new file mode 100644
index 000000000..dbd31370d
--- /dev/null
+++ b/packages/mini-demo/src/pages/modal/component/button.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/mini-demo/src/pages/modal/component/confrim.tsx b/packages/mini-demo/src/pages/modal/component/confrim.tsx
new file mode 100644
index 000000000..91b69fed3
--- /dev/null
+++ b/packages/mini-demo/src/pages/modal/component/confrim.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/mini-demo/src/pages/modal/index.config.ts b/packages/mini-demo/src/pages/modal/index.config.ts
new file mode 100644
index 000000000..8ae8b1f73
--- /dev/null
+++ b/packages/mini-demo/src/pages/modal/index.config.ts
@@ -0,0 +1,3 @@
+export default definePageConfig({
+ navigationBarTitleText: 'Modal'
+})
diff --git a/packages/mini-demo/src/pages/modal/index.scss b/packages/mini-demo/src/pages/modal/index.scss
new file mode 100644
index 000000000..c2d08aa0e
--- /dev/null
+++ b/packages/mini-demo/src/pages/modal/index.scss
@@ -0,0 +1,3 @@
+page {
+ height: 100%;
+}
diff --git a/packages/mini-demo/src/pages/modal/index.tsx b/packages/mini-demo/src/pages/modal/index.tsx
new file mode 100644
index 000000000..4e47dbad2
--- /dev/null
+++ b/packages/mini-demo/src/pages/modal/index.tsx
@@ -0,0 +1,19 @@
+
+import * as React from 'react';
+import Alert from './component/alert';
+import Basic from './component/basic';
+import Button from './component/button';
+import Confrim from './component/confrim';
+
+import './index.scss';
+
+export default () => {
+ return (
+ <>
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/packages/mini-demo/src/site.ts b/packages/mini-demo/src/site.ts
index 4a1f85f37..7f662c80b 100644
--- a/packages/mini-demo/src/site.ts
+++ b/packages/mini-demo/src/site.ts
@@ -72,11 +72,21 @@ const siteMap = {
// name: '动作面板',
// page: '/pages/action-sheet/index',
// },
+ {
+ key: 'Loading',
+ name: '加载中',
+ page: '/pages/loading/index',
+ },
{
key: 'Popup',
name: '动作面板',
page: '/pages/popup/index',
},
+ {
+ key: 'Modal',
+ name: '模态框',
+ page: '/pages/modal/index',
+ },
{
key: 'Collapse',
name: '折叠面板',
diff --git a/packages/zarm/src/loading/demo/basic.mini.tsx b/packages/zarm/src/loading/demo/basic.mini.tsx
new file mode 100644
index 000000000..9b84bee8d
--- /dev/null
+++ b/packages/zarm/src/loading/demo/basic.mini.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/zarm/src/loading/demo/spinner.mini.tsx b/packages/zarm/src/loading/demo/spinner.mini.tsx
new file mode 100644
index 000000000..f47cd713b
--- /dev/null
+++ b/packages/zarm/src/loading/demo/spinner.mini.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/zarm/src/loading/index.mini.ts b/packages/zarm/src/loading/index.mini.ts
new file mode 100644
index 000000000..8c969977b
--- /dev/null
+++ b/packages/zarm/src/loading/index.mini.ts
@@ -0,0 +1,6 @@
+import Loading from './loading.mini';
+
+export type { LoadingProps } from './loading.mini';
+export type { LoadingCssVars } from './interface';
+
+export default Loading;
\ No newline at end of file
diff --git a/packages/zarm/src/loading/index.tsx b/packages/zarm/src/loading/index.tsx
index 2d9a5e565..ce43291c7 100644
--- a/packages/zarm/src/loading/index.tsx
+++ b/packages/zarm/src/loading/index.tsx
@@ -2,18 +2,9 @@ import { createBEM } from '@zarm-design/bem';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import type { HTMLProps } from '../utils/utilityTypes';
-import type { BaseLoadingProps } from './interface';
-
-export interface LoadingCssVars {
- '--size'?: React.CSSProperties['width' | 'height'];
- '--size-large'?: React.CSSProperties['width' | 'height'];
- '--stroke-color'?: React.CSSProperties['stroke'];
- '--stroke-active-color'?: React.CSSProperties['stroke'];
- '--spinner-item-color'?: React.CSSProperties['color'];
- '--spinner-item-width'?: React.CSSProperties['width'];
- '--spinner-item-height'?: React.CSSProperties['height'];
- '--spinner-item-border-radius'?: React.CSSProperties['borderRadius'];
-}
+import type { BaseLoadingProps, LoadingCssVars } from './interface';
+
+export type { LoadingCssVars } from './interface';
export type LoadingProps = BaseLoadingProps &
HTMLProps & {
diff --git a/packages/zarm/src/loading/interface.ts b/packages/zarm/src/loading/interface.ts
index d8a57d7ad..9e1923a2a 100644
--- a/packages/zarm/src/loading/interface.ts
+++ b/packages/zarm/src/loading/interface.ts
@@ -5,3 +5,14 @@ export interface BaseLoadingProps {
type?: 'circular' | 'spinner';
loading?: boolean;
}
+
+export interface LoadingCssVars {
+ '--size'?: React.CSSProperties['width' | 'height'];
+ '--size-large'?: React.CSSProperties['width' | 'height'];
+ '--stroke-color'?: React.CSSProperties['stroke'];
+ '--stroke-active-color'?: React.CSSProperties['stroke'];
+ '--spinner-item-color'?: React.CSSProperties['color'];
+ '--spinner-item-width'?: React.CSSProperties['width'];
+ '--spinner-item-height'?: React.CSSProperties['height'];
+ '--spinner-item-border-radius'?: React.CSSProperties['borderRadius'];
+}
\ No newline at end of file
diff --git a/packages/zarm/src/loading/loading.mini.tsx b/packages/zarm/src/loading/loading.mini.tsx
new file mode 100644
index 000000000..141646954
--- /dev/null
+++ b/packages/zarm/src/loading/loading.mini.tsx
@@ -0,0 +1,82 @@
+import { createBEM } from '@zarm-design/bem';
+import * as React from 'react';
+import { ConfigContext } from '../config-provider';
+import type { HTMLProps } from '../utils/utilityTypes';
+import type { BaseLoadingProps, LoadingCssVars } from './interface';
+
+
+
+export type LoadingProps = BaseLoadingProps &
+ HTMLProps & {
+ onClick?: React.MouseEventHandler;
+ };
+
+const Circular = React.forwardRef((props, ref) => {
+ const { className, size, percent, strokeWidth, loading, ...restProps } = props;
+ const { prefixCls } = React.useContext(ConfigContext);
+ const bem = createBEM('loading', { prefixCls });
+
+ const strokeWidthSty = strokeWidth ? { '--strokeWidth': `${ strokeWidth / 2}px` } : props.style;
+
+ const cls = bem([
+ {
+ circular: loading,
+ [`${size}`]: !!size,
+ },
+ className,
+ ]);
+
+ return (
+
+ );
+});
+
+Circular.displayName = 'Circular';
+
+const Spinner = React.forwardRef((props, ref) => {
+ const { className, size, ...restProps } = props;
+ const { prefixCls } = React.useContext(ConfigContext);
+ const bem = createBEM('loading', { prefixCls });
+
+ const cls = bem([
+ {
+ spinner: true,
+ [`${size}`]: !!size,
+ },
+ className,
+ ]);
+
+ const spinner: React.ReactElement[] = [];
+
+ for (let i = 0; i < 9; i++) {
+ spinner.push();
+ }
+
+ return (
+
+ {spinner}
+
+ );
+});
+
+Spinner.displayName = 'Spinner';
+
+const Loading = React.forwardRef((props, ref) => {
+ if (props.type !== 'spinner') {
+ const { type, ...restProps } = props;
+ return ;
+ }
+ const { strokeWidth, percent, loading, type, ...restProps } = props;
+ return ;
+});
+
+Loading.defaultProps = {
+ type: 'circular',
+ loading: true,
+ strokeWidth: 5,
+ percent: 20,
+};
+
+Loading.displayName = 'Loading';
+
+export default Loading;
diff --git a/packages/zarm/src/loading/style/index.mini.scss b/packages/zarm/src/loading/style/index.mini.scss
new file mode 100644
index 000000000..2cbaa1b14
--- /dev/null
+++ b/packages/zarm/src/loading/style/index.mini.scss
@@ -0,0 +1,34 @@
+@import '../../style/core/index';
+@import 'component';
+
+@include b(loading) {
+ @include define(strokeWidth, 3px);
+
+ @include m(circular) {
+ border-radius: 50%;
+ position: relative;
+ animation: rotate 1.5s linear infinite;
+
+ &::before {
+ content: "";
+ box-sizing: border-box;
+ position: absolute;
+ inset: 0;
+ border-radius: 50%;
+ border: var(--strokeWidth) solid var(--stroke-active-color);
+ animation: circle 1.5s linear infinite;
+ }
+ }
+}
+
+@keyframes rotate {
+ 100% { transform: rotate(360deg); }
+}
+
+@keyframes circle {
+ 0% { clip-path: polygon(50% 50%, 0 0, 0 0, 0 0, 0 0, 0 0); }
+ 25% { clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 0, 100% 0, 100% 0); }
+ 50% { clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 100% 100%, 100% 100%); }
+ 75% { clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 100%); }
+ 100% { clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 0); }
+}
diff --git a/packages/zarm/src/loading/style/index.mini.ts b/packages/zarm/src/loading/style/index.mini.ts
new file mode 100644
index 000000000..2e2fad2c7
--- /dev/null
+++ b/packages/zarm/src/loading/style/index.mini.ts
@@ -0,0 +1,2 @@
+import '../../style';
+import './index.mini.scss';
\ No newline at end of file
diff --git a/packages/zarm/src/modal/Alert.mini.tsx b/packages/zarm/src/modal/Alert.mini.tsx
new file mode 100644
index 000000000..9d4ed2b79
--- /dev/null
+++ b/packages/zarm/src/modal/Alert.mini.tsx
@@ -0,0 +1,39 @@
+import { createBEM } from '@zarm-design/bem';
+import * as React from 'react';
+import { getCustomEventsPath, customEvents } from '../utils/dom/dom.mini';
+import { getRuntimeConfig } from '../config-provider';
+import { ModalShowProps } from './methods';
+
+export interface ModalAlertProps extends Omit {
+ confirmText?: React.ReactNode;
+ onConfirm?: () => boolean | void | Promise;
+ id: string;
+}
+
+export const alert = (props: ModalAlertProps) => {
+ const { className, animationType = 'zoom', confirmText, onConfirm, ...rest } = props;
+ const { prefixCls, locale } = getRuntimeConfig();
+ const bem = createBEM('alert', { prefixCls });
+ const path = getCustomEventsPath(props.id);
+ return new Promise((resolve) => {
+ customEvents.trigger(path, {
+ ...rest,
+ className: bem([className]),
+ animationType,
+ visible: true,
+ actions: [
+ {
+ text: confirmText || locale?.Modal.confirmText,
+ onClick: async () => {
+ const result = await props.onConfirm?.();
+ if (result === false) return;
+ customEvents.trigger(path, { visible: false });
+ resolve(null);
+ },
+ },
+ ],
+ });
+ });
+};
+
+export default alert;
diff --git a/packages/zarm/src/modal/Confirm.mini.tsx b/packages/zarm/src/modal/Confirm.mini.tsx
new file mode 100644
index 000000000..364cf1fff
--- /dev/null
+++ b/packages/zarm/src/modal/Confirm.mini.tsx
@@ -0,0 +1,59 @@
+import { createBEM } from '@zarm-design/bem';
+import * as React from 'react';
+import { getRuntimeConfig } from '../config-provider';
+import { ModalShowProps } from './methods';
+import { getCustomEventsPath, customEvents } from '../utils/dom/dom.mini';
+
+export interface ModalConfirmProps extends Omit {
+ confirmText?: React.ReactNode;
+ cancelText?: React.ReactNode;
+ onConfirm?: () => boolean | void | Promise;
+ onCancel?: () => boolean | void | Promise;
+ id: string;
+}
+
+export const confirm = (props: ModalConfirmProps): Promise => {
+ const {
+ className,
+ animationType = 'zoom',
+ confirmText,
+ cancelText,
+ onConfirm,
+ onCancel,
+ ...rest
+ } = props;
+ const { prefixCls, locale } = getRuntimeConfig();
+ const bem = createBEM('confirm', { prefixCls });
+ const path = getCustomEventsPath(props.id);
+ return new Promise((resolve) => {
+ customEvents.trigger(path, {
+ ...rest,
+ className: bem([className]),
+ animationType,
+ visible: true,
+ actions: [
+ [
+ {
+ theme: 'default',
+ text: cancelText || locale?.Modal.cancelText,
+ onClick: async () => {
+ const result = onCancel?.();
+ if (result === false) return;
+ customEvents.trigger(path, { visible: false });
+ resolve(false);
+ },
+ },
+ {
+ text: confirmText || locale?.Modal.confirmText,
+ onClick: async () => {
+ const result = await onConfirm?.();
+ if (result === false) return;
+ customEvents.trigger(path, { visible: false });
+ resolve(true);
+ },
+ },
+ ],
+ ],
+ });
+ });
+};
diff --git a/packages/zarm/src/modal/Modal.mini.tsx b/packages/zarm/src/modal/Modal.mini.tsx
new file mode 100644
index 000000000..707efa9ed
--- /dev/null
+++ b/packages/zarm/src/modal/Modal.mini.tsx
@@ -0,0 +1,124 @@
+import { createBEM } from '@zarm-design/bem';
+import { Close as CloseIcon } from '@zarm-design/icons';
+import * as React from 'react';
+import { useSetState } from 'ahooks';
+import { View } from '@tarojs/components';
+import { ConfigContext } from '../config-provider';
+import Popup from '../popup';
+import { noop } from '../utils';
+import type { HTMLProps } from '../utils/utilityTypes';
+import type { BaseModalProps, ModalCssVars } from './interface';
+import type { ModalActionProps } from './ModalAction.mini';
+import ModalAction from './ModalAction.mini';
+
+import { useCustomEvent } from '../utils/dom/dom.mini';
+
+export interface ModalProps extends BaseModalProps, HTMLProps {
+ actions?: (ModalActionProps | ModalActionProps[])[];
+ onAction?: (action: ModalActionProps) => void | Promise;
+ id: string;
+}
+
+const Modal = React.forwardRef((props, ref) => {
+ const [state, setState] = useSetState(props);
+
+ const {
+ id,
+ className,
+ title,
+ shape,
+ closable,
+ onClose,
+ children,
+ footer,
+ maskClosable,
+ actions,
+ onAction,
+ ...rest
+ } = state;
+
+ useCustomEvent(id, (prarms) => {
+ setState(prarms);
+ });
+
+ React.useEffect(()=> {
+ setState({
+ ...state,
+ visible: props.visible,
+ });
+ }, [props.visible])
+
+ const { prefixCls } = React.useContext(ConfigContext);
+ const bem = createBEM('modal', { prefixCls });
+
+ const cls = bem([
+ {
+ [`${shape}`]: !!shape,
+ },
+ className,
+ ]);
+
+ const showHeader = title || closable;
+ const hasActions = actions && actions!.length > 0;
+ const showFooter = !!footer || hasActions;
+
+ const actionsRender = actions!.map((action, i) => {
+ const currentAction = Array.isArray(action) ? action : [action];
+
+ return (
+
+ {currentAction.map((child, j) => (
+ {
+ await child.onClick?.();
+ await onAction?.(child);
+ }}
+ />
+ ))}
+
+ );
+ });
+
+ return (
+
+ {
+ event.stopPropagation();
+ }}>
+
+ {showHeader && (
+ <>
+ {title}
+ {closable && }
+ >
+ )}
+ {children}
+
+ {showFooter && {hasActions ? actionsRender : footer}}
+
+
+ );
+});
+
+Modal.defaultProps = {
+ visible: false,
+ animationType: 'fade',
+ width: '70%',
+ mask: true,
+ shape: 'radius',
+ closable: false,
+ maskClosable: false,
+ destroy: true,
+ actions: [],
+};
+
+export default Modal;
diff --git a/packages/zarm/src/modal/Modal.tsx b/packages/zarm/src/modal/Modal.tsx
index f584f1e54..ca1f04ba0 100644
--- a/packages/zarm/src/modal/Modal.tsx
+++ b/packages/zarm/src/modal/Modal.tsx
@@ -5,32 +5,10 @@ import { ConfigContext } from '../config-provider';
import Popup from '../popup';
import { noop } from '../utils';
import type { HTMLProps } from '../utils/utilityTypes';
-import type { BaseModalProps } from './interface';
+import type { BaseModalProps, ModalCssVars } from './interface';
import type { ModalActionProps } from './ModalAction';
import ModalAction from './ModalAction';
-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'];
-}
-
export interface ModalProps extends BaseModalProps, HTMLProps {
actions?: (ModalActionProps | ModalActionProps[])[];
onAction?: (action: ModalActionProps) => void | Promise;
diff --git a/packages/zarm/src/modal/ModalAction.mini.tsx b/packages/zarm/src/modal/ModalAction.mini.tsx
new file mode 100644
index 000000000..76660d211
--- /dev/null
+++ b/packages/zarm/src/modal/ModalAction.mini.tsx
@@ -0,0 +1,45 @@
+import { createBEM } from '@zarm-design/bem';
+import * as React from 'react';
+import { View } from '@tarojs/components';
+import { ConfigContext } from '../config-provider';
+import Loading from '../loading/index.mini';
+import { useSafeState } from '../utils/hooks';
+import type { HTMLProps } from '../utils/utilityTypes';
+import type { BaseModalActionProps } from './interface';
+
+export type ModalActionProps = BaseModalActionProps & HTMLProps;
+
+const ModalAction = React.forwardRef((props, ref) => {
+ const { className, bold, theme, disabled, text, onClick, ...rest } = props;
+ const [loading, setLoading] = useSafeState(false);
+ const { prefixCls } = React.useContext(ConfigContext);
+ const bem = createBEM('modal', { prefixCls });
+ const cls = bem('button', [
+ {
+ [`${theme}`]: !!theme,
+ bold,
+ disabled: disabled || loading,
+ },
+ className,
+ ]);
+
+ return (
+ {
+ setLoading(true);
+ try {
+ await onClick?.();
+ } finally {
+ setLoading(false);
+ }
+ }}
+ >
+ {loading ? : text}
+
+ );
+});
+
+export default ModalAction;
diff --git a/packages/zarm/src/modal/demo/alert.mini.tsx b/packages/zarm/src/modal/demo/alert.mini.tsx
new file mode 100644
index 000000000..f32e5807d
--- /dev/null
+++ b/packages/zarm/src/modal/demo/alert.mini.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/zarm/src/modal/demo/basic.mini.tsx b/packages/zarm/src/modal/demo/basic.mini.tsx
new file mode 100644
index 000000000..2f7718c33
--- /dev/null
+++ b/packages/zarm/src/modal/demo/basic.mini.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')}>
+ 开启
+
+ }
+ > */}
+ {/* */}
+ {/* 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),
+ }
});