Skip to content

Commit 136d324

Browse files
committed
refactor(platform): add/edit modal export ref that open modal
1 parent 0c9b094 commit 136d324

4 files changed

Lines changed: 69 additions & 127 deletions

File tree

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,68 @@
1-
import type { FormGroup } from '@react-devui/ui';
1+
import type { OpenModalFn } from '../../../utils/types';
2+
import type { Device } from './StandardTable';
23
import type { DSelectItem } from '@react-devui/ui/components/select';
34

45
import { isUndefined } from 'lodash';
6+
import React, { useImperativeHandle, useState } from 'react';
57

6-
import { useId } from '@react-devui/hooks';
8+
import { useAsync, useEventCallback, useId } from '@react-devui/hooks';
9+
import { FormControl, FormGroup, useForm, Validators } from '@react-devui/ui';
710
import { DForm, DInput, DModal, DSelect } from '@react-devui/ui';
811

912
export interface AppDeviceModalProps {
10-
aVisible: boolean;
11-
aHeader: string;
12-
aForm: FormGroup;
13-
aUpdateForm: any;
1413
aModelList: DSelectItem<string>[] | undefined;
15-
onClose: () => void;
16-
onSubmit: () => void | boolean | Promise<boolean>;
1714
}
1815

19-
export function AppDeviceModal(props: AppDeviceModalProps): JSX.Element | null {
20-
const { aVisible, aHeader, aForm, aUpdateForm, aModelList, onClose, onSubmit } = props;
16+
function DeviceModal(props: AppDeviceModalProps, ref: React.ForwardedRef<OpenModalFn<Device>>): JSX.Element | null {
17+
const { aModelList } = props;
18+
19+
const async = useAsync();
2120

2221
const uniqueId = useId();
2322
const id = `app-form-${uniqueId}`;
2423

24+
const [visible, setVisible] = useState(false);
25+
const [device, setDevice] = useState<Device>();
26+
const [form, updateForm] = useForm(
27+
() =>
28+
new FormGroup({
29+
name: new FormControl('', Validators.required),
30+
model: new FormControl<string | null>(null, Validators.required),
31+
})
32+
);
33+
34+
const open = useEventCallback<OpenModalFn<Device>>((device) => {
35+
setVisible(true);
36+
setDevice(device);
37+
form.reset(device ? { name: device.name, model: device.model } : undefined);
38+
updateForm();
39+
});
40+
41+
useImperativeHandle(ref, () => open, [open]);
42+
2543
return (
2644
<DModal
27-
dVisible={aVisible}
28-
dHeader={aHeader}
29-
dFooter={<DModal.Footer dOkProps={{ type: 'submit', form: id, disabled: !aForm.valid }} onOkClick={onSubmit}></DModal.Footer>}
45+
dVisible={visible}
46+
dHeader={`${device ? 'Edit' : 'Add'} Device`}
47+
dFooter={
48+
<DModal.Footer
49+
dOkProps={{ type: 'submit', form: id, disabled: !form.valid }}
50+
onOkClick={() =>
51+
new Promise((r) => {
52+
async.setTimeout(() => {
53+
r(true);
54+
}, 500);
55+
})
56+
}
57+
></DModal.Footer>
58+
}
3059
dMaskClosable={false}
31-
dSkipFirstTransition={false}
32-
onClose={onClose}
60+
onClose={() => {
61+
setVisible(false);
62+
}}
3363
>
34-
<DForm id={id} dUpdate={aUpdateForm} dLabelWidth="6em">
35-
<DForm.Group dFormGroup={aForm}>
64+
<DForm id={id} dUpdate={updateForm} dLabelWidth="6em">
65+
<DForm.Group dFormGroup={form}>
3666
<DForm.Item dFormControls={{ name: 'Please enter name!' }} dLabel="Name">
3767
{({ name }) => <DInput dFormControl={name} dPlaceholder="Name" />}
3868
</DForm.Item>
@@ -52,3 +82,5 @@ export function AppDeviceModal(props: AppDeviceModalProps): JSX.Element | null {
5282
</DModal>
5383
);
5484
}
85+
86+
export const AppDeviceModal = React.forwardRef(DeviceModal);

packages/platform/src/app/routes/list/standard-table/StandardTable.tsx

Lines changed: 9 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,21 @@
1-
import type { DeviceDoc, StandardQueryParams } from '../../../utils/types';
1+
import type { DeviceDoc, OpenModalFn, StandardQueryParams } from '../../../utils/types';
22
import type { DSelectItem } from '@react-devui/ui/components/select';
33

44
import { isUndefined } from 'lodash';
5-
import { useEffect, useState } from 'react';
5+
import { useEffect, useRef, useState } from 'react';
66
import { useTranslation } from 'react-i18next';
77
import { Link } from 'react-router-dom';
88

99
import { useAsync, useImmer, useMount } from '@react-devui/hooks';
1010
import { DownOutlined, PlusOutlined } from '@react-devui/icons';
11-
import {
12-
DButton,
13-
DCard,
14-
DCheckbox,
15-
DDropdown,
16-
DModal,
17-
DPagination,
18-
DSelect,
19-
DSeparator,
20-
DSpinner,
21-
DTable,
22-
FormControl,
23-
FormGroup,
24-
useForm,
25-
Validators,
26-
} from '@react-devui/ui';
11+
import { DButton, DCard, DCheckbox, DDropdown, DModal, DPagination, DSelect, DSeparator, DSpinner, DTable } from '@react-devui/ui';
2712

2813
import { AppRouteHeader, AppStatusDot, AppTableFilter } from '../../../components';
2914
import { useAPI, useQueryParams } from '../../../hooks';
3015
import { AppDeviceModal } from './DeviceModal';
3116
import styles from './StandardTable.module.scss';
3217

33-
type Device = DeviceDoc;
18+
export type Device = DeviceDoc;
3419

3520
interface DeviceQueryParams {
3621
keyword: string;
@@ -42,6 +27,8 @@ interface DeviceQueryParams {
4227
}
4328

4429
export default function StandardTable(): JSX.Element | null {
30+
const deviceModalRef = useRef<OpenModalFn<Device>>(null);
31+
4532
const { t } = useTranslation();
4633
const async = useAsync();
4734
const modelApi = useAPI('/device/model');
@@ -84,23 +71,6 @@ export default function StandardTable(): JSX.Element | null {
8471
device: Device;
8572
}>();
8673

87-
const [paramsOfDeviceModal, setParamsOfDeviceModal] = useImmer<{
88-
visible: boolean;
89-
device: Device | undefined;
90-
}>();
91-
const [deviceForm, updateDeviceForm] = useForm(
92-
() =>
93-
new FormGroup({
94-
name: new FormControl('', Validators.required),
95-
model: new FormControl<string | null>(null, Validators.required),
96-
})
97-
);
98-
const openDeviceModal = (device?: Device) => {
99-
setParamsOfDeviceModal({ visible: true, device });
100-
deviceForm.reset(device ? { name: device.name, model: device.model } : undefined);
101-
updateDeviceForm();
102-
};
103-
10474
useMount(() => {
10575
modelApi.list().subscribe({
10676
next: (res) => {
@@ -182,29 +152,7 @@ export default function StandardTable(): JSX.Element | null {
182152
}}
183153
/>
184154
)}
185-
{paramsOfDeviceModal && (
186-
<AppDeviceModal
187-
aVisible={paramsOfDeviceModal.visible}
188-
aHeader={`${paramsOfDeviceModal.device ? 'Edit' : 'Add'} Device`}
189-
aForm={deviceForm}
190-
aUpdateForm={updateDeviceForm}
191-
aModelList={modelList}
192-
onClose={() => {
193-
setParamsOfDeviceModal((draft) => {
194-
if (draft) {
195-
draft.visible = false;
196-
}
197-
});
198-
}}
199-
onSubmit={() =>
200-
new Promise((r) => {
201-
async.setTimeout(() => {
202-
r(true);
203-
}, 500);
204-
})
205-
}
206-
/>
207-
)}
155+
<AppDeviceModal ref={deviceModalRef} aModelList={modelList} />
208156
<AppRouteHeader>
209157
<AppRouteHeader.Breadcrumb
210158
aList={[
@@ -216,7 +164,7 @@ export default function StandardTable(): JSX.Element | null {
216164
aActions={[
217165
<DButton
218166
onClick={() => {
219-
openDeviceModal();
167+
deviceModalRef.current?.();
220168
}}
221169
dIcon={<PlusOutlined />}
222170
>
@@ -377,7 +325,7 @@ export default function StandardTable(): JSX.Element | null {
377325
onItemClick={(action) => {
378326
switch (action) {
379327
case 'edit':
380-
openDeviceModal(data);
328+
deviceModalRef.current?.(data);
381329
break;
382330

383331
case 'delete':

packages/platform/src/app/routes/list/standard-table/detail/Detail.tsx

Lines changed: 9 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
import type { DeviceDoc } from '../../../../utils/types';
1+
import type { OpenModalFn } from '../../../../utils/types';
2+
import type { Device } from '../StandardTable';
23
import type { DSelectItem } from '@react-devui/ui/components/select';
34

45
import { isUndefined } from 'lodash';
5-
import { useState } from 'react';
6+
import { useRef, useState } from 'react';
67
import { useTranslation } from 'react-i18next';
78
import { useParams } from 'react-router-dom';
89

9-
import { useAsync, useImmer, useMount } from '@react-devui/hooks';
10+
import { useMount } from '@react-devui/hooks';
1011
import { EditOutlined } from '@react-devui/icons';
11-
import { DButton, DCard, DSeparator, DSpinner, DTable, FormControl, FormGroup, useForm, Validators } from '@react-devui/ui';
12+
import { DButton, DCard, DSeparator, DSpinner, DTable } from '@react-devui/ui';
1213

1314
import { AppDetailView, AppRouteHeader } from '../../../../components';
1415
import { useAPI } from '../../../../hooks';
1516
import { AppDeviceModal } from '../DeviceModal';
1617
import styles from './Detail.module.scss';
1718

18-
type Device = DeviceDoc;
19-
2019
export default function Detail(): JSX.Element | null {
20+
const deviceModalRef = useRef<OpenModalFn<Device>>(null);
21+
2122
const { t } = useTranslation();
22-
const async = useAsync();
2323

2424
const modelApi = useAPI('/device/model');
2525
const deviceApi = useAPI('/device');
@@ -31,22 +31,6 @@ export default function Detail(): JSX.Element | null {
3131

3232
const [modelList, setModelList] = useState<DSelectItem<string>[]>();
3333

34-
const [paramsOfEditModal, setParamsOfEditModal] = useImmer<{
35-
visible: boolean;
36-
}>();
37-
const [editForm, updateEditForm] = useForm(
38-
() =>
39-
new FormGroup({
40-
name: new FormControl('', Validators.required),
41-
model: new FormControl<string | null>(null, Validators.required),
42-
})
43-
);
44-
const openEditModal = (device: Device) => {
45-
setParamsOfEditModal({ visible: true });
46-
editForm.reset({ name: device.name, model: device.model });
47-
updateEditForm();
48-
};
49-
5034
useMount(() => {
5135
modelApi.list().subscribe({
5236
next: (res) => {
@@ -68,29 +52,7 @@ export default function Detail(): JSX.Element | null {
6852

6953
return (
7054
<>
71-
{paramsOfEditModal && (
72-
<AppDeviceModal
73-
aVisible={paramsOfEditModal.visible}
74-
aHeader={`${device ? 'Edit' : 'Add'} Device`}
75-
aForm={editForm}
76-
aUpdateForm={updateEditForm}
77-
aModelList={modelList}
78-
onClose={() => {
79-
setParamsOfEditModal((draft) => {
80-
if (draft) {
81-
draft.visible = false;
82-
}
83-
});
84-
}}
85-
onSubmit={() =>
86-
new Promise((r) => {
87-
async.setTimeout(() => {
88-
r(true);
89-
}, 500);
90-
})
91-
}
92-
/>
93-
)}
55+
<AppDeviceModal ref={deviceModalRef} aModelList={modelList} />
9456
<AppRouteHeader>
9557
<AppRouteHeader.Breadcrumb
9658
aList={[
@@ -109,9 +71,7 @@ export default function Detail(): JSX.Element | null {
10971
<DButton
11072
disabled={isUndefined(device)}
11173
onClick={() => {
112-
if (!isUndefined(device)) {
113-
openEditModal(device);
114-
}
74+
deviceModalRef.current?.(device);
11575
}}
11676
dIcon={<EditOutlined />}
11777
>

packages/platform/src/app/utils/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export type OpenModalFn<T> = (doc?: T) => void;
2+
13
export interface StandardQueryParams {
24
sort?: string;
35
page?: number;

0 commit comments

Comments
 (0)