@elementor/editor-canvas 3.35.0-434 → 3.35.0-436
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 +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +38 -7
- package/dist/index.mjs +38 -7
- package/package.json +18 -18
- package/src/legacy/__tests__/create-templated-element-type.test.ts +138 -2
- package/src/legacy/create-templated-element-type.ts +46 -10
- package/src/legacy/replacements/manager.ts +2 -1
- package/src/legacy/types.ts +2 -0
package/dist/index.d.mts
CHANGED
|
@@ -100,6 +100,7 @@ declare class ElementView {
|
|
|
100
100
|
getRenderContext(): NamespacedRenderContext | undefined;
|
|
101
101
|
getResolverRenderContext(): RenderContext | undefined;
|
|
102
102
|
getNamespaceKey(): string;
|
|
103
|
+
invalidateRenderCache(): void;
|
|
103
104
|
}
|
|
104
105
|
type JQueryElement = {
|
|
105
106
|
find: (selector: string) => JQueryElement;
|
package/dist/index.d.ts
CHANGED
|
@@ -100,6 +100,7 @@ declare class ElementView {
|
|
|
100
100
|
getRenderContext(): NamespacedRenderContext | undefined;
|
|
101
101
|
getResolverRenderContext(): RenderContext | undefined;
|
|
102
102
|
getNamespaceKey(): string;
|
|
103
|
+
invalidateRenderCache(): void;
|
|
103
104
|
}
|
|
104
105
|
type JQueryElement = {
|
|
105
106
|
find: (selector: string) => JQueryElement;
|
package/dist/index.js
CHANGED
|
@@ -1605,6 +1605,8 @@ function createTemplatedElementView({
|
|
|
1605
1605
|
return class extends BaseView {
|
|
1606
1606
|
#abortController = null;
|
|
1607
1607
|
#childrenRenderPromises = [];
|
|
1608
|
+
#lastResolvedSettingsHash = null;
|
|
1609
|
+
#domUpdateWasSkipped = false;
|
|
1608
1610
|
getTemplateType() {
|
|
1609
1611
|
return "twig";
|
|
1610
1612
|
}
|
|
@@ -1620,8 +1622,9 @@ function createTemplatedElementView({
|
|
|
1620
1622
|
getResolverRenderContext() {
|
|
1621
1623
|
return this._parent?.getResolverRenderContext?.();
|
|
1622
1624
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
+
invalidateRenderCache() {
|
|
1626
|
+
this.#lastResolvedSettingsHash = null;
|
|
1627
|
+
}
|
|
1625
1628
|
render() {
|
|
1626
1629
|
this.#abortController?.abort();
|
|
1627
1630
|
this.#abortController = new AbortController();
|
|
@@ -1630,21 +1633,35 @@ function createTemplatedElementView({
|
|
|
1630
1633
|
return this._currentRenderPromise;
|
|
1631
1634
|
}
|
|
1632
1635
|
async _renderChildren() {
|
|
1633
|
-
super._renderChildren();
|
|
1634
1636
|
this.#childrenRenderPromises = [];
|
|
1637
|
+
if (this.#shouldReuseChildren()) {
|
|
1638
|
+
this.#rerenderExistingChildren();
|
|
1639
|
+
} else {
|
|
1640
|
+
super._renderChildren();
|
|
1641
|
+
}
|
|
1642
|
+
this.#collectChildrenRenderPromises();
|
|
1643
|
+
await this._waitForChildrenToComplete();
|
|
1644
|
+
}
|
|
1645
|
+
#shouldReuseChildren() {
|
|
1646
|
+
return this.#domUpdateWasSkipped && this.children?.length > 0;
|
|
1647
|
+
}
|
|
1648
|
+
#rerenderExistingChildren() {
|
|
1649
|
+
this.children?.each((childView) => {
|
|
1650
|
+
childView.render();
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
#collectChildrenRenderPromises() {
|
|
1635
1654
|
this.children?.each((childView) => {
|
|
1636
1655
|
if (childView._currentRenderPromise) {
|
|
1637
1656
|
this.#childrenRenderPromises.push(childView._currentRenderPromise);
|
|
1638
1657
|
}
|
|
1639
1658
|
});
|
|
1640
|
-
await this._waitForChildrenToComplete();
|
|
1641
1659
|
}
|
|
1642
1660
|
async _waitForChildrenToComplete() {
|
|
1643
1661
|
if (this.#childrenRenderPromises.length > 0) {
|
|
1644
1662
|
await Promise.all(this.#childrenRenderPromises);
|
|
1645
1663
|
}
|
|
1646
1664
|
}
|
|
1647
|
-
// Overriding Marionette original `_renderTemplate` method to inject our renderer.
|
|
1648
1665
|
async _renderTemplate() {
|
|
1649
1666
|
this.triggerMethod("before:render:template");
|
|
1650
1667
|
const process = signalizedProcess(this.#abortController?.signal).then((_, signal) => {
|
|
@@ -1657,6 +1674,14 @@ function createTemplatedElementView({
|
|
|
1657
1674
|
}).then((settings) => {
|
|
1658
1675
|
return this.afterSettingsResolve(settings);
|
|
1659
1676
|
}).then(async (settings) => {
|
|
1677
|
+
const settingsHash = JSON.stringify(settings);
|
|
1678
|
+
const settingsChanged = settingsHash !== this.#lastResolvedSettingsHash;
|
|
1679
|
+
if (!settingsChanged && this.isRendered) {
|
|
1680
|
+
this.#domUpdateWasSkipped = true;
|
|
1681
|
+
return null;
|
|
1682
|
+
}
|
|
1683
|
+
this.#domUpdateWasSkipped = false;
|
|
1684
|
+
this.#lastResolvedSettingsHash = settingsHash;
|
|
1660
1685
|
const context = {
|
|
1661
1686
|
id: this.model.get("id"),
|
|
1662
1687
|
type,
|
|
@@ -1664,7 +1689,12 @@ function createTemplatedElementView({
|
|
|
1664
1689
|
base_styles: baseStylesDictionary
|
|
1665
1690
|
};
|
|
1666
1691
|
return renderer.render(templateKey, context);
|
|
1667
|
-
}).then((html) =>
|
|
1692
|
+
}).then((html) => {
|
|
1693
|
+
if (html === null) {
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
this.$el.html(html);
|
|
1697
|
+
});
|
|
1668
1698
|
await process.execute();
|
|
1669
1699
|
this.bindUIElements();
|
|
1670
1700
|
this.triggerMethod("render:template");
|
|
@@ -1980,10 +2010,11 @@ var createViewWithReplacements = (options) => {
|
|
|
1980
2010
|
element: this.el,
|
|
1981
2011
|
type: this?.model?.get("widgetType") ?? this.container?.model?.get("elType") ?? null,
|
|
1982
2012
|
id: this?.model?.get("id") ?? null,
|
|
1983
|
-
refreshView: this.
|
|
2013
|
+
refreshView: this.refreshView.bind(this)
|
|
1984
2014
|
};
|
|
1985
2015
|
}
|
|
1986
2016
|
refreshView() {
|
|
2017
|
+
this.invalidateRenderCache?.();
|
|
1987
2018
|
this.render();
|
|
1988
2019
|
}
|
|
1989
2020
|
renderOnChange() {
|
package/dist/index.mjs
CHANGED
|
@@ -1564,6 +1564,8 @@ function createTemplatedElementView({
|
|
|
1564
1564
|
return class extends BaseView {
|
|
1565
1565
|
#abortController = null;
|
|
1566
1566
|
#childrenRenderPromises = [];
|
|
1567
|
+
#lastResolvedSettingsHash = null;
|
|
1568
|
+
#domUpdateWasSkipped = false;
|
|
1567
1569
|
getTemplateType() {
|
|
1568
1570
|
return "twig";
|
|
1569
1571
|
}
|
|
@@ -1579,8 +1581,9 @@ function createTemplatedElementView({
|
|
|
1579
1581
|
getResolverRenderContext() {
|
|
1580
1582
|
return this._parent?.getResolverRenderContext?.();
|
|
1581
1583
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
+
invalidateRenderCache() {
|
|
1585
|
+
this.#lastResolvedSettingsHash = null;
|
|
1586
|
+
}
|
|
1584
1587
|
render() {
|
|
1585
1588
|
this.#abortController?.abort();
|
|
1586
1589
|
this.#abortController = new AbortController();
|
|
@@ -1589,21 +1592,35 @@ function createTemplatedElementView({
|
|
|
1589
1592
|
return this._currentRenderPromise;
|
|
1590
1593
|
}
|
|
1591
1594
|
async _renderChildren() {
|
|
1592
|
-
super._renderChildren();
|
|
1593
1595
|
this.#childrenRenderPromises = [];
|
|
1596
|
+
if (this.#shouldReuseChildren()) {
|
|
1597
|
+
this.#rerenderExistingChildren();
|
|
1598
|
+
} else {
|
|
1599
|
+
super._renderChildren();
|
|
1600
|
+
}
|
|
1601
|
+
this.#collectChildrenRenderPromises();
|
|
1602
|
+
await this._waitForChildrenToComplete();
|
|
1603
|
+
}
|
|
1604
|
+
#shouldReuseChildren() {
|
|
1605
|
+
return this.#domUpdateWasSkipped && this.children?.length > 0;
|
|
1606
|
+
}
|
|
1607
|
+
#rerenderExistingChildren() {
|
|
1608
|
+
this.children?.each((childView) => {
|
|
1609
|
+
childView.render();
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
#collectChildrenRenderPromises() {
|
|
1594
1613
|
this.children?.each((childView) => {
|
|
1595
1614
|
if (childView._currentRenderPromise) {
|
|
1596
1615
|
this.#childrenRenderPromises.push(childView._currentRenderPromise);
|
|
1597
1616
|
}
|
|
1598
1617
|
});
|
|
1599
|
-
await this._waitForChildrenToComplete();
|
|
1600
1618
|
}
|
|
1601
1619
|
async _waitForChildrenToComplete() {
|
|
1602
1620
|
if (this.#childrenRenderPromises.length > 0) {
|
|
1603
1621
|
await Promise.all(this.#childrenRenderPromises);
|
|
1604
1622
|
}
|
|
1605
1623
|
}
|
|
1606
|
-
// Overriding Marionette original `_renderTemplate` method to inject our renderer.
|
|
1607
1624
|
async _renderTemplate() {
|
|
1608
1625
|
this.triggerMethod("before:render:template");
|
|
1609
1626
|
const process = signalizedProcess(this.#abortController?.signal).then((_, signal) => {
|
|
@@ -1616,6 +1633,14 @@ function createTemplatedElementView({
|
|
|
1616
1633
|
}).then((settings) => {
|
|
1617
1634
|
return this.afterSettingsResolve(settings);
|
|
1618
1635
|
}).then(async (settings) => {
|
|
1636
|
+
const settingsHash = JSON.stringify(settings);
|
|
1637
|
+
const settingsChanged = settingsHash !== this.#lastResolvedSettingsHash;
|
|
1638
|
+
if (!settingsChanged && this.isRendered) {
|
|
1639
|
+
this.#domUpdateWasSkipped = true;
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
this.#domUpdateWasSkipped = false;
|
|
1643
|
+
this.#lastResolvedSettingsHash = settingsHash;
|
|
1619
1644
|
const context = {
|
|
1620
1645
|
id: this.model.get("id"),
|
|
1621
1646
|
type,
|
|
@@ -1623,7 +1648,12 @@ function createTemplatedElementView({
|
|
|
1623
1648
|
base_styles: baseStylesDictionary
|
|
1624
1649
|
};
|
|
1625
1650
|
return renderer.render(templateKey, context);
|
|
1626
|
-
}).then((html) =>
|
|
1651
|
+
}).then((html) => {
|
|
1652
|
+
if (html === null) {
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
this.$el.html(html);
|
|
1656
|
+
});
|
|
1627
1657
|
await process.execute();
|
|
1628
1658
|
this.bindUIElements();
|
|
1629
1659
|
this.triggerMethod("render:template");
|
|
@@ -1942,10 +1972,11 @@ var createViewWithReplacements = (options) => {
|
|
|
1942
1972
|
element: this.el,
|
|
1943
1973
|
type: this?.model?.get("widgetType") ?? this.container?.model?.get("elType") ?? null,
|
|
1944
1974
|
id: this?.model?.get("id") ?? null,
|
|
1945
|
-
refreshView: this.
|
|
1975
|
+
refreshView: this.refreshView.bind(this)
|
|
1946
1976
|
};
|
|
1947
1977
|
}
|
|
1948
1978
|
refreshView() {
|
|
1979
|
+
this.invalidateRenderCache?.();
|
|
1949
1980
|
this.render();
|
|
1950
1981
|
}
|
|
1951
1982
|
renderOnChange() {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-canvas",
|
|
3
3
|
"description": "Elementor Editor Canvas",
|
|
4
|
-
"version": "3.35.0-
|
|
4
|
+
"version": "3.35.0-436",
|
|
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": "3.35.0-
|
|
41
|
-
"@elementor/editor-controls": "3.35.0-
|
|
42
|
-
"@elementor/editor-documents": "3.35.0-
|
|
43
|
-
"@elementor/editor-elements": "3.35.0-
|
|
44
|
-
"@elementor/editor-interactions": "3.35.0-
|
|
45
|
-
"@elementor/editor-mcp": "3.35.0-
|
|
46
|
-
"@elementor/editor-notifications": "3.35.0-
|
|
47
|
-
"@elementor/editor-props": "3.35.0-
|
|
48
|
-
"@elementor/editor-responsive": "3.35.0-
|
|
49
|
-
"@elementor/editor-styles": "3.35.0-
|
|
50
|
-
"@elementor/editor-styles-repository": "3.35.0-
|
|
51
|
-
"@elementor/editor-ui": "3.35.0-
|
|
52
|
-
"@elementor/editor-v1-adapters": "3.35.0-
|
|
53
|
-
"@elementor/schema": "3.35.0-
|
|
54
|
-
"@elementor/twing": "3.35.0-
|
|
40
|
+
"@elementor/editor": "3.35.0-436",
|
|
41
|
+
"@elementor/editor-controls": "3.35.0-436",
|
|
42
|
+
"@elementor/editor-documents": "3.35.0-436",
|
|
43
|
+
"@elementor/editor-elements": "3.35.0-436",
|
|
44
|
+
"@elementor/editor-interactions": "3.35.0-436",
|
|
45
|
+
"@elementor/editor-mcp": "3.35.0-436",
|
|
46
|
+
"@elementor/editor-notifications": "3.35.0-436",
|
|
47
|
+
"@elementor/editor-props": "3.35.0-436",
|
|
48
|
+
"@elementor/editor-responsive": "3.35.0-436",
|
|
49
|
+
"@elementor/editor-styles": "3.35.0-436",
|
|
50
|
+
"@elementor/editor-styles-repository": "3.35.0-436",
|
|
51
|
+
"@elementor/editor-ui": "3.35.0-436",
|
|
52
|
+
"@elementor/editor-v1-adapters": "3.35.0-436",
|
|
53
|
+
"@elementor/schema": "3.35.0-436",
|
|
54
|
+
"@elementor/twing": "3.35.0-436",
|
|
55
55
|
"@elementor/ui": "1.36.17",
|
|
56
|
-
"@elementor/utils": "3.35.0-
|
|
57
|
-
"@elementor/wp-media": "3.35.0-
|
|
56
|
+
"@elementor/utils": "3.35.0-436",
|
|
57
|
+
"@elementor/wp-media": "3.35.0-436",
|
|
58
58
|
"@floating-ui/react": "^0.27.5",
|
|
59
59
|
"@wordpress/i18n": "^5.13.0"
|
|
60
60
|
},
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { mockLegacyElementor } from 'test-utils';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
canBeTemplated,
|
|
5
|
+
createTemplatedElementType,
|
|
6
|
+
createTemplatedElementView,
|
|
7
|
+
} from '../create-templated-element-type';
|
|
4
8
|
|
|
5
9
|
const MOCK_ELEMENT_TYPE = 'test-element';
|
|
10
|
+
const MOCK_HTML = '<div>Element</div>';
|
|
6
11
|
|
|
7
12
|
const createMockRenderer = () => ( {
|
|
8
13
|
register: jest.fn(),
|
|
9
|
-
render: jest.fn( () => Promise.resolve(
|
|
14
|
+
render: jest.fn( () => Promise.resolve( MOCK_HTML ) ),
|
|
10
15
|
} );
|
|
11
16
|
|
|
12
17
|
const createMockElementConfig = () => ( {
|
|
@@ -54,3 +59,134 @@ describe( 'createTemplatedElementType', () => {
|
|
|
54
59
|
expect( viewClass1 ).toBe( viewClass2 );
|
|
55
60
|
} );
|
|
56
61
|
} );
|
|
62
|
+
|
|
63
|
+
describe( 'createTemplatedElementView', () => {
|
|
64
|
+
beforeEach( () => {
|
|
65
|
+
mockLegacyElementor();
|
|
66
|
+
} );
|
|
67
|
+
|
|
68
|
+
describe( 'class structure', () => {
|
|
69
|
+
it( 'should return twig as template type', () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
const ViewClass = createTemplatedElementView( {
|
|
72
|
+
type: MOCK_ELEMENT_TYPE,
|
|
73
|
+
renderer: createMockRenderer(),
|
|
74
|
+
element: createMockElementConfig(),
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
// Assert
|
|
78
|
+
expect( ViewClass.prototype.getTemplateType() ).toBe( 'twig' );
|
|
79
|
+
} );
|
|
80
|
+
|
|
81
|
+
it( 'should return element type as namespace key', () => {
|
|
82
|
+
// Arrange
|
|
83
|
+
const ViewClass = createTemplatedElementView( {
|
|
84
|
+
type: MOCK_ELEMENT_TYPE,
|
|
85
|
+
renderer: createMockRenderer(),
|
|
86
|
+
element: createMockElementConfig(),
|
|
87
|
+
} );
|
|
88
|
+
|
|
89
|
+
// Assert
|
|
90
|
+
expect( ViewClass.prototype.getNamespaceKey() ).toBe( MOCK_ELEMENT_TYPE );
|
|
91
|
+
} );
|
|
92
|
+
} );
|
|
93
|
+
|
|
94
|
+
describe( 'template registration', () => {
|
|
95
|
+
it( 'should register templates with the renderer', () => {
|
|
96
|
+
// Arrange
|
|
97
|
+
const utils = createMockRenderer();
|
|
98
|
+
const elementConfig = {
|
|
99
|
+
...createMockElementConfig(),
|
|
100
|
+
twig_templates: {
|
|
101
|
+
template1: '<div>Template 1</div>',
|
|
102
|
+
template2: '<div>Template 2</div>',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Act
|
|
107
|
+
createTemplatedElementView( {
|
|
108
|
+
type: MOCK_ELEMENT_TYPE,
|
|
109
|
+
renderer: utils,
|
|
110
|
+
element: elementConfig,
|
|
111
|
+
} );
|
|
112
|
+
|
|
113
|
+
// Assert
|
|
114
|
+
expect( utils.register ).toHaveBeenCalledTimes( 2 );
|
|
115
|
+
expect( utils.register ).toHaveBeenCalledWith( 'template1', '<div>Template 1</div>' );
|
|
116
|
+
expect( utils.register ).toHaveBeenCalledWith( 'template2', '<div>Template 2</div>' );
|
|
117
|
+
} );
|
|
118
|
+
} );
|
|
119
|
+
} );
|
|
120
|
+
|
|
121
|
+
describe( 'canBeTemplated', () => {
|
|
122
|
+
it( 'should return true when all required properties are present', () => {
|
|
123
|
+
// Arrange
|
|
124
|
+
const element = createMockElementConfig();
|
|
125
|
+
|
|
126
|
+
// Act
|
|
127
|
+
const result = canBeTemplated( element );
|
|
128
|
+
|
|
129
|
+
// Assert
|
|
130
|
+
expect( result ).toBe( true );
|
|
131
|
+
} );
|
|
132
|
+
|
|
133
|
+
it( 'should return false when atomic_props_schema is missing', () => {
|
|
134
|
+
// Arrange
|
|
135
|
+
const element = {
|
|
136
|
+
twig_templates: {},
|
|
137
|
+
twig_main_template: 'main',
|
|
138
|
+
base_styles_dictionary: {},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Act
|
|
142
|
+
const result = canBeTemplated( element );
|
|
143
|
+
|
|
144
|
+
// Assert
|
|
145
|
+
expect( result ).toBe( false );
|
|
146
|
+
} );
|
|
147
|
+
|
|
148
|
+
it( 'should return false when twig_templates is missing', () => {
|
|
149
|
+
// Arrange
|
|
150
|
+
const element = {
|
|
151
|
+
twig_main_template: 'main',
|
|
152
|
+
atomic_props_schema: {},
|
|
153
|
+
base_styles_dictionary: {},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Act
|
|
157
|
+
const result = canBeTemplated( element );
|
|
158
|
+
|
|
159
|
+
// Assert
|
|
160
|
+
expect( result ).toBe( false );
|
|
161
|
+
} );
|
|
162
|
+
|
|
163
|
+
it( 'should return false when twig_main_template is missing', () => {
|
|
164
|
+
// Arrange
|
|
165
|
+
const element = {
|
|
166
|
+
twig_templates: {},
|
|
167
|
+
atomic_props_schema: {},
|
|
168
|
+
base_styles_dictionary: {},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Act
|
|
172
|
+
const result = canBeTemplated( element );
|
|
173
|
+
|
|
174
|
+
// Assert
|
|
175
|
+
expect( result ).toBe( false );
|
|
176
|
+
} );
|
|
177
|
+
|
|
178
|
+
it( 'should return false when base_styles_dictionary is missing', () => {
|
|
179
|
+
// Arrange
|
|
180
|
+
const element = {
|
|
181
|
+
twig_templates: {},
|
|
182
|
+
twig_main_template: 'main',
|
|
183
|
+
atomic_props_schema: {},
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Act
|
|
187
|
+
const result = canBeTemplated( element );
|
|
188
|
+
|
|
189
|
+
// Assert
|
|
190
|
+
expect( result ).toBe( false );
|
|
191
|
+
} );
|
|
192
|
+
} );
|
|
@@ -79,6 +79,8 @@ export function createTemplatedElementView( {
|
|
|
79
79
|
return class extends BaseView {
|
|
80
80
|
#abortController: AbortController | null = null;
|
|
81
81
|
#childrenRenderPromises: Promise< void >[] = [];
|
|
82
|
+
#lastResolvedSettingsHash: string | null = null;
|
|
83
|
+
#domUpdateWasSkipped = false;
|
|
82
84
|
|
|
83
85
|
getTemplateType() {
|
|
84
86
|
return 'twig';
|
|
@@ -100,8 +102,10 @@ export function createTemplatedElementView( {
|
|
|
100
102
|
return this._parent?.getResolverRenderContext?.();
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
invalidateRenderCache() {
|
|
106
|
+
this.#lastResolvedSettingsHash = null;
|
|
107
|
+
}
|
|
108
|
+
|
|
105
109
|
render() {
|
|
106
110
|
this.#abortController?.abort();
|
|
107
111
|
this.#abortController = new AbortController();
|
|
@@ -118,17 +122,35 @@ export function createTemplatedElementView( {
|
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
async _renderChildren() {
|
|
121
|
-
super._renderChildren();
|
|
122
|
-
|
|
123
125
|
this.#childrenRenderPromises = [];
|
|
124
126
|
|
|
127
|
+
// Optimize rendering by reusing existing child views instead of recreating them.
|
|
128
|
+
if ( this.#shouldReuseChildren() ) {
|
|
129
|
+
this.#rerenderExistingChildren();
|
|
130
|
+
} else {
|
|
131
|
+
super._renderChildren();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.#collectChildrenRenderPromises();
|
|
135
|
+
await this._waitForChildrenToComplete();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#shouldReuseChildren() {
|
|
139
|
+
return this.#domUpdateWasSkipped && this.children?.length > 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#rerenderExistingChildren() {
|
|
143
|
+
this.children?.each( ( childView: ElementView ) => {
|
|
144
|
+
childView.render();
|
|
145
|
+
} );
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#collectChildrenRenderPromises() {
|
|
125
149
|
this.children?.each( ( childView: ElementView ) => {
|
|
126
150
|
if ( childView._currentRenderPromise ) {
|
|
127
151
|
this.#childrenRenderPromises.push( childView._currentRenderPromise );
|
|
128
152
|
}
|
|
129
153
|
} );
|
|
130
|
-
|
|
131
|
-
await this._waitForChildrenToComplete();
|
|
132
154
|
}
|
|
133
155
|
|
|
134
156
|
async _waitForChildrenToComplete() {
|
|
@@ -137,14 +159,12 @@ export function createTemplatedElementView( {
|
|
|
137
159
|
}
|
|
138
160
|
}
|
|
139
161
|
|
|
140
|
-
// Overriding Marionette original `_renderTemplate` method to inject our renderer.
|
|
141
162
|
async _renderTemplate() {
|
|
142
163
|
this.triggerMethod( 'before:render:template' );
|
|
143
164
|
|
|
144
165
|
const process = signalizedProcess( this.#abortController?.signal as AbortSignal )
|
|
145
166
|
.then( ( _, signal ) => {
|
|
146
167
|
const settings = this.model.get( 'settings' ).toJSON();
|
|
147
|
-
|
|
148
168
|
return resolveProps( {
|
|
149
169
|
props: settings,
|
|
150
170
|
signal,
|
|
@@ -155,7 +175,17 @@ export function createTemplatedElementView( {
|
|
|
155
175
|
return this.afterSettingsResolve( settings );
|
|
156
176
|
} )
|
|
157
177
|
.then( async ( settings ) => {
|
|
158
|
-
|
|
178
|
+
const settingsHash = JSON.stringify( settings );
|
|
179
|
+
const settingsChanged = settingsHash !== this.#lastResolvedSettingsHash;
|
|
180
|
+
|
|
181
|
+
if ( ! settingsChanged && this.isRendered ) {
|
|
182
|
+
this.#domUpdateWasSkipped = true;
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
this.#domUpdateWasSkipped = false;
|
|
186
|
+
|
|
187
|
+
this.#lastResolvedSettingsHash = settingsHash;
|
|
188
|
+
|
|
159
189
|
const context = {
|
|
160
190
|
id: this.model.get( 'id' ),
|
|
161
191
|
type,
|
|
@@ -165,7 +195,13 @@ export function createTemplatedElementView( {
|
|
|
165
195
|
|
|
166
196
|
return renderer.render( templateKey, context );
|
|
167
197
|
} )
|
|
168
|
-
.then( ( html ) =>
|
|
198
|
+
.then( ( html ) => {
|
|
199
|
+
if ( html === null ) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this.$el.html( html );
|
|
204
|
+
} );
|
|
169
205
|
|
|
170
206
|
await process.execute();
|
|
171
207
|
|
|
@@ -45,11 +45,12 @@ export const createViewWithReplacements = ( options: CreateTemplatedElementTypeO
|
|
|
45
45
|
element: this.el,
|
|
46
46
|
type: this?.model?.get( 'widgetType' ) ?? this.container?.model?.get( 'elType' ) ?? null,
|
|
47
47
|
id: this?.model?.get( 'id' ) ?? null,
|
|
48
|
-
refreshView: this.
|
|
48
|
+
refreshView: this.refreshView.bind( this ),
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
refreshView() {
|
|
53
|
+
this.invalidateRenderCache?.();
|
|
53
54
|
this.render();
|
|
54
55
|
}
|
|
55
56
|
|