Skip to content

Commit

Permalink
✨ - feat: implemented useFormDialog hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Xaohs committed Oct 3, 2024
1 parent c601399 commit fc7d1b4
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 77 deletions.
127 changes: 127 additions & 0 deletions src/hooks/dialog/useFormDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import {
AttributeData,
Form,
FormField,
FormProps,
ModalProps,
P,
} from "@maykin-ui/admin-ui";
import React, { useContext, useEffect } from "react";

import { ModalServiceContext } from "../../contexts";
import { useDialog } from "./usedialog";

/**
* Returns a function which, when called: shows a form dialog with a
* confirmation callback and an optional cancellation callback.
*/
export const useFormDialog = () => {
const dialog = useDialog();

/**
* Shows a prompt dialog with a confirmation callback and an optional
* cancellation callback.
* @param title
* @param message
* @param fields
* @param labelConfirm
* @param labelCancel
* @param onConfirm
* @param onCancel
* @param modalProps
* @param formProps
*/
const fn = (
title: string,
message: React.ReactNode,
fields: FormField[],
labelConfirm: string,
labelCancel: string,
onConfirm: (data: AttributeData) => void,
onCancel?: () => void,
modalProps?: Partial<ModalProps>,
formProps?: FormProps,
) => {
dialog(
title,
<>
{typeof message === "string" ? <P>{message}</P> : message}
<PromptForm
message={message}
fields={fields}
labelConfirm={labelConfirm}
labelCancel={labelCancel}
onConfirm={onConfirm}
onCancel={onCancel}
formProps={formProps}
/>
</>,
undefined,
{ allowClose: false, ...modalProps },
);
};

return fn;
};

const PromptForm = ({
message,
fields,
labelConfirm,
labelCancel,
onConfirm,
onCancel,
formProps,
}: {
message: React.ReactNode;
fields: FormField[];
labelConfirm: string;
labelCancel: string;
onConfirm: (data: AttributeData) => void;
onCancel?: () => void;
formProps?: FormProps;
}) => {
const { setModalProps } = useContext(ModalServiceContext);

useEffect(() => {
// Delay the focus slightly to ensure modal and form are fully rendered
const timer = setTimeout(() => {
// We focus a form element, and if none are found, we focus the submit button, and otherwise none
const formElement: HTMLFormElement | null = document.querySelector(
"form input , form textarea , form select , form button[type=submit]",
);
if (formElement) {
formElement.focus();
}
}, 100);

return () => clearTimeout(timer);
}, []);

return (
<>
{typeof message === "string" ? <P>{message}</P> : message}
<Form
fields={fields}
labelSubmit={labelConfirm}
secondaryActions={[
{
children: labelCancel,
type: "button",
variant: "secondary",
onClick: () => {
setModalProps({ open: false });
onCancel?.();
},
},
]}
validateOnChange={true}
onSubmit={(_, data) => {
setModalProps({ open: false });
onConfirm(data);
}}
{...formProps}
/>
</>
);
};
114 changes: 37 additions & 77 deletions src/hooks/dialog/useprompt.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import React, { useContext, useEffect } from "react";
import { FormField } from "@maykin-ui/admin-ui";
import React from "react";

import { Form, ModalProps, P } from "../../components";
import { ModalServiceContext } from "../../contexts";
import { useDialog } from "./usedialog";
import { ModalProps } from "../../components";
import { useFormDialog } from "./useFormDialog";

/**
* Returns a function which, when called: shows a prompt dialog with a
* confirmation callback and an optional cancellation callback.
*/
export const usePrompt = () => {
const dialog = useDialog();
const { setModalProps } = useContext(ModalServiceContext);
const formDialog = useFormDialog();

/**
* Shows a prompt dialog with a confirmation callback and an optional
* cancellation callback.
* @param title
* @param message
* @param label
* @param labelConfirm
* @param labelCancel
* @param onConfirm
* @param onCancel
* @param modalProps
*/
const fn = (
title: string,
message: React.ReactNode,
Expand All @@ -22,80 +33,29 @@ export const usePrompt = () => {
onCancel?: () => void,
modalProps?: Partial<ModalProps>,
) => {
dialog(
const fields: FormField[] = [
{
label,
name: "message",
required: true,
type: "text",
},
];

formDialog(
title,
<PromptForm
message={message}
label={label}
labelConfirm={labelConfirm}
labelCancel={labelCancel}
onConfirm={onConfirm}
onCancel={onCancel}
setModalProps={setModalProps}
/>,
undefined,
{ allowClose: false, ...modalProps },
message,
fields,
labelConfirm,
labelCancel,
(data) => {
const message = data.message as string;
onConfirm(message);
},
onCancel,
modalProps,
);
};

return fn;
};

const PromptForm = ({
message,
label,
labelConfirm,
labelCancel,
onConfirm,
onCancel,
setModalProps,
}: {
message: React.ReactNode;
label: string;
labelConfirm: string;
labelCancel: string;
onConfirm: (message: string) => void;
onCancel?: () => void;
setModalProps: (props: Partial<ModalProps>) => void;
}) => {
useEffect(() => {
// Delay the focus slightly to ensure modal and form are fully rendered
const timer = setTimeout(() => {
// We focus a form element, and if none are found, we focus the submit button, and otherwise none
const formElement: HTMLFormElement | null = document.querySelector(
"form input , form textarea , form select , form button[type=submit]",
);
if (formElement) {
formElement.focus();
}
}, 100);

return () => clearTimeout(timer);
}, []);

return (
<>
{typeof message === "string" ? <P>{message}</P> : message}
<Form
fields={[{ label, name: "message", required: true }]} // No need to pass ref
labelSubmit={labelConfirm}
secondaryActions={[
{
children: labelCancel,
variant: "secondary",
type: "button",
onClick: () => {
setModalProps({ open: false });
onCancel?.();
},
},
]}
validateOnChange={true}
onSubmit={(_, { message }) => {
setModalProps({ open: false });
onConfirm(message as string);
}}
/>
</>
);
};

0 comments on commit fc7d1b4

Please sign in to comment.