Decouple search box and implement it in the app layer

Same as the topic.
I want to add a search box in my project. I have already watched this sample Org Chart Static (gojs.net) and implement it in the diagramWrapper layer (my project layer is based on this sample NorthwoodsSoftware/gojs-react-basic: An example project demonstrating usage of GoJS and React together (github.com) like below

private searchDiagram() {  // called by button
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram()!;
    const input = document.getElementById("mySearch") as HTMLInputElement;
    if (!input) return;
    diagram.focus();

    diagram.startTransaction("highlight search");

    if (input.value) {
      // search four different data properties for the string, any of which may match for success
      // create a case insensitive RegExp from what the user typed
      const safe = input.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
      const regex = new RegExp(safe, "i");
      const results = diagram.findNodesByExample({ name: regex },
        { key: regex },);
      diagram.selectCollection(results);
      //diagram.highlightCollection(results);
      // try to center the diagram at the first node that was found
      if (results.count > 0) diagram.centerRect(results.first()!.actualBounds);
    } else {  // empty string only clears highlighteds collection
      //diagram.clearHighlighteds();
      diagram.clearSelection();
    }

    diagram.commitTransaction("highlight search");
  }
<input type="search" id="mySearch" />
<button onClick={this.searchDiagram.bind(this)}>Search</button>

However, I have no idea how to decouple this function and implement it in app layer since I don’t know how to get and change the diagram in app layer.

I know it may not be a GoJS question, so before you reply, I would like to express my sincere thanks。

I’d suggest keeping the search logic coupled with the diagram since the search box is explicitly used to search the diagram.

If you really want the search at the app level, you can create the ref in the app and pass it to the wrapper as a prop.

App.tsx

class App extends React.Component<{}, AppState> {
  ...

  private diagramRef: React.RefObject<ReactDiagram>;

  constructor(props: object) {
    super(props);
    this.diagramRef = React.createRef();
    ...
  }

  ...

  public render() {
    ...
    <DiagramWrapper
      diagramRef={this.diagramRef}
      nodeDataArray={this.state.nodeDataArray}
      linkDataArray={this.state.linkDataArray}
      modelData={this.state.modelData}
      skipsDiagramUpdate={this.state.skipsDiagramUpdate}
      onDiagramEvent={this.handleDiagramEvent}
      onModelChange={this.handleModelChange}
    />
    ...
  }
}

DiagramWrapper.tsx

interface DiagramProps {
  diagramRef: React.RefObject<ReactDiagram>;
  nodeDataArray: Array<go.ObjectData>;
  linkDataArray: Array<go.ObjectData>;
  modelData: go.ObjectData;
  skipsDiagramUpdate: boolean;
  onDiagramEvent: (e: go.DiagramEvent) => void;
  onModelChange: (e: go.IncrementalData) => void;
}

export class DiagramWrapper extends React.Component<DiagramProps, {}> {
  /**
   * Get the diagram reference and add any desired diagram listeners.
   * Typically the same function will be used for each listener, with the function using a switch statement to handle the events.
   */
  public componentDidMount() {
    if (!this.props.diagramRef.current) return;
    const diagram = this.props.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      diagram.addDiagramListener('ChangedSelection', this.props.onDiagramEvent);
    }
  }

  ...

  public render() {
    return (
      <ReactDiagram
        ref={this.props.diagramRef}
        divClassName='diagram-component'
        style={{ backgroundColor: '#eee' }}
        initDiagram={this.initDiagram}
        nodeDataArray={this.props.nodeDataArray}
        linkDataArray={this.props.linkDataArray}
        modelData={this.props.modelData}
        onModelChange={this.props.onModelChange}
        skipsDiagramUpdate={this.props.skipsDiagramUpdate}
      />
    );
  }
}