"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.SegmentedControl = SegmentedControl;

var React = _interopRequireWildcard(require("react"));

var _reactUid = require("react-uid");

var _styles = require("../styles");

var _overrides = require("../helpers/overrides");

var _focusVisible = require("../utils/focusVisible");

var _constants = require("./constants");

var _styledComponents = require("./styled-components");

var _utils = require("./utils");

var _excluded = ["childKey", "childIndex", "activeKey", "activeSegmentRef", "updateHighlight", "parseKeyDown", "activateOnFocus", "uid", "disabled", "sharedStylingProps", "onChange"],
    _excluded2 = ["artwork", "overrides", "segmentRef", "onClick", "label", "description"];

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

var KEYBOARD_ACTION = {
  next: 'next',
  previous: 'previous'
};

var getLayoutParams = function getLayoutParams(el) {
  if (!el) {
    return {
      length: 0,
      distance: 0
    };
  }

  var _el$getBoundingClient = el.getBoundingClientRect(),
      width = _el$getBoundingClient.width;

  width = Math.floor(width); // Note we're using getBoundingClientRect to take into account
  // the borders.

  return {
    length: width,
    distance: el.offsetLeft
  };
};

var scrollParentToCentreTarget = function scrollParentToCentreTarget(targetNode) {
  var _targetNode$parentNod = targetNode.parentNode.getBoundingClientRect(),
      parentX = _targetNode$parentNod.x,
      parentY = _targetNode$parentNod.y,
      parentWidth = _targetNode$parentNod.width,
      parentHeight = _targetNode$parentNod.height;

  var _targetNode$getBoundi = targetNode.getBoundingClientRect(),
      childX = _targetNode$getBoundi.x,
      childY = _targetNode$getBoundi.y,
      childWidth = _targetNode$getBoundi.width,
      childHeight = _targetNode$getBoundi.height; // get the position of the child centre, relative to parent


  var childCentre = {
    x: childX - parentX + childWidth / 2,
    y: childY - parentY + childHeight / 2
  }; // aim for the centre of the child to be the centre of the parent

  var _targetNode$parentNod2 = targetNode.parentNode,
      scrollLeft = _targetNode$parentNod2.scrollLeft,
      scrollTop = _targetNode$parentNod2.scrollTop;
  var target = {
    x: scrollLeft + childCentre.x - parentWidth / 2,
    y: scrollTop + childCentre.y - parentHeight / 2
  }; // ignore out of bounds, the browser will manage this for us

  targetNode.parentNode.scroll(target.x, target.y);
};

function SegmentedControl(_ref) {
  var _ref$activeKey = _ref.activeKey,
      activeKey = _ref$activeKey === void 0 ? '0' : _ref$activeKey,
      _ref$disabled = _ref.disabled,
      disabled = _ref$disabled === void 0 ? false : _ref$disabled,
      children = _ref.children,
      _ref$fill = _ref.fill,
      fill = _ref$fill === void 0 ? _constants.FILL.intrinsic : _ref$fill,
      _ref$activateOnFocus = _ref.activateOnFocus,
      activateOnFocus = _ref$activateOnFocus === void 0 ? true : _ref$activateOnFocus,
      onChange = _ref.onChange,
      _ref$overrides = _ref.overrides,
      overrides = _ref$overrides === void 0 ? {} : _ref$overrides,
      _ref$uid = _ref.uid,
      customUid = _ref$uid === void 0 ? null : _ref$uid,
      width = _ref.width,
      height = _ref.height;
  // Create unique id prefix for this segments component
  var generatedUid = (0, _reactUid.useUID)();
  var uid = customUid || generatedUid; // Unpack overrides

  var RootOverrides = overrides.Root,
      ActiveOverrides = overrides.Active;

  var _getOverrides = (0, _overrides.getOverrides)(RootOverrides, _styledComponents.StyledRoot),
      _getOverrides2 = _slicedToArray(_getOverrides, 2),
      Root = _getOverrides2[0],
      RootProps = _getOverrides2[1];

  var _getOverrides3 = (0, _overrides.getOverrides)(ActiveOverrides, _styledComponents.StyledActive),
      _getOverrides4 = _slicedToArray(_getOverrides3, 2),
      Active = _getOverrides4[0],
      ActiveProps = _getOverrides4[1];

  var _getOverrides5 = (0, _overrides.getOverrides)(overrides.SegmentList, _styledComponents.StyledSegmentList),
      _getOverrides6 = _slicedToArray(_getOverrides5, 2),
      SegmentList = _getOverrides6[0],
      SegmentListProps = _getOverrides6[1]; // Count key updates
  // We disable a few things until after first mount:
  // - the highlight animation, avoiding an initial slide-in
  // - smooth scrolling active segment into view


  var _React$useState = React.useState(0),
      _React$useState2 = _slicedToArray(_React$useState, 2),
      keyUpdated = _React$useState2[0],
      setKeyUpdated = _React$useState2[1];

  React.useEffect(function () {
    setKeyUpdated(keyUpdated + 1);
  }, [activeKey]); // Positioning the highlight.

  var activeSegmentRef = React.useRef();

  var _React$useState3 = React.useState({
    length: 0,
    distance: 0
  }),
      _React$useState4 = _slicedToArray(_React$useState3, 2),
      highlightLayout = _React$useState4[0],
      setHighlightLayout = _React$useState4[1]; // Create a shared, memoized callback for segments to call on resize.


  var updateHighlight = React.useCallback(function () {
    if (activeSegmentRef.current) {
      setHighlightLayout(getLayoutParams(activeSegmentRef.current));
    }
  }, [activeSegmentRef.current]); // Update highlight on key

  React.useEffect(updateHighlight, [activeSegmentRef.current]); // Scroll active segment into view when the parent has scrollbar on mount and
  // on key change (smooth scroll). Note, if the active key changes while
  // the segment is not in view, the page will scroll it into view.
  // TODO: replace with custom scrolling logic.

  React.useEffect(function () {
    // Flow needs this condition pulled out.
    if (activeSegmentRef.current) {
      if ( // @ts-expect-error todo(flow->ts) maybe parentElement?
      activeSegmentRef.current.parentNode.scrollWidth > // @ts-expect-error todo(flow->ts) maybe parentElement?
      activeSegmentRef.current.parentNode.clientWidth) {
        if (keyUpdated > 1) {
          activeSegmentRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest',
            inline: 'nearest'
          });
        } else {
          scrollParentToCentreTarget(activeSegmentRef.current);
        }
      }
    }
  }, [activeSegmentRef.current]); // Collect shared styling props

  var sharedStylingProps = {
    $fill: fill
  }; // Helper for parsing directional keys
  // TODO(WPT-6473): move to universal keycode aliases

  var _useStyletron = (0, _styles.useStyletron)(),
      _useStyletron2 = _slicedToArray(_useStyletron, 2),
      theme = _useStyletron2[1];

  var parseKeyDown = React.useCallback(function (event) {
    if ((0, _utils.isRTL)(theme.direction)) {
      switch (event.keyCode) {
        case 39:
          return KEYBOARD_ACTION.previous;

        case 37:
          return KEYBOARD_ACTION.next;

        default:
          return null;
      }
    } else {
      switch (event.keyCode) {
        case 37:
          return KEYBOARD_ACTION.previous;

        case 39:
          return KEYBOARD_ACTION.next;

        default:
          return null;
      }
    }
  }, [theme.direction]);
  return /*#__PURE__*/React.createElement(Root, _extends({}, sharedStylingProps, RootProps, {
    $width: width,
    $height: height
  }), /*#__PURE__*/React.createElement(SegmentList, _extends({
    "data-baseweb": "segmented-list",
    role: "listbox",
    "aria-label": "segmented control"
  }, SegmentListProps), React.Children.map(children, function (child, index) {
    if (!child) return;
    return /*#__PURE__*/React.createElement(InternalSegment, _extends({
      childKey: child.key,
      childIndex: index,
      activeKey: activeKey,
      activeSegmentRef: activeSegmentRef,
      updateHighlight: updateHighlight,
      parseKeyDown: parseKeyDown,
      activateOnFocus: activateOnFocus,
      uid: uid,
      disabled: disabled,
      sharedStylingProps: sharedStylingProps,
      onChange: onChange
    }, child.props));
  }), /*#__PURE__*/React.createElement(Active, _extends({
    "data-baseweb": "segment-highlight",
    $length: highlightLayout.length,
    $distance: highlightLayout.distance // This avoids the segment sliding in from the side on mount
    ,
    $animate: keyUpdated > 1,
    "aria-hidden": "true",
    role: "presentation"
  }, sharedStylingProps, ActiveProps))));
}

function InternalSegment(_ref2) {
  var childKey = _ref2.childKey,
      childIndex = _ref2.childIndex,
      activeKey = _ref2.activeKey,
      activeSegmentRef = _ref2.activeSegmentRef,
      updateHighlight = _ref2.updateHighlight,
      parseKeyDown = _ref2.parseKeyDown,
      activateOnFocus = _ref2.activateOnFocus,
      uid = _ref2.uid,
      disabled = _ref2.disabled,
      sharedStylingProps = _ref2.sharedStylingProps,
      onChange = _ref2.onChange,
      props = _objectWithoutProperties(_ref2, _excluded);

  var key = childKey || String(childIndex);
  var isActive = key == activeKey;

  var Artwork = props.artwork,
      _props$overrides = props.overrides,
      overrides = _props$overrides === void 0 ? {} : _props$overrides,
      segmentRef = props.segmentRef,
      _onClick = props.onClick,
      label = props.label,
      description = props.description,
      restProps = _objectWithoutProperties(props, _excluded2); // A way to share our internal activeSegmentRef via the "segmentRef" prop.


  var ref = React.useRef();
  React.useImperativeHandle(segmentRef, function () {
    return isActive ? activeSegmentRef.current : ref.current;
  }); // Track segment dimensions in a ref after each render
  // This is used to compare params when the resize observer fires

  var segmentLayoutParams = React.useRef({
    length: 0,
    distance: 0
  });
  React.useEffect(function () {
    segmentLayoutParams.current = getLayoutParams(isActive ? activeSegmentRef.current : ref.current);
  }); // We need to potentially update the active segment highlight when the width or
  // placement changes for a segment so we listen for resize updates in each segment.

  React.useEffect(function () {
    if (window.ResizeObserver) {
      var observer = new window.ResizeObserver(function (entries) {
        if (entries[0] && entries[0].target) {
          var segmentLayoutParamsAfterResize = getLayoutParams(entries[0].target);

          if (segmentLayoutParamsAfterResize.length !== segmentLayoutParams.current.length || segmentLayoutParamsAfterResize.distance !== segmentLayoutParams.current.distance) {
            updateHighlight();
          }
        }
      });
      observer.observe(isActive ? activeSegmentRef.current : ref.current);
      return function () {
        observer.disconnect();
      };
    }
  }, [activeKey]);
  React.useEffect(updateHighlight, [label]); // Collect overrides

  var SegmentOverrides = overrides.Segment,
      ArtworkContainerOverrides = overrides.ArtworkContainer,
      LabelOverrides = overrides.Label,
      DescriptionOverrides = overrides.Description;

  var _getOverrides7 = (0, _overrides.getOverrides)(SegmentOverrides, _styledComponents.StyledSegment),
      _getOverrides8 = _slicedToArray(_getOverrides7, 2),
      Segment = _getOverrides8[0],
      SegmentProps = _getOverrides8[1];

  var _getOverrides9 = (0, _overrides.getOverrides)(ArtworkContainerOverrides, _styledComponents.StyledArtworkContainer),
      _getOverrides10 = _slicedToArray(_getOverrides9, 2),
      ArtworkContainer = _getOverrides10[0],
      ArtworkContainerProps = _getOverrides10[1];

  var _getOverrides11 = (0, _overrides.getOverrides)(LabelOverrides, _styledComponents.StyledLabel),
      _getOverrides12 = _slicedToArray(_getOverrides11, 2),
      LabelContainer = _getOverrides12[0],
      LabelContainerProps = _getOverrides12[1];

  var _getOverrides13 = (0, _overrides.getOverrides)(DescriptionOverrides, _styledComponents.StyledDescription),
      _getOverrides14 = _slicedToArray(_getOverrides13, 2),
      DescriptionContainer = _getOverrides14[0],
      DescriptionContainerProps = _getOverrides14[1]; // Keyboard focus styling


  var _React$useState5 = React.useState(false),
      _React$useState6 = _slicedToArray(_React$useState5, 2),
      focusVisible = _React$useState6[0],
      setFocusVisible = _React$useState6[1];

  var handleFocus = React.useCallback(function (event) {
    if ((0, _focusVisible.isFocusVisible)(event)) {
      setFocusVisible(true);
    }
  }, []);
  var handleBlur = React.useCallback( // eslint-disable-next-line @typescript-eslint/no-unused-vars
  function (event) {
    if (focusVisible !== false) {
      setFocusVisible(false);
    }
  }, [focusVisible]); // Keyboard focus management
  // @ts-expect-error todo(flow->ts): deps are required

  var handleKeyDown = React.useCallback(function (event) {
    // WAI-ARIA 1.1
    // https://www.w3.org/TR/wai-aria-practices-1.1/#segmentpanel
    // We use directional keys to iterate focus through SegmentedControl.
    // Find all segments eligible for focus
    var availableSegmentedControl = _toConsumableArray(event.target.parentNode.childNodes).filter(function (node) {
      return !node.disabled && node.getAttribute('role') === 'option';
    }); // Exit early if there are no other segments available


    if (availableSegmentedControl.length === 1) return; // Find segment to focus, looping to start/end of list if necessary

    var currentSegmentIndex = availableSegmentedControl.indexOf(event.target);
    var action = parseKeyDown(event);

    if (action) {
      var nextSegment;

      if (action === KEYBOARD_ACTION.previous) {
        if (availableSegmentedControl[currentSegmentIndex - 1]) {
          nextSegment = availableSegmentedControl[currentSegmentIndex - 1];
        } else {
          nextSegment = availableSegmentedControl[availableSegmentedControl.length - 1];
        }
      } else if (action === KEYBOARD_ACTION.next) {
        if (availableSegmentedControl[currentSegmentIndex + 1]) {
          nextSegment = availableSegmentedControl[currentSegmentIndex + 1];
        } else {
          nextSegment = availableSegmentedControl[0];
        }
      }

      if (nextSegment) {
        // Focus the segment
        nextSegment.focus(); // Optionally activate the segment

        if (activateOnFocus) {
          nextSegment.click();
        }
      }
    }
  });
  return /*#__PURE__*/React.createElement(Segment, _extends({
    "data-baseweb": "segment",
    key: key,
    id: (0, _utils.getSegmentId)(uid, key),
    role: "option",
    onKeyDown: handleKeyDown,
    "aria-selected": isActive,
    tabIndex: isActive ? '0' : '-1',
    ref: isActive ? activeSegmentRef : ref,
    disabled: !isActive && disabled,
    type: "button" // so it doesn't trigger a submit when used inside forms
    ,
    $focusVisible: focusVisible,
    $isActive: isActive,
    $hasArtwork: !!Artwork,
    $hasLabel: !!label
  }, sharedStylingProps, restProps, SegmentProps, {
    onClick: function onClick(event) {
      if (typeof onChange === 'function') onChange({
        activeKey: key
      });
      if (typeof _onClick === 'function') _onClick(event);
    },
    onFocus: (0, _focusVisible.forkFocus)(_objectSpread(_objectSpread({}, restProps), SegmentProps), handleFocus),
    onBlur: (0, _focusVisible.forkBlur)(_objectSpread(_objectSpread({}, restProps), SegmentProps), handleBlur)
  }), Artwork ? /*#__PURE__*/React.createElement(ArtworkContainer, _extends({
    "data-baseweb": "artwork-container"
  }, sharedStylingProps, ArtworkContainerProps), /*#__PURE__*/React.createElement(Artwork, {
    size: 20,
    color: "contentPrimary"
  })) : null, label ? /*#__PURE__*/React.createElement(LabelContainer, _extends({
    $hasArtwork: !!Artwork
  }, LabelContainerProps), label ? label : key) : null, description ? /*#__PURE__*/React.createElement(DescriptionContainer, _extends({}, DescriptionOverrides, DescriptionContainerProps), description) : null);
}