@strapi/content-manager 5.61.0 → 5.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/ConfigurationForm/Fields.js +1 -1
- package/dist/admin/components/ConfigurationForm/Fields.js.map +1 -1
- package/dist/admin/components/ConfigurationForm/Fields.mjs +3 -3
- package/dist/admin/components/ConfigurationForm/Fields.mjs.map +1 -1
- package/dist/admin/features/I18nSharedFieldsLock.js +54 -0
- package/dist/admin/features/I18nSharedFieldsLock.js.map +1 -0
- package/dist/admin/features/I18nSharedFieldsLock.mjs +31 -0
- package/dist/admin/features/I18nSharedFieldsLock.mjs.map +1 -0
- package/dist/admin/history/components/VersionInputRenderer.js +7 -2
- package/dist/admin/history/components/VersionInputRenderer.js.map +1 -1
- package/dist/admin/history/components/VersionInputRenderer.mjs +7 -3
- package/dist/admin/history/components/VersionInputRenderer.mjs.map +1 -1
- package/dist/admin/hooks/useContentManagerInitData.js +27 -8
- package/dist/admin/hooks/useContentManagerInitData.js.map +1 -1
- package/dist/admin/hooks/useContentManagerInitData.mjs +29 -10
- package/dist/admin/hooks/useContentManagerInitData.mjs.map +1 -1
- package/dist/admin/hooks/useDocument.js +11 -13
- package/dist/admin/hooks/useDocument.js.map +1 -1
- package/dist/admin/hooks/useDocument.mjs +11 -13
- package/dist/admin/hooks/useDocument.mjs.map +1 -1
- package/dist/admin/hooks/useI18nFieldLock.js +169 -0
- package/dist/admin/hooks/useI18nFieldLock.js.map +1 -0
- package/dist/admin/hooks/useI18nFieldLock.mjs +140 -0
- package/dist/admin/hooks/useI18nFieldLock.mjs.map +1 -0
- package/dist/admin/hooks/usePersistentQueryParams.js +16 -3
- package/dist/admin/hooks/usePersistentQueryParams.js.map +1 -1
- package/dist/admin/hooks/usePersistentQueryParams.mjs +16 -3
- package/dist/admin/hooks/usePersistentQueryParams.mjs.map +1 -1
- package/dist/admin/index.js +5 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +2 -0
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/modules/app.js +8 -1
- package/dist/admin/modules/app.js.map +1 -1
- package/dist/admin/modules/app.mjs +8 -2
- package/dist/admin/modules/app.mjs.map +1 -1
- package/dist/admin/pages/EditView/EditViewPage.js +129 -127
- package/dist/admin/pages/EditView/EditViewPage.js.map +1 -1
- package/dist/admin/pages/EditView/EditViewPage.mjs +129 -127
- package/dist/admin/pages/EditView/EditViewPage.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/DocumentActions.js +11 -4
- package/dist/admin/pages/EditView/components/DocumentActions.js.map +1 -1
- package/dist/admin/pages/EditView/components/DocumentActions.mjs +12 -5
- package/dist/admin/pages/EditView/components/DocumentActions.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.js +31 -21
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.mjs +32 -22
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.js +98 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.js.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.mjs +96 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.mjs.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.js +1103 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.js.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.mjs +1077 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.mjs.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.js +211 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.js.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.mjs +183 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.mjs.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.js +84 -41
- package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.mjs +86 -43
- package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Input.js +86 -20
- package/dist/admin/pages/EditView/components/FormInputs/Component/Input.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Input.mjs +89 -23
- package/dist/admin/pages/EditView/components/FormInputs/Component/Input.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.js +5 -2
- package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.mjs +5 -2
- package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js +142 -22
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs +143 -23
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.js +402 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.js.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.mjs +389 -0
- package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.mjs.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js +84 -33
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs +85 -34
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.js +5 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.mjs +5 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js +17 -4
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs +18 -5
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js +54 -6
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs +54 -6
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js +11 -9
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs +12 -10
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js +9 -4
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs +9 -4
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormLayout.js +66 -63
- package/dist/admin/pages/EditView/components/FormLayout.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormLayout.mjs +66 -63
- package/dist/admin/pages/EditView/components/FormLayout.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/InputRenderer.js +7 -1
- package/dist/admin/pages/EditView/components/InputRenderer.js.map +1 -1
- package/dist/admin/pages/EditView/components/InputRenderer.mjs +7 -1
- package/dist/admin/pages/EditView/components/InputRenderer.mjs.map +1 -1
- package/dist/admin/pages/EditView/utils/data.js +14 -9
- package/dist/admin/pages/EditView/utils/data.js.map +1 -1
- package/dist/admin/pages/EditView/utils/data.mjs +14 -9
- package/dist/admin/pages/EditView/utils/data.mjs.map +1 -1
- package/dist/admin/pages/EditView/utils/forms.js +34 -0
- package/dist/admin/pages/EditView/utils/forms.js.map +1 -1
- package/dist/admin/pages/EditView/utils/forms.mjs +34 -1
- package/dist/admin/pages/EditView/utils/forms.mjs.map +1 -1
- package/dist/admin/pages/ListConfiguration/components/DraggableCard.js +2 -1
- package/dist/admin/pages/ListConfiguration/components/DraggableCard.js.map +1 -1
- package/dist/admin/pages/ListConfiguration/components/DraggableCard.mjs +2 -1
- package/dist/admin/pages/ListConfiguration/components/DraggableCard.mjs.map +1 -1
- package/dist/admin/pages/ListConfiguration/components/EditFieldForm.js +7 -6
- package/dist/admin/pages/ListConfiguration/components/EditFieldForm.js.map +1 -1
- package/dist/admin/pages/ListConfiguration/components/EditFieldForm.mjs +7 -6
- package/dist/admin/pages/ListConfiguration/components/EditFieldForm.mjs.map +1 -1
- package/dist/admin/pages/ListView/ListViewPage.js +11 -2
- package/dist/admin/pages/ListView/ListViewPage.js.map +1 -1
- package/dist/admin/pages/ListView/ListViewPage.mjs +13 -4
- package/dist/admin/pages/ListView/ListViewPage.mjs.map +1 -1
- package/dist/admin/pages/ListView/components/Filters.js +15 -5
- package/dist/admin/pages/ListView/components/Filters.js.map +1 -1
- package/dist/admin/pages/ListView/components/Filters.mjs +16 -7
- package/dist/admin/pages/ListView/components/Filters.mjs.map +1 -1
- package/dist/admin/preview/components/InputPopover.js +18 -8
- package/dist/admin/preview/components/InputPopover.js.map +1 -1
- package/dist/admin/preview/components/InputPopover.mjs +19 -9
- package/dist/admin/preview/components/InputPopover.mjs.map +1 -1
- package/dist/admin/preview/pages/Preview.js +5 -16
- package/dist/admin/preview/pages/Preview.js.map +1 -1
- package/dist/admin/preview/pages/Preview.mjs +5 -16
- package/dist/admin/preview/pages/Preview.mjs.map +1 -1
- package/dist/admin/preview/utils/fieldUtils.js +2 -0
- package/dist/admin/preview/utils/fieldUtils.js.map +1 -1
- package/dist/admin/preview/utils/fieldUtils.mjs +2 -1
- package/dist/admin/preview/utils/fieldUtils.mjs.map +1 -1
- package/dist/admin/services/api.js +2 -1
- package/dist/admin/services/api.js.map +1 -1
- package/dist/admin/services/api.mjs +2 -1
- package/dist/admin/services/api.mjs.map +1 -1
- package/dist/admin/services/documents.js +21 -1
- package/dist/admin/services/documents.js.map +1 -1
- package/dist/admin/services/documents.mjs +21 -2
- package/dist/admin/services/documents.mjs.map +1 -1
- package/dist/admin/services/relations.js +47 -1
- package/dist/admin/services/relations.js.map +1 -1
- package/dist/admin/services/relations.mjs +47 -2
- package/dist/admin/services/relations.mjs.map +1 -1
- package/dist/admin/src/exports.d.ts +2 -0
- package/dist/admin/src/features/I18nSharedFieldsLock.d.ts +22 -0
- package/dist/admin/src/history/components/VersionInputRenderer.d.ts +7 -1
- package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
- package/dist/admin/src/hooks/useI18nFieldLock.d.ts +61 -0
- package/dist/admin/src/modules/app.d.ts +4 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/AddComponentMenu.d.ts +15 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/ComponentCopyModal.d.ts +58 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/CopiedComponents.d.ts +41 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/Initializer.d.ts +7 -3
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/NonRepeatable.d.ts +1 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/Repeatable.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/utils/componentCopy.d.ts +115 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +6 -3
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.d.ts +2 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +5 -1
- package/dist/admin/src/pages/EditView/utils/forms.d.ts +13 -2
- package/dist/admin/src/pages/ListView/components/Filters.d.ts +12 -3
- package/dist/admin/src/preview/services/preview.d.ts +1 -1
- package/dist/admin/src/preview/utils/fieldUtils.d.ts +1 -0
- package/dist/admin/src/services/api.d.ts +1 -1
- package/dist/admin/src/services/components.d.ts +2 -2
- package/dist/admin/src/services/contentTypes.d.ts +3 -3
- package/dist/admin/src/services/documents.d.ts +25 -21
- package/dist/admin/src/services/homepage.d.ts +1 -1
- package/dist/admin/src/services/init.d.ts +1 -1
- package/dist/admin/src/services/relations.d.ts +5 -3
- package/dist/admin/src/services/uid.d.ts +3 -3
- package/dist/admin/translations/en.json.js +41 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +41 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/controllers/relations.js +4 -2
- package/dist/server/controllers/relations.js.map +1 -1
- package/dist/server/controllers/relations.mjs +4 -2
- package/dist/server/controllers/relations.mjs.map +1 -1
- package/dist/server/controllers/utils/metadata.js +3 -1
- package/dist/server/controllers/utils/metadata.js.map +1 -1
- package/dist/server/controllers/utils/metadata.mjs +3 -1
- package/dist/server/controllers/utils/metadata.mjs.map +1 -1
- package/dist/server/history/services/utils.js +3 -1
- package/dist/server/history/services/utils.js.map +1 -1
- package/dist/server/history/services/utils.mjs +3 -1
- package/dist/server/history/services/utils.mjs.map +1 -1
- package/dist/server/homepage/services/homepage-query-utils.js +14 -0
- package/dist/server/homepage/services/homepage-query-utils.js.map +1 -1
- package/dist/server/homepage/services/homepage-query-utils.mjs +14 -1
- package/dist/server/homepage/services/homepage-query-utils.mjs.map +1 -1
- package/dist/server/homepage/services/homepage.js +1 -1
- package/dist/server/homepage/services/homepage.js.map +1 -1
- package/dist/server/homepage/services/homepage.mjs +2 -2
- package/dist/server/homepage/services/homepage.mjs.map +1 -1
- package/dist/server/mcp/sanitizers/shape-relations.js +1 -3
- package/dist/server/mcp/sanitizers/shape-relations.js.map +1 -1
- package/dist/server/mcp/sanitizers/shape-relations.mjs +1 -3
- package/dist/server/mcp/sanitizers/shape-relations.mjs.map +1 -1
- package/dist/server/mcp/schemas/output-schemas.js +2 -1
- package/dist/server/mcp/schemas/output-schemas.js.map +1 -1
- package/dist/server/mcp/schemas/output-schemas.mjs +2 -1
- package/dist/server/mcp/schemas/output-schemas.mjs.map +1 -1
- package/dist/server/services/document-manager.js +3 -2
- package/dist/server/services/document-manager.js.map +1 -1
- package/dist/server/services/document-manager.mjs +3 -2
- package/dist/server/services/document-manager.mjs.map +1 -1
- package/dist/server/services/document-metadata.js +92 -4
- package/dist/server/services/document-metadata.js.map +1 -1
- package/dist/server/services/document-metadata.mjs +92 -4
- package/dist/server/services/document-metadata.mjs.map +1 -1
- package/dist/server/services/permission-checker.js +20 -8
- package/dist/server/services/permission-checker.js.map +1 -1
- package/dist/server/services/permission-checker.mjs +20 -8
- package/dist/server/services/permission-checker.mjs.map +1 -1
- package/dist/server/services/uid.js +2 -1
- package/dist/server/services/uid.js.map +1 -1
- package/dist/server/services/uid.mjs +2 -1
- package/dist/server/services/uid.mjs.map +1 -1
- package/dist/server/src/controllers/relations.d.ts.map +1 -1
- package/dist/server/src/controllers/utils/metadata.d.ts +1 -0
- package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
- package/dist/server/src/history/services/utils.d.ts.map +1 -1
- package/dist/server/src/homepage/services/homepage-query-utils.d.ts +6 -0
- package/dist/server/src/homepage/services/homepage-query-utils.d.ts.map +1 -1
- package/dist/server/src/homepage/services/homepage.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +21 -7
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/mcp/sanitizers/shape-relations.d.ts.map +1 -1
- package/dist/server/src/mcp/schemas/output-schemas.d.ts.map +1 -1
- package/dist/server/src/services/document-manager.d.ts.map +1 -1
- package/dist/server/src/services/document-metadata.d.ts +5 -1
- package/dist/server/src/services/document-metadata.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +21 -7
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/permission-checker.d.ts +6 -5
- package/dist/server/src/services/permission-checker.d.ts.map +1 -1
- package/dist/server/src/services/uid.d.ts.map +1 -1
- package/dist/shared/contracts/collection-types.d.ts +7 -0
- package/dist/shared/contracts/collection-types.d.ts.map +1 -1
- package/dist/shared/contracts/single-types.d.ts +4 -0
- package/dist/shared/contracts/single-types.d.ts.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDocument.mjs","sources":["../../../admin/src/hooks/useDocument.ts"],"sourcesContent":["/**\n * This hook doesn't use a context provider because we fetch directly from the server,\n * this sounds expensive but actually, it's really not. Because we have redux-toolkit-query\n * being a cache layer so if nothing invalidates the cache, we don't fetch again.\n *\n * The network is deduplicated, but the work done inside the hook is not: it can be mounted\n * by hundreds of fields at once, so anything expensive it derives must be cached outside of\n * the hook instance (see `getValidationSchema`).\n */\n\nimport * as React from 'react';\n\nimport {\n useNotification,\n useAPIErrorHandler,\n useQueryParams,\n FormErrors,\n getYupValidationErrors,\n useForm,\n} from '@strapi/admin/strapi-admin';\nimport { useIntl } from 'react-intl';\nimport { useParams } from 'react-router-dom';\nimport { ValidationError } from 'yup';\n\nimport { SINGLE_TYPES } from '../constants/collections';\nimport { type AnyData, transformDocument } from '../pages/EditView/utils/data';\nimport { createDefaultForm } from '../pages/EditView/utils/forms';\nimport { useGetDocumentQuery } from '../services/documents';\nimport { buildValidParams } from '../utils/api';\nimport { createYupSchema } from '../utils/validation';\n\nimport { useContentTypeSchema, ComponentsDictionary } from './useContentTypeSchema';\nimport { useDocumentLayout } from './useDocumentLayout';\n\nimport type { FindOne } from '../../../shared/contracts/collection-types';\nimport type { ContentType } from '../../../shared/contracts/content-types';\nimport type { Modules } from '@strapi/types';\n\ninterface UseDocumentArgs {\n collectionType: string;\n model: string;\n documentId?: string;\n params?: object;\n}\n\ntype UseDocumentOpts = Parameters<typeof useGetDocumentQuery>[1];\n\ntype Document = FindOne.Response['data'];\n\ntype Schema = ContentType;\n\ntype ValidationSchema = ReturnType<typeof createYupSchema>;\n\n// Building the yup schema of a model clones a schema for every nested component and dynamic\n// zone: on large models it takes seconds and gigabytes. `schema` and `components` identities\n// are shared across hook instances (see `useContentTypeSchema`), so build it once per pair,\n// and only when something actually validates.\nconst validationSchemaCache = new WeakMap<\n Schema,\n WeakMap<ComponentsDictionary, ValidationSchema>\n>();\n\nconst getValidationSchema = (schema: Schema, components: ComponentsDictionary) => {\n let byComponents = validationSchemaCache.get(schema);\n\n if (!byComponents) {\n byComponents = new WeakMap();\n validationSchemaCache.set(schema, byComponents);\n }\n\n let validationSchema = byComponents.get(components);\n\n if (!validationSchema) {\n validationSchema = createYupSchema(schema.attributes, components);\n byComponents.set(components, validationSchema);\n }\n\n return validationSchema;\n};\n\ntype UseDocument = (\n args: UseDocumentArgs,\n opts?: UseDocumentOpts\n) => {\n /**\n * These are the schemas of the components used in the content type, organised\n * by their uid.\n */\n components: ComponentsDictionary;\n document?: Document;\n meta?: FindOne.Response['meta'];\n isLoading: boolean;\n /**\n * This is the schema of the content type, it is not the same as the layout.\n */\n schema?: Schema;\n schemas?: Schema[];\n hasError?: boolean;\n refetch: () => void;\n validate: (document: Document) => null | FormErrors;\n /**\n * Get the document's title\n */\n getTitle: (mainField: string) => string;\n /**\n * Get the initial form values for the document\n */\n getInitialFormValues: (isCreatingDocument?: boolean) => AnyData | undefined;\n};\n\n/**\n * Mirrors the server's `isLocalizedAttribute` in\n * `packages/plugins/i18n/server/src/services/content-types.ts`: an attribute\n * counts as localized only when `pluginOptions.i18n.localized` is explicitly\n * `true`. Both `false` and `undefined` mean \"non-localized\" — important here\n * because attributes created without the i18n plugin omit the field entirely,\n * and we still want them inherited when creating a new locale draft.\n */\nconst isLocalizedAttribute = (attribute: { pluginOptions?: object }): boolean => {\n // `pluginOptions` on the base attribute is typed as `object`, so we have to\n // narrow it to the i18n-specific shape here.\n const i18nOptions = attribute.pluginOptions as { i18n?: { localized?: boolean } } | undefined;\n return i18nOptions?.i18n?.localized === true;\n};\n\n/* -------------------------------------------------------------------------------------------------\n * useDocument\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * @alpha\n * @public\n * @description Returns a document based on the model, collection type & id passed as arguments.\n * Also extracts its schema from the redux cache to be used for creating a validation schema.\n * @example\n * ```tsx\n * const { id, model, collectionType } = useParams<{ id: string; model: string; collectionType: string }>();\n *\n * if(!model || !collectionType) return null;\n *\n * const { document, isLoading, validate } = useDocument({ documentId: id, model, collectionType, params: { locale: 'en-GB' } })\n * const { update } = useDocumentActions()\n *\n * const onSubmit = async (document: Document) => {\n * const errors = validate(document);\n *\n * if(errors) {\n * // handle errors\n * }\n *\n * await update({ collectionType, model, id }, document)\n * }\n * ```\n *\n */\nconst useDocument: UseDocument = (args, opts) => {\n const { toggleNotification } = useNotification();\n const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();\n const { formatMessage } = useIntl();\n\n const {\n currentData: data,\n isLoading: isLoadingDocument,\n isFetching: isFetchingDocument,\n error,\n refetch,\n } = useGetDocumentQuery(args, {\n ...opts,\n skip: (!args.documentId && args.collectionType !== SINGLE_TYPES) || opts?.skip,\n });\n const document = data?.data;\n const meta = data?.meta;\n\n const {\n components,\n schema,\n schemas,\n isLoading: isLoadingSchema,\n } = useContentTypeSchema(args.model);\n const isSingleType = schema?.kind === 'singleType';\n\n const getTitle = React.useCallback(\n (mainField: string) => {\n // Always use mainField if it's not an id\n if (mainField !== 'id' && document?.[mainField]) {\n return document[mainField];\n }\n\n // When it's a singleType without a mainField, use the contentType displayName\n if (schema?.kind === 'singleType' && schema.info.displayName) {\n return formatMessage({\n id: schema.info.displayName,\n defaultMessage: schema.info.displayName,\n });\n }\n\n // Otherwise, use a fallback\n return formatMessage({\n id: 'content-manager.containers.untitled',\n defaultMessage: 'Untitled',\n });\n },\n [document, formatMessage, schema]\n );\n\n React.useEffect(() => {\n if (error) {\n toggleNotification({\n type: 'danger',\n message: formatAPIError(error),\n });\n }\n }, [toggleNotification, error, formatAPIError, args.collectionType]);\n\n const validate = React.useCallback(\n (document: Modules.Documents.AnyDocument): FormErrors | null => {\n if (!schema) {\n throw new Error(\n 'There is no validation schema generated, this is likely due to the schema not being loaded yet.'\n );\n }\n\n const validationSchema = getValidationSchema(schema, components);\n\n try {\n validationSchema.validateSync(document, { abortEarly: false, strict: true });\n return null;\n } catch (error) {\n if (error instanceof ValidationError) {\n return getYupValidationErrors(error);\n }\n\n throw error;\n }\n },\n [schema, components]\n );\n\n /**\n * Schema attributes that should be inherited from a sibling locale when\n * creating a new locale draft.\n *\n * Scope intentionally matches what the server populates into\n * `meta.availableLocales` (see `packages/core/content-manager/server/src/services/document-metadata.ts`):\n * - non-localized — `isLocalizedAttribute` semantics, so attributes whose\n * `pluginOptions.i18n.localized` is `undefined` count as non-localized\n * (an attribute created without the i18n plugin has no i18n options at\n * all but is still inherited at save by `copyNonLocalizedFields`).\n * - scalar or media — `component` / `dynamiczone` / `relation` are excluded\n * because the server doesn't populate them in `availableLocales` either,\n * so there'd be nothing to copy from.\n */\n const nonLocalizedScalarAndMediaFields = React.useMemo(() => {\n if (!schema?.attributes) {\n return [];\n }\n\n return Object.keys(schema.attributes).filter((name) => {\n const attribute = schema.attributes[name];\n\n if (isLocalizedAttribute(attribute)) {\n return false;\n }\n\n return (\n attribute.type !== 'component' &&\n attribute.type !== 'dynamiczone' &&\n attribute.type !== 'relation'\n );\n });\n }, [schema]);\n\n /**\n * Here we prepare the form for editing, we need to:\n * - remove prohibited fields from the document (passwords | ADD YOURS WHEN THERES A NEW ONE)\n * - swap out count objects on relations for empty arrays\n * - set __temp_key__ on array objects for drag & drop\n *\n * We also prepare the form for new documents, so we need to:\n * - set default values on fields\n * - inherit non-localized scalar/media values from a sibling locale.\n * Scope is intentionally limited to scalars and media — that is exactly\n * what the server populates into `meta.availableLocales` (see\n * `packages/core/content-manager/server/src/services/document-metadata.ts`).\n * Components, dynamic zones, and relations are not in that payload; the\n * server fills them at save time via `copyNonLocalizedFields` (in\n * `packages/core/core/src/services/document-service/internationalization.ts`)\n * when the new locale row is first created.\n * Baking the inheritance into `initialValues` here is what lets it survive\n * `<Form>` re-inits: the `SET_INITIAL_VALUES` effect in\n * `packages/core/admin/admin/src/components/Form.tsx` re-applies these\n * values whenever `initialValues` changes (e.g. after a locale switch),\n * which the previous side-effect-based prefill in `LocalePickerAction`\n * could not survive on revisits.\n */\n const getInitialFormValues = React.useCallback(\n (isCreatingDocument: boolean = false) => {\n if ((!document && !isCreatingDocument && !isSingleType) || !schema) {\n return undefined;\n }\n\n /**\n * Check that we have an ID so we know the\n * document has been created in some way.\n */\n if (document?.id) {\n return transformDocument(schema, components)(document);\n }\n\n let form: AnyData = createDefaultForm(schema, components);\n\n // Intentionally use `availableLocales[0]`:\n // - The server sorts `getAvailableLocales` with the default locale first\n // (see `document-metadata.ts`), so index 0 is the canonical inheritance\n // source. Sibling locales can drift on non-localized fields because the\n // server only syncs them at locale-creation time, not on subsequent\n // updates — preferring the default keeps the surfaced values stable.\n // - Avoids coupling this hook to `useGetLocalesQuery` just to find the\n // default locale.\n const sibling = meta?.availableLocales?.[0] as Record<string, unknown> | undefined;\n if (sibling && nonLocalizedScalarAndMediaFields.length > 0) {\n const inherited = nonLocalizedScalarAndMediaFields.reduce<AnyData>((acc, name) => {\n if (name in sibling) {\n acc[name] = sibling[name];\n }\n return acc;\n }, {});\n\n form = { ...form, ...inherited };\n }\n\n return transformDocument(schema, components)(form);\n },\n [\n document,\n isSingleType,\n schema,\n components,\n meta?.availableLocales,\n nonLocalizedScalarAndMediaFields,\n ]\n );\n\n const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;\n const hasError = !!error;\n\n return React.useMemo(\n () =>\n ({\n components,\n document,\n meta,\n isLoading,\n hasError,\n schema,\n schemas,\n validate,\n getTitle,\n getInitialFormValues,\n refetch,\n }) satisfies ReturnType<UseDocument>,\n [\n components,\n document,\n meta,\n isLoading,\n hasError,\n schema,\n schemas,\n validate,\n getTitle,\n getInitialFormValues,\n refetch,\n ]\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * useDoc\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * @internal this hook uses the router to extract the model, collection type & id from the url.\n * therefore, it shouldn't be used outside of the content-manager because it won't work as intended.\n */\nconst useDoc = (opts?: UseDocumentOpts) => {\n const { id, slug, collectionType, origin } = useParams<{\n id: string;\n origin: string;\n slug: string;\n collectionType: string;\n }>();\n const [{ query }] = useQueryParams();\n const params = React.useMemo(() => buildValidParams(query), [query]);\n\n if (!collectionType) {\n throw new Error('Could not find collectionType in url params');\n }\n\n if (!slug) {\n throw new Error('Could not find model in url params');\n }\n\n const document = useDocument(\n { documentId: origin || id, model: slug, collectionType, params },\n {\n ...opts,\n skip: id === 'create' || (!origin && !id && collectionType !== SINGLE_TYPES) || opts?.skip,\n }\n );\n\n const returnId = origin || (id === 'create' ? undefined : id);\n\n return {\n collectionType,\n model: slug,\n id: returnId,\n ...document,\n };\n};\n\n/**\n * @public\n * @experimental\n * Content manager context hooks for plugin development.\n * Make sure to use this hook inside the content manager.\n */\nconst useContentManagerContext = () => {\n const {\n collectionType,\n model,\n id,\n components,\n isLoading: isLoadingDoc,\n schema,\n schemas,\n } = useDoc();\n\n const layout = useDocumentLayout(model);\n\n const form = useForm<unknown>('useContentManagerContext', (state) => state);\n\n const isSingleType = collectionType === SINGLE_TYPES;\n const slug = model;\n const isCreatingEntry = id === 'create';\n\n useContentTypeSchema();\n\n const isLoading = isLoadingDoc || layout.isLoading;\n const error = layout.error;\n\n return {\n error,\n isLoading,\n\n // Base metadata\n model,\n collectionType,\n id,\n slug,\n isCreatingEntry,\n isSingleType,\n hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,\n\n // All schema infos\n components,\n contentType: schema,\n contentTypes: schemas,\n\n // Form state\n form,\n\n // layout infos\n layout,\n };\n};\n\nexport { useDocument, useDoc, useContentManagerContext };\nexport type { UseDocument, UseDocumentArgs, Document, Schema, ComponentsDictionary };\n"],"names":["validationSchemaCache","WeakMap","getValidationSchema","schema","components","byComponents","get","set","validationSchema","createYupSchema","attributes","isLocalizedAttribute","attribute","i18nOptions","pluginOptions","i18n","localized","useDocument","args","opts","toggleNotification","useNotification","_unstableFormatAPIError","formatAPIError","useAPIErrorHandler","formatMessage","useIntl","currentData","data","isLoading","isLoadingDocument","isFetching","isFetchingDocument","error","refetch","useGetDocumentQuery","skip","documentId","collectionType","SINGLE_TYPES","document","meta","schemas","isLoadingSchema","useContentTypeSchema","model","isSingleType","kind","getTitle","React","useCallback","mainField","info","displayName","id","defaultMessage","useEffect","type","message","validate","Error","validateSync","abortEarly","strict","ValidationError","getYupValidationErrors","nonLocalizedScalarAndMediaFields","useMemo","Object","keys","filter","name","getInitialFormValues","isCreatingDocument","undefined","transformDocument","form","createDefaultForm","sibling","availableLocales","length","inherited","reduce","acc","hasError","useDoc","slug","origin","useParams","query","useQueryParams","params","buildValidParams","returnId","useContentManagerContext","isLoadingDoc","layout","useDocumentLayout","useForm","state","isCreatingEntry","hasDraftAndPublish","options","draftAndPublish","contentType","contentTypes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useDocument.mjs","sources":["../../../admin/src/hooks/useDocument.ts"],"sourcesContent":["/**\n * This hook doesn't use a context provider because we fetch directly from the server,\n * this sounds expensive but actually, it's really not. Because we have redux-toolkit-query\n * being a cache layer so if nothing invalidates the cache, we don't fetch again.\n *\n * The network is deduplicated, but the work done inside the hook is not: it can be mounted\n * by hundreds of fields at once, so anything expensive it derives must be cached outside of\n * the hook instance (see `getValidationSchema`).\n */\n\nimport * as React from 'react';\n\nimport {\n useNotification,\n useAPIErrorHandler,\n useQueryParams,\n FormErrors,\n getYupValidationErrors,\n useForm,\n} from '@strapi/admin/strapi-admin';\nimport { useIntl } from 'react-intl';\nimport { useParams } from 'react-router-dom';\nimport { ValidationError } from 'yup';\n\nimport { SINGLE_TYPES } from '../constants/collections';\nimport { type AnyData, transformDocument } from '../pages/EditView/utils/data';\nimport { createDefaultForm } from '../pages/EditView/utils/forms';\nimport { useGetDocumentQuery } from '../services/documents';\nimport { buildValidParams } from '../utils/api';\nimport { createYupSchema } from '../utils/validation';\n\nimport { useContentTypeSchema, ComponentsDictionary } from './useContentTypeSchema';\nimport { useDocumentLayout } from './useDocumentLayout';\n\nimport type { FindOne } from '../../../shared/contracts/collection-types';\nimport type { ContentType } from '../../../shared/contracts/content-types';\nimport type { Modules } from '@strapi/types';\n\ninterface UseDocumentArgs {\n collectionType: string;\n model: string;\n documentId?: string;\n params?: object;\n}\n\ntype UseDocumentOpts = Parameters<typeof useGetDocumentQuery>[1];\n\ntype Document = FindOne.Response['data'];\n\ntype Schema = ContentType;\n\ntype ValidationSchema = ReturnType<typeof createYupSchema>;\n\n// Building the yup schema of a model clones a schema for every nested component and dynamic\n// zone: on large models it takes seconds and gigabytes. `schema` and `components` identities\n// are shared across hook instances (see `useContentTypeSchema`), so build it once per pair,\n// and only when something actually validates.\nconst validationSchemaCache = new WeakMap<\n Schema,\n WeakMap<ComponentsDictionary, ValidationSchema>\n>();\n\nconst getValidationSchema = (schema: Schema, components: ComponentsDictionary) => {\n let byComponents = validationSchemaCache.get(schema);\n\n if (!byComponents) {\n byComponents = new WeakMap();\n validationSchemaCache.set(schema, byComponents);\n }\n\n let validationSchema = byComponents.get(components);\n\n if (!validationSchema) {\n validationSchema = createYupSchema(schema.attributes, components);\n byComponents.set(components, validationSchema);\n }\n\n return validationSchema;\n};\n\ntype UseDocument = (\n args: UseDocumentArgs,\n opts?: UseDocumentOpts\n) => {\n /**\n * These are the schemas of the components used in the content type, organised\n * by their uid.\n */\n components: ComponentsDictionary;\n document?: Document;\n meta?: FindOne.Response['meta'];\n isLoading: boolean;\n /**\n * This is the schema of the content type, it is not the same as the layout.\n */\n schema?: Schema;\n schemas?: Schema[];\n hasError?: boolean;\n refetch: () => void;\n validate: (document: Document) => null | FormErrors;\n /**\n * Get the document's title\n */\n getTitle: (mainField: string) => string;\n /**\n * Get the initial form values for the document\n */\n getInitialFormValues: (isCreatingDocument?: boolean) => AnyData | undefined;\n};\n\n/**\n * Mirrors the server's `isLocalizedAttribute` in\n * `packages/plugins/i18n/server/src/services/content-types.ts`: an attribute\n * counts as localized only when `pluginOptions.i18n.localized` is explicitly\n * `true`. Both `false` and `undefined` mean \"non-localized\" — important here\n * because attributes created without the i18n plugin omit the field entirely,\n * and we still want them inherited when creating a new locale draft.\n */\nconst isLocalizedAttribute = (attribute: { pluginOptions?: object }): boolean => {\n // `pluginOptions` on the base attribute is typed as `object`, so we have to\n // narrow it to the i18n-specific shape here.\n const i18nOptions = attribute.pluginOptions as { i18n?: { localized?: boolean } } | undefined;\n return i18nOptions?.i18n?.localized === true;\n};\n\n/* -------------------------------------------------------------------------------------------------\n * useDocument\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * @alpha\n * @public\n * @description Returns a document based on the model, collection type & id passed as arguments.\n * Also extracts its schema from the redux cache to be used for creating a validation schema.\n * @example\n * ```tsx\n * const { id, model, collectionType } = useParams<{ id: string; model: string; collectionType: string }>();\n *\n * if(!model || !collectionType) return null;\n *\n * const { document, isLoading, validate } = useDocument({ documentId: id, model, collectionType, params: { locale: 'en-GB' } })\n * const { update } = useDocumentActions()\n *\n * const onSubmit = async (document: Document) => {\n * const errors = validate(document);\n *\n * if(errors) {\n * // handle errors\n * }\n *\n * await update({ collectionType, model, id }, document)\n * }\n * ```\n *\n */\nconst useDocument: UseDocument = (args, opts) => {\n const { toggleNotification } = useNotification();\n const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();\n const { formatMessage } = useIntl();\n\n const {\n currentData: data,\n isLoading: isLoadingDocument,\n isFetching: isFetchingDocument,\n error,\n refetch,\n } = useGetDocumentQuery(args, {\n ...opts,\n skip: (!args.documentId && args.collectionType !== SINGLE_TYPES) || opts?.skip,\n });\n const document = data?.data;\n const meta = data?.meta;\n\n const {\n components,\n schema,\n schemas,\n isLoading: isLoadingSchema,\n } = useContentTypeSchema(args.model);\n const isSingleType = schema?.kind === 'singleType';\n\n const getTitle = React.useCallback(\n (mainField: string) => {\n // Always use mainField if it's not an id\n if (mainField !== 'id' && document?.[mainField]) {\n return document[mainField];\n }\n\n // When it's a singleType without a mainField, use the contentType displayName\n if (schema?.kind === 'singleType' && schema.info.displayName) {\n return formatMessage({\n id: schema.info.displayName,\n defaultMessage: schema.info.displayName,\n });\n }\n\n // Otherwise, use a fallback\n return formatMessage({\n id: 'content-manager.containers.untitled',\n defaultMessage: 'Untitled',\n });\n },\n [document, formatMessage, schema]\n );\n\n React.useEffect(() => {\n if (error) {\n toggleNotification({\n type: 'danger',\n message: formatAPIError(error),\n });\n }\n }, [toggleNotification, error, formatAPIError, args.collectionType]);\n\n const validate = React.useCallback(\n (document: Modules.Documents.AnyDocument): FormErrors | null => {\n if (!schema) {\n throw new Error(\n 'There is no validation schema generated, this is likely due to the schema not being loaded yet.'\n );\n }\n\n const validationSchema = getValidationSchema(schema, components);\n\n try {\n validationSchema.validateSync(document, { abortEarly: false, strict: true });\n return null;\n } catch (error) {\n if (error instanceof ValidationError) {\n return getYupValidationErrors(error);\n }\n\n throw error;\n }\n },\n [schema, components]\n );\n\n /**\n * Schema attributes that should be inherited from a sibling locale when\n * creating a new locale draft.\n *\n * Scope intentionally matches what the server populates into\n * `meta.availableLocales` (see `packages/core/content-manager/server/src/services/document-metadata.ts`):\n * - non-localized — `isLocalizedAttribute` semantics, so attributes whose\n * `pluginOptions.i18n.localized` is `undefined` count as non-localized\n * (an attribute created without the i18n plugin has no i18n options at\n * all but is still inherited at save by `copyNonLocalizedFields`).\n * - scalar, media, component, or dynamic zone — relations are excluded\n * because the server doesn't populate them in `availableLocales` either.\n */\n const nonLocalizedFieldsToInherit = React.useMemo(() => {\n if (!schema?.attributes) {\n return [];\n }\n\n return Object.keys(schema.attributes).filter((name) => {\n const attribute = schema.attributes[name];\n\n if (isLocalizedAttribute(attribute)) {\n return false;\n }\n\n return attribute.type !== 'relation';\n });\n }, [schema]);\n\n /**\n * Here we prepare the form for editing, we need to:\n * - remove prohibited fields from the document (passwords | ADD YOURS WHEN THERES A NEW ONE)\n * - swap out count objects on relations for empty arrays\n * - set __temp_key__ on array objects for drag & drop\n *\n * We also prepare the form for new documents, so we need to:\n * - set default values on fields\n * - inherit non-localized scalar/media/component/dynamic-zone values from a sibling locale.\n * Scope matches what the server populates into `meta.availableLocales` (see\n * `packages/core/content-manager/server/src/services/document-metadata.ts`).\n * Relations are not in that payload; the server fills them at save time via\n * `copyNonLocalizedFields` (in\n * `packages/core/core/src/services/document-service/internationalization.ts`)\n * when the new locale row is first created.\n * Baking the inheritance into `initialValues` here is what lets it survive\n * `<Form>` re-inits: the `SET_INITIAL_VALUES` effect in\n * `packages/core/admin/admin/src/components/Form.tsx` re-applies these\n * values whenever `initialValues` changes (e.g. after a locale switch),\n * which the previous side-effect-based prefill in `LocalePickerAction`\n * could not survive on revisits.\n */\n const getInitialFormValues = React.useCallback(\n (isCreatingDocument: boolean = false) => {\n if ((!document && !isCreatingDocument && !isSingleType) || !schema) {\n return undefined;\n }\n\n /**\n * Check that we have an ID so we know the\n * document has been created in some way.\n */\n if (document?.id) {\n return transformDocument(schema, components)(document);\n }\n\n let form: AnyData = createDefaultForm(schema, components);\n\n // Intentionally use `availableLocales[0]`:\n // - The server sorts `getAvailableLocales` with the default locale first\n // (see `document-metadata.ts`), so index 0 is the canonical inheritance\n // source. Sibling locales can drift on non-localized fields because the\n // server only syncs them at locale-creation time, not on subsequent\n // updates — preferring the default keeps the surfaced values stable.\n // - Avoids coupling this hook to `useGetLocalesQuery` just to find the\n // default locale.\n const sibling = meta?.availableLocales?.[0] as Record<string, unknown> | undefined;\n if (sibling && nonLocalizedFieldsToInherit.length > 0) {\n const inherited = nonLocalizedFieldsToInherit.reduce<AnyData>((acc, name) => {\n if (name in sibling) {\n acc[name] = sibling[name];\n }\n return acc;\n }, {});\n\n form = { ...form, ...inherited };\n }\n\n return transformDocument(schema, components)(form);\n },\n [\n document,\n isSingleType,\n schema,\n components,\n meta?.availableLocales,\n nonLocalizedFieldsToInherit,\n ]\n );\n\n const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;\n const hasError = !!error;\n\n return React.useMemo(\n () =>\n ({\n components,\n document,\n meta,\n isLoading,\n hasError,\n schema,\n schemas,\n validate,\n getTitle,\n getInitialFormValues,\n refetch,\n }) satisfies ReturnType<UseDocument>,\n [\n components,\n document,\n meta,\n isLoading,\n hasError,\n schema,\n schemas,\n validate,\n getTitle,\n getInitialFormValues,\n refetch,\n ]\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * useDoc\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * @internal this hook uses the router to extract the model, collection type & id from the url.\n * therefore, it shouldn't be used outside of the content-manager because it won't work as intended.\n */\nconst useDoc = (opts?: UseDocumentOpts) => {\n const { id, slug, collectionType, origin } = useParams<{\n id: string;\n origin: string;\n slug: string;\n collectionType: string;\n }>();\n const [{ query }] = useQueryParams();\n const params = React.useMemo(() => buildValidParams(query), [query]);\n\n if (!collectionType) {\n throw new Error('Could not find collectionType in url params');\n }\n\n if (!slug) {\n throw new Error('Could not find model in url params');\n }\n\n const document = useDocument(\n { documentId: origin || id, model: slug, collectionType, params },\n {\n ...opts,\n skip: id === 'create' || (!origin && !id && collectionType !== SINGLE_TYPES) || opts?.skip,\n }\n );\n\n const returnId = origin || (id === 'create' ? undefined : id);\n\n return {\n collectionType,\n model: slug,\n id: returnId,\n ...document,\n };\n};\n\n/**\n * @public\n * @experimental\n * Content manager context hooks for plugin development.\n * Make sure to use this hook inside the content manager.\n */\nconst useContentManagerContext = () => {\n const {\n collectionType,\n model,\n id,\n components,\n isLoading: isLoadingDoc,\n schema,\n schemas,\n } = useDoc();\n\n const layout = useDocumentLayout(model);\n\n const form = useForm<unknown>('useContentManagerContext', (state) => state);\n\n const isSingleType = collectionType === SINGLE_TYPES;\n const slug = model;\n const isCreatingEntry = id === 'create';\n\n useContentTypeSchema();\n\n const isLoading = isLoadingDoc || layout.isLoading;\n const error = layout.error;\n\n return {\n error,\n isLoading,\n\n // Base metadata\n model,\n collectionType,\n id,\n slug,\n isCreatingEntry,\n isSingleType,\n hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,\n\n // All schema infos\n components,\n contentType: schema,\n contentTypes: schemas,\n\n // Form state\n form,\n\n // layout infos\n layout,\n };\n};\n\nexport { useDocument, useDoc, useContentManagerContext };\nexport type { UseDocument, UseDocumentArgs, Document, Schema, ComponentsDictionary };\n"],"names":["validationSchemaCache","WeakMap","getValidationSchema","schema","components","byComponents","get","set","validationSchema","createYupSchema","attributes","isLocalizedAttribute","attribute","i18nOptions","pluginOptions","i18n","localized","useDocument","args","opts","toggleNotification","useNotification","_unstableFormatAPIError","formatAPIError","useAPIErrorHandler","formatMessage","useIntl","currentData","data","isLoading","isLoadingDocument","isFetching","isFetchingDocument","error","refetch","useGetDocumentQuery","skip","documentId","collectionType","SINGLE_TYPES","document","meta","schemas","isLoadingSchema","useContentTypeSchema","model","isSingleType","kind","getTitle","React","useCallback","mainField","info","displayName","id","defaultMessage","useEffect","type","message","validate","Error","validateSync","abortEarly","strict","ValidationError","getYupValidationErrors","nonLocalizedFieldsToInherit","useMemo","Object","keys","filter","name","getInitialFormValues","isCreatingDocument","undefined","transformDocument","form","createDefaultForm","sibling","availableLocales","length","inherited","reduce","acc","hasError","useDoc","slug","origin","useParams","query","useQueryParams","params","buildValidParams","returnId","useContentManagerContext","isLoadingDoc","layout","useDocumentLayout","useForm","state","isCreatingEntry","hasDraftAndPublish","options","draftAndPublish","contentType","contentTypes"],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
5
|
+
var isEqual = require('lodash/isEqual');
|
|
6
|
+
var I18nSharedFieldsLock = require('../features/I18nSharedFieldsLock.js');
|
|
7
|
+
var documents = require('../services/documents.js');
|
|
8
|
+
var useDocumentContext = require('./useDocumentContext.js');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
function _interopNamespace(e) {
|
|
13
|
+
if (e && e.__esModule) return e;
|
|
14
|
+
var n = Object.create(null);
|
|
15
|
+
if (e) {
|
|
16
|
+
Object.keys(e).forEach(function (k) {
|
|
17
|
+
if (k !== 'default') {
|
|
18
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return e[k]; }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
n.default = e;
|
|
27
|
+
return Object.freeze(n);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
31
|
+
var isEqual__default = /*#__PURE__*/_interopDefault(isEqual);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @internal
|
|
35
|
+
* Whether the Content Manager edit view is on the i18n default locale.
|
|
36
|
+
*
|
|
37
|
+
* Returns `true` when i18n is not enabled on the content type, when the active
|
|
38
|
+
* or default locale cannot be determined yet, or when they match.
|
|
39
|
+
*
|
|
40
|
+
* Prefers `meta.defaultLocale` from the document metadata response — NOT
|
|
41
|
+
* `availableLocales[0]`. That list excludes the currently viewed locale, so
|
|
42
|
+
* when editing the default locale `[0]` is a sibling and would incorrectly
|
|
43
|
+
* report "not default" and lock shared fields.
|
|
44
|
+
*
|
|
45
|
+
* On create (`/create?locale=`), document metadata is not fetched, so
|
|
46
|
+
* `meta.defaultLocale` is missing. Fall back to the i18n locales list to tell
|
|
47
|
+
* whether the active locale is the default. Locking still requires a
|
|
48
|
+
* default-locale document entry (`useHasDefaultLocaleEntry`).
|
|
49
|
+
*/ const useIsEditingDefaultLocale = ()=>{
|
|
50
|
+
const [{ query }] = strapiAdmin.useQueryParams();
|
|
51
|
+
const { currentDocument } = useDocumentContext.useDocumentContext('useIsEditingDefaultLocale');
|
|
52
|
+
const contentTypeLocalized = currentDocument.schema?.pluginOptions?.i18n?.localized === true;
|
|
53
|
+
const currentLocale = query?.plugins?.i18n?.locale ?? currentDocument.document?.locale;
|
|
54
|
+
const metaDefaultLocale = currentDocument.meta?.defaultLocale;
|
|
55
|
+
const { data: locales } = documents.useGetI18nLocalesQuery(undefined, {
|
|
56
|
+
skip: !contentTypeLocalized || Boolean(metaDefaultLocale)
|
|
57
|
+
});
|
|
58
|
+
if (!contentTypeLocalized) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
const localesDefault = Array.isArray(locales) ? locales.find((locale)=>locale.isDefault)?.code : undefined;
|
|
62
|
+
const defaultLocale = metaDefaultLocale || localesDefault;
|
|
63
|
+
if (!currentLocale || !defaultLocale) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
return currentLocale === defaultLocale;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* @alpha
|
|
70
|
+
* @public
|
|
71
|
+
* Whether the document being edited has a default-locale entry.
|
|
72
|
+
*
|
|
73
|
+
* `availableLocales` excludes the locale currently open, so a match is a sibling
|
|
74
|
+
* in the default locale. Brand-new creates have no document metadata. A document
|
|
75
|
+
* that exists only in a secondary locale has none either. Shared fields stay
|
|
76
|
+
* editable in both cases: there is no default-locale entry to edit them in.
|
|
77
|
+
*/ const useHasDefaultLocaleEntry = ()=>{
|
|
78
|
+
const { currentDocument } = useDocumentContext.useDocumentContext('useHasDefaultLocaleEntry');
|
|
79
|
+
const meta = currentDocument.meta;
|
|
80
|
+
const contentTypeLocalized = currentDocument.schema?.pluginOptions?.i18n?.localized === true;
|
|
81
|
+
const { data: locales } = documents.useGetI18nLocalesQuery(undefined, {
|
|
82
|
+
skip: !contentTypeLocalized || Boolean(meta?.defaultLocale)
|
|
83
|
+
});
|
|
84
|
+
if (!meta) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const localesDefault = Array.isArray(locales) ? locales.find((locale)=>locale.isDefault)?.code : undefined;
|
|
88
|
+
const defaultLocale = meta.defaultLocale || localesDefault;
|
|
89
|
+
if (!defaultLocale) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return (meta.availableLocales ?? []).some((entry)=>entry.locale === defaultLocale);
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* @alpha
|
|
96
|
+
* @public
|
|
97
|
+
* Whether an attribute holds a value shared by every locale of the entry.
|
|
98
|
+
*/ const isSharedI18nAttribute = (attribute)=>{
|
|
99
|
+
if (!attribute) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
// Match server `isLocalizedAttribute`: relations and uids are always locale-specific
|
|
103
|
+
// even when they omit `pluginOptions.i18n.localized` (CTB never offers that checkbox).
|
|
104
|
+
if (attribute.type === 'relation' || attribute.type === 'uid') {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const i18nOptions = attribute.pluginOptions;
|
|
108
|
+
// Only explicit `localized: true` is localizable for other attribute types.
|
|
109
|
+
return i18nOptions?.i18n?.localized !== true;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Whether shared fields are locked for the document being edited: a non-default
|
|
113
|
+
* locale of a document that already has a default-locale entry. The editor can
|
|
114
|
+
* still unlock them (see `I18nSharedFieldsLock`).
|
|
115
|
+
*/ const useIsSharedFieldsLockActive = ()=>{
|
|
116
|
+
const isDefaultLocale = useIsEditingDefaultLocale();
|
|
117
|
+
const hasDefaultLocaleEntry = useHasDefaultLocaleEntry();
|
|
118
|
+
return !isDefaultLocale && hasDefaultLocaleEntry;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* @internal
|
|
122
|
+
* True when a field should be locked because it is not locale-specific and the
|
|
123
|
+
* editor is on a non-default locale that already has a default-locale entry.
|
|
124
|
+
* Root-level only — nested component fields inherit disable from their parent
|
|
125
|
+
* component input.
|
|
126
|
+
*/ const useShouldLockNonLocalizedField = (attribute, options = {})=>{
|
|
127
|
+
const isLockActive = useIsSharedFieldsLockActive();
|
|
128
|
+
const isUnlocked = I18nSharedFieldsLock.useI18nSharedFieldsLock('useShouldLockNonLocalizedField', (state)=>state.isUnlocked);
|
|
129
|
+
if (!isLockActive || isUnlocked || options.isInsideComponent) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return isSharedI18nAttribute(attribute);
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* @internal
|
|
136
|
+
* Returns a function that removes locked shared fields from the payload that
|
|
137
|
+
* creates a missing locale, so the server copies them whole from the default
|
|
138
|
+
* locale. The form only holds a prefilled copy of them, and relations nested
|
|
139
|
+
* in components or dynamic zones are not carried in that copy. Once the editor
|
|
140
|
+
* unlocks shared fields, the ones they changed are kept.
|
|
141
|
+
*/ const useOmitLockedSharedFieldsOnLocaleCreate = ()=>{
|
|
142
|
+
const { currentDocument } = useDocumentContext.useDocumentContext('useOmitLockedSharedFieldsOnLocaleCreate');
|
|
143
|
+
const isLockActive = useIsSharedFieldsLockActive();
|
|
144
|
+
const isUnlocked = I18nSharedFieldsLock.useI18nSharedFieldsLock('useOmitLockedSharedFieldsOnLocaleCreate', (state)=>state.isUnlocked);
|
|
145
|
+
const values = strapiAdmin.useForm('useOmitLockedSharedFieldsOnLocaleCreate', (state)=>state.values);
|
|
146
|
+
const initialValues = strapiAdmin.useForm('useOmitLockedSharedFieldsOnLocaleCreate', (state)=>state.initialValues);
|
|
147
|
+
const isCreatingLocale = !currentDocument.document?.id;
|
|
148
|
+
const attributes = currentDocument.schema?.attributes;
|
|
149
|
+
return React__namespace.useCallback((data)=>{
|
|
150
|
+
if (!isLockActive || !isCreatingLocale || !attributes) {
|
|
151
|
+
return data;
|
|
152
|
+
}
|
|
153
|
+
return Object.fromEntries(Object.entries(data).filter(([name])=>!isSharedI18nAttribute(attributes[name]) || isUnlocked && !isEqual__default.default(values?.[name], initialValues?.[name])));
|
|
154
|
+
}, [
|
|
155
|
+
attributes,
|
|
156
|
+
initialValues,
|
|
157
|
+
isCreatingLocale,
|
|
158
|
+
isLockActive,
|
|
159
|
+
isUnlocked,
|
|
160
|
+
values
|
|
161
|
+
]);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
exports.isSharedI18nAttribute = isSharedI18nAttribute;
|
|
165
|
+
exports.useHasDefaultLocaleEntry = useHasDefaultLocaleEntry;
|
|
166
|
+
exports.useIsEditingDefaultLocale = useIsEditingDefaultLocale;
|
|
167
|
+
exports.useOmitLockedSharedFieldsOnLocaleCreate = useOmitLockedSharedFieldsOnLocaleCreate;
|
|
168
|
+
exports.useShouldLockNonLocalizedField = useShouldLockNonLocalizedField;
|
|
169
|
+
//# sourceMappingURL=useI18nFieldLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useI18nFieldLock.js","sources":["../../../admin/src/hooks/useI18nFieldLock.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { useForm, useQueryParams } from '@strapi/admin/strapi-admin';\nimport isEqual from 'lodash/isEqual';\n\nimport { useI18nSharedFieldsLock } from '../features/I18nSharedFieldsLock';\nimport { useGetI18nLocalesQuery } from '../services/documents';\n\nimport { useDocumentContext } from './useDocumentContext';\n\n/**\n * @internal\n * Whether the Content Manager edit view is on the i18n default locale.\n *\n * Returns `true` when i18n is not enabled on the content type, when the active\n * or default locale cannot be determined yet, or when they match.\n *\n * Prefers `meta.defaultLocale` from the document metadata response — NOT\n * `availableLocales[0]`. That list excludes the currently viewed locale, so\n * when editing the default locale `[0]` is a sibling and would incorrectly\n * report \"not default\" and lock shared fields.\n *\n * On create (`/create?locale=`), document metadata is not fetched, so\n * `meta.defaultLocale` is missing. Fall back to the i18n locales list to tell\n * whether the active locale is the default. Locking still requires a\n * default-locale document entry (`useHasDefaultLocaleEntry`).\n */\nconst useIsEditingDefaultLocale = (): boolean => {\n const [{ query }] = useQueryParams<{\n plugins?: { i18n?: { locale?: string } };\n }>();\n const { currentDocument } = useDocumentContext('useIsEditingDefaultLocale');\n\n const contentTypeLocalized =\n (currentDocument.schema?.pluginOptions as { i18n?: { localized?: boolean } } | undefined)?.i18n\n ?.localized === true;\n\n const currentLocale = query?.plugins?.i18n?.locale ?? currentDocument.document?.locale;\n const metaDefaultLocale = (currentDocument.meta as { defaultLocale?: string | null } | undefined)\n ?.defaultLocale;\n\n const { data: locales } = useGetI18nLocalesQuery(undefined, {\n skip: !contentTypeLocalized || Boolean(metaDefaultLocale),\n });\n\n if (!contentTypeLocalized) {\n return true;\n }\n\n const localesDefault = Array.isArray(locales)\n ? locales.find((locale) => locale.isDefault)?.code\n : undefined;\n const defaultLocale = metaDefaultLocale || localesDefault;\n\n if (!currentLocale || !defaultLocale) {\n return true;\n }\n\n return currentLocale === defaultLocale;\n};\n\ntype DocumentMetaForLock = {\n availableLocales?: Array<{ locale?: string | null }>;\n defaultLocale?: string | null;\n};\n\n/**\n * @alpha\n * @public\n * Whether the document being edited has a default-locale entry.\n *\n * `availableLocales` excludes the locale currently open, so a match is a sibling\n * in the default locale. Brand-new creates have no document metadata. A document\n * that exists only in a secondary locale has none either. Shared fields stay\n * editable in both cases: there is no default-locale entry to edit them in.\n */\nconst useHasDefaultLocaleEntry = (): boolean => {\n const { currentDocument } = useDocumentContext('useHasDefaultLocaleEntry');\n const meta = currentDocument.meta as DocumentMetaForLock | undefined;\n\n const contentTypeLocalized =\n (currentDocument.schema?.pluginOptions as { i18n?: { localized?: boolean } } | undefined)?.i18n\n ?.localized === true;\n\n const { data: locales } = useGetI18nLocalesQuery(undefined, {\n skip: !contentTypeLocalized || Boolean(meta?.defaultLocale),\n });\n\n if (!meta) {\n return false;\n }\n\n const localesDefault = Array.isArray(locales)\n ? locales.find((locale) => locale.isDefault)?.code\n : undefined;\n const defaultLocale = meta.defaultLocale || localesDefault;\n\n if (!defaultLocale) {\n return false;\n }\n\n return (meta.availableLocales ?? []).some((entry) => entry.locale === defaultLocale);\n};\n\n/**\n * @alpha\n * @public\n * Whether an attribute holds a value shared by every locale of the entry.\n */\nconst isSharedI18nAttribute = (\n attribute: { pluginOptions?: object; type?: string } | undefined\n): boolean => {\n if (!attribute) {\n return false;\n }\n\n // Match server `isLocalizedAttribute`: relations and uids are always locale-specific\n // even when they omit `pluginOptions.i18n.localized` (CTB never offers that checkbox).\n if (attribute.type === 'relation' || attribute.type === 'uid') {\n return false;\n }\n\n const i18nOptions = attribute.pluginOptions as { i18n?: { localized?: boolean } } | undefined;\n\n // Only explicit `localized: true` is localizable for other attribute types.\n return i18nOptions?.i18n?.localized !== true;\n};\n\n/**\n * Whether shared fields are locked for the document being edited: a non-default\n * locale of a document that already has a default-locale entry. The editor can\n * still unlock them (see `I18nSharedFieldsLock`).\n */\nconst useIsSharedFieldsLockActive = (): boolean => {\n const isDefaultLocale = useIsEditingDefaultLocale();\n const hasDefaultLocaleEntry = useHasDefaultLocaleEntry();\n\n return !isDefaultLocale && hasDefaultLocaleEntry;\n};\n\n/**\n * @internal\n * True when a field should be locked because it is not locale-specific and the\n * editor is on a non-default locale that already has a default-locale entry.\n * Root-level only — nested component fields inherit disable from their parent\n * component input.\n */\nconst useShouldLockNonLocalizedField = (\n attribute: { pluginOptions?: object; type?: string } | undefined,\n options: { isInsideComponent?: boolean } = {}\n): boolean => {\n const isLockActive = useIsSharedFieldsLockActive();\n const isUnlocked = useI18nSharedFieldsLock(\n 'useShouldLockNonLocalizedField',\n (state) => state.isUnlocked\n );\n\n if (!isLockActive || isUnlocked || options.isInsideComponent) {\n return false;\n }\n\n return isSharedI18nAttribute(attribute);\n};\n\n/**\n * @internal\n * Returns a function that removes locked shared fields from the payload that\n * creates a missing locale, so the server copies them whole from the default\n * locale. The form only holds a prefilled copy of them, and relations nested\n * in components or dynamic zones are not carried in that copy. Once the editor\n * unlocks shared fields, the ones they changed are kept.\n */\nconst useOmitLockedSharedFieldsOnLocaleCreate = () => {\n const { currentDocument } = useDocumentContext('useOmitLockedSharedFieldsOnLocaleCreate');\n const isLockActive = useIsSharedFieldsLockActive();\n const isUnlocked = useI18nSharedFieldsLock(\n 'useOmitLockedSharedFieldsOnLocaleCreate',\n (state) => state.isUnlocked\n );\n const values = useForm(\n 'useOmitLockedSharedFieldsOnLocaleCreate',\n (state) => state.values as Record<string, unknown>\n );\n const initialValues = useForm(\n 'useOmitLockedSharedFieldsOnLocaleCreate',\n (state) => state.initialValues as Record<string, unknown>\n );\n const isCreatingLocale = !currentDocument.document?.id;\n const attributes = currentDocument.schema?.attributes;\n\n return React.useCallback(\n <TData extends Record<string, unknown>>(data: TData): TData => {\n if (!isLockActive || !isCreatingLocale || !attributes) {\n return data;\n }\n\n return Object.fromEntries(\n Object.entries(data).filter(\n ([name]) =>\n !isSharedI18nAttribute(attributes[name]) ||\n (isUnlocked && !isEqual(values?.[name], initialValues?.[name]))\n )\n ) as TData;\n },\n [attributes, initialValues, isCreatingLocale, isLockActive, isUnlocked, values]\n );\n};\n\nexport {\n isSharedI18nAttribute,\n useHasDefaultLocaleEntry,\n useIsEditingDefaultLocale,\n useOmitLockedSharedFieldsOnLocaleCreate,\n useShouldLockNonLocalizedField,\n};\n"],"names":["useIsEditingDefaultLocale","query","useQueryParams","currentDocument","useDocumentContext","contentTypeLocalized","schema","pluginOptions","i18n","localized","currentLocale","plugins","locale","document","metaDefaultLocale","meta","defaultLocale","data","locales","useGetI18nLocalesQuery","undefined","skip","Boolean","localesDefault","Array","isArray","find","isDefault","code","useHasDefaultLocaleEntry","availableLocales","some","entry","isSharedI18nAttribute","attribute","type","i18nOptions","useIsSharedFieldsLockActive","isDefaultLocale","hasDefaultLocaleEntry","useShouldLockNonLocalizedField","options","isLockActive","isUnlocked","useI18nSharedFieldsLock","state","isInsideComponent","useOmitLockedSharedFieldsOnLocaleCreate","values","useForm","initialValues","isCreatingLocale","id","attributes","React","useCallback","Object","fromEntries","entries","filter","name","isEqual"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA;;;;;;;;;;;;;;;;AAgBC,UACKA,yBAAAA,GAA4B,IAAA;AAChC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,0BAAAA,EAAAA;AAGpB,IAAA,MAAM,EAAEC,eAAe,EAAE,GAAGC,qCAAAA,CAAmB,2BAAA,CAAA;AAE/C,IAAA,MAAMC,uBACJ,eAACF,CAAgBG,MAAM,EAAEC,aAAAA,EAAkEC,MACvFC,SAAAA,KAAc,IAAA;AAEpB,IAAA,MAAMC,gBAAgBT,KAAAA,EAAOU,OAAAA,EAASH,MAAMI,MAAAA,IAAUT,eAAAA,CAAgBU,QAAQ,EAAED,MAAAA;IAChF,MAAME,iBAAAA,GAAqBX,eAAAA,CAAgBY,IAAI,EAC3CC,aAAAA;AAEJ,IAAA,MAAM,EAAEC,IAAAA,EAAMC,OAAO,EAAE,GAAGC,iCAAuBC,SAAAA,EAAW;QAC1DC,IAAAA,EAAM,CAAChB,wBAAwBiB,OAAAA,CAAQR,iBAAAA;AACzC,KAAA,CAAA;AAEA,IAAA,IAAI,CAACT,oBAAAA,EAAsB;QACzB,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMkB,cAAAA,GAAiBC,KAAAA,CAAMC,OAAO,CAACP,OAAAA,CAAAA,GACjCA,OAAAA,CAAQQ,IAAI,CAAC,CAACd,MAAAA,GAAWA,MAAAA,CAAOe,SAAS,GAAGC,IAAAA,GAC5CR,SAAAA;AACJ,IAAA,MAAMJ,gBAAgBF,iBAAAA,IAAqBS,cAAAA;IAE3C,IAAI,CAACb,aAAAA,IAAiB,CAACM,aAAAA,EAAe;QACpC,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,OAAON,aAAAA,KAAkBM,aAAAA;AAC3B;AAOA;;;;;;;;;AASC,UACKa,wBAAAA,GAA2B,IAAA;AAC/B,IAAA,MAAM,EAAE1B,eAAe,EAAE,GAAGC,qCAAAA,CAAmB,0BAAA,CAAA;IAC/C,MAAMW,IAAAA,GAAOZ,gBAAgBY,IAAI;AAEjC,IAAA,MAAMV,uBACJ,eAACF,CAAgBG,MAAM,EAAEC,aAAAA,EAAkEC,MACvFC,SAAAA,KAAc,IAAA;AAEpB,IAAA,MAAM,EAAEQ,IAAAA,EAAMC,OAAO,EAAE,GAAGC,iCAAuBC,SAAAA,EAAW;QAC1DC,IAAAA,EAAM,CAAChB,oBAAAA,IAAwBiB,OAAAA,CAAQP,IAAAA,EAAMC,aAAAA;AAC/C,KAAA,CAAA;AAEA,IAAA,IAAI,CAACD,IAAAA,EAAM;QACT,OAAO,KAAA;AACT,IAAA;AAEA,IAAA,MAAMQ,cAAAA,GAAiBC,KAAAA,CAAMC,OAAO,CAACP,OAAAA,CAAAA,GACjCA,OAAAA,CAAQQ,IAAI,CAAC,CAACd,MAAAA,GAAWA,MAAAA,CAAOe,SAAS,GAAGC,IAAAA,GAC5CR,SAAAA;IACJ,MAAMJ,aAAAA,GAAgBD,IAAAA,CAAKC,aAAa,IAAIO,cAAAA;AAE5C,IAAA,IAAI,CAACP,aAAAA,EAAe;QAClB,OAAO,KAAA;AACT,IAAA;AAEA,IAAA,OAAO,CAACD,IAAAA,CAAKe,gBAAgB,IAAI,EAAC,EAAGC,IAAI,CAAC,CAACC,KAAAA,GAAUA,KAAAA,CAAMpB,MAAM,KAAKI,aAAAA,CAAAA;AACxE;AAEA;;;;IAKA,MAAMiB,wBAAwB,CAC5BC,SAAAA,GAAAA;AAEA,IAAA,IAAI,CAACA,SAAAA,EAAW;QACd,OAAO,KAAA;AACT,IAAA;;;AAIA,IAAA,IAAIA,UAAUC,IAAI,KAAK,cAAcD,SAAAA,CAAUC,IAAI,KAAK,KAAA,EAAO;QAC7D,OAAO,KAAA;AACT,IAAA;IAEA,MAAMC,WAAAA,GAAcF,UAAU3B,aAAa;;IAG3C,OAAO6B,WAAAA,EAAa5B,MAAMC,SAAAA,KAAc,IAAA;AAC1C;AAEA;;;;AAIC,IACD,MAAM4B,2BAAAA,GAA8B,IAAA;AAClC,IAAA,MAAMC,eAAAA,GAAkBtC,yBAAAA,EAAAA;AACxB,IAAA,MAAMuC,qBAAAA,GAAwBV,wBAAAA,EAAAA;AAE9B,IAAA,OAAO,CAACS,eAAAA,IAAmBC,qBAAAA;AAC7B,CAAA;AAEA;;;;;;AAMC,UACKC,8BAAAA,GAAiC,CACrCN,SAAAA,EACAO,OAAAA,GAA2C,EAAE,GAAA;AAE7C,IAAA,MAAMC,YAAAA,GAAeL,2BAAAA,EAAAA;AACrB,IAAA,MAAMM,aAAaC,4CAAAA,CACjB,gCAAA,EACA,CAACC,KAAAA,GAAUA,MAAMF,UAAU,CAAA;AAG7B,IAAA,IAAI,CAACD,YAAAA,IAAgBC,UAAAA,IAAcF,OAAAA,CAAQK,iBAAiB,EAAE;QAC5D,OAAO,KAAA;AACT,IAAA;AAEA,IAAA,OAAOb,qBAAAA,CAAsBC,SAAAA,CAAAA;AAC/B;AAEA;;;;;;;AAOC,UACKa,uCAAAA,GAA0C,IAAA;AAC9C,IAAA,MAAM,EAAE5C,eAAe,EAAE,GAAGC,qCAAAA,CAAmB,yCAAA,CAAA;AAC/C,IAAA,MAAMsC,YAAAA,GAAeL,2BAAAA,EAAAA;AACrB,IAAA,MAAMM,aAAaC,4CAAAA,CACjB,yCAAA,EACA,CAACC,KAAAA,GAAUA,MAAMF,UAAU,CAAA;AAE7B,IAAA,MAAMK,SAASC,mBAAAA,CACb,yCAAA,EACA,CAACJ,KAAAA,GAAUA,MAAMG,MAAM,CAAA;AAEzB,IAAA,MAAME,gBAAgBD,mBAAAA,CACpB,yCAAA,EACA,CAACJ,KAAAA,GAAUA,MAAMK,aAAa,CAAA;AAEhC,IAAA,MAAMC,gBAAAA,GAAmB,CAAChD,eAAAA,CAAgBU,QAAQ,EAAEuC,EAAAA;IACpD,MAAMC,UAAAA,GAAalD,eAAAA,CAAgBG,MAAM,EAAE+C,UAAAA;IAE3C,OAAOC,gBAAAA,CAAMC,WAAW,CACtB,CAAwCtC,IAAAA,GAAAA;AACtC,QAAA,IAAI,CAACyB,YAAAA,IAAgB,CAACS,gBAAAA,IAAoB,CAACE,UAAAA,EAAY;YACrD,OAAOpC,IAAAA;AACT,QAAA;AAEA,QAAA,OAAOuC,MAAAA,CAAOC,WAAW,CACvBD,MAAAA,CAAOE,OAAO,CAACzC,IAAAA,CAAAA,CAAM0C,MAAM,CACzB,CAAC,CAACC,IAAAA,CAAK,GACL,CAAC3B,qBAAAA,CAAsBoB,UAAU,CAACO,IAAAA,CAAK,CAAA,IACtCjB,UAAAA,IAAc,CAACkB,wBAAAA,CAAQb,MAAAA,GAASY,IAAAA,CAAK,EAAEV,aAAAA,GAAgBU,IAAAA,CAAK,CAAA,CAAA,CAAA;IAGrE,CAAA,EACA;AAACP,QAAAA,UAAAA;AAAYH,QAAAA,aAAAA;AAAeC,QAAAA,gBAAAA;AAAkBT,QAAAA,YAAAA;AAAcC,QAAAA,UAAAA;AAAYK,QAAAA;AAAO,KAAA,CAAA;AAEnF;;;;;;;;"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useQueryParams, useForm } from '@strapi/admin/strapi-admin';
|
|
3
|
+
import isEqual from 'lodash/isEqual';
|
|
4
|
+
import { useI18nSharedFieldsLock } from '../features/I18nSharedFieldsLock.mjs';
|
|
5
|
+
import { useGetI18nLocalesQuery } from '../services/documents.mjs';
|
|
6
|
+
import { useDocumentContext } from './useDocumentContext.mjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @internal
|
|
10
|
+
* Whether the Content Manager edit view is on the i18n default locale.
|
|
11
|
+
*
|
|
12
|
+
* Returns `true` when i18n is not enabled on the content type, when the active
|
|
13
|
+
* or default locale cannot be determined yet, or when they match.
|
|
14
|
+
*
|
|
15
|
+
* Prefers `meta.defaultLocale` from the document metadata response — NOT
|
|
16
|
+
* `availableLocales[0]`. That list excludes the currently viewed locale, so
|
|
17
|
+
* when editing the default locale `[0]` is a sibling and would incorrectly
|
|
18
|
+
* report "not default" and lock shared fields.
|
|
19
|
+
*
|
|
20
|
+
* On create (`/create?locale=`), document metadata is not fetched, so
|
|
21
|
+
* `meta.defaultLocale` is missing. Fall back to the i18n locales list to tell
|
|
22
|
+
* whether the active locale is the default. Locking still requires a
|
|
23
|
+
* default-locale document entry (`useHasDefaultLocaleEntry`).
|
|
24
|
+
*/ const useIsEditingDefaultLocale = ()=>{
|
|
25
|
+
const [{ query }] = useQueryParams();
|
|
26
|
+
const { currentDocument } = useDocumentContext('useIsEditingDefaultLocale');
|
|
27
|
+
const contentTypeLocalized = currentDocument.schema?.pluginOptions?.i18n?.localized === true;
|
|
28
|
+
const currentLocale = query?.plugins?.i18n?.locale ?? currentDocument.document?.locale;
|
|
29
|
+
const metaDefaultLocale = currentDocument.meta?.defaultLocale;
|
|
30
|
+
const { data: locales } = useGetI18nLocalesQuery(undefined, {
|
|
31
|
+
skip: !contentTypeLocalized || Boolean(metaDefaultLocale)
|
|
32
|
+
});
|
|
33
|
+
if (!contentTypeLocalized) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
const localesDefault = Array.isArray(locales) ? locales.find((locale)=>locale.isDefault)?.code : undefined;
|
|
37
|
+
const defaultLocale = metaDefaultLocale || localesDefault;
|
|
38
|
+
if (!currentLocale || !defaultLocale) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return currentLocale === defaultLocale;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* @alpha
|
|
45
|
+
* @public
|
|
46
|
+
* Whether the document being edited has a default-locale entry.
|
|
47
|
+
*
|
|
48
|
+
* `availableLocales` excludes the locale currently open, so a match is a sibling
|
|
49
|
+
* in the default locale. Brand-new creates have no document metadata. A document
|
|
50
|
+
* that exists only in a secondary locale has none either. Shared fields stay
|
|
51
|
+
* editable in both cases: there is no default-locale entry to edit them in.
|
|
52
|
+
*/ const useHasDefaultLocaleEntry = ()=>{
|
|
53
|
+
const { currentDocument } = useDocumentContext('useHasDefaultLocaleEntry');
|
|
54
|
+
const meta = currentDocument.meta;
|
|
55
|
+
const contentTypeLocalized = currentDocument.schema?.pluginOptions?.i18n?.localized === true;
|
|
56
|
+
const { data: locales } = useGetI18nLocalesQuery(undefined, {
|
|
57
|
+
skip: !contentTypeLocalized || Boolean(meta?.defaultLocale)
|
|
58
|
+
});
|
|
59
|
+
if (!meta) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const localesDefault = Array.isArray(locales) ? locales.find((locale)=>locale.isDefault)?.code : undefined;
|
|
63
|
+
const defaultLocale = meta.defaultLocale || localesDefault;
|
|
64
|
+
if (!defaultLocale) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return (meta.availableLocales ?? []).some((entry)=>entry.locale === defaultLocale);
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* @alpha
|
|
71
|
+
* @public
|
|
72
|
+
* Whether an attribute holds a value shared by every locale of the entry.
|
|
73
|
+
*/ const isSharedI18nAttribute = (attribute)=>{
|
|
74
|
+
if (!attribute) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// Match server `isLocalizedAttribute`: relations and uids are always locale-specific
|
|
78
|
+
// even when they omit `pluginOptions.i18n.localized` (CTB never offers that checkbox).
|
|
79
|
+
if (attribute.type === 'relation' || attribute.type === 'uid') {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
const i18nOptions = attribute.pluginOptions;
|
|
83
|
+
// Only explicit `localized: true` is localizable for other attribute types.
|
|
84
|
+
return i18nOptions?.i18n?.localized !== true;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Whether shared fields are locked for the document being edited: a non-default
|
|
88
|
+
* locale of a document that already has a default-locale entry. The editor can
|
|
89
|
+
* still unlock them (see `I18nSharedFieldsLock`).
|
|
90
|
+
*/ const useIsSharedFieldsLockActive = ()=>{
|
|
91
|
+
const isDefaultLocale = useIsEditingDefaultLocale();
|
|
92
|
+
const hasDefaultLocaleEntry = useHasDefaultLocaleEntry();
|
|
93
|
+
return !isDefaultLocale && hasDefaultLocaleEntry;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* @internal
|
|
97
|
+
* True when a field should be locked because it is not locale-specific and the
|
|
98
|
+
* editor is on a non-default locale that already has a default-locale entry.
|
|
99
|
+
* Root-level only — nested component fields inherit disable from their parent
|
|
100
|
+
* component input.
|
|
101
|
+
*/ const useShouldLockNonLocalizedField = (attribute, options = {})=>{
|
|
102
|
+
const isLockActive = useIsSharedFieldsLockActive();
|
|
103
|
+
const isUnlocked = useI18nSharedFieldsLock('useShouldLockNonLocalizedField', (state)=>state.isUnlocked);
|
|
104
|
+
if (!isLockActive || isUnlocked || options.isInsideComponent) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return isSharedI18nAttribute(attribute);
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* @internal
|
|
111
|
+
* Returns a function that removes locked shared fields from the payload that
|
|
112
|
+
* creates a missing locale, so the server copies them whole from the default
|
|
113
|
+
* locale. The form only holds a prefilled copy of them, and relations nested
|
|
114
|
+
* in components or dynamic zones are not carried in that copy. Once the editor
|
|
115
|
+
* unlocks shared fields, the ones they changed are kept.
|
|
116
|
+
*/ const useOmitLockedSharedFieldsOnLocaleCreate = ()=>{
|
|
117
|
+
const { currentDocument } = useDocumentContext('useOmitLockedSharedFieldsOnLocaleCreate');
|
|
118
|
+
const isLockActive = useIsSharedFieldsLockActive();
|
|
119
|
+
const isUnlocked = useI18nSharedFieldsLock('useOmitLockedSharedFieldsOnLocaleCreate', (state)=>state.isUnlocked);
|
|
120
|
+
const values = useForm('useOmitLockedSharedFieldsOnLocaleCreate', (state)=>state.values);
|
|
121
|
+
const initialValues = useForm('useOmitLockedSharedFieldsOnLocaleCreate', (state)=>state.initialValues);
|
|
122
|
+
const isCreatingLocale = !currentDocument.document?.id;
|
|
123
|
+
const attributes = currentDocument.schema?.attributes;
|
|
124
|
+
return React.useCallback((data)=>{
|
|
125
|
+
if (!isLockActive || !isCreatingLocale || !attributes) {
|
|
126
|
+
return data;
|
|
127
|
+
}
|
|
128
|
+
return Object.fromEntries(Object.entries(data).filter(([name])=>!isSharedI18nAttribute(attributes[name]) || isUnlocked && !isEqual(values?.[name], initialValues?.[name])));
|
|
129
|
+
}, [
|
|
130
|
+
attributes,
|
|
131
|
+
initialValues,
|
|
132
|
+
isCreatingLocale,
|
|
133
|
+
isLockActive,
|
|
134
|
+
isUnlocked,
|
|
135
|
+
values
|
|
136
|
+
]);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export { isSharedI18nAttribute, useHasDefaultLocaleEntry, useIsEditingDefaultLocale, useOmitLockedSharedFieldsOnLocaleCreate, useShouldLockNonLocalizedField };
|
|
140
|
+
//# sourceMappingURL=useI18nFieldLock.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useI18nFieldLock.mjs","sources":["../../../admin/src/hooks/useI18nFieldLock.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { useForm, useQueryParams } from '@strapi/admin/strapi-admin';\nimport isEqual from 'lodash/isEqual';\n\nimport { useI18nSharedFieldsLock } from '../features/I18nSharedFieldsLock';\nimport { useGetI18nLocalesQuery } from '../services/documents';\n\nimport { useDocumentContext } from './useDocumentContext';\n\n/**\n * @internal\n * Whether the Content Manager edit view is on the i18n default locale.\n *\n * Returns `true` when i18n is not enabled on the content type, when the active\n * or default locale cannot be determined yet, or when they match.\n *\n * Prefers `meta.defaultLocale` from the document metadata response — NOT\n * `availableLocales[0]`. That list excludes the currently viewed locale, so\n * when editing the default locale `[0]` is a sibling and would incorrectly\n * report \"not default\" and lock shared fields.\n *\n * On create (`/create?locale=`), document metadata is not fetched, so\n * `meta.defaultLocale` is missing. Fall back to the i18n locales list to tell\n * whether the active locale is the default. Locking still requires a\n * default-locale document entry (`useHasDefaultLocaleEntry`).\n */\nconst useIsEditingDefaultLocale = (): boolean => {\n const [{ query }] = useQueryParams<{\n plugins?: { i18n?: { locale?: string } };\n }>();\n const { currentDocument } = useDocumentContext('useIsEditingDefaultLocale');\n\n const contentTypeLocalized =\n (currentDocument.schema?.pluginOptions as { i18n?: { localized?: boolean } } | undefined)?.i18n\n ?.localized === true;\n\n const currentLocale = query?.plugins?.i18n?.locale ?? currentDocument.document?.locale;\n const metaDefaultLocale = (currentDocument.meta as { defaultLocale?: string | null } | undefined)\n ?.defaultLocale;\n\n const { data: locales } = useGetI18nLocalesQuery(undefined, {\n skip: !contentTypeLocalized || Boolean(metaDefaultLocale),\n });\n\n if (!contentTypeLocalized) {\n return true;\n }\n\n const localesDefault = Array.isArray(locales)\n ? locales.find((locale) => locale.isDefault)?.code\n : undefined;\n const defaultLocale = metaDefaultLocale || localesDefault;\n\n if (!currentLocale || !defaultLocale) {\n return true;\n }\n\n return currentLocale === defaultLocale;\n};\n\ntype DocumentMetaForLock = {\n availableLocales?: Array<{ locale?: string | null }>;\n defaultLocale?: string | null;\n};\n\n/**\n * @alpha\n * @public\n * Whether the document being edited has a default-locale entry.\n *\n * `availableLocales` excludes the locale currently open, so a match is a sibling\n * in the default locale. Brand-new creates have no document metadata. A document\n * that exists only in a secondary locale has none either. Shared fields stay\n * editable in both cases: there is no default-locale entry to edit them in.\n */\nconst useHasDefaultLocaleEntry = (): boolean => {\n const { currentDocument } = useDocumentContext('useHasDefaultLocaleEntry');\n const meta = currentDocument.meta as DocumentMetaForLock | undefined;\n\n const contentTypeLocalized =\n (currentDocument.schema?.pluginOptions as { i18n?: { localized?: boolean } } | undefined)?.i18n\n ?.localized === true;\n\n const { data: locales } = useGetI18nLocalesQuery(undefined, {\n skip: !contentTypeLocalized || Boolean(meta?.defaultLocale),\n });\n\n if (!meta) {\n return false;\n }\n\n const localesDefault = Array.isArray(locales)\n ? locales.find((locale) => locale.isDefault)?.code\n : undefined;\n const defaultLocale = meta.defaultLocale || localesDefault;\n\n if (!defaultLocale) {\n return false;\n }\n\n return (meta.availableLocales ?? []).some((entry) => entry.locale === defaultLocale);\n};\n\n/**\n * @alpha\n * @public\n * Whether an attribute holds a value shared by every locale of the entry.\n */\nconst isSharedI18nAttribute = (\n attribute: { pluginOptions?: object; type?: string } | undefined\n): boolean => {\n if (!attribute) {\n return false;\n }\n\n // Match server `isLocalizedAttribute`: relations and uids are always locale-specific\n // even when they omit `pluginOptions.i18n.localized` (CTB never offers that checkbox).\n if (attribute.type === 'relation' || attribute.type === 'uid') {\n return false;\n }\n\n const i18nOptions = attribute.pluginOptions as { i18n?: { localized?: boolean } } | undefined;\n\n // Only explicit `localized: true` is localizable for other attribute types.\n return i18nOptions?.i18n?.localized !== true;\n};\n\n/**\n * Whether shared fields are locked for the document being edited: a non-default\n * locale of a document that already has a default-locale entry. The editor can\n * still unlock them (see `I18nSharedFieldsLock`).\n */\nconst useIsSharedFieldsLockActive = (): boolean => {\n const isDefaultLocale = useIsEditingDefaultLocale();\n const hasDefaultLocaleEntry = useHasDefaultLocaleEntry();\n\n return !isDefaultLocale && hasDefaultLocaleEntry;\n};\n\n/**\n * @internal\n * True when a field should be locked because it is not locale-specific and the\n * editor is on a non-default locale that already has a default-locale entry.\n * Root-level only — nested component fields inherit disable from their parent\n * component input.\n */\nconst useShouldLockNonLocalizedField = (\n attribute: { pluginOptions?: object; type?: string } | undefined,\n options: { isInsideComponent?: boolean } = {}\n): boolean => {\n const isLockActive = useIsSharedFieldsLockActive();\n const isUnlocked = useI18nSharedFieldsLock(\n 'useShouldLockNonLocalizedField',\n (state) => state.isUnlocked\n );\n\n if (!isLockActive || isUnlocked || options.isInsideComponent) {\n return false;\n }\n\n return isSharedI18nAttribute(attribute);\n};\n\n/**\n * @internal\n * Returns a function that removes locked shared fields from the payload that\n * creates a missing locale, so the server copies them whole from the default\n * locale. The form only holds a prefilled copy of them, and relations nested\n * in components or dynamic zones are not carried in that copy. Once the editor\n * unlocks shared fields, the ones they changed are kept.\n */\nconst useOmitLockedSharedFieldsOnLocaleCreate = () => {\n const { currentDocument } = useDocumentContext('useOmitLockedSharedFieldsOnLocaleCreate');\n const isLockActive = useIsSharedFieldsLockActive();\n const isUnlocked = useI18nSharedFieldsLock(\n 'useOmitLockedSharedFieldsOnLocaleCreate',\n (state) => state.isUnlocked\n );\n const values = useForm(\n 'useOmitLockedSharedFieldsOnLocaleCreate',\n (state) => state.values as Record<string, unknown>\n );\n const initialValues = useForm(\n 'useOmitLockedSharedFieldsOnLocaleCreate',\n (state) => state.initialValues as Record<string, unknown>\n );\n const isCreatingLocale = !currentDocument.document?.id;\n const attributes = currentDocument.schema?.attributes;\n\n return React.useCallback(\n <TData extends Record<string, unknown>>(data: TData): TData => {\n if (!isLockActive || !isCreatingLocale || !attributes) {\n return data;\n }\n\n return Object.fromEntries(\n Object.entries(data).filter(\n ([name]) =>\n !isSharedI18nAttribute(attributes[name]) ||\n (isUnlocked && !isEqual(values?.[name], initialValues?.[name]))\n )\n ) as TData;\n },\n [attributes, initialValues, isCreatingLocale, isLockActive, isUnlocked, values]\n );\n};\n\nexport {\n isSharedI18nAttribute,\n useHasDefaultLocaleEntry,\n useIsEditingDefaultLocale,\n useOmitLockedSharedFieldsOnLocaleCreate,\n useShouldLockNonLocalizedField,\n};\n"],"names":["useIsEditingDefaultLocale","query","useQueryParams","currentDocument","useDocumentContext","contentTypeLocalized","schema","pluginOptions","i18n","localized","currentLocale","plugins","locale","document","metaDefaultLocale","meta","defaultLocale","data","locales","useGetI18nLocalesQuery","undefined","skip","Boolean","localesDefault","Array","isArray","find","isDefault","code","useHasDefaultLocaleEntry","availableLocales","some","entry","isSharedI18nAttribute","attribute","type","i18nOptions","useIsSharedFieldsLockActive","isDefaultLocale","hasDefaultLocaleEntry","useShouldLockNonLocalizedField","options","isLockActive","isUnlocked","useI18nSharedFieldsLock","state","isInsideComponent","useOmitLockedSharedFieldsOnLocaleCreate","values","useForm","initialValues","isCreatingLocale","id","attributes","React","useCallback","Object","fromEntries","entries","filter","name","isEqual"],"mappings":"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"}
|
|
@@ -4,6 +4,8 @@ var React = require('react');
|
|
|
4
4
|
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
5
5
|
var get = require('lodash/get');
|
|
6
6
|
var set = require('lodash/set');
|
|
7
|
+
var qs = require('qs');
|
|
8
|
+
var reactRouterDom = require('react-router-dom');
|
|
7
9
|
|
|
8
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
11
|
|
|
@@ -30,7 +32,9 @@ const normalizeConfigEntry = (key, entry, scope)=>{
|
|
|
30
32
|
};
|
|
31
33
|
const usePersistentPartialQueryParams = (config)=>{
|
|
32
34
|
const scope = strapiAdmin.usePersistentStateScope();
|
|
33
|
-
const [{ query }
|
|
35
|
+
const [{ query }] = strapiAdmin.useQueryParams();
|
|
36
|
+
const location = reactRouterDom.useLocation();
|
|
37
|
+
const navigate = reactRouterDom.useNavigate();
|
|
34
38
|
const clonedConfig = JSON.stringify(config);
|
|
35
39
|
const [isHydrated, setIsHydrated] = React.useState(false);
|
|
36
40
|
// load query params from local storge
|
|
@@ -56,13 +60,22 @@ const usePersistentPartialQueryParams = (config)=>{
|
|
|
56
60
|
...query
|
|
57
61
|
};
|
|
58
62
|
if (nextQuery.filters !== undefined) {
|
|
59
|
-
|
|
63
|
+
// The URL is written without encoding, as `setQuery` does everywhere, and that cannot change without breaking changes.
|
|
60
64
|
// This ensures that, when reading the persisted values and writing them in the URI, they are encoded properly.
|
|
61
65
|
// We are only encoding the filters because we know they can hold values with special characters. We didn't bother
|
|
62
66
|
// with the other query params because as of today, they are not expected to hold such values.
|
|
63
67
|
nextQuery.filters = strapiAdmin.deepEncodeQueryValues(nextQuery.filters);
|
|
64
68
|
}
|
|
65
|
-
setQuery(nextQuery, 'push', true)
|
|
69
|
+
// What `setQuery(nextQuery, 'push', true)` does, but keeping the history entry's state: a page
|
|
70
|
+
// opened with state (e.g. `forceValidation` from Content Releases) must still find it there.
|
|
71
|
+
navigate({
|
|
72
|
+
search: qs.stringify(nextQuery, {
|
|
73
|
+
encode: false
|
|
74
|
+
})
|
|
75
|
+
}, {
|
|
76
|
+
replace: true,
|
|
77
|
+
state: location.state
|
|
78
|
+
});
|
|
66
79
|
}
|
|
67
80
|
setIsHydrated(true);
|
|
68
81
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePersistentQueryParams.js","sources":["../../../admin/src/hooks/usePersistentQueryParams.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nimport {\n deepEncodeQueryValues,\n usePersistentStateScope,\n useQueryParams,\n} from '@strapi/admin/strapi-admin';\nimport get from 'lodash/get';\nimport set from 'lodash/set';\n\ntype PropertyPath = Parameters<typeof get>[1];\n\ninterface PersistentQueryConfigEntry {\n paths: PropertyPath[];\n scoped?: boolean;\n}\n\nconst filterObjectKeys = (obj: object, keys: PropertyPath[]) => {\n const result: Record<string, unknown> = {};\n\n for (const path of keys) {\n const value = get(obj, path);\n\n if (value !== undefined) {\n set(result, path, value);\n }\n }\n\n return result;\n};\n\nexport type PersistentQueryConfig = Record<string, PersistentQueryConfigEntry>;\n\nconst normalizeConfigEntry = (\n key: string,\n entry: PersistentQueryConfigEntry,\n scope: string | false | undefined\n) => {\n const { paths } = entry;\n const isScoped = entry.scoped === true && !!scope;\n\n return {\n key: isScoped ? `${key}:${scope}` : key,\n paths,\n };\n};\n\nexport const usePersistentPartialQueryParams = (config: PersistentQueryConfig) => {\n const scope = usePersistentStateScope();\n const [{ query }
|
|
1
|
+
{"version":3,"file":"usePersistentQueryParams.js","sources":["../../../admin/src/hooks/usePersistentQueryParams.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nimport {\n deepEncodeQueryValues,\n usePersistentStateScope,\n useQueryParams,\n} from '@strapi/admin/strapi-admin';\nimport get from 'lodash/get';\nimport set from 'lodash/set';\nimport { stringify } from 'qs';\nimport { useLocation, useNavigate } from 'react-router-dom';\n\ntype PropertyPath = Parameters<typeof get>[1];\n\ninterface PersistentQueryConfigEntry {\n paths: PropertyPath[];\n scoped?: boolean;\n}\n\nconst filterObjectKeys = (obj: object, keys: PropertyPath[]) => {\n const result: Record<string, unknown> = {};\n\n for (const path of keys) {\n const value = get(obj, path);\n\n if (value !== undefined) {\n set(result, path, value);\n }\n }\n\n return result;\n};\n\nexport type PersistentQueryConfig = Record<string, PersistentQueryConfigEntry>;\n\nconst normalizeConfigEntry = (\n key: string,\n entry: PersistentQueryConfigEntry,\n scope: string | false | undefined\n) => {\n const { paths } = entry;\n const isScoped = entry.scoped === true && !!scope;\n\n return {\n key: isScoped ? `${key}:${scope}` : key,\n paths,\n };\n};\n\nexport const usePersistentPartialQueryParams = (config: PersistentQueryConfig) => {\n const scope = usePersistentStateScope();\n const [{ query }] = useQueryParams();\n const location = useLocation();\n const navigate = useNavigate();\n const clonedConfig = JSON.stringify(config);\n const [isHydrated, setIsHydrated] = useState(false);\n\n // load query params from local storge\n useEffect(() => {\n const mergedFilteredQuery: Record<string, unknown> = {};\n\n for (const [keyPrefix, entry] of Object.entries(config)) {\n const { key, paths } = normalizeConfigEntry(keyPrefix, entry, scope);\n\n const savedQueryParams = window.localStorage.getItem(key);\n if (!savedQueryParams) continue;\n\n let parsedSavedParams: Record<string, unknown>;\n try {\n parsedSavedParams = JSON.parse(savedQueryParams);\n } catch {\n continue;\n }\n if (Object.keys(parsedSavedParams).length === 0) continue;\n\n const filteredQuery = filterObjectKeys(parsedSavedParams, paths);\n Object.assign(mergedFilteredQuery, filteredQuery);\n }\n\n if (Object.keys(mergedFilteredQuery).length > 0) {\n const nextQuery: Record<string, unknown> = { ...mergedFilteredQuery, ...query };\n\n if (nextQuery.filters !== undefined) {\n // The URL is written without encoding, as `setQuery` does everywhere, and that cannot change without breaking changes.\n // This ensures that, when reading the persisted values and writing them in the URI, they are encoded properly.\n // We are only encoding the filters because we know they can hold values with special characters. We didn't bother\n // with the other query params because as of today, they are not expected to hold such values.\n nextQuery.filters = deepEncodeQueryValues(nextQuery.filters);\n }\n\n // What `setQuery(nextQuery, 'push', true)` does, but keeping the history entry's state: a page\n // opened with state (e.g. `forceValidation` from Content Releases) must still find it there.\n navigate(\n { search: stringify(nextQuery, { encode: false }) },\n { replace: true, state: location.state }\n );\n }\n setIsHydrated(true);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clonedConfig, scope]);\n\n // update local storage\n useEffect(() => {\n for (const [keyPrefix, entry] of Object.entries(config)) {\n const { key, paths } = normalizeConfigEntry(keyPrefix, entry, scope);\n\n const paramsToPersist = filterObjectKeys(query, paths);\n if (Object.keys(paramsToPersist).length === 0) {\n window.localStorage.removeItem(key);\n continue;\n }\n\n window.localStorage.setItem(key, JSON.stringify(paramsToPersist));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [query, clonedConfig, scope]);\n\n return { isHydrated };\n};\n"],"names":["filterObjectKeys","obj","keys","result","path","value","get","undefined","set","normalizeConfigEntry","key","entry","scope","paths","isScoped","scoped","usePersistentPartialQueryParams","config","usePersistentStateScope","query","useQueryParams","location","useLocation","navigate","useNavigate","clonedConfig","JSON","stringify","isHydrated","setIsHydrated","useState","useEffect","mergedFilteredQuery","keyPrefix","Object","entries","savedQueryParams","window","localStorage","getItem","parsedSavedParams","parse","length","filteredQuery","assign","nextQuery","filters","deepEncodeQueryValues","search","encode","replace","state","paramsToPersist","removeItem","setItem"],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import { useState, useEffect } from 'react';
|
|
|
2
2
|
import { usePersistentStateScope, useQueryParams, deepEncodeQueryValues } from '@strapi/admin/strapi-admin';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import set from 'lodash/set';
|
|
5
|
+
import { stringify } from 'qs';
|
|
6
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
5
7
|
|
|
6
8
|
const filterObjectKeys = (obj, keys)=>{
|
|
7
9
|
const result = {};
|
|
@@ -23,7 +25,9 @@ const normalizeConfigEntry = (key, entry, scope)=>{
|
|
|
23
25
|
};
|
|
24
26
|
const usePersistentPartialQueryParams = (config)=>{
|
|
25
27
|
const scope = usePersistentStateScope();
|
|
26
|
-
const [{ query }
|
|
28
|
+
const [{ query }] = useQueryParams();
|
|
29
|
+
const location = useLocation();
|
|
30
|
+
const navigate = useNavigate();
|
|
27
31
|
const clonedConfig = JSON.stringify(config);
|
|
28
32
|
const [isHydrated, setIsHydrated] = useState(false);
|
|
29
33
|
// load query params from local storge
|
|
@@ -49,13 +53,22 @@ const usePersistentPartialQueryParams = (config)=>{
|
|
|
49
53
|
...query
|
|
50
54
|
};
|
|
51
55
|
if (nextQuery.filters !== undefined) {
|
|
52
|
-
|
|
56
|
+
// The URL is written without encoding, as `setQuery` does everywhere, and that cannot change without breaking changes.
|
|
53
57
|
// This ensures that, when reading the persisted values and writing them in the URI, they are encoded properly.
|
|
54
58
|
// We are only encoding the filters because we know they can hold values with special characters. We didn't bother
|
|
55
59
|
// with the other query params because as of today, they are not expected to hold such values.
|
|
56
60
|
nextQuery.filters = deepEncodeQueryValues(nextQuery.filters);
|
|
57
61
|
}
|
|
58
|
-
setQuery(nextQuery, 'push', true)
|
|
62
|
+
// What `setQuery(nextQuery, 'push', true)` does, but keeping the history entry's state: a page
|
|
63
|
+
// opened with state (e.g. `forceValidation` from Content Releases) must still find it there.
|
|
64
|
+
navigate({
|
|
65
|
+
search: stringify(nextQuery, {
|
|
66
|
+
encode: false
|
|
67
|
+
})
|
|
68
|
+
}, {
|
|
69
|
+
replace: true,
|
|
70
|
+
state: location.state
|
|
71
|
+
});
|
|
59
72
|
}
|
|
60
73
|
setIsHydrated(true);
|
|
61
74
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|