import type { ReactNode } from "react"; import { useEffect, useState, isValidElement, Children } from "react"; import type { InferredTypeMap } from "ra-core"; import { ShowBase, InferredElement, getElementsFromRecords, useResourceContext, useShowContext, } from "ra-core"; import { capitalize, singularize } from "inflection"; import { ShowView } from "@/components/admin/show"; import { RecordField } from "@/components/admin/record-field"; import { DateField } from "./date-field"; import { ReferenceField } from "@/components/admin/reference-field"; import { NumberField } from "@/components/admin/number-field"; import { ArrayField } from "@/components/admin/array-field"; import { BadgeField } from "@/components/admin/badge-field"; import { SingleFieldList } from "@/components/admin/single-field-list"; import { ReferenceArrayField } from "@/components/admin/reference-array-field"; /** * A show page that automatically generates fields from your data. * * Inspects the record to infer field types and automatically creates appropriate display fields. * Useful for rapid prototyping. Logs generated code to console when enableLog is true. * * @example * import { ShowGuesser } from '@/components/admin'; * * export const PostShow = () => ; */ export const ShowGuesser = (props: { enableLog?: boolean }) => ( ); const ShowViewGuesser = (props: { enableLog?: boolean }) => { const resource = useResourceContext(); if (!resource) { throw new Error(`Cannot use outside of a ResourceContext`); } const { record } = useShowContext(); const [child, setChild] = useState(null); const { enableLog = process.env.NODE_ENV === "development", ...rest } = props; useEffect(() => { setChild(null); }, [resource]); useEffect(() => { if (record && !child) { const inferredElements = getElementsFromRecords([record], showFieldTypes); const inferredChild = new InferredElement( showFieldTypes.show, null, inferredElements, ); setChild(inferredChild.getElement()); if (!enableLog) return; const representation = inferredChild.getRepresentation(); const components = ["Show"] .concat( Array.from( new Set( Array.from(representation.matchAll(/<([^/\s>]+)/g)) .map((match) => match[1]) .filter( (component) => component !== "span" && component !== "div", ), ), ), ) .sort(); // eslint-disable-next-line no-console console.log( `Guessed Show: ${components .map( (component) => `import { ${component} } from "@/components/admin/${kebabCase( component, )}";`, ) .join("\n")} export const ${capitalize(singularize(resource))}Show = () => ( ${inferredChild.getRepresentation()} );`, ); } }, [record, child, resource, enableLog]); return {child}; }; const showFieldTypes: InferredTypeMap = { show: { component: (props: any) => (
{props.children}
), representation: ( _props: any, children: { getRepresentation: () => string }[], ) => `
${children .map((child) => ` ${child.getRepresentation()}`) .join("\n")}
`, }, reference: { component: (props: any) => ( ), representation: (props: any) => ` `, }, array: { component: ({ children, ...props }: any) => { const childrenArray = Children.toArray(children); return ( 0 && isValidElement(childrenArray[0]) && (childrenArray[0].props as any).source } /> ); }, representation: (props: any, children: any) => ` `, }, referenceArray: { component: (props: any) => ( ), representation: (props: any) => ` `, }, boolean: { component: (props: any) => ( (record[props.source] ? "Yes" : "No")} /> ), representation: (props: any) => ` record[${props.source}] ? 'Yes' : 'No'} />`, }, date: { component: (props: any) => ( ), representation: (props: any) => ` `, }, number: { component: (props: any) => ( ), representation: (props: any) => ` `, }, string: { component: (props: any) => , representation: (props: any) => ``, }, }; const kebabCase = (name: string) => { return name .replace(/([a-z])([A-Z])/g, "$1-$2") .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") .toLowerCase(); };