github.com/replicatedhq/ship@v0.55.0/web/init/src/components/kustomize/kustomize_overlay/AceEditorHOC.jsx (about)

     1  import React from "react";
     2  import ace from "brace";
     3  import AceEditor from "react-ace";
     4  import * as ast from "yaml-ast-parser";
     5  import find from "lodash/find";
     6  
     7  const { addListener, removeListener } = ace.acequire("ace/lib/event");
     8  
     9  export const PATCH_TOKEN = "TO_BE_MODIFIED";
    10  
    11  export class AceEditorHOC extends React.Component {
    12    constructor(props) {
    13      super(props);
    14      this.state = {
    15        activeMarker: [],
    16        markers: [],
    17      };
    18    }
    19  
    20    componentDidUpdate(prevProps) {
    21      const { fileToView } = this.props;
    22      if (fileToView !== prevProps.fileToView) {
    23        if (fileToView.baseContent && fileToView.isSupported) {
    24          const markers = this.createMarkers(fileToView);
    25          this.setState({ markers });
    26        }
    27      }
    28      if (
    29        (this.props.overlayOpen !== prevProps.overlayOpen) ||
    30        (this.props.diffOpen !== prevProps.diffOpen)
    31      ) {
    32        if (this.aceEditorBase) {
    33          this.aceEditorBase.editor.resize();
    34        }
    35      }
    36    }
    37  
    38    componentDidMount() {
    39      addListener(this.aceEditorBase.editor, "click", this.addToOverlay)
    40      addListener(this.aceEditorBase.editor.renderer.scroller, "mousemove", this.setActiveMarker);
    41      addListener(this.aceEditorBase.editor.renderer.scroller, "mouseout", this.setActiveMarker);
    42    }
    43  
    44    componentWillUnmount() {
    45      removeListener(this.aceEditorBase.editor, "click", this.addToOverlay);
    46      removeListener(this.aceEditorBase.editor.renderer.scroller, "mousemove", this.setActiveMarker);
    47      removeListener(this.aceEditorBase.editor.renderer.scroller, "mouseout", this.setActiveMarker)
    48    }
    49  
    50    findMarkerAtRow = (row, markers) => (
    51      find(markers, ({ startRow, endRow }) => ( row >= startRow && row <= endRow ))
    52    )
    53  
    54    addToOverlay = async () => {
    55      const { handleGeneratePatch, handleApplyPatch } = this.props;
    56      const { activeMarker } = this.state;
    57  
    58      if (activeMarker.length > 0) {
    59        const matchingMarker = activeMarker[0];
    60        const { path } = matchingMarker;
    61        await handleApplyPatch().catch();
    62        handleGeneratePatch(path);
    63      }
    64    }
    65  
    66    setActiveMarker = (e) => {
    67      const { clientY } = e;
    68      const { markers, activeMarker } = this.state;
    69  
    70      const renderer = this.aceEditorBase.editor.renderer;
    71      const canvasPos = renderer.scroller.getBoundingClientRect();
    72  
    73      const row = Math.ceil((clientY + renderer.scrollTop - canvasPos.top) / renderer.lineHeight);
    74      const matchingMarker = this.findMarkerAtRow(row, markers);
    75  
    76      if (matchingMarker) {
    77        renderer.setCursorStyle("pointer");
    78        const [ activeMarker0 = {} ] = activeMarker;
    79        if (matchingMarker.startRow !== activeMarker0.startRow) {
    80          this.setState({ activeMarker: [ matchingMarker ] });
    81        }
    82      } else {
    83        this.setState({ activeMarker: [] });
    84      }
    85    }
    86  
    87    createMarkers = (fileToView) => {
    88      if (this.aceEditorBase) {
    89        let markers = [];
    90        const loadedAst = ast.safeLoad(fileToView.baseContent, null);
    91        this.createMarkersRec(loadedAst, [], markers);
    92        return markers;
    93      }
    94    }
    95  
    96    createMarkersRec = (ast, path, markers) => {
    97      const aceDoc = this.aceEditorBase.editor.getSession().getDocument();
    98  
    99      const createMarkersSlice = ({ items }) => {
   100        if (items && items.length > 0) {
   101          for (let i = 0; i < items.length; i++) {
   102            const item = items[i];
   103            this.createMarkersRec(item, [...path, i], markers);
   104          }
   105        }
   106      };
   107  
   108      const createMarkersMap = ({ mappings }) => {
   109        for (const mapping of mappings) {
   110          const { value, key } = mapping;
   111          if (value === null) {
   112            const { startPosition, endPosition } = ast;
   113            const { row: startRow } = aceDoc.indexToPosition(startPosition, 0);
   114            const { row: endRow } = aceDoc.indexToPosition(endPosition, 0);
   115            const nullMarker = {
   116              startRow,
   117              endRow: endRow + 1,
   118              className: "marker-highlight-null",
   119              mapping,
   120              path: [...path, key.value],
   121            }
   122            return markers.push(nullMarker);
   123          }
   124          const newPathKey = key.value;
   125          this.createMarkersRec(value, [...path, newPathKey], markers);
   126        }
   127      };
   128  
   129      if (ast.mappings) {
   130        return createMarkersMap(ast);
   131      }
   132      if (ast.items) {
   133        return createMarkersSlice(ast);
   134      }
   135  
   136      const { startPosition, endPosition } = ast;
   137      const { row: startRow } = aceDoc.indexToPosition(startPosition, 0);
   138      const { row: endRow } = aceDoc.indexToPosition(endPosition, 0);
   139      const newMarker = {
   140        startRow,
   141        endRow: endRow + 1,
   142        className: "marker-highlight",
   143        mapping: ast,
   144        path,
   145      };
   146      markers.push(newMarker);
   147    }
   148  
   149    render() {
   150      const { fileToView } = this.props;
   151      const { activeMarker } = this.state;
   152  
   153      return (
   154        <AceEditor
   155          ref={(editor) => { this.aceEditorBase = editor }}
   156          mode="yaml"
   157          theme="chrome"
   158          className="flex1 flex"
   159          readOnly={true}
   160          value={fileToView && fileToView.baseContent || ""}
   161          height="100%"
   162          width="100%"
   163          editorProps={{
   164            $blockScrolling: Infinity,
   165            useSoftTabs: true,
   166            tabSize: 2,
   167          }}
   168          setOptions={{
   169            scrollPastEnd: false
   170          }}
   171          markers={activeMarker}
   172        />
   173      );
   174    }
   175  }