feat(SchemaForm): Optimising the action feedback of the Button component

This commit is contained in:
haitaoo 2023-04-29 12:25:54 +08:00
parent 9f2607cf35
commit c843dddf0b
4 changed files with 84 additions and 16 deletions

View File

@ -1,9 +1,10 @@
import React, { FC, useState } from 'react'; import React, { FC, useLayoutEffect, useState } from 'react';
import { Button, ButtonProps } from 'react-bootstrap'; import { Button, ButtonProps, Spinner } from 'react-bootstrap';
import { request } from '@/utils'; import { request } from '@/utils';
import type * as Type from '@/common/interface'; import type * as Type from '@/common/interface';
import type { UIAction } from '../index.d'; import type { UIAction } from '../types';
import { useToast } from '@/hooks';
interface Props { interface Props {
fieldName: string; fieldName: string;
@ -24,17 +25,51 @@ const Index: FC<Props> = ({
variant = 'primary', variant = 'primary',
size, size,
}) => { }) => {
const Toast = useToast();
const [isLoading, setLoading] = useState(false); const [isLoading, setLoading] = useState(false);
const handleAction = async () => { const handleNotify = (msg, type: 'success' | 'danger' = 'success') => {
const tm = action?.toastMessage;
if (tm === false || !msg) {
return;
}
Toast.onShow({
msg,
variant: type,
});
};
const handleAction = () => {
if (!action) { if (!action) {
return; return;
} }
setLoading(true); setLoading(true);
const method = action.method || 'get'; request
await request[method](action.url); .request({
method: action.method,
url: action.url,
timeout: 0,
})
.then((resp) => {
if ('message' in resp) {
handleNotify(resp.message, 'success');
}
})
.catch((ex) => {
if (ex && 'msg' in ex) {
handleNotify(ex.msg, 'danger');
}
})
.finally(() => {
setLoading(false); setLoading(false);
});
}; };
useLayoutEffect(() => {
if (action?.loading?.state === 'pending') {
setLoading(true);
}
}, []);
const loadingText = action?.loading?.text || text;
const disabled = isLoading || readOnly; const disabled = isLoading || readOnly;
return ( return (
<div className="d-flex"> <div className="d-flex">
<Button <Button
@ -43,8 +78,19 @@ const Index: FC<Props> = ({
disabled={disabled} disabled={disabled}
size={size} size={size}
variant={variant}> variant={variant}>
{text || fieldName} {isLoading ? (
{isLoading ? '...' : ''} <>
<Spinner
className="align-middle me-2"
animation="border"
size="sm"
variant={variant}
/>
{loadingText}
</>
) : (
text
)}
</Button> </Button>
</div> </div>
); );

View File

@ -1,6 +0,0 @@
export interface UIAction {
url: string;
method?: 'get' | 'post' | 'put' | 'delete';
event?: 'click' | 'change';
handler?: ({evt, formData, request}) => Promise<void>
}

View File

@ -11,7 +11,7 @@ import classnames from 'classnames';
import type * as Type from '@/common/interface'; import type * as Type from '@/common/interface';
import type { UIAction } from './index.d'; import type { UIAction } from './types';
import { import {
Legend, Legend,
Select, Select,

View File

@ -0,0 +1,28 @@
/**
* A few notes on button control
* - Mainly used to send a request and notify the result of the request, and to update the data as required
* - A scenario where a message notification is displayed directly after a click without sending a request, implementing a dedicated control
* - Scenarios where the page jumps directly after a click without sending a request, implementing a dedicated control
*
* @field url : Target address for sending requests
* @field method : Method for sending requests, default `get`
* @field callback: Button event handler function that will fully take over the button events when this field is configured
* *** Incomplete, DO NOT USE ***
* @field loading: Set button loading information
* @field notify: Configure how button action processing results are prompted
*/
/**
* TODO:
* - Refining the type of `notify.options`
*/
export interface UIAction {
url: string;
method?: 'get' | 'post' | 'put' | 'delete';
callback?: () => Promise<void>;
loading?: {
text: string;
state?: 'none' | 'pending' | 'completed';
}
toastMessage?: boolean;
}