graphActionsBar.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import * as React from "react";
  2. import { IconButtonLineComponent } from "../../../lines/iconButtonLineComponent";
  3. import { IActionableKeyFrame } from "./animationCurveEditorComponent";
  4. interface IGraphActionsBarProps {
  5. addKeyframe: () => void;
  6. removeKeyframe: () => void;
  7. frameSelectedKeyframes: () => void;
  8. handleValueChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  9. handleFrameChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  10. flatTangent: () => void;
  11. brokeTangents: () => void;
  12. setLerpToActiveControlPoint: () => void;
  13. brokenMode: boolean;
  14. lerpMode: boolean;
  15. actionableKeyframe: IActionableKeyFrame;
  16. title: string;
  17. enabled: boolean;
  18. setKeyframeValue: (actionableKeyframe: IActionableKeyFrame) => void;
  19. frameRange: { min: number | undefined; max: number | undefined };
  20. }
  21. /**
  22. * Has the buttons and actions for the Canvas Graph.
  23. * Handles input change and actions (flat, broken mode, set linear control points)
  24. */
  25. export class GraphActionsBar extends React.Component<
  26. IGraphActionsBarProps,
  27. { frame: string; value: string; min: number | undefined; max: number | undefined }
  28. > {
  29. private _frameInput: React.RefObject<HTMLInputElement>;
  30. private _valueInput: React.RefObject<HTMLInputElement>;
  31. constructor(props: IGraphActionsBarProps) {
  32. super(props);
  33. this._frameInput = React.createRef();
  34. this._valueInput = React.createRef();
  35. const { frame, value } = this.selectedKeyframeChanged(this.props.actionableKeyframe);
  36. this.state = { frame, value, min: this.props.frameRange.min, max: this.props.frameRange.max };
  37. }
  38. componentDidMount() {
  39. this._frameInput.current?.addEventListener("keyup", this.isEnterKeyUp.bind(this));
  40. this._valueInput.current?.addEventListener("keyup", this.isEnterKeyUp.bind(this));
  41. }
  42. componentDidUpdate(prevProps: IGraphActionsBarProps, prevState: any) {
  43. if (prevProps.actionableKeyframe !== this.props.actionableKeyframe) {
  44. const { frame, value } = this.selectedKeyframeChanged(this.props.actionableKeyframe);
  45. this.setState({ frame, value });
  46. }
  47. if (
  48. prevProps.frameRange.min !== this.props.frameRange.min ||
  49. prevProps.frameRange.max !== this.props.frameRange.max
  50. ) {
  51. this.setState({ min: this.props.frameRange.min, max: this.props.frameRange.max });
  52. }
  53. }
  54. selectedKeyframeChanged(keyframe: IActionableKeyFrame) {
  55. let frame = "";
  56. if (typeof keyframe.frame === "number") {
  57. frame = keyframe.frame.toString();
  58. }
  59. let value = "";
  60. if (typeof keyframe.value === "number") {
  61. value = keyframe.value.toFixed(3);
  62. }
  63. return { frame, value };
  64. }
  65. componentWillUnmount() {
  66. this._frameInput.current?.removeEventListener("keyup", this.isEnterKeyUp.bind(this));
  67. this._valueInput.current?.removeEventListener("keyup", this.isEnterKeyUp.bind(this));
  68. }
  69. isEnterKeyUp(event: KeyboardEvent) {
  70. event.preventDefault();
  71. if (event.key === "Enter") {
  72. const actionableKeyframe: IActionableKeyFrame = { frame: this.getFrame(), value: this.getValue() };
  73. this.props.setKeyframeValue(actionableKeyframe);
  74. }
  75. }
  76. onBlur = (event: React.FocusEvent<HTMLInputElement>) => {
  77. event.preventDefault();
  78. if (event.target.value !== "") {
  79. const actionableKeyframe: IActionableKeyFrame = { frame: this.getFrame(), value: this.getValue() };
  80. this.props.setKeyframeValue(actionableKeyframe);
  81. }
  82. };
  83. getFrame() {
  84. let frame;
  85. if (this.state.frame === "") {
  86. frame = "";
  87. } else {
  88. frame = parseInt(this.state.frame);
  89. }
  90. return frame;
  91. }
  92. getValue() {
  93. let value;
  94. if (this.state.value !== "") {
  95. value = parseFloat(this.state.value);
  96. } else {
  97. value = "";
  98. }
  99. return value;
  100. }
  101. handleValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  102. e.preventDefault();
  103. this.setState({ value: e.target.value });
  104. };
  105. handleFrameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  106. e.preventDefault();
  107. this.setState({ frame: e.target.value });
  108. };
  109. render() {
  110. return (
  111. <div className="actions-wrapper">
  112. <div className="title-container">
  113. <div className="icon babylon-logo"></div>
  114. <div className="title">{this.props.title}</div>
  115. </div>
  116. <div className="buttons-container" style={{ pointerEvents: this.props.enabled ? "all" : "none" }}>
  117. <div className="action-input frame-input">
  118. <input
  119. ref={this._frameInput}
  120. type="number"
  121. onChange={this.handleFrameChange}
  122. value={this.state.frame}
  123. max={this.state.max}
  124. min={this.state.min}
  125. step="1"
  126. disabled={this.props.actionableKeyframe.frame === undefined}
  127. onBlur={this.onBlur}
  128. />
  129. </div>
  130. <div className="action-input">
  131. <input
  132. ref={this._valueInput}
  133. type="number"
  134. value={this.state.value}
  135. onChange={this.handleValueChange}
  136. step="0.01"
  137. disabled={this.props.actionableKeyframe.value === undefined}
  138. onBlur={this.onBlur}
  139. />
  140. </div>
  141. <IconButtonLineComponent tooltip={"Add Keyframe"} icon="new-key" onClick={this.props.addKeyframe} />
  142. <IconButtonLineComponent
  143. tooltip={"Frame selected keyframes"}
  144. icon="frame"
  145. onClick={this.props.frameSelectedKeyframes}
  146. />
  147. <IconButtonLineComponent
  148. tooltip={this.props.brokenMode ? "Flat selected control point" : "Flat control points"}
  149. icon="flat-tangent"
  150. onClick={this.props.flatTangent}
  151. />
  152. <IconButtonLineComponent
  153. tooltip={this.props.brokenMode ? "Broken Mode On" : "Broken Mode Off"}
  154. icon={this.props.brokenMode ? "break-tangent" : "unify-tangent"}
  155. onClick={this.props.brokeTangents}
  156. />
  157. <IconButtonLineComponent
  158. tooltip={"Linear"}
  159. icon="linear-tangent"
  160. onClick={this.props.setLerpToActiveControlPoint}
  161. />
  162. </div>
  163. </div>
  164. );
  165. }
  166. }