treeItemComponent.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. globalState: GlobalState,
  71. entity?: any,
  72. selectedEntity: any,
  73. extensibilityGroups?: IExplorerExtensibilityGroup[],
  74. contextMenuItems?: { label: string, action: () => void }[]
  75. }
  76. export class TreeItemComponent extends React.Component<ITreeItemComponentProps, { isExpanded: boolean, mustExpand: boolean }> {
  77. static _ContextMenuUniqueIdGenerator = 0;
  78. constructor(props: ITreeItemComponentProps) {
  79. super(props);
  80. this.state = { isExpanded: false, mustExpand: false };
  81. }
  82. switchExpandedState(): void {
  83. this.setState({ isExpanded: !this.state.isExpanded, mustExpand: false });
  84. }
  85. shouldComponentUpdate(nextProps: ITreeItemComponentProps, nextState: { isExpanded: boolean }) {
  86. if (!nextState.isExpanded && this.state.isExpanded) {
  87. return true;
  88. }
  89. const items = nextProps.items;
  90. if (items && items.length) {
  91. if (nextProps.selectedEntity) {
  92. for (var item of items) {
  93. if (Tools.LookForItem(item, nextProps.selectedEntity)) {
  94. nextState.isExpanded = true;
  95. return true;
  96. }
  97. }
  98. }
  99. }
  100. return true;
  101. }
  102. expandAll(expand: boolean) {
  103. this.setState({ isExpanded: expand, mustExpand: expand });
  104. }
  105. renderContextMenu() {
  106. if (!this.props.contextMenuItems) {
  107. TreeItemComponent._ContextMenuUniqueIdGenerator++;
  108. return null;
  109. }
  110. return (
  111. <ContextMenu id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator++} className="context-menu">
  112. {
  113. this.props.contextMenuItems.map(c => {
  114. return (
  115. <MenuItem onClick={() => c.action()} key={c.label}>
  116. {c.label}
  117. </MenuItem>
  118. )
  119. })
  120. }
  121. </ContextMenu>
  122. )
  123. }
  124. render() {
  125. const items = this.props.items;
  126. const marginStyle = {
  127. paddingLeft: (10 * (this.props.offset + 0.5)) + "px"
  128. }
  129. if (!items) {
  130. return (
  131. <div className="groupContainer" style={marginStyle}>
  132. <div>
  133. {this.props.label}
  134. </div>
  135. </div>
  136. )
  137. }
  138. if (!items.length) {
  139. return (
  140. <div className="groupContainer" style={marginStyle}>
  141. <ContextMenuTrigger id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator}>
  142. {
  143. this.renderContextMenu()
  144. }
  145. <TreeItemRootHeaderComponent label={this.props.label} />
  146. </ContextMenuTrigger>
  147. </div>
  148. )
  149. }
  150. if (!this.state.isExpanded) {
  151. return (
  152. <div className="groupContainer" style={marginStyle}>
  153. <ContextMenuTrigger id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator}>
  154. {
  155. this.renderContextMenu()
  156. }
  157. <TreeItemExpandableHeaderComponent isExpanded={false} label={this.props.label} onClick={() => this.switchExpandedState()} onExpandAll={expand => this.expandAll(expand)} />
  158. </ContextMenuTrigger>
  159. </div >
  160. )
  161. }
  162. const sortedItems = Tools.SortAndFilter(null, items);
  163. return (
  164. <div>
  165. <div className="groupContainer" style={marginStyle}>
  166. <ContextMenuTrigger id={"contextmenu#" + TreeItemComponent._ContextMenuUniqueIdGenerator}>
  167. {
  168. this.renderContextMenu()
  169. }
  170. <TreeItemExpandableHeaderComponent isExpanded={this.state.isExpanded} label={this.props.label} onClick={() => this.switchExpandedState()} onExpandAll={expand => this.expandAll(expand)} />
  171. </ContextMenuTrigger>
  172. </div>
  173. {
  174. sortedItems.map(item => {
  175. return (
  176. <TreeItemSelectableComponent mustExpand={this.state.mustExpand} extensibilityGroups={this.props.extensibilityGroups}
  177. key={item.uniqueId !== undefined && item.uniqueId !== null ? item.uniqueId : item.name}
  178. offset={this.props.offset + 1} selectedEntity={this.props.selectedEntity} entity={item}
  179. globalState={this.props.globalState} filter={this.props.filter} />
  180. );
  181. })
  182. }
  183. </div>
  184. );
  185. }
  186. }