'use strict'; var React = require('react'); var strapiAdmin = require('@strapi/admin/strapi-admin'); var hooks = require('../constants/hooks.js'); var contentTypes = require('../services/contentTypes.js'); var attributes = require('../utils/attributes.js'); var useContentTypeSchema = require('./useContentTypeSchema.js'); var useDocument = require('./useDocument.js'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React); /* ------------------------------------------------------------------------------------------------- * useDocumentLayout * -----------------------------------------------------------------------------------------------*/ const DEFAULT_SETTINGS = { bulkable: false, filterable: false, searchable: false, pagination: false, defaultSortBy: '', defaultSortOrder: 'asc', mainField: 'id', pageSize: 10 }; /** * @alpha * @description This hook is used to get the layouts for either the edit view or list view of a specific content-type * including the layouts for the components used in the content-type. It also runs the mutation hook waterfall so the data * is consistent wherever it is used. It's a light wrapper around the `useDocument` hook, but provides the `skip` option a document * is not fetched, however, it does fetch the schemas & components if they do not already exist in the cache. * * If the fetch fails, it will display a notification to the user. * * @example * ```tsx * const { model } = useParams<{ model: string }>(); * const { edit: { schema: layout } } = useDocumentLayout(model); * * return layout.map(panel => panel.map(row => row.map(field => ))) * ``` * */ const useDocumentLayout = (model)=>{ const { schema, components } = useDocument.useDocument({ model, collectionType: '' }, { skip: true }); const [{ query }] = strapiAdmin.useQueryParams(); const runHookWaterfall = strapiAdmin.useStrapiApp('useDocumentLayout', (state)=>state.runHookWaterfall); const { toggleNotification } = strapiAdmin.useNotification(); const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(); const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema.useContentTypeSchema(); const { data, isLoading: isLoadingConfigs, error, isFetching: isFetchingConfigs } = contentTypes.useGetContentTypeConfigurationQuery(model); const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs; React__namespace.useEffect(()=>{ if (error) { toggleNotification({ type: 'danger', message: formatAPIError(error) }); } }, [ error, formatAPIError, toggleNotification ]); const editLayout = React__namespace.useMemo(()=>data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : { layout: [], components: {}, metadatas: {}, options: {}, settings: DEFAULT_SETTINGS }, [ data, isLoading, schemas, schema, components ]); const listLayout = React__namespace.useMemo(()=>{ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : { layout: [], metadatas: {}, options: {}, settings: DEFAULT_SETTINGS }; }, [ data, isLoading, schemas, schema, components ]); const { layout: edit } = React__namespace.useMemo(()=>runHookWaterfall(hooks.HOOKS.MUTATE_EDIT_VIEW_LAYOUT, { layout: editLayout, query }), [ editLayout, query, runHookWaterfall ]); return { error, isLoading, edit, list: listLayout }; }; /* ------------------------------------------------------------------------------------------------- * useDocLayout * -----------------------------------------------------------------------------------------------*/ /** * @internal this hook uses the internal useDoc hook, as such it shouldn't be used outside of the * content-manager because it won't work as intended. */ const useDocLayout = ()=>{ const { model } = useDocument.useDoc(); return useDocumentLayout(model); }; /** * @internal * @description takes the configuration data, the schema & the components used in the schema and formats the edit view * versions of the schema & components. This is then used to render the edit view of the content-type. */ const formatEditLayout = (data, { schemas, schema, components })=>{ let currentPanelIndex = 0; /** * The fields arranged by the panels, new panels are made for dynamic zones only. */ const panelledEditAttributes = convertEditLayoutToFieldLayouts(data.contentType.layouts.edit, schema?.attributes, data.contentType.metadatas, { configurations: data.components, schemas: components }, schemas).reduce((panels, row)=>{ if (row.some((field)=>field.type === 'dynamiczone')) { panels.push([ row ]); currentPanelIndex += 2; } else { if (!panels[currentPanelIndex]) { panels.push([ row ]); } else { panels[currentPanelIndex].push(row); } } return panels; }, []); const componentEditAttributes = Object.entries(data.components).reduce((acc, [uid, configuration])=>{ acc[uid] = { layout: convertEditLayoutToFieldLayouts(configuration.layouts.edit, components[uid].attributes, configuration.metadatas, { configurations: data.components, schemas: components }), settings: { ...configuration.settings, icon: components[uid].info.icon, displayName: components[uid].info.displayName } }; return acc; }, {}); const editMetadatas = Object.entries(data.contentType.metadatas).reduce((acc, [attribute, metadata])=>{ return { ...acc, [attribute]: metadata.edit }; }, {}); return { layout: panelledEditAttributes, components: componentEditAttributes, metadatas: editMetadatas, settings: { ...data.contentType.settings, displayName: schema?.info.displayName }, options: { ...schema?.options, ...schema?.pluginOptions, ...data.contentType.options } }; }; /* ------------------------------------------------------------------------------------------------- * convertEditLayoutToFieldLayouts * -----------------------------------------------------------------------------------------------*/ /** * @internal * @description takes the edit layout from either a content-type or a component * and formats it into a generic object that can be used to correctly render * the form fields. */ const convertEditLayoutToFieldLayouts = (rows, attributes$1 = {}, metadatas, components, schemas = [])=>{ return rows.map((row)=>row.map((field)=>{ const attribute = attributes$1[field.name]; if (!attribute) { return null; } const { edit: metadata } = metadatas[field.name]; const settings = attribute.type === 'component' && components ? components.configurations[attribute.component].settings : {}; return { attribute, disabled: !metadata.editable, hint: metadata.description, label: metadata.label ?? '', name: field.name, // @ts-expect-error – mainField does exist on the metadata for a relation. mainField: attributes.getMainField(attribute, metadata.mainField || settings.mainField, { schemas, components: components?.schemas ?? {} }), placeholder: metadata.placeholder ?? '', required: attribute.required ?? false, size: field.size, unique: 'unique' in attribute ? attribute.unique : false, visible: metadata.visible ?? true, type: attribute.type }; }).filter((field)=>field !== null)); }; /* ------------------------------------------------------------------------------------------------- * formatListLayout * -----------------------------------------------------------------------------------------------*/ /** * @internal * @description takes the complete configuration data, the schema & the components used in the schema and * formats a list view layout for the content-type. This is much simpler than the edit view layout as there * are less options to consider. */ const formatListLayout = (data, { schemas, schema, components })=>{ const listMetadatas = Object.entries(data.contentType.metadatas).reduce((acc, [attribute, metadata])=>{ return { ...acc, [attribute]: metadata.list }; }, {}); /** * The fields arranged by the panels, new panels are made for dynamic zones only. */ const listAttributes = convertListLayoutToFieldLayouts(data.contentType.layouts.list, schema?.attributes, listMetadatas, { configurations: data.components, schemas: components }, schemas); return { layout: listAttributes, settings: { ...data.contentType.settings, displayName: schema?.info.displayName }, metadatas: listMetadatas, options: { ...schema?.options, ...schema?.pluginOptions, ...data.contentType.options } }; }; /* ------------------------------------------------------------------------------------------------- * convertListLayoutToFieldLayouts * -----------------------------------------------------------------------------------------------*/ /** * @internal * @description takes the columns from the list view configuration and formats them into a generic object * combinining metadata and attribute data. * * @note We do use this to reformat the list of strings when updating the displayed headers for the list view. */ const convertListLayoutToFieldLayouts = (columns, attributes$1 = {}, metadatas, components, schemas = [])=>{ return columns.map((name)=>{ const attribute = attributes$1[name]; if (!attribute) { return null; } const metadata = metadatas[name]; const settings = attribute.type === 'component' && components ? components.configurations[attribute.component].settings : {}; return { attribute, label: metadata.label ?? '', mainField: attributes.getMainField(attribute, metadata.mainField || settings.mainField, { schemas, components: components?.schemas ?? {} }), name: name, searchable: metadata.searchable ?? true, sortable: metadata.sortable ?? true }; }).filter((field)=>field !== null); }; exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS; exports.convertEditLayoutToFieldLayouts = convertEditLayoutToFieldLayouts; exports.convertListLayoutToFieldLayouts = convertListLayoutToFieldLayouts; exports.useDocLayout = useDocLayout; exports.useDocumentLayout = useDocumentLayout; //# sourceMappingURL=useDocumentLayout.js.map