Skip to content

Commit

Permalink
feat: Only render visible frames, avoid extra history entries (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish authored Dec 18, 2019
1 parent 3232344 commit 462f612
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 83 deletions.
6 changes: 5 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": ["@babel/plugin-proposal-class-properties"]
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
]
}
6 changes: 5 additions & 1 deletion lib/makeWebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ module.exports = async (playroomConfig, options) => {
require.resolve('@babel/preset-typescript')
],
plugins: [
require.resolve('@babel/plugin-proposal-class-properties')
require.resolve('@babel/plugin-proposal-class-properties'),
require.resolve('@babel/plugin-proposal-optional-chaining'),
require.resolve(
'@babel/plugin-proposal-nullish-coalescing-operator'
)
]
}
}
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.7.4",
"@babel/plugin-proposal-optional-chaining": "^7.7.5",
"@babel/preset-env": "^7.7.6",
"@babel/preset-react": "^7.7.4",
"@babel/preset-typescript": "^7.7.4",
Expand Down Expand Up @@ -97,6 +99,7 @@
"re-resizable": "^6.1.1",
"react-codemirror2": "^6.0.0",
"react-docgen-typescript": "^1.16.1",
"react-use": "^13.12.2",
"read-pkg-up": "^7.0.1",
"scope-eval": "^1.0.0",
"style-loader": "^1.0.1",
Expand Down
98 changes: 66 additions & 32 deletions src/Playroom/Preview/Iframe.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,69 @@
import React, { Component, AllHTMLAttributes } from 'react';
import React, {
useState,
useEffect,
useRef,
AllHTMLAttributes,
MutableRefObject
} from 'react';
import { useIntersection } from 'react-use';

interface State {
loaded: boolean;
interface IframeProps extends AllHTMLAttributes<HTMLIFrameElement> {
src: string;
intersectionRootRef: MutableRefObject<Element | null>;
}
interface Props extends AllHTMLAttributes<HTMLIFrameElement> {}

export default class Iframe extends Component<Props, State> {
constructor(props: Props) {
super(props);

this.state = {
loaded: false
};
}

handleLoad = () => {
this.setState({ loaded: true });
};

render() {
const { style, ...props } = this.props;
const { loaded } = this.state;

const combinedStyles = {
...style,
transition: 'opacity .3s ease',
opacity: loaded ? 1 : 0
};

return (
<iframe onLoad={this.handleLoad} {...props} style={combinedStyles} />
);
}

export default function Iframe({
intersectionRootRef,
style,
src,
...restProps
}: IframeProps) {
const [loaded, setLoaded] = useState(false);
const [renderedSrc, setRenderedSrc] = useState<string | null>(null);
const iframeRef = useRef<HTMLIFrameElement | null>(null);
const intersection = useIntersection(iframeRef, {
root: intersectionRootRef.current,
rootMargin: '800px',
threshold: 0
});

const intersectionRatio = intersection?.intersectionRatio ?? null;

useEffect(() => {
if (intersectionRatio === null) {
return;
}

if (intersectionRatio > 0 && src !== renderedSrc) {
setRenderedSrc(src);
}
}, [intersectionRatio, src, renderedSrc]);

useEffect(() => {
if (renderedSrc !== null) {
const location = iframeRef.current?.contentWindow?.location;

if (location) {
location.replace(renderedSrc);
}
}
}, [renderedSrc]);

return (
<iframe
ref={iframeRef}
onLoad={() => setLoaded(true)}
onMouseEnter={() => {
if (src !== renderedSrc) {
setRenderedSrc(src);
}
}}
style={{
...style,
transition: 'opacity .3s ease',
opacity: loaded ? 1 : 0
}}
{...restProps}
/>
);
}
89 changes: 44 additions & 45 deletions src/Playroom/Preview/Preview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component, Fragment } from 'react';
import React, { Fragment, useRef } from 'react';
import flatMap from 'lodash/flatMap';
import Iframe from './Iframe';
import compileJsx from '../../utils/compileJsx';
Expand All @@ -7,58 +7,57 @@ import { PlayroomProps } from '../Playroom';
// @ts-ignore
import styles from './Preview.less';

interface Props {
interface PreviewProps {
code: string;
themes: string[];
widths: PlayroomProps['widths'];
}

export default class Preview extends Component<Props> {
render() {
const { code, themes, widths } = this.props;
export default function Preview({ code, themes, widths }: PreviewProps) {
const scrollingPanelRef = useRef<HTMLDivElement | null>(null);

const frames = flatMap(widths, width =>
themes.map(theme => ({ theme, width }))
);
const frames = flatMap(widths, width =>
themes.map(theme => ({ theme, width }))
);

let renderCode = code;
let renderCode = code;

try {
renderCode = compileJsx(code);
} catch (e) {
renderCode = '';
}
try {
renderCode = compileJsx(code);
} catch (e) {
renderCode = '';
}

return (
<div className={styles.root}>
{frames.map(frame => (
<div
key={`${frame.theme}_${frame.width}`}
className={styles.frameContainer}
>
<div className={styles.frameName}>
{frame.theme === '__PLAYROOM__NO_THEME__' ? (
<strong className={styles.strong}>
{frame.width}
px
</strong>
) : (
<Fragment>
<strong className={styles.strong}>{frame.theme}</strong>
{` \u2013 ${frame.width}px`}
</Fragment>
)}
</div>
<Iframe
src={`frame.html#?themeName=${encodeURIComponent(
frame.theme
)}&code=${encodeURIComponent(renderCode)}`}
className={styles.frame}
style={{ width: frame.width }}
/>
return (
<div ref={scrollingPanelRef} className={styles.root}>
{frames.map(frame => (
<div
key={`${frame.theme}_${frame.width}`}
className={styles.frameContainer}
>
<div className={styles.frameName}>
{frame.theme === '__PLAYROOM__NO_THEME__' ? (
<strong className={styles.strong}>
{frame.width}
px
</strong>
) : (
<Fragment>
<strong className={styles.strong}>{frame.theme}</strong>
{` \u2013 ${frame.width}px`}
</Fragment>
)}
</div>
))}
</div>
);
}
<Iframe
intersectionRootRef={scrollingPanelRef}
src={`frame.html#?themeName=${encodeURIComponent(
frame.theme
)}&code=${encodeURIComponent(renderCode)}`}
className={styles.frame}
style={{ width: frame.width }}
/>
</div>
))}
</div>
);
}
Loading

0 comments on commit 462f612

Please sign in to comment.