Skip to content

Commit df24f7e

Browse files
authored
[Examples Browser] Updates & fixes (playcanvas#3333)
* update the playcanvas dependancy in the examples browser to 1.44.2 * filter out code editor warnings when validating user code edits * support performance and debug builds of playcanvas for individual engine examples * include performance playcanvas in the examples iframe file * show mini stats toggle no longer refreshes the current example * including other functions before the example function in Example classes no longer breaks the build * all paths that do not lead to a specific example now load the hello world example * when switching thumbnail sizes in the side bar, maintain the position of the top most nav item * when first opening the examples browser via a specific url, scroll that examples nav item into view in the side bar * show the code panel on mobile even when no controls are present for an example * stop the input of text in the filter input from closing the side bar on mobile * comments in an example with no indentation no longer breaks example builds * fix ts error in clustered lighting example * only strip out whitespace from the first four columns of the example function lines * correctly hide the controls panel in fullscreen mode on mobile and move the menu to the top in fullscreen mode on mobile so it doesn't cover the ministats
1 parent a492825 commit df24f7e

File tree

13 files changed

+158
-95
lines changed

13 files changed

+158
-95
lines changed

examples/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"html-webpack-plugin": "^5.3.2",
6363
"monaco-editor": "^0.25.2",
6464
"monaco-editor-webpack-plugin": "^4.0.0",
65-
"playcanvas": "^1.43.1",
65+
"playcanvas": "^1.44.2",
6666
"prop-types": "^15.7.2",
6767
"puppeteer": "^10.1.0",
6868
"raw-loader": "^4.0.2",

examples/src/app/code-editor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ const CodeEditor = (props: CodeEditorProps) => {
4343
};
4444

4545
const onValidate = (markers: Array<any>) => {
46-
if (markers.length === 0) {
46+
// filter out markers which are warnings
47+
if (markers.filter((m) => m.severity > 1).length === 0) {
4748
props.setFiles(files);
4849
props.setLintErrors(false);
4950
} else {

examples/src/app/control-panel.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,12 @@ const ControlPanel = (props: any) => {
4848
controls.ui.hidden = !controls.ui.hidden;
4949
};
5050

51-
return <Panel id='controlPanel' class={[!props.controls ? 'empty' : 'null', window.top.innerWidth < 601 ? 'mobile' : null]} resizable='top' headerText='CONTROLS' collapsible={true} collapsed={state.collapsed}>
52-
<Container id= 'controlPanel-tabs' class='tabs-container'>
53-
{props.controls && <Button text='PARAMETERS' class={state.showParameters ? 'selected' : null} id='paramButton' onClick={onClickParametersTab} /> }
51+
return <Panel id='controlPanel' class={[window.top.innerWidth > 600 && !props.controls ? 'empty' : 'null', window.top.innerWidth < 601 ? 'mobile' : null]} resizable='top' headerText={window.top.innerWidth < 601 && props.controls ? 'CONTROLS & CODE' : 'CODE'} collapsible={true} collapsed={state.collapsed}>
52+
{ props.controls && <Container id= 'controlPanel-tabs' class='tabs-container'>
53+
<Button text='PARAMETERS' class={state.showParameters ? 'selected' : null} id='paramButton' onClick={onClickParametersTab} />
5454
<Button text='CODE' id='codeButton' class={state.showCode ? 'selected' : null} onClick={onClickCodeTab}/>
5555
</Container>
56+
}
5657
<Container id='controlPanel-controls'>
5758
{ props.controls }
5859
</Container>

examples/src/app/example-iframe.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import * as playcanvas from 'playcanvas/build/playcanvas.js';
66
// @ts-ignore: library file import
77
import * as playcanvasDebug from 'playcanvas/build/playcanvas.dbg.js';
88
// @ts-ignore: library file import
9+
import * as playcanvasPerformance from 'playcanvas/build/playcanvas.prf.js';
10+
// @ts-ignore: library file import
911
import * as pcx from 'playcanvas/build/playcanvas-extras.js';
1012
// @ts-ignore: library file import
1113
import * as Babel from '@babel/standalone';
@@ -27,14 +29,18 @@ interface ExampleIframeProps {
2729
controls: any,
2830
assets: any,
2931
files: Array<File>,
30-
debug?: boolean,
32+
engine: string,
3133
debugExample?: any
3234
}
3335

3436
const ExampleIframe = (props: ExampleIframeProps) => {
35-
let pc = playcanvas;
36-
if (props.debug) {
37+
let pc: any;
38+
if (props.engine === 'DEBUG') {
3739
pc = playcanvasDebug;
40+
} else if (props.engine === 'PERFORMANCE') {
41+
pc = playcanvasPerformance;
42+
} else {
43+
pc = playcanvas;
3844
}
3945
// expose PlayCanvas as a global in the iframe
4046
(window as any).pc = pc;
@@ -50,13 +56,6 @@ const ExampleIframe = (props: ExampleIframeProps) => {
5056
} catch (e) {
5157
files = props.files;
5258
}
53-
let showMiniStats = false;
54-
// Try to retrieve a set of B64 encoded files from the URL's query params.
55-
// If not present then use the default files passed in the props
56-
try {
57-
showMiniStats = location.hash.split('showMiniStats=')[1].split('&')[0] === 'true';
58-
} catch (e) {
59-
}
6059

6160
const fullscreen = location.hash.includes('fullscreen=true');
6261

@@ -134,9 +133,10 @@ const ExampleIframe = (props: ExampleIframeProps) => {
134133
graphicsDeviceOptions: { alpha: true }
135134
});
136135

137-
if (showMiniStats) {
138-
const miniStats = new pcx.MiniStats(app);
139-
}
136+
const miniStats: any = new pcx.MiniStats(app);
137+
app.on('update', () => {
138+
miniStats.enabled = (window?.parent as any)?._showMiniStats;
139+
});
140140

141141
(window as any).app = app;
142142

examples/src/app/example.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ interface ExampleProps {
1010
files: Array<File>,
1111
defaultFiles: Array<File>,
1212
setDefaultFiles: (value: Array<File>) => void,
13-
path: string,
14-
showMiniStats: boolean
13+
path: string
1514
}
1615

1716
interface ExampleState {
@@ -57,7 +56,7 @@ class Example extends Component <ExampleProps, ExampleState> {
5756
}
5857

5958
get iframePath() {
60-
return `/#/iframe${this.props.path}?showMiniStats=${this.props.showMiniStats}&files=${btoa(encodeURIComponent(JSON.stringify(this.files)))}`;
59+
return `/#/iframe${this.props.path}?files=${btoa(encodeURIComponent(JSON.stringify(this.files)))}`;
6160
}
6261

6362
addAssets(app: pc.Application, assets?: any) {

examples/src/app/helpers/raw-file-loading.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const examples = (() => {
2121
importAll(require.context('../../examples/', true, /\.tsx$/));
2222

2323
const categories: any = {};
24-
const paths: Array<{ path: string, example: any, files: Array<File> }> = [];
24+
const paths: {[key: string]: { path: string, example: any, files: Array<File> }} = {};
2525

2626
Object.keys(exampleFiles).forEach((key: string) => {
2727
if (key.indexOf('./') === 0) {
@@ -62,21 +62,26 @@ export const examples = (() => {
6262
};
6363

6464
const transformedCode = require(`!raw-loader!../../examples/${categorySlug}/${nameSlug}.tsx`).default;
65-
const functionSignatureString = '): void {';
66-
const indexOfAppCallStart = transformedCode.indexOf(functionSignatureString);
67-
const indexOfAppCallEnd = findClosingBracketMatchIndex(transformedCode, indexOfAppCallStart + functionSignatureString.length - 1);
68-
let functionCall = 'function ' + transformedCode.substring(transformedCode.indexOf('example('), indexOfAppCallEnd + 1);
69-
functionCall = functionCall.split('\n')
65+
const functionSignatureStartString = 'example(canvas: HTMLCanvasElement';
66+
const indexOfFunctionSignatureStart = transformedCode.indexOf(functionSignatureStartString);
67+
const functionSignatureEndString = '): void ';
68+
const indexOfFunctionSignatureEnd = indexOfFunctionSignatureStart + transformedCode.substring(indexOfFunctionSignatureStart).indexOf(functionSignatureEndString) + functionSignatureEndString.length;
69+
const indexOfFunctionEnd = findClosingBracketMatchIndex(transformedCode, indexOfFunctionSignatureEnd) + 1;
70+
let functionText = 'function ' + transformedCode.substring(indexOfFunctionSignatureStart, indexOfFunctionEnd);
71+
functionText = functionText.split('\n')
7072
.map((line: string, index: number) => {
7173
if (index === 0) return line;
74+
if (line.substring(0, 4).split('').filter((a) => a !== ' ').length > 0) {
75+
return line;
76+
}
7277
return line.substring(4);
7378
})
7479
.join('\n');
7580

7681
const files: Array<File> = [
7782
{
7883
name: 'example.ts',
79-
text: functionCall
84+
text: functionText
8085
}
8186
];
8287
// @ts-ignore
@@ -103,11 +108,11 @@ export const examples = (() => {
103108
});
104109
}
105110

106-
paths.push({
111+
paths[`/${categorySlug}/${nameSlug}`] = {
107112
path: `/${categorySlug}/${nameSlug}`,
108113
example: exampleFiles[key].default,
109114
files: files
110-
});
115+
};
111116
}
112117
});
113118

examples/src/app/index.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
33
// @ts-ignore: library file import
44
import { Container } from '@playcanvas/pcui/pcui-react';
55
// @ts-ignore: library file import
6-
import { HashRouter as Router, Switch, Route, Redirect } from "react-router-dom";
6+
import { HashRouter as Router, Switch, Route } from "react-router-dom";
77
import SideBar from './sidebar';
88
import CodeEditor from './code-editor';
99
import ExampleIframe from './example-iframe';
@@ -14,19 +14,22 @@ import './styles.css';
1414

1515
interface ExampleRoutesProps {
1616
files: Array<File>,
17-
setDefaultFiles: (files: Array<File>) => void,
18-
showMiniStats: boolean
17+
setDefaultFiles: (files: Array<File>) => void
1918
}
2019
const ExampleRoutes = (props: ExampleRoutesProps) => {
20+
const defaultExample = examples.paths['/misc/hello-world'];
2121
return (
2222
<Switch>
2323
{
24-
examples.paths.map((p) => {
24+
Object.values(examples.paths).map((p) => {
2525
return <Route key={p.path} path={[p.path, `${p.path}.html`]}>
26-
<p.example path={p.path} defaultFiles={p.files} files={props.files} setDefaultFiles={props.setDefaultFiles} showMiniStats={props.showMiniStats} />
26+
<p.example path={p.path} defaultFiles={p.files} files={props.files} setDefaultFiles={props.setDefaultFiles} />
2727
</Route>;
2828
})
2929
}
30+
<Route path='/'>
31+
<defaultExample.example path={defaultExample.path} defaultFiles={defaultExample.files} files={props.files} setDefaultFiles={props.setDefaultFiles} />
32+
</Route>
3033
</Switch>
3134
);
3235
};
@@ -57,7 +60,10 @@ const MainLayout = () => {
5760
// The example files are the files that should be loaded and executed by the example. Upon hitting the play button, the currently set edited files are set to the example files
5861
const [exampleFiles, setExampleFiles] = useState(emptyFiles);
5962
const [lintErrors, setLintErrors] = useState(false);
60-
const [showMiniStats, setShowMiniStats] = useState(false);
63+
64+
const updateShowMiniStats = (value: boolean) => {
65+
(window as any)._showMiniStats = value;
66+
};
6167

6268
const playButtonRef = createRef();
6369
useEffect(() => {
@@ -88,30 +94,27 @@ const MainLayout = () => {
8894
<div id='appInner'>
8995
<Router>
9096
<Switch>
91-
<Route exact path="/">
92-
<Redirect to="/misc/hello-world" />
93-
</Route>
9497
{
95-
examples.paths.map((p) => {
98+
Object.values(examples.paths).map((p) => {
9699
const e = new p.example();
97100
const assetsLoader = e.load;
98101
const controls = e.controls;
99102
return [
100103
<Route key={`/iframe${p.path}`} path={[`/iframe${p.path}`]}>
101-
<ExampleIframe controls={controls} assets={assetsLoader ? assetsLoader().props.children : null} files={p.files} debug={p.path.includes('mini-stats')}/>
104+
<ExampleIframe controls={controls} assets={assetsLoader ? assetsLoader().props.children : null} engine={e.constructor.ENGINE} files={p.files} />
102105
</Route>,
103106
<Route key={`/debug${p.path}`} path={[`/debug${p.path}`]}>
104-
<ExampleIframe controls={controls} assets={assetsLoader ? assetsLoader().props.children : null} files={p.files} debug={p.path.includes('mini-stats')} debugExample={e}/>
107+
<ExampleIframe controls={controls} assets={assetsLoader ? assetsLoader().props.children : null} engine={e.constructor.ENGINE} files={p.files} debugExample={e}/>
105108
</Route>
106109
];
107110
})
108111
}
109-
<Route key='main' path={`/`}>
112+
<Route key='main' path='/'>
110113
<SideBar categories={examples.categories}/>
111114
<Container id='main-view-wrapper'>
112-
<Menu lintErrors={lintErrors} hasEditedFiles={hasEditedFiles()} playButtonRef={playButtonRef} showMiniStats={showMiniStats} setShowMiniStats={setShowMiniStats} />
115+
<Menu lintErrors={lintErrors} hasEditedFiles={hasEditedFiles()} playButtonRef={playButtonRef} setShowMiniStats={updateShowMiniStats} />
113116
<Container id='main-view'>
114-
<ExampleRoutes files={exampleFiles} setDefaultFiles={updateExample.bind(this)} showMiniStats={showMiniStats} />
117+
<ExampleRoutes files={exampleFiles} setDefaultFiles={updateExample.bind(this)} />
115118
<CodeEditor files={editedFiles[0].text.length > 0 ? editedFiles : defaultFiles} setFiles={setEditedFiles.bind(this)} setLintErrors={setLintErrors} />
116119
</Container>
117120
</Container>

examples/src/app/menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ interface MenuProps {
77
lintErrors: boolean,
88
hasEditedFiles: boolean,
99
playButtonRef: any,
10-
showMiniStats: boolean,
1110
setShowMiniStats: (value: boolean) => void
1211
}
1312
const Menu = (props: MenuProps) => {
@@ -23,6 +22,7 @@ const Menu = (props: MenuProps) => {
2322
document.querySelector('#canvas-container').classList.toggle('fullscreen');
2423
const app = document.querySelector('#appInner');
2524
app.classList.toggle('fullscreen');
25+
document.querySelector('iframe').contentDocument.getElementById('appInner').classList.toggle('fullscreen');
2626
if (app.classList.contains('fullscreen')) {
2727
clickFullscreenListener = () => {
2828
app.classList.add('active');

0 commit comments

Comments
 (0)