Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: plugin sizing updates #902

Merged
merged 21 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/simple-app/src/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
align-items: center;
justify-content: center;
font-size: 1rem;
}
}
110 changes: 56 additions & 54 deletions shell/src/PluginLoader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div ref={divRef}>
<div>
<div id="innerDiv" ref={innerDivRef}>
<D2App
config={config}
resizePluginWidth={resizePluginWidth}
{...propsFromParent}
/>
</div>
</div>
<div id="resizeDiv" ref={resizeDivRef} style={{ width: clientWidth }}>
<D2App
config={config}
resizePluginWidth={resizePluginWidth}
{...propsFromParent}
/>
</div>
)
}

PluginResizeInner.propTypes = {
D2App: PropTypes.object,
clientWidth: PropTypes.string,
config: PropTypes.object,
propsFromParent: PropTypes.object,
resizePluginHeight: PropTypes.func,
Expand All @@ -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 (
<D2App
config={config}
resizePluginWidth={resizePluginWidth}
{...propsFromParent}
/>
)
return <D2App config={config} {...propsFromParent} />
}
return (
<PluginResizeInner
Expand All @@ -97,12 +80,14 @@ const PluginInner = ({
propsFromParent={propsFromParent}
resizePluginHeight={resizePluginHeight}
resizePluginWidth={resizePluginWidth}
clientWidth={clientWidth}
/>
)
}

PluginInner.propTypes = {
D2App: PropTypes.object,
clientWidth: PropTypes.string,
config: PropTypes.object,
propsFromParent: PropTypes.object,
resizePluginHeight: PropTypes.func,
Expand All @@ -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) => {
Expand All @@ -127,11 +119,10 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => {
setCommunicationReceived,
alertsAdd,
showAlertsInPlugin,
height,
setPluginHeight,
width,
setPluginWidth,
onError,
clientWidth: clientWidthFromParent,
...explicitlyPassedProps
} = receivedProps

Expand Down Expand Up @@ -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)
}
},
[
Expand All @@ -189,6 +189,7 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => {
setShowAlertsInPlugin,
setResizePluginHeight,
setResizePluginWidth,
setClientWidth,
]
)

Expand Down Expand Up @@ -247,6 +248,7 @@ export const PluginLoader = ({ config, requiredProps, D2App }) => {
propsFromParent={propsFromParent}
resizePluginHeight={resizePluginHeight}
resizePluginWidth={resizePluginWidth}
clientWidth={clientWidth}
/>
</React.Suspense>
</AppAdapter>
Expand Down
Loading