<commit_before>// @flow
/* eslint-env browser */
import * as React from 'react';
import ReactDOM from 'react-dom';
import { View } from 'react-native';
import withTheme, { type Theme } from './core/withTheme';
import { findDOMNode } from 'slate-react';
import EditorMenuButtons from './EditorMenuButtons';
import EditorMenuLink from './EditorMenuLink';
import EditorMenuLinkPreview from './EditorMenuLinkPreview';

export type EditorMenuAction = 
  | {| type: 'BOLD' |}
  | {| type: 'ITALIC' |}
  | {| type: 'HEADING-ONE' |}
  | {| type: 'HEADING-TWO' |}
  | {| type: 'BLOCKQUOTE' |}
  // It's must to use passed change, if any. Slate requires it in onKeyDown.
  | {| type: 'LINK', href: ?string, change?: Object |}
  | {| type: 'FOCUS' |};

export type EditorMenuView = null | 'buttons' | 'link' | 'linkPreview';

type EditorMenuProps = {|
  value: Object,
  onAction: (action: EditorMenuAction) => void,
  theme: Theme,
|};

type EditorMenuState = {|
  left: number,
  top: number,
  view: EditorMenuView,
  hasLinks: boolean,
  linkNode: ?Object,
|};

class EditorMenu extends React.PureComponent<EditorMenuProps, EditorMenuState> {
  state = {
    left: 0,
    top: 0,
    view: null,
    hasLinks: false,
    linkNode: null,
  };

  componentDidMount() {
    this.el = window.document.createElement('div');
    this.modalRoot = window.document.getElementById('__next');
    if (this.modalRoot && this.el) this.modalRoot.appendChild(this.el);
    this.maybeUpdateLeftTopState();
  }

  componentDidUpdate(prevProps) {
    // https://reactjs.org/docs/react-component.html#componentdidmount
    // setState in componentDidUpdate is valid for tooltips, but we have to wrap
    // it in a condition.
    // Without PureComponent, it would cause a loop. With PureComponent, it
    // would cause double measuring of selection rect.
    // We can't use ref with setNativeProps, because:
    // 1) if this.el is set in componentDidMount, we can't set position because
    //    ref is not available yet.
    // 2) if this.el is set in constructor, server and client HTML would be
    //    different.
    // Therefore, this is the only right approach.
    if (this.props.value !== prevProps.value) {
      this.maybeUpdateLeftTopState();
    }
  }

  componentWillUnmount() {
    if (this.modalRoot && this.el) this.modalRoot.removeChild(this.el);
  }

  static getDerivedStateFromProps(
    props: EditorMenuProps,
    state: EditorMenuState,
  ): $Shape<EditorMenuState> {
    const ignoreViewWithFocus = state.view === 'link';
    if (ignoreViewWithFocus) return null;
    const { value } = props;
    const hasLinks = value.inlines.some(inline => inline.type === 'link');
    const linkNode = value.inlines.find(inline => inline.type === 'link');
    const newState = { hasLinks, linkNode };
    if (!value.isBlurred) {
      if (value.selection.isCollapsed) {
        const view = hasLinks ? 'linkPreview' : null;
        return { ...newState, view };
      }
      if (!value.isEmpty) {
        return { ...newState, view: 'buttons' };
      }
    }
    return { ...newState, view: null };
  }

  handleActionsSelectView = view => {
    this.setState({ view });
  };

  handleEditorMenuLinkClose = focusEditor => {
    this.setState({ view: 'buttons' }, () => {
      if (focusEditor === true) this.props.onAction({ type: 'FOCUS' });
    });
  };

  handleEditorMenuLinkSubmit = href => {
    this.props.onAction({ type: 'LINK', href });
  };

  handleKeyModK(change: Object) {
    const { value } = this.props;
    if (value.selection.isCollapsed) {
      const { linkNode } = this.state;
      if (!linkNode) return;
      const href = linkNode.data.get('href');
      // TODO: Use router push for web links.
      window.open(href);
      return;
    }
    if (value.isEmpty) return;
    if (this.state.hasLinks) {
      this.props.onAction({ type: 'LINK', href: null, change });
    } else {
      this.setState({ view: 'link' });
    }
  }

  modalRoot: ?HTMLDivElement;
  el: ?HTMLDivElement;

  maybeUpdateLeftTopState() {
    switch (this.state.view) {
      case 'buttons': {
        const rect = window
          .getSelection()
          .getRangeAt(0)
          .getBoundingClientRect();
        this.setState({
          left: rect.left,
          top: window.pageYOffset + rect.bottom,
        });
        break;
      }
      case 'link': {
        break;<commit_msg>Update Slate deprecated API<commit_after>// @flow
/* eslint-env browser */
import * as React from 'react';
import ReactDOM from 'react-dom';
import { View } from 'react-native';
import withTheme, { type Theme } from './core/withTheme';
import { findDOMNode } from 'slate-react';
import EditorMenuButtons from './EditorMenuButtons';
import EditorMenuLink from './EditorMenuLink';
import EditorMenuLinkPreview from './EditorMenuLinkPreview';

export type EditorMenuAction =
  | {| type: 'BOLD' |}
  | {| type: 'ITALIC' |}
  | {| type: 'HEADING-ONE' |}
  | {| type: 'HEADING-TWO' |}
  | {| type: 'BLOCKQUOTE' |}
  // It's must to use passed change, if any. Slate requires it in onKeyDown.
  | {| type: 'LINK', href: ?string, change?: Object |}
  | {| type: 'FOCUS' |};

export type EditorMenuView = null | 'buttons' | 'link' | 'linkPreview';

type EditorMenuProps = {|
  value: Object,
  onAction: (action: EditorMenuAction) => void,
  theme: Theme,
|};

type EditorMenuState = {|
  left: number,
  top: number,
  view: EditorMenuView,
  hasLinks: boolean,
  linkNode: ?Object,
|};

class EditorMenu extends React.PureComponent<EditorMenuProps, EditorMenuState> {
  state = {
    left: 0,
    top: 0,
    view: null,
    hasLinks: false,
    linkNode: null,
  };

  componentDidMount() {
    this.el = window.document.createElement('div');
    this.modalRoot = window.document.getElementById('__next');
    if (this.modalRoot && this.el) this.modalRoot.appendChild(this.el);
    this.maybeUpdateLeftTopState();
  }

  componentDidUpdate(prevProps) {
    // https://reactjs.org/docs/react-component.html#componentdidmount
    // setState in componentDidUpdate is valid for tooltips, but we have to wrap
    // it in a condition.
    // Without PureComponent, it would cause a loop. With PureComponent, it
    // would cause double measuring of selection rect.
    // We can't use ref with setNativeProps, because:
    // 1) if this.el is set in componentDidMount, we can't set position because
    //    ref is not available yet.
    // 2) if this.el is set in constructor, server and client HTML would be
    //    different.
    // Therefore, this is the only right approach.
    if (this.props.value !== prevProps.value) {
      this.maybeUpdateLeftTopState();
    }
  }

  componentWillUnmount() {
    if (this.modalRoot && this.el) this.modalRoot.removeChild(this.el);
  }

  static getDerivedStateFromProps(
    props: EditorMenuProps,
    state: EditorMenuState,
  ): $Shape<EditorMenuState> {
    const ignoreViewWithFocus = state.view === 'link';
    if (ignoreViewWithFocus) return null;
    const { value } = props;
    const { fragment, selection } = value;
    const hasLinks = value.inlines.some(inline => inline.type === 'link');
    const linkNode = value.inlines.find(inline => inline.type === 'link');
    const newState = { hasLinks, linkNode };
    if (selection.isFocused) {
      if (selection.isCollapsed) {
        const view = hasLinks ? 'linkPreview' : null;
        return { ...newState, view };
      }
      if (fragment.text !== '') {
        return { ...newState, view: 'buttons' };
      }
    }
    return { ...newState, view: null };
  }

  handleActionsSelectView = view => {
    this.setState({ view });
  };

  handleEditorMenuLinkClose = focusEditor => {
    this.setState({ view: 'buttons' }, () => {
      if (focusEditor === true) this.props.onAction({ type: 'FOCUS' });
    });
  };

  handleEditorMenuLinkSubmit = href => {
    this.props.onAction({ type: 'LINK', href });
  };

  handleKeyModK(change: Object) {
    const { value } = this.props;
    const { fragment, selection } = value;
    if (selection.isCollapsed) {
      const { linkNode } = this.state;
      if (!linkNode) return;
      const href = linkNode.data.get('href');
      // TODO: Use router push for web links.
      window.open(href);
      return;
    }
    if (fragment.text === '') return;
    if (this.state.hasLinks) {
      this.props.onAction({ type: 'LINK', href: null, change });
    } else {
      this.setState({ view: 'link' });
    }
  }

  modalRoot: ?HTMLDivElement;
  el: ?HTMLDivElement;

  maybeUpdateLeftTopState() {
    switch (this.state.view) {
      case 'buttons': {
        const rect = window
          .getSelection()
          .getRangeAt(0)
          .getBoundingClientRect();
        this.setState({
          left: rect.left,
          top: window.pageYOffset + rect.bottom,
        });
        break;
      }
      case 'link': {
        break;
{'fraction_non_alphanumeric': 0.1404260114955483, 'fraction_numerical': 0.0011270145384875464, 'mean_word_length': 2.8565840938722293, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 21, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2231590', 'n_tokens_mistral': 2784, 'n_tokens_neox': 2747, 'n_words': 724}