diff --git a/examples/simple-app/src/App.module.css b/examples/simple-app/src/App.module.css index 3ee1159e..0245df79 100644 --- a/examples/simple-app/src/App.module.css +++ b/examples/simple-app/src/App.module.css @@ -7,4 +7,4 @@ align-items: center; justify-content: center; font-size: 1rem; -} \ No newline at end of file +} diff --git a/shell/src/PluginLoader.jsx b/shell/src/PluginLoader.jsx index ee1178df..03d7233a 100644 --- a/shell/src/PluginLoader.jsx +++ b/shell/src/PluginLoader.jsx @@ -10,64 +10,49 @@ const PluginResizeInner = ({ propsFromParent, resizePluginHeight, resizePluginWidth, + clientWidth, }) => { - const divRef = useRef() - const innerDivRef = useRef() + const resizeDivRef = useRef() + useEffect(() => { - if (divRef && divRef.current && resizePluginHeight) { - const container = divRef.current + if (resizeDivRef?.current) { + const resizeDiv = resizeDivRef.current const resizeObserver = new ResizeObserver(() => { - // the additional pixels currently account for possible horizontal scroll bar - resizePluginHeight(container.offsetHeight + 20) + if (resizePluginHeight) { + // offsetHeight takes into account possible scrollbar size + resizePluginHeight(resizeDiv.offsetHeight) + } + if (resizePluginWidth) { + resizePluginWidth(resizeDiv.scrollWidth) + } }) - resizeObserver.observe(container) - return () => { - resizeObserver.unobserve(container) - resizeObserver.disconnect() - } - } - }, [resizePluginHeight]) - const previousWidth = useRef() + resizeObserver.observe(resizeDiv) - const resetWidth = useCallback(() => { - const currentWidth = innerDivRef.current?.scrollWidth - if (resizePluginWidth && currentWidth) { - if ( - previousWidth.current && - Math.abs(currentWidth - previousWidth.current) > 20 - ) { - resizePluginWidth(currentWidth + 20) + return () => { + resizeObserver.unobserve(resizeDiv) + resizeObserver.disconnect() } - previousWidth.current = currentWidth - } - requestAnimationFrame(resetWidth) - }, [resizePluginWidth]) - - useEffect(() => { - if (resizePluginWidth) { - requestAnimationFrame(resetWidth) } - }, [resetWidth, resizePluginWidth]) + }, [resizePluginHeight, resizePluginWidth]) - // inner div disables margin collapsing which would prevent computing correct height + // For the width to be content-driven, the clientWidth needs to be specified + // so that width is not just 'auto'. Then, when content resizes, it will + // actually trigger the resize observer, updating the parent's iframe size return ( -
-
-
- -
-
+
+
) } PluginResizeInner.propTypes = { D2App: PropTypes.object, + clientWidth: PropTypes.string, config: PropTypes.object, propsFromParent: PropTypes.object, resizePluginHeight: PropTypes.func, @@ -80,15 +65,13 @@ const PluginInner = ({ propsFromParent, resizePluginHeight, resizePluginWidth, + clientWidth, }) => { + // If a resize function isn't defined, that value is container-driven and + // doesn't need resizing. + // If neither are defined, then don't need the ResizeInner if (!resizePluginHeight && !resizePluginWidth) { - return ( - - ) + return } return ( ) } PluginInner.propTypes = { D2App: PropTypes.object, + clientWidth: PropTypes.string, config: PropTypes.object, propsFromParent: PropTypes.object, resizePluginHeight: PropTypes.func, @@ -116,8 +101,15 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => { const [showAlertsInPlugin, setShowAlertsInPlugin] = useState(false) const [onPluginError, setOnPluginError] = useState(() => () => {}) const [clearPluginError, setClearPluginError] = useState(() => () => {}) + // These two can be populated with callbacks from the parent. They can be + // called with a new value for the respective dimension, which will be set + // on the iframe element to resize the plugin const [resizePluginHeight, setResizePluginHeight] = useState(null) const [resizePluginWidth, setResizePluginWidth] = useState(null) + // This value will be used as the 'width' property of the div wrapping the + // app inside the plugin. Gets set by a prop from the parent. + // See the Plugin docs or the PluginResizeInner component for more info + const [clientWidth, setClientWidth] = useState(null) const receivePropsFromParent = useCallback( (event) => { @@ -127,11 +119,10 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => { setCommunicationReceived, alertsAdd, showAlertsInPlugin, - height, setPluginHeight, - width, setPluginWidth, onError, + clientWidth: clientWidthFromParent, ...explicitlyPassedProps } = receivedProps @@ -173,12 +164,21 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => { setShowAlertsInPlugin(Boolean(showAlertsInPlugin)) } - if (!height && setPluginHeight) { + // if these resize callbacks are defined, then that dimension isn't + // fixed or container-driven; add them to the props list here. + // It will be called by a resize observer in ResizePluginInner + if (setPluginHeight) { setResizePluginHeight(() => (height) => setPluginHeight(height)) } + // same as height + if (setPluginWidth) { + setResizePluginWidth(() => (width) => { + setPluginWidth(width) + }) + } - if (!width && setPluginWidth) { - setResizePluginWidth(() => (width) => setPluginWidth(width)) + if (clientWidthFromParent) { + setClientWidth(clientWidthFromParent) } }, [ @@ -189,6 +189,7 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => { setShowAlertsInPlugin, setResizePluginHeight, setResizePluginWidth, + setClientWidth, ] ) @@ -247,6 +248,7 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => { propsFromParent={propsFromParent} resizePluginHeight={resizePluginHeight} resizePluginWidth={resizePluginWidth} + clientWidth={clientWidth} />