@elementor/editor-canvas 4.0.0-manual → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +127 -78
- package/dist/index.mjs +92 -43
- package/package.json +18 -18
- package/src/hooks/__tests__/use-style-items.test.ts +57 -0
- package/src/hooks/use-style-items.ts +2 -2
- package/src/index.ts +1 -1
- package/src/legacy/create-nested-templated-element-type.ts +15 -2
- package/src/legacy/create-templated-element-type.ts +8 -0
- package/src/legacy/types.ts +3 -1
- package/src/mcp/canvas-mcp.ts +5 -7
- package/src/mcp/resources/breakpoints-resource.ts +11 -4
- package/src/mcp/resources/document-structure-resource.ts +18 -13
- package/src/mcp/tools/build-composition/schema.ts +1 -1
- package/src/mcp/tools/build-composition/tool.ts +5 -1
- package/src/mcp/utils/__tests__/get-composition-target-container.test.ts +59 -0
- package/src/mcp/utils/get-composition-target-container.ts +15 -0
- package/src/renderers/__tests__/create-styles-renderer.test.ts +117 -0
- package/src/renderers/create-styles-renderer.ts +13 -3
- package/src/style-commands/__tests__/paste-style.test.ts +5 -3
- package/src/style-commands/__tests__/reset-style.test.ts +3 -3
- package/src/style-commands/paste-style.ts +7 -1
- package/src/style-commands/reset-style.ts +1 -1
- package/src/style-commands/undoable-actions/paste-element-style.ts +1 -1
- package/src/style-commands/undoable-actions/reset-element-style.ts +1 -1
- package/src/transformers/styles/__tests__/size-transformer.test.ts +24 -0
- package/src/transformers/styles/size-transformer.ts +3 -0
- /package/src/{style-commands/utils.ts → utils/command-utils.ts} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { v1ReadyEvent } from "@elementor/editor-v1-adapters";
|
|
3
3
|
var BREAKPOINTS_SCHEMA_URI = "elementor://breakpoints/list";
|
|
4
4
|
var initBreakpointsResource = (reg) => {
|
|
5
|
-
const {
|
|
5
|
+
const { resource, sendResourceUpdated } = reg;
|
|
6
6
|
const getBreakpointsList = () => {
|
|
7
7
|
const { breakpoints } = window.elementor?.config?.responsive || {};
|
|
8
8
|
if (!breakpoints) {
|
|
@@ -26,9 +26,16 @@ var initBreakpointsResource = (reg) => {
|
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
resource(
|
|
30
|
+
"breakpoints ",
|
|
31
|
+
BREAKPOINTS_SCHEMA_URI,
|
|
32
|
+
{
|
|
33
|
+
description: "Breakpoints list."
|
|
34
|
+
},
|
|
35
|
+
() => {
|
|
36
|
+
return buildResourceResponse();
|
|
37
|
+
}
|
|
38
|
+
);
|
|
32
39
|
window.addEventListener(v1ReadyEvent().name, () => {
|
|
33
40
|
sendResourceUpdated({
|
|
34
41
|
uri: BREAKPOINTS_SCHEMA_URI,
|
|
@@ -799,14 +806,22 @@ var UnknownStyleStateError = createError({
|
|
|
799
806
|
var SELECTORS_MAP = {
|
|
800
807
|
class: "."
|
|
801
808
|
};
|
|
809
|
+
var DEFAULT_BREAKPOINT = "desktop";
|
|
810
|
+
var DEFAULT_STATE = "normal";
|
|
811
|
+
function getStyleUniqueKey(style) {
|
|
812
|
+
const breakpoint = style.variants[0]?.meta?.breakpoint ?? DEFAULT_BREAKPOINT;
|
|
813
|
+
const state = style.variants[0]?.meta?.state ?? DEFAULT_STATE;
|
|
814
|
+
return `${style.id}-${breakpoint}-${state}`;
|
|
815
|
+
}
|
|
802
816
|
function createStylesRenderer({ resolve, breakpoints, selectorPrefix = "" }) {
|
|
803
817
|
return async ({ styles, signal }) => {
|
|
804
|
-
const
|
|
818
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
805
819
|
const uniqueStyles = styles.filter((style) => {
|
|
806
|
-
|
|
820
|
+
const key = getStyleUniqueKey(style);
|
|
821
|
+
if (seenKeys.has(key)) {
|
|
807
822
|
return false;
|
|
808
823
|
}
|
|
809
|
-
|
|
824
|
+
seenKeys.add(key);
|
|
810
825
|
return true;
|
|
811
826
|
});
|
|
812
827
|
const stylesCssPromises = uniqueStyles.map(async (style) => {
|
|
@@ -983,7 +998,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
|
|
|
983
998
|
}
|
|
984
999
|
async function createItems(signal) {
|
|
985
1000
|
const allStyles = provider.actions.all();
|
|
986
|
-
const styles = allStyles.reverse().map((style) => {
|
|
1001
|
+
const styles = [...allStyles].reverse().map((style) => {
|
|
987
1002
|
return {
|
|
988
1003
|
...style,
|
|
989
1004
|
cssName: provider.actions.resolveCssName(style.id)
|
|
@@ -991,7 +1006,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
|
|
|
991
1006
|
});
|
|
992
1007
|
return renderStyles({ styles: breakToBreakpoints(styles), signal }).then((rendered) => {
|
|
993
1008
|
rebuildCache(cache, allStyles, rendered);
|
|
994
|
-
return
|
|
1009
|
+
return getOrderedItems(cache);
|
|
995
1010
|
});
|
|
996
1011
|
}
|
|
997
1012
|
function breakToBreakpoints(styles) {
|
|
@@ -1591,6 +1606,9 @@ var shadowTransformer = createTransformer((value) => {
|
|
|
1591
1606
|
|
|
1592
1607
|
// src/transformers/styles/size-transformer.ts
|
|
1593
1608
|
var sizeTransformer = createTransformer((value) => {
|
|
1609
|
+
if (value.unit === "auto") {
|
|
1610
|
+
return "auto";
|
|
1611
|
+
}
|
|
1594
1612
|
return value.unit === "custom" ? value.size : `${value.size}${value.unit}`;
|
|
1595
1613
|
});
|
|
1596
1614
|
|
|
@@ -1974,6 +1992,7 @@ function createTemplatedElementView({
|
|
|
1974
1992
|
this._lastResolvedSettingsHash = settingsHash;
|
|
1975
1993
|
const context = {
|
|
1976
1994
|
id: this.model.get("id"),
|
|
1995
|
+
interaction_id: this.getInteractionId(),
|
|
1977
1996
|
type,
|
|
1978
1997
|
settings,
|
|
1979
1998
|
base_styles: baseStylesDictionary
|
|
@@ -2008,6 +2027,11 @@ function createTemplatedElementView({
|
|
|
2008
2027
|
_openEditingPanel(options) {
|
|
2009
2028
|
this._doAfterRender(() => super._openEditingPanel(options));
|
|
2010
2029
|
}
|
|
2030
|
+
getInteractionId() {
|
|
2031
|
+
const originId = this.model.get("originId");
|
|
2032
|
+
const id = this.model.get("id");
|
|
2033
|
+
return originId ?? id;
|
|
2034
|
+
}
|
|
2011
2035
|
};
|
|
2012
2036
|
}
|
|
2013
2037
|
|
|
@@ -2040,10 +2064,11 @@ function createNestedTemplatedElementType({
|
|
|
2040
2064
|
}
|
|
2041
2065
|
function buildEditorAttributes(model) {
|
|
2042
2066
|
const id = model.get("id");
|
|
2067
|
+
const originId = model.get("originId");
|
|
2043
2068
|
const cid = model.cid ?? "";
|
|
2044
2069
|
const attrs = {
|
|
2045
2070
|
"data-model-cid": cid,
|
|
2046
|
-
"data-interaction-id": id,
|
|
2071
|
+
"data-interaction-id": originId ?? id,
|
|
2047
2072
|
"x-ignore": "true"
|
|
2048
2073
|
};
|
|
2049
2074
|
return Object.entries(attrs).map(([key, value]) => `${key}="${value}"`).join(" ");
|
|
@@ -2077,6 +2102,9 @@ function createNestedTemplatedElementView({
|
|
|
2077
2102
|
invalidateRenderCache() {
|
|
2078
2103
|
this._lastResolvedSettingsHash = null;
|
|
2079
2104
|
},
|
|
2105
|
+
renderOnChange() {
|
|
2106
|
+
this.render();
|
|
2107
|
+
},
|
|
2080
2108
|
render() {
|
|
2081
2109
|
this._abortController?.abort();
|
|
2082
2110
|
this._abortController = new AbortController();
|
|
@@ -2120,6 +2148,7 @@ function createNestedTemplatedElementView({
|
|
|
2120
2148
|
this._lastResolvedSettingsHash = settingsHash;
|
|
2121
2149
|
const context = {
|
|
2122
2150
|
id: model.get("id"),
|
|
2151
|
+
interaction_id: this.getInteractionId(),
|
|
2123
2152
|
type,
|
|
2124
2153
|
settings,
|
|
2125
2154
|
base_styles: baseStylesDictionary,
|
|
@@ -2249,6 +2278,11 @@ function createNestedTemplatedElementView({
|
|
|
2249
2278
|
},
|
|
2250
2279
|
_openEditingPanel(options) {
|
|
2251
2280
|
this._doAfterRender(() => parentOpenEditingPanel.call(this, options));
|
|
2281
|
+
},
|
|
2282
|
+
getInteractionId() {
|
|
2283
|
+
const originId = this.model.get("originId");
|
|
2284
|
+
const id = this.model.get("id");
|
|
2285
|
+
return originId ?? id;
|
|
2252
2286
|
}
|
|
2253
2287
|
});
|
|
2254
2288
|
}
|
|
@@ -2907,7 +2941,7 @@ function initTabsModelExtensions() {
|
|
|
2907
2941
|
import { __privateListenTo as listenTo, commandEndEvent as commandEndEvent4 } from "@elementor/editor-v1-adapters";
|
|
2908
2942
|
var DOCUMENT_STRUCTURE_URI = "elementor://document/structure";
|
|
2909
2943
|
var initDocumentStructureResource = (reg) => {
|
|
2910
|
-
const {
|
|
2944
|
+
const { resource, sendResourceUpdated } = reg;
|
|
2911
2945
|
let currentDocumentStructure = null;
|
|
2912
2946
|
const updateDocumentStructure = () => {
|
|
2913
2947
|
const structure = getDocumentStructure();
|
|
@@ -2929,17 +2963,23 @@ var initDocumentStructureResource = (reg) => {
|
|
|
2929
2963
|
updateDocumentStructure
|
|
2930
2964
|
);
|
|
2931
2965
|
updateDocumentStructure();
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2966
|
+
resource(
|
|
2967
|
+
"document-structure",
|
|
2968
|
+
DOCUMENT_STRUCTURE_URI,
|
|
2969
|
+
{
|
|
2970
|
+
description: "Document structure."
|
|
2971
|
+
},
|
|
2972
|
+
async () => {
|
|
2973
|
+
return {
|
|
2974
|
+
contents: [
|
|
2975
|
+
{
|
|
2976
|
+
uri: DOCUMENT_STRUCTURE_URI,
|
|
2977
|
+
text: JSON.stringify(getDocumentStructure(), null, 2)
|
|
2978
|
+
}
|
|
2979
|
+
]
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
);
|
|
2943
2983
|
};
|
|
2944
2984
|
function getDocumentStructure() {
|
|
2945
2985
|
const extendedWindow = window;
|
|
@@ -2979,6 +3019,7 @@ function extractElementData(element) {
|
|
|
2979
3019
|
}
|
|
2980
3020
|
|
|
2981
3021
|
// src/mcp/tools/build-composition/tool.ts
|
|
3022
|
+
import { getCurrentDocument } from "@elementor/editor-documents";
|
|
2982
3023
|
import {
|
|
2983
3024
|
createElement as createElement8,
|
|
2984
3025
|
deleteElement,
|
|
@@ -3409,6 +3450,16 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3409
3450
|
}
|
|
3410
3451
|
};
|
|
3411
3452
|
|
|
3453
|
+
// src/mcp/utils/get-composition-target-container.ts
|
|
3454
|
+
import { COMPONENT_DOCUMENT_TYPE } from "@elementor/editor-documents";
|
|
3455
|
+
function getCompositionTargetContainer(documentContainer, documentType) {
|
|
3456
|
+
const firstChild = documentContainer.children?.[0];
|
|
3457
|
+
if (documentType === COMPONENT_DOCUMENT_TYPE && firstChild) {
|
|
3458
|
+
return firstChild;
|
|
3459
|
+
}
|
|
3460
|
+
return documentContainer;
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3412
3463
|
// src/mcp/tools/build-composition/prompt.ts
|
|
3413
3464
|
import { toolPrompts } from "@elementor/editor-mcp";
|
|
3414
3465
|
var generatePrompt = () => {
|
|
@@ -3548,7 +3599,7 @@ Note: No height/width specified on any element - flexbox handles layout automati
|
|
|
3548
3599
|
};
|
|
3549
3600
|
|
|
3550
3601
|
// src/mcp/tools/build-composition/schema.ts
|
|
3551
|
-
import {
|
|
3602
|
+
import { z } from "@elementor/schema";
|
|
3552
3603
|
var inputSchema = {
|
|
3553
3604
|
xmlStructure: z.string().describe("The XML structure representing the composition to be built"),
|
|
3554
3605
|
elementConfig: z.record(
|
|
@@ -3604,6 +3655,8 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
3604
3655
|
const errors = [];
|
|
3605
3656
|
const rootContainers = [];
|
|
3606
3657
|
const documentContainer = getContainer3("document");
|
|
3658
|
+
const currentDocument = getCurrentDocument();
|
|
3659
|
+
const targetContainer = getCompositionTargetContainer(documentContainer, currentDocument?.type.value);
|
|
3607
3660
|
try {
|
|
3608
3661
|
const compositionBuilder = CompositionBuilder.fromXMLString(xmlStructure, {
|
|
3609
3662
|
createElement: createElement8,
|
|
@@ -3616,7 +3669,7 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
3616
3669
|
configErrors,
|
|
3617
3670
|
invalidStyles,
|
|
3618
3671
|
rootContainers: generatedRootContainers
|
|
3619
|
-
} = compositionBuilder.build(
|
|
3672
|
+
} = compositionBuilder.build(targetContainer);
|
|
3620
3673
|
rootContainers.push(...generatedRootContainers);
|
|
3621
3674
|
generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
|
|
3622
3675
|
if (configErrors.length) {
|
|
@@ -4000,14 +4053,12 @@ var initGetElementConfigTool = (reg) => {
|
|
|
4000
4053
|
var initCanvasMcp = (reg) => {
|
|
4001
4054
|
const { setMCPDescription } = reg;
|
|
4002
4055
|
setMCPDescription(
|
|
4003
|
-
`Everything related to
|
|
4056
|
+
`Everything related to V4 ( Atomic ) canvas.
|
|
4004
4057
|
# Canvas workflow for new compositions
|
|
4005
|
-
-
|
|
4006
|
-
-
|
|
4007
|
-
-
|
|
4008
|
-
|
|
4009
|
-
- Build valid XML with minimal inline styles (layout/positioning only)
|
|
4010
|
-
- Apply global classes to elements`
|
|
4058
|
+
- Configure elements settings and styles
|
|
4059
|
+
- Build compositions/sections out of V4 atomic elements using context aware designs using the website resources
|
|
4060
|
+
- Get and retrieve element configuration values
|
|
4061
|
+
`
|
|
4011
4062
|
);
|
|
4012
4063
|
initWidgetsSchemaResource(reg);
|
|
4013
4064
|
initDocumentStructureResource(reg);
|
|
@@ -4225,18 +4276,7 @@ import {
|
|
|
4225
4276
|
commandStartEvent
|
|
4226
4277
|
} from "@elementor/editor-v1-adapters";
|
|
4227
4278
|
|
|
4228
|
-
// src/
|
|
4229
|
-
import {
|
|
4230
|
-
createElementStyle as createElementStyle2,
|
|
4231
|
-
deleteElementStyle,
|
|
4232
|
-
getElementStyles as getElementStyles3,
|
|
4233
|
-
updateElementStyle as updateElementStyle2
|
|
4234
|
-
} from "@elementor/editor-elements";
|
|
4235
|
-
import { ELEMENTS_STYLES_RESERVED_LABEL } from "@elementor/editor-styles-repository";
|
|
4236
|
-
import { undoable as undoable2 } from "@elementor/editor-v1-adapters";
|
|
4237
|
-
import { __ as __6 } from "@wordpress/i18n";
|
|
4238
|
-
|
|
4239
|
-
// src/style-commands/utils.ts
|
|
4279
|
+
// src/utils/command-utils.ts
|
|
4240
4280
|
import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache8 } from "@elementor/editor-elements";
|
|
4241
4281
|
import { CLASSES_PROP_KEY } from "@elementor/editor-props";
|
|
4242
4282
|
import { __ as __5 } from "@wordpress/i18n";
|
|
@@ -4279,6 +4319,15 @@ function getTitleForContainers(containers) {
|
|
|
4279
4319
|
}
|
|
4280
4320
|
|
|
4281
4321
|
// src/style-commands/undoable-actions/paste-element-style.ts
|
|
4322
|
+
import {
|
|
4323
|
+
createElementStyle as createElementStyle2,
|
|
4324
|
+
deleteElementStyle,
|
|
4325
|
+
getElementStyles as getElementStyles3,
|
|
4326
|
+
updateElementStyle as updateElementStyle2
|
|
4327
|
+
} from "@elementor/editor-elements";
|
|
4328
|
+
import { ELEMENTS_STYLES_RESERVED_LABEL } from "@elementor/editor-styles-repository";
|
|
4329
|
+
import { undoable as undoable2 } from "@elementor/editor-v1-adapters";
|
|
4330
|
+
import { __ as __6 } from "@wordpress/i18n";
|
|
4282
4331
|
var undoablePasteElementStyle = () => undoable2(
|
|
4283
4332
|
{
|
|
4284
4333
|
do: ({ containers, newStyle }) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-canvas",
|
|
3
3
|
"description": "Elementor Editor Canvas",
|
|
4
|
-
"version": "4.0.0
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -37,24 +37,24 @@
|
|
|
37
37
|
"react-dom": "^18.3.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@elementor/editor": "4.0.0
|
|
41
|
-
"@elementor/editor-controls": "4.0.0
|
|
42
|
-
"@elementor/editor-documents": "4.0.0
|
|
43
|
-
"@elementor/editor-elements": "4.0.0
|
|
44
|
-
"@elementor/editor-interactions": "4.0.0
|
|
45
|
-
"@elementor/editor-mcp": "4.0.0
|
|
46
|
-
"@elementor/editor-notifications": "4.0.0
|
|
47
|
-
"@elementor/editor-props": "4.0.0
|
|
48
|
-
"@elementor/editor-responsive": "4.0.0
|
|
49
|
-
"@elementor/editor-styles": "4.0.0
|
|
50
|
-
"@elementor/editor-styles-repository": "4.0.0
|
|
51
|
-
"@elementor/editor-ui": "4.0.0
|
|
52
|
-
"@elementor/editor-v1-adapters": "4.0.0
|
|
53
|
-
"@elementor/schema": "4.0.0
|
|
54
|
-
"@elementor/twing": "4.0.0
|
|
40
|
+
"@elementor/editor": "4.0.0",
|
|
41
|
+
"@elementor/editor-controls": "4.0.0",
|
|
42
|
+
"@elementor/editor-documents": "4.0.0",
|
|
43
|
+
"@elementor/editor-elements": "4.0.0",
|
|
44
|
+
"@elementor/editor-interactions": "4.0.0",
|
|
45
|
+
"@elementor/editor-mcp": "4.0.0",
|
|
46
|
+
"@elementor/editor-notifications": "4.0.0",
|
|
47
|
+
"@elementor/editor-props": "4.0.0",
|
|
48
|
+
"@elementor/editor-responsive": "4.0.0",
|
|
49
|
+
"@elementor/editor-styles": "4.0.0",
|
|
50
|
+
"@elementor/editor-styles-repository": "4.0.0",
|
|
51
|
+
"@elementor/editor-ui": "4.0.0",
|
|
52
|
+
"@elementor/editor-v1-adapters": "4.0.0",
|
|
53
|
+
"@elementor/schema": "4.0.0",
|
|
54
|
+
"@elementor/twing": "4.0.0",
|
|
55
55
|
"@elementor/ui": "1.36.17",
|
|
56
|
-
"@elementor/utils": "4.0.0
|
|
57
|
-
"@elementor/wp-media": "4.0.0
|
|
56
|
+
"@elementor/utils": "4.0.0",
|
|
57
|
+
"@elementor/wp-media": "4.0.0",
|
|
58
58
|
"@floating-ui/react": "^0.27.5",
|
|
59
59
|
"@wordpress/i18n": "^5.13.0"
|
|
60
60
|
},
|
|
@@ -128,6 +128,9 @@ describe( 'useStyleItems', () => {
|
|
|
128
128
|
|
|
129
129
|
jest.mocked( stylesRepository ).getProviders.mockReturnValue( [ mockProvider1, mockProvider2 ] );
|
|
130
130
|
|
|
131
|
+
const provider1OriginalOrder = mockProvider1.actions.all().map( ( s ) => s.id );
|
|
132
|
+
const provider2OriginalOrder = mockProvider2.actions.all().map( ( s ) => s.id );
|
|
133
|
+
|
|
131
134
|
let attachPreviewCallback: () => Promise< void >;
|
|
132
135
|
|
|
133
136
|
jest.mocked( registerDataHook ).mockImplementation( ( position, command, callback ) => {
|
|
@@ -156,6 +159,9 @@ describe( 'useStyleItems', () => {
|
|
|
156
159
|
{ id: 'style2', breakpoint: 'desktop' },
|
|
157
160
|
{ id: 'style1', breakpoint: 'desktop' },
|
|
158
161
|
] );
|
|
162
|
+
|
|
163
|
+
expect( mockProvider1.actions.all().map( ( s ) => s.id ) ).toEqual( provider1OriginalOrder );
|
|
164
|
+
expect( mockProvider2.actions.all().map( ( s ) => s.id ) ).toEqual( provider2OriginalOrder );
|
|
159
165
|
} );
|
|
160
166
|
|
|
161
167
|
it( 'should return style items ordered by provider priority and breakpoint', async () => {
|
|
@@ -283,6 +289,57 @@ describe( 'useStyleItems', () => {
|
|
|
283
289
|
expect( result.current ).toHaveLength( 2 );
|
|
284
290
|
} );
|
|
285
291
|
|
|
292
|
+
it( 'should maintain breakpoint order after style update', async () => {
|
|
293
|
+
// Arrange.
|
|
294
|
+
const renderStylesMock = jest.fn().mockImplementation( ( { styles } ) =>
|
|
295
|
+
Promise.resolve(
|
|
296
|
+
styles.map( ( style: StyleDefinition ) => ( {
|
|
297
|
+
id: style.id,
|
|
298
|
+
breakpoint: style?.variants[ 0 ]?.meta.breakpoint || 'desktop',
|
|
299
|
+
} ) )
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
jest.mocked( useStyleRenderer ).mockReturnValue( renderStylesMock );
|
|
304
|
+
|
|
305
|
+
const mockProvider = createMockStylesProvider( { key: 'provider1', priority: 1 }, [
|
|
306
|
+
createMockStyleDefinitionWithVariants( {
|
|
307
|
+
id: 'style1',
|
|
308
|
+
variants: [
|
|
309
|
+
{ meta: { breakpoint: null, state: null }, props: { padding: '10px' }, custom_css: null },
|
|
310
|
+
{ meta: { breakpoint: 'tablet', state: null }, props: { padding: '8px' }, custom_css: null },
|
|
311
|
+
{ meta: { breakpoint: 'mobile', state: null }, props: { padding: '5px' }, custom_css: null },
|
|
312
|
+
],
|
|
313
|
+
} ),
|
|
314
|
+
] );
|
|
315
|
+
|
|
316
|
+
jest.mocked( stylesRepository ).getProviders.mockReturnValue( [ mockProvider ] );
|
|
317
|
+
|
|
318
|
+
// Act - initial render.
|
|
319
|
+
const { result } = renderHook( () => useStyleItems() );
|
|
320
|
+
|
|
321
|
+
await act( async () => {
|
|
322
|
+
mockProvider.actions.updateProps?.( {
|
|
323
|
+
id: 'style1',
|
|
324
|
+
meta: { breakpoint: 'tablet', state: null },
|
|
325
|
+
props: { padding: '12px' },
|
|
326
|
+
} );
|
|
327
|
+
} );
|
|
328
|
+
|
|
329
|
+
// Assert - items should be ordered by breakpoint (desktop, tablet, mobile).
|
|
330
|
+
const breakpointOrder = result.current.map( ( item ) => item.breakpoint );
|
|
331
|
+
expect( breakpointOrder ).toEqual( [ 'desktop', 'tablet', 'mobile' ] );
|
|
332
|
+
|
|
333
|
+
// Act - update again (should maintain order).
|
|
334
|
+
await act( async () => {
|
|
335
|
+
mockProvider.actions.update?.( { id: 'style1', label: 'Updated' } );
|
|
336
|
+
} );
|
|
337
|
+
|
|
338
|
+
// Assert - order should still be maintained.
|
|
339
|
+
const breakpointOrderAfterUpdate = result.current.map( ( item ) => item.breakpoint );
|
|
340
|
+
expect( breakpointOrderAfterUpdate ).toEqual( [ 'desktop', 'tablet', 'mobile' ] );
|
|
341
|
+
} );
|
|
342
|
+
|
|
286
343
|
it( 'should only re-render changed styles on differential update', async () => {
|
|
287
344
|
// Arrange.
|
|
288
345
|
const renderStylesMock = jest.fn().mockImplementation( ( { styles } ) =>
|
|
@@ -181,7 +181,7 @@ function createProviderSubscriber( { provider, renderStyles, setStyleItems, cach
|
|
|
181
181
|
async function createItems( signal: AbortSignal ) {
|
|
182
182
|
const allStyles = provider.actions.all();
|
|
183
183
|
|
|
184
|
-
const styles = allStyles.reverse().map( ( style ) => {
|
|
184
|
+
const styles = [ ...allStyles ].reverse().map( ( style ) => {
|
|
185
185
|
return {
|
|
186
186
|
...style,
|
|
187
187
|
cssName: provider.actions.resolveCssName( style.id ),
|
|
@@ -191,7 +191,7 @@ function createProviderSubscriber( { provider, renderStyles, setStyleItems, cach
|
|
|
191
191
|
return renderStyles( { styles: breakToBreakpoints( styles ), signal } ).then( ( rendered ) => {
|
|
192
192
|
rebuildCache( cache, allStyles, rendered );
|
|
193
193
|
|
|
194
|
-
return
|
|
194
|
+
return getOrderedItems( cache );
|
|
195
195
|
} );
|
|
196
196
|
}
|
|
197
197
|
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { BREAKPOINTS_SCHEMA_URI } from './mcp/resources/breakpoints-resource';
|
|
|
2
2
|
export { STYLE_SCHEMA_URI } from './mcp/resources/widgets-schema-resource';
|
|
3
3
|
|
|
4
4
|
export { init } from './init';
|
|
5
|
-
export { isAtomicWidget } from './
|
|
5
|
+
export { isAtomicWidget } from './utils/command-utils';
|
|
6
6
|
|
|
7
7
|
export {
|
|
8
8
|
createTemplatedElementView,
|
|
@@ -61,13 +61,14 @@ export function createNestedTemplatedElementType( {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function buildEditorAttributes( model:
|
|
64
|
+
function buildEditorAttributes( model: ElementView[ 'model' ] ): string {
|
|
65
65
|
const id = model.get( 'id' );
|
|
66
|
+
const originId = model.get( 'originId' );
|
|
66
67
|
const cid = model.cid ?? '';
|
|
67
68
|
|
|
68
69
|
const attrs: Record< string, string > = {
|
|
69
70
|
'data-model-cid': cid,
|
|
70
|
-
'data-interaction-id': id,
|
|
71
|
+
'data-interaction-id': originId ?? id,
|
|
71
72
|
'x-ignore': 'true',
|
|
72
73
|
};
|
|
73
74
|
|
|
@@ -116,6 +117,10 @@ export function createNestedTemplatedElementView( {
|
|
|
116
117
|
this._lastResolvedSettingsHash = null;
|
|
117
118
|
},
|
|
118
119
|
|
|
120
|
+
renderOnChange() {
|
|
121
|
+
this.render();
|
|
122
|
+
},
|
|
123
|
+
|
|
119
124
|
render() {
|
|
120
125
|
this._abortController?.abort();
|
|
121
126
|
this._abortController = new AbortController();
|
|
@@ -181,6 +186,7 @@ export function createNestedTemplatedElementView( {
|
|
|
181
186
|
|
|
182
187
|
const context = {
|
|
183
188
|
id: model.get( 'id' ),
|
|
189
|
+
interaction_id: this.getInteractionId(),
|
|
184
190
|
type,
|
|
185
191
|
settings,
|
|
186
192
|
base_styles: baseStylesDictionary,
|
|
@@ -361,5 +367,12 @@ export function createNestedTemplatedElementView( {
|
|
|
361
367
|
_openEditingPanel( options?: { scrollIntoView: boolean } ) {
|
|
362
368
|
this._doAfterRender( () => parentOpenEditingPanel.call( this, options ) );
|
|
363
369
|
},
|
|
370
|
+
|
|
371
|
+
getInteractionId() {
|
|
372
|
+
const originId = this.model.get( 'originId' );
|
|
373
|
+
const id = this.model.get( 'id' );
|
|
374
|
+
|
|
375
|
+
return originId ?? id;
|
|
376
|
+
},
|
|
364
377
|
} ) as unknown as typeof ElementView;
|
|
365
378
|
}
|
|
@@ -162,6 +162,7 @@ export function createTemplatedElementView( {
|
|
|
162
162
|
|
|
163
163
|
const context = {
|
|
164
164
|
id: this.model.get( 'id' ),
|
|
165
|
+
interaction_id: this.getInteractionId(),
|
|
165
166
|
type,
|
|
166
167
|
settings,
|
|
167
168
|
base_styles: baseStylesDictionary,
|
|
@@ -206,5 +207,12 @@ export function createTemplatedElementView( {
|
|
|
206
207
|
_openEditingPanel( options?: { scrollIntoView: boolean } ) {
|
|
207
208
|
this._doAfterRender( () => super._openEditingPanel( options ) );
|
|
208
209
|
}
|
|
210
|
+
|
|
211
|
+
getInteractionId() {
|
|
212
|
+
const originId = this.model.get( 'originId' );
|
|
213
|
+
const id = this.model.get( 'id' );
|
|
214
|
+
|
|
215
|
+
return originId ?? id;
|
|
216
|
+
}
|
|
209
217
|
};
|
|
210
218
|
}
|
package/src/legacy/types.ts
CHANGED
|
@@ -203,6 +203,7 @@ type BackboneCollection< Model extends object > = {
|
|
|
203
203
|
|
|
204
204
|
export type ElementModel = {
|
|
205
205
|
id: string;
|
|
206
|
+
originId?: string;
|
|
206
207
|
elType: string;
|
|
207
208
|
settings: BackboneModel< Props >;
|
|
208
209
|
editor_settings: Record< string, unknown >;
|
|
@@ -223,13 +224,14 @@ type ContextMenuGroup = {
|
|
|
223
224
|
actions: ContextMenuAction[];
|
|
224
225
|
};
|
|
225
226
|
|
|
227
|
+
export type ContextMenuEventData = { location: string; secondaryLocation: string; trigger: string };
|
|
226
228
|
export type ContextMenuAction = {
|
|
227
229
|
name: string;
|
|
228
230
|
icon: string;
|
|
229
231
|
title: string | ( () => string );
|
|
230
232
|
shortcut?: string;
|
|
231
233
|
isEnabled: () => boolean;
|
|
232
|
-
callback: ( _: unknown, eventData:
|
|
234
|
+
callback: ( _: unknown, eventData: ContextMenuEventData ) => void;
|
|
233
235
|
};
|
|
234
236
|
|
|
235
237
|
export type ReplacementSettings = {
|
package/src/mcp/canvas-mcp.ts
CHANGED
|
@@ -10,14 +10,12 @@ import { initGetElementConfigTool } from './tools/get-element-config/tool';
|
|
|
10
10
|
export const initCanvasMcp = ( reg: MCPRegistryEntry ) => {
|
|
11
11
|
const { setMCPDescription } = reg;
|
|
12
12
|
setMCPDescription(
|
|
13
|
-
`Everything related to
|
|
13
|
+
`Everything related to V4 ( Atomic ) canvas.
|
|
14
14
|
# Canvas workflow for new compositions
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
- Build valid XML with minimal inline styles (layout/positioning only)
|
|
20
|
-
- Apply global classes to elements`
|
|
15
|
+
- Configure elements settings and styles
|
|
16
|
+
- Build compositions/sections out of V4 atomic elements using context aware designs using the website resources
|
|
17
|
+
- Get and retrieve element configuration values
|
|
18
|
+
`
|
|
21
19
|
);
|
|
22
20
|
initWidgetsSchemaResource( reg );
|
|
23
21
|
initDocumentStructureResource( reg );
|
|
@@ -5,7 +5,7 @@ import { v1ReadyEvent } from '@elementor/editor-v1-adapters';
|
|
|
5
5
|
export const BREAKPOINTS_SCHEMA_URI = 'elementor://breakpoints/list';
|
|
6
6
|
|
|
7
7
|
export const initBreakpointsResource = ( reg: MCPRegistryEntry ) => {
|
|
8
|
-
const {
|
|
8
|
+
const { resource, sendResourceUpdated } = reg;
|
|
9
9
|
|
|
10
10
|
const getBreakpointsList = () => {
|
|
11
11
|
const { breakpoints } = ( window as unknown as ExtendedWindow ).elementor?.config?.responsive || {};
|
|
@@ -34,9 +34,16 @@ export const initBreakpointsResource = ( reg: MCPRegistryEntry ) => {
|
|
|
34
34
|
],
|
|
35
35
|
} );
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
resource(
|
|
38
|
+
'breakpoints ',
|
|
39
|
+
BREAKPOINTS_SCHEMA_URI,
|
|
40
|
+
{
|
|
41
|
+
description: 'Breakpoints list.',
|
|
42
|
+
},
|
|
43
|
+
() => {
|
|
44
|
+
return buildResourceResponse();
|
|
45
|
+
}
|
|
46
|
+
);
|
|
40
47
|
|
|
41
48
|
window.addEventListener( v1ReadyEvent().name, () => {
|
|
42
49
|
sendResourceUpdated( {
|
|
@@ -39,7 +39,7 @@ type ElementorContainer = {
|
|
|
39
39
|
export const DOCUMENT_STRUCTURE_URI = 'elementor://document/structure';
|
|
40
40
|
|
|
41
41
|
export const initDocumentStructureResource = ( reg: MCPRegistryEntry ) => {
|
|
42
|
-
const {
|
|
42
|
+
const { resource, sendResourceUpdated } = reg;
|
|
43
43
|
|
|
44
44
|
let currentDocumentStructure: string | null = null;
|
|
45
45
|
|
|
@@ -69,18 +69,23 @@ export const initDocumentStructureResource = ( reg: MCPRegistryEntry ) => {
|
|
|
69
69
|
// Initialize on load
|
|
70
70
|
updateDocumentStructure();
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
resource(
|
|
73
|
+
'document-structure',
|
|
74
|
+
DOCUMENT_STRUCTURE_URI,
|
|
75
|
+
{
|
|
76
|
+
description: 'Document structure.',
|
|
77
|
+
},
|
|
78
|
+
async () => {
|
|
79
|
+
return {
|
|
80
|
+
contents: [
|
|
81
|
+
{
|
|
82
|
+
uri: DOCUMENT_STRUCTURE_URI,
|
|
83
|
+
text: JSON.stringify( getDocumentStructure(), null, 2 ),
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
);
|
|
84
89
|
};
|
|
85
90
|
|
|
86
91
|
function getDocumentStructure() {
|