@chris-c-brine/form-dialog 1.0.5 → 1.0.6

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/README.md CHANGED
@@ -40,36 +40,118 @@ This package has the following peer dependencies that need to be installed in yo
40
40
 
41
41
  ## Login Form Usage Example
42
42
 
43
+ ```tsx
44
+ // LoginPage.tsx
45
+ import { useCallback, type FC, type PropsWithChildren } from "react";
46
+ import { Container, IconButton } from "@mui/material";
47
+ import { globalErrorAtom, useUser } from "@features";
48
+ import { useDialog } from "@chris-c-brine/form-dialog";
49
+ import { useForm, type SubmitHandler } from "react-hook-form-mui";
50
+ import { LoginDialog } from "./LoginDialog";
51
+ import LoginForm from ".//LoginForm";
52
+ import { Lock as LockIcon, Person as PersonIcon } from "@mui/icons-material";
53
+ import { useSetAtom } from "jotai";
54
+
55
+ const defaultLoginFormValues = { username: "", password: "" };
56
+ type SubmitLogin = SubmitHandler<typeof defaultLoginFormValues>;
57
+ const LoginPage: FC = () => {
58
+ const { dialogProps, closeDialog, openDialog } = useDialog();
59
+ const { setUser } = useUser();
60
+ const setError = useSetAtom(globalErrorAtom);
61
+
62
+ const formContext = useForm({ defaultValues: defaultLoginFormValues });
63
+
64
+ const onSuccess: SubmitLogin = useCallback(
65
+ (data, event) => {
66
+ event?.preventDefault(); // Stop default html form submit
67
+ formContext.reset(); // Reset form
68
+ setUser({ name: data.username, isActive: true }); // Update User (and/or other business logic)
69
+ closeDialog(); // Close Dialog
70
+ setError({
71
+ message: <>Hello {data.username}!</>,
72
+ title: 'Successful Login!',
73
+ severity: "success",
74
+ icon: <PersonIcon sx={{fontSize: 35}} />
75
+ });
76
+ },
77
+ [formContext, setUser, closeDialog, setError],
78
+ );
79
+
80
+ return (
81
+ <LoginFormContainer>
82
+ <LoginForm onSuccess={onSuccess}>
83
+ <IconButton color="primary" onClick={openDialog} sx={{ py: 1 }}>
84
+ <LockIcon sx={{ fontSize: 50 }} />
85
+ </IconButton>
86
+ </LoginForm>
87
+ <LoginDialog
88
+ dialogProps={{ ...dialogProps }}
89
+ handleSubmit={onSuccess}
90
+ onClose={closeDialog}
91
+ />
92
+ </LoginFormContainer>
93
+ );
94
+ };
95
+
96
+ export default LoginPage;
97
+
98
+ const LoginFormContainer: FC<PropsWithChildren> = ({ children }) => (
99
+ <Container
100
+ component="main"
101
+ maxWidth={"xs"}
102
+ sx={{
103
+ display: "flex",
104
+ alignItems: "center",
105
+ justifyContent: "center",
106
+ height: "85vh",
107
+ overflow: "hidden",
108
+ animation: "fadeIn 1s ease-in-out",
109
+ "@keyframes fadeIn": {
110
+ from: { opacity: 0 },
111
+ to: { opacity: 1 },
112
+ },
113
+ }}
114
+ >
115
+ {children}
116
+ </Container>
117
+ );
118
+ ```
119
+ ```tsx
120
+ // LoginPageConstants.ts
121
+ import type { SubmitHandler } from "react-hook-form";
122
+
123
+ export const defaultLoginFormValues = { username: "", password: "" };
124
+ export type LoginForm = typeof defaultLoginFormValues;
125
+ export type SubmitLogin = SubmitHandler<LoginForm>;
126
+ ```
43
127
  ```tsx
44
128
  // LoginFormBase.tsx
45
- import {
46
- TextFieldElement,
47
- PasswordElement,
48
- type PasswordElementProps,
49
- useFormContext,
50
- } from "react-hook-form-mui";
129
+ import {TextFieldElement, PasswordElement, type PasswordElementProps, useFormContext} from "react-hook-form-mui";
51
130
  import { memo, useEffect } from "react";
52
- import { useFormDialog, AutoGrid, type AutoGridProps } from "@chris-c-brine/form-dialog";
131
+ import { useFormDialog, AutoGrid, type AutoGridProps } from "@chris-c-brine/form-dialog";
53
132
 
54
- export type NameProp = {name?: string; };
55
- export type LoginFormProps = NameProp & Pick<AutoGridProps, "columnCount">;
133
+ /**
134
+ * Login Form
135
+ */
136
+ export type NameProp = {
137
+ name?: string;
138
+ };
56
139
 
140
+ export type LoginFormProps = NameProp & Pick<AutoGridProps, "columnCount">;
57
141
  const LoginFormBase = memo(function ({ name, columnCount = 1 }: LoginFormProps) {
58
- const { disabled } = useFormDialog();
59
-
60
- useEffect(() => formNameLog(name), [name]);
61
-
62
- return (
63
- <AutoGrid
64
- columnCount={columnCount}
65
- columnSpacing={2}
66
- rowSpacing={1}
67
- components={[
68
- <UserName key={`${name}-username`} disabled={disabled} />,
69
- <Password key={`${name}-password`} disabled={disabled} />,
70
- ]}
71
- />
72
- );
142
+ const { disabled } = useFormDialog();
143
+
144
+ return (
145
+ <AutoGrid
146
+ columnCount={columnCount}
147
+ columnSpacing={2}
148
+ rowSpacing={1}
149
+ components={[
150
+ <UserName key={`${name}-username`} disabled={disabled} />,
151
+ <Password key={`${name}-password`} disabled={disabled} />,
152
+ ]}
153
+ />
154
+ );
73
155
  });
74
156
 
75
157
  LoginFormBase.displayName = "LoginForm";
@@ -80,25 +162,23 @@ export default LoginFormBase;
80
162
  * Inputs
81
163
  */
82
164
  const UserName = ({ disabled }: Pick<PasswordElementProps, "disabled">) => (
83
- <TextFieldElement
84
- name="username"
85
- label="Username"
86
- required
87
- autoFocus
88
- autoComplete="off"
89
- fullWidth
90
- margin={"dense"}
91
- size={"medium"}
92
- disabled={disabled}
93
- slotProps={{
94
- inputLabel: { shrink: true },
95
- }}
96
- />
165
+ <TextFieldElement
166
+ name="username"
167
+ label="Username"
168
+ required
169
+ autoFocus
170
+ autoComplete="off"
171
+ fullWidth
172
+ margin={"dense"}
173
+ size={"medium"}
174
+ disabled={disabled}
175
+ slotProps={{
176
+ inputLabel: { shrink: true },
177
+ }}
178
+ />
97
179
  );
98
180
 
99
- UserName.displayName = "UserName";
100
-
101
- const Password = function ({ disabled }: Pick<PasswordElementProps, "disabled">) {
181
+ const Password = ({ disabled }: Pick<PasswordElementProps, "disabled">) => {
102
182
  const { setValue, getValues } = useFormContext();
103
183
  const password = getValues("password");
104
184
 
@@ -110,7 +190,7 @@ const Password = function ({ disabled }: Pick<PasswordElementProps, "disabled">)
110
190
 
111
191
  return (
112
192
  <PasswordElement
113
- name={"passwordDisabled"}
193
+ name={"password"}
114
194
  label={"Password"}
115
195
  fullWidth
116
196
  required
@@ -125,60 +205,15 @@ const Password = function ({ disabled }: Pick<PasswordElementProps, "disabled">)
125
205
  />
126
206
  );
127
207
  };
128
- Password.displayName = "Password";
129
- ```
130
- ```tsx
131
- // LoginDialog.tsx
132
- import { FormDialog, FormDialogActions, type FormDialogProps, PersistForm } from "@chris-c-brine/form-dialog";
133
- import { useMemo, type FC } from "react";
134
- import { useForm } from "react-hook-form-mui";
135
- import LoginFormBase from "../forms/LoginFormBase";
136
- import {
137
- defaultLoginFormValues,
138
- type LoginForm,
139
- type SubmitLogin,
140
- } from "@pages/LoginPage/LoginPageConstants";
141
- import { useDialog } from "@hooks";
142
- import { devLog } from "@utils";
143
-
144
- const formKey = "dialog-login-form";
145
-
146
- /* Basic Form Dialog Test */
147
- type LoginDialogProps = Pick<ReturnType<typeof useDialog>, "dialogProps"> &
148
- Pick<FormDialogProps<LoginForm>, "onClose"> & {
149
- handleSubmit: SubmitLogin;
150
- };
151
-
152
- const LoginDialog: FC<LoginDialogProps> = ({ dialogProps, handleSubmit }) => {
153
- const formContext = useForm({ defaultValues: defaultLoginFormValues });
154
- const loginForm = useMemo(() => <LoginFormBase name={formKey} columnCount={2} />, []);
155
-
156
- return (
157
- <FormDialog
158
- {...dialogProps}
159
- formProps={{ onSuccess: handleSubmit, formContext, onError: devLog("Form Errors") }}
160
- title={"Basic Form Dialog Test"}
161
- titleProps={{ variant: "h5", textAlign: "center" }}
162
- actions={<FormDialogActions resetProps={{ formKey }} submitProps={{ maxAttempts: 3 }} />}
163
- >
164
- <PersistForm formName={formKey}>{loginForm}</PersistForm>
165
- </FormDialog>
166
- );
167
- };
168
-
169
- LoginDialog.displayName = "LoginDialog";
170
-
171
- export default LoginDialog;
172
208
  ```
173
209
  ```tsx
174
210
  // LoginForm.tsx
175
211
  import { Lock as LockIcon } from "@mui/icons-material";
176
212
  import { Box, Typography, type TypographyProps } from "@mui/material";
177
- import { type SubmitLogin, defaultLoginFormValues } from "@pages/LoginPage/LoginPageConstants";
178
- import { devLog } from "@utils";
213
+ import { type SubmitLogin, defaultLoginFormValues } from "./LoginPageConstants";
214
+ import LoginFormBase from "./LoginFormBase";
179
215
  import { merge } from "lodash";
180
216
  import type { FC, PropsWithChildren } from "react";
181
- import LoginFormBase from "./LoginFormBase";
182
217
  import { FormDialogActions, FormDialogProvider, PaperForm } from "@chris-c-brine/form-dialog";
183
218
 
184
219
  const AltIcon = () => <LockIcon sx={{ mr: 1, fontSize: 20 }} />;
@@ -233,7 +268,7 @@ const LoginPaperForm: FC<LoginPaperFormProps> = ({ children, onSuccess }) => {
233
268
  formProps={{
234
269
  defaultValues: defaultLoginFormValues,
235
270
  onSuccess,
236
- onError: devLog("Form Errors"),
271
+ onError: (e) => console.log(e),
237
272
  }}
238
273
  elevation={3}
239
274
  sx={{
@@ -247,79 +282,42 @@ const LoginPaperForm: FC<LoginPaperFormProps> = ({ children, onSuccess }) => {
247
282
  </FormDialogProvider>
248
283
  );
249
284
  };
250
-
251
285
  ```
252
286
  ```tsx
253
- // LoginPage.tsx
254
- import { useCallback, useEffect, useState, type FC, type PropsWithChildren } from "react";
255
- import { Container, IconButton } from "@mui/material";
256
- import { useUser } from "@features";
257
- import { useDialog } from "@chris-c-brine/form-dialog";
258
- import { useForm, type SubmitHandler } from "react-hook-form-mui";
259
- import { toast } from "react-toastify";
260
- import LoginDialog from "./components/dialogs/LoginDialog";
261
- import LoginForm from "./components/forms/LoginForm";
262
- import { Lock as LockIcon } from "@mui/icons-material";
287
+ // LoginDialog.tsx
288
+ import {useDialog, FormDialog, FormDialogActions, type FormDialogProps, PersistForm } from "@chris-c-brine/form-dialog";
289
+ import { type FC } from "react";
290
+ import { useForm } from "react-hook-form-mui";
291
+ import LoginFormBase from "./LoginFormBase";
292
+ import {
293
+ defaultLoginFormValues,
294
+ type LoginForm,
295
+ type SubmitLogin,
296
+ } from "./LoginPageConstants";
263
297
 
264
- const defaultLoginFormValues = { username: "", password: "" };
265
- type SubmitLogin = SubmitHandler<typeof defaultLoginFormValues>;
266
- const LoginPage: FC = () => {
267
- const { dialogProps, closeDialog, openDialog } = useDialog();
268
- const { setUser } = useUser();
269
- const [showDialog, setShowDialog] = useState(dialogProps.open);
298
+ const formKey = "dialog-login-form";
270
299
 
271
- useEffect(() => {
272
- if (!showDialog && dialogProps.open) {
273
- setShowDialog(true);
274
- }
275
- }, [dialogProps.open, showDialog]);
300
+ /* Basic Form Dialog Test */
301
+ type LoginDialogProps = Pick<ReturnType<typeof useDialog>, "dialogProps"> &
302
+ Pick<FormDialogProps<LoginForm>, "onClose"> & {
303
+ handleSubmit: SubmitLogin;
304
+ };
276
305
 
306
+ export const LoginDialog: FC<LoginDialogProps> = ({ dialogProps, handleSubmit }) => {
277
307
  const formContext = useForm({ defaultValues: defaultLoginFormValues });
278
- const onSuccess: SubmitLogin = useCallback(
279
- (data, event) => {
280
- event?.preventDefault(); // Stop default form submit
281
- formContext.reset(); // Reset form
282
- setUser({ name: data.username, isActive: true }); // Update User (and/or other business logic)
283
- closeDialog(); // Close Dialog
284
- toast.success("Error Dialog Test!"); // Signal success beyond just closing the dialog
285
- },
286
- [formContext, setUser, closeDialog],
287
- );
288
308
 
289
309
  return (
290
- <LoginFormContainer>
291
- <LoginForm onSuccess={onSuccess}>
292
- <IconButton color="primary" onClick={openDialog} sx={{ py: 1 }}>
293
- <LockIcon sx={{ fontSize: 50 }} />
294
- </IconButton>
295
- </LoginForm>
296
- {showDialog && (
297
- <LoginDialog dialogProps={dialogProps} handleSubmit={onSuccess} onClose={undefined} />
298
- )}
299
- </LoginFormContainer>
310
+ <FormDialog
311
+ {...dialogProps}
312
+ formProps={{ onSuccess: handleSubmit, formContext, onError: (e) => console.log(e) }}
313
+ title={"Basic Persist Form Dialog Test"}
314
+ titleProps={{ variant: "h5", textAlign: "center" }}
315
+ actions={<FormDialogActions resetProps={{ formKey }} submitProps={{ maxAttempts: 3 }} />}
316
+ >
317
+ <PersistForm formName={formKey}>
318
+ <LoginFormBase name={formKey} columnCount={2} />
319
+ </PersistForm>
320
+ </FormDialog>
300
321
  );
301
322
  };
302
-
303
- export default LoginPage;
304
-
305
- const LoginFormContainer: FC<PropsWithChildren> = ({ children }) => (
306
- <Container
307
- component="main"
308
- maxWidth={"xs"}
309
- sx={{
310
- display: "flex",
311
- alignItems: "center",
312
- justifyContent: "center",
313
- height: "85vh",
314
- overflow: "hidden",
315
- animation: "fadeIn 1s ease-in-out",
316
- "@keyframes fadeIn": {
317
- from: { opacity: 0 },
318
- to: { opacity: 1 },
319
- },
320
- }}
321
- >
322
- {children}
323
- </Container>
324
- );
325
323
  ```
@@ -31,10 +31,6 @@ export type FormResetButtonProps = Omit<ButtonProps, "onClick"> & {
31
31
  * - Form-wide disabled state from FormDialogContext
32
32
  *
33
33
  * @example
34
- * // Basic usage
35
- * <FormResetButton />
36
- *
37
- * @example
38
34
  * // With persistence integration
39
35
  * <FormResetButton formKey="user-profile-form" />
40
36
  *
@@ -28,9 +28,6 @@ export type FormSubmitButtonProps = Omit<LoadingButtonProps, "onClick"> & {
28
28
  * - Explicit-disabled prop
29
29
  * - Form-wide disabled state from FormDialogContext
30
30
  *
31
- * @example
32
- * // Basic usage
33
- * <FormSubmitButton />
34
31
  *
35
32
  * @example
36
33
  * // With custom text and max attempts
@@ -1,7 +1,7 @@
1
1
  import type { FC, ReactNode } from "react";
2
2
  import { type BlackoutDialogProps } from "./BlackoutDialog";
3
3
  import type { DialogContentProps, DialogTitleProps, DialogActionsProps } from "@mui/material";
4
- export type BaseDialogProps = Omit<BlackoutDialogProps, "children" | "title" | "content"> & {
4
+ export type BaseDialogProps = BlackoutDialogProps & {
5
5
  /**
6
6
  * Title content for the dialog
7
7
  * When provided, renders a DialogTitle component
@@ -12,11 +12,6 @@ export type BaseDialogProps = Omit<BlackoutDialogProps, "children" | "title" | "
12
12
  * Only applied when title is provided
13
13
  */
14
14
  titleProps?: DialogTitleProps;
15
- /**
16
- * Main content of the dialog
17
- * When provided, renders a DialogContent component
18
- */
19
- children?: ReactNode;
20
15
  /**
21
16
  * Props passed to the DialogContent component
22
17
  * Only applied when children is provided
@@ -57,27 +52,11 @@ export type BaseDialogProps = Omit<BlackoutDialogProps, "children" | "title" | "
57
52
  * consistent styling and behavior.
58
53
  *
59
54
  * @example
60
- * // Basic usage with all sections
61
- * <BaseDialog
62
- * open={open}
63
- * onClose={handleClose}
64
- * title="Confirmation"
65
- * actions={
66
- * <>
67
- * <Button onClick={handleCancel}>Cancel</Button>
68
- * <Button onClick={handleConfirm}>Confirm</Button>
69
- * </>
70
- * }
71
- * >
72
- * Are you sure you want to proceed?
73
- * </BaseDialog>
74
- *
75
- * @example
76
55
  * // Customized with section props
77
56
  * <BaseDialog
78
57
  * open={isOpen}
79
58
  * title="Advanced Settings"
80
- * titleProps={{ sx: { bgcolor: 'primary.main', color: 'white' } }}
59
+ * titleProps={{ sx: { backgroundColor: 'primary.main', color: 'white' } }}
81
60
  * contentProps={{ sx: { p: 3 } }}
82
61
  * actionsProps={{ sx: { justifyContent: 'space-between' } }}
83
62
  * actions={<SettingsDialogActions />}
@@ -1,6 +1,6 @@
1
1
  import { type DialogProps } from "@mui/material";
2
- import { type FC, type PropsWithChildren } from "react";
3
- export type BlackoutDialogProps = DialogProps & Partial<PropsWithChildren> & {
2
+ import { type FC } from "react";
3
+ export type BlackoutDialogProps = Omit<DialogProps, "title"> & {
4
4
  /**
5
5
  * An optional unique string identifier
6
6
  * @default 'blackout-dialog'
@@ -6,7 +6,7 @@ import { FormSubmitButtonProps } from "../buttons/FormSubmitButton";
6
6
  /**
7
7
  * Props for the FormDialogActions component
8
8
  */
9
- export type FormDialogActionsProps = PropsWithChildren & {
9
+ export type FormDialogActionsProps = Partial<PropsWithChildren> & {
10
10
  /**
11
11
  * Props to customize the cancel button
12
12
  */
@@ -6,6 +6,7 @@ export * from "./forms/PersistForm";
6
6
  export * from "./buttons/FormCancelButton";
7
7
  export * from "./buttons/FormSubmitButton";
8
8
  export * from "./buttons/FormResetButton";
9
+ export * from "./buttons/LoadingButton";
9
10
  export * from "./dialogs/FormDialogActions";
10
11
  export * from "./AutoGrid";
11
12
  export * from "./GridSpacer";
@@ -1,8 +1,20 @@
1
+ import { DialogProps } from "@mui/material";
1
2
  export interface UseDialogProps {
2
3
  /** Initial open state of the dialog */
3
4
  open?: boolean;
4
- /** Whether to keep the dialog mounted when closed */
5
- keepMounted?: boolean;
5
+ /** Additional props to spread onto the dialog component */
6
+ dialogProps?: Omit<DialogProps, "open" | "onClose">;
7
+ }
8
+ export interface UseDialogReturn extends Omit<DialogProps, "open" | "onClose"> {
9
+ /** Function to close the dialog */
10
+ closeDialog: () => void;
11
+ /** Function to open the dialog */
12
+ openDialog: () => void;
13
+ /** Props to spread onto the dialog component */
14
+ dialogProps: DialogProps & {
15
+ open: boolean;
16
+ onClose: () => void;
17
+ };
6
18
  }
7
19
  /**
8
20
  * Hook for managing dialog state and providing dialog control functions
@@ -21,17 +33,9 @@ export interface UseDialogProps {
21
33
  *
22
34
  * @example
23
35
  * // With initial configuration
24
- * const { dialogProps } = useDialog({ keepMounted: false });
36
+ * const { dialogProps } = useDialog({ keepMounted: true});
25
37
  *
26
38
  *
27
39
  * @param props - Optional configuration options
28
40
  */
29
- export declare const useDialog: (props?: UseDialogProps) => {
30
- closeDialog: () => void;
31
- openDialog: () => void;
32
- dialogProps: {
33
- open: boolean;
34
- onClose: () => void;
35
- keepMounted: boolean;
36
- };
37
- };
41
+ export declare const useDialog: (props?: UseDialogProps) => UseDialogReturn;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export * from "./components";
2
- export * from "./utils";
3
2
  export { usePersistedForm, useFormDialog, useDialog } from "./hooks";
4
3
  export { FormDialogProvider } from "./state/FormDialogProvider";