From 83f0396a482261c8e74e71008ec596f7e65d75d6 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 28 Jan 2024 00:11:00 +0000 Subject: [PATCH 01/11] cleaned up linting --- examples/src/app/MainLayout.mjs | 31 +++--- examples/src/app/code-editor.mjs | 76 ++++++++++---- examples/src/app/device-selector.mjs | 27 +++-- examples/src/app/error-boundary.mjs | 80 +++++++------- examples/src/app/example.mjs | 122 +++++++++++++--------- examples/src/app/helpers/example-data.mjs | 3 + examples/src/app/helpers/strings.mjs | 34 +++--- examples/src/app/iframeUtils.mjs | 1 + examples/src/app/index.mjs | 1 + examples/src/app/jsx.mjs | 2 +- examples/src/app/menu.mjs | 52 +++++---- examples/src/app/polyfillFunctionCall.mjs | 8 +- examples/src/app/polyfills.mjs | 3 +- examples/src/app/sidebar.mjs | 105 +++++++++++-------- examples/src/app/utils.mjs | 1 + examples/src/assetPath.mjs | 22 ++-- examples/src/iframe/index.mjs | 1 + examples/src/importmap.js | 2 + examples/src/loadES5.mjs | 3 +- 19 files changed, 346 insertions(+), 228 deletions(-) diff --git a/examples/src/app/MainLayout.mjs b/examples/src/app/MainLayout.mjs index 49bd6e21ca6..eecdaa6f0e1 100644 --- a/examples/src/app/MainLayout.mjs +++ b/examples/src/app/MainLayout.mjs @@ -1,15 +1,16 @@ -import { Component } from 'react'; +import { Component } from 'react'; import { HashRouter, Switch, Route, Redirect } from 'react-router-dom'; -import { CodeEditor } from './code-editor.mjs'; -import { ErrorBoundary } from './error-boundary.mjs'; -import { Example } from './example.mjs'; -import { iframeHideStats, iframeShowStats } from './iframeUtils.mjs'; -import { jsx } from './jsx.mjs'; -import { Menu } from './menu.mjs'; -import { SideBar } from './sidebar.mjs'; -import { getOrientation } from './utils.mjs'; -import { Container } from '@playcanvas/pcui/react'; +import { CodeEditor } from './code-editor.mjs'; +import { ErrorBoundary } from './error-boundary.mjs'; +import { Example } from './example.mjs'; +import { iframeHideStats, iframeShowStats } from './iframeUtils.mjs'; +import { jsx } from './jsx.mjs'; +import { Menu } from './menu.mjs'; +import { SideBar } from './sidebar.mjs'; +import { getOrientation } from './utils.mjs'; +import { Container } from '@playcanvas/pcui/react'; +// eslint-disable-next-line jsdoc/require-property /** * @typedef {object} Props */ @@ -25,7 +26,7 @@ const TypedComponent = Component; class MainLayout extends TypedComponent { /** @type {State} */ state = { - orientation: getOrientation(), + orientation: getOrientation() }; componentDidMount() { @@ -44,20 +45,20 @@ class MainLayout extends TypedComponent { } /** - * @param {boolean} value + * @param {boolean} value - Show MiniStats state. */ updateShowMiniStats = (value) => { - // console.log("updateShowMiniStats", value); if (value) { iframeShowStats(); } else { iframeHideStats(); } - } + }; render() { const { orientation } = this.state; - return jsx("div", { id: 'appInner' }, + return jsx( + "div", { id: 'appInner' }, jsx(HashRouter, null, jsx(Switch, null, jsx(Route, { exact: true, path: '/' }, diff --git a/examples/src/app/code-editor.mjs b/examples/src/app/code-editor.mjs index c1e08a66fae..421ec827ed6 100644 --- a/examples/src/app/code-editor.mjs +++ b/examples/src/app/code-editor.mjs @@ -11,7 +11,7 @@ loader.config({ paths: { vs: './node_modules/monaco-editor/min/vs' } }); function getShowMinimap() { let showMinimap = true; if (localStorage.getItem("showMinimap")) { - showMinimap = localStorage.getItem("showMinimap") === 'true' ? true : false; + showMinimap = localStorage.getItem("showMinimap") === 'true'; } return showMinimap; } @@ -23,20 +23,24 @@ const FILE_TYPE_LANGUAGES = { 'frag': null, 'javascript': 'javascript', 'js': 'javascript', - 'mjs': 'javascript', + 'mjs': 'javascript' }; +/** + * @type {import('monaco-editor').editor.IStandaloneCodeEditor} + */ let monacoEditor; +// eslint-disable-next-line jsdoc/require-property /** * @typedef {object} Props */ /** * @typedef {object} State - * @property {Record} files - * @property {string} selectedFile - * @property {boolean} showMinimap + * @property {Record} files - The example files. + * @property {string} selectedFile - The selected file. + * @property {boolean} showMinimap - The state of showing the Minimap */ /** @type {typeof Component} */ @@ -45,7 +49,7 @@ const TypedComponent = Component; class CodeEditor extends TypedComponent { /** @type {State} */ state = { - files: {'example.mjs': '// init'}, + files: { 'example.mjs': '// init' }, selectedFile: 'example.mjs', showMinimap: getShowMinimap() }; @@ -75,26 +79,39 @@ class CodeEditor extends TypedComponent { window.removeEventListener("requestedFiles", this.handleRequestedFiles); } + /** + * @param {Event} event - The event. + */ handleExampleLoad(event) { // console.log("CodeEditor got files event", event); /** @type {Record} */ + // @ts-ignore const files = event.files; this.mergeState({ files, selectedFile: 'example.mjs' }); } + /** + * @param {Event} event - The event. + */ + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars handleExampleLoading(event) { this.mergeState({ - files: {'example.mjs': '// reloading'} + files: { 'example.mjs': '// reloading' } }); } + /** + * @param {Event} event - The event. + */ handleRequestedFiles(event) { + // @ts-ignore const files = event.detail; this.mergeState({ files }); } /** - * @param {import('@monaco-editor/react').Monaco} monaco + * @param {import('@monaco-editor/react').Monaco} monaco - The monaco editor. */ beforeMount(monaco) { fetch(pcTypes).then((r) => { @@ -112,30 +129,37 @@ class CodeEditor extends TypedComponent { } /** - * @param {import('monaco-editor').editor.IStandaloneCodeEditor} editor + * @param {import('monaco-editor').editor.IStandaloneCodeEditor} editor - The monaco editor. */ editorDidMount(editor) { + // @ts-ignore window.editor = editor; monacoEditor = editor; // Hot reload code via Shift + Enter + // @ts-ignore + // eslint-disable-next-line no-undef editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, iframeHotReload); const codePane = document.getElementById('codePane'); + if (!codePane) { + return; + } codePane.classList.add('multiple-files'); if (!this.state.files[this.state.selectedFile]) { this.mergeState({ - selectedFile: 'example.mjs', + selectedFile: 'example.mjs' }); } // @ts-ignore - codePane.ui.on('resize', () => { - localStorage.setItem('codePaneStyle', codePane.getAttribute('style')); - }); + codePane.ui.on('resize', () => localStorage.setItem('codePaneStyle', codePane.getAttribute('style'))); const codePaneStyle = localStorage.getItem('codePaneStyle'); if (codePaneStyle) { codePane.setAttribute('style', codePaneStyle); } // set up the code panel toggle button const panelToggleDiv = codePane.querySelector('.panel-toggle'); + if (!panelToggleDiv) { + return; + } panelToggleDiv.addEventListener('click', function () { codePane.classList.toggle('collapsed'); localStorage.setItem('codePaneCollapsed', codePane.classList.contains('collapsed') ? 'true' : 'false'); @@ -148,6 +172,8 @@ class CodeEditor extends TypedComponent { // keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter], // contextMenuGroupId: 'navigation', contextMenuOrder: 1.5, + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars run: (editor) => { const showMinimap = !getShowMinimap(); localStorage.setItem("showMinimap", `${showMinimap}`); @@ -157,7 +183,7 @@ class CodeEditor extends TypedComponent { } /** - * @param {string} value + * @param {string} value - The on change state. */ onChange(value) { const { files, selectedFile } = this.state; @@ -185,10 +211,11 @@ class CodeEditor extends TypedComponent { for (const name in files) { const button = jsx(Button, { key: name, + // @ts-ignore id: `code-editor-file-tab-${name}`, text: name, class: name === selectedFile ? 'selected' : null, - onClick: () => this.selectFile(name), + onClick: () => this.selectFile(name) }); tabs.push(button); } @@ -198,6 +225,7 @@ class CodeEditor extends TypedComponent { render() { setTimeout(iframeResize, 50); const { files, selectedFile, showMinimap } = this.state; + // @ts-ignore const language = FILE_TYPE_LANGUAGES[selectedFile.split('.').pop()]; let value = files[selectedFile]; if (value) { @@ -221,18 +249,19 @@ class CodeEditor extends TypedComponent { minimap: { enabled: showMinimap } - }, + } /** - * @todo Without a key the syntax highlighting mode isn't updated. + * TODO: Without a key the syntax highlighting mode isn't updated. * But WITH a key the theme information isn't respected any longer... this * is probably a Monaco bug, which we need to file. Related: * https://github.com/microsoft/monaco-editor/issues/1713 - */ - //key: selectedFile, + */ + // key: selectedFile, }; return jsx( Panel, { + // @ts-ignore headerText: 'CODE', id: 'codePane', class: localStorage.getItem('codePaneCollapsed') === 'true' ? 'collapsed' : null, @@ -249,16 +278,19 @@ class CodeEditor extends TypedComponent { jsx( Container, { + // @ts-ignore class: 'tabs-wrapper' }, jsx( Container, { + // @ts-ignore class: 'code-editor-menu-container' }, jsx( Button, { + // @ts-ignore id: 'play-button', icon: 'E304', text: '', @@ -267,6 +299,7 @@ class CodeEditor extends TypedComponent { ), jsx( Button, { + // @ts-ignore icon: 'E259', text: '', onClick: () => { @@ -279,14 +312,15 @@ class CodeEditor extends TypedComponent { jsx( Container, { + // @ts-ignore class: 'tabs-container' }, - this.renderTabs(), + this.renderTabs() ) ), jsx(MonacoEditor, options) ); - }; + } } export { CodeEditor }; diff --git a/examples/src/app/device-selector.mjs b/examples/src/app/device-selector.mjs index a0c50031eab..7c858546247 100644 --- a/examples/src/app/device-selector.mjs +++ b/examples/src/app/device-selector.mjs @@ -11,12 +11,12 @@ const deviceTypeNames = { [DEVICETYPE_WEBGL1]: 'WebGL 1', [DEVICETYPE_WEBGL2]: 'WebGL 2', [DEVICETYPE_WEBGPU]: 'WebGPU', - [DEVICETYPE_NULL ]: 'Null', + [DEVICETYPE_NULL]: 'Null' }; /** * @typedef {object} Props - * @property {Function} onSelect + * @property {Function} onSelect - On select handler. */ /** @@ -33,15 +33,16 @@ class DeviceSelector extends TypedComponent { state = { fallbackOrder: null, disabledOptions: null, - activeDevice: this.preferredGraphicsDevice, + activeDevice: this.preferredGraphicsDevice }; /** - * @param {Props} props + * @param {Props} props - Component properties. */ constructor(props) { super(props); - window.addEventListener('updateActiveDevice', event => { + window.addEventListener('updateActiveDevice', (event) => { + // @ts-ignore const activeDevice = event.detail; this.onSetActiveGraphicsDevice(activeDevice); }); @@ -61,10 +62,12 @@ class DeviceSelector extends TypedComponent { */ set preferredGraphicsDevice(value) { localStorage.setItem('preferredGraphicsDevice', value); + // @ts-ignore window.preferredGraphicsDevice = value; } get preferredGraphicsDevice() { + // @ts-ignore return window.preferredGraphicsDevice; } @@ -106,14 +109,14 @@ class DeviceSelector extends TypedComponent { */ updateMiniStats(value) { const disableMiniStats = value === DEVICETYPE_WEBGPU || value === DEVICETYPE_NULL; - const miniStatsEnabled = document.getElementById('showMiniStatsButton').ui.class.contains('selected'); + const miniStatsEnabled = document.getElementById('showMiniStatsButton')?.ui.class.contains('selected'); if (disableMiniStats && miniStatsEnabled) { - document.getElementById('showMiniStatsButton').ui.class.remove('selected'); + document.getElementById('showMiniStatsButton')?.ui.class.remove('selected'); } } /** - * @param {string} value + * @param {string} value - Is graphics device active */ onSetActiveGraphicsDevice(value) { if (!this.preferredGraphicsDevice) { @@ -124,7 +127,7 @@ class DeviceSelector extends TypedComponent { } /** - * @param {string} value - The newly picked graphics device. + * @param {string} value - The newly picked graphics device. */ onSetPreferredGraphicsDevice(value) { this.mergeState({ disabledOptions: null, activeDevice: value }); @@ -141,14 +144,16 @@ class DeviceSelector extends TypedComponent { { t: deviceTypeNames[DEVICETYPE_WEBGL1], v: DEVICETYPE_WEBGL1 }, { t: deviceTypeNames[DEVICETYPE_WEBGL2], v: DEVICETYPE_WEBGL2 }, { t: deviceTypeNames[DEVICETYPE_WEBGPU], v: DEVICETYPE_WEBGPU }, - { t: deviceTypeNames[DEVICETYPE_NULL ], v: DEVICETYPE_NULL }, + { t: deviceTypeNames[DEVICETYPE_NULL], v: DEVICETYPE_NULL } ], value: activeDevice, + // @ts-ignore fallbackOrder, + // @ts-ignore disabledOptions, onSelect: this.onSetPreferredGraphicsDevice.bind(this), prefix: 'Active Device: ' - }) + }); } } diff --git a/examples/src/app/error-boundary.mjs b/examples/src/app/error-boundary.mjs index ac69758e875..57e64cf24b4 100644 --- a/examples/src/app/error-boundary.mjs +++ b/examples/src/app/error-boundary.mjs @@ -1,41 +1,49 @@ import { Component } from "react"; import { fragment, jsx } from "./jsx.mjs"; class ErrorBoundary extends Component { - constructor(props) { - super(props); - this.state = { hasError: false }; - } - static getDerivedStateFromError(error) { - // Update state so the next render will show the fallback UI. - return { hasError: true }; - } - componentDidCatch(error, errorInfo) { - // You can also log the error to an error reporting service - //logErrorToMyService(error, errorInfo); - console.warn(error, errorInfo); - } - resetState() { - console.log("reset error"); - this.setState({hasError: false}) - } - render() { - if (this.state.hasError) { - // You can render any custom fallback UI - //return

Something went wrong.

; - return fragment( - jsx("pre", null, "Something went wrong."), - jsx( - 'button', - { - onClick: this.resetState.bind(this), - }, - 'retry' - ) - ); + /** + * @param {any} props - The properties. + */ + constructor(props) { + super(props); + this.state = { hasError: false }; + } + + /** + * @returns {object} - The state. + */ + static getDerivedStateFromError() { + // Update state so the next render will show the fallback UI. + return { hasError: true }; + } + + /** + * @param {any} error - The error. + * @param {any} errorInfo - The error info. + */ + componentDidCatch(error, errorInfo) { + // You can also log the error to an error reporting service + // logErrorToMyService(error, errorInfo); + console.warn(error, errorInfo); + } + + resetState() { + console.log("reset error"); + this.setState({ hasError: false }); + } + + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + // return

Something went wrong.

; + return fragment( + jsx("pre", null, "Something went wrong."), + jsx("button", { + onClick: this.resetState.bind(this) + }, "retry") + ); + } + return this.props.children; } - return this.props.children; - } } -export { - ErrorBoundary -}; +export { ErrorBoundary }; diff --git a/examples/src/app/example.mjs b/examples/src/app/example.mjs index d476235606b..2b53aa9867a 100644 --- a/examples/src/app/example.mjs +++ b/examples/src/app/example.mjs @@ -25,7 +25,7 @@ import * as ReactPCUI from '@playcanvas/pcui/react'; /** * @typedef {object} Props - * @property {{params: {category: string, example: string}}} match + * @property {{params: {category: string, example: string}}} match - The match object. */ /** @@ -33,7 +33,7 @@ import * as ReactPCUI from '@playcanvas/pcui/react'; * @property {'portrait' | 'landscape'} orientation - The orientation. * @property {boolean} collapsed - Collapsed or not. * @property {boolean} exampleLoaded - Example is loaded or not. - * @property {Function} controls - Controls function from example. + * @property {Function | null} controls - Controls function from example. * @property {'code' | 'parameters'} show - Used in case of mobile view. * @property {Record} files - Files of example (controls, shaders, example itself) * @property {string} description - Description of example. @@ -46,14 +46,15 @@ class Example extends TypedComponent { /** @type {State} */ state = { orientation: getOrientation(), + // @ts-ignore collapsed: window.top.innerWidth < MIN_DESKTOP_WIDTH, exampleLoaded: false, - //controls: () => jsx('pre', null, 'Status: initial'), controls: () => undefined, + // @ts-ignore showDeviceSelector: true, show: 'code', - files: {'example.mjs': '// loading'}, - description: '', + files: { 'example.mjs': '// loading' }, + description: '' }; /** @@ -72,23 +73,31 @@ class Example extends TypedComponent { this.mergeState({ orientation: getOrientation() }); } + /** + * @param {Event} event - Event. + */ onExampleLoading(event) { this.mergeState({ exampleLoaded: false, - //controls: () => jsx('h1', null, 'state: reload'), controls: null, - showDeviceSelector: event.detail.showDeviceSelector, + // @ts-ignore + showDeviceSelector: event.detail.showDeviceSelector }); } + /** + * @param {Event} event - Event. + */ onExampleLoad(event) { - /** @type {Record} */ + // @ts-ignore const { files, description } = event; + // @ts-ignore const controlsSrc = files['controls.mjs']; if (controlsSrc) { let controls; try { - controls = Function('return ' + controlsSrc)(); + // eslint-disable-next-line no-new-func + controls = new Function('return ' + controlsSrc)(); } catch (e) { controls = () => jsx('pre', null, 'error: ' + e.toString()); } @@ -96,47 +105,55 @@ class Example extends TypedComponent { exampleLoaded: true, controls, files, - description, + description }); - // console.log("controlsSrc", controlsSrc); } else { // When switching examples from one with controls to one without controls... this.mergeState({ exampleLoaded: true, controls: null, files, - description, + description }); } } + /** + * @param {Event} event - Event. + */ onUpdateFiles(event) { + // @ts-ignore const files = event.detail.files; - // console.log("updateFiles", files); const controlsSrc = files['controls.mjs'] ?? 'null'; if (!files['controls.mjs']) { this.mergeState({ exampleLoaded: true, - controls: null, + controls: null }); } let controls; try { - controls = Function('return ' + controlsSrc)(); + // eslint-disable-next-line no-new-func + controls = new Function('return ' + controlsSrc)(); } catch (e) { controls = () => jsx('pre', null, e.toString()); } this.mergeState({ exampleLoaded: true, - controls, + controls }); } - + componentDidMount() { // PCUI should just have a "onHeaderClick" but can't find anything const controlPanel = document.getElementById("controlPanel"); + if (!controlPanel) { + return; + } const controlPanelHeader = controlPanel.querySelector('.pcui-panel-header'); + // @ts-ignore controlPanelHeader.onclick = () => this.toggleCollapse(); + // Other events this.handleRequestedFiles = this.handleRequestedFiles.bind(this); this.onLayoutChange = this.onLayoutChange.bind(this); @@ -166,13 +183,16 @@ class Example extends TypedComponent { } get iframePath() { + /** @type {{ category: string; name: string }} */ + // @ts-ignore const example = examples.paths[this.path]; return `${iframePath}/${example.category}_${example.name}.html`; - // todo: Complete standalone ES6 version, currently ignored, because focus is on MVP + // TODO: Complete standalone ES6 version, currently ignored, because focus is on MVP // return `${iframePath}/index.html?category=${example.category}&example=${example.name}`; } renderDeviceSelector() { + // @ts-ignore const { showDeviceSelector } = this.state; if (!showDeviceSelector) { @@ -180,7 +200,7 @@ class Example extends TypedComponent { } return jsx(DeviceSelector, { - onSelect: iframeReload, // reload the iframe after updating the device + onSelect: iframeReload // reload the iframe after updating the device }); } @@ -194,13 +214,14 @@ class Example extends TypedComponent { ErrorBoundary, null, jsx(this.state.controls, { + // @ts-ignore observer: window.observerData, PCUI, ReactPCUI, React, jsx, - fragment, - }), + fragment + }) ); } @@ -214,12 +235,12 @@ class Example extends TypedComponent { Container, { id: 'descriptionPanel', - class: orientation === 'portrait' ? 'mobile' : null, + class: orientation === 'portrait' ? 'mobile' : null }, jsx('span', { dangerouslySetInnerHTML: { - __html: description, - }, + __html: description + } }) ); } @@ -231,20 +252,27 @@ class Example extends TypedComponent { * 1) Hoping that the toggle functionality just happens to be calibrated * to the on/off toggling. * 2) Setting "collapsed" state everywhere via informed guesses. + * + * @type {boolean} */ get collapsed() { const controlPanel = document.getElementById("controlPanel"); + if (!controlPanel) { + return false; + } const collapsed = controlPanel.classList.contains("pcui-collapsed"); return collapsed; } toggleCollapse() { this.mergeState({ collapsed: !this.collapsed }); - //console.log("Example#toggleCollapse> was ", collapsed); } + /** + * @param {Event} event - Event. + */ handleRequestedFiles(event) { - // console.log('Example#handleRequestedFiles, files: ', event.detail); + // @ts-ignore const files = event.detail; this.mergeState({ files }); } @@ -259,14 +287,9 @@ class Example extends TypedComponent { resizable: 'top', headerText: 'CODE & CONTROLS', collapsible: true, - collapsed, - //header: jsx('h1', null, "data header"), - //onClick: this.toggleCollapse.bind(this), - //onExpand: this.toggleCollapse.bind(this), + collapsed }, - // jsx('button', null, "Example#renderPortrait()"), this.renderDeviceSelector(), - //this.renderControls(), jsx( Container, { @@ -296,30 +319,29 @@ class Example extends TypedComponent { class: show === 'description' ? 'selected' : null, id: 'descButton', onClick: () => this.mergeState({ show: 'description' }) - }) : null, + }) : null ), - // jsx('button', {onClick: () => console.log(this.state)}, "Example#renderPortrait"), show === 'parameters' && jsx( Container, { id: 'controlPanel-controls' }, - this.renderControls(), + this.renderControls() ), show === 'code' && jsx( MonacoEditor, { options: { readOnly: true, - theme: 'vs-dark', + theme: 'vs-dark' }, defaultLanguage: "javascript", - value: files['example.mjs'], + value: files['example.mjs'] } - ), - ), + ) + ) ), - this.renderDescription(), + this.renderDescription() ); } @@ -333,21 +355,20 @@ class Example extends TypedComponent { resizable: 'top', headerText: 'CONTROLS', collapsible: true, - collapsed, + collapsed }, - // jsx('button', {onClick: () => console.log(this.state)}, "Example#renderLandscape"), this.renderDeviceSelector(), - this.renderControls(), + this.renderControls() ), - this.renderDescription(), + this.renderDescription() ); } render() { const { iframePath } = this; const { orientation, exampleLoaded } = this.state; - // console.log("Example#render", JSON.stringify(this.state, null, 2)); - return jsx(Container, + return jsx( + Container, { id: "canvas-container" }, @@ -357,13 +378,12 @@ class Example extends TypedComponent { key: iframePath, src: iframePath }), - orientation === 'portrait' ? this.renderPortrait() : this.renderLandscape(), - ); + orientation === 'portrait' ? this.renderPortrait() : this.renderLandscape() + ); } } +// @ts-ignore const ExamptWithRouter = withRouter(Example); -export { - ExamptWithRouter as Example -}; +export { ExamptWithRouter as Example }; diff --git a/examples/src/app/helpers/example-data.mjs b/examples/src/app/helpers/example-data.mjs index 92c5951f1a0..81d06addf5b 100644 --- a/examples/src/app/helpers/example-data.mjs +++ b/examples/src/app/helpers/example-data.mjs @@ -9,9 +9,12 @@ Object.keys(exampleData).forEach((categorySlug) => { categories[categorySlug] = { examples: {} }; + // @ts-ignore Object.keys(exampleData[categorySlug]).forEach((exampleSlug, i) => { const name = kebabCaseToPascalCase(exampleSlug); + // @ts-ignore categories[categorySlug].examples[exampleSlug] = name; + // @ts-ignore const data = exampleData[categorySlug][exampleSlug]; const files = [ { diff --git a/examples/src/app/helpers/strings.mjs b/examples/src/app/helpers/strings.mjs index baae2cd73fa..83a3ae37bde 100644 --- a/examples/src/app/helpers/strings.mjs +++ b/examples/src/app/helpers/strings.mjs @@ -1,36 +1,39 @@ /** + * @param {string} string - The source string. + * @returns {string} - The capitalized string. + * * @example * capitalizeFirstLetter("test") // Outputs 'Test' - * @param {string} string - * @returns {string} */ function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } /** + * @param {string} str - The string. + * @returns {string} - The kebab-case-format + * * @example * toKebabCase('BlendTrees1D'); // Outputs: 'blend-trees-1d' * toKebabCase('LightsBakedAO'); // Outputs 'lights-baked-a-o' - * @param {string} str - The string. - * @returns String in kebab-case-format */ function toKebabCase(str) { return str - .replace(/([A-Z])([A-Z])/g, '$1-$2') // case for "...AO" -> '...-a-o' - .replace(/([a-z])([A-Z])/g, '$1-$2') - .replace(/([A-Z])([A-Z])([a-z])/g, '$1-$2$3') - .toLowerCase() - .replaceAll("1d", "-1d") - .replaceAll("2d", "-2d") - .replaceAll("3d", "-3d") + .replace(/([A-Z])([A-Z])/g, '$1-$2') // case for "...AO" -> '...-a-o' + .replace(/([a-z])([A-Z])/g, '$1-$2') + .replace(/([A-Z])([A-Z])([a-z])/g, '$1-$2$3') + .toLowerCase() + .replace(/1d/g, "-1d") + .replace(/2d/g, "-2d") + .replace(/3d/g, "-3d"); } /** + * @param {string} str - The string. + * @returns {string} - The pascal case + * * @example * kebabCaseToPascalCase("user-interface"); // Outputs 'UserInterface' - * @param {string} str - The string. - * @returns {string} */ function kebabCaseToPascalCase(str) { return str @@ -43,10 +46,11 @@ function kebabCaseToPascalCase(str) { } /** - * @example - * countLeadingSpaces(' Hello!'); // Result: 2 * @param {string} str - String with leading spaces. * @returns {number} Number of spaces. + * + * @example + * countLeadingSpaces(' Hello!'); // Result: 2 */ function countLeadingSpaces(str) { let count = 0; diff --git a/examples/src/app/iframeUtils.mjs b/examples/src/app/iframeUtils.mjs index 2d647e48689..b99884de1d3 100644 --- a/examples/src/app/iframeUtils.mjs +++ b/examples/src/app/iframeUtils.mjs @@ -50,6 +50,7 @@ function iframeDestroy() { } function iframeReady() { try { + // @ts-ignore return getIframeWindow()?.eval('ready === true'); } catch (e) {} return false; diff --git a/examples/src/app/index.mjs b/examples/src/app/index.mjs index 83cff0d0b8a..e42b6180fe5 100644 --- a/examples/src/app/index.mjs +++ b/examples/src/app/index.mjs @@ -6,6 +6,7 @@ import '@playcanvas/pcui/styles'; function main() { // render out the app const container = document.getElementById('app'); + // @ts-ignore const root = createRoot(container); root.render(jsx(MainLayout, null)); } diff --git a/examples/src/app/jsx.mjs b/examples/src/app/jsx.mjs index e22271f40a2..1ddbe88e8b7 100644 --- a/examples/src/app/jsx.mjs +++ b/examples/src/app/jsx.mjs @@ -1,3 +1,3 @@ import {createElement, Fragment} from 'react'; export const jsx = createElement; -export const fragment = (...args) => jsx(Fragment, null, ...args); +export const fragment = (/** @type {any} */ ...args) => jsx(Fragment, null, ...args); diff --git a/examples/src/app/menu.mjs b/examples/src/app/menu.mjs index c583daf9613..75e352ede7f 100644 --- a/examples/src/app/menu.mjs +++ b/examples/src/app/menu.mjs @@ -2,13 +2,13 @@ import { Component } from 'react'; import { Button, Container } from '@playcanvas/pcui/react'; import { jsx } from './jsx.mjs'; import { logo } from '../assetPath.mjs'; -//import { iframeShowStats } from './code-editor.mjs'; /** * @typedef {object} Props - * @property {(value: boolean) => void} setShowMiniStats + * @property {(value: boolean) => void} setShowMiniStats - The state set function . */ +// eslint-disable-next-line jsdoc/require-property /** * @typedef {object} State */ @@ -20,45 +20,52 @@ class Menu extends TypedComponent { mouseTimeout = null; /** - * @param {Props} props + * @param {Props} props - Component properties. */ constructor(props) { super(props); this.handleExampleLoad = this.handleExampleLoad.bind(this); } + /** @type {EventListener | null} */ clickFullscreenListener = null; toggleFullscreen() { + const contentDocument = document.querySelector('iframe')?.contentDocument; + if (!contentDocument) { + return; + } if (this.clickFullscreenListener) { - document.querySelector('iframe').contentDocument.removeEventListener('mousemove', this.clickFullscreenListener); + contentDocument.removeEventListener('mousemove', this.clickFullscreenListener); } - document.querySelector('#canvas-container').classList.toggle('fullscreen'); + document.querySelector('#canvas-container')?.classList.toggle('fullscreen'); const app = document.querySelector('#appInner'); - app.classList.toggle('fullscreen'); - document.querySelector('iframe').contentDocument.getElementById('appInner').classList.toggle('fullscreen'); - if (app.classList.contains('fullscreen')) { + app?.classList.toggle('fullscreen'); + contentDocument.getElementById('appInner')?.classList.toggle('fullscreen'); + if (app?.classList.contains('fullscreen')) { this.clickFullscreenListener = () => { - app.classList.add('active'); + app?.classList.add('active'); if (this.mouseTimeout) { window.clearTimeout(this.mouseTimeout); } + // @ts-ignore this.mouseTimeout = setTimeout(() => { app.classList.remove('active'); }, 2000); }; - document.querySelector('iframe').contentDocument.addEventListener('mousemove', this.clickFullscreenListener); + contentDocument.addEventListener('mousemove', this.clickFullscreenListener); } - }; + } componentDidMount() { - const escapeKeyEvent = (e) => { + const escapeKeyEvent = (/** @type {{ keyCode: number; }} */ e) => { + // @ts-ignore if (e.keyCode === 27 && document.querySelector('#canvas-container').classList.contains('fullscreen')) { this.toggleFullscreen(); } }; const iframe = document.querySelector('iframe'); if (iframe) { - iframe.contentDocument.addEventListener('keydown', escapeKeyEvent); + iframe.contentDocument?.addEventListener('keydown', escapeKeyEvent); } else { console.warn('Menu#useEffect> iframe undefined'); } @@ -67,6 +74,7 @@ class Menu extends TypedComponent { } handleExampleLoad() { + // @ts-ignore const selected = document.getElementById('showMiniStatsButton').classList.contains('selected'); // console.log('Menu#handleExampleLoad, selected:', selected); this.props.setShowMiniStats(selected); @@ -74,8 +82,7 @@ class Menu extends TypedComponent { render() { return jsx( - Container, - { + Container, { id: 'menu' }, jsx(Container, @@ -103,15 +110,16 @@ class Menu extends TypedComponent { class: 'selected', text: '', onClick: () => { - document.getElementById('showMiniStatsButton').classList.toggle('selected'); - const selected = document.getElementById('showMiniStatsButton').classList.contains('selected'); - this.props.setShowMiniStats(selected); - - - }, + document.getElementById('showMiniStatsButton')?.classList.toggle('selected'); + const selected = document.getElementById('showMiniStatsButton')?.classList.contains('selected'); + this.props.setShowMiniStats(!!selected); + } }), jsx(Button, { - icon: 'E127', text: '', id: 'fullscreen-button', onClick: this.toggleFullscreen.bind(this) + icon: 'E127', + text: '', + id: 'fullscreen-button', + onClick: this.toggleFullscreen.bind(this) }) ) ); diff --git a/examples/src/app/polyfillFunctionCall.mjs b/examples/src/app/polyfillFunctionCall.mjs index c66374ed850..1957451ecd3 100644 --- a/examples/src/app/polyfillFunctionCall.mjs +++ b/examples/src/app/polyfillFunctionCall.mjs @@ -1,5 +1,10 @@ const functionCall = Function.prototype.call; +/** + * @param {any} thisArg - This context. + * @param {any[]} args - Arguments + * @returns {Function} - The bound function + */ function polyCall(thisArg, ...args) { if (this.toString().startsWith('class')) { return Object.assign(thisArg, new this(...args)); @@ -13,9 +18,10 @@ function polyCall(thisArg, ...args) { * // doesn't start with 'class', so not changing any behaviour * debugger; // step through with F11 to debug * Object.prototype.toString.call(1) === '[object Number]' - * + * */ function enablePolyfillFunctionCall() { + // eslint-disable-next-line no-extend-native Function.prototype.call = polyCall; } export { enablePolyfillFunctionCall }; diff --git a/examples/src/app/polyfills.mjs b/examples/src/app/polyfills.mjs index 6434f657355..9953d22304a 100644 --- a/examples/src/app/polyfills.mjs +++ b/examples/src/app/polyfills.mjs @@ -4,8 +4,9 @@ enablePolyfillFunctionCall(); // polyfill slice on UInt8Array if (!Uint8Array.prototype.slice) { + // eslint-disable-next-line no-extend-native Object.defineProperty(Uint8Array.prototype, 'slice', { - value: function (begin, end) { + value: function (/** @type {any} */ begin, /** @type {any} */ end) { return new Uint8Array(Array.prototype.slice.call(this, begin, end)); } }); diff --git a/examples/src/app/sidebar.mjs b/examples/src/app/sidebar.mjs index 34d1208a0f2..6dc0f49f8d4 100644 --- a/examples/src/app/sidebar.mjs +++ b/examples/src/app/sidebar.mjs @@ -9,6 +9,7 @@ import { jsx } from './jsx.mjs'; import { getOrientation } from './utils.mjs'; import { iframeDestroy } from './iframeUtils.mjs'; +// eslint-disable-next-line jsdoc/require-property /** * @typedef {object} Props */ @@ -35,14 +36,22 @@ export class SideBar extends TypedComponent { filteredCategories: null, hash: location.hash, observer: new Observer({ largeThumbnails: false }), + // @ts-ignore collapsed: window.top.innerWidth < MIN_DESKTOP_WIDTH, - orientation: getOrientation(), - } + orientation: getOrientation() + }; componentDidMount() { // PCUI should just have a "onHeaderClick" but can't find anything const sideBar = document.getElementById("sideBar"); + if (!sideBar) { + return; + } const sideBarHeader = sideBar.querySelector('.pcui-panel-header'); + if (!sideBarHeader) { + return; + } + // @ts-ignore sideBarHeader.onclick = () => this.toggleCollapse(); this.setupControlPanelToggleButton(); // setup events @@ -59,11 +68,14 @@ export class SideBar extends TypedComponent { setupControlPanelToggleButton() { // set up the control panel toggle button const sideBar = document.getElementById('sideBar'); + if (!sideBar) { + return; + } window.addEventListener('hashchange', () => { this.mergeState({ hash: location.hash }); }); + /** @type {Element} */ this.state.observer.on('largeThumbnails:set', () => { - /** @type {HTMLElement} */ let topNavItem; let minTopNavItemDistance = Number.MAX_VALUE; document.querySelectorAll('.nav-item').forEach((nav) => { @@ -74,13 +86,16 @@ export class SideBar extends TypedComponent { } }); sideBar.classList.toggle('small-thumbnails'); + // @ts-ignore topNavItem.scrollIntoView(); }); sideBar.classList.add('visible'); // when first opening the examples browser via a specific example, scroll it into view + // @ts-ignore if (!window._scrolledToExample) { const examplePath = location.hash.split('/'); document.getElementById(`link-${examplePath[1]}-${examplePath[2]}`)?.scrollIntoView(); + // @ts-ignore window._scrolledToExample = true; } } @@ -125,6 +140,7 @@ export class SideBar extends TypedComponent { return null; } Object.keys(defaultCategories[category].examples).forEach((example) => { + // @ts-ignore const title = defaultCategories[category].examples[example]; if (title.search(reg) !== -1) { if (!updatedCategories[category]) { @@ -135,6 +151,7 @@ export class SideBar extends TypedComponent { } }; } else { + // @ts-ignore updatedCategories[category].examples[example] = title; } } @@ -142,10 +159,11 @@ export class SideBar extends TypedComponent { }); this.mergeState({ filteredCategories: updatedCategories }); } + onClickExample() { - // this.mergeState({ collapsed: true }); iframeDestroy(); } + renderContents() { const categories = this.state.filteredCategories || this.state.defaultCategories; if (Object.keys(categories).length === 0) { @@ -162,46 +180,48 @@ export class SideBar extends TypedComponent { collapsible: true, collapsed: false }, - jsx("ul", { - className: "category-nav" - }, - Object.keys(categories[category].examples).sort((a, b) => (a > b ? 1 : -1)).map((example) => { - //console.log({ category, example }); - const isSelected = new RegExp(`/${category}/${example}$`).test(hash); - const className = `nav-item ${isSelected ? 'selected' : null}`; - return jsx(Link, { - key: example, - to: `/${category}/${example}`, - onClick: this.onClickExample.bind(this), + jsx("ul", + { + className: "category-nav" }, - jsx("div", { className: className, id: `link-${category}-${example}` }, - jsx( - "img", - { - className: 'small-thumbnail', - loading: "lazy", - src: thumbnailPath + `${category}_${example}_small.png` - } - ), - jsx("img", { - className: 'large-thumbnail', - loading: "lazy", - src: thumbnailPath + `${category}_${example}_large.png` - }), - jsx( - "div", - { - className: 'nav-item-text' - }, - example.split('-').join(' ').toUpperCase() - ) - ) - ); - }) + Object.keys(categories[category].examples).sort((a, b) => (a > b ? 1 : -1)).map((example) => { + const isSelected = new RegExp(`/${category}/${example}$`).test(hash); + const className = `nav-item ${isSelected ? 'selected' : null}`; + return jsx(Link, + { + key: example, + to: `/${category}/${example}`, + onClick: this.onClickExample.bind(this) + }, + jsx("div", { className: className, id: `link-${category}-${example}` }, + jsx( + "img", + { + className: 'small-thumbnail', + loading: "lazy", + src: thumbnailPath + `${category}_${example}_small.png` + } + ), + jsx("img", { + className: 'large-thumbnail', + loading: "lazy", + src: thumbnailPath + `${category}_${example}_large.png` + }), + jsx( + "div", + { + className: 'nav-item-text' + }, + example.split('-').join(' ').toUpperCase() + ) + ) + ); + }) ) ); }); } + render() { const { observer, collapsed, orientation } = this.state; const panelOptions = { @@ -215,6 +235,7 @@ export class SideBar extends TypedComponent { ] }; if (orientation === 'portrait') { + // @ts-ignore panelOptions.class = 'small-thumbnails'; panelOptions.collapsed = collapsed; } @@ -224,7 +245,7 @@ export class SideBar extends TypedComponent { class: 'filter-input', keyChange: true, placeholder: "Filter...", - onChange: this.onChangeFilter.bind(this), + onChange: this.onChangeFilter.bind(this) }), jsx(LabelGroup, { text: 'Large thumbnails:' }, jsx(BooleanInput, { @@ -235,7 +256,7 @@ export class SideBar extends TypedComponent { ), jsx(Container, { id: 'sideBar-contents' }, this.renderContents() - ), - ) + ) + ); } } diff --git a/examples/src/app/utils.mjs b/examples/src/app/utils.mjs index 017c5235cb6..a2746d993d3 100644 --- a/examples/src/app/utils.mjs +++ b/examples/src/app/utils.mjs @@ -4,6 +4,7 @@ import { MIN_DESKTOP_WIDTH } from './constants.mjs'; * @returns {'portrait'|'landscape'} Orientation, which is either 'portrait' (width < 601 px) or * 'landscape' (every width >= 601, not aspect related) */ +// @ts-ignore const getOrientation = () => window.top.innerWidth < MIN_DESKTOP_WIDTH ? 'portrait': 'landscape'; export { getOrientation }; diff --git a/examples/src/assetPath.mjs b/examples/src/assetPath.mjs index c12752275f0..e87bd14d266 100644 --- a/examples/src/assetPath.mjs +++ b/examples/src/assetPath.mjs @@ -6,7 +6,7 @@ const href = typeof location !== 'undefined' ? location.href : ''; * const assetPath = getAssetPath(); // 'http://127.0.0.1/playcanvas-engine/examples/assets/' * // Test output for href: * // http://127.0.0.1/playcanvas-engine/examples/src/examples/animation/test-examples.html - * @returns {string} + * @returns {string} - The path. */ function getAssetPath() { const i = href.indexOf("/examples/"); @@ -20,7 +20,7 @@ export const assetPath = getAssetPath(); /** * @example * http://127.0.0.1/playcanvas-engine/examples/../build/playcanvas.d.ts - * @returns {string} + * @returns {string} - The path. */ function getPlayCanvasTypes() { const i = href.indexOf("/examples/"); @@ -35,7 +35,7 @@ export const pcTypes = getPlayCanvasTypes(); * @example * console.log(getScriptsPath()); * // Outputs: 'http://127.0.0.1/playcanvas-engine/scripts/' - * @returns {string} + * @returns {string} - The path. */ function getScriptsPath() { const i = href.indexOf("/examples/"); @@ -50,10 +50,10 @@ export const scriptsPath = getScriptsPath(); * @example * console.log(getIframePath()); * // Outputs: http://127.0.0.1/playcanvas-engine/examples/src/iframe/ - * @returns {string} + * @returns {string} - The path. */ function getIframePath() { - const i = href.indexOf("/examples/") + const i = href.indexOf("/examples/"); if (i === -1) { // npm run serve return '/iframe/'; } @@ -65,7 +65,7 @@ export const iframePath = getIframePath(); * @example * console.log(getAmmoPath()); * // Outputs: "http://127.0.0.1/playcanvas-engine/examples/src/lib/ammo/"; - * @returns {string} + * @returns {string} - The path. */ function getAmmoPath() { const i = href.indexOf("/examples/"); @@ -80,7 +80,7 @@ export const ammoPath = getAmmoPath(); * @example * console.log(getBasisPath()); * // Outputs: "http://127.0.0.1/playcanvas-engine/examples/src/lib/basis/"; - * @returns {string} + * @returns {string} - The path. */ function getBasisPath() { const i = href.indexOf("/examples/"); @@ -95,7 +95,7 @@ export const basisPath = getBasisPath(); * @example * console.log(getDracoPath()); * // Outputs: 'http://127.0.0.1/playcanvas-engine/examples/src/lib/draco/' - * @returns {string} + * @returns {string} - The path. */ function getDracoPath() { const i = href.indexOf("/examples/"); @@ -110,7 +110,7 @@ export const dracoPath = getDracoPath(); * @example * console.log(getDracoPath()); * // Outputs: 'http://127.0.0.1/playcanvas-engine/examples/src/lib/glslang/' - * @returns {string} + * @returns {string} - The path. */ function getGlslangPath() { const i = href.indexOf("/examples/"); @@ -125,7 +125,7 @@ export const glslangPath = getGlslangPath(); * @example * console.log(getDracoPath()); * // Outputs: 'http://127.0.0.1/playcanvas-engine/examples/src/lib/twgsl/' - * @returns {string} + * @returns {string} - The path. */ function getTwgslPath() { const i = href.indexOf("/examples/"); @@ -140,7 +140,7 @@ export const twgslPath = getTwgslPath(); * @example * console.log(getIframePath()); * // Outputs: http://127.0.0.1/playcanvas-engine/examples/src/iframe/ - * @returns {string} + * @returns {string} - The path. */ function getThumbnailPath() { const i = href.lastIndexOf("/examples/"); diff --git a/examples/src/iframe/index.mjs b/examples/src/iframe/index.mjs index 7725e06dd0a..30e94672486 100644 --- a/examples/src/iframe/index.mjs +++ b/examples/src/iframe/index.mjs @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import * as observer from '@playcanvas/observer'; import * as pcx from 'playcanvas-extras'; import * as realExamples from "../examples/index.mjs"; diff --git a/examples/src/importmap.js b/examples/src/importmap.js index da8c306c631..829edffc78a 100644 --- a/examples/src/importmap.js +++ b/examples/src/importmap.js @@ -1,8 +1,10 @@ +// @ts-ignore window.process = { env: { NODE_ENV: 'development' } }; +// @ts-ignore const playcanvasEngineThis = document.currentScript.src + '/../../../'; const playcanvasEngine = playcanvasEngineThis; // const playcanvasEngine = '/playcanvas-engine-jsdoc/'; diff --git a/examples/src/loadES5.mjs b/examples/src/loadES5.mjs index 059dcec1fb3..14ea3c65983 100644 --- a/examples/src/loadES5.mjs +++ b/examples/src/loadES5.mjs @@ -3,6 +3,7 @@ * const CORE = await load('https://cdn.jsdelivr.net/npm/@loaders.gl/core@2.3.6/dist/dist.min.js'); * const DRACO = await load('https://cdn.jsdelivr.net/npm/@loaders.gl/draco@2.3.6/dist/dist.min.js'); * @param {string} url - The URL to ES5 file. + * @returns {Promise} - The module exports */ export async function loadES5(url) { const res = await fetch(url); @@ -10,5 +11,5 @@ export async function loadES5(url) { const module = { exports: {} }; - return (Function('module', 'exports', txt).call(module, module, module.exports), module).exports; + return (Function.prototype.constructor('module', 'exports', txt).call(module, module, module.exports), module).exports; } From 7d0cad3216e5ed5d668f27558073e284a1dc9774 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 28 Jan 2024 00:46:22 +0000 Subject: [PATCH 02/11] added localstorage saving of sidebar collapse. --- examples/src/app/sidebar.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/src/app/sidebar.mjs b/examples/src/app/sidebar.mjs index 6dc0f49f8d4..faf14ff50cf 100644 --- a/examples/src/app/sidebar.mjs +++ b/examples/src/app/sidebar.mjs @@ -37,7 +37,7 @@ export class SideBar extends TypedComponent { hash: location.hash, observer: new Observer({ largeThumbnails: false }), // @ts-ignore - collapsed: window.top.innerWidth < MIN_DESKTOP_WIDTH, + collapsed: localStorage.getItem('sideBarCollapsed') === 'true' || window.top.innerWidth < MIN_DESKTOP_WIDTH, orientation: getOrientation() }; @@ -112,6 +112,7 @@ export class SideBar extends TypedComponent { toggleCollapse() { const { collapsed } = this.state; + localStorage.setItem('sideBarCollapsed', `${!collapsed}`); this.mergeState({ collapsed: !collapsed }); } From 3b1bc66d925be2d4b7aacf57074447b1f9b66956 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 28 Jan 2024 14:10:52 +0000 Subject: [PATCH 03/11] removed unused iframe html and js files and reverted to use Function in loadES5 --- examples/src/iframe/index.html | 35 ---------------------------------- examples/src/iframe/index.mjs | 7 ------- examples/src/loadES5.mjs | 3 ++- 3 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 examples/src/iframe/index.html delete mode 100644 examples/src/iframe/index.mjs diff --git a/examples/src/iframe/index.html b/examples/src/iframe/index.html deleted file mode 100644 index 014edf5dbc4..00000000000 --- a/examples/src/iframe/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - -
-
- -
-
- - - -
-
- -
-
- - - diff --git a/examples/src/iframe/index.mjs b/examples/src/iframe/index.mjs deleted file mode 100644 index 30e94672486..00000000000 --- a/examples/src/iframe/index.mjs +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import * as observer from '@playcanvas/observer'; -import * as pcx from 'playcanvas-extras'; -import * as realExamples from "../examples/index.mjs"; -import * as pc from "playcanvas"; -import * as dirs from '../assetPath.mjs'; -import '../app/polyfills.mjs'; diff --git a/examples/src/loadES5.mjs b/examples/src/loadES5.mjs index 14ea3c65983..db4f5dbc3c6 100644 --- a/examples/src/loadES5.mjs +++ b/examples/src/loadES5.mjs @@ -11,5 +11,6 @@ export async function loadES5(url) { const module = { exports: {} }; - return (Function.prototype.constructor('module', 'exports', txt).call(module, module, module.exports), module).exports; + // eslint-disable-next-line no-new-func + return (Function('module', 'exports', txt).call(module, module, module.exports), module).exports; } From 9406b5c49912398909704ab7bbc56d066e54e6b8 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 28 Jan 2024 17:50:50 +0000 Subject: [PATCH 04/11] cleaned up linting for example scripts --- examples/scripts/example-data.mjs | 6 +++ examples/scripts/example-directory.mjs | 38 ++++++++------- .../scripts/generate-standalone-files.mjs | 46 +++++++++++++++---- examples/scripts/thumbnails.mjs | 25 +++++++--- 4 files changed, 81 insertions(+), 34 deletions(-) diff --git a/examples/scripts/example-data.mjs b/examples/scripts/example-data.mjs index 5d4e9919b07..e3e05fbd2f4 100644 --- a/examples/scripts/example-data.mjs +++ b/examples/scripts/example-data.mjs @@ -7,6 +7,7 @@ import { toKebabCase } from '../src/app/helpers/strings.mjs'; * It would be possible to *not* pregenerate example-data.js, but then we would include all * examples only to generate the list in the UI... which would be a waste. */ +// @ts-ignore const __filename = fileURLToPath(import.meta.url); const MAIN_DIR = `${dirname(__filename)}/../`; const exampleData = {}; @@ -15,7 +16,9 @@ if (!fs.existsSync(`${MAIN_DIR}/dist/`)) { } for (const category_ in realExamples) { const category = toKebabCase(category_); + // @ts-ignore exampleData[category] = {}; + // @ts-ignore const examples = realExamples[category_]; for (const exampleName_ in examples) { const release = process.env.NODE_ENV !== 'development'; @@ -25,8 +28,11 @@ for (const category_ in realExamples) { } const example = toKebabCase(exampleName_).replace('-example', ''); // turn: turn into simple array... + // @ts-ignore exampleData[category][example] = {}; + // @ts-ignore exampleData[category][example].nameSlug = example; + // @ts-ignore exampleData[category][example].categorySlug = category; } } diff --git a/examples/scripts/example-directory.mjs b/examples/scripts/example-directory.mjs index 6623ce32041..7662e21c110 100644 --- a/examples/scripts/example-directory.mjs +++ b/examples/scripts/example-directory.mjs @@ -1,32 +1,35 @@ import fs from 'fs'; import { dirname } from 'path'; import { fileURLToPath } from 'url'; -import * as realExamples from "../src/examples/index.mjs"; +import * as realExamples from '../src/examples/index.mjs'; import { toKebabCase } from '../src/app/helpers/strings.mjs'; + /** * @param {object} options - The options. * @param {string} options.path - The path. * @param {string} options.exampleTitle - The example title. * @param {string} options.largeThumbnailName - The large thumbnail name. - * @returns {string} + * @returns {string} - The template string. */ function template({ path, exampleTitle, largeThumbnailName }) { return ` - - - - - - - - - - - -

Please follow this link.

- - `; - } + + + + + + + + + + + +

Please follow this link.

+ + `; +} + +// @ts-ignore const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const MAIN_DIR = `${__dirname}/../`; @@ -37,6 +40,7 @@ for (const category_ in realExamples) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir); } + // @ts-ignore const examples = realExamples[category_]; categoriesList.push({ name: category, diff --git a/examples/scripts/generate-standalone-files.mjs b/examples/scripts/generate-standalone-files.mjs index eede8ec853f..ad38dc2d2d1 100644 --- a/examples/scripts/generate-standalone-files.mjs +++ b/examples/scripts/generate-standalone-files.mjs @@ -3,6 +3,8 @@ import { dirname } from 'path'; import { fileURLToPath } from 'url'; import * as realExamples from "../src/examples/index.mjs"; import { toKebabCase } from '../src/app/helpers/strings.mjs'; + +// @ts-ignore const __filename = fileURLToPath(import.meta.url); const MAIN_DIR = `${dirname(__filename)}/../`; const exampleData = {}; @@ -14,20 +16,28 @@ if (!fs.existsSync(`${MAIN_DIR}/dist/iframe/`)) { } for (const category_ in realExamples) { const category = toKebabCase(category_); + // @ts-ignore exampleData[category] = {}; + // @ts-ignore const examples = realExamples[category_]; - for (const exampleName_ in examples) { + for (const exampleName_ in examples) { const exampleClass = examples[exampleName_]; const example = toKebabCase(exampleName_).replace('-example', ''); + // @ts-ignore exampleData[category][example] = {}; const exampleFunc = exampleClass.example.toString(); + // @ts-ignore exampleData[category][example].example = exampleFunc; + // @ts-ignore exampleData[category][example].nameSlug = example; + // @ts-ignore exampleData[category][example].categorySlug = category; if (exampleClass.FILES) { + // @ts-ignore exampleData[category][example].files = exampleClass.FILES; } if (exampleClass.controls) { + // @ts-ignore exampleData[category][example].controls = exampleClass.controls.toString(); } const dropEnding = exampleName_.replace(/Example$/, ""); // TestExample -> Test @@ -39,10 +49,12 @@ for (const category_ in realExamples) { /** * Choose engine based on `Example#ENGINE`, e.g. ClusteredLightingExample picks: * static ENGINE = 'PERFORMANCE'; - * @param {'PERFORMANCE'|'DEBUG'|undefined} str + * + * @param {'PERFORMANCE'|'DEBUG'|undefined} type - The build type. + * @returns {string} - The build file. */ -function engineFor(str) { - switch (str) { +function engineFor(type) { + switch (type) { case 'PERFORMANCE': return './playcanvas.prf.js'; case 'DEBUG': @@ -51,11 +63,25 @@ function engineFor(str) { return './playcanvas.js'; } +/** + * @typedef {object} ExampleClass + * @property {Function} example - The example function. + * @property {Function} [controls] - The controls function. + * @property {object[]} [imports] - The imports array. + * @property {string[]} [es5libs] - The ES5Libs array. + * @property {string} DESCRIPTION - The example description. + * @property {"PERFORMANCE" | "DEBUG" | undefined} ENGINE - The engine type. + * @property {object} FILES - The object of extra files to include (e.g shaders). + * @property {boolean} INCLUDE_AR_LINK - Include AR link png. + * @property {boolean} NO_DEVICE_SELECTOR - No device selector. + * @property {boolean} NO_CANVAS - No canvas element. + * @property {boolean} NO_MINISTATS - No ministats. + * @property {boolean} WEBGPU_ENABLED - If webGPU is enabled. + */ /** * @param {string} category - The category. * @param {string} example - The example. - * @param {object} exampleClass - The example class. - * @param {object} ministats - Should ministats be enabled? + * @param {ExampleClass} exampleClass - The example class. * @returns {string} File to write as standalone example. */ function generateExampleFile(category, example, exampleClass) { @@ -63,7 +89,7 @@ function generateExampleFile(category, example, exampleClass) { ${category}: ${example} - ${exampleClass.es5libs?.map(_ => ``).join('\n') || ''} + ${exampleClass.es5libs?.map((/** @type {string} */ src) => ``).join('\n') || ''}
@@ -83,7 +109,7 @@ function generateExampleFile(category, example, exampleClass) {