From e8898b18bef39e025eb6df298c144bd70ccceb1b Mon Sep 17 00:00:00 2001 From: Anmol Date: Tue, 21 May 2024 18:36:32 +0530 Subject: [PATCH] Remove hotscript dependency --- packages/ra-core/package.json | 3 +- packages/ra-core/src/util/useFieldValue.ts | 34 +- packages/ra-ui-materialui/package.json | 3 +- .../ra-ui-materialui/src/field/FileField.tsx | 173 ++++---- .../ra-ui-materialui/src/field/ImageField.tsx | 165 ++++---- packages/ra-ui-materialui/src/field/types.ts | 369 ++++++++++-------- 6 files changed, 400 insertions(+), 347 deletions(-) diff --git a/packages/ra-core/package.json b/packages/ra-core/package.json index 8b3059c28b0..d1958e35db8 100644 --- a/packages/ra-core/package.json +++ b/packages/ra-core/package.json @@ -63,7 +63,6 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "eventemitter3": "^5.0.1", - "hotscript": "^1.0.12", "inflection": "^3.0.0", "jsonexport": "^3.2.0", "lodash": "~4.17.5", @@ -71,4 +70,4 @@ "react-is": "^18.2.0" }, "gitHead": "587df4c27bfcec4a756df4f95e5fc14728dfc0d7" -} \ No newline at end of file +} diff --git a/packages/ra-core/src/util/useFieldValue.ts b/packages/ra-core/src/util/useFieldValue.ts index 6050c7c5d05..4fca21e16f0 100644 --- a/packages/ra-core/src/util/useFieldValue.ts +++ b/packages/ra-core/src/util/useFieldValue.ts @@ -1,5 +1,4 @@ import get from 'lodash/get'; -import { Call, Objects } from 'hotscript'; import { useRecordContext } from '../controller'; import { useSourceContext } from '../core'; @@ -18,30 +17,29 @@ import { useSourceContext } from '../core'; * } */ export const useFieldValue = < - RecordType extends Record = Record + RecordType extends Record = Record >( - params: UseFieldValueOptions + params: UseFieldValueOptions ) => { - const { defaultValue, source } = params; - const sourceContext = useSourceContext(); - const record = useRecordContext(params); + const { defaultValue, source } = params; + const sourceContext = useSourceContext(); + const record = useRecordContext(params); - return get( - record, - sourceContext?.getSource(source) ?? source, - defaultValue - ); + return get( + record, + sourceContext?.getSource(source) ?? source, + defaultValue + ); }; export interface UseFieldValueOptions< - RecordType extends Record = Record + RecordType extends Record = Record > { - // FIXME: Find a way to throw a type error when defaultValue is not of RecordType[Source] type - defaultValue?: any; - source: Call extends never - ? AnyString - : Call; - record?: RecordType; + // FIXME: Find a way to throw a type error when defaultValue is not of RecordType[Source] type + defaultValue?: any; + + source?: keyof RecordType extends never ? AnyString : keyof RecordType; + record?: RecordType; } type AnyString = string & {}; diff --git a/packages/ra-ui-materialui/package.json b/packages/ra-ui-materialui/package.json index bc864deffc5..eab4064a64d 100644 --- a/packages/ra-ui-materialui/package.json +++ b/packages/ra-ui-materialui/package.json @@ -68,7 +68,6 @@ "clsx": "^2.1.1", "css-mediaquery": "^0.1.2", "dompurify": "^2.4.3", - "hotscript": "^1.0.12", "inflection": "^3.0.0", "jsonexport": "^3.2.0", "lodash": "~4.17.5", @@ -78,4 +77,4 @@ "react-transition-group": "^4.4.5" }, "gitHead": "587df4c27bfcec4a756df4f95e5fc14728dfc0d7" -} \ No newline at end of file +} diff --git a/packages/ra-ui-materialui/src/field/FileField.tsx b/packages/ra-ui-materialui/src/field/FileField.tsx index de8301aaaef..a0c8711ca62 100644 --- a/packages/ra-ui-materialui/src/field/FileField.tsx +++ b/packages/ra-ui-materialui/src/field/FileField.tsx @@ -3,7 +3,6 @@ import { styled } from '@mui/material/styles'; import get from 'lodash/get'; import Typography from '@mui/material/Typography'; import { useFieldValue, useTranslate } from 'ra-core'; -import { Call, Objects } from 'hotscript'; import { sanitizeFieldRestProps } from './sanitizeFieldRestProps'; import { FieldProps } from './types'; @@ -24,116 +23,114 @@ import { Link } from '@mui/material'; * */ export const FileField = < - RecordType extends Record = Record + RecordType extends Record = Record >( - props: FileFieldProps + props: FileFieldProps ) => { - const { - className, - emptyText, - title, - src, - target, - download, - ping, - rel, - ...rest - } = props; - const sourceValue = useFieldValue(props); - const titleValue = - useFieldValue({ - ...props, - // @ts-ignore We ignore here because title might be a custom label or undefined instead of a field name - source: title, - })?.toString() ?? title; - const translate = useTranslate(); + const { + className, + emptyText, + title, + src, + target, + download, + ping, + rel, + ...rest + } = props; + const sourceValue = useFieldValue(props); + const titleValue = + useFieldValue({ + ...props, + // @ts-ignore We ignore here because title might be a custom label or undefined instead of a field name + source: title, + })?.toString() ?? title; + const translate = useTranslate(); - if (!sourceValue) { - return emptyText ? ( - - {emptyText && translate(emptyText, { _: emptyText })} - - ) : ( - - ); - } - - if (Array.isArray(sourceValue)) { - return ( - - {sourceValue.map((file, index) => { - const fileTitleValue = title - ? get(file, title, title) - : title; - const srcValue = src ? get(file, src, title) : title; - - return ( -
  • - e.stopPropagation()} - > - {fileTitleValue} - -
  • - ); - })} -
    - ); - } + if (!sourceValue) { + return emptyText ? ( + + {emptyText && translate(emptyText, { _: emptyText })} + + ) : ( + + ); + } + if (Array.isArray(sourceValue)) { return ( - - + {sourceValue.map((file, index) => { + const fileTitleValue = title + ? get(file, title, title) + : title; + const srcValue = src ? get(file, src, title) : title; + + return ( +
  • + - {titleValue} - - + onClick={e => e.stopPropagation()} + > + {fileTitleValue} + +
  • + ); + })} + ); + } + + return ( + + + {titleValue} + + + ); }; export interface FileFieldProps< - RecordType extends Record = Record + RecordType extends Record = Record > extends FieldProps { - src?: string; - title?: Call extends never - ? AnyString - : Call | AnyString; - target?: string; - download?: boolean | string; - ping?: string; - rel?: string; - sx?: SxProps; + src?: string; + title?: keyof RecordType extends never ? AnyString : keyof RecordType | AnyString; + target?: string; + download?: boolean | string; + ping?: string; + rel?: string; + sx?: SxProps; } type AnyString = string & {}; const PREFIX = 'RaFileField'; const Root = styled('div', { - name: PREFIX, - overridesResolver: (props, styles) => styles.root, + name: PREFIX, + overridesResolver: (props, styles) => styles.root, })({ - display: 'inline-block', + display: 'inline-block', }); const StyledList = styled('ul')({ - display: 'inline-block', + display: 'inline-block', }); diff --git a/packages/ra-ui-materialui/src/field/ImageField.tsx b/packages/ra-ui-materialui/src/field/ImageField.tsx index dc8daa04b79..f439418469a 100644 --- a/packages/ra-ui-materialui/src/field/ImageField.tsx +++ b/packages/ra-ui-materialui/src/field/ImageField.tsx @@ -3,82 +3,81 @@ import { styled } from '@mui/material/styles'; import { Box, Typography } from '@mui/material'; import get from 'lodash/get'; import { useFieldValue, useTranslate } from 'ra-core'; -import { Call, Objects } from 'hotscript'; import { sanitizeFieldRestProps } from './sanitizeFieldRestProps'; import { FieldProps } from './types'; import { SxProps } from '@mui/system'; export const ImageField = < - RecordType extends Record = Record + RecordType extends Record = Record >( - props: ImageFieldProps + props: ImageFieldProps ) => { - const { className, emptyText, src, title, ...rest } = props; - const sourceValue = useFieldValue(props); - const titleValue = - useFieldValue({ - ...props, - // @ts-ignore We ignore here because title might be a custom label or undefined instead of a field name - source: title, - })?.toString() ?? title; - const translate = useTranslate(); + const { className, emptyText, src, title, ...rest } = props; + const sourceValue = useFieldValue(props); + const titleValue = + useFieldValue({ + ...props, + // @ts-ignore We ignore here because title might be a custom label or undefined instead of a field name + source: title, + })?.toString() ?? title; + const translate = useTranslate(); - if (!sourceValue) { - return emptyText ? ( - - {emptyText && translate(emptyText, { _: emptyText })} - - ) : ( - - ); - } - - if (Array.isArray(sourceValue)) { - return ( - -
      - {sourceValue.map((file, index) => { - const fileTitleValue = title - ? get(file, title, title) - : title; - const srcValue = src ? get(file, src, title) : title; - - return ( -
    • - {fileTitleValue} -
    • - ); - })} -
    -
    - ); - } + if (!sourceValue) { + return emptyText ? ( + + {emptyText && translate(emptyText, { _: emptyText })} + + ) : ( + + ); + } + if (Array.isArray(sourceValue)) { return ( - - {titleValue} - + +
      + {sourceValue.map((file, index) => { + const fileTitleValue = title + ? get(file, title, title) + : title; + const srcValue = src ? get(file, src, title) : title; + + return ( +
    • + {fileTitleValue} +
    • + ); + })} +
    +
    ); + } + + return ( + + {titleValue} + + ); }; // What? TypeScript loses the displayName if we don't set it explicitly @@ -87,34 +86,32 @@ ImageField.displayName = 'ImageField'; const PREFIX = 'RaImageField'; export const ImageFieldClasses = { - list: `${PREFIX}-list`, - image: `${PREFIX}-image`, + list: `${PREFIX}-list`, + image: `${PREFIX}-image`, }; const Root = styled(Box, { - name: PREFIX, - overridesResolver: (props, styles) => styles.root, + name: PREFIX, + overridesResolver: (props, styles) => styles.root, })({ - [`& .${ImageFieldClasses.list}`]: { - display: 'flex', - listStyleType: 'none', - }, - [`& .${ImageFieldClasses.image}`]: { - margin: '0.25rem', - width: 200, - height: 100, - objectFit: 'contain', - }, + [`& .${ImageFieldClasses.list}`]: { + display: 'flex', + listStyleType: 'none', + }, + [`& .${ImageFieldClasses.image}`]: { + margin: '0.25rem', + width: 200, + height: 100, + objectFit: 'contain', + }, }); export interface ImageFieldProps< - RecordType extends Record = Record + RecordType extends Record = Record > extends FieldProps { - src?: string; - title?: Call extends never - ? AnyString - : Call | AnyString; - sx?: SxProps; + src?: string; + title?: keyof RecordType extends never ? AnyString : keyof RecordType | AnyString; + sx?: SxProps; } type AnyString = string & {}; diff --git a/packages/ra-ui-materialui/src/field/types.ts b/packages/ra-ui-materialui/src/field/types.ts index a0f16e6dd28..5cacd7bcbc6 100644 --- a/packages/ra-ui-materialui/src/field/types.ts +++ b/packages/ra-ui-materialui/src/field/types.ts @@ -1,163 +1,226 @@ import { ReactElement } from 'react'; +import PropTypes from 'prop-types'; import { TableCellProps } from '@mui/material/TableCell'; -import { Call, Objects } from 'hotscript'; type TextAlign = TableCellProps['align']; type SortOrder = 'ASC' | 'DESC'; type AnyString = string & {}; export interface FieldProps< - RecordType extends Record = Record + RecordType extends Record = Record > { - /** - * The field to use for sorting when users click this column head, if sortable. - * - * @see https://marmelab.com/react-admin/Fields.html#sortby - * @example - * const PostList = () => ( - * - * - * - * - * - * - * - * - * ); - */ - sortBy?: Call | AnyString; - - /** - * The order used for sorting when users click this column head, if sortable. - * - * @see https://marmelab.com/react-admin/Fields.html#sortbyorder - * @example - * const PostList = () => ( - * - * - * - * - * - * - * ); - */ - sortByOrder?: SortOrder; - - /** - * Name of the property to display. - * - * @see https://marmelab.com/react-admin/Fields.html#source - * @example - * const CommentList = () => ( - * - * - * - * - * - * - * ); - */ - source: Call extends never - ? AnyString - : Call; - - /** - * Label to use as column header when using or . - * Defaults to the capitalized field name. Set to false to disable the label. - * - * @see https://marmelab.com/react-admin/Fields.html#label - * @example - * const PostList = () => ( - * - * - * - * - * - * - * ); - */ - label?: string | ReactElement | boolean; - - /** - * Set it to false to disable the click handler on the column header when used inside . - * - * @see https://marmelab.com/react-admin/Fields.html#sortable - * @example - * const PostList = () => ( - * - * - * - * - * - * - * - * - * ); - */ - sortable?: boolean; - - /** - * A class name to apply to the root div element - */ - className?: string; - - /** - * A class name to apply to the cell element when used inside . - */ - cellClassName?: string; - - /** - * A class name to apply to the header cell element when used inside . - */ - headerClassName?: string; - - /** - * The text alignment for the cell content, when used inside . - * - * @see https://marmelab.com/react-admin/Fields.html#textalign - * @example - * const BasketTotal = () => { - * const record = useRecordContext(); - * if (!record) return null; - * const total = record.items.reduce((total, item) => total + item.price, 0); - * return {total}; - * } - * BasketTotal.defaultProps = { - * textAlign: 'right', - * }; - */ - textAlign?: TextAlign; - - /** - * The text to display when the field value is empty. Defaults to empty string. - * - * @see https://marmelab.com/react-admin/Fields.html#emptytext - * @example - * const PostList = () => ( - * - * - * - * - * - * - * ); - */ - emptyText?: string; - - /** - * @deprecated - */ - fullWidth?: boolean; - - /** - * The current record to use. Defaults to the `RecordContext` value. - * - * @see https://marmelab.com/react-admin/Fields.html#record - */ - record?: RecordType; - - /** - * The resource name. Defaults to the `ResourceContext` value. - */ - resource?: string; + /** + * The field to use for sorting when users click this column head, if sortable. + * + * @see https://marmelab.com/react-admin/Fields.html#sortby + * @example + * const PostList = () => ( + * + * + * + * + * + * + * + * + * ); + */ + sortBy: keyof RecordType; + /** + * The order used for sorting when users click this column head, if sortable. + * + * @see https://marmelab.com/react-admin/Fields.html#sortbyorder + * @example + * const PostList = () => ( + * + * + * + * + * + * + * ); + */ + sortByOrder?: SortOrder; + + /** + * Name of the property to display. + * + * @see https://marmelab.com/react-admin/Fields.html#source + * @example + * const CommentList = () => ( + * + * + * + * + * + * + * ); + */ + source?: keyof RecordType extends never ? AnyString : keyof RecordType; + + /** + * Label to use as column header when using or . + * Defaults to the capitalized field name. Set to false to disable the label. + * + * @see https://marmelab.com/react-admin/Fields.html#label + * @example + * const PostList = () => ( + * + * + * + * + * + * + * ); + */ + label?: string | ReactElement | boolean; + + /** + * Set it to false to disable the click handler on the column header when used inside . + * + * @see https://marmelab.com/react-admin/Fields.html#sortable + * @example + * const PostList = () => ( + * + * + * + * + * + * + * + * + * ); + */ + sortable?: boolean; + + /** + * A class name to apply to the root div element + */ + className?: string; + + /** + * A class name to apply to the cell element when used inside . + */ + cellClassName?: string; + + /** + * A class name to apply to the header cell element when used inside . + */ + headerClassName?: string; + + /* + * @deprecated this property is not used anymore + */ + formClassName?: string; + + /** + * The text alignment for the cell content, when used inside . + * + * @see https://marmelab.com/react-admin/Fields.html#textalign + * @example + * import { List, Datagrid, TextField } from 'react-admin'; + * const PostList = () => ( + * + * + * + * + * + * + * + * + * ); + */ + textAlign?: TextAlign; + + /** + * The text to display when the field value is empty. Defaults to empty string. + * + * @see https://marmelab.com/react-admin/Fields.html#emptytext + * @example + * const PostList = () => ( + * + * + * + * + * + * + * ); + */ + emptyText?: string; + + /** + * @deprecated + */ + fullWidth?: boolean; + + /** + * The current record to use. Defaults to the `RecordContext` value. + * + * @see https://marmelab.com/react-admin/Fields.html#record + */ + record?: RecordType; + + /** + * The resource name. Defaults to the `ResourceContext` value. + */ + resource?: string; } + +/** + * @deprecated use FieldProps instead + */ +export interface PublicFieldProps< + RecordType extends Record = Record, + SortByType = unknown +> { + sortBy?: unknown extends SortByType + ? keyof RecordType + : SortByType; + sortByOrder?: SortOrder; + source?: keyof RecordType; + label?: string | ReactElement | boolean; + sortable?: boolean; + className?: string; + cellClassName?: string; + headerClassName?: string; + /* + * @deprecated this property is not used anymore + */ + formClassName?: string; + textAlign?: TextAlign; + emptyText?: string; + fullWidth?: boolean; + record?: RecordType; + resource?: string; +} + +/** + * @deprecated use FieldProps instead + */ +export interface InjectedFieldProps { + record?: RecordType; + resource?: string; +} + +export const fieldPropTypes = { + sortBy: PropTypes.string, + sortByOrder: PropTypes.oneOf(['ASC', 'DESC']), + source: PropTypes.string, + label: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element, + PropTypes.bool, + ]), + sortable: PropTypes.bool, + className: PropTypes.string, + cellClassName: PropTypes.string, + headerClassName: PropTypes.string, + textAlign: PropTypes.oneOf([ + 'inherit', + 'left', + 'center', + 'right', + 'justify', + ]), + emptyText: PropTypes.string, +};