David `Deltakosh` Catuhe 5 years ago
parent
commit
f582df74aa

+ 1 - 0
Playground/imgs/examples.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55 55"><defs><style>.cls-1{fill:none;}</style></defs><title>examplesButton</title><g id="Icon_buttons" data-name="Icon buttons"><rect class="cls-1" width="55" height="55"/><path d="M36.14,22.87V36.62H24.89v-2.5h-2.5v-2.5h-2.5V17.87H27l2.5,2.5h4.11v2.5Zm-6.25,7.5v-7.5H26.14V19.12h-5V30.37Zm2.5,2.5V21.62H31.14v10h-7.5v1.25Zm2.51-8.75H33.64v10h-7.5v1.25H34.9Zm-7.51-2.5H29L27.39,20Z"/></g></svg>

+ 9 - 0
Playground/src/components/commandBarComponent.tsx

@@ -39,9 +39,14 @@ export class CommandBarComponent extends React.Component<ICommandBarComponentPro
         this.props.globalState.onInspectorRequiredObservable.notifyObservers();
     }
 
+    onExamples() {
+        this.props.globalState.onExamplesDisplayChangedObservable.notifyObservers();
+    }
+
     public render() {
         return (
             <div className={"commands " + (this.props.globalState.language === "JS" ? "background-js" : "background-ts")}>
+                <div className="commands-left">
                 <CommandButtonComponent globalState={this.props.globalState} tooltip="Run" icon="play" shortcut="Alt+Enter" isActive={true} onClick={()=> this.onPlay()}/>
                 <CommandButtonComponent globalState={this.props.globalState} tooltip="Save" icon="save" shortcut="Ctrl+S" isActive={false} onClick={()=> this.onSave()}/>
                 <CommandButtonComponent globalState={this.props.globalState} tooltip="Inspector" icon="inspector" isActive={false} onClick={()=> this.onInspector()}/>
@@ -118,6 +123,10 @@ export class CommandBarComponent extends React.Component<ICommandBarComponentPro
                         onClick: () => {this.props.globalState.onDisplayMetadataObservable.notifyObservers(true)}
                     }
                 ]}/>
+                </div>
+                <div className="commands-right">
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Examples" icon="examples" onClick={()=> this.onExamples()} isActive={false}/>
+                </div>
             </div>
         );
     }

+ 6 - 0
Playground/src/components/commandDropdownComponent.tsx

@@ -70,6 +70,12 @@ export class CommandDropdownComponent extends React.Component<ICommandDropdownCo
                                                 }
                                                 {
                                                     m.subItems &&
+                                                    <div className="command-dropdown-arrow">
+                                                        {">"}
+                                                    </div>
+                                                }
+                                                {
+                                                    m.subItems &&
                                                     <div className={"sub-items " + (this.props.globalState.language === "JS" ? "background-js" : "background-ts")}>
                                                         {
                                                             m.subItems.map(s => {

+ 136 - 0
Playground/src/components/examplesComponent.tsx

@@ -0,0 +1,136 @@
+import * as React from "react";
+import { GlobalState } from '../globalState';
+
+require("../scss/examples.scss");
+
+interface IExamplesComponentProps {
+    globalState: GlobalState;
+}
+
+export class ExamplesComponent extends React.Component<IExamplesComponentProps, {filter: string}> {  
+    private _state = "";
+    private _rootRef: React.RefObject<HTMLDivElement>;
+    private _scripts: {
+        title: string;
+        samples: {
+            title: string;
+            doc: string;
+            icon: string;
+            PGID: string;
+            description: string;
+        }[];
+    }[];  
+  
+    public constructor(props: IExamplesComponentProps) {
+        super(props);
+        this._loadScripts();
+
+        this.state = {filter: ""};
+        this._rootRef = React.createRef();
+
+        this.props.globalState.onExamplesDisplayChangedObservable.add(() => {
+            if (this._state === "") {
+                this._rootRef.current!.classList.add("visible");
+                this._state = "visible";
+            } else {
+                this._rootRef.current!.classList.remove("visible");
+                this._state = "";
+            }
+        });
+    }  
+
+    private _loadScripts() {
+        var xhr = new XMLHttpRequest();
+
+        if (this.props.globalState.language === "JS") {
+            xhr.open('GET', 'https://raw.githubusercontent.com/BabylonJS/Documentation/master/examples/list.json', true);
+        } else {
+            xhr.open('GET', 'https://raw.githubusercontent.com/BabylonJS/Documentation/master/examples/list_ts.json', true);
+        }
+
+        xhr.onreadystatechange = () => {
+            if (xhr.readyState === 4) {
+                if (xhr.status === 200) {
+                    this._scripts = JSON.parse(xhr.response)["examples"];
+
+                    this._scripts.sort((a, b) => {
+                        if (a.title < b.title) {
+                            return -1;
+                        }
+                        return 1;
+                    });
+
+                    this._scripts.forEach(s => {
+                        s.samples.sort((a, b) => {
+                            if (a.title < b.title) {
+                                return -1;
+                            }
+                            return 1;
+                        });
+                    });
+
+                    this.forceUpdate();
+                }
+            }
+        }
+
+        xhr.send(null);
+    }
+
+
+    private _onLoadPG(id: string) {
+        this.props.globalState.onLoadRequiredObservable.notifyObservers(id);
+    }
+
+    public render() {
+        if (!this._scripts) {
+            return null;
+        }
+
+        return (
+            <div id="examples" className={this._state} ref={this._rootRef}>
+                <div id="examples-header">Examples</div>
+                <div id="examples-filter">
+                    <input type="text" placeholder="Filter examples" value={this.state.filter} onChange={evt => {
+                        this.setState({filter: evt.target.value});
+                    }}/>
+                </div>
+                <div id="examples-list">
+                    {
+                        this._scripts.map(s => {
+                            let active = s.samples.filter(ss => {
+                                return !this.state.filter 
+                                    || ss.title.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1
+                                    || ss.description.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1
+                            });
+
+                            if (active.length === 0) {
+                                return null;
+                            }
+
+                            return(
+                                <div key={s.title} className="example-category">
+                                    <div className="example-category-title">
+                                        {s.title}
+                                    </div>
+                                    {
+                                        active.map(ss => {
+                                            return (
+                                                <div className="example" key={ss.title} onClick={() => this._onLoadPG(ss.PGID)}>
+                                                    <img src={ss.icon.replace("icons", "https://doc.babylonjs.com/examples/icons")}/>
+                                                    <div className="example-title">{ss.title}</div>
+                                                    <div className="example-description">{ss.description}</div>
+                                                    <a className="example-link" href={ss.doc} target="_blank">Documentation</a>
+                                                </div>
+                                            )
+                                        })
+                                    }
+                                </div>
+                            )
+                        })
+                    }
+                </div>
+            </div>
+        )
+    }
+}

+ 6 - 0
Playground/src/components/hamburgerMenu.tsx

@@ -57,6 +57,11 @@ export class HamburgerMenuComponent extends React.Component<IHamburgerMenuCompon
         this.setState({isExpanded: false});
     }
 
+    onExamples() {
+        this.props.globalState.onExamplesDisplayChangedObservable.notifyObservers();
+        this.setState({isExpanded: false});
+    }
+
     switch() {
         this.setState({isExpanded: !this.state.isExpanded});
     }
@@ -81,6 +86,7 @@ export class HamburgerMenuComponent extends React.Component<IHamburgerMenuCompon
                     <CommandButtonComponent globalState={this.props.globalState} tooltip="Clear code" icon="clear" isActive={false} onClick={()=> this.onClear()}/>
                     <CommandButtonComponent globalState={this.props.globalState} tooltip="Format code" icon="options" isActive={false} onClick={()=> this.onFormatCode()}/>
                     <CommandButtonComponent globalState={this.props.globalState} tooltip="Metadata" icon="options" isActive={false} onClick={()=> this.onMetadata()}/>
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Examples" icon="examples" onClick={()=> this.onExamples()} isActive={false}/>
                 </div>
             </>
         );

+ 3 - 1
Playground/src/globalState.ts

@@ -30,7 +30,8 @@ export class GlobalState {
     public onSavedObservable = new Observable<void>();
     public onNewRequiredObservable = new Observable<void>();
     public onClearRequiredObservable = new Observable<void>();
-    public onSaveRequiredObservable = new Observable<void>();
+    public onSaveRequiredObservable = new Observable<void>();    
+    public onLoadRequiredObservable = new Observable<string>();
     public onErrorObservable = new Observable<Nullable<CompilationError>>();    
     public onMobileDefaultModeChangedObservable = new Observable<void>();
     public onDisplayWaitRingObservable = new Observable<boolean>();
@@ -48,6 +49,7 @@ export class GlobalState {
     public onFontSizeChangedObservable = new Observable<void>();
     public onLanguageChangedObservable = new Observable<void>();
     public onNavigateRequiredObservable = new Observable<{lineNumber: number, column: number}>();
+    public onExamplesDisplayChangedObservable = new Observable<void>();
 
     public loadingCodeInProgress = false;
     public onCodeLoaded = new Observable<string>();

+ 2 - 0
Playground/src/playground.tsx

@@ -13,6 +13,7 @@ import { HamburgerMenuComponent } from './components/hamburgerMenu';
 import { Utilities } from './tools/utilities';
 import { ShortcutManager } from './tools/shortcutManager';
 import { ErrorDisplayComponent } from './components/errorDisplayComponent';
+import { ExamplesComponent } from './components/examplesComponent';
 
 require("./scss/main.scss");
 const Split = require('split.js').default;
@@ -117,6 +118,7 @@ export class Playground extends React.Component<IPlaygroundProps, {errorMessage:
                     window.innerWidth < 1024 &&
                     <HamburgerMenuComponent globalState={this._globalState}/>
                 }
+                <ExamplesComponent globalState={this._globalState}/>
                 <FooterComponent globalState={this._globalState}/>    
                 <ErrorDisplayComponent globalState={this._globalState}/>            
                 <WaitRingComponent globalState={this._globalState}/>

+ 25 - 2
Playground/src/scss/commandBar.scss

@@ -2,7 +2,16 @@
 .commands {
     grid-row: 1;
     grid-column: 3;
-    display: flex;
+
+    .commands-left {
+        float: left;
+        display: flex;
+    }
+
+    .commands-right {
+        float: right;
+        display: flex;
+    }
 
     &.background-ts {
         .command-button, .command-dropdown  {
@@ -48,6 +57,12 @@
             transform-origin: center;
             transform: scale(0.95);
         }
+
+        img {
+            &.active {
+                filter: invert(100%);
+            }
+        }
     }
 
     .command-dropdown-root {
@@ -123,7 +138,6 @@
             grid-template-rows: 100%;
             position: relative;
 
-
             &:hover {
                 .sub-items {
                     display: block;
@@ -140,6 +154,15 @@
                 grid-row: 1;
             }
 
+            .command-dropdown-arrow {
+                grid-column: 2;
+                grid-row: 1;    
+                font-size: 28px;
+                font-weight: bold;
+                padding-bottom: 10px;
+                padding-left: 4px;
+            }
+
             .sub-items {
                 position: absolute;
                 left: 200px;

+ 132 - 0
Playground/src/scss/examples.scss

@@ -0,0 +1,132 @@
+#examples {    
+    grid-row: 2;
+    grid-column: 3;
+    position: absolute;
+    top:55px;
+    right: 0;
+    bottom: 35px;
+    background: white;
+    transform: translateX(380px);
+    opacity: 0;
+    transition: all 0.2s ease;
+
+    &.visible {
+        transform: translateX(0);
+        opacity: 1;
+    }
+
+    width: 380px;
+    display: grid;
+    grid-template-columns: 100%;
+    grid-template-rows: 60px 60px calc(100% - 120px);
+    overflow: hidden;
+
+    #examples-header {
+        grid-row: 1;
+        grid-column: 1;
+        background: #201936;
+        color:white;
+        font-size: 24px;
+        display: grid;
+        align-content: center;
+        justify-content: center;
+    }
+    
+    #examples-filter {
+        grid-row: 2;
+        grid-column: 1;        
+        display: grid;
+        align-content: center;
+        justify-content: center;
+
+        input {
+            border: none;
+            padding: 0;
+            border-bottom: solid 1px #337ab7;
+            background: linear-gradient(to bottom, rgba(255,255,255,0) 96%, #337ab7 4%);
+            background-position: -1000px 0;
+            background-size: 1000px 100%;
+            background-repeat: no-repeat;
+            color: black;
+            width: 250px;
+
+            &:focus {
+                outline: none;
+            }
+        }
+    }
+
+    #examples-list {
+        padding: 5px;
+        overflow-y: auto;
+        grid-row: 3;
+        grid-column: 1;
+        user-select: none;
+
+        .example-category { 
+            .example-category-title { 
+                background: #BB464B;
+                margin: 10px 0;
+                color: white;   
+                font-size: 22px;
+                height: 60px;
+                display: grid;
+                align-content: center;
+                justify-content: center;
+            }
+
+            .example {
+                margin-bottom: 10px;
+                background: #ebebeb;
+                display: grid;
+                height: 125px;
+                grid-template-columns: 125px 1fr;
+                grid-template-rows: 40px 60px 25px;
+                cursor: pointer;
+
+                &:hover {
+                    outline: #BB464B solid 2px;
+                }
+
+                &:active {
+                    transform: scale(0.95);
+                }
+
+                img {                    
+                    grid-row: 1 / 4;
+                    grid-column: 1 / 3;
+                }
+
+                .example-title {
+                    grid-row: 1;
+                    grid-column: 2;
+                    font-size: 16px;
+                    font-weight: bold;
+                    margin-left: 10px;
+                }
+
+                .example-description {
+                    grid-row: 2;
+                    grid-column: 2;
+                    font-size: 14px;
+                    margin-left: 10px;
+                }
+                
+                .example-link {
+                    grid-row: 3;
+                    grid-column: 2;
+                    font-size: 14px;
+                    margin-left: 10px;
+                    text-decoration: underline;
+                    color: #BB464B;
+                }
+            }
+       }  
+    }
+}
+
+@media screen and (max-width: 1024px) {
+    #examples {
+        top: 40px;
+    }
+}

+ 1 - 1
Playground/src/scss/footer.scss

@@ -1,7 +1,7 @@
 #footer {
     width: 100%;
     height: 100%;
-    grid-column: 1 / 3;
+    grid-column: 1 / 4;
     grid-row: 3;
     padding: 0;
     margin: 0;

+ 4 - 0
Playground/src/scss/hamburgerMenu.scss

@@ -95,6 +95,10 @@
         img {
             grid-column: 1;
             grid-row: 1;
+
+            &.active {
+                filter: invert(100%);
+            }
         }
 
         &:hover {

+ 1 - 1
Playground/src/scss/header.scss

@@ -1,7 +1,7 @@
 #pg-header {
     width: 100%;
     height: 100%;
-    grid-column: 2;
+    grid-column: 2 / 4;
     grid-row: 1;
     padding: 0;
     margin: 0;

+ 8 - 3
Playground/src/tools/loadManager.ts

@@ -8,6 +8,11 @@ export class LoadManager {
         // Check the url to prepopulate data        
         this._checkHash();
         window.addEventListener("hashchange", () => this._checkHash());
+
+        globalState.onLoadRequiredObservable.add(id => {
+            globalState.onDisplayWaitRingObservable.notifyObservers(true);
+            this._loadPlayground(id);
+        });
     }
 
     private _cleanHash() {
@@ -51,12 +56,12 @@ export class LoadManager {
                 parent.location.hash = pgHash;
             }
             this._previousHash = pgHash;
-            this.globalState.loadingCodeInProgress = true;
-            this._loadPlayground(pgHash.substr(1))
+            this._loadPlayground(pgHash.substr(1));
         }        
     }
 
-    private _loadPlayground(id: string) {
+    private _loadPlayground(id: string) {        
+        this.globalState.loadingCodeInProgress = true;
         try {
             var xmlHttp = new XMLHttpRequest();
             xmlHttp.onreadystatechange = () => {