瀏覽代碼

Code docs

Alejandro Toledo 4 年之前
父節點
當前提交
b83a3383b2
共有 15 個文件被更改,包括 825 次插入681 次删除
  1. 44 10
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/addAnimation.tsx
  2. 19 36
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/anchorSvgPoint.tsx
  3. 356 247
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx
  4. 11 10
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationListTree.tsx
  5. 18 50
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/controls.tsx
  6. 65 62
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/editorControls.tsx
  7. 36 48
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar.tsx
  8. 20 16
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint.tsx
  9. 10 6
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/loadsnippet.tsx
  10. 3 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/notification.tsx
  11. 68 65
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/playhead.tsx
  12. 8 12
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/saveSnippet.tsx
  13. 2 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/scale-label.tsx
  14. 55 36
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx
  15. 110 83
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/timeline.tsx

+ 44 - 10
inspector/src/components/actionTabs/tabs/propertyGrids/animations/addAnimation.tsx

@@ -3,8 +3,8 @@ import { ButtonLineComponent } from "../../../lines/buttonLineComponent";
 import { Observable } from "babylonjs/Misc/observable";
 import { PropertyChangedEvent } from "../../../../../components/propertyChangedEvent";
 import { Animation } from "babylonjs/Animations/animation";
-//import { Vector2, Vector3, Quaternion } from "babylonjs/Maths/math.vector";
-//import { Color3, Color4 } from "babylonjs/Maths/math.color";
+//import { Vector2, Vector3, Quaternion } from "babylonjs/Maths/math.vector"; // Remove comment lines when these imports are supported
+//import { Color3, Color4 } from "babylonjs/Maths/math.color"; // Remove comment lines when these imports are supported
 import { IAnimatable } from "babylonjs/Animations/animatable.interface";
 import { IAnimationKey } from "babylonjs/Animations/animationKey";
 
@@ -21,7 +21,16 @@ interface IAddAnimationProps {
 }
 
 /**
- * Controls the creation of a new animation
+ * Controls the creation of a new animation.
+ * @property {boolean} isOpen controls if the add animation pannel is open or closed in the editor controls;
+ * @property {()=>void} close sends the message to close this panel setting isOpen to false;
+ * @property {IAnimatable} entity is the animation object to add the animation to;
+ * @property {Observable<PropertyChangedEvent>} onPropertyChangedObservable is the registered observable
+ * @property {(message: string) => void} setNotificationMessage sends the message string to display errors on the message box
+ * @property {() => void} finishedUpdate tells the parent component the update on the animation collection has completed
+ * @property { (animation: Animation) => void} addedNewAnimation sends the animation to the editor to process how its is rendered
+ * @property {number} fps Frames per second of the animation.
+ * @property {Animation | undefined} selectedToUpdate the selected animation so we can update the renderer curve.
  */
 export class AddAnimation extends React.Component<
     IAddAnimationProps,
@@ -50,6 +59,11 @@ export class AddAnimation extends React.Component<
         };
     }
 
+    /**
+     * We decide wether the animation will be added or edited
+     * @param prevProps Previous props.
+     * @param prevState Previous state.
+     */
     componentDidUpdate(prevProps: IAddAnimationProps, prevState: any) {
         if (this.props.selectedToUpdate !== undefined && this.props.selectedToUpdate !== prevProps.selectedToUpdate) {
             this.setState(this.setInitialState(this.props.selectedToUpdate));
@@ -60,6 +74,13 @@ export class AddAnimation extends React.Component<
         }
     }
 
+    /**
+     * Updates the animation with the correct properties
+     * Updates its name, loopmode and targetProperty.
+     * This allows the edition of the animation in the curve editor.
+     * We can only update these props for the animation.
+     * Keyframes of course are updated on the curve editor.
+     */
     updateAnimation = () => {
         if (this.props.selectedToUpdate !== undefined) {
             const oldNameValue = this.props.selectedToUpdate.name;
@@ -78,6 +99,10 @@ export class AddAnimation extends React.Component<
         }
     };
 
+    /**
+     * Returns the animation type as string
+     * @param type Type of animation so we return a string.
+     */
     getTypeAsString(type: number) {
         switch (type) {
             case Animation.ANIMATIONTYPE_FLOAT:
@@ -99,6 +124,14 @@ export class AddAnimation extends React.Component<
         }
     }
 
+    /**
+     * Process the creation of a new animation.
+     * We verify is the property to animate is present on the object
+     * We verify if the property derives from a correct class *type of animation to add an animation
+     * At the end we create an empty animation named animation.
+     * The animation keys array is set to empty so we can add keyframes in the editor.
+     * We return the animation to the curve editor and update the entity animation collection
+     */
     addAnimation = () => {
         if (this.state.animationName != "" && this.state.animationTargetProperty != "") {
             let matchTypeTargetProperty = this.state.animationTargetProperty.split(".");
@@ -155,7 +188,7 @@ export class AddAnimation extends React.Component<
                 } else {
                     let animation = new Animation(this.state.animationName, this.state.animationTargetProperty, this.props.fps, animationDataType);
 
-                    // Start with two keyframes
+                    // Start with empty keyframes
                     var keys: IAnimationKey[] = [];
 
                     animation.setKeys(keys);
@@ -166,7 +199,7 @@ export class AddAnimation extends React.Component<
                         this.raiseOnPropertyChanged(updatedCollection, store);
                         this.props.entity.animations = updatedCollection;
                         this.props.addedNewAnimation(animation);
-                        //Cleaning form fields
+                        //Here we clean the form fields
                         this.setState({
                             animationName: "",
                             animationTargetPath: "",
@@ -255,14 +288,15 @@ export class AddAnimation extends React.Component<
                         <div className="label-input">
                             <label>Type</label>
                             <select onChange={this.handleTypeChange} value={this.state.animationType}>
+                                {/** Uncomment the following lines when other animation types are available */}
                                 {/* <option value={Animation.ANIMATIONTYPE_COLOR3}>Color3</option>
-                <option value={Animation.ANIMATIONTYPE_COLOR4}>Color4</option> */}
+                                    <option value={Animation.ANIMATIONTYPE_COLOR4}>Color4</option> */}
                                 <option value={Animation.ANIMATIONTYPE_FLOAT}>Float</option>
                                 {/* <option value={Animation.ANIMATIONTYPE_VECTOR3}>Vector3</option>
-                <option value={Animation.ANIMATIONTYPE_VECTOR2}>Vector2</option>
-                <option value={Animation.ANIMATIONTYPE_QUATERNION}>
-                  Quaternion
-                </option> */}
+                                    <option value={Animation.ANIMATIONTYPE_VECTOR2}>Vector2</option>
+                                    <option value={Animation.ANIMATIONTYPE_QUATERNION}>
+                                        Quaternion
+                                    </option> */}
                             </select>
                         </div>
                     )}

+ 19 - 36
inspector/src/components/actionTabs/tabs/propertyGrids/animations/anchorSvgPoint.tsx

@@ -14,6 +14,16 @@ interface IAnchorSvgPointProps {
 
 /**
  * Renders the control point to a keyframe.
+ * Each keyframe has left and right control points to control de tangent of the curve
+ * This controls the inTangent and outTangent values for the keyframe in the animation.
+ * @property {Vector2} control is the control point to control de curve tangent
+ * @property {Vector2} anchor represents the Keyframe point which acts origin point.
+ * @property {boolen} active tells the component if the control point is currently active
+ * @property {string} type (left/right) if the control will be the left or right control point
+ * @property {boolean} selected if the control point is currently selected. If selected we can move the control point and will become active
+ * @property {(id: string) => void;} selectControlPoint sends the id of the control point to the parent component to tell if it is selected
+ * @property {{ from: number; to: number }} framesInCanvasView controls from/to which keyframe should the control point can expand and control de curve
+ * The frames in canvas tells us how many frames are currently visible in the canvas and therefore control the width of the line between the control and anchor point
  */
 export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps, { visiblePoint: Vector2 }> {
     constructor(props: IAnchorSvgPointProps) {
@@ -31,6 +41,11 @@ export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps, { visi
         this.props.selectControlPoint(this.props.type);
     };
 
+    /**
+     * Controls where should we render the visible point (representing the control point)
+     * The visible control point differs from the control point for UX reasons. The control point
+     * expands beyond the visible canvas.
+     */
     setVisiblePoint() {
         const quarterDistance = (this.props.framesInCanvasView.to - this.props.framesInCanvasView.from) / 10;
         const distanceOnFlat = Math.abs(this.props.anchor.x - this.props.control.x);
@@ -47,44 +62,12 @@ export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps, { visi
         const visibleCircle = this.props.selected ? "#ffc017" : "black";
         return (
             <>
-                <line
-                    className={`control-point ${this.props.active ? "active" : ""}`}
-                    x1={this.props.anchor.x}
-                    y1={this.props.anchor.y}
-                    x2={this.state.visiblePoint.x}
-                    y2={this.state.visiblePoint.y}
-                    strokeWidth="0.8%"
-                />
-                <svg
-                    x={this.state.visiblePoint.x}
-                    y={this.state.visiblePoint.y}
-                    style={{ overflow: "visible" }}
-                    onClick={this.select}
-                >
-                    <circle
-                        type={this.props.type}
-                        data-id={this.props.index}
-                        className={visibleCircleClass}
-                        cx="0"
-                        cy="0"
-                        r="0.75%"
-                        stroke="aqua"
-                        strokeWidth={strokeVisibleCircle}
-                        fill={visibleCircle}
-                    />
+                <line className={`control-point ${this.props.active ? "active" : ""}`} x1={this.props.anchor.x} y1={this.props.anchor.y} x2={this.state.visiblePoint.x} y2={this.state.visiblePoint.y} strokeWidth="0.8%" />
+                <svg x={this.state.visiblePoint.x} y={this.state.visiblePoint.y} style={{ overflow: "visible" }} onClick={this.select}>
+                    <circle type={this.props.type} data-id={this.props.index} className={visibleCircleClass} cx="0" cy="0" r="0.75%" stroke="aqua" strokeWidth={strokeVisibleCircle} fill={visibleCircle} />
                 </svg>
                 <svg x={this.props.control.x} y={this.props.control.y} style={{ overflow: "visible", display: "none" }}>
-                    <circle
-                        type={this.props.type}
-                        data-id={this.props.index}
-                        className={nonVisibleCircleClass}
-                        cx="0"
-                        cy="0"
-                        r="0.7%"
-                        stroke="white"
-                        strokeWidth={0}
-                        fill={"white"}
-                    />
+                    <circle type={this.props.type} data-id={this.props.index} className={nonVisibleCircleClass} cx="0" cy="0" r="0.7%" stroke="white" strokeWidth={0} fill={"white"} />
                 </svg>
             </>
         );

File diff suppressed because it is too large
+ 356 - 247
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx


+ 11 - 10
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationListTree.tsx

@@ -8,13 +8,21 @@ import { IconButtonLineComponent } from "../../../lines/iconButtonLineComponent"
 import { Nullable } from "babylonjs/types";
 
 interface IAnimationListTreeProps {
+    // If the animation is targeted animation or not
     isTargetedAnimation: boolean;
+    // The entity that is being targetd by the animations
     entity: IAnimatable | TargetedAnimation;
+    // The currently selected animations
     selected: Animation | null;
+    // The obeservable
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    // Event to send the selected animation and the coordinate to render the correct curve
     selectAnimation: (selected: Animation, coordinate?: SelectedCoordinate) => void;
+    // Event to empty the animation list
     empty: () => void;
+    // Event to edit the selected animation
     editAnimation: (selected: Animation) => void;
+    // Event to deselect the animation
     deselectAnimation: () => void;
 }
 
@@ -26,6 +34,7 @@ interface Item {
     open: boolean;
 }
 
+// Collection of coordinates available in different animated target property types.
 export enum SelectedCoordinate {
     x = 0,
     y = 1,
@@ -195,9 +204,7 @@ export class AnimationListTree extends React.Component<
                         <div className="spacer"></div>
                     )
                 ) : null}
-                <ul className={`sub-list ${this.state.animationList && this.state.animationList[i].open ? "" : "hidden"}`}>
-                    {childrenElements.map((c) => this.coordinateItem(i, animation, c.id, c.color, c.coordinate))}
-                </ul>
+                <ul className={`sub-list ${this.state.animationList && this.state.animationList[i].open ? "" : "hidden"}`}>{childrenElements.map((c) => this.coordinateItem(i, animation, c.id, c.color, c.coordinate))}</ul>
             </li>
         );
     }
@@ -212,13 +219,7 @@ export class AnimationListTree extends React.Component<
                         <div className={`animation-bullet`}></div>
                         <p>{animation.targetProperty}</p>
                         <IconButtonLineComponent tooltip="Options" icon="small animation-options" onClick={editAnimation} />
-                        {!(this.props.entity instanceof TargetedAnimation) ? (
-                            this.props.selected && this.props.selected.name === animation.name ? (
-                                <IconButtonLineComponent tooltip="Remove" icon="small animation-delete" onClick={this.deleteAnimation} />
-                            ) : (
-                                <div className="spacer"></div>
-                            )
-                        ) : null}
+                        {!(this.props.entity instanceof TargetedAnimation) ? this.props.selected && this.props.selected.name === animation.name ? <IconButtonLineComponent tooltip="Remove" icon="small animation-delete" onClick={this.deleteAnimation} /> : <div className="spacer"></div> : null}
                     </li>
                 );
             case Animation.ANIMATIONTYPE_VECTOR2:

+ 18 - 50
inspector/src/components/actionTabs/tabs/propertyGrids/animations/controls.tsx

@@ -3,13 +3,21 @@ import { IAnimationKey } from "babylonjs/Animations/animationKey";
 import { IconButtonLineComponent } from "../../../lines/iconButtonLineComponent";
 
 interface IControlsProps {
+    // Keyframes to choose start or end of animation playback
     keyframes: IAnimationKey[] | null;
+    // The currently selected animation keyframe
     selected: IAnimationKey | null;
+    // The current frame number
     currentFrame: number;
+    // Event to change the current frame
     onCurrentFrameChange: (frame: number) => void;
+    // Event to communicate canvas repposition
     repositionCanvas: (keyframe: IAnimationKey) => void;
+    // Event to play, pause or play backwards the animation
     playPause: (direction: number) => void;
+    // If the animation is playing
     isPlaying: boolean;
+    // The reference to the scrollable dom object to set its position
     scrollable: React.RefObject<HTMLDivElement>;
 }
 
@@ -86,70 +94,30 @@ export class Controls extends React.Component<IControlsProps, { selected: IAnima
     render() {
         return (
             <div className="controls">
-                <IconButtonLineComponent
-                    tooltip="Animation Start"
-                    icon="animation-start"
-                    onClick={this.moveToAnimationStart}
-                ></IconButtonLineComponent>
-                <IconButtonLineComponent
-                    tooltip="Previous Keyframe"
-                    icon="animation-lastkey"
-                    onClick={this.previousKeyframe}
-                ></IconButtonLineComponent>
+                <IconButtonLineComponent tooltip="Animation Start" icon="animation-start" onClick={this.moveToAnimationStart}></IconButtonLineComponent>
+                <IconButtonLineComponent tooltip="Previous Keyframe" icon="animation-lastkey" onClick={this.previousKeyframe}></IconButtonLineComponent>
                 {this.props.isPlaying ? (
                     <div className="stop-container">
                         {this.state.playingType === "reverse" ? (
                             <>
-                                <IconButtonLineComponent
-                                    tooltip="Pause"
-                                    icon="animation-stop"
-                                    onClick={this.pause}
-                                ></IconButtonLineComponent>
-                                <IconButtonLineComponent
-                                    tooltip="Play Forward"
-                                    icon="animation-playfwd"
-                                    onClick={this.play}
-                                ></IconButtonLineComponent>
+                                <IconButtonLineComponent tooltip="Pause" icon="animation-stop" onClick={this.pause}></IconButtonLineComponent>
+                                <IconButtonLineComponent tooltip="Play Forward" icon="animation-playfwd" onClick={this.play}></IconButtonLineComponent>
                             </>
                         ) : (
                             <>
-                                <IconButtonLineComponent
-                                    tooltip="Play Reverse"
-                                    icon="animation-playrev"
-                                    onClick={this.playBackwards}
-                                ></IconButtonLineComponent>
-                                <IconButtonLineComponent
-                                    tooltip="Pause"
-                                    icon="animation-stop"
-                                    onClick={this.pause}
-                                ></IconButtonLineComponent>
+                                <IconButtonLineComponent tooltip="Play Reverse" icon="animation-playrev" onClick={this.playBackwards}></IconButtonLineComponent>
+                                <IconButtonLineComponent tooltip="Pause" icon="animation-stop" onClick={this.pause}></IconButtonLineComponent>
                             </>
                         )}
                     </div>
                 ) : (
                     <div className="stop-container">
-                        <IconButtonLineComponent
-                            tooltip="Play Reverse"
-                            icon="animation-playrev"
-                            onClick={this.playBackwards}
-                        ></IconButtonLineComponent>
-                        <IconButtonLineComponent
-                            tooltip="Play Forward"
-                            icon="animation-playfwd"
-                            onClick={this.play}
-                        ></IconButtonLineComponent>
+                        <IconButtonLineComponent tooltip="Play Reverse" icon="animation-playrev" onClick={this.playBackwards}></IconButtonLineComponent>
+                        <IconButtonLineComponent tooltip="Play Forward" icon="animation-playfwd" onClick={this.play}></IconButtonLineComponent>
                     </div>
                 )}
-                <IconButtonLineComponent
-                    tooltip="Next Keyframe"
-                    icon="animation-nextkey"
-                    onClick={this.nextKeyframe}
-                ></IconButtonLineComponent>
-                <IconButtonLineComponent
-                    tooltip="Animation End"
-                    icon="animation-end"
-                    onClick={this.moveToAnimationEnd}
-                ></IconButtonLineComponent>
+                <IconButtonLineComponent tooltip="Next Keyframe" icon="animation-nextkey" onClick={this.nextKeyframe}></IconButtonLineComponent>
+                <IconButtonLineComponent tooltip="Animation End" icon="animation-end" onClick={this.moveToAnimationEnd}></IconButtonLineComponent>
             </div>
         );
     }

+ 65 - 62
inspector/src/components/actionTabs/tabs/propertyGrids/animations/editorControls.tsx

@@ -14,38 +14,59 @@ import { LockObject } from "../lockObject";
 import { GlobalState } from "../../../../globalState";
 
 interface IEditorControlsProps {
+    // if the entity has a targeted animation
     isTargetedAnimation: boolean;
+    // Entity if it is animatable or targeted animation type
     entity: IAnimatable | TargetedAnimation;
+    // The current selected animation
     selected: Animation | null;
+    // The global lock object
     lockObject: LockObject;
+    // The observable
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    // Event to send the message to the notification bar
     setNotificationMessage: (message: string) => void;
+    // Event to send the selected animation and the coordinate (x, y, z) axis to render a curve
     selectAnimation: (selected: Animation, axis?: SelectedCoordinate) => void;
+    // Event to set the frames per second of the animation
     setFps: (fps: number) => void;
+    // Event to set if the animation loops
     setIsLooping: () => void;
+    // The global state
     globalState: GlobalState;
+    // The snippet server address
     snippetServer: string;
+    // Event to deselect an animation
     deselectAnimation: () => void;
+    // The frames per second
     fps: number;
 }
 
+interface IEditorControlsState {
+    // If the add animation tab is currently visible in the editor
+    isAnimationTabOpen: boolean;
+    // If the edit animation tab is currently visible in the editor
+    isEditTabOpen: boolean;
+    // If the load animations tab is currently visible in the editor
+    isLoadTabOpen: boolean;
+    // If the save animations tab is currently visible in the editor
+    isSaveTabOpen: boolean;
+    // If the loop toggle is active or not.
+    isLoopActive: boolean;
+    // How many animations we have in the tree
+    animationsCount: number;
+    // Frames per second of the selected animation
+    framesPerSecond: number;
+    // The snippet ID the user needs to input to save/load (needs to switch tab)
+    snippetId: string;
+    // The currently selected animation
+    selected: Animation | undefined;
+}
+
 /**
  * Renders the Curve Editor controls to create, save, remove, load and edit animations
  */
-export class EditorControls extends React.Component<
-    IEditorControlsProps,
-    {
-        isAnimationTabOpen: boolean;
-        isEditTabOpen: boolean;
-        isLoadTabOpen: boolean;
-        isSaveTabOpen: boolean;
-        isLoopActive: boolean;
-        animationsCount: number;
-        framesPerSecond: number;
-        snippetId: string;
-        selected: Animation | undefined;
-    }
-> {
+export class EditorControls extends React.Component<IEditorControlsProps, IEditorControlsState> {
     constructor(props: IEditorControlsProps) {
         super(props);
         let count = this.props.isTargetedAnimation ? 1 : (this.props.entity as IAnimatable).animations?.length ?? 0;
@@ -62,12 +83,20 @@ export class EditorControls extends React.Component<
         };
     }
 
+    /**
+     * Makes sure the frames per second receive the updated prop
+     * @param prevProps previous properties
+     */
     componentDidUpdate(prevProps: IEditorControlsProps) {
         if (this.props.fps !== prevProps.fps) {
             this.setState({ framesPerSecond: this.props.fps });
         }
     }
 
+    /**
+     * Add the nimation, recounts the list and opens the correct tab
+     * @param animation The recently empty created animation
+     */
     onAnimationAdded = (animation: Animation) => {
         this.setState({
             animationsCount: this.recountAnimations(),
@@ -77,6 +106,9 @@ export class EditorControls extends React.Component<
         this.props.selectAnimation(animation, undefined);
     };
 
+    /**
+     * Set state when the animations list has finished updated
+     */
     finishedUpdate = () => {
         this.setState({
             isEditTabOpen: true,
@@ -85,10 +117,14 @@ export class EditorControls extends React.Component<
         });
     };
 
+    // Recount animations
     recountAnimations() {
         return (this.props.entity as IAnimatable).animations?.length ?? 0;
     }
 
+    /**
+     * Toggles loop
+     */
     changeLoopBehavior = () => {
         this.setState({
             isLoopActive: !this.state.isLoopActive,
@@ -109,6 +145,7 @@ export class EditorControls extends React.Component<
         this.handleTabs(3);
     };
 
+    // Opens/Closes the tabs
     handleTabs(tab: number) {
         let state = {
             isAnimationTabOpen: true,
@@ -155,6 +192,7 @@ export class EditorControls extends React.Component<
         this.setState(state);
     }
 
+    // Set state of FPS and sends the fps to parent
     handleChangeFps = (fps: number) => {
         this.props.setFps(fps);
         this.setState({ framesPerSecond: fps });
@@ -197,6 +235,10 @@ export class EditorControls extends React.Component<
         }
     };
 
+    /**
+     * The currently selected animation to edit it
+     * @param selected Selected animation
+     */
     editAnimation = (selected: Animation) => {
         this.setState({
             selected: selected,
@@ -207,11 +249,12 @@ export class EditorControls extends React.Component<
         });
     };
 
+    // Set state of the snippet id
     setSnippetId = (id: string) => {
         this.setState({ snippetId: id });
     };
 
-     /**
+    /**
      * Marks animation tab closed and hides the tab
      */
     onCloseAddAnimation = () => {
@@ -222,49 +265,17 @@ export class EditorControls extends React.Component<
         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.handleFirstTab}></IconButtonLineComponent>
-                    )}
-                    <IconButtonLineComponent
-                        active={this.state.isLoadTabOpen}
-                        tooltip="Load Animation"
-                        icon="medium load"
-                        onClick={this.handleSecondTab}></IconButtonLineComponent>
-                    {this.state.animationsCount === 0 ? null : (
-                        <IconButtonLineComponent
-                            active={this.state.isSaveTabOpen}
-                            tooltip="Save Animation"
-                            icon="medium save"
-                            onClick={this.handleThirdTab}></IconButtonLineComponent>
-                    )}
-                    {this.state.animationsCount === 0 ? null : (
-                        <IconButtonLineComponent
-                            active={this.state.isEditTabOpen}
-                            tooltip="Edit Animations"
-                            icon="medium animation-edit"
-                            onClick={this.handleFourthTab}></IconButtonLineComponent>
-                    )}
+                    {this.props.isTargetedAnimation ? null : <IconButtonLineComponent active={this.state.isAnimationTabOpen} tooltip="Add Animation" icon="medium add-animation" onClick={this.handleFirstTab}></IconButtonLineComponent>}
+                    <IconButtonLineComponent active={this.state.isLoadTabOpen} tooltip="Load Animation" icon="medium load" onClick={this.handleSecondTab}></IconButtonLineComponent>
+                    {this.state.animationsCount === 0 ? null : <IconButtonLineComponent active={this.state.isSaveTabOpen} tooltip="Save Animation" icon="medium save" onClick={this.handleThirdTab}></IconButtonLineComponent>}
+                    {this.state.animationsCount === 0 ? null : <IconButtonLineComponent active={this.state.isEditTabOpen} tooltip="Edit Animations" icon="medium animation-edit" onClick={this.handleFourthTab}></IconButtonLineComponent>}
                     {this.state.isEditTabOpen ? (
                         <div className="input-fps">
-                            <NumericInputComponent
-                                label={""}
-                                precision={0}
-                                value={this.state.framesPerSecond}
-                                onChange={this.handleChangeFps}
-                            />
+                            <NumericInputComponent label={""} precision={0} value={this.state.framesPerSecond} onChange={this.handleChangeFps} />
                             <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.changeLoopBehavior}></IconButtonLineComponent>
-                    ) : null}
+                    {this.state.isEditTabOpen ? <IconButtonLineComponent tooltip="Loop/Unloop" icon={`medium ${this.state.isLoopActive ? "loop-active last" : "loop-inactive last"}`} onClick={this.changeLoopBehavior}></IconButtonLineComponent> : null}
                 </div>
                 {this.props.isTargetedAnimation ? null : (
                     <AddAnimation
@@ -293,15 +304,7 @@ export class EditorControls extends React.Component<
                     />
                 ) : null}
 
-                {this.state.isSaveTabOpen ? (
-                    <SaveSnippet
-                        lockObject={this.props.lockObject}
-                        animations={(this.props.entity as IAnimatable).animations}
-                        snippetServer={this.props.snippetServer}
-                        globalState={this.props.globalState}
-                        snippetId={this.state.snippetId}
-                    />
-                ) : null}
+                {this.state.isSaveTabOpen ? <SaveSnippet lockObject={this.props.lockObject} animations={(this.props.entity as IAnimatable).animations} snippetServer={this.props.snippetServer} globalState={this.props.globalState} snippetId={this.state.snippetId} /> : null}
 
                 {this.state.isEditTabOpen ? (
                     <AnimationListTree

+ 36 - 48
inspector/src/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar.tsx

@@ -3,20 +3,35 @@ import { IconButtonLineComponent } from "../../../lines/iconButtonLineComponent"
 import { IActionableKeyFrame } from "./animationCurveEditorComponent";
 
 interface IGraphActionsBarProps {
+    // Add a keyframe to animation
     addKeyframe: () => void;
+    // Remove keyframe to animation
     removeKeyframe: () => void;
+    // Shows the selected keyframes in the current visible canvas changing scale
     frameSelectedKeyframes: () => void;
+    // Handles the value change of the keyframe
     handleValueChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+    // Handles the frame change of the keyframe
     handleFrameChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+    // Flats the selected control point relative to its keyframe
     flatTangent: () => void;
+    // Allows the user to change the tangent values of control points independently
     brokeTangents: () => void;
+    // Set a linear interpolation to the next keyframe on the selected control point
     setLerpToActiveControlPoint: () => void;
+    // If broken mode is active or not
     brokenMode: boolean;
+    // If the linear interpolation botton is active
     lerpMode: boolean;
+    // The currently selected keyframe to perform value or frame updates
     actionableKeyframe: IActionableKeyFrame;
+    // Name of the selected entity and animation
     title: string;
+    // If the graph controls are enabled or not
     enabled: boolean;
+    // Sets the keyframe value on the actionableKeyFrame
     setKeyframeValue: (actionableKeyframe: IActionableKeyFrame) => void;
+    // How many frames are between selected keyframes
     frameRange: { min: number | undefined; max: number | undefined };
 }
 
@@ -24,10 +39,7 @@ interface IGraphActionsBarProps {
  * Has the buttons and actions for the Canvas Graph.
  * Handles input change and actions (flat, broken mode, set linear control points)
  */
-export class GraphActionsBar extends React.Component<
-    IGraphActionsBarProps,
-    { frame: string; value: string; min: number | undefined; max: number | undefined }
-> {
+export class GraphActionsBar extends React.Component<IGraphActionsBarProps, { frame: string; value: string; min: number | undefined; max: number | undefined }> {
     private _frameInput: React.RefObject<HTMLInputElement>;
     private _valueInput: React.RefObject<HTMLInputElement>;
     constructor(props: IGraphActionsBarProps) {
@@ -38,25 +50,28 @@ export class GraphActionsBar extends React.Component<
         this.state = { frame, value, min: this.props.frameRange.min, max: this.props.frameRange.max };
     }
 
+    // Listen to keyup changes to handle if the input event has ended or change
     componentDidMount() {
         this._frameInput.current?.addEventListener("keyup", this.isEnterKeyUp.bind(this));
         this._valueInput.current?.addEventListener("keyup", this.isEnterKeyUp.bind(this));
     }
 
+    // Set the changing state of frame, value and range of the actionablekeyframe
     componentDidUpdate(prevProps: IGraphActionsBarProps, prevState: any) {
         if (prevProps.actionableKeyframe !== this.props.actionableKeyframe) {
             const { frame, value } = this.selectedKeyframeChanged(this.props.actionableKeyframe);
             this.setState({ frame, value });
         }
 
-        if (
-            prevProps.frameRange.min !== this.props.frameRange.min ||
-            prevProps.frameRange.max !== this.props.frameRange.max
-        ) {
+        if (prevProps.frameRange.min !== this.props.frameRange.min || prevProps.frameRange.max !== this.props.frameRange.max) {
             this.setState({ min: this.props.frameRange.min, max: this.props.frameRange.max });
         }
     }
 
+    /**
+     * Returns the frame and value for the keyframe
+     * @param keyframe The keyframe to update
+     */
     selectedKeyframeChanged(keyframe: IActionableKeyFrame) {
         let frame = "";
         if (typeof keyframe.frame === "number") {
@@ -69,11 +84,13 @@ export class GraphActionsBar extends React.Component<
         return { frame, value };
     }
 
+    // Remove listeners
     componentWillUnmount() {
         this._frameInput.current?.removeEventListener("keyup", this.isEnterKeyUp.bind(this));
         this._valueInput.current?.removeEventListener("keyup", this.isEnterKeyUp.bind(this));
     }
 
+    // Trigger the change on the keyframe
     isEnterKeyUp(event: KeyboardEvent) {
         event.preventDefault();
 
@@ -83,6 +100,7 @@ export class GraphActionsBar extends React.Component<
         }
     }
 
+    // Trigger the chnage on the keyframe on blur
     onBlur = (event: React.FocusEvent<HTMLInputElement>) => {
         event.preventDefault();
         if (event.target.value !== "") {
@@ -91,6 +109,7 @@ export class GraphActionsBar extends React.Component<
         }
     };
 
+    // Gets the keyframe frame
     getFrame() {
         let frame;
         if (this.state.frame === "") {
@@ -102,6 +121,7 @@ export class GraphActionsBar extends React.Component<
         return frame;
     }
 
+    // Gets the keyframe value
     getValue() {
         let value;
         if (this.state.value !== "") {
@@ -112,11 +132,13 @@ export class GraphActionsBar extends React.Component<
         return value;
     }
 
+    // Set keyframe value state
     handleValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         e.preventDefault();
         this.setState({ value: e.target.value });
     };
 
+    // Set the keyframe frame state
     handleFrameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         e.preventDefault();
         this.setState({ frame: e.target.value });
@@ -131,50 +153,16 @@ export class GraphActionsBar extends React.Component<
                 </div>
                 <div className={`buttons-container ${this.props.enabled ? "pointer-events-enabled" : "pointer-events-disabled"}`}>
                     <div className="action-input frame-input">
-                        <input
-                            ref={this._frameInput}
-                            type="number"
-                            onChange={this.handleFrameChange}
-                            value={this.state.frame}
-                            max={this.state.max}
-                            min={this.state.min}
-                            step="1"
-                            disabled={this.props.actionableKeyframe.frame === undefined}
-                            onBlur={this.onBlur}
-                        />
+                        <input ref={this._frameInput} type="number" onChange={this.handleFrameChange} value={this.state.frame} max={this.state.max} min={this.state.min} step="1" disabled={this.props.actionableKeyframe.frame === undefined} onBlur={this.onBlur} />
                     </div>
                     <div className="action-input">
-                        <input
-                            ref={this._valueInput}
-                            type="number"
-                            value={this.state.value}
-                            onChange={this.handleValueChange}
-                            step="0.01"
-                            disabled={this.props.actionableKeyframe.value === undefined}
-                            onBlur={this.onBlur}
-                        />
+                        <input ref={this._valueInput} type="number" value={this.state.value} onChange={this.handleValueChange} step="0.01" disabled={this.props.actionableKeyframe.value === undefined} onBlur={this.onBlur} />
                     </div>
                     <IconButtonLineComponent tooltip={"Add Keyframe"} icon="new-key" onClick={this.props.addKeyframe} />
-                    <IconButtonLineComponent
-                        tooltip={"Frame selected keyframes"}
-                        icon="frame"
-                        onClick={this.props.frameSelectedKeyframes}
-                    />
-                    <IconButtonLineComponent
-                        tooltip={this.props.brokenMode ? "Flat selected control point" : "Flat control points"}
-                        icon="flat-tangent"
-                        onClick={this.props.flatTangent}
-                    />
-                    <IconButtonLineComponent
-                        tooltip={this.props.brokenMode ? "Broken Mode On" : "Broken Mode Off"}
-                        icon={this.props.brokenMode ? "break-tangent" : "unify-tangent"}
-                        onClick={this.props.brokeTangents}
-                    />
-                    <IconButtonLineComponent
-                        tooltip={"Linear"}
-                        icon="linear-tangent"
-                        onClick={this.props.setLerpToActiveControlPoint}
-                    />
+                    <IconButtonLineComponent tooltip={"Frame selected keyframes"} icon="frame" onClick={this.props.frameSelectedKeyframes} />
+                    <IconButtonLineComponent tooltip={this.props.brokenMode ? "Flat selected control point" : "Flat control points"} icon="flat-tangent" onClick={this.props.flatTangent} />
+                    <IconButtonLineComponent tooltip={this.props.brokenMode ? "Broken Mode On" : "Broken Mode Off"} icon={this.props.brokenMode ? "break-tangent" : "unify-tangent"} onClick={this.props.brokeTangents} />
+                    <IconButtonLineComponent tooltip={"Linear"} icon="linear-tangent" onClick={this.props.setLerpToActiveControlPoint} />
                 </div>
             </div>
         );

+ 20 - 16
inspector/src/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint.tsx

@@ -6,13 +6,21 @@ const keyInactive = require("./assets/keyInactiveIcon.svg") as string;
 const keySelected = require("./assets/keySelectedIcon.svg") as string;
 
 export interface IKeyframeSvgPoint {
+    // Keyframe point to render in svg canvas
     keyframePoint: Vector2;
+    // Right control point (controls curve)
     rightControlPoint: Vector2 | null;
+    // Left control point (controls curve)
     leftControlPoint: Vector2 | null;
+    // svg keyframe id
     id: string;
+    // If the keyframe is selected on canvas
     selected: boolean;
+    // I the left control point is being dragged or active
     isLeftActive: boolean;
+    // I the right control point is being dragged or active
     isRightActive: boolean;
+    // The parent curve id
     curveId?: ICurveMetaData;
 }
 
@@ -23,15 +31,25 @@ export interface ICurveMetaData {
 }
 
 interface IKeyframeSvgPointProps {
+    // Keyframe point to render in svg canvas
     keyframePoint: Vector2;
+    // Left control point (controls curve)
     leftControlPoint: Vector2 | null;
+    // Right control point (controls curve)
     rightControlPoint: Vector2 | null;
+    // svg keyframe id
     id: string;
+    // If the keyframe is selected on canvas
     selected: boolean;
+    // Select keyframe and mode of selection
     selectKeyframe: (id: string, multiselect: boolean) => void;
+    // Returns the id of the selected keyframe and its animation property type
     selectedControlPoint: (type: string, id: string) => void;
+    // I the left control point is being dragged or active
     isLeftActive: boolean;
+    // I the right control point is being dragged or active
     isRightActive: boolean;
+    // Current visible frames in canvas
     framesInCanvasView: { from: number; to: number };
 }
 
@@ -61,22 +79,8 @@ export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
         const svgImageIcon = this.props.selected ? keySelected : keyInactive;
         return (
             <>
-                <svg
-                    className="draggable"
-                    x={this.props.keyframePoint.x}
-                    y={this.props.keyframePoint.y}
-                    style={{ overflow: "visible", cursor: "pointer" }}
-                >
-                    <image
-                        data-id={this.props.id}
-                        className="draggable"
-                        x="-1"
-                        y="-1.5"
-                        width="3"
-                        height="3"
-                        href={svgImageIcon}
-                        onClick={this.select}
-                    />
+                <svg className="draggable" x={this.props.keyframePoint.x} y={this.props.keyframePoint.y} style={{ overflow: "visible", cursor: "pointer" }}>
+                    <image data-id={this.props.id} className="draggable" x="-1" y="-1.5" width="3" height="3" href={svgImageIcon} onClick={this.select} />
                 </svg>
                 {this.props.leftControlPoint && (
                     <AnchorSvgPoint

+ 10 - 6
inspector/src/components/actionTabs/tabs/propertyGrids/animations/loadsnippet.tsx

@@ -13,14 +13,23 @@ import { IAnimatable } from "babylonjs/Animations/animatable.interface";
 import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
 
 interface ILoadSnippetProps {
+    // Animations to load
     animations: Animation[];
+    // Observable
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    // Global lock object
     lockObject: LockObject;
+    // Global state
     globalState: GlobalState;
+    // Snippet server address
     snippetServer: string;
+    // Function to set the id of the snippert
     setSnippetId: (id: string) => void;
+    // entity to reference the animations
     entity: IAnimatable | TargetedAnimation;
+    // sets the message for error or warning
     setNotificationMessage: (message: string) => void;
+    // tells if animation have loaded successfully
     animationsLoaded: (numberOfAnimations: number) => void;
 }
 
@@ -95,12 +104,7 @@ export class LoadSnippet extends React.Component<ILoadSnippetProps, { snippetId:
     render() {
         return (
             <div className="load-container">
-                <TextInputLineComponent
-                    label="Snippet Id"
-                    lockObject={this.props.lockObject}
-                    value={this.state.snippetId}
-                    onChange={this.change}
-                />
+                <TextInputLineComponent label="Snippet Id" lockObject={this.props.lockObject} value={this.state.snippetId} onChange={this.change} />
                 <ButtonLineComponent label="Load from snippet server" onClick={this.loadFromSnippet} />
                 <div className="load-browse">
                     <p>Local File</p>

+ 3 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/notification.tsx

@@ -1,8 +1,11 @@
 import * as React from "react";
 
 interface IPlayheadProps {
+    // Message to display
     message: string;
+    // open or close state
     open: boolean;
+    // event to close the notification bar
     close: () => void;
 }
 

+ 68 - 65
inspector/src/components/actionTabs/tabs/propertyGrids/animations/playhead.tsx

@@ -1,80 +1,83 @@
-import * as React from 'react';
+import * as React from "react";
 
 interface IPlayheadProps {
-  frame: number;
-  offset: number;
-  onCurrentFrameChange: (frame: number) => void;
+    // Current frame
+    frame: number;
+    // SVG distane to render de playhead (margin)
+    offset: number;
+    // Event to change frame
+    onCurrentFrameChange: (frame: number) => void;
 }
 
 /**
  * Renders the Playhead
  */
 export class Playhead extends React.Component<IPlayheadProps> {
-  private _direction: number;
-  private _active: boolean;
-  constructor(props: IPlayheadProps) {
-    super(props);
-  }
+    private _direction: number;
+    private _active: boolean;
+    constructor(props: IPlayheadProps) {
+        super(props);
+    }
 
-  dragStart(e: React.TouchEvent<HTMLDivElement>): void;
-  dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-  dragStart(e: any) {
-    e.preventDefault();
-    this._direction = e.clientX;
-    this._active = true;
-  }
+    dragStart(e: React.TouchEvent<HTMLDivElement>): void;
+    dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+    dragStart(e: any) {
+        e.preventDefault();
+        this._direction = e.clientX;
+        this._active = true;
+    }
 
-  drag(e: React.TouchEvent<HTMLDivElement>): void;
-  drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-  drag(e: any) {
-    e.preventDefault();
-    if (this._active) {
-      let moved = e.pageX - this._direction;
-      if (Math.sign(moved) === -1) {
-        this.props.onCurrentFrameChange(this.props.frame - 1);
-      } else {
-        this.props.onCurrentFrameChange(this.props.frame + 1);
-      }
+    drag(e: React.TouchEvent<HTMLDivElement>): void;
+    drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+    drag(e: any) {
+        e.preventDefault();
+        if (this._active) {
+            let moved = e.pageX - this._direction;
+            if (Math.sign(moved) === -1) {
+                this.props.onCurrentFrameChange(this.props.frame - 1);
+            } else {
+                this.props.onCurrentFrameChange(this.props.frame + 1);
+            }
+        }
     }
-  }
 
-  dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
-  dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-  dragEnd(e: any) {
-    e.preventDefault();
-    this._direction = 0;
-    this._active = false;
-  }
+    dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
+    dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+    dragEnd(e: any) {
+        e.preventDefault();
+        this._direction = 0;
+        this._active = false;
+    }
 
-  calculateMove() {
-    return `calc(${this.props.frame * this.props.offset}px + 20px)`;
-  }
+    calculateMove() {
+        return `calc(${this.props.frame * this.props.offset}px + 20px)`;
+    }
 
-  render() {
-    return (
-      <div
-        className='playhead-wrapper'
-        id='playhead'
-        style={{
-          left: this.calculateMove(),
-        }}
-      >
-        <div className='playhead-line'></div>
-        <div
-          className='playhead-handle'
-          onMouseMove={(e) => this.drag(e)}
-          onTouchMove={(e) => this.drag(e)}
-          onTouchStart={(e) => this.dragStart(e)}
-          onTouchEnd={(e) => this.dragEnd(e)}
-          onMouseDown={(e) => this.dragStart(e)}
-          onMouseUp={(e) => this.dragEnd(e)}
-          onMouseLeave={(e) => this.dragEnd(e)}
-          onDragStart={() => false}
-        >
-          <div className='playhead-circle'></div>
-          <div className='playhead'>{this.props.frame}</div>
-        </div>
-      </div>
-    );
-  }
+    render() {
+        return (
+            <div
+                className="playhead-wrapper"
+                id="playhead"
+                style={{
+                    left: this.calculateMove(),
+                }}
+            >
+                <div className="playhead-line"></div>
+                <div
+                    className="playhead-handle"
+                    onMouseMove={(e) => this.drag(e)}
+                    onTouchMove={(e) => this.drag(e)}
+                    onTouchStart={(e) => this.dragStart(e)}
+                    onTouchEnd={(e) => this.dragEnd(e)}
+                    onMouseDown={(e) => this.dragStart(e)}
+                    onMouseUp={(e) => this.dragEnd(e)}
+                    onMouseLeave={(e) => this.dragEnd(e)}
+                    onDragStart={() => false}
+                >
+                    <div className="playhead-circle"></div>
+                    <div className="playhead">{this.props.frame}</div>
+                </div>
+            </div>
+        );
+    }
 }

+ 8 - 12
inspector/src/components/actionTabs/tabs/propertyGrids/animations/saveSnippet.tsx

@@ -9,11 +9,17 @@ import { Nullable } from "babylonjs/types";
 import { GlobalState } from "../../../../globalState";
 
 interface ISaveSnippetProps {
+    // Animation to save
     animations: Nullable<Animation[]>;
+    // Observable
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    // Global lock object
     lockObject: LockObject;
+    // Global state
     globalState: GlobalState;
+    // Snippet server address
     snippetServer: string;
+    // Snippert id to save the snippet
     snippetId: string;
 }
 
@@ -108,11 +114,7 @@ export class SaveSnippet extends React.Component<ISaveSnippetProps, { selectedAn
                             });
                         }
 
-                        alert(
-                            "Animations saved with ID: " +
-                                serverId +
-                                " (please note that the id was also saved to your clipboard)"
-                        );
+                        alert("Animations saved with ID: " + serverId + " (please note that the id was also saved to your clipboard)");
                     } else {
                         alert("Unable to save your animations");
                     }
@@ -145,13 +147,7 @@ export class SaveSnippet extends React.Component<ISaveSnippetProps, { selectedAn
                                 <li key={i}>
                                     <div>
                                         <label>
-                                            <input
-                                                id={`save_${i}`}
-                                                name={`save_${animation?.name}`}
-                                                type="checkbox"
-                                                checked={this.state.selectedAnimations[i].selected}
-                                                onChange={this.handleCheckboxChange}
-                                            />
+                                            <input id={`save_${i}`} name={`save_${animation?.name}`} type="checkbox" checked={this.state.selectedAnimations[i].selected} onChange={this.handleCheckboxChange} />
                                             {animation?.name}
                                         </label>
                                     </div>

+ 2 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/scale-label.tsx

@@ -2,7 +2,9 @@ import * as React from "react";
 import { CurveScale } from "./animationCurveEditorComponent";
 
 interface ISwitchButtonProps {
+    /** Current scale */
     current: CurveScale;
+    /** On click change of scale */
     action?: (event: CurveScale) => void;
 }
 

+ 55 - 36
inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx

@@ -3,27 +3,42 @@ import { Vector2 } from "babylonjs/Maths/math.vector";
 import { IKeyframeSvgPoint } from "./keyframeSvgPoint";
 
 interface ISvgDraggableAreaProps {
+    // List of SVG Points in curve
     keyframeSvgPoints: IKeyframeSvgPoint[];
+    // Update the position of the selected SVG Point
     updatePosition: (updatedKeyframe: IKeyframeSvgPoint, id: string) => void;
+    // The scale of the curve respect to the visible canvas
     scale: number;
+    // The SVG canvas viewBox
     viewBoxScale: number;
+    // Deselects all keyframes
     deselectKeyframes: () => void;
+    // Remove the selected keyframes
     removeSelectedKeyframes: (points: IKeyframeSvgPoint[]) => void;
+    // How much y panning the user has done to move the canvas (relative to canvas)
     panningY: (panningY: number) => void;
+    // How much x panning the user has done to move the canvas (relative to canvas)
     panningX: (panningX: number) => void;
+    // Moves the current selected frame back and forth depending on number of frames
     setCurrentFrame: (direction: number) => void;
+    // The anchor point of the canvas to center it
     positionCanvas?: Vector2;
+    // If the canvas is set to reposition
     repositionCanvas?: boolean;
+    // If the canvas reposition event has ended
     canvasPositionEnded: () => void;
+    // Resets the selected keyframe
     resetActionableKeyframe: () => void;
+    // How many frames should be visible in the current canvas scale
     framesInCanvasView: { from: number; to: number };
+    // How many frames has the canvas added or removed depending on window resize event
     framesResized: number;
 }
 
 /**
  * The SvgDraggableArea is a wrapper for SVG Canvas the interaction
  *
- * Here we control the drag and key behavior for the SVG components. 
+ * Here we control the drag and key behavior for the SVG components.
  */
 export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, { panX: number; panY: number }> {
     private _active: boolean;
@@ -58,6 +73,7 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         this.state = { panX: 0, panY: 0 };
     }
 
+    // Listen to key events to be able to drag and set the correct canvas client width
     componentDidMount() {
         this._draggableArea.current?.addEventListener("keydown", this.keyDown.bind(this));
         this._draggableArea.current?.addEventListener("keyup", this.keyUp.bind(this));
@@ -66,12 +82,9 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         }, 500);
     }
 
+    // Makes sure the canvas has resposition correctly
     componentDidUpdate(prevProps: ISvgDraggableAreaProps) {
-        if (
-            this.props.positionCanvas !== prevProps.positionCanvas &&
-            this.props.positionCanvas !== undefined &&
-            this.props.repositionCanvas
-        ) {
+        if (this.props.positionCanvas !== prevProps.positionCanvas && this.props.positionCanvas !== undefined && this.props.repositionCanvas) {
             this.setState(
                 {
                     panX: this.props.positionCanvas.x,
@@ -87,12 +100,15 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
     dragStart = (e: React.MouseEvent<SVGSVGElement, MouseEvent>): void => {
         e.preventDefault();
         if ((e.target as SVGSVGElement).classList.contains("draggable")) {
+            // Set dragging as active
             this._active = true;
+            // If we are trying to move a keyframe, set the current dragging point being moved
             const dataId = (e.target as SVGSVGElement).getAttribute("data-id");
             if (dataId !== null) {
                 this._currentPointId = dataId;
             }
 
+            // If we are trying to move a keyframe control point, set the dragging control point
             if ((e.target as SVGSVGElement).classList.contains("control-point")) {
                 const type = (e.target as SVGSVGElement).getAttribute("type");
                 if (type !== null) {
@@ -101,19 +117,18 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
             }
         }
 
+        // Controls if we are trying to drag the playhead
         if ((e.target as SVGSVGElement).classList.contains("svg-playhead")) {
             this._active = true;
             this._playheadSelected = true;
             this._playheadDrag = e.clientX - e.currentTarget.getBoundingClientRect().left;
         }
 
+        // Controls if we are trying to pan the content in the canvas
         if ((e.target as SVGSVGElement).classList.contains("pannable")) {
             if (this._isControlKeyPress) {
                 this._active = true;
-                this._panStart.set(
-                    e.clientX - e.currentTarget.getBoundingClientRect().left,
-                    e.clientY - e.currentTarget.getBoundingClientRect().top
-                );
+                this._panStart.set(e.clientX - e.currentTarget.getBoundingClientRect().left, e.clientY - e.currentTarget.getBoundingClientRect().top);
             }
         }
     };
@@ -129,20 +144,13 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
                 if ((e.target as SVGSVGElement).classList.contains("pannable")) {
                     if (this._isControlKeyPress) {
                         if (this._panStart.x !== 0 && this._panStart.y !== 0) {
-                            this._panStop.set(
-                                e.clientX - e.currentTarget.getBoundingClientRect().left,
-                                e.clientY - e.currentTarget.getBoundingClientRect().top
-                            );
+                            this._panStop.set(e.clientX - e.currentTarget.getBoundingClientRect().left, e.clientY - e.currentTarget.getBoundingClientRect().top);
                             this.panDirection();
                         }
                     }
                 }
                 // Handles the playhead dragging
-                if (
-                    e.currentTarget.classList.contains("linear") &&
-                    this._playheadDrag !== 0 &&
-                    this._playheadSelected
-                ) {
+                if (e.currentTarget.classList.contains("linear") && this._playheadDrag !== 0 && this._playheadSelected) {
                     const moving = e.clientX - e.currentTarget.getBoundingClientRect().left;
 
                     const draggableAreaWidth = e.currentTarget.clientWidth;
@@ -182,6 +190,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         }
     };
 
+    /**
+     *  Resets the dragging state
+     * @param e Drag event
+     */
     dragEnd = (e: React.MouseEvent<SVGSVGElement, MouseEvent>): void => {
         e.preventDefault();
         this._active = false;
@@ -195,6 +207,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         this._movedY = 0;
     };
 
+    /**
+     * Gets the current position of the mouse in a SVG Canvas
+     * @param e Mouse event
+     */
     getMousePosition = (e: React.MouseEvent<SVGSVGElement, MouseEvent>): Vector2 | undefined => {
         if (this._draggableArea.current) {
             var svg = this._draggableArea.current as SVGSVGElement;
@@ -210,9 +226,9 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
     };
 
     /**
-    * Handles the canvas panning direction and sets the X and Y values to move the
-    * SVG canvas
-    */
+     * Handles the canvas panning direction and sets the X and Y values to move the
+     * SVG canvas
+     */
     panDirection() {
         let directionX = 1;
         if (this._movedX < this._panStop.x) {
@@ -256,6 +272,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         this.props.panningX(Math.round(newX));
     }
 
+    /**
+     * Allows dragging
+     * @param e Keyboard event
+     */
     keyDown(e: KeyboardEvent) {
         e.preventDefault();
         if (e.keyCode === 17 || e.keyCode === 32) {
@@ -264,6 +284,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         }
     }
 
+    /**
+     * Allows multiple selection
+     * @param e Keyboard event
+     */
     keyUp(e: KeyboardEvent) {
         e.preventDefault();
         if (e.keyCode === 17 || e.keyCode === 32) {
@@ -282,6 +306,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         }
     }
 
+    /**
+     * Focus on the SVG canvas
+     * @param e Mouse event
+     */
     focus = (e: React.MouseEvent<SVGSVGElement>) => {
         e.preventDefault();
         this._draggableArea.current?.focus();
@@ -295,6 +323,9 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         }
     };
 
+    /**
+     * Is the control point active or not
+     */
     isNotControlPointActive() {
         const activeControlPoints = this.props.keyframeSvgPoints.filter((x) => x.isLeftActive || x.isRightActive);
         if (activeControlPoints.length !== 0) {
@@ -305,22 +336,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
     }
 
     render() {
-        const viewBoxScaling = `${this.props.positionCanvas?.x} ${this.props.positionCanvas?.y} ${Math.round(
-            this.props.scale * 200
-        )} ${Math.round(this.props.scale * 100)}`;
+        const viewBoxScaling = `${this.props.positionCanvas?.x} ${this.props.positionCanvas?.y} ${Math.round(this.props.scale * 200)} ${Math.round(this.props.scale * 100)}`;
         return (
             <>
-                <svg
-                    className="linear pannable"
-                    ref={this._draggableArea}
-                    tabIndex={0}
-                    onMouseMove={this.drag}
-                    onMouseDown={this.dragStart}
-                    onMouseUp={this.dragEnd}
-                    onMouseLeave={this.dragEnd}
-                    onClick={this.focus}
-                    viewBox={viewBoxScaling}
-                >
+                <svg className="linear pannable" ref={this._draggableArea} tabIndex={0} onMouseMove={this.drag} onMouseDown={this.dragStart} onMouseUp={this.dragEnd} onMouseLeave={this.dragEnd} onClick={this.focus} viewBox={viewBoxScaling}>
                     {this.props.children}
                 </svg>
             </>

+ 110 - 83
inspector/src/components/actionTabs/tabs/propertyGrids/animations/timeline.tsx

@@ -3,38 +3,56 @@ import { IAnimationKey } from "babylonjs/Animations/animationKey";
 import { Controls } from "./controls";
 
 interface ITimelineProps {
+    // Keyframes list in the animation
     keyframes: IAnimationKey[] | null;
+    // The selected animation keyframe
     selected: IAnimationKey | null;
+    // The current frame of the selected animation keyframe
     currentFrame: number;
+    // Selects the frame of an animation keyframe
     onCurrentFrameChange: (frame: number) => void;
+    // Changes animation length limit (visible on canvas)
     onAnimationLimitChange: (limit: number) => void;
+    // Keyframe to drag by the user
     dragKeyframe: (frame: number, index: number) => void;
+    // Starts/stops the animation (0 stops, -1 plays backward, 1 normal)
     playPause: (direction: number) => void;
+    // If animation is playing
     isPlaying: boolean;
+    // The last visible frame on the canvas. Controls the length of the visible timeline
     animationLimit: number;
+    // Frames per second
     fps: number;
+    // Reposition the canvas and center it to the selected keyframe
     repositionCanvas: (keyframe: IAnimationKey) => void;
+    // Change proportion of the resized window
     resizeWindowProportion: number;
 }
 
+interface ITimelineState {
+    // Selected keyframe
+    selected: IAnimationKey;
+    // Active frame
+    activeKeyframe: number | null;
+    // Start of the timeline scrollbar
+    start: number;
+    // End of the timeline scrollbar
+    end: number;
+    // Current widht of the scrollbar
+    scrollWidth: number | undefined;
+    // The length of the visible frame
+    selectionLength: number[];
+    // Limit of the visible frames
+    limitValue: number;
+}
+
 /**
  * The Timeline for the curve editor
  *
- * Has a scrollbar that can be resized and move to left and right. 
+ * Has a scrollbar that can be resized and move to left and right.
  * The timeline does not affect the Canvas but only the frame container.
  */
-export class Timeline extends React.Component<
-    ITimelineProps,
-    {
-        selected: IAnimationKey;
-        activeKeyframe: number | null;
-        start: number;
-        end: number;
-        scrollWidth: number | undefined;
-        selectionLength: number[];
-        limitValue: number;
-    }
-> {
+export class Timeline extends React.Component<ITimelineProps, ITimelineState> {
     // Div Elements to display the timeline
     private _scrollable: React.RefObject<HTMLDivElement>;
     private _scrollbarHandle: React.RefObject<HTMLDivElement>;
@@ -128,18 +146,17 @@ export class Timeline extends React.Component<
                     scrollWidth: this.calculateScrollWidth(0, newEnd),
                 });
                 if (this._scrollbarHandle.current && this._scrollContainer.current) {
-                    this._scrollbarHandle.current.style.left = `${
-                        this._scrollContainer.current.getBoundingClientRect().left + this._marginScrollbar
-                    }px`;
+                    this._scrollbarHandle.current.style.left = `${this._scrollContainer.current.getBoundingClientRect().left + this._marginScrollbar}px`;
                 }
             }
         );
     }
 
     /**
-    * @param {number} start Frame from which the scrollbar should begin.
-    * @param {number} end Last frame for the timeline.
-    */
+     * Set scrollwidth on the timeline
+     * @param {number} start Frame from which the scrollbar should begin.
+     * @param {number} end Last frame for the timeline.
+     */
     calculateScrollWidth(start: number, end: number) {
         if (this._scrollContainer.current && this.props.animationLimit !== 0) {
             const containerMarginLeftRight = this._marginScrollbar * 2;
@@ -157,20 +174,36 @@ export class Timeline extends React.Component<
         }
     }
 
+    /**
+     * Play animation backwards
+     * @param event Mouse event
+     */
     playBackwards(event: React.MouseEvent<HTMLDivElement>) {
         this.props.playPause(-1);
     }
 
+    /**
+     * Play animation
+     * @param event Mouse event
+     */
     play(event: React.MouseEvent<HTMLDivElement>) {
         this.props.playPause(1);
     }
 
+    /**
+     * Pause the animation
+     * @param event Mouse event
+     */
     pause(event: React.MouseEvent<HTMLDivElement>) {
         if (this.props.isPlaying) {
             this.props.playPause(1);
         }
     }
 
+    /**
+     * Set the selected frame
+     * @param event Mouse event
+     */
     setCurrentFrame = (event: React.MouseEvent<HTMLDivElement>) => {
         event.preventDefault();
         if (this._scrollable.current) {
@@ -184,8 +217,8 @@ export class Timeline extends React.Component<
     };
 
     /**
-    * Handles the change of number of frames available in the timeline.
-    */
+     * Handles the change of number of frames available in the timeline.
+     */
     handleLimitChange(event: React.ChangeEvent<HTMLInputElement>) {
         event.preventDefault();
         let newLimit = parseInt(event.target.value);
@@ -197,12 +230,20 @@ export class Timeline extends React.Component<
         });
     }
 
+    /**
+     * Starts the scrollbar dragging
+     * @param e Mouse event on SVG Element
+     */
     dragStart = (e: React.MouseEvent<SVGSVGElement, MouseEvent>): void => {
         e.preventDefault();
         this.setState({ activeKeyframe: parseInt((e.target as SVGSVGElement).id.replace("kf_", "")) });
         this._direction = e.clientX;
     };
 
+    /**
+     * Update the canvas visible frames while dragging
+     * @param e Mouse event
+     */
     drag = (e: React.MouseEvent<SVGSVGElement, MouseEvent>): void => {
         e.preventDefault();
         if (this.props.keyframes) {
@@ -226,8 +267,8 @@ export class Timeline extends React.Component<
     };
 
     /**
-    * Check if the frame is being used as a Keyframe by the animation
-    */
+     * Check if the frame is being used as a Keyframe by the animation
+     */
     isFrameBeingUsed(frame: number, direction: number) {
         let used = this.props.keyframes?.find((kf) => kf.frame === frame);
         if (used) {
@@ -238,12 +279,20 @@ export class Timeline extends React.Component<
         }
     }
 
+    /**
+     * Reset drag state
+     * @param e Mouse event on SVG Element
+     */
     dragEnd = (e: React.MouseEvent<SVGSVGElement, MouseEvent>): void => {
         e.preventDefault();
         this._direction = 0;
         this.setState({ activeKeyframe: null });
     };
 
+    /**
+     * Change position of the scrollbar
+     * @param e Mouse event
+     */
     scrollDragStart = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
         e.preventDefault();
         this._scrollContainer.current && this._scrollContainer.current.focus();
@@ -267,6 +316,10 @@ export class Timeline extends React.Component<
         }
     };
 
+    /**
+     * Change size of scrollbar
+     * @param e Mouse event
+     */
     scrollDrag = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
         e.preventDefault();
         if ((e.target as HTMLDivElement).className === "scrollbar") {
@@ -282,6 +335,10 @@ export class Timeline extends React.Component<
         }
     };
 
+    /**
+     * Reset scroll drag
+     * @param e Mouse event
+     */
     scrollDragEnd = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
         e.preventDefault();
         this._scrolling = false;
@@ -290,10 +347,10 @@ export class Timeline extends React.Component<
     };
 
     /**
-    * Sets the start, end and selection length of the scrollbar. This will control the width and
-    * height of the scrollbar as well as the number of frames available
-    * @param {number} pageX Controls the X axis of the scrollbar movement.
-    */
+     * Sets the start, end and selection length of the scrollbar. This will control the width and
+     * height of the scrollbar as well as the number of frames available
+     * @param {number} pageX Controls the X axis of the scrollbar movement.
+     */
     moveScrollbar(pageX: number) {
         if (this._scrolling && this._scrollbarHandle.current && this._scrollContainer.current) {
             const moved = pageX - this._shiftX;
@@ -321,8 +378,9 @@ export class Timeline extends React.Component<
     }
 
     /**
-    * Controls the resizing of the scrollbar from the right handle
-    */
+     * Controls the resizing of the scrollbar from the right handle
+     * @param {number} clientX how much mouse has moved
+     */
     resizeScrollbarRight(clientX: number) {
         if (this._scrollContainer.current && this._scrollbarHandle.current) {
             const moving = clientX - this._scrollContainer.current.getBoundingClientRect().left;
@@ -351,8 +409,9 @@ export class Timeline extends React.Component<
     }
 
     /**
-    * Controls the resizing of the scrollbar from the left handle
-    */
+     * Controls the resizing of the scrollbar from the left handle
+     *  @param {number} clientX how much mouse has moved
+     */
     resizeScrollbarLeft(clientX: number) {
         if (this._scrollContainer.current && this._scrollbarHandle.current) {
             const moving = clientX - this._scrollContainer.current.getBoundingClientRect().left;
@@ -370,8 +429,7 @@ export class Timeline extends React.Component<
             }
 
             if (!(framesTo >= this.state.end - 20)) {
-                let toleft =
-                    framesTo * unit + this._scrollContainer.current.getBoundingClientRect().left + this._marginScrollbar * 2;
+                let toleft = framesTo * unit + this._scrollContainer.current.getBoundingClientRect().left + this._marginScrollbar * 2;
                 if (this._scrollbarHandle.current) {
                     this._scrollbarHandle.current.style.left = toleft + "px";
                 }
@@ -385,12 +443,18 @@ export class Timeline extends React.Component<
     }
 
     /**
-    * Returns array with the expected length between two numbers
-    */
+     * Returns array with the expected length between two numbers
+     * @param start initial visible frame
+     * @param stop last visible frame
+     */
     range(start: number, end: number) {
         return Array.from({ length: end - start }, (_, i) => start + i * 1);
     }
 
+    /**
+     * Get the animation keyframe
+     * @param frame Frame
+     */
     getKeyframe(frame: number) {
         if (this.props.keyframes) {
             return this.props.keyframes.find((x) => x.frame === frame);
@@ -399,6 +463,10 @@ export class Timeline extends React.Component<
         }
     }
 
+    /**
+     * Get the current animation keyframe
+     * @param frame Frame
+     */
     getCurrentFrame(frame: number) {
         if (this.props.currentFrame === frame) {
             return true;
@@ -443,32 +511,14 @@ export class Timeline extends React.Component<
                                                 <>
                                                     {frame % Math.round(this.state.selectionLength.length / 20) === 0 ? (
                                                         <>
-                                                            <text
-                                                                x={(i * 100) / this.state.selectionLength.length + "%"}
-                                                                y="18"
-                                                                style={{ fontSize: 10, fill: "#555555" }}
-                                                            >
+                                                            <text x={(i * 100) / this.state.selectionLength.length + "%"} y="18" style={{ fontSize: 10, fill: "#555555" }}>
                                                                 {frame}
                                                             </text>
-                                                            <line
-                                                                x1={(i * 100) / this.state.selectionLength.length + "%"}
-                                                                y1="22"
-                                                                x2={(i * 100) / this.state.selectionLength.length + "%"}
-                                                                y2="40"
-                                                                style={{ stroke: "#555555", strokeWidth: 0.5 }}
-                                                            />
+                                                            <line x1={(i * 100) / this.state.selectionLength.length + "%"} y1="22" x2={(i * 100) / this.state.selectionLength.length + "%"} y2="40" style={{ stroke: "#555555", strokeWidth: 0.5 }} />
                                                         </>
                                                     ) : null}
                                                     {this.getCurrentFrame(frame) ? (
-                                                        <svg
-                                                            x={
-                                                                this._scrollable.current
-                                                                    ? this._scrollable.current.clientWidth /
-                                                                      this.state.selectionLength.length /
-                                                                      2
-                                                                    : 1
-                                                            }
-                                                        >
+                                                        <svg x={this._scrollable.current ? this._scrollable.current.clientWidth / this.state.selectionLength.length / 2 : 1}>
                                                             <line
                                                                 x1={(i * 100) / this.state.selectionLength.length + "%"}
                                                                 y1="0"
@@ -476,10 +526,7 @@ export class Timeline extends React.Component<
                                                                 y2="40"
                                                                 style={{
                                                                     stroke: "rgba(18, 80, 107, 0.26)",
-                                                                    strokeWidth: this._scrollable.current
-                                                                        ? this._scrollable.current.clientWidth /
-                                                                          this.state.selectionLength.length
-                                                                        : 1,
+                                                                    strokeWidth: this._scrollable.current ? this._scrollable.current.clientWidth / this.state.selectionLength.length : 1,
                                                                 }}
                                                             />
                                                         </svg>
@@ -487,14 +534,7 @@ export class Timeline extends React.Component<
 
                                                     {this.getKeyframe(frame) ? (
                                                         <svg key={`kf_${i}`} tabIndex={i + 40}>
-                                                            <line
-                                                                id={`kf_${i.toString()}`}
-                                                                x1={(i * 100) / this.state.selectionLength.length + "%"}
-                                                                y1="0"
-                                                                x2={(i * 100) / this.state.selectionLength.length + "%"}
-                                                                y2="40"
-                                                                style={{ stroke: "#ffc017", strokeWidth: 1 }}
-                                                            />
+                                                            <line id={`kf_${i.toString()}`} x1={(i * 100) / this.state.selectionLength.length + "%"} y1="0" x2={(i * 100) / this.state.selectionLength.length + "%"} y2="40" style={{ stroke: "#ffc017", strokeWidth: 1 }} />
                                                         </svg>
                                                     ) : null}
                                                 </>
@@ -505,14 +545,7 @@ export class Timeline extends React.Component<
                             </svg>
                         </div>
 
-                        <div
-                            className="timeline-scroll-handle"
-                            onMouseMove={this.scrollDrag}
-                            onMouseDown={this.scrollDragStart}
-                            onMouseUp={this.scrollDragEnd}
-                            onMouseLeave={this.scrollDragEnd}
-                            onDragStart={this.dragDomFalse}
-                        >
+                        <div className="timeline-scroll-handle" onMouseMove={this.scrollDrag} onMouseDown={this.scrollDragStart} onMouseUp={this.scrollDragEnd} onMouseLeave={this.scrollDragEnd} onDragStart={this.dragDomFalse}>
                             <div className="scroll-handle" ref={this._scrollContainer} tabIndex={60}>
                                 <div className="handle" ref={this._scrollbarHandle} style={{ width: this.state.scrollWidth }}>
                                     <div className="left-grabber">
@@ -538,13 +571,7 @@ export class Timeline extends React.Component<
                         </div>
 
                         <div className="input-frame">
-                            <input
-                                ref={this._inputAnimationLimit}
-                                type="number"
-                                value={this.state.limitValue}
-                                onChange={(e) => this.handleLimitChange(e)}
-                                onBlur={(e) => this.onInputBlur(e)}
-                            ></input>
+                            <input ref={this._inputAnimationLimit} type="number" value={this.state.limitValue} onChange={(e) => this.handleLimitChange(e)} onBlur={(e) => this.onInputBlur(e)}></input>
                         </div>
                     </div>
                 </div>