treeItemComponent.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import * as React from "react";
  2. import { Nullable } from "babylonjs/types";
  3. import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
  4. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
  5. import { faPlus, faMinus, faBan, faExpandArrowsAlt, faCompress } from '@fortawesome/free-solid-svg-icons';
  6. import { TreeItemSelectableComponent } from "./treeItemSelectableComponent";
  7. import { Tools } from "../../tools";
  8. import { GlobalState } from "../globalState";
  9. import { ContextMenu, MenuItem, ContextMenuTrigger } from "react-contextmenu";
  10. interface ITreeItemExpandableHeaderComponentProps {
  11. isExpanded: boolean,
  12. label: string,
  13. onClick: () => void,
  14. onExpandAll: (expand: boolean) => void
  15. }
  16. class TreeItemExpandableHeaderComponent extends React.Component<ITreeItemExpandableHeaderComponentProps> {
  17. constructor(props: ITreeItemExpandableHeaderComponentProps) {
  18. super(props);
  19. }
  20. expandAll() {
  21. this.props.onExpandAll(!this.props.isExpanded);
  22. }
  23. render() {
  24. const chevron = this.props.isExpanded ? <FontAwesomeIcon icon={faMinus} /> : <FontAwesomeIcon icon={faPlus} />
  25. const expandAll = this.props.isExpanded ? <FontAwesomeIcon icon={faCompress} /> : <FontAwesomeIcon icon={faExpandArrowsAlt} />
  26. return (
  27. <div className="expandableHeader">
  28. <div className="text">
  29. <div className="arrow icon" onClick={() => this.props.onClick()}>
  30. {chevron}
  31. </div>
  32. <div className="text-value">
  33. {this.props.label}
  34. </div>
  35. </div>
  36. <div className="expandAll icon" onClick={() => this.expandAll()} title={this.props.isExpanded ? "Collapse all" : "Expand all"}>
  37. {expandAll}
  38. </div>
  39. </div>
  40. )
  41. }
  42. }
  43. interface ITreeItemRootHeaderComponentProps {
  44. label: string
  45. }
  46. class TreeItemRootHeaderComponent extends React.Component<ITreeItemRootHeaderComponentProps> {
  47. constructor(props: ITreeItemRootHeaderComponentProps) {
  48. super(props);
  49. }
  50. render() {
  51. return (
  52. <div className="expandableHeader">
  53. <div className="text">
  54. <div className="arrow icon">
  55. <FontAwesomeIcon icon={faBan} />
  56. </div>
  57. <div className="text-value">
  58. {this.props.label}
  59. </div>
  60. </div>
  61. </div>
  62. )
  63. }
  64. }
  65. export interface ITreeItemComponentProps {
  66. items?: Nullable<any[]>,
  67. label: string,
  68. offset: number,
  69. filter: Nullable<string>,
  70. forceSubitems?: boolean,
  71. globalState: GlobalState,
  72. entity?: any,
  73. selectedEntity: any,
  74. extensibilityGroups?: IExplorerExtensibilityGroup[],
  75. contextMenuItems?: { label: string, action: () => void }[]
  76. }
  77. export class TreeItemComponent extends React.Component<ITreeItemComponentProps, { isExpanded: boolean, mustExpand: boolean }> {
  78. static _ContextMenuUniqueIdGenerator = 0;
  79. constructor(props: ITreeItemComponentProps) {
  80. super(props);
  81. this.state = { isExpanded: false, mustExpand: false };
  82. }
  83. switchExpandedState(): void {
  84. this.setState({ isExpanded: !this.state.isExpanded, mustExpand: false });
  85. }
  86. shouldComponentUpdate(nextProps: ITreeItemComponentProps, nextState: { isExpanded: boolean }) {
  87. if (!nextState.isExpanded && this.state.isExpanded) {
  88. return true;
  89. }
  90. const items = nextProps.items;
  91. if (items && items.length) {
  92. if (nextProps.selectedEntity) {
  93. for (var item of items) {
  94. if (Tools.LookForItem(item, nextProps.selectedEntity)) {
  95. nextState.isExpanded = true;
  96. return true;
  97. }
  98. }
  99. }
  100. }
  101. return true;
  102. }
  103. expandAll(expand: boolean) {
  104. this.setState({ isExpanded: expand, mustExpand: expand });
  105. }
  106. renderContextMenu() {
  107. if (!this.props.contextMenuItems) {
  108. TreeItemComponent._ContextMenuUniqueIdGenerator++;
  109. return null;
  110. }
  111. return (
  112. <ContextMenu id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator++} className="context-menu">
  113. {
  114. this.props.contextMenuItems.map(c => {
  115. return (
  116. <MenuItem onClick={() => c.action()} key={c.label}>
  117. {c.label}
  118. </MenuItem>
  119. )
  120. })
  121. }
  122. </ContextMenu>
  123. )
  124. }
  125. render() {
  126. let items = this.props.items;
  127. const marginStyle = {
  128. paddingLeft: (10 * (this.props.offset + 0.5)) + "px"
  129. }
  130. if (!items) {
  131. if (this.props.forceSubitems) {
  132. items = [];
  133. } else {
  134. return (
  135. <div className="groupContainer" style={marginStyle}>
  136. <div>
  137. {this.props.label}
  138. </div>
  139. </div>
  140. )
  141. }
  142. }
  143. if (!items.length) {
  144. return (
  145. <div className="groupContainer" style={marginStyle}>
  146. <ContextMenuTrigger id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator}>
  147. {
  148. this.renderContextMenu()
  149. }
  150. <TreeItemRootHeaderComponent label={this.props.label} />
  151. </ContextMenuTrigger>
  152. </div>
  153. )
  154. }
  155. if (!this.state.isExpanded) {
  156. return (
  157. <div className="groupContainer" style={marginStyle}>
  158. <ContextMenuTrigger id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator}>
  159. {
  160. this.renderContextMenu()
  161. }
  162. <TreeItemExpandableHeaderComponent isExpanded={false} label={this.props.label} onClick={() => this.switchExpandedState()} onExpandAll={expand => this.expandAll(expand)} />
  163. </ContextMenuTrigger>
  164. </div >
  165. )
  166. }
  167. const sortedItems = Tools.SortAndFilter(null, items);
  168. return (
  169. <div>
  170. <div className="groupContainer" style={marginStyle}>
  171. <ContextMenuTrigger id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator}>
  172. {
  173. this.renderContextMenu()
  174. }
  175. <TreeItemExpandableHeaderComponent isExpanded={this.state.isExpanded} label={this.props.label} onClick={() => this.switchExpandedState()} onExpandAll={expand => this.expandAll(expand)} />
  176. </ContextMenuTrigger>
  177. </div>
  178. {
  179. sortedItems.map(item => {
  180. return (
  181. <TreeItemSelectableComponent mustExpand={this.state.mustExpand} extensibilityGroups={this.props.extensibilityGroups}
  182. key={item.uniqueId !== undefined && item.uniqueId !== null ? item.uniqueId : item.name}
  183. offset={this.props.offset + 1} selectedEntity={this.props.selectedEntity} entity={item}
  184. globalState={this.props.globalState} filter={this.props.filter} />
  185. );
  186. })
  187. }
  188. </div>
  189. );
  190. }
  191. }