From 9a3c9047bf3b18d439bfad757c7eae6e199bc942 Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Wed, 21 Feb 2024 16:15:14 +0100 Subject: [PATCH 1/6] feature(website): make preview panel resizable --- website/package.json | 1 + .../DevicePreview/AppetizeFrame.tsx | 6 +- .../DevicePreview/DevicePreview.tsx | 9 +- .../DevicePreview/MyDeviceFrame.tsx | 4 +- website/src/client/components/EditorView.tsx | 399 ++++++++++-------- yarn.lock | 11 +- 6 files changed, 241 insertions(+), 189 deletions(-) diff --git a/website/package.json b/website/package.json index 4f6da1513..edf28d7d8 100644 --- a/website/package.json +++ b/website/package.json @@ -73,6 +73,7 @@ "react-dom": "^16.8.1", "react-helmet-async": "^0.2.0", "react-redux": "^6.0.0", + "react-resizable-panels": "^2.0.9", "react-resize-detector": "^6.7.4", "react-router-dom": "^4.4.0-beta.6", "react-simple-code-editor": "^0.11.0", diff --git a/website/src/client/components/DevicePreview/AppetizeFrame.tsx b/website/src/client/components/DevicePreview/AppetizeFrame.tsx index b6085b865..823f162fe 100644 --- a/website/src/client/components/DevicePreview/AppetizeFrame.tsx +++ b/website/src/client/components/DevicePreview/AppetizeFrame.tsx @@ -226,10 +226,13 @@ function resolveAppetizePopupUrl(config: AppetizeSdkConfig) { const styles = StyleSheet.create({ container: { position: 'relative', - height: 672, + height: '100%', + width: '100%', overflow: 'hidden', margin: 'auto', zIndex: 2, + padding: 32, + boxSizing: 'border-box', }, containerEmbedded: { position: 'relative', @@ -241,5 +244,6 @@ const styles = StyleSheet.create({ frame: { border: 0, height: '100%', + width: '100%', }, }); diff --git a/website/src/client/components/DevicePreview/DevicePreview.tsx b/website/src/client/components/DevicePreview/DevicePreview.tsx index cd524b43e..76990a16c 100644 --- a/website/src/client/components/DevicePreview/DevicePreview.tsx +++ b/website/src/client/components/DevicePreview/DevicePreview.tsx @@ -151,7 +151,7 @@ class DevicePreview extends React.PureComponent { return (
{isEmbedded ? null : (
@@ -218,9 +218,10 @@ export default withThemeName(DevicePreview); const styles = StyleSheet.create({ container: { position: 'relative', - maxWidth: '50%', - overflowX: 'hidden', - overflowY: 'auto', + width: '100%', + // maxWidth: '50%', + // overflowX: 'hidden', + // overflowY: 'auto', display: 'none', flexDirection: 'column', diff --git a/website/src/client/components/DevicePreview/MyDeviceFrame.tsx b/website/src/client/components/DevicePreview/MyDeviceFrame.tsx index 7fa8a7edf..26f7e969d 100644 --- a/website/src/client/components/DevicePreview/MyDeviceFrame.tsx +++ b/website/src/client/components/DevicePreview/MyDeviceFrame.tsx @@ -60,9 +60,10 @@ class MyDeviceFrame extends React.PureComponent { const { experienceURL, isEmbedded, width } = this.props; const { connectedDevices, copiedToClipboard } = this.state; const isConnected = connectedDevices.length >= 1; + const fixedWidthStyle = isEmbedded ? { width } : undefined; return ( -
+
Copied to clipboard!

@@ -265,6 +266,7 @@ const styles = StyleSheet.create({ borderRadius: 4, padding: 10, marginLeft: 10, + margin: '0 auto 16px auto', }, experienceLink: { cursor: 'pointer', diff --git a/website/src/client/components/EditorView.tsx b/website/src/client/components/EditorView.tsx index 900892a35..02751d04d 100644 --- a/website/src/client/components/EditorView.tsx +++ b/website/src/client/components/EditorView.tsx @@ -41,6 +41,8 @@ import { isMobile } from '../utils/detectPlatform'; import { isScript, isJson, isTest } from '../utils/fileUtilities'; import lintFile from '../utils/lintFile'; import prettierCode from '../utils/prettierCode'; +import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; +import constants from '../configs/constants'; const EDITOR_LOAD_FALLBACK_TIMEOUT = 3000; @@ -450,188 +452,208 @@ class EditorView extends React.Component { onDownloadCode={this.props.onDownloadAsync} onPublishAsync={onPublishAsync} /> -
-
- - - {/* Don't load it conditionally since we need the _EditorComponent object to be available */} - }> => { - if (isMobile(userAgent)) { - // Monaco doesn't work great on mobile` - // Use simple editor for better experience - const editor = await import('./Editor/SimpleEditor'); - this.setState({ loadedEditor: 'simple' }); - return editor; - } - - let timeout: any; - - const MonacoEditorPromise = import( - /* webpackPreload: true */ './Editor/MonacoEditor' - ).then((editor) => ({ editor, type: 'monaco' })); - - // Fallback to simple editor if monaco editor takes too long to load - const SimpleEditorPromise = new Promise((resolve, reject) => { - timeout = setTimeout(() => { - this._showBanner('slow-connection'); - - import('./Editor/SimpleEditor').then(resolve, reject); - }, EDITOR_LOAD_FALLBACK_TIMEOUT); - }).then((editor) => ({ editor, type: 'simple' })); - - return Promise.race([ - MonacoEditorPromise.catch(() => SimpleEditorPromise), - SimpleEditorPromise, - ]).then(({ editor, type }: any) => { - this.setState({ loadedEditor: type }); - - clearTimeout(timeout); - - return editor; - }); - }} - > - {({ loaded, data: Comp }) => { - this._EditorComponent = Comp; - const file = files[selectedFile]; - if (file) { - if (file.type === 'ASSET') { - return ; + + +
+ + + {/* Don't load it conditionally since we need the _EditorComponent object to be available */} + }> => { + if (isMobile(userAgent)) { + // Monaco doesn't work great on mobile` + // Use simple editor for better experience + const editor = await import('./Editor/SimpleEditor'); + this.setState({ loadedEditor: 'simple' }); + return editor; } - const { contents } = file; - const isMarkdown = selectedFile.endsWith('.md'); - - if (isMarkdown && this.state.isMarkdownPreview) { - return ( - <> - import('./Markdown/MarkdownPreview')}> - {({ loaded: mdLoaded, data: MarkdownPreview }) => { - if (mdLoaded && MarkdownPreview) { - return ; - } - - return ; - }} - - - - ); - } - - if (loaded && Comp) { - return ( - <> - - {isMarkdown ? ( + let timeout: any; + + const MonacoEditorPromise = import( + /* webpackPreload: true */ './Editor/MonacoEditor' + ).then((editor) => ({ editor, type: 'monaco' })); + + // Fallback to simple editor if monaco editor takes too long to load + const SimpleEditorPromise = new Promise((resolve, reject) => { + timeout = setTimeout(() => { + this._showBanner('slow-connection'); + + import('./Editor/SimpleEditor').then(resolve, reject); + }, EDITOR_LOAD_FALLBACK_TIMEOUT); + }).then((editor) => ({ editor, type: 'simple' })); + + return Promise.race([ + MonacoEditorPromise.catch(() => SimpleEditorPromise), + SimpleEditorPromise, + ]).then(({ editor, type }: any) => { + this.setState({ loadedEditor: type }); + + clearTimeout(timeout); + + return editor; + }); + }} + > + {({ loaded, data: Comp }) => { + this._EditorComponent = Comp; + const file = files[selectedFile]; + if (file) { + if (file.type === 'ASSET') { + return ; + } + + const { contents } = file; + const isMarkdown = selectedFile.endsWith('.md'); + + if (isMarkdown && this.state.isMarkdownPreview) { + return ( + <> + import('./Markdown/MarkdownPreview')}> + {({ loaded: mdLoaded, data: MarkdownPreview }) => { + if (mdLoaded && MarkdownPreview) { + return ; + } + + return ; + }} + - ) : null} - - ); + + ); + } + + if (loaded && Comp) { + return ( + <> + + {isMarkdown ? ( + + ) : null} + + ); + } + } else { + return ; } - } else { - return ; - } - - return ; - }} - - - {preferences.panelsShown ? ( - - ) : null} -
+ + return ; + }} +
+
+ {preferences.panelsShown ? ( + + ) : null} +
+ {previewShown ? ( - + <> + +
+
+
+
+
+ + + + + ) : null} -
+ Date: Thu, 22 Feb 2024 13:00:15 +0100 Subject: [PATCH 2/6] fix(website): hide panel resize when collapsed and restyle handle --- website/src/client/components/EditorView.tsx | 27 +++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/website/src/client/components/EditorView.tsx b/website/src/client/components/EditorView.tsx index 02751d04d..83c4a58f6 100644 --- a/website/src/client/components/EditorView.tsx +++ b/website/src/client/components/EditorView.tsx @@ -2,6 +2,7 @@ import { StyleSheet, css } from 'aphrodite'; import debounce from 'lodash/debounce'; import * as React from 'react'; import { connect } from 'react-redux'; +import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import AssetViewer from './AssetViewer'; import { withDependencyManager } from './DependencyManager'; @@ -35,14 +36,13 @@ import KeybindingsManager from './shared/KeybindingsManager'; import LazyLoad from './shared/LazyLoad'; import ModalDialog from './shared/ModalDialog'; import ProgressIndicator from './shared/ProgressIndicator'; +import constants from '../configs/constants'; import { Viewer, SnackFiles, Annotation, SDKVersion } from '../types'; import Analytics from '../utils/Analytics'; import { isMobile } from '../utils/detectPlatform'; import { isScript, isJson, isTest } from '../utils/fileUtilities'; import lintFile from '../utils/lintFile'; import prettierCode from '../utils/prettierCode'; -import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; -import constants from '../configs/constants'; const EDITOR_LOAD_FALLBACK_TIMEOUT = 3000; @@ -474,7 +474,9 @@ class EditorView extends React.Component { /> {/* Don't load it conditionally since we need the _EditorComponent object to be available */} }> => { + load={async (): Promise<{ + default: React.ComponentType; + }> => { if (isMobile(userAgent)) { // Monaco doesn't work great on mobile` // Use simple editor for better experience @@ -615,11 +617,9 @@ class EditorView extends React.Component { {previewShown ? ( <> - +
-
-
Date: Thu, 22 Feb 2024 13:02:06 +0100 Subject: [PATCH 3/6] chore(website): enable tablet Appetize previews --- website/src/client/utils/Appetize.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/website/src/client/utils/Appetize.ts b/website/src/client/utils/Appetize.ts index 3b594a046..eb06fa6d3 100644 --- a/website/src/client/utils/Appetize.ts +++ b/website/src/client/utils/Appetize.ts @@ -53,9 +53,7 @@ function createAppetizeDeviceList(devices?: typeof cachedAppetizeDevices) { return []; } - return devices - .map((device) => ({ deviceName: device.name, deviceId: device.id })) - .filter(({ deviceId }) => !deviceId.includes('ipad') && !deviceId.includes('galaxytab')); + return devices.map((device) => ({ deviceName: device.name, deviceId: device.id })); } async function fetchAppetizeDevices(): Promise { From c1c30e8573944c7390b5d2528f6e8327bd5efc0c Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Thu, 22 Feb 2024 13:03:42 +0100 Subject: [PATCH 4/6] fix(website): filter Appetize device selector by platform --- website/src/client/utils/Appetize.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/website/src/client/utils/Appetize.ts b/website/src/client/utils/Appetize.ts index eb06fa6d3..bde89f1be 100644 --- a/website/src/client/utils/Appetize.ts +++ b/website/src/client/utils/Appetize.ts @@ -37,7 +37,10 @@ let cachedAppetizeDevices: export function useAppetizeDevices(platform: 'android' | 'ios') { const [devices, setDevices] = useState(cachedAppetizeDevices); - const deviceList = useMemo(() => createAppetizeDeviceList(devices), [platform, devices]); + const deviceList = useMemo( + () => createAppetizeDeviceList(platform, devices), + [platform, devices] + ); useEffect(() => { if (!cachedAppetizeDevices) { @@ -48,12 +51,17 @@ export function useAppetizeDevices(platform: 'android' | 'ios') { return deviceList; } -function createAppetizeDeviceList(devices?: typeof cachedAppetizeDevices) { +function createAppetizeDeviceList( + platform: 'android' | 'ios', + devices?: typeof cachedAppetizeDevices +) { if (!devices) { return []; } - return devices.map((device) => ({ deviceName: device.name, deviceId: device.id })); + return devices + .filter((device) => device.platform === platform) + .map((device) => ({ deviceName: device.name, deviceId: device.id })); } async function fetchAppetizeDevices(): Promise { From ef2805db88c7d1d396a82001469e20cfc72ec25e Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Thu, 22 Feb 2024 13:15:12 +0100 Subject: [PATCH 5/6] fix(website): keep panel resize hover style when dragging --- website/src/client/components/EditorView.tsx | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/website/src/client/components/EditorView.tsx b/website/src/client/components/EditorView.tsx index 83c4a58f6..7158eee29 100644 --- a/website/src/client/components/EditorView.tsx +++ b/website/src/client/components/EditorView.tsx @@ -80,6 +80,7 @@ type State = { lintedFiles: LintedFiles; lintAnnotations: Annotation[]; shouldPreventRedirectWarning: boolean; + isPanelResizing: boolean; }; const BANNER_TIMEOUT_SHORT = 1500; @@ -95,6 +96,7 @@ class EditorView extends React.Component { lintedFiles: {}, lintAnnotations: [], shouldPreventRedirectWarning: false, + isPanelResizing: false, }; static getDerivedStateFromProps(props: Props, state: State) { @@ -347,6 +349,12 @@ class EditorView extends React.Component { shouldPreventRedirectWarning: false, }); + onPanelResizing = (isDragging: boolean) => { + this.setState({ + isPanelResizing: isDragging, + }); + }; + render() { const { currentModal, currentBanner, lintAnnotations } = this.state; @@ -617,7 +625,13 @@ class EditorView extends React.Component { {previewShown ? ( <> - +
@@ -815,6 +829,11 @@ const styles = StyleSheet.create({ }, }, + // Note(cedric): aphrodite does not support styling by attribute, so this is now handled by state + panelResizeTouchAreaActive: { + backgroundColor: c('hover'), + }, + resizeHandleContainer: { position: 'relative', top: '50%', From 29af5bf352a57ba619e3c4cd356b3a3c9a59318f Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Fri, 23 Feb 2024 09:50:11 +0100 Subject: [PATCH 6/6] refactor(website): clean up unused styling --- website/src/client/components/DevicePreview/DevicePreview.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/website/src/client/components/DevicePreview/DevicePreview.tsx b/website/src/client/components/DevicePreview/DevicePreview.tsx index 76990a16c..e29c97072 100644 --- a/website/src/client/components/DevicePreview/DevicePreview.tsx +++ b/website/src/client/components/DevicePreview/DevicePreview.tsx @@ -219,9 +219,6 @@ const styles = StyleSheet.create({ container: { position: 'relative', width: '100%', - // maxWidth: '50%', - // overflowX: 'hidden', - // overflowY: 'auto', display: 'none', flexDirection: 'column',