ui-layout-manager-dev 0.0.1

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.
Files changed (51) hide show
  1. package/.storybook/main.js +22 -0
  2. package/.storybook/manager.js +6 -0
  3. package/.storybook/preview.js +13 -0
  4. package/LICENSE +201 -0
  5. package/README.md +80 -0
  6. package/babel.config.js +6 -0
  7. package/dist/cjs/index.js +15 -0
  8. package/dist/cjs/index.js.map +1 -0
  9. package/dist/esm/LayoutWorker.js +494 -0
  10. package/dist/esm/index.js +15 -0
  11. package/dist/esm/index.js.map +1 -0
  12. package/jsconfig.json +13 -0
  13. package/package.json +68 -0
  14. package/rollup.config.mjs +49 -0
  15. package/src/components/LayoutManager/Components/Container/Container.jsx +136 -0
  16. package/src/components/LayoutManager/Components/Container/Container.scss +23 -0
  17. package/src/components/LayoutManager/Components/HandleBar/HandleBar.jsx +183 -0
  18. package/src/components/LayoutManager/Components/HandleBar/HandleBar.scss +74 -0
  19. package/src/components/LayoutManager/Components/LazyLoader/LazyLoader.js +33 -0
  20. package/src/components/LayoutManager/Components/LazyLoader/LazyLoader.scss +7 -0
  21. package/src/components/LayoutManager/Components/RootContainer/RootContainer.jsx +106 -0
  22. package/src/components/LayoutManager/Components/RootContainer/RootContainer.scss +13 -0
  23. package/src/components/LayoutManager/Controller/LAYOUT_WORKER_PROTOCOL.js +11 -0
  24. package/src/components/LayoutManager/Controller/LayoutController.js +201 -0
  25. package/src/components/LayoutManager/Controller/TRANSFORMATION_TYPES.js +6 -0
  26. package/src/components/LayoutManager/Controller/Worker/HandleRulesEnforcer.js +179 -0
  27. package/src/components/LayoutManager/Controller/Worker/LayoutEditor.js +183 -0
  28. package/src/components/LayoutManager/Controller/Worker/LayoutWorker.js +41 -0
  29. package/src/components/LayoutManager/Controller/Worker/ParentRuleEnforcer.js +79 -0
  30. package/src/components/LayoutManager/Controller/Worker/Size.js +29 -0
  31. package/src/components/LayoutManager/LayoutManager.jsx +31 -0
  32. package/src/components/LayoutManager/LayoutManager.scss +0 -0
  33. package/src/components/LayoutManager/Providers/ComponentRegistryContext.js +7 -0
  34. package/src/components/LayoutManager/Providers/LayoutProvider.js +48 -0
  35. package/src/components/LayoutManager/docs/ui_layout_manager_system_diagram.JPG +0 -0
  36. package/src/components/LayoutManager/index.js +1 -0
  37. package/src/index.js +1 -0
  38. package/src/stories/LayoutManager.stories.js +64 -0
  39. package/src/stories/LayoutManager.stories.scss +7 -0
  40. package/src/stories/layouts/vsCode/default.json +195 -0
  41. package/src/stories/layouts/vsCode/sample1.json +151 -0
  42. package/src/stories/sample_components/editor/EditorVSCode.jsx +11 -0
  43. package/src/stories/sample_components/editor/filetree.json +1 -0
  44. package/src/stories/sample_components/flow/Flow.jsx +10 -0
  45. package/src/stories/sample_components/flow/SampleTree.json +8 -0
  46. package/src/stories/sample_components/map/MapSample.jsx +43 -0
  47. package/src/stories/sample_components/map/MapSample.scss +3 -0
  48. package/src/stories/sample_components/stack/Stack.jsx +21 -0
  49. package/tests/LayoutEditor/LayoutEditor.test.js +14 -0
  50. package/tests/LayoutEditor/layouts/default.json +195 -0
  51. package/vitest.config.js +8 -0
@@ -0,0 +1,183 @@
1
+ // @ts-nocheck
2
+ import React, { useEffect, useState, useRef, useCallback } from "react";
3
+ import PropTypes from 'prop-types';
4
+ import { useLayoutController } from "../../Providers/LayoutProvider";
5
+
6
+ import "./HandleBar.scss";
7
+
8
+ /**
9
+ *
10
+ */
11
+ export const HandleBar = ({orientation, parent, sibling1, sibling2}) => {
12
+
13
+ const controller = useLayoutController();
14
+ const dragStartInfo = useRef(null)
15
+ const handleRef = useRef(null);
16
+ const timerRef = useRef(null);
17
+
18
+ const MIN_PANEL_SIZE = 50;
19
+
20
+ /**
21
+ * This function saves the relevant info on mouse down.
22
+ * It does the following:
23
+ * - Determines the dynamic prop being modified (width or height)
24
+ * - Determines the mouse down property to track (clientY or clientX)
25
+ * @param {MouseEvent} e
26
+ */
27
+ const handleMouseDown = (e) => {
28
+ e.preventDefault();
29
+ e.stopPropagation();
30
+ document.addEventListener("mousemove", handleMouseMove);
31
+ document.addEventListener("mouseup", handleMouseUp);
32
+
33
+ let downKey, propKey, hoverClass;
34
+ if (orientation === "horizontal") {
35
+ downKey = "clientY";
36
+ propKey = "height";
37
+ hoverClass = "handleBarHorizontalHover";
38
+ } else if (orientation === "vertical"){
39
+ downKey = "clientX";
40
+ propKey = "width";
41
+ hoverClass = "handleBarVerticalHover";
42
+ }
43
+
44
+ const parentRef = controller.containerRefs[parent];
45
+ const sibling1Ref = controller.containerRefs[sibling1];
46
+ const sibling2Ref = controller.containerRefs[sibling2];
47
+
48
+ // Get the min, max sizes of siblings 1 and 2
49
+ let sibling1LayoutConfig, sibling2LayoutConfig;
50
+ const parentContainer = controller.ldf.containers[parent];
51
+ for (let i = 0; i < parentContainer.children.length; i++) {
52
+ if (parentContainer.children[i].containerId === sibling1) {
53
+ sibling1LayoutConfig = parentContainer.children[i].size;
54
+ } else if (parentContainer.children[i].containerId === sibling2) {
55
+ sibling2LayoutConfig = parentContainer.children[i].size;
56
+ }
57
+ }
58
+
59
+ dragStartInfo.current = {
60
+ "downValueY": e[downKey],
61
+ "hoverClass": hoverClass,
62
+ "downKey": downKey,
63
+ "propKey": propKey,
64
+ "parentSize": parentRef.getBoundingClientRect()[propKey],
65
+ "parentRef": parentRef,
66
+ "sibling1Ref": sibling1Ref,
67
+ "sibling2Ref": sibling2Ref,
68
+ "sibling1LayoutConfig": sibling1LayoutConfig,
69
+ "sibling2LayoutConfig": sibling2LayoutConfig,
70
+ "sibling1Size": sibling1Ref.getBoundingClientRect()[propKey],
71
+ "sibling2Size": sibling2Ref.getBoundingClientRect()[propKey],
72
+ }
73
+
74
+ handleRef.current.classList.add(hoverClass);
75
+ }
76
+
77
+ function getRelativeMousePosition(event, parentContainer) {
78
+ const parentBounds = parentContainer.getBoundingClientRect();
79
+ const relativeX = event.clientX - parentBounds.left;
80
+ const relativeY = event.clientY - parentBounds.top;
81
+ return { x: relativeX, y: relativeY };
82
+ }
83
+
84
+ /**
85
+ * This function is called when the mouse is being dragged and
86
+ * it uses the delta from the starting down point to calculate
87
+ * the new sizes (width or height).
88
+ * @param {Event} e
89
+ * @returns
90
+ */
91
+ const handleMouseMove = (e) => {
92
+ if (!dragStartInfo.current) return;
93
+ e.preventDefault();
94
+ e.stopPropagation();
95
+
96
+ const startInfo = dragStartInfo.current;
97
+
98
+ // Use delta from starting down point to calculate new heights
99
+ const delta = e[startInfo.downKey] - startInfo.downValueY;
100
+ const newSibling1Size = startInfo.sibling1Size + delta;
101
+ const newSibling2Size = startInfo.sibling2Size - delta;
102
+
103
+ clearTimeout(timerRef.current);
104
+
105
+ timerRef.current = setTimeout(() => {
106
+ // Resize here
107
+ controller.moveHandleBar({
108
+ handle: getRelativeMousePosition(e, startInfo.parentRef),
109
+ parent: parent,
110
+ sibling1: sibling1,
111
+ sibling2: sibling2
112
+ });
113
+ }, 0.1);
114
+
115
+ // Don't update container sizes we are past min or max values.
116
+ const sibling1SizeKeys = Object.keys(startInfo.sibling1LayoutConfig);
117
+ if (sibling1SizeKeys.includes("min") && newSibling1Size <= startInfo.sibling1LayoutConfig.min.value ||
118
+ sibling1SizeKeys.includes("max") && newSibling1Size >= startInfo.sibling1LayoutConfig.max.value) {
119
+ return;
120
+ }
121
+
122
+ // Don't update container sizes we are past min or max values.
123
+ const sibling2SizeKeys = Object.keys(startInfo.sibling2LayoutConfig);
124
+ if (sibling2SizeKeys.includes("min") && newSibling2Size <= startInfo.sibling2LayoutConfig.min.value ||
125
+ sibling2SizeKeys.includes("max") && newSibling2Size >= startInfo.sibling2LayoutConfig.max.value) {
126
+ return;
127
+ }
128
+
129
+ // If both siblings are type fill, then set sizes. Set min size of sibling sizes to 50px;
130
+ // TODO: Make into constants and I think this should be evaluated inside the controller.
131
+ const sibling1Type = startInfo.sibling1LayoutConfig.initial.type;
132
+ const sibling2Type = startInfo.sibling2LayoutConfig.initial.type;
133
+ if (sibling1Type === "fill" && sibling2Type === "fill" && newSibling1Size > 50 && newSibling2Size > 50) {
134
+ controller.containerRefs[sibling1].style[startInfo.propKey] = newSibling1Size + "px";
135
+ controller.containerRefs[sibling2].style[startInfo.propKey] = newSibling2Size + "px";
136
+ return;
137
+ }
138
+
139
+ // Don't update fill types, flex box will take care of that
140
+ if (!(sibling1Type === "fill")) {
141
+ controller.containerRefs[sibling1].style[startInfo.propKey] = newSibling1Size + "px";
142
+ }
143
+ if (!(sibling2Type === "fill")) {
144
+ controller.containerRefs[sibling2].style[startInfo.propKey] = newSibling2Size + "px";
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Perform cleanup after drag event has finished.
150
+ * @param {Event} e
151
+ */
152
+ const handleMouseUp = (e) => {
153
+ e.preventDefault();
154
+ e.stopPropagation();
155
+ document.removeEventListener("mousemove", handleMouseMove);
156
+ document.removeEventListener("mouseup", handleMouseUp);
157
+ handleRef.current.classList.remove(dragStartInfo.current.hoverClass);
158
+ dragStartInfo.current = null;
159
+ }
160
+
161
+ return (
162
+ <React.Fragment >
163
+ {
164
+ orientation === "horizontal"?
165
+ <div onMouseDown={(e) => handleMouseDown(e)} className="handleBarHorizontalContainer">
166
+ <div ref={handleRef} className="handleBarHorizontal"></div>
167
+ </div>:
168
+ orientation === "vertical"?
169
+ <div onMouseDown={(e) => handleMouseDown(e)} className="handleBarVerticalContainer">
170
+ <div ref={handleRef} className="handleBarVertical"></div>
171
+ </div>:
172
+ null
173
+ }
174
+ </React.Fragment>
175
+ );
176
+ };
177
+
178
+ HandleBar.propTypes = {
179
+ orientation: PropTypes.string,
180
+ sibling1: PropTypes.string,
181
+ sibling2: PropTypes.string,
182
+ parent: PropTypes.string
183
+ }
@@ -0,0 +1,74 @@
1
+ $handle-bar-color: #414141;
2
+ $handle-bar-size: 1px;
3
+ $handle-bar-size-expanded: 5px;
4
+ $handle-bar-hover-color: #007acc;
5
+
6
+ // Creates the container which will hold the handle bar
7
+ .handleBarHorizontalContainer {
8
+ position:relative;
9
+ width:100%;
10
+ height: $handle-bar-size;
11
+ background-color: $handle-bar-color;
12
+ }
13
+
14
+ // Create handle bar and center it
15
+ .handleBarHorizontal{
16
+ position:absolute;
17
+ top: 50%;
18
+ height: $handle-bar-size;
19
+ left:0;
20
+ right:0;
21
+ transform: translateY(-50%);
22
+ transition: all 0.2s ease;
23
+ }
24
+
25
+ // Update the height and let it animate
26
+ .handleBarHorizontal:hover {
27
+ background-color: $handle-bar-hover-color;
28
+ cursor: ns-resize;
29
+ height: $handle-bar-size-expanded;
30
+ z-index: 10;
31
+ }
32
+
33
+ // Set by the component so hover is active even
34
+ // when mouse isn't over handle during drag
35
+ .handleBarHorizontalHover {
36
+ background-color: $handle-bar-hover-color;
37
+ cursor: ns-resize;
38
+ height: $handle-bar-size-expanded;
39
+ z-index: 10;
40
+ }
41
+
42
+
43
+ // See horizontal style comments, they are the same for vertical
44
+ .handleBarVerticalContainer {
45
+ position:relative;
46
+ height:100%;
47
+ width: $handle-bar-size;
48
+ background-color: $handle-bar-color;
49
+ z-index: 1;
50
+ }
51
+
52
+ .handleBarVertical{
53
+ position: absolute;
54
+ top: 0;
55
+ width: $handle-bar-size;
56
+ left: 50%;
57
+ bottom: 0;
58
+ transform: translateX(-50%);
59
+ transition: all 0.3s ease;
60
+ }
61
+
62
+ .handleBarVertical:hover {
63
+ background-color: $handle-bar-hover-color;
64
+ cursor: ew-resize;
65
+ width: $handle-bar-size-expanded;
66
+ z-index: 10;
67
+ }
68
+
69
+ .handleBarVerticalHover {
70
+ background-color: $handle-bar-hover-color;
71
+ cursor: ew-resize;
72
+ width: $handle-bar-size-expanded;
73
+ z-index: 10;
74
+ }
@@ -0,0 +1,33 @@
1
+ import React, { lazy, useMemo, Suspense, useContext } from "react";
2
+ import PropTypes from 'prop-types';
3
+ import ComponentRegistryContext from "../../Providers/ComponentRegistryContext";
4
+
5
+ import "./LazyLoader.scss"
6
+
7
+ /**
8
+ * LazyLoader component that renders a component
9
+ * specified in the ldf file.
10
+ *
11
+ * @param {Object} content
12
+ */
13
+ export const LazyLoader = ({content}) => {
14
+ const registry = useContext(ComponentRegistryContext);
15
+
16
+ const LazyComponent = useMemo(() => {
17
+ if (registry && content && "component" in content && content["component"] in registry) {
18
+ return lazy(registry[content["component"]]);
19
+ }
20
+ }, [registry, content]);
21
+
22
+ return (
23
+ <div className="lazyContainer">
24
+ <Suspense fallback={<div>Loading...</div>}>
25
+ {LazyComponent && <LazyComponent />}
26
+ </Suspense>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ LazyLoader.propTypes = {
32
+ content: PropTypes.object,
33
+ }
@@ -0,0 +1,7 @@
1
+ .lazyContainer {
2
+ position: absolute;
3
+ top: 0px;
4
+ bottom: 0px;
5
+ left: 0px;
6
+ right: 0px;
7
+ }
@@ -0,0 +1,106 @@
1
+ import React, { useEffect, useLayoutEffect, useState, useRef, useCallback, useContext } from "react";
2
+ import PropTypes from 'prop-types';
3
+ import { Container } from "../Container/Container";
4
+ import { useLayoutController } from "../../Providers/LayoutProvider";
5
+
6
+ import "./RootContainer.scss"
7
+ /**
8
+ * Root node for the layout tree. This component will start
9
+ * rendering the tree and it will also watch for changes in the
10
+ * root container sizes to process layout changes.
11
+ *
12
+ * @return {React.ReactElement}
13
+ */
14
+ export const RootContainer = () => {
15
+ const controller = useLayoutController();
16
+
17
+ const rootRef = useRef(null);
18
+ const timerRef = useRef(null);
19
+ const resizingRef = useRef(false);
20
+
21
+ // Create the container API that will be used by the controller.
22
+ const rootContainerAPI = useRef({});
23
+ rootContainerAPI.current = {};
24
+
25
+ const [childElements, setChildElements] = useState(null);
26
+
27
+ /**
28
+ * Renders child containers recursively.
29
+ */
30
+ const processContainer = useCallback((node) => {
31
+ const childElements = [];
32
+ for (let index = 0; index < node.children.length; index++) {
33
+ const childNode = node.children[index];
34
+
35
+ if (childNode.type === "container") {
36
+ const child = controller.ldf.containers[node.children[index].containerId];
37
+ child.parent = node;
38
+ childElements.push(
39
+ <Container key={index} meta={node.children[index]} id={child.id} node={child}/>
40
+ );
41
+ } else if (childNode.type === "handleBar") {
42
+ if (node.orientation === "horizontal") {
43
+ childElements.push(
44
+ <div className="verticalLine"></div>
45
+ );
46
+ } else if (node.orientation === "vertical") {
47
+ childElements.push(
48
+ <div className="horizontalLine"></div>
49
+ );
50
+ }
51
+ }
52
+ };
53
+ return childElements;
54
+ },[controller]);
55
+
56
+
57
+ useLayoutEffect(() => {
58
+ if (controller) {
59
+ const rootNode = controller.ldf.containers[controller.ldf.layoutRoot];
60
+ const hasChildren = rootNode.children && rootNode.children.length > 0
61
+ controller.registerContainer(rootNode.id, rootContainerAPI, rootRef.current);
62
+
63
+ if (hasChildren) {
64
+ if (rootNode.orientation === "horizontal") {
65
+ rootRef.current.style.flexDirection = "row";
66
+ } else if (rootNode.orientation === "vertical") {
67
+ rootRef.current.style.flexDirection = "column";
68
+ }
69
+ }
70
+
71
+ setChildElements(hasChildren?processContainer(rootNode):null);
72
+
73
+ // Create resize observer to monitor changes in the root container size.
74
+ const observer = new ResizeObserver((entries) => {
75
+
76
+ if (!resizingRef.current) resizingRef.current = true;
77
+
78
+ for (let entry of entries) {
79
+ const { width, height } = entry.contentRect;
80
+
81
+ clearTimeout(timerRef.current);
82
+
83
+ timerRef.current = setTimeout(() => {
84
+ resizingRef.current = false;
85
+ controller.handleRootResize(width, height);
86
+ }, 1);
87
+ }
88
+ });
89
+
90
+ observer.observe(rootRef.current);
91
+
92
+ return () => {
93
+ controller.unregisterContainer(controller.ldf.layoutRoot);
94
+ observer.disconnect();
95
+ }
96
+ }
97
+ }, [controller]);
98
+
99
+ return (
100
+ <div className="root-container">
101
+ <div ref={rootRef} className="relative-container">
102
+ {childElements}
103
+ </div>
104
+ </div>
105
+ );
106
+ }
@@ -0,0 +1,13 @@
1
+ .root-container {
2
+ width: 100%;
3
+ height: 100%;
4
+ overflow: hidden;
5
+ }
6
+
7
+ .relative-container {
8
+ position: relative;
9
+ display:flex;
10
+ width:100%;
11
+ height: 100%;
12
+ overflow: hidden;
13
+ }
@@ -0,0 +1,11 @@
1
+ let LAYOUT_WORKER_PROTOCOL = {
2
+ INITIALIZE: 1,
3
+ INITIALIZE_FLEXBOX: 2,
4
+ APPLY_SIZES: 3,
5
+ ERROR: 4,
6
+ TRANSFORMATIONS: 5,
7
+ MOVE_HANDLE_BAR: 6
8
+ };
9
+ LAYOUT_WORKER_PROTOCOL = Object.freeze(LAYOUT_WORKER_PROTOCOL);
10
+
11
+ export default LAYOUT_WORKER_PROTOCOL;
@@ -0,0 +1,201 @@
1
+ import LAYOUT_WORKER_PROTOCOL from "./LAYOUT_WORKER_PROTOCOL";
2
+ import TRANSFORMATION_TYPES from "./TRANSFORMATION_TYPES";
3
+
4
+ /**
5
+ * This controller is responsible for managing the layout of the application.
6
+ * - It will handle the registration and unregistration of containers.
7
+ * - It will handle the layout changes and notify the worker to process the layout changes.
8
+ * - It will update the container sizes with the updated values calculated by the worker.
9
+ *
10
+ * @class LayoutController
11
+ */
12
+ export class LayoutController {
13
+
14
+ /**
15
+ * Constructor
16
+ *
17
+ * @param {Object} ldf - Layout Definition JSON object
18
+ */
19
+ constructor(ldf) {
20
+ this.containers = {};
21
+ this.containerRefs = {};
22
+ this.ldf = ldf;
23
+ this.numberOfContainers = 0;
24
+ this.registeredContainers = 0;
25
+ this.layoutLoaded = false;
26
+
27
+ this.numberOfContainers = this.ldf.containers ? Object.keys(this.ldf.containers).length: 0;
28
+
29
+ try {
30
+ this.worker = new Worker(
31
+ new URL('./Worker/LayoutWorker.js', import.meta.url),
32
+ { type: 'module' }
33
+ );
34
+ this.worker.onmessage = this.handleWorkerMessage.bind(this);
35
+ this.worker.onerror = (error) => console.error('Worker error:', error);
36
+ this.sendToWorker(LAYOUT_WORKER_PROTOCOL.INITIALIZE, {ldf: ldf})
37
+
38
+ } catch (error) {
39
+ console.error('Failed to create worker:', error);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Sends message to worker with the provided arguments.
45
+ * @param {Number} code
46
+ * @param {Object} args
47
+ */
48
+ sendToWorker(code, args) {
49
+ this.worker.postMessage({
50
+ code:code,
51
+ args: args
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Allows containers to register themselves with the controller.
57
+ * @param {String} id
58
+ * @param {Object} containerApi
59
+ * @param {HTMLElement} containerRef
60
+ */
61
+ registerContainer(id, containerApi, containerRef) {
62
+ if (!(id in this.containers)) {
63
+ this.registeredContainers += 1
64
+ }
65
+
66
+ this.containers[id] = containerApi;
67
+ this.containerRefs[id] = containerRef;
68
+
69
+ console.log(`Registered container with id: ${id} `);
70
+
71
+ if (this.registeredContainers === this.numberOfContainers && !this.layoutLoaded) {
72
+ console.log("All containers registered, layout is ready.");
73
+ this.sendToWorker(LAYOUT_WORKER_PROTOCOL.INITIALIZE_FLEXBOX);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Allows containers to unregister themselves with the controller.
79
+ * @param {String} id
80
+ */
81
+ unregisterContainer(id) {
82
+ delete this.containers[id];
83
+ }
84
+
85
+ /**
86
+ * This function is called when the root container is resized.
87
+ * It will notify the worker to process the layout changes.
88
+ */
89
+ handleRootResize() {
90
+ if (!this.layoutLoaded) return;
91
+ // console.log("Root container resized to:", width, height);
92
+ const sizes = {};
93
+ for (const id in this.containerRefs) {
94
+ if (this.containerRefs.hasOwnProperty(id)) {
95
+ const boundingRect = this.containerRefs[id].getBoundingClientRect();
96
+ sizes[id] = {
97
+ width: boundingRect.width,
98
+ height: boundingRect.height
99
+ };
100
+ }
101
+ }
102
+ this.sendToWorker(
103
+ LAYOUT_WORKER_PROTOCOL.APPLY_SIZES,
104
+ { sizes: sizes }
105
+ );
106
+ }
107
+
108
+
109
+ /**
110
+ * Move handle bar is called by the handle bar component with the
111
+ * metadata about its parent and the siblings being resized. This
112
+ * information is parsed and passed to the layout editor to enforce
113
+ * the layout rules.
114
+ * @param {Object} metadata
115
+ */
116
+ moveHandleBar(metadata) {
117
+ let sizes = {};
118
+ const containerIds = [metadata.parent, metadata.sibling1, metadata.sibling2];
119
+ for (const containerId of containerIds) {
120
+ let boundingRect = this.containerRefs[containerId].getBoundingClientRect();
121
+ sizes[containerId] = {
122
+ width: boundingRect.width,
123
+ height: boundingRect.height
124
+ };
125
+ }
126
+
127
+ metadata.sizes = sizes;
128
+ this.sendToWorker(
129
+ LAYOUT_WORKER_PROTOCOL.MOVE_HANDLE_BAR,
130
+ {
131
+ metadata: metadata
132
+ }
133
+ );
134
+
135
+ // TODO: This is temporary, after handle bar move, the layout rules are
136
+ // applied to react to new container sizes. This can be done more efficiently
137
+ // because we only need to react the containers that were changed. This calculates
138
+ // the entire layout.
139
+ this.handleRootResize();
140
+ }
141
+
142
+ /**
143
+ * Apply the given transformations
144
+ * @param {Object} transformations
145
+ * @param {Object} isInitial
146
+ */
147
+ applyTransformations (transformations, isInitial) {
148
+ requestAnimationFrame(() => {
149
+ for (const transformation of transformations) {
150
+ switch (transformation.type) {
151
+ case TRANSFORMATION_TYPES.UPDATE_SIZE:
152
+ this.containers[transformation.id].current.updateStyles(
153
+ transformation.args.style
154
+ );
155
+ break;
156
+ case TRANSFORMATION_TYPES.REMOVE_NODE:
157
+ break;
158
+ default:
159
+ console.warn("Unknown transformation was requested.");
160
+ break;
161
+ }
162
+ };
163
+ if (isInitial) {
164
+ this.layoutLoaded = true;
165
+ }
166
+ });
167
+ };
168
+
169
+ /**
170
+ * Handles messages from worker
171
+ * @param {Object} event
172
+ */
173
+ handleWorkerMessage(event) {
174
+ let transformations;
175
+ switch(event.data.type) {
176
+ case LAYOUT_WORKER_PROTOCOL.INITIALIZE_FLEXBOX:
177
+ transformations = event.data.data;
178
+ this.applyTransformations(transformations, true);
179
+ break;
180
+ case LAYOUT_WORKER_PROTOCOL.TRANSFORMATIONS:
181
+ transformations = event.data.data;
182
+ this.applyTransformations(transformations, false);
183
+ break;
184
+ case LAYOUT_WORKER_PROTOCOL.ERROR:
185
+ console.error("Error from worker:", event.data);
186
+ break;
187
+ default:
188
+ break;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Performs cleanup when the controller is destroyed.
194
+ */
195
+ destroy() {
196
+ if (this.worker) {
197
+ this.worker.terminate();
198
+ this.worker = null;
199
+ }
200
+ }
201
+ }
@@ -0,0 +1,6 @@
1
+ let TRANSFORMATION_TYPES = {
2
+ UPDATE_SIZE: 1
3
+ };
4
+ TRANSFORMATION_TYPES = Object.freeze(TRANSFORMATION_TYPES);
5
+
6
+ export default TRANSFORMATION_TYPES;