@strapi/content-manager 0.0.0-next.965570e6e12a33ad098ddb3174a9750fddb03be2 → 0.0.0-next.96a532b33a712f19b498ed0a82a84752247cd899
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/EditFieldForm.js +1 -1
- package/dist/admin/components/ConfigurationForm/EditFieldForm.js.map +1 -1
- package/dist/admin/components/ConfigurationForm/EditFieldForm.mjs +1 -1
- package/dist/admin/components/ConfigurationForm/EditFieldForm.mjs.map +1 -1
- package/dist/admin/components/ConfigurationForm/Fields.js +4 -1
- package/dist/admin/components/ConfigurationForm/Fields.js.map +1 -1
- package/dist/admin/components/ConfigurationForm/Fields.mjs +5 -2
- package/dist/admin/components/ConfigurationForm/Fields.mjs.map +1 -1
- package/dist/admin/components/ConfigurationForm/Form.js +1 -1
- package/dist/admin/components/ConfigurationForm/Form.js.map +1 -1
- package/dist/admin/components/ConfigurationForm/Form.mjs +3 -3
- package/dist/admin/components/ConfigurationForm/Form.mjs.map +1 -1
- package/dist/admin/components/DragPreviews/CardDragPreview.js +3 -1
- package/dist/admin/components/DragPreviews/CardDragPreview.js.map +1 -1
- package/dist/admin/components/DragPreviews/CardDragPreview.mjs +3 -1
- package/dist/admin/components/DragPreviews/CardDragPreview.mjs.map +1 -1
- package/dist/admin/components/DragPreviews/ComponentDragPreview.js +3 -1
- package/dist/admin/components/DragPreviews/ComponentDragPreview.js.map +1 -1
- package/dist/admin/components/DragPreviews/ComponentDragPreview.mjs +3 -1
- package/dist/admin/components/DragPreviews/ComponentDragPreview.mjs.map +1 -1
- package/dist/admin/components/DragPreviews/RelationDragPreview.js +3 -1
- package/dist/admin/components/DragPreviews/RelationDragPreview.js.map +1 -1
- package/dist/admin/components/DragPreviews/RelationDragPreview.mjs +3 -1
- package/dist/admin/components/DragPreviews/RelationDragPreview.mjs.map +1 -1
- package/dist/admin/components/LeftMenu.js +90 -48
- package/dist/admin/components/LeftMenu.js.map +1 -1
- package/dist/admin/components/LeftMenu.mjs +92 -50
- package/dist/admin/components/LeftMenu.mjs.map +1 -1
- package/dist/admin/components/Widgets.js +22 -6
- package/dist/admin/components/Widgets.js.map +1 -1
- package/dist/admin/components/Widgets.mjs +22 -6
- package/dist/admin/components/Widgets.mjs.map +1 -1
- package/dist/admin/history/components/VersionHeader.js +1 -0
- package/dist/admin/history/components/VersionHeader.js.map +1 -1
- package/dist/admin/history/components/VersionHeader.mjs +1 -0
- package/dist/admin/history/components/VersionHeader.mjs.map +1 -1
- package/dist/admin/history/components/VersionsList.js +1 -1
- package/dist/admin/history/components/VersionsList.js.map +1 -1
- package/dist/admin/history/components/VersionsList.mjs +1 -1
- package/dist/admin/history/components/VersionsList.mjs.map +1 -1
- package/dist/admin/history/pages/History.js +7 -7
- package/dist/admin/history/pages/History.js.map +1 -1
- package/dist/admin/history/pages/History.mjs +7 -7
- package/dist/admin/history/pages/History.mjs.map +1 -1
- package/dist/admin/layout.js +27 -6
- package/dist/admin/layout.js.map +1 -1
- package/dist/admin/layout.mjs +28 -7
- package/dist/admin/layout.mjs.map +1 -1
- package/dist/admin/pages/EditView/EditViewPage.js +7 -3
- package/dist/admin/pages/EditView/EditViewPage.js.map +1 -1
- package/dist/admin/pages/EditView/EditViewPage.mjs +8 -4
- package/dist/admin/pages/EditView/EditViewPage.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/DocumentActions.js +6 -0
- package/dist/admin/pages/EditView/components/DocumentActions.js.map +1 -1
- package/dist/admin/pages/EditView/components/DocumentActions.mjs +6 -0
- package/dist/admin/pages/EditView/components/DocumentActions.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.js +66 -2
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.mjs +66 -2
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.js +2 -2
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.mjs +2 -2
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/utils/links.js +7 -3
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/utils/links.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/utils/links.mjs +7 -3
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/utils/links.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js +2 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs +3 -2
- package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js +1 -2
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs +1 -2
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js +4 -3
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs +5 -4
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js +2 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs +3 -2
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.js +2 -2
- package/dist/admin/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.mjs +2 -2
- package/dist/admin/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormLayout.js +13 -8
- package/dist/admin/pages/EditView/components/FormLayout.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormLayout.mjs +14 -8
- package/dist/admin/pages/EditView/components/FormLayout.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/Header.js +85 -25
- package/dist/admin/pages/EditView/components/Header.js.map +1 -1
- package/dist/admin/pages/EditView/components/Header.mjs +87 -27
- package/dist/admin/pages/EditView/components/Header.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/InputRenderer.js +6 -3
- package/dist/admin/pages/EditView/components/InputRenderer.js.map +1 -1
- package/dist/admin/pages/EditView/components/InputRenderer.mjs +6 -3
- package/dist/admin/pages/EditView/components/InputRenderer.mjs.map +1 -1
- package/dist/admin/pages/EditView/utils/data.js +1 -1
- package/dist/admin/pages/EditView/utils/data.js.map +1 -1
- package/dist/admin/pages/EditView/utils/data.mjs +1 -1
- package/dist/admin/pages/EditView/utils/data.mjs.map +1 -1
- package/dist/admin/pages/ListConfiguration/ListConfigurationPage.js +1 -0
- package/dist/admin/pages/ListConfiguration/ListConfigurationPage.js.map +1 -1
- package/dist/admin/pages/ListConfiguration/ListConfigurationPage.mjs +1 -0
- package/dist/admin/pages/ListConfiguration/ListConfigurationPage.mjs.map +1 -1
- package/dist/admin/pages/ListView/components/Filters.js +4 -1
- package/dist/admin/pages/ListView/components/Filters.js.map +1 -1
- package/dist/admin/pages/ListView/components/Filters.mjs +4 -1
- package/dist/admin/pages/ListView/components/Filters.mjs.map +1 -1
- package/dist/admin/pages/ListView/components/ViewSettingsMenu.js +5 -2
- package/dist/admin/pages/ListView/components/ViewSettingsMenu.js.map +1 -1
- package/dist/admin/pages/ListView/components/ViewSettingsMenu.mjs +5 -2
- package/dist/admin/pages/ListView/components/ViewSettingsMenu.mjs.map +1 -1
- package/dist/admin/preview/components/InputPopover.js +130 -14
- package/dist/admin/preview/components/InputPopover.js.map +1 -1
- package/dist/admin/preview/components/InputPopover.mjs +112 -15
- package/dist/admin/preview/components/InputPopover.mjs.map +1 -1
- package/dist/admin/preview/components/PreviewHeader.js +0 -1
- package/dist/admin/preview/components/PreviewHeader.js.map +1 -1
- package/dist/admin/preview/components/PreviewHeader.mjs +0 -1
- package/dist/admin/preview/components/PreviewHeader.mjs.map +1 -1
- package/dist/admin/preview/hooks/usePreviewInputManager.js +18 -8
- package/dist/admin/preview/hooks/usePreviewInputManager.js.map +1 -1
- package/dist/admin/preview/hooks/usePreviewInputManager.mjs +18 -8
- package/dist/admin/preview/hooks/usePreviewInputManager.mjs.map +1 -1
- package/dist/admin/preview/pages/Preview.js +12 -10
- package/dist/admin/preview/pages/Preview.js.map +1 -1
- package/dist/admin/preview/pages/Preview.mjs +14 -12
- package/dist/admin/preview/pages/Preview.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 +107 -0
- package/dist/admin/preview/utils/fieldUtils.js.map +1 -0
- package/dist/admin/preview/utils/fieldUtils.mjs +102 -0
- package/dist/admin/preview/utils/fieldUtils.mjs.map +1 -0
- package/dist/admin/preview/utils/previewScript.js +134 -50
- package/dist/admin/preview/utils/previewScript.js.map +1 -1
- package/dist/admin/preview/utils/previewScript.mjs +134 -50
- package/dist/admin/preview/utils/previewScript.mjs.map +1 -1
- package/dist/admin/services/documents.js +9 -1
- package/dist/admin/services/documents.js.map +1 -1
- package/dist/admin/services/documents.mjs +9 -1
- package/dist/admin/services/documents.mjs.map +1 -1
- package/dist/admin/src/components/LeftMenu.d.ts +3 -1
- package/dist/admin/src/components/Widgets.d.ts +6 -2
- package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/links.d.ts +2 -0
- package/dist/admin/src/pages/EditView/components/FormLayout.d.ts +0 -3
- package/dist/admin/src/pages/EditView/components/Header.d.ts +4 -0
- package/dist/admin/src/preview/components/InputPopover.d.ts +1 -1
- package/dist/admin/src/preview/hooks/usePreviewInputManager.d.ts +2 -1
- package/dist/admin/src/preview/pages/Preview.d.ts +6 -2
- package/dist/admin/src/preview/services/preview.d.ts +1 -1
- package/dist/admin/src/preview/utils/constants.d.ts +35 -0
- package/dist/admin/src/preview/utils/fieldUtils.d.ts +22 -0
- package/dist/admin/src/preview/utils/previewScript.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 +16 -16
- 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 +2 -2
- package/dist/admin/src/services/uid.d.ts +3 -3
- 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/dist/server/controllers/collection-types.js +22 -2
- package/dist/server/controllers/collection-types.js.map +1 -1
- package/dist/server/controllers/collection-types.mjs +22 -2
- package/dist/server/controllers/collection-types.mjs.map +1 -1
- package/dist/server/controllers/relations.js +6 -4
- package/dist/server/controllers/relations.js.map +1 -1
- package/dist/server/controllers/relations.mjs +6 -4
- package/dist/server/controllers/relations.mjs.map +1 -1
- package/dist/server/controllers/single-types.js +9 -0
- package/dist/server/controllers/single-types.js.map +1 -1
- package/dist/server/controllers/single-types.mjs +9 -0
- package/dist/server/controllers/single-types.mjs.map +1 -1
- package/dist/server/preview/services/preview-config.js +8 -33
- package/dist/server/preview/services/preview-config.js.map +1 -1
- package/dist/server/preview/services/preview-config.mjs +9 -34
- package/dist/server/preview/services/preview-config.mjs.map +1 -1
- package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
- package/dist/server/src/controllers/relations.d.ts.map +1 -1
- package/dist/server/src/controllers/single-types.d.ts.map +1 -1
- package/dist/server/src/preview/services/preview-config.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
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
|
+
}
|
|
11
|
+
// Helper function to parse path with array indices and return clean attribute names
|
|
12
|
+
const parsePathWithIndices = (path)=>{
|
|
13
|
+
// Split by dots, then parse array indices from each part. For example:
|
|
14
|
+
// input "components.4.field.relations.2.name"
|
|
15
|
+
// output [{name: "components", index: 4}, {name: "field"}, {name: "relations", index: 2}, {name: "name"}]
|
|
16
|
+
return path.split('.').map((part)=>{
|
|
17
|
+
const numericIndex = parseInt(part, 10);
|
|
18
|
+
if (!isNaN(numericIndex) && part === numericIndex.toString()) {
|
|
19
|
+
// This part is a pure numeric index, return it as an index for the previous part
|
|
20
|
+
return {
|
|
21
|
+
name: '',
|
|
22
|
+
index: numericIndex
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
name: part
|
|
27
|
+
};
|
|
28
|
+
}).reduce((acc, part)=>{
|
|
29
|
+
if (part.name === '' && part.index !== undefined) {
|
|
30
|
+
// This is an index, attach it to the previous part
|
|
31
|
+
if (acc.length > 0) {
|
|
32
|
+
acc[acc.length - 1].index = part.index;
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
acc.push(part);
|
|
36
|
+
}
|
|
37
|
+
return acc;
|
|
38
|
+
}, []);
|
|
39
|
+
};
|
|
40
|
+
function getAttributeSchemaFromPath({ path, schema, components, document }) {
|
|
41
|
+
/**
|
|
42
|
+
* Create the function that will be recursively called.
|
|
43
|
+
* We don't do recursion on getAttributeSchemaFromPath itself because:
|
|
44
|
+
* - it takes a path string, not the parsed array that's better for recursion
|
|
45
|
+
* - even when several levels deep, we still need access to the root schema and components
|
|
46
|
+
*/ const visitor = (currentPathParts, currentAttributes, currentData)=>{
|
|
47
|
+
const [currentPart, ...remainingParts] = currentPathParts;
|
|
48
|
+
// Get the data and schema for the current path
|
|
49
|
+
const currentAttribute = currentAttributes[currentPart.name];
|
|
50
|
+
if (!currentAttribute) {
|
|
51
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
|
52
|
+
}
|
|
53
|
+
if (currentAttribute.type === 'relation') {
|
|
54
|
+
throw new PreviewFieldError('RELATIONS_NOT_HANDLED');
|
|
55
|
+
}
|
|
56
|
+
if (currentAttribute.type === 'component') {
|
|
57
|
+
const componentAttributes = components[currentAttribute.component].attributes;
|
|
58
|
+
if (currentAttribute.repeatable) {
|
|
59
|
+
// We must have the index, otherwise we don't know what data to use
|
|
60
|
+
if (currentPart.index === undefined) {
|
|
61
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
|
62
|
+
}
|
|
63
|
+
return visitor(remainingParts, componentAttributes, currentData[currentPart.name][currentPart.index]);
|
|
64
|
+
}
|
|
65
|
+
// Non repeatable component
|
|
66
|
+
return visitor(remainingParts, componentAttributes, currentData[currentPart.name]);
|
|
67
|
+
}
|
|
68
|
+
if (currentAttribute.type === 'dynamiczone') {
|
|
69
|
+
// We must have the index, otherwise we don't know what component we're dealing with
|
|
70
|
+
if (currentPart.index === undefined) {
|
|
71
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
|
72
|
+
}
|
|
73
|
+
const componentData = currentData[currentPart.name][currentPart.index];
|
|
74
|
+
const componentAttributes = components[componentData.__component].attributes;
|
|
75
|
+
return visitor(remainingParts, componentAttributes, componentData);
|
|
76
|
+
}
|
|
77
|
+
// Plain regular field. It ends the recursion
|
|
78
|
+
return currentAttributes[currentPart.name];
|
|
79
|
+
};
|
|
80
|
+
return visitor(parsePathWithIndices(path), schema.attributes, document);
|
|
81
|
+
}
|
|
82
|
+
function parseFieldMetaData(strapiSource) {
|
|
83
|
+
const searchParams = new URLSearchParams(strapiSource);
|
|
84
|
+
const path = searchParams.get('path');
|
|
85
|
+
const type = searchParams.get('type');
|
|
86
|
+
const documentId = searchParams.get('documentId');
|
|
87
|
+
const locale = searchParams.get('locale');
|
|
88
|
+
const model = searchParams.get('model');
|
|
89
|
+
const kind = searchParams.get('kind');
|
|
90
|
+
if (!path || !type || !documentId || !model) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
path,
|
|
95
|
+
type: type,
|
|
96
|
+
documentId,
|
|
97
|
+
locale: locale ?? null,
|
|
98
|
+
model: model,
|
|
99
|
+
kind: kind ? kind : undefined
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
exports.PreviewFieldError = PreviewFieldError;
|
|
104
|
+
exports.getAttributeSchemaFromPath = getAttributeSchemaFromPath;
|
|
105
|
+
exports.parseFieldMetaData = parseFieldMetaData;
|
|
106
|
+
exports.parsePathWithIndices = parsePathWithIndices;
|
|
107
|
+
//# sourceMappingURL=fieldUtils.js.map
|
|
@@ -0,0 +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 { 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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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
}
|
|
9
|
+
// Helper function to parse path with array indices and return clean attribute names
|
|
10
|
+
const parsePathWithIndices = (path)=>{
|
|
11
|
+
// Split by dots, then parse array indices from each part. For example:
|
|
12
|
+
// input "components.4.field.relations.2.name"
|
|
13
|
+
// output [{name: "components", index: 4}, {name: "field"}, {name: "relations", index: 2}, {name: "name"}]
|
|
14
|
+
return path.split('.').map((part)=>{
|
|
15
|
+
const numericIndex = parseInt(part, 10);
|
|
16
|
+
if (!isNaN(numericIndex) && part === numericIndex.toString()) {
|
|
17
|
+
// This part is a pure numeric index, return it as an index for the previous part
|
|
18
|
+
return {
|
|
19
|
+
name: '',
|
|
20
|
+
index: numericIndex
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
name: part
|
|
25
|
+
};
|
|
26
|
+
}).reduce((acc, part)=>{
|
|
27
|
+
if (part.name === '' && part.index !== undefined) {
|
|
28
|
+
// This is an index, attach it to the previous part
|
|
29
|
+
if (acc.length > 0) {
|
|
30
|
+
acc[acc.length - 1].index = part.index;
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
acc.push(part);
|
|
34
|
+
}
|
|
35
|
+
return acc;
|
|
36
|
+
}, []);
|
|
37
|
+
};
|
|
38
|
+
function getAttributeSchemaFromPath({ path, schema, components, document }) {
|
|
39
|
+
/**
|
|
40
|
+
* Create the function that will be recursively called.
|
|
41
|
+
* We don't do recursion on getAttributeSchemaFromPath itself because:
|
|
42
|
+
* - it takes a path string, not the parsed array that's better for recursion
|
|
43
|
+
* - even when several levels deep, we still need access to the root schema and components
|
|
44
|
+
*/ const visitor = (currentPathParts, currentAttributes, currentData)=>{
|
|
45
|
+
const [currentPart, ...remainingParts] = currentPathParts;
|
|
46
|
+
// Get the data and schema for the current path
|
|
47
|
+
const currentAttribute = currentAttributes[currentPart.name];
|
|
48
|
+
if (!currentAttribute) {
|
|
49
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
|
50
|
+
}
|
|
51
|
+
if (currentAttribute.type === 'relation') {
|
|
52
|
+
throw new PreviewFieldError('RELATIONS_NOT_HANDLED');
|
|
53
|
+
}
|
|
54
|
+
if (currentAttribute.type === 'component') {
|
|
55
|
+
const componentAttributes = components[currentAttribute.component].attributes;
|
|
56
|
+
if (currentAttribute.repeatable) {
|
|
57
|
+
// We must have the index, otherwise we don't know what data to use
|
|
58
|
+
if (currentPart.index === undefined) {
|
|
59
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
|
60
|
+
}
|
|
61
|
+
return visitor(remainingParts, componentAttributes, currentData[currentPart.name][currentPart.index]);
|
|
62
|
+
}
|
|
63
|
+
// Non repeatable component
|
|
64
|
+
return visitor(remainingParts, componentAttributes, currentData[currentPart.name]);
|
|
65
|
+
}
|
|
66
|
+
if (currentAttribute.type === 'dynamiczone') {
|
|
67
|
+
// We must have the index, otherwise we don't know what component we're dealing with
|
|
68
|
+
if (currentPart.index === undefined) {
|
|
69
|
+
throw new PreviewFieldError('INVALID_FIELD_PATH');
|
|
70
|
+
}
|
|
71
|
+
const componentData = currentData[currentPart.name][currentPart.index];
|
|
72
|
+
const componentAttributes = components[componentData.__component].attributes;
|
|
73
|
+
return visitor(remainingParts, componentAttributes, componentData);
|
|
74
|
+
}
|
|
75
|
+
// Plain regular field. It ends the recursion
|
|
76
|
+
return currentAttributes[currentPart.name];
|
|
77
|
+
};
|
|
78
|
+
return visitor(parsePathWithIndices(path), schema.attributes, document);
|
|
79
|
+
}
|
|
80
|
+
function parseFieldMetaData(strapiSource) {
|
|
81
|
+
const searchParams = new URLSearchParams(strapiSource);
|
|
82
|
+
const path = searchParams.get('path');
|
|
83
|
+
const type = searchParams.get('type');
|
|
84
|
+
const documentId = searchParams.get('documentId');
|
|
85
|
+
const locale = searchParams.get('locale');
|
|
86
|
+
const model = searchParams.get('model');
|
|
87
|
+
const kind = searchParams.get('kind');
|
|
88
|
+
if (!path || !type || !documentId || !model) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
path,
|
|
93
|
+
type: type,
|
|
94
|
+
documentId,
|
|
95
|
+
locale: locale ?? null,
|
|
96
|
+
model: model,
|
|
97
|
+
kind: kind ? kind : undefined
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { PreviewFieldError, getAttributeSchemaFromPath, parseFieldMetaData, parsePathWithIndices };
|
|
102
|
+
//# sourceMappingURL=fieldUtils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fieldUtils.mjs","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":"AAQA;AACO,MAAMA,iBAA0BC,SAAAA,KAAAA,CAAAA;AAGrCC,IAAAA,WAAAA,CAAYC,UAA+B,CAAE;AAC3C,QAAA,KAAK,CAACA,UAAAA,CAAAA;QACN,IAAI,CAACC,IAAI,GAAG,mBAAA;QACZ,IAAI,CAACD,UAAU,GAAGA,UAAAA;AACpB;AACF;AAIA;AACO,MAAME,uBAAuB,CAACC,IAAAA,GAAAA;;;;AAInC,IAAA,OAAOA,KACJC,KAAK,CAAC,GACNC,CAAAA,CAAAA,GAAG,CAAC,CAACC,IAAAA,GAAAA;QACJ,MAAMC,YAAAA,GAAeC,SAASF,IAAM,EAAA,EAAA,CAAA;AACpC,QAAA,IAAI,CAACG,KAAMF,CAAAA,YAAAA,CAAAA,IAAiBD,IAASC,KAAAA,YAAAA,CAAaG,QAAQ,EAAI,EAAA;;YAE5D,OAAO;gBAAET,IAAM,EAAA,EAAA;gBAAIU,KAAOJ,EAAAA;AAAa,aAAA;AACzC;QACA,OAAO;YAAEN,IAAMK,EAAAA;AAAK,SAAA;KAErBM,CAAAA,CAAAA,MAAM,CAAC,CAACC,GAAiBP,EAAAA,IAAAA,GAAAA;AACxB,QAAA,IAAIA,KAAKL,IAAI,KAAK,MAAMK,IAAKK,CAAAA,KAAK,KAAKG,SAAW,EAAA;;YAEhD,IAAID,GAAAA,CAAIE,MAAM,GAAG,CAAG,EAAA;gBAClBF,GAAG,CAACA,IAAIE,MAAM,GAAG,EAAE,CAACJ,KAAK,GAAGL,IAAAA,CAAKK,KAAK;AACxC;SACK,MAAA;AACLE,YAAAA,GAAAA,CAAIG,IAAI,CAACV,IAAAA,CAAAA;AACX;QACA,OAAOO,GAAAA;AACT,KAAA,EAAG,EAAE,CAAA;AACT;AAEO,SAASI,0BAA2B,CAAA,EACzCd,IAAI,EACJe,MAAM,EACNC,UAAU,EACVC,QAAQ,EAMT,EAAA;AACC;;;;;AAKC,MACD,MAAMC,OAAAA,GAAU,CACdC,gBAAAA,EACAC,iBACAC,EAAAA,WAAAA,GAAAA;AAEA,QAAA,MAAM,CAACC,WAAAA,EAAa,GAAGC,cAAAA,CAAe,GAAGJ,gBAAAA;;AAGzC,QAAA,MAAMK,gBAAmBJ,GAAAA,iBAAiB,CAACE,WAAAA,CAAYxB,IAAI,CAAC;AAE5D,QAAA,IAAI,CAAC0B,gBAAkB,EAAA;AACrB,YAAA,MAAM,IAAI9B,iBAAkB,CAAA,oBAAA,CAAA;AAC9B;QAEA,IAAI8B,gBAAAA,CAAiBC,IAAI,KAAK,UAAY,EAAA;AACxC,YAAA,MAAM,IAAI/B,iBAAkB,CAAA,uBAAA,CAAA;AAC9B;QAEA,IAAI8B,gBAAAA,CAAiBC,IAAI,KAAK,WAAa,EAAA;AACzC,YAAA,MAAMC,sBAAsBV,UAAU,CAACQ,iBAAiBG,SAAS,CAAC,CAACC,UAAU;YAC7E,IAAIJ,gBAAAA,CAAiBK,UAAU,EAAE;;gBAE/B,IAAIP,WAAAA,CAAYd,KAAK,KAAKG,SAAW,EAAA;AACnC,oBAAA,MAAM,IAAIjB,iBAAkB,CAAA,oBAAA,CAAA;AAC9B;gBACA,OAAOwB,OAAAA,CACLK,cACAG,EAAAA,mBAAAA,EACAL,WAAW,CAACC,WAAYxB,CAAAA,IAAI,CAAC,CAACwB,WAAYd,CAAAA,KAAK,CAAC,CAAA;AAEpD;;AAGA,YAAA,OAAOU,QAAQK,cAAgBG,EAAAA,mBAAAA,EAAqBL,WAAW,CAACC,WAAAA,CAAYxB,IAAI,CAAC,CAAA;AACnF;QAEA,IAAI0B,gBAAAA,CAAiBC,IAAI,KAAK,aAAe,EAAA;;YAE3C,IAAIH,WAAAA,CAAYd,KAAK,KAAKG,SAAW,EAAA;AACnC,gBAAA,MAAM,IAAIjB,iBAAkB,CAAA,oBAAA,CAAA;AAC9B;YAEA,MAAMoC,aAAAA,GAAgBT,WAAW,CAACC,WAAAA,CAAYxB,IAAI,CAAC,CAACwB,WAAYd,CAAAA,KAAK,CAAC;AACtE,YAAA,MAAMkB,sBAAsBV,UAAU,CAACc,cAAcC,WAAW,CAAC,CAACH,UAAU;YAC5E,OAAOV,OAAAA,CAAQK,gBAAgBG,mBAAqBI,EAAAA,aAAAA,CAAAA;AACtD;;AAGA,QAAA,OAAOV,iBAAiB,CAACE,WAAYxB,CAAAA,IAAI,CAAC;AAC5C,KAAA;AAEA,IAAA,OAAOoB,OAAQnB,CAAAA,oBAAAA,CAAqBC,IAAOe,CAAAA,EAAAA,MAAAA,CAAOa,UAAU,EAAEX,QAAAA,CAAAA;AAChE;AAEO,SAASe,mBAAmBC,YAAoB,EAAA;IACrD,MAAMC,YAAAA,GAAe,IAAIC,eAAgBF,CAAAA,YAAAA,CAAAA;IACzC,MAAMjC,IAAAA,GAAOkC,YAAaE,CAAAA,GAAG,CAAC,MAAA,CAAA;IAC9B,MAAMX,IAAAA,GAAOS,YAAaE,CAAAA,GAAG,CAAC,MAAA,CAAA;IAC9B,MAAMC,UAAAA,GAAaH,YAAaE,CAAAA,GAAG,CAAC,YAAA,CAAA;IACpC,MAAME,MAAAA,GAASJ,YAAaE,CAAAA,GAAG,CAAC,QAAA,CAAA;IAChC,MAAMG,KAAAA,GAAQL,YAAaE,CAAAA,GAAG,CAAC,OAAA,CAAA;IAC/B,MAAMI,IAAAA,GAAON,YAAaE,CAAAA,GAAG,CAAC,MAAA,CAAA;AAE9B,IAAA,IAAI,CAACpC,IAAQ,IAAA,CAACyB,QAAQ,CAACY,UAAAA,IAAc,CAACE,KAAO,EAAA;QAC3C,OAAO,IAAA;AACT;IAEA,OAAO;AACLvC,QAAAA,IAAAA;QACAyB,IAAMA,EAAAA,IAAAA;AACNY,QAAAA,UAAAA;AACAC,QAAAA,MAAAA,EAAQA,MAAU,IAAA,IAAA;QAClBC,KAAOA,EAAAA,KAAAA;AACPC,QAAAA,IAAAA,EAAMA,OAAQA,IAAkC7B,GAAAA;AAClD,KAAA;AACF;;;;"}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* ---------------------------------------------------------------------------------------------*/ const HIGHLIGHT_PADDING = 2; // in pixels
|
|
13
13
|
const HIGHLIGHT_HOVER_COLOR = window.STRAPI_HIGHLIGHT_HOVER_COLOR ?? '#4945ff'; // dark primary500
|
|
14
14
|
const HIGHLIGHT_ACTIVE_COLOR = window.STRAPI_HIGHLIGHT_ACTIVE_COLOR ?? '#7b79ff'; // dark primary600
|
|
15
|
+
const HIGHLIGHT_STYLES_ID = 'strapi-preview-highlight-styles';
|
|
16
|
+
const DOUBLE_CLICK_TIMEOUT = 300; // milliseconds to wait for potential double-click
|
|
15
17
|
const DISABLE_STEGA_DECODING = window.STRAPI_DISABLE_STEGA_DECODING ?? false;
|
|
16
18
|
const SOURCE_ATTRIBUTE = 'data-strapi-source';
|
|
17
19
|
const OVERLAY_ID = 'strapi-preview-overlay';
|
|
@@ -19,7 +21,8 @@
|
|
|
19
21
|
STRAPI_FIELD_FOCUS: 'strapiFieldFocus',
|
|
20
22
|
STRAPI_FIELD_BLUR: 'strapiFieldBlur',
|
|
21
23
|
STRAPI_FIELD_CHANGE: 'strapiFieldChange',
|
|
22
|
-
STRAPI_FIELD_FOCUS_INTENT: 'strapiFieldFocusIntent'
|
|
24
|
+
STRAPI_FIELD_FOCUS_INTENT: 'strapiFieldFocusIntent',
|
|
25
|
+
STRAPI_FIELD_SINGLE_CLICK_HINT: 'strapiFieldSingleClickHint'
|
|
23
26
|
};
|
|
24
27
|
/**
|
|
25
28
|
* Calling the function in no-run mode lets us retrieve the constants from other files and keep
|
|
@@ -38,6 +41,9 @@
|
|
|
38
41
|
payload
|
|
39
42
|
}, '*');
|
|
40
43
|
};
|
|
44
|
+
const getElementsByPath = (path)=>{
|
|
45
|
+
return document.querySelectorAll(`[${SOURCE_ATTRIBUTE}*="path=${path}"]`);
|
|
46
|
+
};
|
|
41
47
|
/* -----------------------------------------------------------------------------------------------
|
|
42
48
|
* Functionality pieces
|
|
43
49
|
* ---------------------------------------------------------------------------------------------*/ const setupStegaDOMObserver = async ()=>{
|
|
@@ -52,9 +58,10 @@
|
|
|
52
58
|
const directTextContent = directTextNodes.map((node)=>node.textContent || '').join('');
|
|
53
59
|
if (directTextContent) {
|
|
54
60
|
try {
|
|
61
|
+
// TODO: check if we can call split instead of decode+clean
|
|
55
62
|
const result = stegaDecode(directTextContent);
|
|
56
|
-
if (result) {
|
|
57
|
-
element.setAttribute(SOURCE_ATTRIBUTE, result.
|
|
63
|
+
if (result && 'strapiSource' in result) {
|
|
64
|
+
element.setAttribute(SOURCE_ATTRIBUTE, result.strapiSource);
|
|
58
65
|
// Remove encoded part from DOM text content (to avoid breaking links for example)
|
|
59
66
|
directTextNodes.forEach((node)=>{
|
|
60
67
|
if (node.textContent) {
|
|
@@ -100,6 +107,37 @@
|
|
|
100
107
|
});
|
|
101
108
|
return observer;
|
|
102
109
|
};
|
|
110
|
+
const createHighlightStyles = ()=>{
|
|
111
|
+
const existingStyles = document.getElementById(HIGHLIGHT_STYLES_ID);
|
|
112
|
+
// Remove existing styles to avoid duplicates
|
|
113
|
+
if (existingStyles) {
|
|
114
|
+
existingStyles.remove();
|
|
115
|
+
}
|
|
116
|
+
const styleElement = document.createElement('style');
|
|
117
|
+
styleElement.id = HIGHLIGHT_STYLES_ID;
|
|
118
|
+
styleElement.textContent = `
|
|
119
|
+
.strapi-highlight {
|
|
120
|
+
position: absolute;
|
|
121
|
+
outline: 2px solid transparent;
|
|
122
|
+
pointer-events: auto;
|
|
123
|
+
border-radius: 2px;
|
|
124
|
+
background-color: transparent;
|
|
125
|
+
will-change: transform;
|
|
126
|
+
transition: outline-color 0.1s ease-in-out;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.strapi-highlight:hover {
|
|
130
|
+
outline-color: ${HIGHLIGHT_HOVER_COLOR} !important;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.strapi-highlight.strapi-highlight-focused {
|
|
134
|
+
outline-color: ${HIGHLIGHT_ACTIVE_COLOR} !important;
|
|
135
|
+
outline-width: 3px !important;
|
|
136
|
+
}
|
|
137
|
+
`;
|
|
138
|
+
document.head.appendChild(styleElement);
|
|
139
|
+
return styleElement;
|
|
140
|
+
};
|
|
103
141
|
const createOverlaySystem = ()=>{
|
|
104
142
|
// Clean up before creating a new overlay so we can safely call previewScript multiple times
|
|
105
143
|
window.__strapi_previewCleanup?.();
|
|
@@ -122,6 +160,7 @@
|
|
|
122
160
|
const elementsToHighlight = new Map();
|
|
123
161
|
const eventListeners = [];
|
|
124
162
|
const focusedHighlights = [];
|
|
163
|
+
const pendingClicks = new Map(); // number is timeout id
|
|
125
164
|
let focusedField = null;
|
|
126
165
|
const drawHighlight = (target, highlight)=>{
|
|
127
166
|
if (!highlight) return;
|
|
@@ -141,27 +180,57 @@
|
|
|
141
180
|
return;
|
|
142
181
|
}
|
|
143
182
|
const highlight = document.createElement('div');
|
|
144
|
-
highlight.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
background-color: transparent;
|
|
150
|
-
will-change: transform;
|
|
151
|
-
transition: outline-color 0.1s ease-in-out;
|
|
152
|
-
`;
|
|
153
|
-
// Move hover detection to the underlying element
|
|
154
|
-
const mouseEnterHandler = ()=>{
|
|
155
|
-
if (!focusedHighlights.includes(highlight)) {
|
|
156
|
-
highlight.style.outlineColor = HIGHLIGHT_HOVER_COLOR;
|
|
183
|
+
highlight.className = 'strapi-highlight';
|
|
184
|
+
const clickHandler = (event)=>{
|
|
185
|
+
// Skip if this is a re-dispatched event from our delayed handler to avoid infinite loops
|
|
186
|
+
if (event.__strapi_redispatched) {
|
|
187
|
+
return;
|
|
157
188
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
189
|
+
// Prevent the immediate action for interactive elements
|
|
190
|
+
event.preventDefault();
|
|
191
|
+
event.stopPropagation();
|
|
192
|
+
// Clear any existing timeout for this element
|
|
193
|
+
const existingTimeout = pendingClicks.get(element);
|
|
194
|
+
if (existingTimeout) {
|
|
195
|
+
window.clearTimeout(existingTimeout);
|
|
196
|
+
pendingClicks.delete(element);
|
|
162
197
|
}
|
|
198
|
+
// Set up a delayed single-click handler
|
|
199
|
+
const timeout = window.setTimeout(()=>{
|
|
200
|
+
pendingClicks.delete(element);
|
|
201
|
+
// Send single-click hint notification
|
|
202
|
+
sendMessage(INTERNAL_EVENTS.STRAPI_FIELD_SINGLE_CLICK_HINT, null);
|
|
203
|
+
// Re-trigger the click on the underlying element after the double-click timeout
|
|
204
|
+
// Create a new event to dispatch with a marker to prevent re-handling
|
|
205
|
+
const newEvent = new MouseEvent('click', {
|
|
206
|
+
bubbles: true,
|
|
207
|
+
cancelable: true,
|
|
208
|
+
view: window,
|
|
209
|
+
detail: 1,
|
|
210
|
+
button: event.button,
|
|
211
|
+
buttons: event.buttons,
|
|
212
|
+
clientX: event.clientX,
|
|
213
|
+
clientY: event.clientY,
|
|
214
|
+
ctrlKey: event.ctrlKey,
|
|
215
|
+
altKey: event.altKey,
|
|
216
|
+
shiftKey: event.shiftKey,
|
|
217
|
+
metaKey: event.metaKey
|
|
218
|
+
});
|
|
219
|
+
newEvent.__strapi_redispatched = true;
|
|
220
|
+
element.dispatchEvent(newEvent);
|
|
221
|
+
}, DOUBLE_CLICK_TIMEOUT);
|
|
222
|
+
pendingClicks.set(element, timeout);
|
|
163
223
|
};
|
|
164
|
-
const doubleClickHandler = ()=>{
|
|
224
|
+
const doubleClickHandler = (event)=>{
|
|
225
|
+
// Prevent the default behavior on double-click
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
event.stopPropagation();
|
|
228
|
+
// Clear any pending single-click action
|
|
229
|
+
const existingTimeout = pendingClicks.get(element);
|
|
230
|
+
if (existingTimeout) {
|
|
231
|
+
clearTimeout(existingTimeout);
|
|
232
|
+
pendingClicks.delete(element);
|
|
233
|
+
}
|
|
165
234
|
const sourceAttribute = element.getAttribute(SOURCE_ATTRIBUTE);
|
|
166
235
|
if (sourceAttribute) {
|
|
167
236
|
const rect = element.getBoundingClientRect();
|
|
@@ -184,25 +253,20 @@
|
|
|
184
253
|
event.preventDefault();
|
|
185
254
|
}
|
|
186
255
|
};
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
element.addEventListener('mousedown', mouseDownHandler);
|
|
256
|
+
highlight.addEventListener('click', clickHandler);
|
|
257
|
+
highlight.addEventListener('dblclick', doubleClickHandler);
|
|
258
|
+
highlight.addEventListener('mousedown', mouseDownHandler);
|
|
191
259
|
// Store event listeners for cleanup
|
|
192
260
|
eventListeners.push({
|
|
193
|
-
element,
|
|
194
|
-
type: '
|
|
195
|
-
handler:
|
|
261
|
+
element: highlight,
|
|
262
|
+
type: 'click',
|
|
263
|
+
handler: clickHandler
|
|
196
264
|
}, {
|
|
197
|
-
element,
|
|
198
|
-
type: 'mouseleave',
|
|
199
|
-
handler: mouseLeaveHandler
|
|
200
|
-
}, {
|
|
201
|
-
element,
|
|
265
|
+
element: highlight,
|
|
202
266
|
type: 'dblclick',
|
|
203
267
|
handler: doubleClickHandler
|
|
204
268
|
}, {
|
|
205
|
-
element,
|
|
269
|
+
element: highlight,
|
|
206
270
|
type: 'mousedown',
|
|
207
271
|
handler: mouseDownHandler
|
|
208
272
|
});
|
|
@@ -213,15 +277,21 @@
|
|
|
213
277
|
const removeHighlightForElement = (element)=>{
|
|
214
278
|
const highlight = elementsToHighlight.get(element);
|
|
215
279
|
if (!highlight) return;
|
|
280
|
+
// Clear any pending click timeout for this element
|
|
281
|
+
const pendingTimeout = pendingClicks.get(element);
|
|
282
|
+
if (pendingTimeout) {
|
|
283
|
+
window.clearTimeout(pendingTimeout);
|
|
284
|
+
pendingClicks.delete(element);
|
|
285
|
+
}
|
|
216
286
|
highlight.remove();
|
|
217
287
|
elementsToHighlight.delete(element);
|
|
218
|
-
// Remove event listeners for this
|
|
219
|
-
const listenersToRemove = eventListeners.filter((listener)=>listener.element ===
|
|
288
|
+
// Remove event listeners for this highlight
|
|
289
|
+
const listenersToRemove = eventListeners.filter((listener)=>listener.element === highlight);
|
|
220
290
|
listenersToRemove.forEach(({ element, type, handler })=>{
|
|
221
291
|
element.removeEventListener(type, handler);
|
|
222
292
|
});
|
|
223
|
-
// Mutate eventListeners to remove listeners for this
|
|
224
|
-
eventListeners.splice(0, eventListeners.length, ...eventListeners.filter((listener)=>listener.element !==
|
|
293
|
+
// Mutate eventListeners to remove listeners for this highlight
|
|
294
|
+
eventListeners.splice(0, eventListeners.length, ...eventListeners.filter((listener)=>listener.element !== highlight));
|
|
225
295
|
};
|
|
226
296
|
// Process all existing elements with source attributes
|
|
227
297
|
const initialElements = window.document.querySelectorAll(`[${SOURCE_ATTRIBUTE}]`);
|
|
@@ -245,7 +315,11 @@
|
|
|
245
315
|
setFocusedField: (field)=>{
|
|
246
316
|
focusedField = field;
|
|
247
317
|
},
|
|
248
|
-
getFocusedField: ()=>focusedField
|
|
318
|
+
getFocusedField: ()=>focusedField,
|
|
319
|
+
clearAllPendingClicks: ()=>{
|
|
320
|
+
pendingClicks.forEach((timeout)=>clearTimeout(timeout));
|
|
321
|
+
pendingClicks.clear();
|
|
322
|
+
}
|
|
249
323
|
};
|
|
250
324
|
};
|
|
251
325
|
/**
|
|
@@ -364,8 +438,7 @@
|
|
|
364
438
|
if (event.data.type === INTERNAL_EVENTS.STRAPI_FIELD_CHANGE) {
|
|
365
439
|
const { field, value } = event.data.payload;
|
|
366
440
|
if (!field) return;
|
|
367
|
-
|
|
368
|
-
matchingElements.forEach((element)=>{
|
|
441
|
+
getElementsByPath(field).forEach((element)=>{
|
|
369
442
|
if (element instanceof HTMLElement) {
|
|
370
443
|
element.textContent = value || '';
|
|
371
444
|
}
|
|
@@ -380,17 +453,21 @@
|
|
|
380
453
|
if (!field) return;
|
|
381
454
|
// Clear existing focused highlights
|
|
382
455
|
highlightManager.focusedHighlights.forEach((highlight)=>{
|
|
383
|
-
highlight.
|
|
456
|
+
highlight.classList.remove('strapi-highlight-focused');
|
|
384
457
|
});
|
|
385
458
|
highlightManager.focusedHighlights.length = 0;
|
|
386
459
|
// Set new focused field and highlight matching elements
|
|
387
460
|
highlightManager.setFocusedField(field);
|
|
388
|
-
|
|
389
|
-
|
|
461
|
+
getElementsByPath(field).forEach((element, index)=>{
|
|
462
|
+
if (index === 0) {
|
|
463
|
+
element.scrollIntoView({
|
|
464
|
+
behavior: 'smooth',
|
|
465
|
+
block: 'center'
|
|
466
|
+
});
|
|
467
|
+
}
|
|
390
468
|
const highlight = highlightManager.highlights[Array.from(highlightManager.elements).indexOf(element)];
|
|
391
469
|
if (highlight) {
|
|
392
|
-
highlight.
|
|
393
|
-
highlight.style.outlineWidth = '3px';
|
|
470
|
+
highlight.classList.add('strapi-highlight-focused');
|
|
394
471
|
highlightManager.focusedHighlights.push(highlight);
|
|
395
472
|
}
|
|
396
473
|
});
|
|
@@ -401,8 +478,7 @@
|
|
|
401
478
|
const { field } = event.data.payload;
|
|
402
479
|
if (field !== highlightManager.getFocusedField()) return;
|
|
403
480
|
highlightManager.focusedHighlights.forEach((highlight)=>{
|
|
404
|
-
highlight.
|
|
405
|
-
highlight.style.outlineWidth = '2px';
|
|
481
|
+
highlight.classList.remove('strapi-highlight-focused');
|
|
406
482
|
});
|
|
407
483
|
highlightManager.focusedHighlights.length = 0;
|
|
408
484
|
highlightManager.setFocusedField(null);
|
|
@@ -420,29 +496,37 @@
|
|
|
420
496
|
messageEventListener
|
|
421
497
|
];
|
|
422
498
|
};
|
|
423
|
-
const createCleanupSystem = (overlay, observers, scrollManager, eventHandlers)=>{
|
|
499
|
+
const createCleanupSystem = (overlay, observers, scrollManager, eventHandlers, highlightManager)=>{
|
|
424
500
|
window.__strapi_previewCleanup = ()=>{
|
|
425
501
|
observers.resizeObserver.disconnect();
|
|
426
502
|
observers.highlightObserver.disconnect();
|
|
427
503
|
observers.stegaObserver?.disconnect();
|
|
428
504
|
// Clean up scroll listeners
|
|
429
505
|
scrollManager.cleanup();
|
|
506
|
+
// Clear all pending click timeouts
|
|
507
|
+
highlightManager.clearAllPendingClicks();
|
|
430
508
|
// Remove highlight event listeners
|
|
431
509
|
eventHandlers.forEach(({ element, type, handler })=>{
|
|
432
510
|
element.removeEventListener(type, handler);
|
|
433
511
|
});
|
|
512
|
+
// Clean up CSS styles
|
|
513
|
+
const existingStyles = document.getElementById(HIGHLIGHT_STYLES_ID);
|
|
514
|
+
if (existingStyles) {
|
|
515
|
+
existingStyles.remove();
|
|
516
|
+
}
|
|
434
517
|
overlay.remove();
|
|
435
518
|
};
|
|
436
519
|
};
|
|
437
520
|
/* -----------------------------------------------------------------------------------------------
|
|
438
521
|
* Orchestration
|
|
439
522
|
* ---------------------------------------------------------------------------------------------*/ setupStegaDOMObserver().then((stegaObserver)=>{
|
|
523
|
+
createHighlightStyles();
|
|
440
524
|
const overlay = createOverlaySystem();
|
|
441
525
|
const highlightManager = createHighlightManager(overlay);
|
|
442
526
|
const observers = setupObservers(highlightManager, stegaObserver);
|
|
443
527
|
const scrollManager = setupScrollManagement(highlightManager);
|
|
444
528
|
const eventHandlers = setupEventHandlers(highlightManager);
|
|
445
|
-
createCleanupSystem(overlay, observers, scrollManager, eventHandlers);
|
|
529
|
+
createCleanupSystem(overlay, observers, scrollManager, eventHandlers, highlightManager);
|
|
446
530
|
});
|
|
447
531
|
};
|
|
448
532
|
|