@strapi/content-manager 5.23.4 → 5.23.5
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/preview/components/InputPopover.js +29 -9
- package/dist/admin/preview/components/InputPopover.js.map +1 -1
- package/dist/admin/preview/components/InputPopover.mjs +31 -11
- package/dist/admin/preview/components/InputPopover.mjs.map +1 -1
- package/dist/admin/preview/utils/constants.js +34 -0
- package/dist/admin/preview/utils/constants.js.map +1 -1
- package/dist/admin/preview/utils/constants.mjs +34 -1
- package/dist/admin/preview/utils/constants.mjs.map +1 -1
- package/dist/admin/preview/utils/fieldUtils.js +13 -4
- package/dist/admin/preview/utils/fieldUtils.js.map +1 -1
- package/dist/admin/preview/utils/fieldUtils.mjs +13 -5
- package/dist/admin/preview/utils/fieldUtils.mjs.map +1 -1
- package/dist/admin/preview/utils/previewScript.js +120 -44
- package/dist/admin/preview/utils/previewScript.js.map +1 -1
- package/dist/admin/preview/utils/previewScript.mjs +120 -44
- package/dist/admin/preview/utils/previewScript.mjs.map +1 -1
- package/dist/admin/src/preview/utils/constants.d.ts +35 -0
- package/dist/admin/src/preview/utils/fieldUtils.d.ts +6 -0
- package/dist/admin/src/preview/utils/previewScript.d.ts +1 -0
- package/dist/admin/translations/en.json.js +5 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +5 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/package.json +5 -5
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var React = require('react');
|
5
5
|
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
6
6
|
var designSystem = require('@strapi/design-system');
|
7
|
+
var reactIntl = require('react-intl');
|
7
8
|
var InputRenderer = require('../../pages/EditView/components/InputRenderer.js');
|
8
9
|
var Preview = require('../pages/Preview.js');
|
9
10
|
var constants = require('../utils/constants.js');
|
@@ -44,6 +45,7 @@ function useHasInputPopoverParent() {
|
|
44
45
|
const schema = Preview.usePreviewContext('InputPopover', (state)=>state.schema);
|
45
46
|
const components = Preview.usePreviewContext('InputPopover', (state)=>state.components);
|
46
47
|
const { toggleNotification } = strapiAdmin.useNotification();
|
48
|
+
const { formatMessage } = reactIntl.useIntl();
|
47
49
|
React__namespace.useEffect(()=>{
|
48
50
|
/**
|
49
51
|
* We receive window events sent from the user's preview via the injected script.
|
@@ -58,11 +60,11 @@ function useHasInputPopoverParent() {
|
|
58
60
|
}
|
59
61
|
if (event.data?.type === constants.INTERNAL_EVENTS.STRAPI_FIELD_FOCUS_INTENT) {
|
60
62
|
const fieldMetaData = fieldUtils.parseFieldMetaData(event.data.payload.path);
|
61
|
-
// TODO: check if notification better
|
62
63
|
if (!fieldMetaData) {
|
64
|
+
const { type, message } = constants.PREVIEW_ERROR_MESSAGES.INCOMPLETE_STRAPI_SOURCE;
|
63
65
|
toggleNotification({
|
64
|
-
type
|
65
|
-
message:
|
66
|
+
type,
|
67
|
+
message: formatMessage(message)
|
66
68
|
});
|
67
69
|
return;
|
68
70
|
}
|
@@ -71,9 +73,10 @@ function useHasInputPopoverParent() {
|
|
71
73
|
* current document. This doesn't do anything about fields that may come from relations to
|
72
74
|
* the current document however.
|
73
75
|
*/ if (fieldMetaData.documentId !== document.documentId) {
|
76
|
+
const { type, message } = constants.PREVIEW_ERROR_MESSAGES.DIFFERENT_DOCUMENT;
|
74
77
|
toggleNotification({
|
75
|
-
type
|
76
|
-
message:
|
78
|
+
type,
|
79
|
+
message: formatMessage(message)
|
77
80
|
});
|
78
81
|
return;
|
79
82
|
}
|
@@ -91,14 +94,29 @@ function useHasInputPopoverParent() {
|
|
91
94
|
attribute
|
92
95
|
});
|
93
96
|
} catch (error) {
|
94
|
-
if (error instanceof
|
97
|
+
if (error instanceof fieldUtils.PreviewFieldError) {
|
98
|
+
const { type, message } = constants.PREVIEW_ERROR_MESSAGES[error.messageKey];
|
95
99
|
toggleNotification({
|
96
|
-
type
|
100
|
+
type,
|
101
|
+
message: formatMessage(message)
|
102
|
+
});
|
103
|
+
} else if (error instanceof Error) {
|
104
|
+
toggleNotification({
|
105
|
+
type: 'danger',
|
97
106
|
message: error.message
|
98
107
|
});
|
99
108
|
}
|
100
109
|
}
|
101
110
|
}
|
111
|
+
if (event.data?.type === constants.INTERNAL_EVENTS.STRAPI_FIELD_SINGLE_CLICK_HINT) {
|
112
|
+
toggleNotification({
|
113
|
+
type: 'info',
|
114
|
+
message: formatMessage({
|
115
|
+
id: 'content-manager.preview.info.single-click-hint',
|
116
|
+
defaultMessage: 'Double click to edit'
|
117
|
+
})
|
118
|
+
});
|
119
|
+
}
|
102
120
|
};
|
103
121
|
window.addEventListener('message', handleMessage);
|
104
122
|
return ()=>{
|
@@ -110,7 +128,8 @@ function useHasInputPopoverParent() {
|
|
110
128
|
iframeRef,
|
111
129
|
schema,
|
112
130
|
setPopoverField,
|
113
|
-
toggleNotification
|
131
|
+
toggleNotification,
|
132
|
+
formatMessage
|
114
133
|
]);
|
115
134
|
if (!popoverField || !iframeRef.current) {
|
116
135
|
return null;
|
@@ -124,7 +143,8 @@ function useHasInputPopoverParent() {
|
|
124
143
|
left: iframeRect.left + 'px',
|
125
144
|
width: iframeRect.width + 'px',
|
126
145
|
height: iframeRect.height + 'px',
|
127
|
-
zIndex: 4
|
146
|
+
zIndex: 4,
|
147
|
+
onClick: ()=>iframeRef.current?.focus()
|
128
148
|
}),
|
129
149
|
/*#__PURE__*/ jsxRuntime.jsx(InputPopoverProvider, {
|
130
150
|
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Popover.Root, {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"InputPopover.js","sources":["../../../../admin/src/preview/components/InputPopover.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { createContext, useNotification } from '@strapi/admin/strapi-admin';\nimport { Box, Popover } from '@strapi/design-system';\n\nimport { type UseDocument } from '../../hooks/useDocument';\nimport { InputRenderer } from '../../pages/EditView/components/InputRenderer';\nimport { usePreviewContext } from '../pages/Preview';\nimport { INTERNAL_EVENTS } from '../utils/constants';\nimport { parseFieldMetaData, getAttributeSchemaFromPath } from '../utils/fieldUtils';\n\n/* -------------------------------------------------------------------------------------------------\n * Context utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * No need for actual data in the context. It's just to let children check if they're rendered\n * inside of a preview InputPopover without relying on prop drilling.\n */\ninterface InputPopoverContextValue {}\n\nconst [InputPopoverProvider, useInputPopoverContext] =\n createContext<InputPopoverContextValue>('InputPopover');\n\nfunction useHasInputPopoverParent() {\n const context = useInputPopoverContext('useHasInputPopoverParent', () => true, false);\n\n // useContext will return undefined if the called is not wrapped in the provider\n return context !== undefined;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * InputPopover\n * -----------------------------------------------------------------------------------------------*/\n\nconst InputPopover = ({ documentResponse }: { documentResponse: ReturnType<UseDocument> }) => {\n const iframeRef = usePreviewContext('InputPopover', (state) => state.iframeRef);\n const popoverField = usePreviewContext('InputPopover', (state) => state.popoverField);\n const setPopoverField = usePreviewContext('InputPopover', (state) => state.setPopoverField);\n const document = usePreviewContext('InputPopover', (state) => state.document);\n const schema = usePreviewContext('InputPopover', (state) => state.schema);\n const components = usePreviewContext('InputPopover', (state) => state.components);\n const { toggleNotification } = useNotification();\n\n React.useEffect(() => {\n /**\n * We receive window events sent from the user's preview via the injected script.\n * We listen to the ones here that target a specific field.\n */\n const handleMessage = (event: MessageEvent) => {\n // Only listen to events from the preview iframe\n if (iframeRef.current) {\n const previewOrigin = new URL(iframeRef.current?.src).origin;\n if (event.origin !== previewOrigin) {\n return;\n }\n }\n\n if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_FOCUS_INTENT) {\n const fieldMetaData = parseFieldMetaData(event.data.payload.path);\n\n // TODO: check if notification better\n if (!fieldMetaData) {\n toggleNotification({\n type: 'warning',\n message: 'Incomplete strapiSource attribute',\n });\n return;\n }\n\n /**\n * Ignore (for now) content that comes from separate API requests than the one for the\n * current document. This doesn't do anything about fields that may come from relations to\n * the current document however.\n */\n if (fieldMetaData.documentId !== document.documentId) {\n toggleNotification({\n type: 'warning',\n message: 'This field comes from a different document',\n });\n return;\n }\n\n try {\n const attribute = getAttributeSchemaFromPath({\n path: fieldMetaData.path,\n components,\n schema,\n document,\n });\n\n // We're able to handle the field, set it in context so the popover can pick it up\n setPopoverField({ ...fieldMetaData, position: event.data.payload.position, attribute });\n } catch (error) {\n if (error instanceof Error) {\n toggleNotification({ type: 'warning', message: error.message });\n }\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [components, document, iframeRef, schema, setPopoverField, toggleNotification]);\n if (!popoverField || !iframeRef.current) {\n return null;\n }\n\n const iframeRect = iframeRef.current.getBoundingClientRect();\n\n return (\n <>\n {/**\n * Overlay an empty div on top of the iframe while the popover is open so it can\n * intercept clicks. Without it, we wouldn't be able to close the popover by clicking outside,\n * because the click would be detected by the iframe window, not by the admin.\n **/}\n <Box\n position={'fixed'}\n top={iframeRect.top + 'px'}\n left={iframeRect.left + 'px'}\n width={iframeRect.width + 'px'}\n height={iframeRect.height + 'px'}\n zIndex={4}\n />\n <InputPopoverProvider>\n <Popover.Root open={true} onOpenChange={(open) => !open && setPopoverField(null)}>\n <Popover.Trigger>\n <Box\n position=\"fixed\"\n width={popoverField.position.width + 'px'}\n height={popoverField.position.height + 'px'}\n top={0}\n left={0}\n transform={`translate(${iframeRect.left + popoverField.position.left}px, ${iframeRect.top + popoverField.position.top}px)`}\n />\n </Popover.Trigger>\n <Popover.Content sideOffset={4}>\n <Box padding={4} width=\"400px\">\n {/* @ts-expect-error the types of `attribute` clash for some reason */}\n <InputRenderer\n document={documentResponse}\n attribute={popoverField.attribute}\n // TODO: retrieve the proper label from the layout\n label={popoverField.path}\n name={popoverField.path}\n type={popoverField.attribute.type}\n visible={true}\n />\n </Box>\n </Popover.Content>\n </Popover.Root>\n </InputPopoverProvider>\n </>\n );\n};\n\nexport { InputPopover, useHasInputPopoverParent };\n"],"names":["InputPopoverProvider","useInputPopoverContext","createContext","useHasInputPopoverParent","context","undefined","InputPopover","documentResponse","iframeRef","usePreviewContext","state","popoverField","setPopoverField","document","schema","components","toggleNotification","useNotification","React","useEffect","handleMessage","event","current","previewOrigin","URL","src","origin","data","type","INTERNAL_EVENTS","STRAPI_FIELD_FOCUS_INTENT","fieldMetaData","parseFieldMetaData","payload","path","message","documentId","attribute","getAttributeSchemaFromPath","position","error","Error","window","addEventListener","removeEventListener","iframeRect","getBoundingClientRect","_jsxs","_Fragment","_jsx","Box","top","left","width","height","zIndex","Popover","Root","open","onOpenChange","Trigger","transform","Content","sideOffset","padding","InputRenderer","label","name","visible"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"InputPopover.js","sources":["../../../../admin/src/preview/components/InputPopover.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { createContext, useNotification } from '@strapi/admin/strapi-admin';\nimport { Box, Popover } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { type UseDocument } from '../../hooks/useDocument';\nimport { InputRenderer } from '../../pages/EditView/components/InputRenderer';\nimport { usePreviewContext } from '../pages/Preview';\nimport { INTERNAL_EVENTS, PREVIEW_ERROR_MESSAGES } from '../utils/constants';\nimport {\n parseFieldMetaData,\n getAttributeSchemaFromPath,\n PreviewFieldError,\n} from '../utils/fieldUtils';\n\n/* -------------------------------------------------------------------------------------------------\n * Context utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * No need for actual data in the context. It's just to let children check if they're rendered\n * inside of a preview InputPopover without relying on prop drilling.\n */\ninterface InputPopoverContextValue {}\n\nconst [InputPopoverProvider, useInputPopoverContext] =\n createContext<InputPopoverContextValue>('InputPopover');\n\nfunction useHasInputPopoverParent() {\n const context = useInputPopoverContext('useHasInputPopoverParent', () => true, false);\n\n // useContext will return undefined if the called is not wrapped in the provider\n return context !== undefined;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * InputPopover\n * -----------------------------------------------------------------------------------------------*/\n\nconst InputPopover = ({ documentResponse }: { documentResponse: ReturnType<UseDocument> }) => {\n const iframeRef = usePreviewContext('InputPopover', (state) => state.iframeRef);\n const popoverField = usePreviewContext('InputPopover', (state) => state.popoverField);\n const setPopoverField = usePreviewContext('InputPopover', (state) => state.setPopoverField);\n const document = usePreviewContext('InputPopover', (state) => state.document);\n const schema = usePreviewContext('InputPopover', (state) => state.schema);\n const components = usePreviewContext('InputPopover', (state) => state.components);\n\n const { toggleNotification } = useNotification();\n const { formatMessage } = useIntl();\n\n React.useEffect(() => {\n /**\n * We receive window events sent from the user's preview via the injected script.\n * We listen to the ones here that target a specific field.\n */\n const handleMessage = (event: MessageEvent) => {\n // Only listen to events from the preview iframe\n if (iframeRef.current) {\n const previewOrigin = new URL(iframeRef.current?.src).origin;\n if (event.origin !== previewOrigin) {\n return;\n }\n }\n\n if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_FOCUS_INTENT) {\n const fieldMetaData = parseFieldMetaData(event.data.payload.path);\n\n if (!fieldMetaData) {\n const { type, message } = PREVIEW_ERROR_MESSAGES.INCOMPLETE_STRAPI_SOURCE;\n toggleNotification({ type, message: formatMessage(message) });\n return;\n }\n\n /**\n * Ignore (for now) content that comes from separate API requests than the one for the\n * current document. This doesn't do anything about fields that may come from relations to\n * the current document however.\n */\n if (fieldMetaData.documentId !== document.documentId) {\n const { type, message } = PREVIEW_ERROR_MESSAGES.DIFFERENT_DOCUMENT;\n toggleNotification({ type, message: formatMessage(message) });\n return;\n }\n\n try {\n const attribute = getAttributeSchemaFromPath({\n path: fieldMetaData.path,\n components,\n schema,\n document,\n });\n\n // We're able to handle the field, set it in context so the popover can pick it up\n setPopoverField({ ...fieldMetaData, position: event.data.payload.position, attribute });\n } catch (error) {\n if (error instanceof PreviewFieldError) {\n const { type, message } = PREVIEW_ERROR_MESSAGES[error.messageKey];\n toggleNotification({ type, message: formatMessage(message) });\n } else if (error instanceof Error) {\n toggleNotification({ type: 'danger', message: error.message });\n }\n }\n }\n\n if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_SINGLE_CLICK_HINT) {\n toggleNotification({\n type: 'info',\n message: formatMessage({\n id: 'content-manager.preview.info.single-click-hint',\n defaultMessage: 'Double click to edit',\n }),\n });\n }\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [components, document, iframeRef, schema, setPopoverField, toggleNotification, formatMessage]);\n if (!popoverField || !iframeRef.current) {\n return null;\n }\n\n const iframeRect = iframeRef.current.getBoundingClientRect();\n\n return (\n <>\n {/**\n * Overlay an empty div on top of the iframe while the popover is open so it can\n * intercept clicks. Without it, we wouldn't be able to close the popover by clicking outside,\n * because the click would be detected by the iframe window, not by the admin.\n **/}\n <Box\n position={'fixed'}\n top={iframeRect.top + 'px'}\n left={iframeRect.left + 'px'}\n width={iframeRect.width + 'px'}\n height={iframeRect.height + 'px'}\n zIndex={4}\n onClick={() => iframeRef.current?.focus()}\n />\n <InputPopoverProvider>\n <Popover.Root open={true} onOpenChange={(open) => !open && setPopoverField(null)}>\n <Popover.Trigger>\n <Box\n position=\"fixed\"\n width={popoverField.position.width + 'px'}\n height={popoverField.position.height + 'px'}\n top={0}\n left={0}\n transform={`translate(${iframeRect.left + popoverField.position.left}px, ${iframeRect.top + popoverField.position.top}px)`}\n />\n </Popover.Trigger>\n <Popover.Content sideOffset={4}>\n <Box padding={4} width=\"400px\">\n {/* @ts-expect-error the types of `attribute` clash for some reason */}\n <InputRenderer\n document={documentResponse}\n attribute={popoverField.attribute}\n // TODO: retrieve the proper label from the layout\n label={popoverField.path}\n name={popoverField.path}\n type={popoverField.attribute.type}\n visible={true}\n />\n </Box>\n </Popover.Content>\n </Popover.Root>\n </InputPopoverProvider>\n </>\n );\n};\n\nexport { InputPopover, useHasInputPopoverParent };\n"],"names":["InputPopoverProvider","useInputPopoverContext","createContext","useHasInputPopoverParent","context","undefined","InputPopover","documentResponse","iframeRef","usePreviewContext","state","popoverField","setPopoverField","document","schema","components","toggleNotification","useNotification","formatMessage","useIntl","React","useEffect","handleMessage","event","current","previewOrigin","URL","src","origin","data","type","INTERNAL_EVENTS","STRAPI_FIELD_FOCUS_INTENT","fieldMetaData","parseFieldMetaData","payload","path","message","PREVIEW_ERROR_MESSAGES","INCOMPLETE_STRAPI_SOURCE","documentId","DIFFERENT_DOCUMENT","attribute","getAttributeSchemaFromPath","position","error","PreviewFieldError","messageKey","Error","STRAPI_FIELD_SINGLE_CLICK_HINT","id","defaultMessage","window","addEventListener","removeEventListener","iframeRect","getBoundingClientRect","_jsxs","_Fragment","_jsx","Box","top","left","width","height","zIndex","onClick","focus","Popover","Root","open","onOpenChange","Trigger","transform","Content","sideOffset","padding","InputRenderer","label","name","visible"],"mappings":"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"}
|
@@ -2,10 +2,11 @@ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
3
3
|
import { createContext, useNotification } from '@strapi/admin/strapi-admin';
|
4
4
|
import { Box, Popover } from '@strapi/design-system';
|
5
|
+
import { useIntl } from 'react-intl';
|
5
6
|
import { InputRenderer as MemoizedInputRenderer } from '../../pages/EditView/components/InputRenderer.mjs';
|
6
7
|
import { usePreviewContext } from '../pages/Preview.mjs';
|
7
|
-
import { INTERNAL_EVENTS } from '../utils/constants.mjs';
|
8
|
-
import { parseFieldMetaData, getAttributeSchemaFromPath } from '../utils/fieldUtils.mjs';
|
8
|
+
import { INTERNAL_EVENTS, PREVIEW_ERROR_MESSAGES } from '../utils/constants.mjs';
|
9
|
+
import { parseFieldMetaData, getAttributeSchemaFromPath, PreviewFieldError } from '../utils/fieldUtils.mjs';
|
9
10
|
|
10
11
|
const [InputPopoverProvider, useInputPopoverContext] = createContext('InputPopover');
|
11
12
|
function useHasInputPopoverParent() {
|
@@ -23,6 +24,7 @@ function useHasInputPopoverParent() {
|
|
23
24
|
const schema = usePreviewContext('InputPopover', (state)=>state.schema);
|
24
25
|
const components = usePreviewContext('InputPopover', (state)=>state.components);
|
25
26
|
const { toggleNotification } = useNotification();
|
27
|
+
const { formatMessage } = useIntl();
|
26
28
|
React.useEffect(()=>{
|
27
29
|
/**
|
28
30
|
* We receive window events sent from the user's preview via the injected script.
|
@@ -37,11 +39,11 @@ function useHasInputPopoverParent() {
|
|
37
39
|
}
|
38
40
|
if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_FOCUS_INTENT) {
|
39
41
|
const fieldMetaData = parseFieldMetaData(event.data.payload.path);
|
40
|
-
// TODO: check if notification better
|
41
42
|
if (!fieldMetaData) {
|
43
|
+
const { type, message } = PREVIEW_ERROR_MESSAGES.INCOMPLETE_STRAPI_SOURCE;
|
42
44
|
toggleNotification({
|
43
|
-
type
|
44
|
-
message:
|
45
|
+
type,
|
46
|
+
message: formatMessage(message)
|
45
47
|
});
|
46
48
|
return;
|
47
49
|
}
|
@@ -50,9 +52,10 @@ function useHasInputPopoverParent() {
|
|
50
52
|
* current document. This doesn't do anything about fields that may come from relations to
|
51
53
|
* the current document however.
|
52
54
|
*/ if (fieldMetaData.documentId !== document.documentId) {
|
55
|
+
const { type, message } = PREVIEW_ERROR_MESSAGES.DIFFERENT_DOCUMENT;
|
53
56
|
toggleNotification({
|
54
|
-
type
|
55
|
-
message:
|
57
|
+
type,
|
58
|
+
message: formatMessage(message)
|
56
59
|
});
|
57
60
|
return;
|
58
61
|
}
|
@@ -70,14 +73,29 @@ function useHasInputPopoverParent() {
|
|
70
73
|
attribute
|
71
74
|
});
|
72
75
|
} catch (error) {
|
73
|
-
if (error instanceof
|
76
|
+
if (error instanceof PreviewFieldError) {
|
77
|
+
const { type, message } = PREVIEW_ERROR_MESSAGES[error.messageKey];
|
74
78
|
toggleNotification({
|
75
|
-
type
|
79
|
+
type,
|
80
|
+
message: formatMessage(message)
|
81
|
+
});
|
82
|
+
} else if (error instanceof Error) {
|
83
|
+
toggleNotification({
|
84
|
+
type: 'danger',
|
76
85
|
message: error.message
|
77
86
|
});
|
78
87
|
}
|
79
88
|
}
|
80
89
|
}
|
90
|
+
if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_SINGLE_CLICK_HINT) {
|
91
|
+
toggleNotification({
|
92
|
+
type: 'info',
|
93
|
+
message: formatMessage({
|
94
|
+
id: 'content-manager.preview.info.single-click-hint',
|
95
|
+
defaultMessage: 'Double click to edit'
|
96
|
+
})
|
97
|
+
});
|
98
|
+
}
|
81
99
|
};
|
82
100
|
window.addEventListener('message', handleMessage);
|
83
101
|
return ()=>{
|
@@ -89,7 +107,8 @@ function useHasInputPopoverParent() {
|
|
89
107
|
iframeRef,
|
90
108
|
schema,
|
91
109
|
setPopoverField,
|
92
|
-
toggleNotification
|
110
|
+
toggleNotification,
|
111
|
+
formatMessage
|
93
112
|
]);
|
94
113
|
if (!popoverField || !iframeRef.current) {
|
95
114
|
return null;
|
@@ -103,7 +122,8 @@ function useHasInputPopoverParent() {
|
|
103
122
|
left: iframeRect.left + 'px',
|
104
123
|
width: iframeRect.width + 'px',
|
105
124
|
height: iframeRect.height + 'px',
|
106
|
-
zIndex: 4
|
125
|
+
zIndex: 4,
|
126
|
+
onClick: ()=>iframeRef.current?.focus()
|
107
127
|
}),
|
108
128
|
/*#__PURE__*/ jsx(InputPopoverProvider, {
|
109
129
|
children: /*#__PURE__*/ jsxs(Popover.Root, {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"InputPopover.mjs","sources":["../../../../admin/src/preview/components/InputPopover.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { createContext, useNotification } from '@strapi/admin/strapi-admin';\nimport { Box, Popover } from '@strapi/design-system';\n\nimport { type UseDocument } from '../../hooks/useDocument';\nimport { InputRenderer } from '../../pages/EditView/components/InputRenderer';\nimport { usePreviewContext } from '../pages/Preview';\nimport { INTERNAL_EVENTS } from '../utils/constants';\nimport { parseFieldMetaData, getAttributeSchemaFromPath } from '../utils/fieldUtils';\n\n/* -------------------------------------------------------------------------------------------------\n * Context utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * No need for actual data in the context. It's just to let children check if they're rendered\n * inside of a preview InputPopover without relying on prop drilling.\n */\ninterface InputPopoverContextValue {}\n\nconst [InputPopoverProvider, useInputPopoverContext] =\n createContext<InputPopoverContextValue>('InputPopover');\n\nfunction useHasInputPopoverParent() {\n const context = useInputPopoverContext('useHasInputPopoverParent', () => true, false);\n\n // useContext will return undefined if the called is not wrapped in the provider\n return context !== undefined;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * InputPopover\n * -----------------------------------------------------------------------------------------------*/\n\nconst InputPopover = ({ documentResponse }: { documentResponse: ReturnType<UseDocument> }) => {\n const iframeRef = usePreviewContext('InputPopover', (state) => state.iframeRef);\n const popoverField = usePreviewContext('InputPopover', (state) => state.popoverField);\n const setPopoverField = usePreviewContext('InputPopover', (state) => state.setPopoverField);\n const document = usePreviewContext('InputPopover', (state) => state.document);\n const schema = usePreviewContext('InputPopover', (state) => state.schema);\n const components = usePreviewContext('InputPopover', (state) => state.components);\n const { toggleNotification } = useNotification();\n\n React.useEffect(() => {\n /**\n * We receive window events sent from the user's preview via the injected script.\n * We listen to the ones here that target a specific field.\n */\n const handleMessage = (event: MessageEvent) => {\n // Only listen to events from the preview iframe\n if (iframeRef.current) {\n const previewOrigin = new URL(iframeRef.current?.src).origin;\n if (event.origin !== previewOrigin) {\n return;\n }\n }\n\n if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_FOCUS_INTENT) {\n const fieldMetaData = parseFieldMetaData(event.data.payload.path);\n\n // TODO: check if notification better\n if (!fieldMetaData) {\n toggleNotification({\n type: 'warning',\n message: 'Incomplete strapiSource attribute',\n });\n return;\n }\n\n /**\n * Ignore (for now) content that comes from separate API requests than the one for the\n * current document. This doesn't do anything about fields that may come from relations to\n * the current document however.\n */\n if (fieldMetaData.documentId !== document.documentId) {\n toggleNotification({\n type: 'warning',\n message: 'This field comes from a different document',\n });\n return;\n }\n\n try {\n const attribute = getAttributeSchemaFromPath({\n path: fieldMetaData.path,\n components,\n schema,\n document,\n });\n\n // We're able to handle the field, set it in context so the popover can pick it up\n setPopoverField({ ...fieldMetaData, position: event.data.payload.position, attribute });\n } catch (error) {\n if (error instanceof Error) {\n toggleNotification({ type: 'warning', message: error.message });\n }\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [components, document, iframeRef, schema, setPopoverField, toggleNotification]);\n if (!popoverField || !iframeRef.current) {\n return null;\n }\n\n const iframeRect = iframeRef.current.getBoundingClientRect();\n\n return (\n <>\n {/**\n * Overlay an empty div on top of the iframe while the popover is open so it can\n * intercept clicks. Without it, we wouldn't be able to close the popover by clicking outside,\n * because the click would be detected by the iframe window, not by the admin.\n **/}\n <Box\n position={'fixed'}\n top={iframeRect.top + 'px'}\n left={iframeRect.left + 'px'}\n width={iframeRect.width + 'px'}\n height={iframeRect.height + 'px'}\n zIndex={4}\n />\n <InputPopoverProvider>\n <Popover.Root open={true} onOpenChange={(open) => !open && setPopoverField(null)}>\n <Popover.Trigger>\n <Box\n position=\"fixed\"\n width={popoverField.position.width + 'px'}\n height={popoverField.position.height + 'px'}\n top={0}\n left={0}\n transform={`translate(${iframeRect.left + popoverField.position.left}px, ${iframeRect.top + popoverField.position.top}px)`}\n />\n </Popover.Trigger>\n <Popover.Content sideOffset={4}>\n <Box padding={4} width=\"400px\">\n {/* @ts-expect-error the types of `attribute` clash for some reason */}\n <InputRenderer\n document={documentResponse}\n attribute={popoverField.attribute}\n // TODO: retrieve the proper label from the layout\n label={popoverField.path}\n name={popoverField.path}\n type={popoverField.attribute.type}\n visible={true}\n />\n </Box>\n </Popover.Content>\n </Popover.Root>\n </InputPopoverProvider>\n </>\n );\n};\n\nexport { InputPopover, useHasInputPopoverParent };\n"],"names":["InputPopoverProvider","useInputPopoverContext","createContext","useHasInputPopoverParent","context","undefined","InputPopover","documentResponse","iframeRef","usePreviewContext","state","popoverField","setPopoverField","document","schema","components","toggleNotification","useNotification","React","useEffect","handleMessage","event","current","previewOrigin","URL","src","origin","data","type","INTERNAL_EVENTS","STRAPI_FIELD_FOCUS_INTENT","fieldMetaData","parseFieldMetaData","payload","path","message","documentId","attribute","getAttributeSchemaFromPath","position","error","Error","window","addEventListener","removeEventListener","iframeRect","getBoundingClientRect","_jsxs","_Fragment","_jsx","Box","top","left","width","height","zIndex","Popover","Root","open","onOpenChange","Trigger","transform","Content","sideOffset","padding","InputRenderer","label","name","visible"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"InputPopover.mjs","sources":["../../../../admin/src/preview/components/InputPopover.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { createContext, useNotification } from '@strapi/admin/strapi-admin';\nimport { Box, Popover } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { type UseDocument } from '../../hooks/useDocument';\nimport { InputRenderer } from '../../pages/EditView/components/InputRenderer';\nimport { usePreviewContext } from '../pages/Preview';\nimport { INTERNAL_EVENTS, PREVIEW_ERROR_MESSAGES } from '../utils/constants';\nimport {\n parseFieldMetaData,\n getAttributeSchemaFromPath,\n PreviewFieldError,\n} from '../utils/fieldUtils';\n\n/* -------------------------------------------------------------------------------------------------\n * Context utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * No need for actual data in the context. It's just to let children check if they're rendered\n * inside of a preview InputPopover without relying on prop drilling.\n */\ninterface InputPopoverContextValue {}\n\nconst [InputPopoverProvider, useInputPopoverContext] =\n createContext<InputPopoverContextValue>('InputPopover');\n\nfunction useHasInputPopoverParent() {\n const context = useInputPopoverContext('useHasInputPopoverParent', () => true, false);\n\n // useContext will return undefined if the called is not wrapped in the provider\n return context !== undefined;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * InputPopover\n * -----------------------------------------------------------------------------------------------*/\n\nconst InputPopover = ({ documentResponse }: { documentResponse: ReturnType<UseDocument> }) => {\n const iframeRef = usePreviewContext('InputPopover', (state) => state.iframeRef);\n const popoverField = usePreviewContext('InputPopover', (state) => state.popoverField);\n const setPopoverField = usePreviewContext('InputPopover', (state) => state.setPopoverField);\n const document = usePreviewContext('InputPopover', (state) => state.document);\n const schema = usePreviewContext('InputPopover', (state) => state.schema);\n const components = usePreviewContext('InputPopover', (state) => state.components);\n\n const { toggleNotification } = useNotification();\n const { formatMessage } = useIntl();\n\n React.useEffect(() => {\n /**\n * We receive window events sent from the user's preview via the injected script.\n * We listen to the ones here that target a specific field.\n */\n const handleMessage = (event: MessageEvent) => {\n // Only listen to events from the preview iframe\n if (iframeRef.current) {\n const previewOrigin = new URL(iframeRef.current?.src).origin;\n if (event.origin !== previewOrigin) {\n return;\n }\n }\n\n if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_FOCUS_INTENT) {\n const fieldMetaData = parseFieldMetaData(event.data.payload.path);\n\n if (!fieldMetaData) {\n const { type, message } = PREVIEW_ERROR_MESSAGES.INCOMPLETE_STRAPI_SOURCE;\n toggleNotification({ type, message: formatMessage(message) });\n return;\n }\n\n /**\n * Ignore (for now) content that comes from separate API requests than the one for the\n * current document. This doesn't do anything about fields that may come from relations to\n * the current document however.\n */\n if (fieldMetaData.documentId !== document.documentId) {\n const { type, message } = PREVIEW_ERROR_MESSAGES.DIFFERENT_DOCUMENT;\n toggleNotification({ type, message: formatMessage(message) });\n return;\n }\n\n try {\n const attribute = getAttributeSchemaFromPath({\n path: fieldMetaData.path,\n components,\n schema,\n document,\n });\n\n // We're able to handle the field, set it in context so the popover can pick it up\n setPopoverField({ ...fieldMetaData, position: event.data.payload.position, attribute });\n } catch (error) {\n if (error instanceof PreviewFieldError) {\n const { type, message } = PREVIEW_ERROR_MESSAGES[error.messageKey];\n toggleNotification({ type, message: formatMessage(message) });\n } else if (error instanceof Error) {\n toggleNotification({ type: 'danger', message: error.message });\n }\n }\n }\n\n if (event.data?.type === INTERNAL_EVENTS.STRAPI_FIELD_SINGLE_CLICK_HINT) {\n toggleNotification({\n type: 'info',\n message: formatMessage({\n id: 'content-manager.preview.info.single-click-hint',\n defaultMessage: 'Double click to edit',\n }),\n });\n }\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [components, document, iframeRef, schema, setPopoverField, toggleNotification, formatMessage]);\n if (!popoverField || !iframeRef.current) {\n return null;\n }\n\n const iframeRect = iframeRef.current.getBoundingClientRect();\n\n return (\n <>\n {/**\n * Overlay an empty div on top of the iframe while the popover is open so it can\n * intercept clicks. Without it, we wouldn't be able to close the popover by clicking outside,\n * because the click would be detected by the iframe window, not by the admin.\n **/}\n <Box\n position={'fixed'}\n top={iframeRect.top + 'px'}\n left={iframeRect.left + 'px'}\n width={iframeRect.width + 'px'}\n height={iframeRect.height + 'px'}\n zIndex={4}\n onClick={() => iframeRef.current?.focus()}\n />\n <InputPopoverProvider>\n <Popover.Root open={true} onOpenChange={(open) => !open && setPopoverField(null)}>\n <Popover.Trigger>\n <Box\n position=\"fixed\"\n width={popoverField.position.width + 'px'}\n height={popoverField.position.height + 'px'}\n top={0}\n left={0}\n transform={`translate(${iframeRect.left + popoverField.position.left}px, ${iframeRect.top + popoverField.position.top}px)`}\n />\n </Popover.Trigger>\n <Popover.Content sideOffset={4}>\n <Box padding={4} width=\"400px\">\n {/* @ts-expect-error the types of `attribute` clash for some reason */}\n <InputRenderer\n document={documentResponse}\n attribute={popoverField.attribute}\n // TODO: retrieve the proper label from the layout\n label={popoverField.path}\n name={popoverField.path}\n type={popoverField.attribute.type}\n visible={true}\n />\n </Box>\n </Popover.Content>\n </Popover.Root>\n </InputPopoverProvider>\n </>\n );\n};\n\nexport { InputPopover, useHasInputPopoverParent };\n"],"names":["InputPopoverProvider","useInputPopoverContext","createContext","useHasInputPopoverParent","context","undefined","InputPopover","documentResponse","iframeRef","usePreviewContext","state","popoverField","setPopoverField","document","schema","components","toggleNotification","useNotification","formatMessage","useIntl","React","useEffect","handleMessage","event","current","previewOrigin","URL","src","origin","data","type","INTERNAL_EVENTS","STRAPI_FIELD_FOCUS_INTENT","fieldMetaData","parseFieldMetaData","payload","path","message","PREVIEW_ERROR_MESSAGES","INCOMPLETE_STRAPI_SOURCE","documentId","DIFFERENT_DOCUMENT","attribute","getAttributeSchemaFromPath","position","error","PreviewFieldError","messageKey","Error","STRAPI_FIELD_SINGLE_CLICK_HINT","id","defaultMessage","window","addEventListener","removeEventListener","iframeRect","getBoundingClientRect","_jsxs","_Fragment","_jsx","Box","top","left","width","height","zIndex","onClick","focus","Popover","Root","open","onOpenChange","Trigger","transform","Content","sideOffset","padding","InputRenderer","label","name","visible"],"mappings":"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"}
|
@@ -16,7 +16,41 @@ const scriptResponse = previewScript.previewScript(false);
|
|
16
16
|
STRAPI_UPDATE: 'strapiUpdate',
|
17
17
|
STRAPI_SCRIPT: 'strapiScript'
|
18
18
|
};
|
19
|
+
/**
|
20
|
+
* Error messages for preview field operations.
|
21
|
+
* This information is used to trigger notifications.
|
22
|
+
*/ const PREVIEW_ERROR_MESSAGES = {
|
23
|
+
INVALID_FIELD_PATH: {
|
24
|
+
type: 'danger',
|
25
|
+
message: {
|
26
|
+
id: 'content-manager.preview.error.invalid-field-path',
|
27
|
+
defaultMessage: 'Could not locate this field in the current document'
|
28
|
+
}
|
29
|
+
},
|
30
|
+
RELATIONS_NOT_HANDLED: {
|
31
|
+
type: 'info',
|
32
|
+
message: {
|
33
|
+
id: 'content-manager.preview.error.relations-not-handled',
|
34
|
+
defaultMessage: 'Inline editing for relations is not currently supported.'
|
35
|
+
}
|
36
|
+
},
|
37
|
+
INCOMPLETE_STRAPI_SOURCE: {
|
38
|
+
type: 'danger',
|
39
|
+
message: {
|
40
|
+
id: 'content-manager.preview.error.incomplete-strapi-source',
|
41
|
+
defaultMessage: 'This field is missing some required preview information'
|
42
|
+
}
|
43
|
+
},
|
44
|
+
DIFFERENT_DOCUMENT: {
|
45
|
+
type: 'info',
|
46
|
+
message: {
|
47
|
+
id: 'content-manager.preview.error.different-document',
|
48
|
+
defaultMessage: 'This field comes from a different document'
|
49
|
+
}
|
50
|
+
}
|
51
|
+
};
|
19
52
|
|
20
53
|
exports.INTERNAL_EVENTS = INTERNAL_EVENTS;
|
54
|
+
exports.PREVIEW_ERROR_MESSAGES = PREVIEW_ERROR_MESSAGES;
|
21
55
|
exports.PUBLIC_EVENTS = PUBLIC_EVENTS;
|
22
56
|
//# sourceMappingURL=constants.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"constants.js","sources":["../../../../admin/src/preview/utils/constants.ts"],"sourcesContent":["import { previewScript } from './previewScript';\n\nconst scriptResponse = previewScript(false);\n\n/**\n * These events can be changed safely. They're used by the content manager admin on one side, and by\n * the preview script on the other. We own both ends, and they're not documented to users, so we can\n * do what we want with them.\n */\nexport const INTERNAL_EVENTS = scriptResponse!.INTERNAL_EVENTS;\n\n/**\n * These events are documented to users, and will be hardcoded in their frontends.\n * Changing any of these would be a breaking change.\n */\nexport const PUBLIC_EVENTS = {\n PREVIEW_READY: 'previewReady',\n STRAPI_UPDATE: 'strapiUpdate',\n STRAPI_SCRIPT: 'strapiScript',\n} as const;\n"],"names":["scriptResponse","previewScript","INTERNAL_EVENTS","PUBLIC_EVENTS","PREVIEW_READY","STRAPI_UPDATE","STRAPI_SCRIPT"],"mappings":";;;;
|
1
|
+
{"version":3,"file":"constants.js","sources":["../../../../admin/src/preview/utils/constants.ts"],"sourcesContent":["import { NotificationConfig } from '@strapi/admin/strapi-admin';\nimport { MessageDescriptor } from 'react-intl';\n\nimport { previewScript } from './previewScript';\n\nconst scriptResponse = previewScript(false);\n\n/**\n * These events can be changed safely. They're used by the content manager admin on one side, and by\n * the preview script on the other. We own both ends, and they're not documented to users, so we can\n * do what we want with them.\n */\nexport const INTERNAL_EVENTS = scriptResponse!.INTERNAL_EVENTS;\n\n/**\n * These events are documented to users, and will be hardcoded in their frontends.\n * Changing any of these would be a breaking change.\n */\nexport const PUBLIC_EVENTS = {\n PREVIEW_READY: 'previewReady',\n STRAPI_UPDATE: 'strapiUpdate',\n STRAPI_SCRIPT: 'strapiScript',\n} as const;\n\n/**\n * Error messages for preview field operations.\n * This information is used to trigger notifications.\n */\nexport const PREVIEW_ERROR_MESSAGES = {\n INVALID_FIELD_PATH: {\n type: 'danger',\n message: {\n id: 'content-manager.preview.error.invalid-field-path',\n defaultMessage: 'Could not locate this field in the current document',\n },\n },\n RELATIONS_NOT_HANDLED: {\n type: 'info',\n message: {\n id: 'content-manager.preview.error.relations-not-handled',\n defaultMessage: 'Inline editing for relations is not currently supported.',\n },\n },\n INCOMPLETE_STRAPI_SOURCE: {\n type: 'danger',\n message: {\n id: 'content-manager.preview.error.incomplete-strapi-source',\n defaultMessage: 'This field is missing some required preview information',\n },\n },\n DIFFERENT_DOCUMENT: {\n type: 'info',\n message: {\n id: 'content-manager.preview.error.different-document',\n defaultMessage: 'This field comes from a different document',\n },\n },\n} as const satisfies Record<\n string,\n { message: MessageDescriptor; type: NonNullable<NotificationConfig['type']> }\n>;\n"],"names":["scriptResponse","previewScript","INTERNAL_EVENTS","PUBLIC_EVENTS","PREVIEW_READY","STRAPI_UPDATE","STRAPI_SCRIPT","PREVIEW_ERROR_MESSAGES","INVALID_FIELD_PATH","type","message","id","defaultMessage","RELATIONS_NOT_HANDLED","INCOMPLETE_STRAPI_SOURCE","DIFFERENT_DOCUMENT"],"mappings":";;;;AAKA,MAAMA,iBAAiBC,2BAAc,CAAA,KAAA,CAAA;AAErC;;;;AAIC,IACM,MAAMC,eAAkBF,GAAAA,cAAAA,CAAgBE;AAE/C;;;UAIaC,aAAgB,GAAA;IAC3BC,aAAe,EAAA,cAAA;IACfC,aAAe,EAAA,cAAA;IACfC,aAAe,EAAA;AACjB;AAEA;;;UAIaC,sBAAyB,GAAA;IACpCC,kBAAoB,EAAA;QAClBC,IAAM,EAAA,QAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,kDAAA;YACJC,cAAgB,EAAA;AAClB;AACF,KAAA;IACAC,qBAAuB,EAAA;QACrBJ,IAAM,EAAA,MAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,qDAAA;YACJC,cAAgB,EAAA;AAClB;AACF,KAAA;IACAE,wBAA0B,EAAA;QACxBL,IAAM,EAAA,QAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,wDAAA;YACJC,cAAgB,EAAA;AAClB;AACF,KAAA;IACAG,kBAAoB,EAAA;QAClBN,IAAM,EAAA,MAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,kDAAA;YACJC,cAAgB,EAAA;AAClB;AACF;AACF;;;;;;"}
|
@@ -14,6 +14,39 @@ const scriptResponse = previewScript(false);
|
|
14
14
|
STRAPI_UPDATE: 'strapiUpdate',
|
15
15
|
STRAPI_SCRIPT: 'strapiScript'
|
16
16
|
};
|
17
|
+
/**
|
18
|
+
* Error messages for preview field operations.
|
19
|
+
* This information is used to trigger notifications.
|
20
|
+
*/ const PREVIEW_ERROR_MESSAGES = {
|
21
|
+
INVALID_FIELD_PATH: {
|
22
|
+
type: 'danger',
|
23
|
+
message: {
|
24
|
+
id: 'content-manager.preview.error.invalid-field-path',
|
25
|
+
defaultMessage: 'Could not locate this field in the current document'
|
26
|
+
}
|
27
|
+
},
|
28
|
+
RELATIONS_NOT_HANDLED: {
|
29
|
+
type: 'info',
|
30
|
+
message: {
|
31
|
+
id: 'content-manager.preview.error.relations-not-handled',
|
32
|
+
defaultMessage: 'Inline editing for relations is not currently supported.'
|
33
|
+
}
|
34
|
+
},
|
35
|
+
INCOMPLETE_STRAPI_SOURCE: {
|
36
|
+
type: 'danger',
|
37
|
+
message: {
|
38
|
+
id: 'content-manager.preview.error.incomplete-strapi-source',
|
39
|
+
defaultMessage: 'This field is missing some required preview information'
|
40
|
+
}
|
41
|
+
},
|
42
|
+
DIFFERENT_DOCUMENT: {
|
43
|
+
type: 'info',
|
44
|
+
message: {
|
45
|
+
id: 'content-manager.preview.error.different-document',
|
46
|
+
defaultMessage: 'This field comes from a different document'
|
47
|
+
}
|
48
|
+
}
|
49
|
+
};
|
17
50
|
|
18
|
-
export { INTERNAL_EVENTS, PUBLIC_EVENTS };
|
51
|
+
export { INTERNAL_EVENTS, PREVIEW_ERROR_MESSAGES, PUBLIC_EVENTS };
|
19
52
|
//# sourceMappingURL=constants.mjs.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"constants.mjs","sources":["../../../../admin/src/preview/utils/constants.ts"],"sourcesContent":["import { previewScript } from './previewScript';\n\nconst scriptResponse = previewScript(false);\n\n/**\n * These events can be changed safely. They're used by the content manager admin on one side, and by\n * the preview script on the other. We own both ends, and they're not documented to users, so we can\n * do what we want with them.\n */\nexport const INTERNAL_EVENTS = scriptResponse!.INTERNAL_EVENTS;\n\n/**\n * These events are documented to users, and will be hardcoded in their frontends.\n * Changing any of these would be a breaking change.\n */\nexport const PUBLIC_EVENTS = {\n PREVIEW_READY: 'previewReady',\n STRAPI_UPDATE: 'strapiUpdate',\n STRAPI_SCRIPT: 'strapiScript',\n} as const;\n"],"names":["scriptResponse","previewScript","INTERNAL_EVENTS","PUBLIC_EVENTS","PREVIEW_READY","STRAPI_UPDATE","STRAPI_SCRIPT"],"mappings":";;
|
1
|
+
{"version":3,"file":"constants.mjs","sources":["../../../../admin/src/preview/utils/constants.ts"],"sourcesContent":["import { NotificationConfig } from '@strapi/admin/strapi-admin';\nimport { MessageDescriptor } from 'react-intl';\n\nimport { previewScript } from './previewScript';\n\nconst scriptResponse = previewScript(false);\n\n/**\n * These events can be changed safely. They're used by the content manager admin on one side, and by\n * the preview script on the other. We own both ends, and they're not documented to users, so we can\n * do what we want with them.\n */\nexport const INTERNAL_EVENTS = scriptResponse!.INTERNAL_EVENTS;\n\n/**\n * These events are documented to users, and will be hardcoded in their frontends.\n * Changing any of these would be a breaking change.\n */\nexport const PUBLIC_EVENTS = {\n PREVIEW_READY: 'previewReady',\n STRAPI_UPDATE: 'strapiUpdate',\n STRAPI_SCRIPT: 'strapiScript',\n} as const;\n\n/**\n * Error messages for preview field operations.\n * This information is used to trigger notifications.\n */\nexport const PREVIEW_ERROR_MESSAGES = {\n INVALID_FIELD_PATH: {\n type: 'danger',\n message: {\n id: 'content-manager.preview.error.invalid-field-path',\n defaultMessage: 'Could not locate this field in the current document',\n },\n },\n RELATIONS_NOT_HANDLED: {\n type: 'info',\n message: {\n id: 'content-manager.preview.error.relations-not-handled',\n defaultMessage: 'Inline editing for relations is not currently supported.',\n },\n },\n INCOMPLETE_STRAPI_SOURCE: {\n type: 'danger',\n message: {\n id: 'content-manager.preview.error.incomplete-strapi-source',\n defaultMessage: 'This field is missing some required preview information',\n },\n },\n DIFFERENT_DOCUMENT: {\n type: 'info',\n message: {\n id: 'content-manager.preview.error.different-document',\n defaultMessage: 'This field comes from a different document',\n },\n },\n} as const satisfies Record<\n string,\n { message: MessageDescriptor; type: NonNullable<NotificationConfig['type']> }\n>;\n"],"names":["scriptResponse","previewScript","INTERNAL_EVENTS","PUBLIC_EVENTS","PREVIEW_READY","STRAPI_UPDATE","STRAPI_SCRIPT","PREVIEW_ERROR_MESSAGES","INVALID_FIELD_PATH","type","message","id","defaultMessage","RELATIONS_NOT_HANDLED","INCOMPLETE_STRAPI_SOURCE","DIFFERENT_DOCUMENT"],"mappings":";;AAKA,MAAMA,iBAAiBC,aAAc,CAAA,KAAA,CAAA;AAErC;;;;AAIC,IACM,MAAMC,eAAkBF,GAAAA,cAAAA,CAAgBE;AAE/C;;;UAIaC,aAAgB,GAAA;IAC3BC,aAAe,EAAA,cAAA;IACfC,aAAe,EAAA,cAAA;IACfC,aAAe,EAAA;AACjB;AAEA;;;UAIaC,sBAAyB,GAAA;IACpCC,kBAAoB,EAAA;QAClBC,IAAM,EAAA,QAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,kDAAA;YACJC,cAAgB,EAAA;AAClB;AACF,KAAA;IACAC,qBAAuB,EAAA;QACrBJ,IAAM,EAAA,MAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,qDAAA;YACJC,cAAgB,EAAA;AAClB;AACF,KAAA;IACAE,wBAA0B,EAAA;QACxBL,IAAM,EAAA,QAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,wDAAA;YACJC,cAAgB,EAAA;AAClB;AACF,KAAA;IACAG,kBAAoB,EAAA;QAClBN,IAAM,EAAA,MAAA;QACNC,OAAS,EAAA;YACPC,EAAI,EAAA,kDAAA;YACJC,cAAgB,EAAA;AAClB;AACF;AACF;;;;"}
|
@@ -1,5 +1,13 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
|
+
// Generic error class for preview field operations
|
4
|
+
class PreviewFieldError extends Error {
|
5
|
+
constructor(messageKey){
|
6
|
+
super(messageKey);
|
7
|
+
this.name = 'PreviewFieldError';
|
8
|
+
this.messageKey = messageKey;
|
9
|
+
}
|
10
|
+
}
|
3
11
|
// Helper function to parse path with array indices and return clean attribute names
|
4
12
|
const parsePathWithIndices = (path)=>{
|
5
13
|
// Split by dots, then parse array indices from each part. For example:
|
@@ -40,17 +48,17 @@ function getAttributeSchemaFromPath({ path, schema, components, document }) {
|
|
40
48
|
// Get the data and schema for the current path
|
41
49
|
const currentAttribute = currentAttributes[currentPart.name];
|
42
50
|
if (!currentAttribute) {
|
43
|
-
throw new
|
51
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
44
52
|
}
|
45
53
|
if (currentAttribute.type === 'relation') {
|
46
|
-
throw new
|
54
|
+
throw new PreviewFieldError('RELATIONS_NOT_HANDLED');
|
47
55
|
}
|
48
56
|
if (currentAttribute.type === 'component') {
|
49
57
|
const componentAttributes = components[currentAttribute.component].attributes;
|
50
58
|
if (currentAttribute.repeatable) {
|
51
59
|
// We must have the index, otherwise we don't know what data to use
|
52
60
|
if (currentPart.index === undefined) {
|
53
|
-
throw new
|
61
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
54
62
|
}
|
55
63
|
return visitor(remainingParts, componentAttributes, currentData[currentPart.name][currentPart.index]);
|
56
64
|
}
|
@@ -60,7 +68,7 @@ function getAttributeSchemaFromPath({ path, schema, components, document }) {
|
|
60
68
|
if (currentAttribute.type === 'dynamiczone') {
|
61
69
|
// We must have the index, otherwise we don't know what component we're dealing with
|
62
70
|
if (currentPart.index === undefined) {
|
63
|
-
throw new
|
71
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
64
72
|
}
|
65
73
|
const componentData = currentData[currentPart.name][currentPart.index];
|
66
74
|
const componentAttributes = components[componentData.__component].attributes;
|
@@ -92,6 +100,7 @@ function parseFieldMetaData(strapiSource) {
|
|
92
100
|
};
|
93
101
|
}
|
94
102
|
|
103
|
+
exports.PreviewFieldError = PreviewFieldError;
|
95
104
|
exports.getAttributeSchemaFromPath = getAttributeSchemaFromPath;
|
96
105
|
exports.parseFieldMetaData = parseFieldMetaData;
|
97
106
|
exports.parsePathWithIndices = parsePathWithIndices;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"fieldUtils.js","sources":["../../../../admin/src/preview/utils/fieldUtils.ts"],"sourcesContent":["import { type FieldContentSourceMap } from '@strapi/admin/strapi-admin';\n\nimport type { PreviewContextValue } from '../pages/Preview';\nimport type { Modules, Schema, Struct, UID } from '@strapi/types';\n\ntype PathPart = { name: string; index?: number };\n\n// Helper function to parse path with array indices and return clean attribute names\nexport const parsePathWithIndices = (path: string): PathPart[] => {\n // Split by dots, then parse array indices from each part. For example:\n // input \"components.4.field.relations.2.name\"\n // output [{name: \"components\", index: 4}, {name: \"field\"}, {name: \"relations\", index: 2}, {name: \"name\"}]\n return path\n .split('.')\n .map((part) => {\n const numericIndex = parseInt(part, 10);\n if (!isNaN(numericIndex) && part === numericIndex.toString()) {\n // This part is a pure numeric index, return it as an index for the previous part\n return { name: '', index: numericIndex };\n }\n return { name: part };\n })\n .reduce((acc: PathPart[], part) => {\n if (part.name === '' && part.index !== undefined) {\n // This is an index, attach it to the previous part\n if (acc.length > 0) {\n acc[acc.length - 1].index = part.index;\n }\n } else {\n acc.push(part);\n }\n return acc;\n }, []);\n};\n\nexport function getAttributeSchemaFromPath({\n path,\n schema,\n components,\n document,\n}: {\n path: string;\n schema: PreviewContextValue['schema'] | PreviewContextValue['components'][string];\n components: PreviewContextValue['components'];\n document: Modules.Documents.AnyDocument;\n}): Schema.Attribute.AnyAttribute {\n /**\n * Create the function that will be recursively called.\n * We don't do recursion on getAttributeSchemaFromPath itself because:\n * - it takes a path string, not the parsed array that's better for recursion\n * - even when several levels deep, we still need access to the root schema and components\n */\n const visitor = (\n currentPathParts: PathPart[],\n currentAttributes: Schema.Attributes,\n currentData: any\n ): Schema.Attribute.AnyAttribute => {\n const [currentPart, ...remainingParts] = currentPathParts;\n\n // Get the data and schema for the current path\n const currentAttribute = currentAttributes[currentPart.name];\n\n if (!currentAttribute) {\n throw new
|
1
|
+
{"version":3,"file":"fieldUtils.js","sources":["../../../../admin/src/preview/utils/fieldUtils.ts"],"sourcesContent":["import { type FieldContentSourceMap } from '@strapi/admin/strapi-admin';\n\nimport type { PREVIEW_ERROR_MESSAGES } from './constants';\nimport type { PreviewContextValue } from '../pages/Preview';\nimport type { Modules, Schema, Struct, UID } from '@strapi/types';\n\ntype PreviewErrorMessage = keyof typeof PREVIEW_ERROR_MESSAGES;\n\n// Generic error class for preview field operations\nexport class PreviewFieldError extends Error {\n public readonly messageKey: PreviewErrorMessage;\n\n constructor(messageKey: PreviewErrorMessage) {\n super(messageKey);\n this.name = 'PreviewFieldError';\n this.messageKey = messageKey;\n }\n}\n\ntype PathPart = { name: string; index?: number };\n\n// Helper function to parse path with array indices and return clean attribute names\nexport const parsePathWithIndices = (path: string): PathPart[] => {\n // Split by dots, then parse array indices from each part. For example:\n // input \"components.4.field.relations.2.name\"\n // output [{name: \"components\", index: 4}, {name: \"field\"}, {name: \"relations\", index: 2}, {name: \"name\"}]\n return path\n .split('.')\n .map((part) => {\n const numericIndex = parseInt(part, 10);\n if (!isNaN(numericIndex) && part === numericIndex.toString()) {\n // This part is a pure numeric index, return it as an index for the previous part\n return { name: '', index: numericIndex };\n }\n return { name: part };\n })\n .reduce((acc: PathPart[], part) => {\n if (part.name === '' && part.index !== undefined) {\n // This is an index, attach it to the previous part\n if (acc.length > 0) {\n acc[acc.length - 1].index = part.index;\n }\n } else {\n acc.push(part);\n }\n return acc;\n }, []);\n};\n\nexport function getAttributeSchemaFromPath({\n path,\n schema,\n components,\n document,\n}: {\n path: string;\n schema: PreviewContextValue['schema'] | PreviewContextValue['components'][string];\n components: PreviewContextValue['components'];\n document: Modules.Documents.AnyDocument;\n}): Schema.Attribute.AnyAttribute {\n /**\n * Create the function that will be recursively called.\n * We don't do recursion on getAttributeSchemaFromPath itself because:\n * - it takes a path string, not the parsed array that's better for recursion\n * - even when several levels deep, we still need access to the root schema and components\n */\n const visitor = (\n currentPathParts: PathPart[],\n currentAttributes: Schema.Attributes,\n currentData: any\n ): Schema.Attribute.AnyAttribute => {\n const [currentPart, ...remainingParts] = currentPathParts;\n\n // Get the data and schema for the current path\n const currentAttribute = currentAttributes[currentPart.name];\n\n if (!currentAttribute) {\n throw new PreviewFieldError('INVALID_FIELD_PATH');\n }\n\n if (currentAttribute.type === 'relation') {\n throw new PreviewFieldError('RELATIONS_NOT_HANDLED');\n }\n\n if (currentAttribute.type === 'component') {\n const componentAttributes = components[currentAttribute.component].attributes;\n if (currentAttribute.repeatable) {\n // We must have the index, otherwise we don't know what data to use\n if (currentPart.index === undefined) {\n throw new PreviewFieldError('INVALID_FIELD_PATH');\n }\n return visitor(\n remainingParts,\n componentAttributes,\n currentData[currentPart.name][currentPart.index]\n );\n }\n\n // Non repeatable component\n return visitor(remainingParts, componentAttributes, currentData[currentPart.name]);\n }\n\n if (currentAttribute.type === 'dynamiczone') {\n // We must have the index, otherwise we don't know what component we're dealing with\n if (currentPart.index === undefined) {\n throw new PreviewFieldError('INVALID_FIELD_PATH');\n }\n\n const componentData = currentData[currentPart.name][currentPart.index];\n const componentAttributes = components[componentData.__component].attributes;\n return visitor(remainingParts, componentAttributes, componentData);\n }\n\n // Plain regular field. It ends the recursion\n return currentAttributes[currentPart.name];\n };\n\n return visitor(parsePathWithIndices(path), schema.attributes, document);\n}\n\nexport function parseFieldMetaData(strapiSource: string): FieldContentSourceMap | null {\n const searchParams = new URLSearchParams(strapiSource);\n const path = searchParams.get('path');\n const type = searchParams.get('type');\n const documentId = searchParams.get('documentId');\n const locale = searchParams.get('locale');\n const model = searchParams.get('model');\n const kind = searchParams.get('kind');\n\n if (!path || !type || !documentId || !model) {\n return null;\n }\n\n return {\n path,\n type: type as Schema.Attribute.AnyAttribute['type'],\n documentId,\n locale: locale ?? null,\n model: model as UID.Schema | undefined,\n kind: kind ? (kind as Struct.ContentTypeKind) : undefined,\n };\n}\n"],"names":["PreviewFieldError","Error","constructor","messageKey","name","parsePathWithIndices","path","split","map","part","numericIndex","parseInt","isNaN","toString","index","reduce","acc","undefined","length","push","getAttributeSchemaFromPath","schema","components","document","visitor","currentPathParts","currentAttributes","currentData","currentPart","remainingParts","currentAttribute","type","componentAttributes","component","attributes","repeatable","componentData","__component","parseFieldMetaData","strapiSource","searchParams","URLSearchParams","get","documentId","locale","model","kind"],"mappings":"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"}
|
@@ -1,3 +1,11 @@
|
|
1
|
+
// Generic error class for preview field operations
|
2
|
+
class PreviewFieldError extends Error {
|
3
|
+
constructor(messageKey){
|
4
|
+
super(messageKey);
|
5
|
+
this.name = 'PreviewFieldError';
|
6
|
+
this.messageKey = messageKey;
|
7
|
+
}
|
8
|
+
}
|
1
9
|
// Helper function to parse path with array indices and return clean attribute names
|
2
10
|
const parsePathWithIndices = (path)=>{
|
3
11
|
// Split by dots, then parse array indices from each part. For example:
|
@@ -38,17 +46,17 @@ function getAttributeSchemaFromPath({ path, schema, components, document }) {
|
|
38
46
|
// Get the data and schema for the current path
|
39
47
|
const currentAttribute = currentAttributes[currentPart.name];
|
40
48
|
if (!currentAttribute) {
|
41
|
-
throw new
|
49
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
42
50
|
}
|
43
51
|
if (currentAttribute.type === 'relation') {
|
44
|
-
throw new
|
52
|
+
throw new PreviewFieldError('RELATIONS_NOT_HANDLED');
|
45
53
|
}
|
46
54
|
if (currentAttribute.type === 'component') {
|
47
55
|
const componentAttributes = components[currentAttribute.component].attributes;
|
48
56
|
if (currentAttribute.repeatable) {
|
49
57
|
// We must have the index, otherwise we don't know what data to use
|
50
58
|
if (currentPart.index === undefined) {
|
51
|
-
throw new
|
59
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
52
60
|
}
|
53
61
|
return visitor(remainingParts, componentAttributes, currentData[currentPart.name][currentPart.index]);
|
54
62
|
}
|
@@ -58,7 +66,7 @@ function getAttributeSchemaFromPath({ path, schema, components, document }) {
|
|
58
66
|
if (currentAttribute.type === 'dynamiczone') {
|
59
67
|
// We must have the index, otherwise we don't know what component we're dealing with
|
60
68
|
if (currentPart.index === undefined) {
|
61
|
-
throw new
|
69
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
62
70
|
}
|
63
71
|
const componentData = currentData[currentPart.name][currentPart.index];
|
64
72
|
const componentAttributes = components[componentData.__component].attributes;
|
@@ -90,5 +98,5 @@ function parseFieldMetaData(strapiSource) {
|
|
90
98
|
};
|
91
99
|
}
|
92
100
|
|
93
|
-
export { getAttributeSchemaFromPath, parseFieldMetaData, parsePathWithIndices };
|
101
|
+
export { PreviewFieldError, getAttributeSchemaFromPath, parseFieldMetaData, parsePathWithIndices };
|
94
102
|
//# sourceMappingURL=fieldUtils.mjs.map
|