Repository: excalidraw/excalidraw
File: src/components/App.tsx (TypeScript)

Code before:
    const elements = this.scene.getElements();

    const options: ContextMenuOption[] = [];
    if (probablySupportsClipboardBlob && elements.length > 0) {
      options.push(actionCopyAsPng);
    }

    if (probablySupportsClipboardWriteText && elements.length > 0) {
      options.push(actionCopyAsSvg);
    }

    const selectedElements = getSelectedElements(elements, this.state);

    let allowBinding = false;
    if (selectedElements.length === 2) {
      const textElement =
        isTextElement(selectedElements[0]) ||
        isTextElement(selectedElements[1]);

      const bindingContainer =
        isTextBindableContainer(selectedElements[0]) ||
        isTextBindableContainer(selectedElements[1]);
      if (textElement && bindingContainer) {
        allowBinding = true;
      }
    }

    if (type === "canvas") {
      const viewModeOptions = [
        ...options,
        typeof this.props.gridModeEnabled === "undefined" &&
          actionToggleGridMode,
        typeof this.props.zenModeEnabled === "undefined" && actionToggleZenMode,
        typeof this.props.viewModeEnabled === "undefined" &&
          actionToggleViewMode,
        actionToggleStats,
      ];

      if (this.state.viewModeEnabled) {
        ContextMenu.push({
          options: viewModeOptions,
          top,
          left,
          actionManager: this.actionManager,
          appState: this.state,
          container: this.excalidrawContainerRef.current!,
          elements,
        });
      } else {
        ContextMenu.push({
          options: [

Review comment (suggestion):
1) let's put this into `action.contextMenuPredicate` instead
2) as discussed on chat, if we're not gonna allow replacing for now, then we should not show the item if the container already has a bound text


Code after:
      this.actionManager.getAppState(),
    );

    const mayBeAllowBinding = actionBindText.contextItemPredicate(
      this.actionManager.getElementsIncludingDeleted(),
      this.actionManager.getAppState(),
    );

    const separator = "separator";

    const elements = this.scene.getElements();

    const options: ContextMenuOption[] = [];
    if (probablySupportsClipboardBlob && elements.length > 0) {
      options.push(actionCopyAsPng);
    }

    if (probablySupportsClipboardWriteText && elements.length > 0) {
      options.push(actionCopyAsSvg);
    }
    if (type === "canvas") {
      const viewModeOptions = [
        ...options,
        typeof this.props.gridModeEnabled === "undefined" &&
          actionToggleGridMode,
        typeof this.props.zenModeEnabled === "undefined" && actionToggleZenMode,
        typeof this.props.viewModeEnabled === "undefined" &&
          actionToggleViewMode,
        actionToggleStats,
      ];

      if (this.state.viewModeEnabled) {
        ContextMenu.push({
          options: viewModeOptions,
          top,
          left,
          actionManager: this.actionManager,
          appState: this.state,
          container: this.excalidrawContainerRef.current!,
          elements,
        });
      } else {
        ContextMenu.push({
          options: [
            this.deviceType.isMobile &&
              navigator.clipboard && {
                name: "paste",
                perform: (elements, appStates) => {
                  this.pasteFromClipboard(null);
                  return {
                    commitToHistory: false,