antd-overlay 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RaineySpace
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -7,11 +7,11 @@ Ant Design Modal/Drawer 命令式调用方案。
7
7
 
8
8
  ## 特性
9
9
 
10
- - 🚀 **命令式调用** - 通过函数调用打开/关闭覆盖层,无需管理 `open` 状态
11
- - 🎨 **动画支持** - 正确处理打开/关闭动画,避免动画未完成就卸载组件
12
- - 🌍 **全局挂载** - 支持跨组件调用,覆盖层可挂载到全局容器
10
+ - 🚀 **命令式调用** - 通过函数调用打开/关闭覆盖层,无需在业务里维护 `open` 状态
11
+ - 🎨 **动画支持** - 正确处理打开/关闭动画(Modal 使用 `afterClose`,Drawer 使用 `afterOpenChange`),避免动画未完成就卸载
12
+ - 🌍 **全局挂载** - 支持跨组件调用,覆盖层挂载到 `AntdOverlayProvider` 统一容器
13
13
  - 📦 **类型安全** - 完整的 TypeScript 类型支持
14
- - 🔧 **灵活扩展** - 支持自定义覆盖层组件
14
+ - 🔧 **灵活扩展** - `useOverlay` + `propsAdapter` 可对接自定义覆盖层组件
15
15
 
16
16
  ## 安装
17
17
 
@@ -34,24 +34,40 @@ yarn add antd-overlay
34
34
  }
35
35
  ```
36
36
 
37
+ ## 本地开发与示例
38
+
39
+ ```bash
40
+ pnpm install
41
+ pnpm dev:demo # 启动 Vite 演示(demo/:Modal / Drawer / useOverlay)
42
+ pnpm build # 使用 tsup 构建 dist
43
+ pnpm typecheck # TypeScript 检查
44
+ ```
45
+
46
+ 演示入口将 `ConfigProvider` 与 `AntdOverlayProvider` 组合使用,与线上应用推荐结构一致。
47
+
37
48
  ## 快速开始
38
49
 
39
50
  ### 1. 包裹 Provider(可选,仅全局 Hook 需要)
40
51
 
41
52
  ```tsx
42
53
  import { AntdOverlayProvider } from 'antd-overlay';
54
+ import { ConfigProvider } from 'antd';
43
55
 
44
56
  function App() {
45
57
  return (
46
- <AntdOverlayProvider>
47
- <YourApp />
48
- </AntdOverlayProvider>
58
+ <ConfigProvider>
59
+ <AntdOverlayProvider>
60
+ <YourApp />
61
+ </AntdOverlayProvider>
62
+ </ConfigProvider>
49
63
  );
50
64
  }
51
65
  ```
52
66
 
53
67
  ### 2. 创建覆盖层组件
54
68
 
69
+ 自定义 Modal 需实现 `CustomModalProps`(继承 antd `ModalProps` 与 `CustomOverlayProps`)。请将 antd `Modal` 的 `open` 接到 props 上,由 Hook 注入显示状态。
70
+
55
71
  ```tsx
56
72
  import { Modal, Input } from 'antd';
57
73
  import { CustomModalProps } from 'antd-overlay';
@@ -66,15 +82,17 @@ const MyModal: React.FC<MyModalProps> = ({
66
82
  customClose,
67
83
  customOk,
68
84
  initialValue,
85
+ ...props
69
86
  }) => {
70
87
  const [value, setValue] = useState(initialValue || '');
71
88
 
72
89
  return (
73
90
  <Modal
74
- title="输入内容"
75
91
  open={open}
92
+ title="输入内容"
76
93
  onCancel={customClose}
77
94
  onOk={() => customOk?.({ result: value })}
95
+ {...props}
78
96
  >
79
97
  <Input value={value} onChange={(e) => setValue(e.target.value)} />
80
98
  </Modal>
@@ -91,10 +109,10 @@ import { useModal, useGlobalModal } from 'antd-overlay';
91
109
  function LocalUsage() {
92
110
  const [openModal, holder] = useModal(MyModal);
93
111
 
94
- const handleOpen = async () => {
112
+ const handleOpen = () => {
95
113
  const controller = openModal({ initialValue: 'hello' });
96
- // controller.update({ initialValue: 'updated' }); // 可动态更新
97
- // controller.close(); // 可编程式关闭
114
+ // controller.update({ initialValue: 'updated' });
115
+ // controller.close();
98
116
  };
99
117
 
100
118
  return (
@@ -117,25 +135,23 @@ function GlobalUsage() {
117
135
  }
118
136
  ```
119
137
 
138
+ `openModal(...)` 返回 `OverlayController`:可调用 `update` 传入要更新的字段(与当前已保存的 props 及 Hook 的 `defaultProps` **浅合并**,同名键以本次 `update` 入参为准)、`close` 关闭(会尊重动画配置)。
139
+
120
140
  ## API
121
141
 
122
142
  ### Provider
123
143
 
124
144
  #### `AntdOverlayProvider`
125
145
 
126
- 全局覆盖层容器,使用 `useGlobalModal`、`useGlobalDrawer`、`useGlobalOverlay` 时需要在应用外层包裹。
146
+ 全局覆盖层容器;使用 `useGlobalModal`、`useGlobalDrawer`、`useGlobalOverlay` 时需要在应用内包裹。
127
147
 
128
148
  **属性:**
129
- - `children: React.ReactNode` - 子节点
130
- - `defaultModalProps?: Partial<ModalProps>` - 默认 Modal 属性,会应用到所有 Modal
131
- - `defaultDrawerProps?: Partial<DrawerProps>` - 默认 Drawer 属性,会应用到所有 Drawer
132
149
 
133
- ```tsx
134
- <AntdOverlayProvider>
135
- <App />
136
- </AntdOverlayProvider>
150
+ - `children: React.ReactNode`
151
+ - `defaultModalProps?: Partial<ModalProps>` — 默认 Modal 属性,与每次 `open` / `update` 传入的 props 合并(传入方优先)
152
+ - `defaultDrawerProps?: Partial<DrawerProps>` — 同上,作用于 Drawer
137
153
 
138
- // 或设置默认属性
154
+ ```tsx
139
155
  <AntdOverlayProvider
140
156
  defaultModalProps={{ centered: true, maskClosable: false }}
141
157
  defaultDrawerProps={{ width: 600 }}
@@ -144,93 +160,54 @@ function GlobalUsage() {
144
160
  </AntdOverlayProvider>
145
161
  ```
146
162
 
163
+ #### `useAntdOverlayContext()`
164
+
165
+ 读取 Context(含 `holders`、`addHolder`、`removeHolder` 及默认 Modal/Drawer 配置)。**必须在 `AntdOverlayProvider` 内使用**;一般供扩展或库内集成,业务侧很少直接使用。
166
+
147
167
  ### Modal Hooks
148
168
 
149
169
  #### `useModal<T>(Component, options?)`
150
170
 
151
- 局部 Modal 管理 Hook。
171
+ 局部 Modal Hook。
152
172
 
153
- **参数:**
154
- - `Component: React.FC<T>` - Modal 组件,需实现 `CustomModalProps` 接口
155
- - `options?: UseModalOptions` - 配置选项
156
- - `animation?: boolean` - 是否启用动画,默认 `true`
173
+ **`options`(`UseModalOptions`)** 与底层 `useOverlay` 一致(不含 `propsAdapter` / `keyPrefix`,由内部固定):
157
174
 
158
- **返回值:**
159
- - `[openModal, holder]` - 打开函数和需要渲染的 holder 节点
175
+ - `animation?: boolean` — 是否等待关闭动画后再卸载,默认 `true`
176
+ - `defaultProps?: Partial<Omit<T, 'customClose'>>` 每次打开/更新时与入参合并的默认属性
177
+ - 另可将 Modal 相关字段写在 `options` 顶层,与 `defaultProps` 合并时**顶层字段优先**
160
178
 
161
- ```tsx
162
- const [openModal, holder] = useModal(MyModal);
163
- ```
179
+ **返回值:** `[openModal, holder]` — `openModal` 为 `OverlayOpener<T>`,返回 `OverlayController<T>`。
164
180
 
165
181
  #### `useGlobalModal<T>(Component, options?)`
166
182
 
167
- 全局 Modal 管理 Hook,无需手动渲染 holder
168
-
169
- **参数:** 同 `useModal`
170
-
171
- **返回值:**
172
- - `openModal` - 打开函数
173
-
174
- ```tsx
175
- const openModal = useGlobalModal(MyModal);
176
- ```
183
+ 全局 Modal Hook;无需渲染 `holder`。
177
184
 
178
185
  #### `generateUseModalHook<T>(Component)`
179
186
 
180
- 为特定 Modal 组件生成专用 Hook 工厂函数。
187
+ 为指定 Modal 组件生成 `{ useModal, useGlobalModal }`,二者均可传入 `options?: UseModalOptions`。
181
188
 
182
189
  ```tsx
183
- // modal.tsx
184
190
  export const {
185
191
  useModal: useMyModal,
186
192
  useGlobalModal: useGlobalMyModal,
187
193
  } = generateUseModalHook(MyModal);
188
-
189
- // usage.tsx
190
- const openModal = useGlobalMyModal();
191
194
  ```
192
195
 
193
196
  ### Drawer Hooks
194
197
 
195
- #### `useDrawer<T>(Component, options?)`
196
-
197
- 局部 Drawer 管理 Hook。
198
+ #### `useDrawer<T>(Component, options?)` / `useGlobalDrawer<T>(Component, options?)`
198
199
 
199
- ```tsx
200
- const [openDrawer, holder] = useDrawer(MyDrawer);
201
- ```
202
-
203
- #### `useGlobalDrawer<T>(Component, options?)`
204
-
205
- 全局 Drawer 管理 Hook。
206
-
207
- ```tsx
208
- const openDrawer = useGlobalDrawer(MyDrawer);
209
- ```
200
+ 语义与 Modal 侧相同,选项类型为 `UseDrawerOptions`(同样支持 `animation`、`defaultProps` 及顶层 Drawer 属性)。
210
201
 
211
202
  #### `generateUseDrawerHook<T>(Component)`
212
203
 
213
- 为特定 Drawer 组件生成专用 Hook 工厂函数。
214
-
215
- ```tsx
216
- export const {
217
- useDrawer: useMyDrawer,
218
- useGlobalDrawer: useGlobalMyDrawer,
219
- } = generateUseDrawerHook(MyDrawer);
220
- ```
204
+ 生成 `{ useDrawer, useGlobalDrawer }`。
221
205
 
222
206
  ### 通用 Overlay Hooks
223
207
 
224
208
  #### `useOverlay<T>(Component, options?)`
225
209
 
226
- 通用覆盖层管理 Hook,适用于自定义覆盖层组件。
227
-
228
- **参数:**
229
- - `Component: React.FC<T>` - 覆盖层组件
230
- - `options?: UseOverlayOptions<T>` - 配置选项
231
- - `animation?: boolean` - 是否启用动画,默认 `true`
232
- - `keyPrefix?: string` - React key 前缀
233
- - `propsAdapter?: (props, state) => T` - 属性适配器函数
210
+ 通用覆盖层 Hook;需自行提供 `propsAdapter`,把内部 `state.open` / `state.onClose` / `state.onAnimationEnd` 映射到组件 API。
234
211
 
235
212
  ```tsx
236
213
  const [openOverlay, holder] = useOverlay(MyOverlay, {
@@ -238,7 +215,7 @@ const [openOverlay, holder] = useOverlay(MyOverlay, {
238
215
  ...props,
239
216
  visible: state.open,
240
217
  onClose: state.onClose,
241
- afterVisibleChange: (visible) => {
218
+ afterVisibleChange: (visible: boolean) => {
242
219
  if (!visible) state.onAnimationEnd();
243
220
  },
244
221
  }),
@@ -247,72 +224,57 @@ const [openOverlay, holder] = useOverlay(MyOverlay, {
247
224
 
248
225
  #### `useGlobalOverlay<T>(Component, options?)`
249
226
 
250
- 全局通用覆盖层管理 Hook。
227
+ 全局版本,依赖 `AntdOverlayProvider`。
251
228
 
252
229
  #### `generateUseOverlayHook<T>(Component, defaultOptions?)`
253
230
 
254
- 为特定覆盖层组件生成专用 Hook 工厂函数。
231
+ 生成绑定组件的 `useOverlay` / `useGlobalOverlay`;调用时可再传 `options` 与 `defaultOptions` 浅合并。
255
232
 
256
233
  ### 类型定义
257
234
 
258
- #### `CustomModalProps<T, R>`
259
-
260
- Modal 组件属性接口,继承自 `antd` 的 `ModalProps`。
235
+ #### `CustomOverlayProps<T, R>`
261
236
 
262
237
  ```typescript
263
- interface CustomModalProps<T = any, R = void> extends ModalProps {
238
+ interface CustomOverlayProps<T = any, R = void> {
264
239
  open?: boolean;
265
240
  customClose: () => void;
266
241
  customOk?: (value: T) => R;
267
242
  }
268
243
  ```
269
244
 
270
- #### `CustomDrawerProps<T, R>`
245
+ #### `CustomModalProps<T, R>` / `CustomDrawerProps<T, R>`
271
246
 
272
- Drawer 组件属性接口,继承自 `antd` `DrawerProps`。
247
+ 分别为 `ModalProps` / `DrawerProps` `CustomOverlayProps` 的交叉类型。
273
248
 
274
- ```typescript
275
- interface CustomDrawerProps<T = any, R = void> extends DrawerProps {
276
- open?: boolean;
277
- customClose: () => void;
278
- customOk?: (value: T) => R;
279
- }
280
- ```
249
+ #### `InternalOverlayProps<T>`
281
250
 
282
- #### `CustomOverlayProps<T, R>`
251
+ `Omit<T, 'customClose'>`,表示打开/更新时可传入的属性(由 Hook 注入 `customClose`)。
283
252
 
284
- 通用覆盖层组件属性接口。
253
+ #### `OverlayController<T>`
285
254
 
286
255
  ```typescript
287
- interface CustomOverlayProps<T = any, R = void> {
288
- open?: boolean;
289
- customClose: () => void;
290
- customOk?: (value: T) => R;
256
+ interface OverlayController<T extends CustomOverlayProps> {
257
+ /** 与 defaultProps、当前 props 浅合并后更新 */
258
+ readonly update: (props: InternalOverlayProps<T>) => void;
259
+ readonly close: () => void;
291
260
  }
292
261
  ```
293
262
 
294
- #### `OverlayController<T>`
295
-
296
- 覆盖层控制器,由 `openModal`/`openDrawer`/`openOverlay` 返回。
263
+ #### `OverlayOpener<T>`
297
264
 
298
- ```typescript
299
- interface OverlayController<T> {
300
- update: (props: Omit<T, 'customClose'>) => void;
301
- close: () => void;
302
- }
303
- ```
265
+ `(initialize?: InternalOverlayProps<T>) => OverlayController<T>`。
304
266
 
305
267
  ## 完整示例
306
268
 
307
269
  ### 确认删除 Modal
308
270
 
309
271
  ```tsx
310
- import { Modal, message } from 'antd';
272
+ import React, { useState } from 'react';
273
+ import { Modal, message, List, Button } from 'antd';
311
274
  import { CustomModalProps, useGlobalModal } from 'antd-overlay';
312
275
 
313
276
  interface ConfirmDeleteModalProps extends CustomModalProps<void> {
314
277
  itemName: string;
315
- onConfirm: () => Promise<void>;
316
278
  }
317
279
 
318
280
  const ConfirmDeleteModal: React.FC<ConfirmDeleteModalProps> = ({
@@ -320,17 +282,16 @@ const ConfirmDeleteModal: React.FC<ConfirmDeleteModalProps> = ({
320
282
  customClose,
321
283
  customOk,
322
284
  itemName,
323
- onConfirm,
285
+ ...props
324
286
  }) => {
325
287
  const [loading, setLoading] = useState(false);
326
288
 
327
289
  const handleOk = async () => {
328
290
  setLoading(true);
329
291
  try {
330
- await onConfirm();
331
292
  message.success('删除成功');
332
293
  customOk?.();
333
- } catch (error) {
294
+ } catch {
334
295
  message.error('删除失败');
335
296
  } finally {
336
297
  setLoading(false);
@@ -339,37 +300,40 @@ const ConfirmDeleteModal: React.FC<ConfirmDeleteModalProps> = ({
339
300
 
340
301
  return (
341
302
  <Modal
342
- title="确认删除"
343
303
  open={open}
304
+ title="确认删除"
344
305
  onCancel={customClose}
345
306
  onOk={handleOk}
346
307
  confirmLoading={loading}
347
308
  okText="删除"
348
309
  okType="danger"
310
+ {...props}
349
311
  >
350
- 确定要删除 "{itemName}" 吗?此操作不可恢复。
312
+ 确定要删除 &quot;{itemName}&quot; 吗?此操作不可恢复。
351
313
  </Modal>
352
314
  );
353
315
  };
354
316
 
355
- // 使用
317
+ // 使用(示例数据与类型请按项目替换)
356
318
  function ItemList() {
357
319
  const openConfirm = useGlobalModal(ConfirmDeleteModal);
358
320
 
359
- const handleDelete = (item: Item) => {
321
+ const handleDelete = (item: { id: number; name: string }) => {
360
322
  openConfirm({
361
323
  itemName: item.name,
362
- onConfirm: () => deleteItem(item.id),
324
+ customOk: () => {
325
+ /* deleteItem(item.id) */
326
+ },
363
327
  });
364
328
  };
365
329
 
366
330
  return (
367
331
  <List
368
- dataSource={items}
332
+ dataSource={[]}
369
333
  renderItem={(item) => (
370
334
  <List.Item
371
335
  actions={[
372
- <Button danger onClick={() => handleDelete(item)}>
336
+ <Button key="del" danger onClick={() => handleDelete(item)}>
373
337
  删除
374
338
  </Button>,
375
339
  ]}
@@ -385,6 +349,7 @@ function ItemList() {
385
349
  ### 用户详情 Drawer
386
350
 
387
351
  ```tsx
352
+ import React, { useEffect, useState } from 'react';
388
353
  import { Drawer, Descriptions, Spin } from 'antd';
389
354
  import { CustomDrawerProps, generateUseDrawerHook } from 'antd-overlay';
390
355
 
@@ -396,26 +361,22 @@ const UserDetailDrawer: React.FC<UserDetailDrawerProps> = ({
396
361
  open,
397
362
  customClose,
398
363
  userId,
364
+ ...props
399
365
  }) => {
400
- const [user, setUser] = useState<User | null>(null);
366
+ const [user, setUser] = useState<{ name: string; email: string; phone: string } | null>(null);
401
367
  const [loading, setLoading] = useState(false);
402
368
 
403
369
  useEffect(() => {
404
370
  if (open && userId) {
405
371
  setLoading(true);
406
- fetchUser(userId)
372
+ Promise.resolve(/* fetchUser(userId) */)
407
373
  .then(setUser)
408
374
  .finally(() => setLoading(false));
409
375
  }
410
376
  }, [open, userId]);
411
377
 
412
378
  return (
413
- <Drawer
414
- title="用户详情"
415
- open={open}
416
- onClose={customClose}
417
- width={500}
418
- >
379
+ <Drawer open={open} title="用户详情" onClose={customClose} width={500} {...props}>
419
380
  {loading ? (
420
381
  <Spin />
421
382
  ) : user ? (
@@ -429,27 +390,24 @@ const UserDetailDrawer: React.FC<UserDetailDrawerProps> = ({
429
390
  );
430
391
  };
431
392
 
432
- // 导出专用 Hook
433
393
  export const {
434
394
  useDrawer: useUserDetailDrawer,
435
395
  useGlobalDrawer: useGlobalUserDetailDrawer,
436
396
  } = generateUseDrawerHook(UserDetailDrawer);
437
397
 
438
- // 使用
439
398
  function UserCard({ userId }: { userId: number }) {
440
399
  const openDetail = useGlobalUserDetailDrawer();
441
400
 
442
- return (
443
- <Card onClick={() => openDetail({ userId })}>
444
- 查看详情
445
- </Card>
446
- );
401
+ return <div onClick={() => openDetail({ userId })}>查看详情</div>;
447
402
  }
448
403
  ```
449
404
 
450
405
  ### 动态更新 Modal
451
406
 
452
407
  ```tsx
408
+ import { Button } from 'antd';
409
+ // UploadModal、delay 由业务自行实现
410
+
453
411
  function ProgressModal() {
454
412
  const [openModal, holder] = useModal(UploadModal);
455
413
 
@@ -479,11 +437,11 @@ function ProgressModal() {
479
437
 
480
438
  ```
481
439
  ┌─────────────────────────────────────────────────────────┐
482
- │ useModal / useDrawer │ <- 业务层封装
440
+ │ useModal / useDrawer │ 业务层封装
483
441
  ├─────────────────────────────────────────────────────────┤
484
- │ useOverlay / useGlobalOverlay │ <- 核心逻辑层
442
+ │ useOverlay / useGlobalOverlay │ 核心逻辑层
485
443
  ├─────────────────────────────────────────────────────────┤
486
- │ AntdOverlayProvider │ <- 全局容器层
444
+ │ AntdOverlayProvider │ 全局容器层
487
445
  └─────────────────────────────────────────────────────────┘
488
446
  ```
489
447
 
package/dist/index.cjs CHANGED
@@ -52,8 +52,16 @@ function useOverlay(OverlayComponent, options = {}) {
52
52
  const {
53
53
  animation = true,
54
54
  keyPrefix = "use-overlay",
55
- propsAdapter = defaultPropsAdapter
55
+ defaultProps: defaultPropsFromOption,
56
+ propsAdapter = defaultPropsAdapter,
57
+ ...restDefaultFromTopLevel
56
58
  } = options;
59
+ const mergedDefaultProps = {
60
+ ...defaultPropsFromOption,
61
+ ...restDefaultFromTopLevel
62
+ };
63
+ const defaultPropsRef = React2.useRef(mergedDefaultProps);
64
+ defaultPropsRef.current = mergedDefaultProps;
57
65
  const id = React2.useId();
58
66
  const key = `${keyPrefix}-${id}`;
59
67
  const [open, setOpen] = React2.useState(false);
@@ -95,13 +103,20 @@ function useOverlay(OverlayComponent, options = {}) {
95
103
  (initializeProps) => {
96
104
  setRenderEnable(true);
97
105
  setOpen(true);
98
- setProps(initializeProps);
106
+ setProps({
107
+ ...defaultPropsRef.current,
108
+ ...initializeProps
109
+ });
99
110
  return {
100
111
  /**
101
112
  * 更新覆盖层属性
102
- * 注意:这是完全替换,不是合并
113
+ * 与 defaultProps 合并,传入的 newProps 优先
103
114
  */
104
- update: (newProps) => setProps(newProps),
115
+ update: (newProps) => setProps((prev) => ({
116
+ ...defaultPropsRef.current,
117
+ ...prev,
118
+ ...newProps
119
+ })),
105
120
  /**
106
121
  * 关闭覆盖层
107
122
  */