@applica-software-guru/react-admin 1.0.36 → 1.0.38

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 (111) hide show
  1. package/.prettierrc +4 -4
  2. package/dist/components/MainCard.d.ts.map +1 -1
  3. package/dist/components/index.d.ts +1 -0
  4. package/dist/components/index.d.ts.map +1 -1
  5. package/dist/components/ra-forms/Edit.d.ts.map +1 -1
  6. package/dist/components/ra-forms/LongForm/{DispositionProps.d.ts → types.d.ts} +10 -4
  7. package/dist/components/ra-forms/LongForm/types.d.ts.map +1 -0
  8. package/dist/components/ra-forms/LongForm/useFormRootPath.d.ts +3 -3
  9. package/dist/components/ra-forms/LongForm/useFormRootPath.d.ts.map +1 -1
  10. package/dist/components/ra-inputs/AttachmentInput.d.ts +4 -1
  11. package/dist/components/ra-inputs/AttachmentInput.d.ts.map +1 -1
  12. package/dist/components/ra-inputs/LabeledInput.d.ts +2 -1
  13. package/dist/components/ra-inputs/LabeledInput.d.ts.map +1 -1
  14. package/dist/components/ra-inputs/SmartTextInput.d.ts.map +1 -1
  15. package/dist/contexts/ThemeConfigContext.d.ts.map +1 -1
  16. package/dist/hooks/useAppConfig.d.ts +3 -0
  17. package/dist/hooks/useAppConfig.d.ts.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/react-admin.cjs.js +49 -49
  20. package/dist/react-admin.es.js +7942 -7640
  21. package/dist/react-admin.umd.js +50 -50
  22. package/package.json +3 -2
  23. package/playground/src/.prettierrc +8 -0
  24. package/playground/src/App.js +21 -21
  25. package/playground/src/components/pages/CustomPage.jsx +4 -4
  26. package/playground/src/components/pages/index.jsx +2 -2
  27. package/playground/src/components/ra-forms/DeviceForm.js +7 -14
  28. package/playground/src/components/ra-forms/UserForm.js +10 -24
  29. package/playground/src/components/ra-forms/index.js +4 -5
  30. package/playground/src/components/ra-lists/DeviceList.js +8 -16
  31. package/playground/src/components/ra-lists/UserList.js +17 -40
  32. package/playground/src/components/ra-lists/index.js +4 -4
  33. package/playground/src/contexts/index.js +1 -1
  34. package/playground/src/{resource → entities}/device.js +6 -6
  35. package/playground/src/{resource → entities}/i18n-message.js +1 -2
  36. package/playground/src/entities/index.js +4 -0
  37. package/playground/src/{resource → entities}/notification.js +0 -2
  38. package/playground/src/menu.js +18 -9
  39. package/playground/src/theme.js +10 -2
  40. package/react-admin.code-workspace +9 -0
  41. package/src/components/ActionsMenu.tsx +91 -0
  42. package/src/components/MainCard.jsx +29 -32
  43. package/src/components/index.jsx +21 -33
  44. package/src/components/ra-buttons/CreateInDialogButton.tsx +261 -0
  45. package/src/components/ra-custom/ListItem.tsx +147 -0
  46. package/src/components/ra-custom/index.tsx +2 -0
  47. package/src/components/ra-fields/AttachmentField.tsx +88 -0
  48. package/src/components/ra-fields/BaseAttachmentField.tsx +82 -0
  49. package/src/components/ra-forms/Edit.jsx +12 -6
  50. package/src/components/ra-forms/FormHeader.tsx +63 -0
  51. package/src/components/ra-forms/LongForm/LongForm.tsx +59 -0
  52. package/src/components/ra-forms/LongForm/LongFormSidebar.tsx +44 -0
  53. package/src/components/ra-forms/LongForm/{LongFormTab.jsx → LongFormTab.tsx} +47 -46
  54. package/src/components/ra-forms/LongForm/LongFormTabs.tsx +72 -0
  55. package/src/components/ra-forms/LongForm/LongFormView.tsx +152 -0
  56. package/{dist/components/ra-forms/LongForm/index.d.ts → src/components/ra-forms/LongForm/index.tsx} +1 -2
  57. package/src/components/ra-forms/LongForm/types.ts +15 -0
  58. package/src/components/ra-forms/LongForm/useFormRootPath.ts +26 -0
  59. package/src/components/ra-forms/{SimpleForm.jsx → SimpleForm.tsx} +38 -27
  60. package/src/components/ra-inputs/AttachmentInput.jsx +42 -25
  61. package/src/components/ra-inputs/LabeledInput.jsx +27 -32
  62. package/src/components/ra-inputs/SelectInput.jsx +11 -11
  63. package/src/components/ra-inputs/SmartTextInput.jsx +22 -26
  64. package/src/components/ra-inputs/TextInput.jsx +9 -9
  65. package/src/contexts/AppConfigContext.tsx +67 -0
  66. package/src/contexts/ThemeConfigContext.jsx +25 -29
  67. package/src/index.jsx +13 -7
  68. package/src/utils/index.js +2 -2
  69. package/src/utils/lang.js +7 -7
  70. package/src/utils/time.js +7 -7
  71. package/dist/components/ActionsMenu.d.ts +0 -15
  72. package/dist/components/ActionsMenu.d.ts.map +0 -1
  73. package/dist/components/ra-buttons/CreateInDialogButton.d.ts +0 -37
  74. package/dist/components/ra-buttons/CreateInDialogButton.d.ts.map +0 -1
  75. package/dist/components/ra-fields/AttachmentField.d.ts +0 -28
  76. package/dist/components/ra-fields/AttachmentField.d.ts.map +0 -1
  77. package/dist/components/ra-fields/BaseAttachmentField.d.ts +0 -17
  78. package/dist/components/ra-fields/BaseAttachmentField.d.ts.map +0 -1
  79. package/dist/components/ra-forms/FormHeader.d.ts +0 -17
  80. package/dist/components/ra-forms/FormHeader.d.ts.map +0 -1
  81. package/dist/components/ra-forms/LongForm/DispositionProps.d.ts.map +0 -1
  82. package/dist/components/ra-forms/LongForm/LongForm.d.ts +0 -45
  83. package/dist/components/ra-forms/LongForm/LongForm.d.ts.map +0 -1
  84. package/dist/components/ra-forms/LongForm/LongFormHeader.d.ts +0 -20
  85. package/dist/components/ra-forms/LongForm/LongFormHeader.d.ts.map +0 -1
  86. package/dist/components/ra-forms/LongForm/LongFormTab.d.ts +0 -43
  87. package/dist/components/ra-forms/LongForm/LongFormTab.d.ts.map +0 -1
  88. package/dist/components/ra-forms/LongForm/LongFormTabs.d.ts +0 -21
  89. package/dist/components/ra-forms/LongForm/LongFormTabs.d.ts.map +0 -1
  90. package/dist/components/ra-forms/LongForm/LongFormView.d.ts +0 -29
  91. package/dist/components/ra-forms/LongForm/LongFormView.d.ts.map +0 -1
  92. package/dist/components/ra-forms/LongForm/index.d.ts.map +0 -1
  93. package/dist/components/ra-forms/SimpleForm.d.ts +0 -26
  94. package/dist/components/ra-forms/SimpleForm.d.ts.map +0 -1
  95. package/dist/contexts/AppConfigContext.d.ts +0 -22
  96. package/dist/contexts/AppConfigContext.d.ts.map +0 -1
  97. package/playground/src/resource/index.js +0 -4
  98. package/src/components/ActionsMenu.jsx +0 -77
  99. package/src/components/ra-buttons/CreateInDialogButton.jsx +0 -203
  100. package/src/components/ra-fields/AttachmentField.jsx +0 -82
  101. package/src/components/ra-fields/BaseAttachmentField.jsx +0 -72
  102. package/src/components/ra-forms/FormHeader.jsx +0 -42
  103. package/src/components/ra-forms/LongForm/DispositionProps.jsx +0 -10
  104. package/src/components/ra-forms/LongForm/LongForm.jsx +0 -38
  105. package/src/components/ra-forms/LongForm/LongFormHeader.jsx +0 -24
  106. package/src/components/ra-forms/LongForm/LongFormTabs.jsx +0 -63
  107. package/src/components/ra-forms/LongForm/LongFormView.jsx +0 -129
  108. package/src/components/ra-forms/LongForm/index.jsx +0 -2
  109. package/src/components/ra-forms/LongForm/useFormRootPath.jsx +0 -22
  110. package/src/contexts/AppConfigContext.jsx +0 -54
  111. /package/playground/src/{resource → entities}/user.js +0 -0
@@ -1,203 +0,0 @@
1
- import {
2
- Button,
3
- CreateButtonClasses,
4
- CreateContextProvider,
5
- SaveButton,
6
- useCreateController,
7
- useNotify,
8
- useRedirect,
9
- useResourceContext,
10
- useTranslate,
11
- } from 'react-admin'
12
- import { Dialog, Fab, useMediaQuery } from '@mui/material'
13
- import React, { useCallback, useState } from 'react'
14
-
15
- import { Add } from '@mui/icons-material'
16
- import PropTypes from 'prop-types'
17
- import { Toolbar } from '../ra-forms'
18
- import clsx from 'clsx'
19
- import { styled } from '@mui/material/styles'
20
- import { useAppConfig } from '../../hooks'
21
- import { useQueryClient } from 'react-query'
22
-
23
- const updateColl = (old, data) => {
24
- const id = data.id
25
- if (!old) return [data]
26
- const index = old.findIndex((record) => record.id === id)
27
- if (index === -1) {
28
- return [...old, data]
29
- }
30
-
31
- return [...old.slice(0, index), { ...old[index], ...data }, ...old.slice(index + 1)]
32
- }
33
- const setManyReferenceQueryData = (res, data) => {
34
- const result = res && res.data ? { data: updateColl(res.data, data), total: res.total + 1 } : res
35
- return result
36
- }
37
- const setManyQueryData = (coll, data) => (coll && coll.length > 0 ? updateColl(coll, data) : coll)
38
- const setListQueryData = (res, data) =>
39
- res && res.data ? { ...res, data: updateColl(res.data, data), total: res.total + 1 } : res
40
-
41
- const CreateInDialogContent = ({ onClose, record, children, redirect: _redirect }) => {
42
- const queryClient = useQueryClient()
43
- const resource = useResourceContext()
44
- const redirect = useRedirect()
45
- const notify = useNotify()
46
- const handleSuccess = useCallback(
47
- (data) => {
48
- const now = Date.now()
49
- const updatedAt = now
50
-
51
- queryClient.setQueryData([resource, 'getOne', { id: data.id }], data)
52
- queryClient.setQueriesData([resource, 'getList'], (res) => setListQueryData(res, data), {
53
- updatedAt,
54
- })
55
- queryClient.setQueriesData([resource, 'getMany'], (coll) => setManyQueryData(coll, data), {
56
- updatedAt,
57
- })
58
- queryClient.setQueriesData(
59
- [resource, 'getManyReference'],
60
- (res) => setManyReferenceQueryData(res, data),
61
- { updatedAt },
62
- )
63
-
64
- onClose()
65
- notify('ra.notification.created')
66
- if (_redirect !== undefined) {
67
- redirect(_redirect, resource, data.id, data)
68
- }
69
- },
70
- [onClose, queryClient, resource, notify, redirect, _redirect],
71
- )
72
- const { save, isLoading } = useCreateController({
73
- mutationOptions: {
74
- onSuccess: handleSuccess,
75
- },
76
- })
77
- return (
78
- <CreateContextProvider
79
- value={{
80
- record: record,
81
- save: save,
82
- saving: isLoading,
83
- redirect: _redirect,
84
- }}
85
- >
86
- {React.cloneElement(children, {
87
- ...children.props,
88
- toolbar: (
89
- <Toolbar>
90
- <Button variant="text" size="medium" label="ra.action.cancel" onClick={onClose} />
91
- <SaveButton />
92
- </Toolbar>
93
- ),
94
- })}
95
- </CreateContextProvider>
96
- )
97
- }
98
-
99
- CreateInDialogContent.propTypes = {
100
- children: PropTypes.node,
101
- onClose: PropTypes.func,
102
- record: PropTypes.object,
103
- redirect: PropTypes.oneOf(['list', 'edit', 'show', false]),
104
- }
105
- CreateInDialogContent.defaultProps = {
106
- redirect: false,
107
- }
108
- const scrollStates = {
109
- true: { _scrollToTop: true },
110
- false: {},
111
- }
112
-
113
- const StyledFab = styled(Fab, {
114
- name: 'RaApplicaCreateInDialogButton',
115
- overridesResolver: (_props, styles) => styles.root,
116
- })(({ theme }) => ({
117
- [`&.${CreateButtonClasses.floating}`]: {
118
- color: theme.palette.getContrastText(theme.palette.primary.main),
119
- margin: 0,
120
- top: 'auto',
121
- right: 20,
122
- bottom: 60,
123
- left: 'auto',
124
- position: 'fixed',
125
- zIndex: 1000,
126
- },
127
- }))
128
- const CreateInDialogButton = ({
129
- fullWidth,
130
- maxWidth,
131
- label,
132
- record,
133
- redirect,
134
- scrollToTop,
135
- className,
136
- sx,
137
- ...props
138
- }) => {
139
- const [open, setOpen] = useState(false)
140
- const translate = useTranslate()
141
- const resource = useResourceContext()
142
- const { openDialog, closeDialog } = useAppConfig()
143
-
144
- const handleOpen = useCallback(
145
- () => openDialog(resource, () => setOpen(true)),
146
- [openDialog, resource],
147
- )
148
- const handleClose = useCallback(
149
- () => closeDialog(resource, () => setOpen(false)),
150
- [closeDialog, resource],
151
- )
152
- const isSmall = useMediaQuery((theme) => theme.breakpoints.down('md'))
153
- return (
154
- <>
155
- {isSmall ? (
156
- <StyledFab
157
- {...props}
158
- state={scrollStates[String(scrollToTop)]}
159
- // @ts-ignore FabProps ships its own runtime palette `FabPropsColorOverrides` provoking an overlap error with `ButtonProps`
160
- color="primary"
161
- className={clsx(CreateButtonClasses.floating, className)}
162
- aria-label={label && translate(label)}
163
- onClick={handleOpen}
164
- >
165
- <Add />
166
- </StyledFab>
167
- ) : (
168
- <Button {...props} sx={sx} label={label} onClick={handleOpen}>
169
- <Add />
170
- </Button>
171
- )}
172
- <Dialog open={open} onClose={handleClose} fullWidth={fullWidth} maxWidth={maxWidth}>
173
- <CreateInDialogContent
174
- {...props}
175
- redirect={redirect}
176
- record={record}
177
- onClose={handleClose}
178
- />
179
- </Dialog>
180
- </>
181
- )
182
- }
183
-
184
- CreateInDialogButton.defaultProps = {
185
- fullWidth: true,
186
- maxWidth: 'md',
187
- label: 'ra.action.create',
188
- scrollToTop: true,
189
- }
190
-
191
- CreateInDialogButton.propTypes = {
192
- ...Button.propTypes,
193
- redirect: PropTypes.oneOf(['list', 'edit', 'show', false]),
194
- fullWidth: PropTypes.bool,
195
- maxWidth: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', false]),
196
- label: PropTypes.string,
197
- record: PropTypes.object,
198
- scrollToTop: PropTypes.bool,
199
- className: PropTypes.string,
200
- sx: PropTypes.object,
201
- }
202
-
203
- export default CreateInDialogButton
@@ -1,82 +0,0 @@
1
- import { Box, Typography } from '@mui/material'
2
- import {
3
- FileField as RaFileField,
4
- useDataProvider,
5
- useRecordContext,
6
- useResourceContext,
7
- useTranslate
8
- } from 'react-admin'
9
- import { useCallback, useEffect, useMemo, useState } from 'react'
10
-
11
- import BaseAttachmentField from './BaseAttachmentField'
12
- import PropTypes from 'prop-types'
13
- import dayjs from 'dayjs'
14
- import { get } from 'lodash'
15
-
16
- const AttachmentField = ({ entityId, property, ...props }) => {
17
- const [user, setUser] = useState(null)
18
- const record = useRecordContext(props)
19
- const dataProvider = useDataProvider()
20
- const resource = useResourceContext()
21
- const handleClick = useCallback(
22
- async (e) => {
23
- e.preventDefault()
24
- e.stopPropagation()
25
- const item = get(record, props?.source)
26
- const entity = resource.replace('entities/', '')
27
- const attachment = await dataProvider.getFile(
28
- `/attachments/${entity}/${entityId || record?.id}/${property || props?.source}/${
29
- item?.id || record?.id
30
- }`
31
- )
32
- const link = document.createElement('a')
33
- link.href = attachment
34
- link.download = get(record, props?.title || props?.source)
35
- link.click()
36
- },
37
- [dataProvider, record, entityId, resource, property, props?.source, props?.title]
38
- )
39
- const _record = useMemo(
40
- () => ({
41
- ...record,
42
- [props?.source]: record?.src || get(record, props?.source),
43
- [props?.title]: record?.title || get(record, props?.title || props?.source)
44
- }),
45
- [record, props?.source, props?.title]
46
- )
47
- const translate = useTranslate()
48
- useEffect(() => {
49
- if (!record || !record?.userId) return
50
- dataProvider.getOne('entities/user', { id: record?.userId }).then(({ data }) => setUser(data))
51
- }, [record, dataProvider])
52
-
53
- return (
54
- <BaseAttachmentField
55
- {...props}
56
- record={_record}
57
- onClick={handleClick}
58
- title={
59
- <Box sx={{ cursor: 'pointer' }}>
60
- <Typography variant="subtitle1">{get(_record, props?.title || props?.source)}</Typography>
61
- <Typography variant="caption" color="secondary">
62
- {get(_record, 'sizeDescription')}
63
- {user && ` | `}
64
- {user &&
65
- translate('ra.attachment.info', {
66
- user: user?.name,
67
- created: dayjs(_record?.createdAt).format('DD/MM/YYYY HH:mm')
68
- })}
69
- </Typography>
70
- </Box>
71
- }
72
- />
73
- )
74
- }
75
-
76
- AttachmentField.propTypes = {
77
- ...RaFileField.propTypes,
78
- source: PropTypes.string,
79
- title: PropTypes.string
80
- }
81
-
82
- export default AttachmentField
@@ -1,72 +0,0 @@
1
- import * as React from 'react'
2
-
3
- import { useRecordContext, useTranslate } from 'ra-core'
4
-
5
- import PropTypes from 'prop-types'
6
- import Typography from '@mui/material/Typography'
7
- import get from 'lodash/get'
8
- import { styled } from '@mui/material/styles'
9
-
10
- export const BaseAttachmentField = (props) => {
11
- const { className, emptyText, source, title, ...rest } = props
12
- const record = useRecordContext(props)
13
- const sourceValue = get(record, source)
14
- const translate = useTranslate()
15
-
16
- if (!sourceValue) {
17
- return emptyText ? (
18
- <Typography component="span" variant="body2" className={className} {...rest}>
19
- {emptyText && translate(emptyText, { _: emptyText })}
20
- </Typography>
21
- ) : (
22
- <Root className={className} {...rest} />
23
- )
24
- }
25
-
26
- if (Array.isArray(sourceValue)) {
27
- return (
28
- <StyledList className={className} {...rest}>
29
- {sourceValue.map((file, index) => {
30
- const fileTitleValue = get(file, title) || title
31
-
32
- return <li key={index}>{fileTitleValue}</li>
33
- })}
34
- </StyledList>
35
- )
36
- }
37
-
38
- const titleValue = get(record, title) || title
39
-
40
- return (
41
- <Root className={className} {...rest}>
42
- {titleValue}
43
- </Root>
44
- )
45
- }
46
-
47
- BaseAttachmentField.propTypes = {
48
- src: PropTypes.string,
49
- title: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.element]),
50
- target: PropTypes.string,
51
- download: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
52
- ping: PropTypes.string,
53
- rel: PropTypes.string,
54
- emptyText: PropTypes.string,
55
- source: PropTypes.string.isRequired,
56
- className: PropTypes.string,
57
- }
58
-
59
- const PREFIX = 'RaApplicaBaseAttachmentField'
60
-
61
- const Root = styled('div', {
62
- name: PREFIX,
63
- overridesResolver: (props, styles) => styles.root,
64
- })({
65
- display: 'inline-block',
66
- })
67
-
68
- const StyledList = styled('ul')({
69
- display: 'inline-block',
70
- })
71
-
72
- export default BaseAttachmentField
@@ -1,42 +0,0 @@
1
- import { CardHeader, Divider } from '@mui/material'
2
-
3
- import { Fragment } from 'react'
4
- import PropTypes from 'prop-types'
5
- import { styled } from '@mui/material/styles'
6
- import { useTranslate } from 'react-admin'
7
-
8
- const StyledCardHeader = styled(CardHeader)(({ theme }) => ({
9
- marginTop: theme.spacing(4),
10
- marginLeft: 0,
11
- marginRight: 0,
12
- paddingLeft: 0,
13
- paddingRight: 0,
14
- }))
15
-
16
- const StyledDivider = styled(Divider)(({ theme }) => ({
17
- marginLeft: `-${theme.spacing(2.5)}`,
18
- marginRight: `-${theme.spacing(2.5)}`,
19
- marginBottom: theme.spacing(2),
20
- width: `calc(100% + ${theme.spacing(5)})`,
21
- }))
22
-
23
- const FormHeader = ({ title, divider }) => {
24
- const translate = useTranslate()
25
- return (
26
- <Fragment>
27
- <StyledCardHeader title={translate(title, { _: title })} />
28
- {divider && <StyledDivider />}
29
- </Fragment>
30
- )
31
- }
32
-
33
- FormHeader.propTypes = {
34
- title: PropTypes.string.isRequired,
35
- divider: PropTypes.bool,
36
- }
37
-
38
- FormHeader.defaultProps = {
39
- divider: true,
40
- }
41
-
42
- export default FormHeader
@@ -1,10 +0,0 @@
1
- import PropTypes from 'prop-types'
2
- const DispositionProps = PropTypes.shape({
3
- xl: PropTypes.number,
4
- lg: PropTypes.number,
5
- md: PropTypes.number,
6
- sm: PropTypes.number,
7
- xs: PropTypes.number,
8
- })
9
-
10
- export default DispositionProps
@@ -1,38 +0,0 @@
1
- import DispositionProps from './DispositionProps'
2
- import { Form } from 'react-admin'
3
- import LongFormTab from './LongFormTab'
4
- import LongFormTabHeader from './LongFormHeader'
5
- import LongFormView from './LongFormView'
6
- import PropTypes from 'prop-types'
7
- import useFormRootPath from './useFormRootPath'
8
- import { useThemeConfig } from '../../../hooks'
9
-
10
- const LongForm = ({ spacing: _spacing, ...props }) => {
11
- const formRootPathname = useFormRootPath()
12
- const { spacing: _themeSpacing } = useThemeConfig()
13
- const spacing = _spacing || _themeSpacing
14
- return (
15
- <Form formRootPathname={formRootPathname} {...props}>
16
- <LongFormView formRootPathname={formRootPathname} {...props} spacing={spacing} />
17
- </Form>
18
- )
19
- }
20
-
21
- LongForm.propTypes = {
22
- syncWithLocation: PropTypes.bool,
23
- spacing: PropTypes.number,
24
- tabsDisposition: DispositionProps,
25
- contentDisposition: DispositionProps,
26
- }
27
-
28
- LongForm.defaultProps = {
29
- syncWithLocation: false,
30
- spacing: 2,
31
- tabsDisposition: { xl: 3, lg: 3, md: 4, sm: 4, xs: 12 },
32
- contentDisposition: { xl: 9, lg: 9, md: 8, sm: 8, xs: 12 },
33
- }
34
-
35
- LongForm.Tab = LongFormTab
36
- LongForm.Header = LongFormTabHeader
37
-
38
- export default LongForm
@@ -1,24 +0,0 @@
1
- import PropTypes from 'prop-types'
2
- import { styled } from '@mui/material/styles'
3
- import { useMediaQuery } from '@mui/material'
4
- const StyledSidebar = styled('div')(({ theme }) => ({
5
- marginBottom: theme.spacing(2),
6
- }))
7
-
8
- const LongFormTabHeader = ({ children, visibility }) => {
9
- const isVisible = useMediaQuery((theme) => theme.breakpoints.up(visibility))
10
- return isVisible ? <StyledSidebar>{children}</StyledSidebar> : null
11
- }
12
-
13
- LongFormTabHeader.propTypes = {
14
- visibility: PropTypes.oneOf(['xl', 'lg', 'md', 'sm', 'xs']),
15
- position: PropTypes.oneOf(['top', 'bottom']).isRequired,
16
- children: PropTypes.node.isRequired,
17
- }
18
-
19
- LongFormTabHeader.defaultProps = {
20
- position: 'top',
21
- visibility: 'md',
22
- }
23
-
24
- export default LongFormTabHeader
@@ -1,63 +0,0 @@
1
- import { matchPath, useLocation } from 'react-router'
2
-
3
- import { List } from '@mui/material'
4
- import MainCard from '../../MainCard'
5
- import PropTypes from 'prop-types'
6
- import React from 'react'
7
- import { styled } from '@mui/system'
8
-
9
- const StyledList = styled(List, {
10
- name: 'RaLongFormTabs',
11
- slot: 'Root',
12
- })(({ theme }) => ({
13
- p: 0,
14
-
15
- '& .MuiListItemIcon-root': {
16
- minWidth: 32,
17
- color: theme.palette.grey[500],
18
- },
19
- '& .MuiListItemButton-root.Mui-selected': {
20
- borderRight: `2px solid ${theme.palette.primary.main}`,
21
- marginRight: `-2px`,
22
- },
23
- }))
24
-
25
- const LongFormTabs = ({ children, syncWithLocation, value, url, onChange }) => {
26
- const location = useLocation()
27
- return (
28
- <MainCard>
29
- <StyledList component="nav">
30
- {React.Children.map(children, (tab, index) => {
31
- if (!React.isValidElement(tab)) return null
32
- const tabPath = getTabbedFormTabFullPath(tab, index)
33
- const selected = syncWithLocation
34
- ? !!matchPath(`${url}/${tabPath}`, location.pathname)
35
- : index === value
36
-
37
- return React.cloneElement(tab, {
38
- intent: 'header',
39
- value: syncWithLocation ? tabPath : index,
40
- syncWithLocation,
41
- onChange,
42
- url,
43
- selected,
44
- })
45
- })}
46
- </StyledList>
47
- </MainCard>
48
- )
49
- }
50
-
51
- export const getTabbedFormTabFullPath = (tab, index) =>
52
- tab.props.path != null ? tab.props.path : index > 0 ? index.toString() : ''
53
-
54
- LongFormTabs.propTypes = {
55
- children: PropTypes.node,
56
- url: PropTypes.string,
57
- tabsWithErrors: PropTypes.arrayOf(PropTypes.string),
58
- syncWithLocation: PropTypes.bool,
59
- onChange: PropTypes.func,
60
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
61
- }
62
-
63
- export default LongFormTabs
@@ -1,129 +0,0 @@
1
- import React, { useMemo, useState } from 'react'
2
- import { Route, Routes, matchPath, useLocation, useResolvedPath } from 'react-router'
3
- import { getTabbedFormTabFullPath, useResourceContext } from 'react-admin'
4
-
5
- import DispositionProps from './DispositionProps'
6
- import { Grid } from '@mui/material'
7
- import LongFormCard from './LongFormHeader'
8
- import LongFormTab from './LongFormTab'
9
- import LongFormTabs from './LongFormTabs'
10
- import PropTypes from 'prop-types'
11
- import { styled } from '@mui/material/styles'
12
-
13
- const StyledGrid = styled(Grid, {
14
- name: 'RaLongFormView',
15
- slot: 'Root',
16
- })(({ theme }) => ({
17
- '& .MuiToolbar-root': {
18
- marginTop: theme.spacing(2),
19
- marginLeft: `-${theme.spacing(3)}`,
20
- marginRight: `-${theme.spacing(3)}`,
21
- marginBottom: `-${theme.spacing(2)}`,
22
- borderTop: `1px solid ${theme.palette.divider}`,
23
- },
24
- }))
25
-
26
- const isSidebar = (child, position) =>
27
- child && child.type === LongFormCard && child.props.position === position
28
- const isTab = (child) => child && child.type === LongFormTab
29
-
30
- const LongFormView = ({
31
- children,
32
- formRootPathname,
33
- syncWithLocation,
34
- spacing,
35
- tabs,
36
- tabsDisposition,
37
- contentDisposition,
38
- ...props
39
- }) => {
40
- const [tabValue, setTabValue] = useState(0)
41
- const resolvedPath = useResolvedPath('')
42
- const resource = useResourceContext(props)
43
- const location = useLocation()
44
- const topSidebars = useMemo(
45
- () => React.Children.toArray(children).find((child) => isSidebar(child, 'top')),
46
- [children],
47
- )
48
- const bottomSidebars = useMemo(
49
- () => React.Children.toArray(children).find((child) => isSidebar(child, 'bottom')),
50
- [children],
51
- )
52
- const tabChildrens = useMemo(
53
- () => React.Children.toArray(children).filter((child) => isTab(child)),
54
- [children],
55
- )
56
-
57
- const handleTabChange = (value) => {
58
- if (!syncWithLocation) {
59
- setTabValue(value)
60
- }
61
- }
62
- const renderTabs = () =>
63
- React.cloneElement(
64
- tabs,
65
- {
66
- onChange: handleTabChange,
67
- syncWithLocation,
68
- url: formRootPathname,
69
- value: tabValue,
70
- intent: 'header',
71
- },
72
- tabChildrens,
73
- )
74
- return (
75
- <StyledGrid container spacing={spacing * 2}>
76
- <Grid item {...tabsDisposition}>
77
- {topSidebars}
78
- {syncWithLocation ? (
79
- <Routes>
80
- <Route path="/*" element={renderTabs()} />
81
- </Routes>
82
- ) : (
83
- renderTabs()
84
- )}
85
- {bottomSidebars}
86
- </Grid>
87
- <Grid item {...contentDisposition}>
88
- {/* All tabs are rendered (not only the one in focus), to allow validation
89
- on tabs not in focus. The tabs receive a `hidden` property, which they'll
90
- use to hide the tab using CSS if it's not the one in focus.
91
- See https://github.com/marmelab/react-admin/issues/1866 */}
92
- {React.Children.map(tabChildrens, (tab, index) => {
93
- if (!tab) {
94
- return null
95
- }
96
- const tabPath = getTabbedFormTabFullPath(tab, index)
97
- const hidden = syncWithLocation
98
- ? !matchPath(`${resolvedPath.pathname}/${tabPath}`, location.pathname)
99
- : tabValue !== index
100
-
101
- return React.isValidElement(tab)
102
- ? React.cloneElement(tab, {
103
- intent: 'content',
104
- resource,
105
- hidden,
106
- value: syncWithLocation ? tabPath : index,
107
- })
108
- : null
109
- })}
110
- </Grid>
111
- </StyledGrid>
112
- )
113
- }
114
-
115
- LongFormView.propTypes = {
116
- children: PropTypes.node,
117
- spacing: PropTypes.number,
118
- syncWithLocation: PropTypes.bool,
119
- tabs: PropTypes.element,
120
- formRootPathname: PropTypes.string,
121
- tabsDisposition: DispositionProps,
122
- contentDisposition: DispositionProps,
123
- }
124
-
125
- LongFormView.defaultProps = {
126
- tabs: <LongFormTabs />,
127
- }
128
-
129
- export default LongFormView
@@ -1,2 +0,0 @@
1
- import LongForm from './LongForm'
2
- export default LongForm
@@ -1,22 +0,0 @@
1
- import { matchPath, useLocation } from 'react-router-dom'
2
-
3
- /**
4
- * This hook infers the tabbed form root path from the current location.
5
- */
6
- const useFormRootPath = () => {
7
- const location = useLocation()
8
- const createMatch = matchPath(':resource/create/*', location.pathname)
9
- const editMatch = matchPath(':resource/:id/*', location.pathname)
10
-
11
- if (createMatch) {
12
- return createMatch.pathnameBase
13
- }
14
-
15
- if (editMatch) {
16
- return editMatch.pathnameBase
17
- }
18
-
19
- return ''
20
- }
21
-
22
- export default useFormRootPath