瀏覽代碼

Load Snippet and Save Snippet style (#8365)

* Styling

* styling

Co-authored-by: Alejandro Toledo <alex@pixelspace.com>
Alejandro Toledo Martinez 5 年之前
父節點
當前提交
47d923afa6

+ 1 - 1
dist/preview release/what's new.md

@@ -8,7 +8,7 @@
 - Added HDR texture filtering tools to the sandbox ([Sebavan](https://github.com/sebavan/))
 - Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added editing of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
-- Added Curve editor to manage selected entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
+- Added Curve editor to manage entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
 - Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 - Added support for **thin instances** for faster mesh instances. [Doc](https://doc.babylonjs.com/how_to/how_to_use_thininstances) ([Popov72](https://github.com/Popov72))
 

+ 5 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -16,6 +16,7 @@ import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
 import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
 import { EditorControls } from './editorControls';
 import { SelectedCoordinate } from './animationListTree';
+import { LockObject } from "../lockObject";
 
 require("./curveEditor.scss");
 
@@ -24,6 +25,7 @@ interface IAnimationCurveEditorComponentProps {
     playOrPause?: () => void;
     scene: Scene;
     entity: IAnimatable | TargetedAnimation;
+    lockObject: LockObject
 }
 
 interface ICanvasAxis {
@@ -1118,10 +1120,12 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
                 <div className="content">
                     <div className="row">
-                        <EditorControls selectAnimation={(animation: Animation, axis?: SelectedCoordinate) => this.selectAnimation(animation, axis)}
+                        <EditorControls
+                            selectAnimation={(animation: Animation, axis?: SelectedCoordinate) => this.selectAnimation(animation, axis)}
                             isTargetedAnimation={this._isTargetedAnimation}
                             entity={this.props.entity}
                             selected={this.state.selected}
+                            lockObject={this.props.lockObject}
                             setNotificationMessage={(message: string) => { this.setState({ notification: message }) }}
                         />
 

+ 42 - 41
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx

@@ -181,7 +181,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                     </LineContainerComponent>
                 }
                 {
-                    animations && 
+                    animations &&
                     <>
                         <LineContainerComponent globalState={this.props.globalState} title="ANIMATIONS">
                             <TextLineComponent label="Count" value={animations.length.toString()} />
@@ -200,13 +200,14 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                                     id="curve-editor"
                                     title="Curve Animation Editor"
                                     size={{ width: 1024, height: 490 }}
-                                    onOpen={(window: Window) => {  }}
+                                    onOpen={(window: Window) => { }}
                                     onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
 
-                                    <AnimationCurveEditorComponent 
-                                        scene={this.props.scene} 
-                                        entity={animatableAsAny} 
-                                        close={(event) => this.onCloseAnimationCurveEditor(event.view)} 
+                                    <AnimationCurveEditorComponent
+                                        scene={this.props.scene}
+                                        entity={animatableAsAny}
+                                        close={(event) => this.onCloseAnimationCurveEditor(event.view)}
+                                        lockObject={this.props.lockObject}
                                         playOrPause={() => this.playOrPause()} />
                                 </PopupComponent>
                             }
@@ -214,43 +215,43 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                         {
                             animations.length > 0 &&
                             <LineContainerComponent globalState={this.props.globalState} title="ANIMATION GENERAL CONTROL">
-                            <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="From" target={this._animationControl} propertyName="from" onChange={() => this.onChangeFromOrTo()} />
-                            <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="To" target={this._animationControl} propertyName="to" onChange={() => this.onChangeFromOrTo()} />
-                            <CheckBoxLineComponent label="Loop" onSelect={value => this._animationControl.loop = value} isSelected={() => this._animationControl.loop} />
-                            {
-                                this._isPlaying &&
-                                <SliderLineComponent ref={this.timelineRef} label="Current frame" minimum={this._animationControl.from} maximum={this._animationControl.to}
-                                    step={(this._animationControl.to - this._animationControl.from) / 1000.0} directValue={this.state.currentFrame}
-                                    onInput={value => this.onCurrentFrameChange(value)}
-                                />
-                            }
-                            <ButtonLineComponent label={this._isPlaying ? "Stop" : "Play"} onClick={() => this.playOrPause()} />
-                            {
-                                (this._ranges.length > 0 || this._animations && this._animations.length > 0) &&
-                                <>
-                                    <CheckBoxLineComponent label="Enable override" onSelect={value => {
-                                        if (value) {
-                                            animatableAsAny.animationPropertiesOverride = new AnimationPropertiesOverride();
-                                            animatableAsAny.animationPropertiesOverride.blendingSpeed = 0.05;
-                                        } else {
-                                            animatableAsAny.animationPropertiesOverride = null;
-                                        }
-                                        this.forceUpdate();
-                                    }} isSelected={() => animatableAsAny.animationPropertiesOverride != null}
-                                        onValueChanged={() => this.forceUpdate()}
+                                <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="From" target={this._animationControl} propertyName="from" onChange={() => this.onChangeFromOrTo()} />
+                                <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="To" target={this._animationControl} propertyName="to" onChange={() => this.onChangeFromOrTo()} />
+                                <CheckBoxLineComponent label="Loop" onSelect={value => this._animationControl.loop = value} isSelected={() => this._animationControl.loop} />
+                                {
+                                    this._isPlaying &&
+                                    <SliderLineComponent ref={this.timelineRef} label="Current frame" minimum={this._animationControl.from} maximum={this._animationControl.to}
+                                        step={(this._animationControl.to - this._animationControl.from) / 1000.0} directValue={this.state.currentFrame}
+                                        onInput={value => this.onCurrentFrameChange(value)}
                                     />
-                                    {
-                                        animatableAsAny.animationPropertiesOverride != null &&
-                                        <div>
-                                            <CheckBoxLineComponent label="Enable blending" target={animatableAsAny.animationPropertiesOverride} propertyName="enableBlending" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                                            <SliderLineComponent label="Blending speed" target={animatableAsAny.animationPropertiesOverride} propertyName="blendingSpeed" minimum={0} maximum={0.1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                                        </div>
-                                    }
-                                </>
-                            }
-                        </LineContainerComponent>
+                                }
+                                <ButtonLineComponent label={this._isPlaying ? "Stop" : "Play"} onClick={() => this.playOrPause()} />
+                                {
+                                    (this._ranges.length > 0 || this._animations && this._animations.length > 0) &&
+                                    <>
+                                        <CheckBoxLineComponent label="Enable override" onSelect={value => {
+                                            if (value) {
+                                                animatableAsAny.animationPropertiesOverride = new AnimationPropertiesOverride();
+                                                animatableAsAny.animationPropertiesOverride.blendingSpeed = 0.05;
+                                            } else {
+                                                animatableAsAny.animationPropertiesOverride = null;
+                                            }
+                                            this.forceUpdate();
+                                        }} isSelected={() => animatableAsAny.animationPropertiesOverride != null}
+                                            onValueChanged={() => this.forceUpdate()}
+                                        />
+                                        {
+                                            animatableAsAny.animationPropertiesOverride != null &&
+                                            <div>
+                                                <CheckBoxLineComponent label="Enable blending" target={animatableAsAny.animationPropertiesOverride} propertyName="enableBlending" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                                                <SliderLineComponent label="Blending speed" target={animatableAsAny.animationPropertiesOverride} propertyName="blendingSpeed" minimum={0} maximum={0.1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                                            </div>
+                                        }
+                                    </>
+                                }
+                            </LineContainerComponent>
                         }
-                        </>
+                    </>
                 }
             </div>
         );

File diff suppressed because it is too large
+ 1044 - 865
inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss


+ 206 - 102
inspector/src/components/actionTabs/tabs/propertyGrids/animations/editorControls.tsx

@@ -1,115 +1,219 @@
-
 import * as React from "react";
 
 import { Observable } from "babylonjs/Misc/observable";
 import { PropertyChangedEvent } from "../../../../../components/propertyChangedEvent";
-import { Animation } from 'babylonjs/Animations/animation';
-import { IconButtonLineComponent } from '../../../lines/iconButtonLineComponent';
-import { NumericInputComponent } from '../../../lines/numericInputComponent';
-import { AddAnimation } from './addAnimation';
-import { AnimationListTree, SelectedCoordinate } from './animationListTree';
-import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
+import { Animation } from "babylonjs/Animations/animation";
+import { IconButtonLineComponent } from "../../../lines/iconButtonLineComponent";
+import { NumericInputComponent } from "../../../lines/numericInputComponent";
+import { AddAnimation } from "./addAnimation";
+import { AnimationListTree, SelectedCoordinate } from "./animationListTree";
+import { IAnimatable } from "babylonjs/Animations/animatable.interface";
 import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
-
+import { LoadSnippet } from "./loadsnippet";
+import { SaveSnippet } from "./saveSnippet";
+import { LockObject } from "../lockObject";
 
 interface IEditorControlsProps {
-    isTargetedAnimation: boolean;
-    entity: IAnimatable | TargetedAnimation;
-    selected: Animation | null
-    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
-    setNotificationMessage: (message: string) => void;
-    selectAnimation: (selected: Animation, axis?: SelectedCoordinate) => void;
+  isTargetedAnimation: boolean;
+  entity: IAnimatable | TargetedAnimation;
+  selected: Animation | null;
+  lockObject: LockObject;
+  onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+  setNotificationMessage: (message: string) => void;
+  selectAnimation: (selected: Animation, axis?: SelectedCoordinate) => void;
 }
 
-export class EditorControls extends React.Component<IEditorControlsProps, { isAnimationTabOpen: boolean, isEditTabOpen: boolean, isLoadTabOpen: boolean, isSaveTabOpen: boolean, isLoopActive: boolean, animationsCount: number; framesPerSecond: number }>{
-
-    constructor(props: IEditorControlsProps) {
-        super(props);
-        let count = this.props.isTargetedAnimation ? 1 : (this.props.entity as IAnimatable).animations?.length ?? 0;
-        this.state = { isAnimationTabOpen: count === 0 ? true : false, isEditTabOpen: count === 0 ? false : true, isSaveTabOpen: false, isLoadTabOpen: false, isLoopActive: false, animationsCount: count, framesPerSecond: 60 }
+export class EditorControls extends React.Component<
+  IEditorControlsProps,
+  {
+    isAnimationTabOpen: boolean;
+    isEditTabOpen: boolean;
+    isLoadTabOpen: boolean;
+    isSaveTabOpen: boolean;
+    isLoopActive: boolean;
+    animationsCount: number;
+    framesPerSecond: number;
+  }
+> {
+  constructor(props: IEditorControlsProps) {
+    super(props);
+    let count = this.props.isTargetedAnimation
+      ? 1
+      : (this.props.entity as IAnimatable).animations?.length ?? 0;
+    this.state = {
+      isAnimationTabOpen: count === 0 ? true : false,
+      isEditTabOpen: count === 0 ? false : true,
+      isSaveTabOpen: false,
+      isLoadTabOpen: false,
+      isLoopActive: false,
+      animationsCount: count,
+      framesPerSecond: 60,
+    };
+  }
+
+  animationAdded() {
+    this.setState({
+      animationsCount: this.recountAnimations(),
+      isEditTabOpen: true,
+      isAnimationTabOpen: false,
+    });
+  }
+
+  recountAnimations() {
+    return (this.props.entity as IAnimatable).animations?.length ?? 0;
+  }
+
+  handleTabs(tab: number) {
+    let state = {
+      isAnimationTabOpen: true,
+      isLoadTabOpen: false,
+      isSaveTabOpen: false,
+      isEditTabOpen: false,
+    };
+
+    switch (tab) {
+      case 0:
+        state = {
+          isAnimationTabOpen: true,
+          isLoadTabOpen: false,
+          isSaveTabOpen: false,
+          isEditTabOpen: false,
+        };
+        break;
+      case 1:
+        state = {
+          isAnimationTabOpen: false,
+          isLoadTabOpen: true,
+          isSaveTabOpen: false,
+          isEditTabOpen: false,
+        };
+        break;
+      case 2:
+        state = {
+          isAnimationTabOpen: false,
+          isLoadTabOpen: false,
+          isSaveTabOpen: true,
+          isEditTabOpen: false,
+        };
+        break;
+      case 3:
+        state = {
+          isAnimationTabOpen: false,
+          isLoadTabOpen: false,
+          isSaveTabOpen: false,
+          isEditTabOpen: true,
+        };
+        break;
     }
 
-    animationAdded() {
-        this.setState({ animationsCount: this.recountAnimations(), isEditTabOpen: true, isAnimationTabOpen: false });
-    }
-
-    recountAnimations() {
-        return (this.props.entity as IAnimatable).animations?.length ?? 0;
-    }
-
-    handleTabs(tab: number) {
-
-        let state = { isAnimationTabOpen: true, isLoadTabOpen: false, isSaveTabOpen: false, isEditTabOpen: false };
-
-        switch (tab) {
-            case 0:
-                state = { isAnimationTabOpen: true, isLoadTabOpen: false, isSaveTabOpen: false, isEditTabOpen: false };
-                break;
-            case 1:
-                state = { isAnimationTabOpen: false, isLoadTabOpen: true, isSaveTabOpen: false, isEditTabOpen: false };
-                break;
-            case 2:
-                state = { isAnimationTabOpen: false, isLoadTabOpen: false, isSaveTabOpen: true, isEditTabOpen: false };
-                break;
-            case 3:
-                state = { isAnimationTabOpen: false, isLoadTabOpen: false, isSaveTabOpen: false, isEditTabOpen: true };
-                break;
-        }
-
-        this.setState(state);
-    }
-
-    handleChangeFps(fps: number) {
-        this.setState({ framesPerSecond: fps });
-    }
-
-    emptiedList() {
-        this.setState({ animationsCount: this.recountAnimations(), isEditTabOpen: false, isAnimationTabOpen: true });
-    }
-
-    render() {
-        return (
-            <div className="animation-list">
-                <div className="controls-header">
-                    {this.props.isTargetedAnimation ? null : <IconButtonLineComponent active={this.state.isAnimationTabOpen} tooltip="Add Animation" icon="medium add-animation" onClick={() => this.handleTabs(0)}></IconButtonLineComponent>}
-                    <IconButtonLineComponent active={this.state.isLoadTabOpen} tooltip="Load Animation" icon="medium load" onClick={() => this.handleTabs(1)}></IconButtonLineComponent>
-                    <IconButtonLineComponent active={this.state.isSaveTabOpen} tooltip="Save Animation" icon="medium save" onClick={() => this.handleTabs(2)}></IconButtonLineComponent>
-                    {this.state.animationsCount === 0 ? null :
-                        <IconButtonLineComponent active={this.state.isEditTabOpen} tooltip="Edit Animations" icon="medium animation-edit" onClick={() => this.handleTabs(3)}></IconButtonLineComponent>
-                    }
-                    {this.state.isEditTabOpen ?
-                        <div className="input-fps">
-                            <NumericInputComponent label={""} precision={0} value={this.state.framesPerSecond} onChange={(framesPerSecond: number) => this.handleChangeFps(framesPerSecond)} />
-                            <p>fps</p>
-                        </div> : null
-                    }
-                    {this.state.isEditTabOpen ?
-                        <IconButtonLineComponent tooltip="Loop/Unloop" icon={`medium ${this.state.isLoopActive ? 'loop-active last' : 'loop-inactive last'}`} onClick={() => { this.setState({ isLoopActive: !this.state.isLoopActive }) }}></IconButtonLineComponent> : null
-                    }
-                </div>
-                {this.props.isTargetedAnimation ? null :
-                    <AddAnimation
-                        isOpen={this.state.isAnimationTabOpen}
-                        close={() => { this.setState({ isAnimationTabOpen: false }) }}
-                        entity={(this.props.entity as IAnimatable)}
-                        setNotificationMessage={(message: string) => { this.props.setNotificationMessage(message) }}
-                        changed={() => this.animationAdded()}
-                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+    this.setState(state);
+  }
+
+  handleChangeFps(fps: number) {
+    this.setState({ framesPerSecond: fps });
+  }
+
+  emptiedList() {
+    this.setState({
+      animationsCount: this.recountAnimations(),
+      isEditTabOpen: false,
+      isAnimationTabOpen: true,
+    });
+  }
+
+  render() {
+    return (
+      <div className="animation-list">
+        <div className="controls-header">
+          {this.props.isTargetedAnimation ? null : (
+            <IconButtonLineComponent
+              active={this.state.isAnimationTabOpen}
+              tooltip="Add Animation"
+              icon="medium add-animation"
+              onClick={() => this.handleTabs(0)}
+            ></IconButtonLineComponent>
+          )}
+          <IconButtonLineComponent
+            active={this.state.isLoadTabOpen}
+            tooltip="Load Animation"
+            icon="medium load"
+            onClick={() => this.handleTabs(1)}
+          ></IconButtonLineComponent>
+          <IconButtonLineComponent
+            active={this.state.isSaveTabOpen}
+            tooltip="Save Animation"
+            icon="medium save"
+            onClick={() => this.handleTabs(2)}
+          ></IconButtonLineComponent>
+          {this.state.animationsCount === 0 ? null : (
+            <IconButtonLineComponent
+              active={this.state.isEditTabOpen}
+              tooltip="Edit Animations"
+              icon="medium animation-edit"
+              onClick={() => this.handleTabs(3)}
+            ></IconButtonLineComponent>
+          )}
+          {this.state.isEditTabOpen ? (
+            <div className="input-fps">
+              <NumericInputComponent
+                label={""}
+                precision={0}
+                value={this.state.framesPerSecond}
+                onChange={(framesPerSecond: number) =>
+                  this.handleChangeFps(framesPerSecond)
                 }
-
-                {this.state.isLoadTabOpen ? <div>Load</div> : null}
-
-                {this.state.isSaveTabOpen ? <div>Save</div> : null}
-
-                {this.state.isEditTabOpen ? <AnimationListTree
-                    isTargetedAnimation={this.props.isTargetedAnimation}
-                    entity={this.props.entity}
-                    selected={this.props.selected}
-                    onPropertyChangedObservable={this.props.onPropertyChangedObservable}
-                    empty={() => this.emptiedList()}
-                    selectAnimation={this.props.selectAnimation} />
-                    : null}
+              />
+              <p>fps</p>
             </div>
-        );
-    }
-}
+          ) : null}
+          {this.state.isEditTabOpen ? (
+            <IconButtonLineComponent
+              tooltip="Loop/Unloop"
+              icon={`medium ${
+                this.state.isLoopActive
+                  ? "loop-active last"
+                  : "loop-inactive last"
+              }`}
+              onClick={() => {
+                this.setState({ isLoopActive: !this.state.isLoopActive });
+              }}
+            ></IconButtonLineComponent>
+          ) : null}
+        </div>
+        {this.props.isTargetedAnimation ? null : (
+          <AddAnimation
+            isOpen={this.state.isAnimationTabOpen}
+            close={() => {
+              this.setState({ isAnimationTabOpen: false });
+            }}
+            entity={this.props.entity as IAnimatable}
+            setNotificationMessage={(message: string) => {
+              this.props.setNotificationMessage(message);
+            }}
+            changed={() => this.animationAdded()}
+            onPropertyChangedObservable={this.props.onPropertyChangedObservable}
+          />
+        )}
+
+        {this.state.isLoadTabOpen ? (
+          <LoadSnippet lockObject={this.props.lockObject} animations={[]} />
+        ) : null}
+
+        {this.state.isSaveTabOpen ? (
+          <SaveSnippet lockObject={this.props.lockObject} animations={[]} />
+        ) : null}
+
+        {this.state.isEditTabOpen ? (
+          <AnimationListTree
+            isTargetedAnimation={this.props.isTargetedAnimation}
+            entity={this.props.entity}
+            selected={this.props.selected}
+            onPropertyChangedObservable={this.props.onPropertyChangedObservable}
+            empty={() => this.emptiedList()}
+            selectAnimation={this.props.selectAnimation}
+          />
+        ) : null}
+      </div>
+    );
+  }
+}

+ 51 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/loadsnippet.tsx

@@ -0,0 +1,51 @@
+import * as React from "react";
+import { Observable } from "babylonjs/Misc/observable";
+import { PropertyChangedEvent } from "../../../../../components/propertyChangedEvent";
+import { Animation } from "babylonjs/Animations/animation";
+import { ButtonLineComponent } from "../../../lines/buttonLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { LockObject } from "../lockObject";
+
+interface ILoadSnippetProps {
+  animations: Animation[];
+  onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+  lockObject: LockObject;
+}
+
+export class LoadSnippet extends React.Component<
+  ILoadSnippetProps,
+  { server: string }
+> {
+  private _serverAddress: string;
+  constructor(props: ILoadSnippetProps) {
+    super(props);
+    this._serverAddress = "-";
+    this.state = { server: "" };
+  }
+
+  change(value: string) {
+    this.setState({ server: value });
+  }
+
+  render() {
+    return (
+      <div className="load-container">
+        <TextInputLineComponent
+          label="Snippet Server"
+          lockObject={this.props.lockObject}
+          value={this.state.server}
+          onChange={(value: string) => this.change(value)}
+        />
+        <ButtonLineComponent label="Load" onClick={() => {}} />
+        <div className="load-browse">
+          <p>Local File</p>
+          <ButtonLineComponent label="Browse" onClick={() => {}} />
+        </div>
+        <div className="load-server">
+          <p>Snippet Server : </p>
+          <p>{this._serverAddress}</p>
+        </div>
+      </div>
+    );
+  }
+}

+ 90 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/saveSnippet.tsx

@@ -0,0 +1,90 @@
+import * as React from "react";
+import { Observable } from "babylonjs/Misc/observable";
+import { PropertyChangedEvent } from "../../../../../components/propertyChangedEvent";
+import { ButtonLineComponent } from "../../../lines/buttonLineComponent";
+import { LockObject } from "../lockObject";
+
+interface ISaveSnippetProps {
+  animations: any[];
+  onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+  lockObject: LockObject;
+}
+
+interface SelectedAnimation {
+  id: string;
+  name: string;
+  index: number;
+  selected: boolean;
+}
+
+export class SaveSnippet extends React.Component<
+  ISaveSnippetProps,
+  { selectedAnimations: SelectedAnimation[] }
+> {
+  private _serverAddress: string;
+  constructor(props: ISaveSnippetProps) {
+    super(props);
+    this._serverAddress = "-";
+    let animList = this.props.animations.map((animation, i) => {
+      return {
+        id: `${animation.name}_${animation.targetProperty}`,
+        name: animation.name,
+        index: i,
+        selected: false,
+      };
+    });
+    this.state = { selectedAnimations: animList };
+  }
+
+  handleCheckboxChange(e: React.ChangeEvent<HTMLInputElement>) {
+    e.preventDefault();
+
+    let index = parseInt(e.target.id.replace("save_", ""));
+
+    let updated = this.state.selectedAnimations.map((item) => {
+      if (item.index === index) {
+        item.selected = !item.selected;
+      }
+      return item;
+    });
+
+    this.setState({ selectedAnimations: updated });
+  }
+
+  render() {
+    return (
+      <div className="save-container">
+        <div className="item-list">
+          <ul>
+            {this.props.animations.map((animation, i) => {
+              return (
+                <li key={i}>
+                  <div>
+                    <label>
+                      <input
+                        id={`save_${i}`}
+                        name={`save_${animation.name}`}
+                        type="checkbox"
+                        checked={this.state.selectedAnimations[i].selected}
+                        onChange={(e) => this.handleCheckboxChange(e)}
+                      />
+                      {animation.name}
+                    </label>
+                  </div>
+                </li>
+              );
+            })}
+          </ul>
+        </div>
+        <div className="save-buttons">
+          <ButtonLineComponent label="Save Snippet" onClick={() => {}} />
+          <ButtonLineComponent label="Save File" onClick={() => {}} />
+        </div>
+        <div className="save-server">
+          <p>Snippet Server:</p>
+          <p>{this._serverAddress}</p>
+        </div>
+      </div>
+    );
+  }
+}

+ 116 - 82
inspector/src/components/actionTabs/tabs/propertyGrids/animations/targetedAnimationPropertyGridComponent.tsx

@@ -9,102 +9,136 @@ import { ButtonLineComponent } from "../../../lines/buttonLineComponent";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { TextLineComponent } from "../../../lines/textLineComponent";
 import { LockObject } from "../lockObject";
-import { GlobalState } from '../../../../globalState';
-import { TextInputLineComponent } from '../../../lines/textInputLineComponent';
-import { PopupComponent } from '../animations/popupComponent';
-import { AnimationCurveEditorComponent } from '../animations/animationCurveEditorComponent';
+import { GlobalState } from "../../../../globalState";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { PopupComponent } from "../animations/popupComponent";
+import { AnimationCurveEditorComponent } from "../animations/animationCurveEditorComponent";
 import { AnimationGroup } from "babylonjs/Animations/animationGroup";
 
 interface ITargetedAnimationGridComponentProps {
-    globalState: GlobalState;
-    targetedAnimation: TargetedAnimation,
-    scene: Scene,
-    lockObject: LockObject,    
-    onSelectionChangedObservable?: Observable<any>,
-    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+  globalState: GlobalState;
+  targetedAnimation: TargetedAnimation;
+  scene: Scene;
+  lockObject: LockObject;
+  onSelectionChangedObservable?: Observable<any>;
+  onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
 }
 
-export class TargetedAnimationGridComponent extends React.Component<ITargetedAnimationGridComponentProps> {
+export class TargetedAnimationGridComponent extends React.Component<
+  ITargetedAnimationGridComponentProps
+> {
+  private _isCurveEditorOpen: boolean;
+  private _animationGroup: AnimationGroup | undefined;
+  constructor(props: ITargetedAnimationGridComponentProps) {
+    super(props);
+    this._animationGroup = this.props.scene.animationGroups.find((ag) => {
+      let ta = ag.targetedAnimations.find(
+        (ta) => ta === this.props.targetedAnimation
+      );
+      return ta !== undefined;
+    });
+  }
 
-    private _isCurveEditorOpen: boolean;
-    private _animationGroup: AnimationGroup | undefined;
-    constructor(props: ITargetedAnimationGridComponentProps) {
-        super(props);
-        this._animationGroup = this.props.scene.animationGroups.find(ag => {
-            let ta = ag.targetedAnimations.find(ta => ta === this.props.targetedAnimation);  
-            return ta !== undefined;
-        });
-    }
-
-    onOpenAnimationCurveEditor() {
-        this._isCurveEditorOpen = true;
-    }
+  onOpenAnimationCurveEditor() {
+    this._isCurveEditorOpen = true;
+  }
 
-    onCloseAnimationCurveEditor(window: Window | null) {
-        this._isCurveEditorOpen = false;
-        if (window !== null) {
-            window.close();
-        }
+  onCloseAnimationCurveEditor(window: Window | null) {
+    this._isCurveEditorOpen = false;
+    if (window !== null) {
+      window.close();
     }
+  }
 
-    playOrPause() {
-        if (this._animationGroup){
-            if (this._animationGroup.isPlaying) {
-                this._animationGroup.stop();
-            } else {
-                this._animationGroup.start();
-            }
-            this.forceUpdate();
-        } 
+  playOrPause() {
+    if (this._animationGroup) {
+      if (this._animationGroup.isPlaying) {
+        this._animationGroup.stop();
+      } else {
+        this._animationGroup.start();
+      }
+      this.forceUpdate();
     }
+  }
 
-    deleteAnimation() {        
-        if (this._animationGroup) {
-            let index = this._animationGroup.targetedAnimations.indexOf(this.props.targetedAnimation);
+  deleteAnimation() {
+    if (this._animationGroup) {
+      let index = this._animationGroup.targetedAnimations.indexOf(
+        this.props.targetedAnimation
+      );
 
-            if (index > -1) {
-                this._animationGroup.targetedAnimations.splice(index, 1);
-                this.props.onSelectionChangedObservable?.notifyObservers(null);
+      if (index > -1) {
+        this._animationGroup.targetedAnimations.splice(index, 1);
+        this.props.onSelectionChangedObservable?.notifyObservers(null);
 
-                if (this._animationGroup.isPlaying) {
-                    this._animationGroup.stop();
-                    this._animationGroup.start();
-                }
-            }
+        if (this._animationGroup.isPlaying) {
+          this._animationGroup.stop();
+          this._animationGroup.start();
         }
+      }
     }
+  }
 
-    render() {
-        const targetedAnimation = this.props.targetedAnimation;
+  render() {
+    const targetedAnimation = this.props.targetedAnimation;
 
-        return (
-            <div className="pane">
-                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
-                    <TextLineComponent label="Class" value={targetedAnimation.getClassName()} />
-                    <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={targetedAnimation.animation} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
-                    {
-                        targetedAnimation.target.name &&
-                        <TextLineComponent label="Target" value={targetedAnimation.target.name} onLink={() => this.props.globalState.onSelectionChangedObservable.notifyObservers(targetedAnimation)}/>
-                    }
-                    <ButtonLineComponent label="Edit animation" onClick={() => this.onOpenAnimationCurveEditor()} />
-                    {
-                        this._isCurveEditorOpen && <PopupComponent
-                            id="curve-editor"
-                            title="Curve Animation Editor"
-                            size={{ width: 1024, height: 512 }}
-                            onOpen={(window: Window) => {  }}
-                            onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
-
-                            <AnimationCurveEditorComponent
-                                scene={this.props.scene} 
-                                entity={targetedAnimation as any} 
-                                playOrPause={() => this.playOrPause()}
-                                close={(event) => this.onCloseAnimationCurveEditor(event.view)} />
-                        </PopupComponent>
-                    }                    
-                    <ButtonLineComponent label="Dispose" onClick={() => this.deleteAnimation()} />
-                </LineContainerComponent>
-            </div>
-        );
-    }
-}
+    return (
+      <div className="pane">
+        <LineContainerComponent
+          globalState={this.props.globalState}
+          title="GENERAL"
+        >
+          <TextLineComponent
+            label="Class"
+            value={targetedAnimation.getClassName()}
+          />
+          <TextInputLineComponent
+            lockObject={this.props.lockObject}
+            label="Name"
+            target={targetedAnimation.animation}
+            propertyName="name"
+            onPropertyChangedObservable={this.props.onPropertyChangedObservable}
+          />
+          {targetedAnimation.target.name && (
+            <TextLineComponent
+              label="Target"
+              value={targetedAnimation.target.name}
+              onLink={() =>
+                this.props.globalState.onSelectionChangedObservable.notifyObservers(
+                  targetedAnimation
+                )
+              }
+            />
+          )}
+          <ButtonLineComponent
+            label="Edit animation"
+            onClick={() => this.onOpenAnimationCurveEditor()}
+          />
+          {this._isCurveEditorOpen && (
+            <PopupComponent
+              id="curve-editor"
+              title="Curve Animation Editor"
+              size={{ width: 1024, height: 512 }}
+              onOpen={(window: Window) => {}}
+              onClose={(window: Window) =>
+                this.onCloseAnimationCurveEditor(window)
+              }
+            >
+              <AnimationCurveEditorComponent
+                scene={this.props.scene}
+                entity={targetedAnimation as any}
+                playOrPause={() => this.playOrPause()}
+                lockObject={this.props.lockObject}
+                close={(event) => this.onCloseAnimationCurveEditor(event.view)}
+              />
+            </PopupComponent>
+          )}
+          <ButtonLineComponent
+            label="Dispose"
+            onClick={() => this.deleteAnimation()}
+          />
+        </LineContainerComponent>
+      </div>
+    );
+  }
+}