Files
pole-book/server/node_modules/@strapi/content-manager/dist/admin/hooks/useDocumentLayout.mjs

287 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as React from 'react';
import { useQueryParams, useStrapiApp, useNotification, useAPIErrorHandler } from '@strapi/admin/strapi-admin';
import { HOOKS } from '../constants/hooks.mjs';
import { useGetContentTypeConfigurationQuery } from '../services/contentTypes.mjs';
import { getMainField } from '../utils/attributes.mjs';
import { useContentTypeSchema } from './useContentTypeSchema.mjs';
import { useDocument, useDoc } from './useDocument.mjs';
/* -------------------------------------------------------------------------------------------------
* 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 => <Field.Root {...field} />)))
* ```
*
*/ const useDocumentLayout = (model)=>{
const { schema, components } = useDocument({
model,
collectionType: ''
}, {
skip: true
});
const [{ query }] = useQueryParams();
const runHookWaterfall = useStrapiApp('useDocumentLayout', (state)=>state.runHookWaterfall);
const { toggleNotification } = useNotification();
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
const { data, isLoading: isLoadingConfigs, error, isFetching: isFetchingConfigs } = useGetContentTypeConfigurationQuery(model);
const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
React.useEffect(()=>{
if (error) {
toggleNotification({
type: 'danger',
message: formatAPIError(error)
});
}
}, [
error,
formatAPIError,
toggleNotification
]);
const editLayout = React.useMemo(()=>data && !isLoading ? formatEditLayout(data, {
schemas,
schema,
components
}) : {
layout: [],
components: {},
metadatas: {},
options: {},
settings: DEFAULT_SETTINGS
}, [
data,
isLoading,
schemas,
schema,
components
]);
const listLayout = React.useMemo(()=>{
return data && !isLoading ? formatListLayout(data, {
schemas,
schema,
components
}) : {
layout: [],
metadatas: {},
options: {},
settings: DEFAULT_SETTINGS
};
}, [
data,
isLoading,
schemas,
schema,
components
]);
const { layout: edit } = React.useMemo(()=>runHookWaterfall(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 } = 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 = {}, metadatas, components, schemas = [])=>{
return rows.map((row)=>row.map((field)=>{
const attribute = attributes[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: 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 = {}, metadatas, components, schemas = [])=>{
return columns.map((name)=>{
const attribute = attributes[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: getMainField(attribute, metadata.mainField || settings.mainField, {
schemas,
components: components?.schemas ?? {}
}),
name: name,
searchable: metadata.searchable ?? true,
sortable: metadata.sortable ?? true
};
}).filter((field)=>field !== null);
};
export { DEFAULT_SETTINGS, convertEditLayoutToFieldLayouts, convertListLayoutToFieldLayouts, useDocLayout, useDocumentLayout };
//# sourceMappingURL=useDocumentLayout.mjs.map