@applica-software-guru/react-admin 1.5.281 → 1.5.283

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.
Files changed (56) hide show
  1. package/dist/components/Pagination/Pagination.d.ts +15 -0
  2. package/dist/components/Pagination/Pagination.d.ts.map +1 -0
  3. package/dist/components/Pagination/PaginationActions.d.ts +12 -0
  4. package/dist/components/Pagination/PaginationActions.d.ts.map +1 -0
  5. package/dist/components/ra-forms/LongForm/Tab.d.ts.map +1 -1
  6. package/dist/components/ra-forms/WizardForm/Content.d.ts +15 -0
  7. package/dist/components/ra-forms/WizardForm/Content.d.ts.map +1 -0
  8. package/dist/components/ra-forms/WizardForm/Form.d.ts +38 -0
  9. package/dist/components/ra-forms/WizardForm/Form.d.ts.map +1 -0
  10. package/dist/components/ra-forms/WizardForm/Provider.d.ts +48 -0
  11. package/dist/components/ra-forms/WizardForm/Provider.d.ts.map +1 -0
  12. package/dist/components/ra-forms/WizardForm/Stepper.d.ts +18 -0
  13. package/dist/components/ra-forms/WizardForm/Stepper.d.ts.map +1 -0
  14. package/dist/components/ra-forms/WizardForm/Toolbar.d.ts +20 -0
  15. package/dist/components/ra-forms/WizardForm/Toolbar.d.ts.map +1 -0
  16. package/dist/components/ra-forms/WizardForm/index.d.ts +4 -0
  17. package/dist/components/ra-forms/WizardForm/index.d.ts.map +1 -0
  18. package/dist/components/ra-forms/index.d.ts +1 -0
  19. package/dist/components/ra-forms/index.d.ts.map +1 -1
  20. package/dist/components/ra-lists/List.d.ts.map +1 -1
  21. package/dist/components/ra-lists/SimpleList.d.ts +2 -2
  22. package/dist/react-admin.cjs.js +58 -58
  23. package/dist/react-admin.cjs.js.gz +0 -0
  24. package/dist/react-admin.cjs.js.map +1 -1
  25. package/dist/react-admin.es.js +9507 -9168
  26. package/dist/react-admin.es.js.gz +0 -0
  27. package/dist/react-admin.es.js.map +1 -1
  28. package/dist/react-admin.umd.js +56 -56
  29. package/dist/react-admin.umd.js.gz +0 -0
  30. package/dist/react-admin.umd.js.map +1 -1
  31. package/dist/utils/index.d.ts +1 -0
  32. package/dist/utils/index.d.ts.map +1 -1
  33. package/dist/utils/react.d.ts +14 -0
  34. package/dist/utils/react.d.ts.map +1 -0
  35. package/package.json +1 -1
  36. package/src/components/Pagination/Pagination.tsx +184 -0
  37. package/src/components/Pagination/PaginationActions.tsx +84 -0
  38. package/src/components/Pagination/index.ts +2 -0
  39. package/src/components/ra-forms/LongForm/Tab.tsx +2 -11
  40. package/src/components/ra-forms/WizardForm/Content.tsx +82 -0
  41. package/src/components/ra-forms/WizardForm/Form.tsx +113 -0
  42. package/src/components/ra-forms/WizardForm/Provider.tsx +62 -0
  43. package/src/components/ra-forms/WizardForm/Stepper.tsx +113 -0
  44. package/src/components/ra-forms/WizardForm/Toolbar.tsx +58 -0
  45. package/src/components/ra-forms/WizardForm/index.ts +4 -0
  46. package/src/components/ra-forms/index.ts +1 -0
  47. package/src/components/ra-lists/List.tsx +2 -1
  48. package/src/playground/App.jsx +2 -1
  49. package/src/playground/components/ra-forms/TestWizardForm/AdvancedUsage.jsx +176 -0
  50. package/src/playground/components/ra-forms/TestWizardForm/BaseUsage.jsx +115 -0
  51. package/src/playground/components/ra-forms/TestWizardForm/TestWizardForm.jsx +27 -0
  52. package/src/playground/components/ra-forms/TestWizardForm/index.jsx +1 -0
  53. package/src/playground/components/ra-forms/index.jsx +1 -0
  54. package/src/playground/menu.jsx +8 -0
  55. package/src/utils/index.ts +1 -0
  56. package/src/utils/react.ts +25 -0
@@ -1,5 +1,6 @@
1
1
  export * from './lang';
2
2
  export * from './localStorage';
3
3
  export * from './localizedValue';
4
+ export * from './react';
4
5
  export * from './time';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+ /**
3
+ * This file contains utility functions that can be reused across the project.
4
+ */
5
+ /**
6
+ * Originally made for LongForm component to find sources.
7
+ * Walks the children of a React element and calls a callback function for each child.
8
+ *
9
+ * @param {ReactNode} children - The children of a React element.
10
+ * @param {Function} callback - The callback function to call for each child.
11
+ */
12
+ declare function walkChildren(children: ReactNode, callback: (el: ReactElement) => void): void;
13
+ export { walkChildren };
14
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../../src/utils/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAkB,MAAM,OAAO,CAAC;AAGhE;;GAEG;AAEH;;;;;;GAMG;AACH,iBAAS,YAAY,CAAC,QAAQ,WAAgB,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,QAQnF;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
package/package.json CHANGED
@@ -106,5 +106,5 @@
106
106
  "type": "module",
107
107
  "types": "dist/index.d.ts",
108
108
  "typings": "dist/index.d.ts",
109
- "version": "1.5.281"
109
+ "version": "1.5.283"
110
110
  }
@@ -0,0 +1,184 @@
1
+ import { FC, ReactElement, memo, useCallback, useMemo, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { TablePagination, TablePaginationBaseProps, Theme, Toolbar, useMediaQuery } from '@mui/material';
4
+ import {
5
+ ComponentPropType,
6
+ ListPaginationContextValue,
7
+ sanitizeListRestProps,
8
+ useListPaginationContext,
9
+ useTranslate
10
+ } from 'ra-core';
11
+
12
+ import { PaginationActions, PaginationActionsProps } from './PaginationActions';
13
+ import { debounce } from 'lodash';
14
+
15
+ const Pagination: FC<PaginationProps> = memo((props) => {
16
+ const { rowsPerPageOptions = DefaultRowsPerPageOptions, actions, limit = null, ...rest } = props;
17
+ const { isLoading, hasNextPage, page, perPage, total, setPage, setPerPage } = useListPaginationContext(props);
18
+ const translate = useTranslate();
19
+ const isSmall = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
20
+ const [currentPage, setCurrentPage] = useState(0); // Stato per la UI
21
+
22
+ const totalPages = useMemo(() => {
23
+ return total != null ? Math.ceil(total / perPage) : undefined;
24
+ }, [perPage, total]);
25
+
26
+ // eslint-disable-next-line react-hooks/exhaustive-deps
27
+ const debouncedPageChange = useCallback(
28
+ debounce((page) => {
29
+ setPage(page + 1);
30
+ }, 500),
31
+ [setPage]
32
+ );
33
+
34
+ /**
35
+ * Warning: Material UI's page is 0-based
36
+ */
37
+ const handlePageChange = useCallback(
38
+ (event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null, page: number) => {
39
+ if (!event) {
40
+ return;
41
+ }
42
+
43
+ event.preventDefault();
44
+ if (page < 0 || (totalPages !== undefined && page > totalPages - 1)) {
45
+ throw new Error(
46
+ translate('ra.navigation.page_out_of_boundaries', {
47
+ page: page + 1
48
+ })
49
+ );
50
+ }
51
+
52
+ const arrowSelected =
53
+ ((event.target as HTMLElement).dataset?.testid || (event.target as HTMLElement).classList?.value) ?? '';
54
+ // check if user is clicking on the arrows or on the numbers
55
+ const isArrowClick =
56
+ arrowSelected.includes('MuiPaginationItem-previousNext') ||
57
+ arrowSelected.includes('NavigateBeforeIcon') ||
58
+ arrowSelected.includes('NavigateNextIcon') ||
59
+ arrowSelected.includes('KeyboardArrowLeftIcon') ||
60
+ arrowSelected.includes('KeyboardArrowRightIcon');
61
+
62
+ setCurrentPage(page);
63
+
64
+ if (isArrowClick) {
65
+ // apply debounced API call for arrows clicks
66
+ debouncedPageChange(page);
67
+ } else {
68
+ // apply immediate API call for number clicks
69
+ setPage(page + 1);
70
+ }
71
+ },
72
+ [debouncedPageChange, setPage, translate, totalPages]
73
+ );
74
+
75
+ const handlePerPageChange = useCallback(
76
+ (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
77
+ setPerPage(Number(event.target.value));
78
+ },
79
+ [setPerPage]
80
+ );
81
+
82
+ const labelDisplayedRows = useCallback(
83
+ ({ from, to, count }: { from: number; to: number; count: number }) =>
84
+ count === -1 && hasNextPage
85
+ ? translate('ra.navigation.partial_page_range_info', {
86
+ offsetBegin: from,
87
+ offsetEnd: to,
88
+ _: `%{from}-%{to} of more than %{to}`
89
+ })
90
+ : translate('ra.navigation.page_range_info', {
91
+ offsetBegin: from,
92
+ offsetEnd: to,
93
+ total: count === -1 ? to : count,
94
+ _: `%{from}-%{to} of %{count === -1 ? to : count}`
95
+ }),
96
+ [translate, hasNextPage]
97
+ );
98
+
99
+ const labelItem = useCallback(
100
+ (type: string) => translate(`ra.navigation.${type}`, { _: `Go to ${type} page` }),
101
+ [translate]
102
+ );
103
+
104
+ if (isLoading) {
105
+ return <Toolbar variant="dense" />;
106
+ }
107
+
108
+ // Avoid rendering TablePagination if "page" value is invalid
109
+ if (total === 0 || page < 1 || (totalPages !== undefined && page > totalPages)) {
110
+ if (limit != null && process.env.NODE_ENV === 'development') {
111
+ console.warn(
112
+ 'The Pagination limit prop is deprecated. Empty state should be handled by the component displaying data (Datagrid, SimpleList).'
113
+ );
114
+ }
115
+ return null;
116
+ }
117
+
118
+ if (isSmall) {
119
+ return (
120
+ <TablePagination
121
+ count={total == null ? -1 : total}
122
+ rowsPerPage={perPage}
123
+ page={currentPage}
124
+ onPageChange={handlePageChange}
125
+ rowsPerPageOptions={emptyArray}
126
+ component="span"
127
+ labelDisplayedRows={labelDisplayedRows}
128
+ {...sanitizeListRestProps(rest)}
129
+ />
130
+ );
131
+ }
132
+
133
+ const ActionsComponent = actions
134
+ ? actions // overridden by caller
135
+ : !isLoading && total != null
136
+ ? PaginationActions // regular navigation
137
+ : undefined; // partial navigation (uses default TablePaginationActions)
138
+
139
+ return (
140
+ <TablePagination
141
+ count={total == null ? -1 : total}
142
+ rowsPerPage={perPage}
143
+ page={currentPage}
144
+ onPageChange={handlePageChange}
145
+ onRowsPerPageChange={handlePerPageChange}
146
+ // @ts-ignore
147
+ ActionsComponent={ActionsComponent}
148
+ nextIconButtonProps={{
149
+ disabled: !hasNextPage
150
+ }}
151
+ component="span"
152
+ labelRowsPerPage={translate('ra.navigation.page_rows_per_page')}
153
+ labelDisplayedRows={labelDisplayedRows}
154
+ getItemAriaLabel={labelItem}
155
+ rowsPerPageOptions={rowsPerPageOptions}
156
+ {...sanitizeListRestProps(rest)}
157
+ />
158
+ );
159
+ });
160
+
161
+ Pagination.propTypes = {
162
+ actions: ComponentPropType,
163
+ limit: PropTypes.element,
164
+ rowsPerPageOptions: PropTypes.arrayOf(
165
+ PropTypes.oneOfType([
166
+ PropTypes.number,
167
+ PropTypes.shape({
168
+ label: PropTypes.string.isRequired,
169
+ value: PropTypes.number.isRequired
170
+ })
171
+ ])
172
+ ) as PropTypes.Validator<(number | { label: string; value: number })[] | null | undefined>
173
+ };
174
+
175
+ const DefaultRowsPerPageOptions = [5, 10, 25, 50];
176
+ const emptyArray: any[] = [];
177
+
178
+ interface PaginationProps extends TablePaginationBaseProps, Partial<ListPaginationContextValue> {
179
+ rowsPerPageOptions?: Array<number | { label: string; value: number }>;
180
+ actions?: FC<PaginationActionsProps>;
181
+ limit?: ReactElement;
182
+ }
183
+
184
+ export { Pagination };
@@ -0,0 +1,84 @@
1
+ import { FC, memo } from 'react';
2
+ import { styled } from '@mui/material/styles';
3
+ import { Pagination, PaginationProps } from '@mui/material';
4
+ import PropTypes from 'prop-types';
5
+ import { useTranslate } from 'ra-core';
6
+
7
+ const PaginationActions: FC<PaginationActionsProps> = memo((props) => {
8
+ const { page, rowsPerPage, count, onPageChange, size = 'small', className, ...rest } = props;
9
+ const translate = useTranslate();
10
+
11
+ const nbPages = Math.ceil(count / rowsPerPage) || 1;
12
+
13
+ if (nbPages === 1) {
14
+ return <Root className={className} />;
15
+ }
16
+
17
+ function getItemAriaLabel(type: 'page' | 'first' | 'last' | 'next' | 'previous', page: number, selected: boolean) {
18
+ if (type === 'page') {
19
+ return selected
20
+ ? translate('ra.navigation.current_page', {
21
+ page,
22
+ _: `page ${page}`
23
+ })
24
+ : translate('ra.navigation.page', {
25
+ page,
26
+ _: `Go to page ${page}`
27
+ });
28
+ }
29
+ return translate(`ra.navigation.${type}`, { _: `Go to ${type} page` });
30
+ }
31
+
32
+ return (
33
+ <Root className={className}>
34
+ <Pagination
35
+ size={size}
36
+ count={nbPages}
37
+ // <TablePagination>, the parent, uses 0-based pagination
38
+ // while <Pagination> uses 1-based pagination
39
+ page={page + 1}
40
+ onChange={(e: any, page) => onPageChange(e, page - 1)}
41
+ {...sanitizeRestProps(rest)}
42
+ getItemAriaLabel={getItemAriaLabel}
43
+ />
44
+ </Root>
45
+ );
46
+ });
47
+
48
+ interface PaginationActionsProps extends PaginationProps {
49
+ page: number;
50
+ rowsPerPage: number;
51
+ count: number;
52
+ onPageChange: (event: MouseEvent, page: number) => void;
53
+ }
54
+ /**
55
+ * PaginationActions propTypes are copied over from Material UI’s
56
+ * TablePaginationActions propTypes. See
57
+ * https://github.com/mui/material-ui/blob/869692ecf3812bc4577ed4dde81a9911c5949695/packages/material-ui/src/TablePaginationActions/TablePaginationActions.js#L53-L85
58
+ * for reference.
59
+ */
60
+ PaginationActions.propTypes = {
61
+ count: PropTypes.number.isRequired,
62
+ onPageChange: PropTypes.func.isRequired,
63
+ page: PropTypes.number.isRequired,
64
+ rowsPerPage: PropTypes.number.isRequired,
65
+ color: PropTypes.oneOf(['primary', 'secondary', 'standard']),
66
+ size: PropTypes.oneOf(['small', 'medium', 'large'])
67
+ };
68
+
69
+ const PREFIX = 'RaPaginationActions';
70
+
71
+ const Root = styled('div', {
72
+ name: PREFIX,
73
+ overridesResolver: (styles) => styles.root
74
+ })(() => ({
75
+ flexShrink: 0,
76
+ ml: 4
77
+ }));
78
+
79
+ function sanitizeRestProps({ nextIconButtonProps, backIconButtonProps, slotProps, ...rest }: any) {
80
+ return rest;
81
+ }
82
+
83
+ export { PaginationActions };
84
+ export type { PaginationActionsProps };
@@ -0,0 +1,2 @@
1
+ export * from './Pagination';
2
+ export * from './PaginationActions';
@@ -3,25 +3,16 @@ import { useIsActive } from '@/components/ra-forms/LongForm/hooks';
3
3
  import { IItem } from '@/components/ra-forms/LongForm/types';
4
4
  import { getId } from '@/components/ra-forms/LongForm/utils';
5
5
  import { Optional } from '@/types';
6
+ import { walkChildren } from '@/utils';
6
7
  import { Box } from '@mui/material';
7
8
  import _ from 'lodash';
8
- import React, { Children, ReactElement, ReactNode, cloneElement, isValidElement, useEffect, useMemo } from 'react';
9
+ import React, { Children, cloneElement, isValidElement, useEffect, useMemo } from 'react';
9
10
  import { useFormState } from 'react-hook-form';
10
11
 
11
12
  type IBaseItemProps = React.PropsWithChildren<Optional<IItem, 'id' | 'index'> & { sources: Array<string> }>;
12
13
  type ITabProps = IBaseItemProps;
13
14
  type IGroupProps = IBaseItemProps;
14
15
 
15
- function walkChildren(children: ReactNode = [], callback: (el: ReactElement) => void) {
16
- const _children = _.isArray(children) ? children : [children];
17
- const validChildren = _.filter(_children, (child) => isValidElement(child));
18
- _.each(validChildren, (child) => {
19
- callback(child);
20
- // @ts-ignore @ts-expect-error Property 'children' does not exist on type '{}'.
21
- walkChildren(child?.props?.children ?? [], callback);
22
- });
23
- }
24
-
25
16
  function BaseItem(props: IBaseItemProps) {
26
17
  const { errors } = useFormState();
27
18
  const countErrors = useErrorCount();
@@ -0,0 +1,82 @@
1
+ import { Children, ReactElement, ReactNode, isValidElement, useMemo } from 'react';
2
+ import { Box, Button, Divider, Grid } from '@mui/material';
3
+ import { MainCard } from '@/components/MainCard';
4
+ import { Toolbar } from './Toolbar';
5
+ import { useTranslate } from 'react-admin';
6
+ import _ from 'lodash';
7
+ import { useWizardFormContext } from './Provider';
8
+ import { Stepper } from './Stepper';
9
+ import { walkChildren } from '@/utils';
10
+
11
+ interface ContentProps {
12
+ title?: ReactNode | string;
13
+ subheader?: ReactNode | string;
14
+ secondary?: ReactNode | string;
15
+ toolbar?: ReactElement;
16
+ progress?: ReactNode;
17
+ isSmall: boolean;
18
+ modal: boolean;
19
+ sx?: any;
20
+ setCurrentStep: (step: number) => void;
21
+ }
22
+
23
+ function Content({ title, subheader, secondary, toolbar, progress, isSmall, modal, sx, setCurrentStep }: ContentProps) {
24
+ const { currentStep, steps } = useWizardFormContext();
25
+ const translate = useTranslate();
26
+ const isHorizontal = isSmall || modal;
27
+
28
+ const cancelButton = useMemo(() => {
29
+ if (!toolbar) return null;
30
+
31
+ return Children.toArray(toolbar.props.children).find(
32
+ (child) =>
33
+ isValidElement(child) && child.type === Button && child.props?.children === translate('ra.action.cancel')
34
+ );
35
+ }, [toolbar, translate]);
36
+
37
+ const stepFields: Array<Array<string>> = steps.map((step: ReactElement) => {
38
+ const fields: Array<string> = [];
39
+ const { sources } = step.props;
40
+ if (sources !== undefined) {
41
+ fields.push(...sources);
42
+ } else {
43
+ walkChildren(step, (el) => {
44
+ if (el?.props?.source !== undefined) {
45
+ fields.push(el.props.source);
46
+ }
47
+ });
48
+ }
49
+ return fields;
50
+ });
51
+
52
+ return (
53
+ <Grid container spacing={2} sx={sx}>
54
+ <Grid item xl={modal ? 12 : 2} lg={modal ? 12 : 3} md={modal ? 12 : 3} sm={modal ? 12 : 4} xs={12}>
55
+ {progress || <Stepper isHorizontal={isHorizontal} setCurrentStep={setCurrentStep} stepFields={stepFields} />}
56
+ </Grid>
57
+ {modal ? <Divider sx={{ width: '100%' }} /> : null}
58
+ <Grid
59
+ item
60
+ xl={modal ? 12 : 10}
61
+ lg={modal ? 12 : 9}
62
+ md={modal ? 12 : 9}
63
+ sm={modal ? 12 : 8}
64
+ xs={12}
65
+ sx={{ paddingTop: modal ? '0 !important' : null }}
66
+ >
67
+ <MainCard
68
+ title={title || steps[currentStep].props.label}
69
+ subheader={subheader}
70
+ secondary={secondary}
71
+ border={!modal}
72
+ divider
73
+ >
74
+ <Box>{steps[currentStep]}</Box>
75
+ {toolbar && !cancelButton ? toolbar : <Toolbar cancelButton={cancelButton} />}
76
+ </MainCard>
77
+ </Grid>
78
+ </Grid>
79
+ );
80
+ }
81
+
82
+ export { Content };
@@ -0,0 +1,113 @@
1
+ import { Children, ReactElement, ReactNode, useState } from 'react';
2
+ import { Theme, styled, useMediaQuery } from '@mui/material';
3
+ import { FormProps, Form as RaForm } from 'react-admin';
4
+ import { Provider } from './Provider';
5
+ import { Content } from './Content';
6
+
7
+ type WizardFormProps = FormProps & {
8
+ toolbar?: ReactElement;
9
+ progress?: ReactElement;
10
+ title?: ReactNode | string;
11
+ subheader?: ReactNode | string;
12
+ secondary?: ReactNode | string;
13
+ sx?: any;
14
+ modal?: boolean;
15
+ };
16
+
17
+ const StyledForm = styled(RaForm, {
18
+ shouldForwardProp: (prop) => prop !== 'modal'
19
+ })<{ modal: boolean }>(({ theme, modal }) => ({
20
+ [theme.breakpoints.down('sm')]: !modal
21
+ ? {
22
+ paddingBottom: `${theme.spacing(2.5)}`
23
+ }
24
+ : {},
25
+ '& .MuiToolbar-root': {
26
+ paddingLeft: '0 !important',
27
+ paddingRight: '0 !important',
28
+ paddingBottom: '0 !important',
29
+ paddingTop: `${theme.spacing(2)} !important`
30
+ }
31
+ }));
32
+
33
+ /**
34
+ * Form component for handling wizard-style forms with multiple steps.
35
+ *
36
+ * @param {object} props - The properties object.
37
+ * @param {React.ReactNode} props.children - The child components representing each step of the wizard.
38
+ * @param {React.ReactNode} props.toolbar - The toolbar component to be displayed.
39
+ * @param {React.ReactNode} props.progress - The progress indicator component.
40
+ * @param {string} props.title - The title of the form.
41
+ * @param {string | null} [props.subheader=null] - The subheader text of the form.
42
+ * @param {React.ReactNode | null} [props.secondary=null] - The secondary content of the form.
43
+ * @param {object} props.sx - The style object for custom styling.
44
+ * @param {boolean} [props.modal=false] - Flag indicating if the form is displayed in a modal.
45
+ * @param {object} props.rest - Additional properties passed to the form.
46
+ *
47
+ * @returns {JSX.Element | null} The rendered form component.
48
+ */
49
+ function Form({
50
+ children,
51
+ toolbar,
52
+ progress,
53
+ title,
54
+ subheader = null,
55
+ secondary = null,
56
+ sx,
57
+ modal = false,
58
+ ...props
59
+ }: WizardFormProps): JSX.Element | null {
60
+ const [currentStep, setCurrentStep] = useState(0);
61
+ const steps = Children.toArray(children) as Array<ReactElement>;
62
+ const isSmall = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
63
+
64
+ function hasNextStep(): boolean {
65
+ return currentStep < steps.length - 1;
66
+ }
67
+ function hasPreviousStep(): boolean {
68
+ return currentStep > 0;
69
+ }
70
+
71
+ function goToNextStep(): void {
72
+ setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
73
+ }
74
+
75
+ function goToPreviousStep(): void {
76
+ setCurrentStep((prev) => Math.max(prev - 1, 0));
77
+ }
78
+
79
+ const wizardFormContextValue = {
80
+ currentStep,
81
+ steps,
82
+ hasNextStep,
83
+ hasPreviousStep,
84
+ goToNextStep,
85
+ goToPreviousStep
86
+ };
87
+
88
+ return (
89
+ <Provider value={wizardFormContextValue}>
90
+ <StyledForm {...props} modal={modal}>
91
+ <Content
92
+ title={title}
93
+ subheader={subheader}
94
+ secondary={secondary}
95
+ modal={modal}
96
+ sx={sx}
97
+ toolbar={toolbar}
98
+ progress={progress}
99
+ isSmall={isSmall}
100
+ setCurrentStep={setCurrentStep}
101
+ />
102
+ </StyledForm>
103
+ </Provider>
104
+ );
105
+ }
106
+
107
+ function Step({ children }: { children: ReactNode; label: string; icon?: ReactNode; sources?: Array<string> }) {
108
+ return <>{children}</>;
109
+ }
110
+
111
+ Form.Step = Step;
112
+
113
+ export { Form };
@@ -0,0 +1,62 @@
1
+ import { ReactElement, ReactNode, createContext, useContext } from 'react';
2
+
3
+ interface WizardFormContextValue {
4
+ currentStep: number;
5
+ steps: Array<ReactElement>;
6
+ hasNextStep: () => boolean;
7
+ hasPreviousStep: () => boolean;
8
+ goToNextStep: () => void;
9
+ goToPreviousStep: () => void;
10
+ }
11
+
12
+ const WizardFormContext = createContext<WizardFormContextValue | undefined>(undefined);
13
+
14
+ /**
15
+ * Custom hook to access the wizard form context.
16
+ * This hook provides access to the current step, steps, and navigation methods
17
+ * for a wizard form.
18
+ *
19
+ * @throws Will throw an error if used outside of a `WizardFormProvider`.
20
+ *
21
+ * @property {number} currentStep - The index of the current step in the wizard form.
22
+ * @property {Array<ReactElement>} steps - An array of React elements representing the steps of the wizard form.
23
+ *
24
+ * @method hasNextStep
25
+ * @returns {boolean} - Returns true if there is a next step available, otherwise false.
26
+ *
27
+ * @method hasPreviousStep
28
+ * @returns {boolean} - Returns true if there is a previous step available, otherwise false.
29
+ *
30
+ * @method goToNextStep
31
+ * @returns {void} - Advances to the next step in the wizard form.
32
+ *
33
+ * @method goToPreviousStep
34
+ * @returns {void} - Goes back to the previous step in the wizard form.
35
+ *
36
+ *
37
+ * @example
38
+ * const { hasNextStep, goToNextStep } = useWizardFormContext();
39
+ * return (
40
+ * <Button disabled={!hasNextStep} onClick={goToNextStep}>
41
+ * Next
42
+ * </Button>
43
+ * );
44
+ */
45
+ function useWizardFormContext() {
46
+ const context = useContext(WizardFormContext);
47
+ if (!context) {
48
+ throw new Error('useWizardFormContext must be used within a WizardFormProvider');
49
+ }
50
+ return context;
51
+ }
52
+
53
+ interface WizardFormProviderProps {
54
+ value: WizardFormContextValue;
55
+ children: ReactNode;
56
+ }
57
+
58
+ function Provider({ value, children }: WizardFormProviderProps) {
59
+ return <WizardFormContext.Provider value={value}>{children}</WizardFormContext.Provider>;
60
+ }
61
+
62
+ export { Provider, useWizardFormContext };