Source

adminjs/src/frontend/components/property-type/base-property-component.tsx

  1. import React, { useMemo } from 'react'
  2. import { ReactComponentLike } from 'prop-types'
  3. import { Box } from '@adminjs/design-system'
  4. import ErrorBoundary from '../app/error-boundary'
  5. import * as ArrayType from './array'
  6. import * as MixedType from './mixed'
  7. import * as defaultType from './default-type'
  8. import * as boolean from './boolean'
  9. import * as datetime from './datetime'
  10. import * as richtext from './richtext'
  11. import * as reference from './reference'
  12. import * as textarea from './textarea'
  13. import * as password from './password'
  14. import { BasePropertyComponentProps } from './base-property-props'
  15. import { PropertyType } from '../../../backend/adapters/property/base-property'
  16. import { PropertyJSON } from '../../interfaces'
  17. let globalAny: any = {}
  18. try {
  19. globalAny = window
  20. } catch (error) {
  21. if (error.message !== 'window is not defined') {
  22. throw error
  23. }
  24. }
  25. const types: Record<PropertyType, any> = {
  26. textarea,
  27. boolean,
  28. datetime,
  29. reference,
  30. password,
  31. date: datetime,
  32. richtext,
  33. string: defaultType,
  34. number: defaultType,
  35. float: defaultType,
  36. mixed: null,
  37. }
  38. /**
  39. * @load ./base-property-component.doc.md
  40. * @component
  41. * @name BasePropertyComponent
  42. * @subcategory Application
  43. * @class
  44. * @hideconstructor
  45. */
  46. const BasePropertyComponent: React.FC<BasePropertyComponentProps> = (props) => {
  47. const { property: baseProperty, resource, record, filter, where, onChange } = props
  48. const property: PropertyJSON = useMemo(() => ({
  49. ...baseProperty,
  50. // we fill the path if it is not there. That is why all the actual Component Renderers are
  51. // called with the path set to this root path. Next mixed and array components adds to this
  52. // path either index (for array) or subProperty name.
  53. path: (baseProperty as PropertyJSON).path || baseProperty.propertyPath,
  54. }), [baseProperty])
  55. const testId = `property-${where}-${property.path}`
  56. let Component: ReactComponentLike = (types[property.type] && types[property.type][where])
  57. || defaultType[where]
  58. if (property.components && property.components[where]) {
  59. const component = property.components[where]
  60. if (!component) {
  61. throw new Error(`there is no "${property.path}.components.${where}"`)
  62. }
  63. Component = globalAny.AdminJS.UserComponents[component]
  64. return (
  65. <ErrorBoundary>
  66. <Box data-testid={testId}>
  67. <Component
  68. property={property}
  69. resource={resource}
  70. record={record}
  71. filter={filter}
  72. onChange={onChange}
  73. where={where}
  74. />
  75. </Box>
  76. </ErrorBoundary>
  77. )
  78. }
  79. const Array = ArrayType[where]
  80. const Mixed = MixedType[where]
  81. if (baseProperty.isArray) {
  82. if (!Array) { return (<div />) }
  83. return (
  84. <Array
  85. {...props}
  86. property={property}
  87. ItemComponent={BasePropertyComponent}
  88. testId={testId}
  89. />
  90. )
  91. }
  92. if (baseProperty.type === 'mixed') {
  93. if (!Mixed) { return (<div />) }
  94. return (
  95. <Mixed
  96. {...props}
  97. property={property}
  98. ItemComponent={BasePropertyComponent}
  99. testId={testId}
  100. />
  101. )
  102. }
  103. return (
  104. <ErrorBoundary>
  105. <Box data-testid={testId}>
  106. <Component
  107. property={property}
  108. resource={resource}
  109. record={record}
  110. filter={filter}
  111. onChange={onChange}
  112. where={where}
  113. />
  114. </Box>
  115. </ErrorBoundary>
  116. )
  117. }
  118. export {
  119. BasePropertyComponent as default,
  120. BasePropertyComponent,
  121. }