function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import * as React from 'react';
import { CompositeDecorator, EditorState, Modifier, SelectionState, ContentState } from 'draft-js';
import noop from 'lodash/noop';
import DraftJSMentionSelectorCore from './DraftJSMentionSelectorCore';
import DraftMentionItem from './DraftMentionItem';
import DraftTimestampItem from './DraftTimestampItem';
import FormInput from '../form/FormInput';
import * as messages from '../input-messages';
import Toggle from '../../toggle/Toggle';
import { UNEDITABLE_TIMESTAMP_TEXT } from './utils';
import { convertSecondsToHMMSS } from '../../../utils/timestamp';
/**
 * Scans a Draft ContentBlock for entity ranges, so they can be annotated
 * @see docs at {@link https://draftjs.org/docs/advanced-topics-decorators.html#compositedecorator}
 * @param {ContentBlock} contentBlock
 * @param {function} callback
 * @param {ContentState} contentState
 */
const mentionStrategy = (contentBlock, callback, contentState) => {
  contentBlock.findEntityRanges(character => {
    const entityKey = character.getEntity();
    const ret = entityKey !== null && contentState.getEntity(entityKey).getType() === 'MENTION';
    return ret;
  }, callback);
};

/**
 * Scans a Draft ContentBlock for timestamp entity ranges
 * @see docs at {@link https://draftjs.org/docs/advanced-topics-decorators.html#compositedecorator}
 * @param {ContentBlock} contentBlock
 * @param {function} callback
 * @param {ContentState} contentState
 */
const timestampStrategy = (contentBlock, callback, contentState) => {
  if (!contentBlock || !contentState) {
    return;
  }
  contentBlock.findEntityRanges(character => {
    const entityKey = character.getEntity();
    const hasEntityKey = entityKey !== null;
    // $FlowFixMe
    const entityType = hasEntityKey && contentState?.getEntity(entityKey)?.getType();
    const timeStampEntityFound = entityType === UNEDITABLE_TIMESTAMP_TEXT;
    return timeStampEntityFound;
  }, callback);
};
class DraftJSMentionSelector extends React.Component {
  constructor(props) {
    super(props);
    _defineProperty(this, "getIsVideoTimestampEnabled", () => {
      const {
        timestampLabel
      } = this.props;
      return !!timestampLabel && timestampLabel.trim() !== '';
    });
    _defineProperty(this, "toggleTimestamp", (editorState, forceOn = false) => {
      if (!editorState) return;
      const currentContent = editorState.getCurrentContent();
      let updatedContent;
      let newIsTimestampToggledOn;
      const {
        isTimestampToggledOn
      } = this.state;

      // If timestamp is already prepended and forceOn is true, do not toggle it.
      if (isTimestampToggledOn && forceOn) {
        return;
      }
      const timestampLengthIncludingSpace = this.getTimestampLength(currentContent);
      const isTimestampEntityPresent = timestampLengthIncludingSpace > 0;

      // check if we need to toggle the timestamp on and that the timestamp entity is not already present in the content
      if ((!isTimestampToggledOn || forceOn) && !isTimestampEntityPresent) {
        // get the current timestamp
        const {
          timestamp,
          timestampInMilliseconds
        } = this.getVideoTimestamp();
        const {
          fileVersionId
        } = this.props;
        const timestampText = `${timestamp}`;
        // Create a new entity for the timestamp. It is immutable so it will not be editable. Adding
        // timestampInMilliseconds, and fileVersionId to the entity data which will be used when the comment form is submitted
        // and will be added to the text of the comment. This will let us filter out timetsamped comments based on version and also
        // be able to click the timestamp button in comments in the sidebar and got to the proper place in the video.
        // $FlowFixMe
        const contentWithTimestampEntity = currentContent.createEntity(UNEDITABLE_TIMESTAMP_TEXT,
        // Entity type
        'IMMUTABLE', {
          timestampInMilliseconds,
          fileVersionId
        });

        // Create a selection at the very beginning of the input box for the timestamp
        const selectionAtStart = SelectionState.createEmpty(contentWithTimestampEntity.getFirstBlock().getKey()).merge({
          anchorOffset: 0,
          focusOffset: 0
        });

        // First insert the timestamp text followed by a space
        updatedContent = Modifier.insertText(contentWithTimestampEntity, selectionAtStart, `${timestampText} `);

        // Then select the timestamp text not including the space
        const selectionWithTimestamp = SelectionState.createEmpty(updatedContent.getFirstBlock().getKey()).merge({
          anchorOffset: 0,
          focusOffset: timestampText.length
        });

        // Get the entity key for the timestamp entity
        const entityKey = contentWithTimestampEntity.getLastCreatedEntityKey();

        // Apply the timestamp entity to selected timestamp text. This will ensure that the timestamp is uneditable and that
        // the decorator will apply the proper styling to the timestamp.
        updatedContent = Modifier.applyEntity(updatedContent, selectionWithTimestamp, entityKey);
        newIsTimestampToggledOn = true;
      } else {
        // Create a selection range for the timestamp text and space so that we know what to remove and
        // remove it from the beginning of the input box. This uses the timestsamp length that we calculated earlier.
        const selectionToRemove = SelectionState.createEmpty(currentContent.getFirstBlock().getKey()).merge({
          anchorOffset: 0,
          focusOffset: timestampLengthIncludingSpace
        });

        // Remove the timestamp text and space. No need for an entity key because we are not applying any entity to the text.
        updatedContent = Modifier.replaceText(currentContent, selectionToRemove, '');
        newIsTimestampToggledOn = false;
      }

      // Position cursor after the timestamp and space (if adding) or at the beginning (if removing)
      const cursorOffset = newIsTimestampToggledOn ? timestampLengthIncludingSpace : 0;
      // Create a selection that ensures the cursor is outside any entity. This is important because we want to ensure
      // that the cursor is not inside the timestamp component when it is displayed
      const finalSelection = SelectionState.createEmpty(updatedContent.getFirstBlock().getKey()).merge({
        anchorOffset: cursorOffset,
        focusOffset: cursorOffset
      });

      // Create a new EditorState with the updated content
      let newEditorState = EditorState.push(editorState, updatedContent, 'insert-characters');
      // Apply selection first
      newEditorState = EditorState.forceSelection(newEditorState, finalSelection);

      // Update state with new timestamp status
      this.setState({
        isTimestampToggledOn: newIsTimestampToggledOn
      });

      // handle the change in the editor state
      this.handleChange(newEditorState);
    });
    /**
     * Event handler called on blur. Triggers validation
     * @param {SyntheticFocusEvent} event The event object
     * @returns {void}
     */
    _defineProperty(this, "handleBlur", event => {
      if (this.props.validateOnBlur && this.containerEl && event.relatedTarget instanceof Node && !this.containerEl.contains(event.relatedTarget)) {
        this.checkValidity();
      }
    });
    _defineProperty(this, "handleFocus", event => {
      const {
        onFocus
      } = this.props;
      if (onFocus) {
        onFocus(event);
      }
    });
    _defineProperty(this, "getIsTimestampEntityPresent", currentContent => {
      return this.getTimestampLength(currentContent) > 0;
    });
    /**
     * Calculates the length of the timestamp entity in the current block
     * @param {ContentState} currentContent The current content state
     * @param {ContentBlock} block The content block to analyze
     * @returns {number} The length of the timestamp entity (including the space after it)
     */
    _defineProperty(this, "getTimestampLength", currentContent => {
      // $FlowFixMe
      const block = currentContent?.getFirstBlock();
      if (!currentContent || !block) {
        return 0;
      }
      let timestampLength = 0;
      const characterList = block.getCharacterList();

      // get the length of the timestamp entity. This will include the space after the timestamp.
      for (let i = 0; i < characterList.size; i += 1) {
        const char = characterList.get(i);
        if (char && char.getEntity()) {
          const entity = currentContent.getEntity(char.getEntity());
          if (entity.getType() === UNEDITABLE_TIMESTAMP_TEXT) {
            timestampLength = i + 1;
          }
        }
      }
      // Include the space after the timestamp
      return timestampLength ? timestampLength + 1 : 0;
    });
    /**
     * Updates editorState, rechecks validity
     * @param {EditorState} nextEditorState The new editor state to set in the state
     * @returns {void}
     */
    _defineProperty(this, "handleChange", nextEditorState => {
      const {
        internalEditorState,
        isTimestampToggledOn
      } = this.state;
      const {
        onChange
      } = this.props;

      // Check if timestamp entity is still present in the content if video timestamping is enabled.
      // Update the timestamp prepended state to false if the timestamp entity is no longer present in the editor content
      // This can happen when the user deletes it with the backspace key.
      if (this.getIsVideoTimestampEnabled() && isTimestampToggledOn) {
        const currentContent = nextEditorState.getCurrentContent();
        const firstBlock = currentContent.getFirstBlock();
        const timestampLength = this.getTimestampLength(currentContent);
        const timestampEntityFound = timestampLength > 0;
        // If timestamp entity is no longer present, update the state
        if (!timestampEntityFound) {
          this.setState({
            isTimestampToggledOn: false
          });
        } else {
          // Check if the timestamp entity is at the beginning of the content, if not do not update the editor state.
          // This is to prevent the user from inserting text before the timestamp entity.
          const characterList = firstBlock.getCharacterList();
          const firstChar = characterList.get(0);
          if (firstChar && !firstChar.getEntity()) {
            return;
          }
        }
      }
      onChange(nextEditorState);
      if (internalEditorState) {
        const newState = {
          internalEditorState: nextEditorState
        };
        this.setState(newState);
      }
    });
    _defineProperty(this, "handleValidityStateUpdateHandler", () => {
      const {
        isTouched
      } = this.state;
      if (!isTouched) {
        return;
      }
      const error = this.getErrorFromValidityState();
      this.setState({
        error
      });
    });
    _defineProperty(this, "checkValidity", () => {
      this.handleValidityStateUpdateHandler();
    });
    _defineProperty(this, "getVideoTimestamp", () => {
      const videoContainer = document.querySelector('.bp-media-container');
      // $FlowFixMe
      const video = videoContainer?.querySelector('video');
      const currentTime = video?.currentTime || 0;

      // We need to get the nubmer of seconds in HMMSS format to display in the timestamp button
      // and the timestamp in milliseconds to use when the comment form is submitted. This is because
      // milliseconds are more precise than seconds and we need to make sure that we go to the right frame
      // when the comment timestamp is clicked in the sidebar.
      const totalSeconds = Math.floor(currentTime);
      const timestampToDisplay = convertSecondsToHMMSS(totalSeconds);
      const timestampInMilliseconds = Math.floor(currentTime * 1000);
      return {
        timestamp: timestampToDisplay,
        timestampInMilliseconds
      };
    });
    this.compositeDecorator = new CompositeDecorator([{
      strategy: mentionStrategy,
      component: DraftMentionItem
    }, {
      strategy: timestampStrategy,
      component: DraftTimestampItem
    }]);

    // @NOTE:
    // This component might be either own its EditorState (in which case it lives in `this.state.internalEditorState`)
    // or be a controlled component whose EditorState is passed in via the `editorState` prop.
    // If `props.editorState` is set, `internalEditorState` is `null`,
    // otherwise we initialize it here
    this.state = {
      contacts: [],
      isTouched: false,
      internalEditorState: props.editorState ? null : EditorState.createEmpty(this.compositeDecorator),
      error: null,
      isTimestampToggledOn: false
    };
  }
  static getDerivedStateFromProps(nextProps) {
    const {
      contacts
    } = nextProps;
    return contacts ? {
      contacts
    } : null;
  }
  componentDidMount() {
    // if video timestamping is enabled we need to check if a timestamp entity is present in the editor state passed in via props
    // and if it is then set the isTimestampToggledOn state to true. This will happen when the user is editing a comment
    // that has a timestamp entity.
    if (this.getIsVideoTimestampEnabled()) {
      const {
        isTimestampToggledOn,
        internalEditorState
      } = this.state;
      const {
        editorState: externalEditorState
      } = this.props;
      const currentEditorState = internalEditorState || externalEditorState;
      // if video timestamping is enabled and the editor state is being passed in check if a timestamp entity is present
      // and if it is then set the isTimestampToggledOn state to true.
      if (!isTimestampToggledOn && currentEditorState) {
        const currentContent = currentEditorState.getCurrentContent();
        const isTimeStampEntityPresent = this.getIsTimestampEntityPresent(currentContent);
        if (isTimeStampEntityPresent) {
          this.setState({
            isTimestampToggledOn: true
          });
        }
      }
    }
  }
  componentDidUpdate(prevProps, prevState) {
    const {
      internalEditorState: prevInternalEditorState
    } = prevState;
    const {
      internalEditorState
    } = this.state;
    const {
      editorState: prevEditorStateFromProps,
      isRequired: prevIsRequiredFromProps
    } = prevProps;
    const {
      editorState,
      isRequired
    } = this.props;

    // Determine whether we're working with the internal editor state or
    // external editor state passed in from props
    const prevEditorState = prevInternalEditorState || prevEditorStateFromProps;
    const currentEditorState = internalEditorState || editorState;

    // Only handle isTouched state transitions and check validity if the
    // editorState references are different. This is to avoid getting stuck
    // in an infinite loop of checking validity because checkValidity always
    // calls setState({ error })
    if (prevEditorState && currentEditorState && prevEditorState !== currentEditorState) {
      const newState = this.getDerivedStateFromEditorState(currentEditorState, prevEditorState);
      if (newState) {
        this.setState(newState, this.checkValidityIfAllowed);
      } else {
        this.checkValidityIfAllowed();
      }
    }

    // if isRequired is false then the comment box will be closed and we want
    // to make sure that isTimestampToggledOn is always set to false in this casee
    if (this.getIsVideoTimestampEnabled() && isRequired !== prevIsRequiredFromProps && isRequired === false) {
      this.setState({
        isTimestampToggledOn: false
      });
    }

    // If timestamplabel is set and isRequired is true then force the timestamp
    // to be added to the editor state as that is the specified default behavior for video comments
    if (this.getIsVideoTimestampEnabled() && isRequired !== prevIsRequiredFromProps && isRequired === true) {
      this.toggleTimestamp(currentEditorState, true);
    }
  }
  getDerivedStateFromEditorState(currentEditorState, previousEditorState) {
    const isPreviousEditorStateEmpty = this.isEditorStateEmpty(previousEditorState);
    const isCurrentEditorStateEmpty = this.isEditorStateEmpty(currentEditorState);
    const isNewEditorState = isCurrentEditorStateEmpty && !isPreviousEditorStateEmpty;
    const isEditorStateDirty = isPreviousEditorStateEmpty && !isCurrentEditorStateEmpty;
    let newState = null;
    // Detect case where controlled EditorState is created anew and empty.
    // If next editorState is empty and the current editorState is not empty
    // that means it is a new empty state and this component should not be marked dirty
    if (isNewEditorState) {
      newState = {
        isTouched: false,
        error: null
      };
    } else if (isEditorStateDirty) {
      // Detect case where controlled EditorState has been made dirty
      // If the current editorState is empty and the next editorState is not
      // empty then this is the first interaction so mark this component dirty
      newState = {
        isTouched: true
      };
    }
    return newState;
  }
  checkValidityIfAllowed() {
    const {
      validateOnBlur
    } = this.props;
    if (!validateOnBlur) {
      this.checkValidity();
    }
  }
  isEditorStateEmpty(editorState) {
    const text = editorState.getCurrentContent().getPlainText().trim();
    const lastChangeType = editorState.getLastChangeType();
    return text.length === 0 && lastChangeType === null;
  }

  /**
   * @returns {string}
   */
  getErrorFromValidityState() {
    const {
      editorState: externalEditorState,
      isRequired,
      maxLength,
      minLength
    } = this.props;
    const {
      internalEditorState
    } = this.state;

    // manually check for content length if isRequired is true
    const editorState = internalEditorState || externalEditorState;
    const {
      length
    } = editorState.getCurrentContent().getPlainText().trim();
    if (isRequired && !length) {
      return messages.valueMissing();
    }
    if (typeof minLength !== 'undefined' && length < minLength) {
      return messages.tooShort(minLength);
    }
    if (typeof maxLength !== 'undefined' && length > maxLength) {
      return messages.tooLong(maxLength);
    }
    return null;
  }
  render() {
    const {
      className = '',
      contactsLoaded,
      editorState: externalEditorState,
      hideLabel,
      isDisabled,
      isRequired,
      label,
      description,
      mentionTriggers,
      name,
      onMention,
      placeholder,
      selectorRow,
      startMentionMessage,
      onReturn,
      timestampLabel
    } = this.props;
    const {
      contacts,
      internalEditorState,
      error,
      isTimestampToggledOn: timestampToggledOn
    } = this.state;
    const {
      handleBlur,
      handleChange,
      handleFocus,
      toggleTimestamp
    } = this;
    let editorState = internalEditorState || externalEditorState;

    // Ensure the editor state has the composite decorator
    if (editorState.getDecorator() !== this.compositeDecorator) {
      editorState = EditorState.set(editorState, {
        decorator: this.compositeDecorator
      });
    }
    return /*#__PURE__*/React.createElement("div", {
      ref: containerEl => {
        this.containerEl = containerEl;
      },
      className: className
    }, /*#__PURE__*/React.createElement(FormInput, {
      name: name,
      onValidityStateUpdate: this.handleValidityStateUpdateHandler
    }, /*#__PURE__*/React.createElement(DraftJSMentionSelectorCore, {
      contacts: contacts,
      contactsLoaded: contactsLoaded,
      editorState: editorState,
      error: error,
      hideLabel: hideLabel,
      isDisabled: isDisabled,
      isRequired: isRequired,
      label: label,
      description: description,
      mentionTriggers: mentionTriggers,
      onBlur: handleBlur,
      onChange: handleChange,
      onFocus: handleFocus,
      onMention: onMention,
      onReturn: onReturn,
      placeholder: placeholder,
      selectorRow: selectorRow,
      startMentionMessage: startMentionMessage
    }), isRequired && this.getIsVideoTimestampEnabled() && /*#__PURE__*/React.createElement(Toggle, {
      className: "bcs-CommentTimestamp-toggle",
      "data-target-id": "Toggle-CommentTimestamp"
      // $FlowFixMe - timestampLabel is guaranteed to be defined when getIsVideoTimestampEnabled() returns true
      ,
      label: timestampLabel,
      isOn: timestampToggledOn,
      onChange: () => toggleTimestamp(editorState)
    })));
  }
}
_defineProperty(DraftJSMentionSelector, "defaultProps", {
  isRequired: false,
  onChange: noop,
  validateOnBlur: true
});
export default DraftJSMentionSelector;
//# sourceMappingURL=DraftJSMentionSelector.js.map