Fix playground handling of visible scrollbars (#1514)

Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1514

On machines with scrollbars, we shouldn't show them unconditionally, and the toolbar should be in the area within them.

This also fixes a couple bugs:
1. Preview not rendering based on correct code when light/dark mode changes
2. Crash on start on mobile safari
3. Incorrect rendering of preview on mobile safari

This also fixes a bug where the playground re-rendering (e.g. on theme change) makes the preview snap back to the initial code passes.

https://yoga-website-next-git-fork-nickgerleman-exp-194d90-fbopensource.vercel.app/

Reviewed By: shwanton

Differential Revision: D52145666

fbshipit-source-id: 50184305987aab4cbcd066f37582997dfdc78c02
This commit is contained in:
Nick Gerleman
2023-12-13 20:05:29 -08:00
committed by Facebook GitHub Bot
parent 738d04fcb0
commit 43cb24fdce
5 changed files with 73 additions and 36 deletions

View File

@@ -13,6 +13,7 @@ import React, {
lazy,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
@@ -45,7 +46,11 @@ export type Props = Readonly<{
export default function Playground({code, height, autoFocus}: Props) {
const prismTheme = usePrismTheme();
const playgroundRef = useRef<HTMLDivElement>(null);
const editorScrollRef = useRef<HTMLDivElement>(null);
const [isLoaded, setIsLoaded] = useState(false);
const [liveCode, setLiveCode] = useState(code ?? defaultCode);
const [scrollbarWidth, setScrollbarWidth] = useState(0);
const LivePreviewWrapper = useCallback(
(props: React.ComponentProps<'div'>) => {
@@ -64,35 +69,52 @@ export default function Playground({code, height, autoFocus}: Props) {
if (isLoaded && autoFocus) {
const codeElem = playgroundRef?.current?.querySelector('.prism-code');
const sel = window.getSelection();
if (codeElem != null && sel != null) {
if (codeElem?.clientHeight && sel != null) {
sel.selectAllChildren(codeElem);
sel.collapseToStart();
}
}
}, [isLoaded, autoFocus]);
useLayoutEffect(() => {
// The toolbar is positioned relative to the outside of the scrolling
// container so it stays in the same place when scrolling, but this means
// it isn't automatically adjusted for scrollbar width
if (editorScrollRef.current) {
setScrollbarWidth(
editorScrollRef.current.offsetWidth -
editorScrollRef.current.clientWidth,
);
}
});
const heightStyle = height
? ({'--yg-playground-height': height} as React.CSSProperties)
: undefined;
const resolvedCode = code ?? defaultCode;
return (
<LiveProvider code={resolvedCode} theme={prismTheme} scope={{Layout, Node}}>
<LiveProvider code={liveCode} theme={prismTheme} scope={{Layout, Node}}>
<div className={styles.wrapper} ref={playgroundRef} style={heightStyle}>
<div className={clsx(styles.playgroundRow, 'container')}>
<div className={clsx(styles.editorColumn)}>
<EditorToolbar
getCode={useCallback(
() =>
nullthrows(
playgroundRef.current?.querySelector('.prism-code')
?.textContent,
),
[],
)}
/>
<LiveEditor className={clsx(styles.playgroundEditor)} />
<div className={clsx(styles.editorColumn, 'playground-editor')}>
<div className={styles.editorScroll} ref={editorScrollRef}>
<EditorToolbar
className={styles.editorToolbar}
style={{paddingRight: scrollbarWidth + 'px'}}
getCode={useCallback(
() =>
nullthrows(
playgroundRef.current?.querySelector('.prism-code')
?.textContent,
),
[],
)}
/>
<LiveEditor
className={clsx(styles.playgroundEditor)}
onChange={setLiveCode}
/>
</div>
</div>
<div className={clsx(styles.previewColumn)}>
<LivePreview