Skip to content

Commit bc78857

Browse files
authored
refactor(framework): Deprecate RenderScheduler in favor of Render.js (#2728)
1 parent d8ea023 commit bc78857

32 files changed

+280
-247
lines changed

packages/base/bundle.esm.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import "./dist/test-resources/elements/GenericExt.js";
1515
import "./dist/test-resources/assets/Themes.js";
1616

1717
// used in test pages
18-
import RenderScheduler from "./dist/RenderScheduler.js";
19-
window.RenderScheduler = RenderScheduler;
18+
import { renderFinished } from "./dist/Render.js";
2019
import { isIE } from "./dist/Device.js";
2120
window.isIE = isIE; // attached to the window object for testing purposes
2221

@@ -52,4 +51,5 @@ window["sap-ui-webcomponents-bundle"] = {
5251
registerI18nBundle,
5352
fetchI18nBundle,
5453
getI18nBundle,
54+
renderFinished,
5555
};

packages/base/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"devDependencies": {
3030
"@ui5/webcomponents-tools": "1.0.0-rc.11",
3131
"array-uniq": "^2.0.0",
32-
"chromedriver": "87.0.5",
32+
"chromedriver": "88.0.0",
3333
"copy-and-watch": "^0.1.4",
3434
"eslint": "^5.13.0",
3535
"eslint-config-airbnb-base": "^13.1.0",

packages/base/src/Render.js

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import EventProvider from "./EventProvider.js";
2+
import RenderQueue from "./RenderQueue.js";
3+
import { getAllRegisteredTags } from "./CustomElementsRegistry.js";
4+
import { isRtlAware } from "./locale/RTLAwareRegistry.js";
5+
6+
const registeredElements = new Set();
7+
const eventProvider = new EventProvider();
8+
9+
const invalidatedWebComponents = new RenderQueue(); // Queue for invalidated web components
10+
11+
let renderTaskPromise,
12+
renderTaskPromiseResolve;
13+
14+
let mutationObserverTimer;
15+
16+
let queuePromise;
17+
18+
19+
/**
20+
* Schedules a render task (if not already scheduled) to render the component
21+
*
22+
* @param webComponent
23+
* @returns {Promise}
24+
*/
25+
const renderDeferred = async webComponent => {
26+
// Enqueue the web component
27+
invalidatedWebComponents.add(webComponent);
28+
29+
// Schedule a rendering task
30+
await scheduleRenderTask();
31+
};
32+
33+
/**
34+
* Renders a component synchronously and adds it to the registry of rendered components
35+
*
36+
* @param webComponent
37+
*/
38+
const renderImmediately = webComponent => {
39+
eventProvider.fireEvent("beforeComponentRender", webComponent);
40+
registeredElements.add(webComponent);
41+
webComponent._render();
42+
};
43+
44+
/**
45+
* Cancels the rendering of a component, if awaiting to be rendered, and removes it from the registry of rendered components
46+
*
47+
* @param webComponent
48+
*/
49+
const cancelRender = webComponent => {
50+
invalidatedWebComponents.remove(webComponent);
51+
registeredElements.delete(webComponent);
52+
};
53+
54+
/**
55+
* Schedules a rendering task, if not scheduled already
56+
*/
57+
const scheduleRenderTask = async () => {
58+
if (!queuePromise) {
59+
queuePromise = new Promise(resolve => {
60+
window.requestAnimationFrame(() => {
61+
// Render all components in the queue
62+
63+
// console.log(`--------------------RENDER TASK START------------------------------`); // eslint-disable-line
64+
invalidatedWebComponents.process(renderImmediately);
65+
// console.log(`--------------------RENDER TASK END------------------------------`); // eslint-disable-line
66+
67+
// Resolve the promise so that callers of renderDeferred can continue
68+
queuePromise = null;
69+
resolve();
70+
71+
// Wait for Mutation observer before the render task is considered finished
72+
if (!mutationObserverTimer) {
73+
mutationObserverTimer = setTimeout(() => {
74+
mutationObserverTimer = undefined;
75+
if (invalidatedWebComponents.isEmpty()) {
76+
_resolveTaskPromise();
77+
}
78+
}, 200);
79+
}
80+
});
81+
});
82+
}
83+
84+
await queuePromise;
85+
};
86+
87+
/**
88+
* return a promise that will be resolved once all invalidated web components are rendered
89+
*/
90+
const whenDOMUpdated = () => {
91+
if (renderTaskPromise) {
92+
return renderTaskPromise;
93+
}
94+
95+
renderTaskPromise = new Promise(resolve => {
96+
renderTaskPromiseResolve = resolve;
97+
window.requestAnimationFrame(() => {
98+
if (invalidatedWebComponents.isEmpty()) {
99+
renderTaskPromise = undefined;
100+
resolve();
101+
}
102+
});
103+
});
104+
105+
return renderTaskPromise;
106+
};
107+
108+
const whenAllCustomElementsAreDefined = () => {
109+
const definedPromises = getAllRegisteredTags().map(tag => customElements.whenDefined(tag));
110+
return Promise.all(definedPromises);
111+
};
112+
113+
const renderFinished = async () => {
114+
await whenAllCustomElementsAreDefined();
115+
await whenDOMUpdated();
116+
};
117+
118+
const _resolveTaskPromise = () => {
119+
if (!invalidatedWebComponents.isEmpty()) {
120+
// More updates are pending. Resolve will be called again
121+
return;
122+
}
123+
124+
if (renderTaskPromiseResolve) {
125+
renderTaskPromiseResolve();
126+
renderTaskPromiseResolve = undefined;
127+
renderTaskPromise = undefined;
128+
}
129+
};
130+
131+
/**
132+
* Re-renders all UI5 Elements on the page, with the option to specify filters to rerender only some components.
133+
*
134+
* Usage:
135+
* reRenderAllUI5Elements() -> re-renders all components
136+
* reRenderAllUI5Elements({tag: "ui5-button"}) -> re-renders only instances of ui5-button
137+
* reRenderAllUI5Elements({rtlAware: true}) -> re-renders only rtlAware components
138+
* reRenderAllUI5Elements({languageAware: true}) -> re-renders only languageAware components
139+
* reRenderAllUI5Elements({rtlAware: true, languageAware: true}) -> re-renders components that are rtlAware or languageAware
140+
* etc...
141+
*
142+
* @public
143+
* @param {Object|undefined} filters - Object with keys that can be "rtlAware" or "languageAware"
144+
* @returns {Promise<void>}
145+
*/
146+
const reRenderAllUI5Elements = async filters => {
147+
registeredElements.forEach(element => {
148+
const tag = element.constructor.getMetadata().getTag();
149+
const rtlAware = isRtlAware(element.constructor);
150+
const languageAware = element.constructor.getMetadata().isLanguageAware();
151+
if (!filters || (filters.tag === tag) || (filters.rtlAware && rtlAware) || (filters.languageAware && languageAware)) {
152+
renderDeferred(element);
153+
}
154+
});
155+
await renderFinished();
156+
};
157+
158+
const attachBeforeComponentRender = listener => {
159+
eventProvider.attachEvent("beforeComponentRender", listener);
160+
};
161+
162+
const detachBeforeComponentRender = listener => {
163+
eventProvider.detachEvent("beforeComponentRender", listener);
164+
};
165+
166+
export {
167+
renderDeferred,
168+
renderImmediately,
169+
cancelRender,
170+
renderFinished,
171+
reRenderAllUI5Elements,
172+
attachBeforeComponentRender,
173+
detachBeforeComponentRender,
174+
};

packages/base/src/RenderScheduler.js

+16-152
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,43 @@
1-
import EventProvider from "./EventProvider.js";
2-
import RenderQueue from "./RenderQueue.js";
3-
import { getAllRegisteredTags } from "./CustomElementsRegistry.js";
4-
import { isRtlAware } from "./locale/RTLAwareRegistry.js";
5-
6-
const registeredElements = new Set();
7-
const eventProvider = new EventProvider();
8-
9-
// Queue for invalidated web components
10-
const invalidatedWebComponents = new RenderQueue();
11-
12-
let renderTaskPromise,
13-
renderTaskPromiseResolve;
14-
15-
let mutationObserverTimer;
16-
17-
let queuePromise;
1+
import {
2+
renderDeferred,
3+
renderImmediately,
4+
renderFinished,
5+
} from "./Render.js";
186

197
/**
20-
* Class that manages the rendering/re-rendering of web components
21-
* This is always asynchronous
8+
* @deprecated Use the Render.js module instead
229
*/
2310
class RenderScheduler {
24-
constructor() {
25-
throw new Error("Static class");
26-
}
27-
2811
/**
2912
* Schedules a render task (if not already scheduled) to render the component
3013
*
3114
* @param webComponent
3215
* @returns {Promise}
16+
* @deprecated Use renderDeferred from the Render.js module instead
3317
*/
3418
static async renderDeferred(webComponent) {
35-
// Enqueue the web component
36-
invalidatedWebComponents.add(webComponent);
37-
38-
// Schedule a rendering task
39-
await RenderScheduler.scheduleRenderTask();
19+
console.log("RenderScheduler.renderDeferred is deprecated, please use renderDeferred, exported by Render.js instead"); // eslint-disable-line
20+
await renderDeferred(webComponent);
4021
}
4122

4223
/**
4324
* Renders a component synchronously
4425
*
4526
* @param webComponent
27+
* @deprecated Use renderImmediately from the Render.js module instead
4628
*/
4729
static renderImmediately(webComponent) {
48-
eventProvider.fireEvent("beforeComponentRender", webComponent);
49-
webComponent._render();
30+
console.log("RenderScheduler.renderImmediately is deprecated, please use renderImmediately, exported by Render.js instead"); // eslint-disable-line
31+
return renderImmediately(webComponent);
5032
}
5133

5234
/**
53-
* Cancels the rendering of a component, added to the queue with renderDeferred
54-
*
55-
* @param webComponent
35+
* @deprecated Use renderFinished from the Render.js module instead
36+
* @returns {Promise<void>}
5637
*/
57-
static cancelRender(webComponent) {
58-
invalidatedWebComponents.remove(webComponent);
59-
}
60-
61-
/**
62-
* Schedules a rendering task, if not scheduled already
63-
*/
64-
static async scheduleRenderTask() {
65-
if (!queuePromise) {
66-
queuePromise = new Promise(resolve => {
67-
window.requestAnimationFrame(() => {
68-
// Render all components in the queue
69-
70-
// console.log(`--------------------RENDER TASK START------------------------------`); // eslint-disable-line
71-
invalidatedWebComponents.process(RenderScheduler.renderImmediately);
72-
// console.log(`--------------------RENDER TASK END------------------------------`); // eslint-disable-line
73-
74-
// Resolve the promise so that callers of renderDeferred can continue
75-
queuePromise = null;
76-
resolve();
77-
78-
// Wait for Mutation observer before the render task is considered finished
79-
if (!mutationObserverTimer) {
80-
mutationObserverTimer = setTimeout(() => {
81-
mutationObserverTimer = undefined;
82-
if (invalidatedWebComponents.isEmpty()) {
83-
RenderScheduler._resolveTaskPromise();
84-
}
85-
}, 200);
86-
}
87-
});
88-
});
89-
}
90-
91-
await queuePromise;
92-
}
93-
94-
/**
95-
* return a promise that will be resolved once all invalidated web components are rendered
96-
*/
97-
static whenDOMUpdated() {
98-
if (renderTaskPromise) {
99-
return renderTaskPromise;
100-
}
101-
102-
renderTaskPromise = new Promise(resolve => {
103-
renderTaskPromiseResolve = resolve;
104-
window.requestAnimationFrame(() => {
105-
if (invalidatedWebComponents.isEmpty()) {
106-
renderTaskPromise = undefined;
107-
resolve();
108-
}
109-
});
110-
});
111-
112-
return renderTaskPromise;
113-
}
114-
115-
static whenAllCustomElementsAreDefined() {
116-
const definedPromises = getAllRegisteredTags().map(tag => customElements.whenDefined(tag));
117-
return Promise.all(definedPromises);
118-
}
119-
12038
static async whenFinished() {
121-
await RenderScheduler.whenAllCustomElementsAreDefined();
122-
await RenderScheduler.whenDOMUpdated();
123-
}
124-
125-
static _resolveTaskPromise() {
126-
if (!invalidatedWebComponents.isEmpty()) {
127-
// More updates are pending. Resolve will be called again
128-
return;
129-
}
130-
131-
if (renderTaskPromiseResolve) {
132-
renderTaskPromiseResolve.call(this);
133-
renderTaskPromiseResolve = undefined;
134-
renderTaskPromise = undefined;
135-
}
136-
}
137-
138-
static register(element) {
139-
registeredElements.add(element);
140-
}
141-
142-
static deregister(element) {
143-
registeredElements.delete(element);
144-
}
145-
146-
/**
147-
* Re-renders all UI5 Elements on the page, with the option to specify filters to rerender only some components.
148-
*
149-
* Usage:
150-
* reRenderAllUI5Elements() -> rerenders all components
151-
* reRenderAllUI5Elements({tag: "ui5-button"}) -> re-renders only instances of ui5-button
152-
* reRenderAllUI5Elements({rtlAware: true}) -> re-renders only rtlAware components
153-
* reRenderAllUI5Elements({languageAware: true}) -> re-renders only languageAware components
154-
* reRenderAllUI5Elements({rtlAware: true, languageAware: true}) -> re-renders components that are rtlAware or languageAware
155-
* etc...
156-
*
157-
* @public
158-
* @param {Object|undefined} filters - Object with keys that can be "rtlAware" or "languageAware"
159-
*/
160-
static reRenderAllUI5Elements(filters) {
161-
registeredElements.forEach(element => {
162-
const tag = element.constructor.getMetadata().getTag();
163-
const rtlAware = isRtlAware(element.constructor);
164-
const languageAware = element.constructor.getMetadata().isLanguageAware();
165-
if (!filters || (filters.tag === tag) || (filters.rtlAware && rtlAware) || (filters.languageAware && languageAware)) {
166-
RenderScheduler.renderDeferred(element);
167-
}
168-
});
169-
}
170-
171-
static attachBeforeComponentRender(listener) {
172-
eventProvider.attachEvent("beforeComponentRender", listener);
173-
}
174-
175-
static detachBeforeComponentRender(listener) {
176-
eventProvider.detachEvent("beforeComponentRender", listener);
39+
console.log("RenderScheduler.whenFinished is deprecated, please use renderFinished, exported by Render.js instead"); // eslint-disable-line
40+
await renderFinished();
17741
}
17842
}
17943

0 commit comments

Comments
 (0)