import _extends from "@babel/runtime/helpers/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _objectSpread from "@babel/runtime/helpers/objectSpread";

/**
 * Web Tabs Component
 *
 */
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import keycode from 'keycode';
import { isTrue, registerElement, validateDOMAttributes, dispatchCustomElementEvent } from '../../shared/component-helper';
const renderProps = {
  render: null
};
export const propTypes = {
  data: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.shape({
    title: PropTypes.string.isRequired,
    key: PropTypes.string.isRequired,
    selected: PropTypes.bool,
    disabled: PropTypes.bool
  })), PropTypes.objectOf(PropTypes.shape({
    title: PropTypes.string.isRequired,
    selected: PropTypes.bool,
    disabled: PropTypes.bool
  }))]),
  label: PropTypes.string,
  selected_key: PropTypes.string,
  align: PropTypes.oneOf(['left', 'center', 'right']),
  section_style: PropTypes.string,
  section_spacing: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  use_hash: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  id: PropTypes.string,
  class: PropTypes.string,

  /** React props */
  className: PropTypes.string,
  children: PropTypes.oneOfType([PropTypes.object, PropTypes.node, PropTypes.func]),
  // Web Component props
  render: PropTypes.func
};
export const defaultProps = _objectSpread({
  data: null,
  label: null,
  selected_key: null,
  align: 'left',
  section_style: null,
  section_spacing: null,
  use_hash: false,
  id: null,
  class: null,

  /** React props */
  className: null,
  children: null
}, renderProps);

var _ref13 =
/*#__PURE__*/
React.createElement("span", null, "Tab content not found");

export default class Tabs extends PureComponent {
  static enableWebComponent() {
    registerElement(Tabs.tagName, Tabs, defaultProps);
  }

  static getDerivedStateFromProps(props, state) {
    if (state._listenForPropChanges) {
      if (props.data) {
        if (state._data !== props.data) {
          state._data = props.data;
          state.data = Tabs.getData(props);
        }
      }

      if (props.selected_key && state._selected_key !== props.selected_key) {
        state.selected_key = state._selected_key = props.selected_key;
      }
    }

    state._listenForPropChanges = true;
    return state;
  }

  static getData(props) {
    let res = []; // check if we have to use the children prop to prepare our data

    const data = !props.data && props.children ? props.children : props.data; // if it is a React Component - collect data from Tabs.Content component

    if (Array.isArray(props.children) && (typeof props.children[0] === 'function' || React.isValidElement(props.children[0]))) {
      res = props.children.reduce((acc, content, i) => {
        if (content.props && content.props.displayName === 'CustomContent' // we use this sollution, as Component.displayName
        ) {
            // tabs data from main prop
            const dataProps = props.tabs && Array.isArray(props.tabs) && props.tabs[i] || {}; // props from the "CustomContent" Component

            const componentProps = _objectSpread({}, content.props);

            if (componentProps.title === null) {
              delete componentProps.title;
            }

            const _dataProps$componentP = _objectSpread({}, dataProps, componentProps, {
              children: null // remove children, if there is some

            }),
                  {
              title,
              key: _key,
              hash
            } = _dataProps$componentP,
                  rest = _objectWithoutProperties(_dataProps$componentP, ["title", "key", "hash"]);

            acc.push(_objectSpread({
              title,
              key: (!_key && hash ? hash : _key) || generateKey(title),
              content
            }, rest));
          }

        return acc;
      }, []);
    } // continue, while the children didn't contain our data


    if (!(res && res.length > 0)) {
      // if data is array, it looks good!
      if (props.data && Array.isArray(data)) {
        res = data; // it may be a json
      } else if (typeof data === 'string') {
        res = data[0] === '[' ? JSON.parse(data) : []; // but it may also be an object
      } else if (data && typeof data === 'object') {
        res = Object.entries(data).reduce((acc, _ref) => {
          let [key, obj] = _ref;
          acc.push(_objectSpread({
            key
          }, obj));
          return acc;
        }, []);
      }
    }

    return res || [];
  }

  constructor(props) {
    super(props);

    _defineProperty(this, "onKeyDownHandler", e => {
      switch (keycode(e)) {
        case 'left':
          e.preventDefault();
          this.prevTab();
          this.setFocusOnTablist();
          break;

        case 'right':
          e.preventDefault();
          this.nextTab();
          this.setFocusOnTablist();
          break;
      }
    });

    _defineProperty(this, "prevTab", () => {
      this.openTab(-1);
    });

    _defineProperty(this, "nextTab", () => {
      this.openTab(+1);
    });

    _defineProperty(this, "setFocusOnTablist", () => {
      setTimeout(() => {
        if (this._tablistRef.current) {
          this._tablistRef.current.focus();
        }
      }, 1);
    });

    _defineProperty(this, "openTabByDOM", e => {
      const target = e.target.nodeName === 'SPAN' ? e.target.parentElement : e.target;
      const selected_key = String(target.className).match(/tab--([-_a-z0-9]+)/i)[1];
      this.openTab(selected_key);
      this.setFocusOnTablist();
    });

    _defineProperty(this, "openTab", selected_key => {
      if (parseFloat(selected_key)) {
        const currentData = this.state.data.filter((_ref2) => {
          let {
            disabled
          } = _ref2;
          return !disabled;
        });
        const currentIndex = currentData.reduce((acc, _ref3, i) => {
          let {
            key
          } = _ref3;
          return key === this.state.selected_key ? i : acc;
        }, -1);
        let nextIndex = currentIndex + selected_key;

        if (nextIndex < 0) {
          nextIndex = currentData.length - 1;
        }

        if (nextIndex >= currentData.length) {
          nextIndex = 0;
        }

        selected_key = currentData.reduce((acc, _ref4, i) => {
          let {
            key
          } = _ref4;
          return i === nextIndex ? key : acc;
        }, null);
      }

      if (selected_key) {
        this.setState({
          selected_key
        });
      }

      dispatchCustomElementEvent(this, 'on_change', {
        key: selected_key
      });

      if (this.props.use_hash && typeof window !== 'undefined') {
        try {
          window.history.replaceState(undefined, undefined, "#".concat(selected_key));
        } catch (e) {
          console.log('Tabs Error:', e);
        }
      }
    });

    this._id = props.id || "dnb-tabs-".concat(Math.round(Math.random() * 999)); // cause we need an id anyway

    const data = Tabs.getData(props);

    let _selected_key = props.selected_key || data.reduce((acc, _ref5) => {
      let {
        selected,
        key
      } = _ref5;
      return selected ? key : acc;
    }, null) || data[0] && data[0].key; // check if we have to open a diffrent tab


    if (props.use_hash && typeof window !== 'undefined') {
      try {
        const use_this_key = String(window.location.hash).replace('#', '');

        if (use_this_key && String(use_this_key).length > 0) {
          const keyExists = data.some((_ref6) => {
            let {
              key
            } = _ref6;
            return key === use_this_key;
          });

          if (keyExists) {
            _selected_key = use_this_key;
          }
        }
      } catch (e) {
        console.log('Tabs Error:', e);
      }
    }

    this.state = {
      _listenForPropChanges: true,
      selected_key: _selected_key,
      _selected_key: _selected_key,
      _data: props.data,
      data
    };
    this._tablistRef = React.createRef();
  }

  isSelected(tabKey) {
    return this.state.selected_key === tabKey;
  }

  renderSelectedTab(tabKey) {
    return "dnb-tablink tab--".concat(tabKey, " ").concat(this.isSelected(tabKey) ? 'selected' : '');
  }

  renderContent() {
    let useKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
    const {
      children
    } = this.props;

    if (!useKey) {
      const {
        selected_key,
        data
      } = this.state; // just to make sure we never get an empty content

      const keyExists = data.some((_ref7) => {
        let {
          key
        } = _ref7;
        return key === selected_key;
      });

      if (keyExists) {
        useKey = selected_key;
      } else {
        // key did not exists, so we get the first one
        useKey = data[0].key;
      }
    }

    let content = null;

    if (children) {
      if (typeof children === 'object' && children[useKey]) {
        // if content is provided as an object
        content = children[useKey];
      } else if (typeof children === 'function') {
        // if content is provided as a render prop
        content = children.apply(this, [useKey]);
      } else if (React.isValidElement(children)) {
        content = children;
      }
    } // check of the content is provided in the "data" part instead


    if (!content) {
      let items = [];

      if (Array.isArray(this.state.data)) {
        items = this.state.data;
      } else if (Array.isArray(children)) {
        items = children;
      } // if content was provided as a React Component like "Tabs.Content"
      // - or the content was provided as a content prop i data


      if (items) {
        content = items.filter((_ref8) => {
          let {
            key
          } = _ref8;
          return key && useKey && key === useKey;
        }).reduce((acc, _ref9) => {
          let {
            content
          } = _ref9;
          return content || acc;
        }, null);
      }
    }

    if (typeof content === 'function') {
      const Component = content;
      content = React.createElement(Component, null);
    }

    return content;
  }

  render() {
    var _this = this;

    const _this$props = this.props,
          {
      render: customRenderer,
      label,
      align,
      section_style,
      section_spacing,
      className,
      class: _className,
      selected_key: _selected_key,
      //eslint-disable-line
      id,
      //eslint-disable-line
      data,
      //eslint-disable-line
      use_hash,
      //eslint-disable-line
      children,
      //eslint-disable-line
      on_change
    } = _this$props,
          props = _objectWithoutProperties(_this$props, ["render", "label", "align", "section_style", "section_spacing", "className", "class", "selected_key", "id", "data", "use_hash", "children", "on_change"]);

    const {
      selected_key
    } = this.state; // To have a reusable Component laster, do this like that

    const Tabs = () => {
      const tabs = this.state.data.map((_ref10) => {
        let {
          title,
          key,
          disabled = false
        } = _ref10;
        const params = {};

        if (this.isSelected(key)) {
          params['aria-controls'] = "".concat(this._id, "-content-").concat(key);
        }

        if (isTrue(disabled)) {
          params.disabled = true;
          params['aria-disabled'] = true;
        }

        return React.createElement("button", _extends({
          key: "tab--".concat(key),
          role: "tab",
          tabIndex: "-1",
          id: "".concat(this._id, "-tab-").concat(key),
          "aria-selected": this.isSelected(key),
          className: this.renderSelectedTab(key),
          onClick: this.openTabByDOM
        }, params), React.createElement("span", {
          className: "dnb-tablink-title"
        }, title), React.createElement(Dummy, null, title));
      });
      const params = {};

      if (label) {
        params['aria-label'] = label;
      }

      return React.createElement("div", _extends({
        role: "tablist",
        className: "dnb-tabs__tabs__tablist",
        tabIndex: "0",
        onKeyDown: this.onKeyDownHandler,
        ref: this._tablistRef
      }, params), tabs);
    };

    Tabs.displayName = 'Tabs'; // To have a reusable Component laster, do this like that

    const TabsList = (_ref11) => {
      let {
        children,
        className
      } = _ref11;
      return React.createElement("div", {
        className: classnames('dnb-tabs__tabs', align ? "dnb-tabs__tabs--".concat(align) : null, section_style ? "dnb-section dnb-section--".concat(section_style) : null, section_spacing ? "dnb-section--spacing-".concat(isTrue(section_spacing) ? 'default' : section_spacing) : null, className)
      }, children);
    };

    TabsList.displayName = 'TabsList'; // To have a reusable Component laster, do this like that

    const Wrapper = (_ref12) => {
      let {
        children
      } = _ref12,
          rest = _objectWithoutProperties(_ref12, ["children"]);

      const params = _objectSpread({}, props, {
        className: classnames('dnb-tabs', className, _className) // also used for code markup simulation

      });

      validateDOMAttributes(this.props, params);
      return React.createElement("div", _extends({}, params, rest), children);
    };

    Wrapper.displayName = 'TabsWrapper';

    const Content = function Content() {
      let {
        showEmptyMessage = false
      } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

      const content = _this.renderContent();

      return React.createElement(ContentWrapper, {
        id: _this._id,
        selected_key: selected_key
      }, content || showEmptyMessage && _ref13);
    };

    Content.displayName = 'TabContent'; // here we reuse the component, if it has a custom renderer

    if (typeof customRenderer === 'function') {
      return customRenderer({
        Wrapper,
        Content,
        TabsList,
        Tabs
      });
    }

    return React.createElement(Wrapper, null, React.createElement(TabsList, null, React.createElement(Tabs, null)), React.createElement(Content, {
      showEmptyMessage: true
    }));
  }

}

_defineProperty(Tabs, "tagName", 'dnb-tabs');

_defineProperty(Tabs, "defaultProps", defaultProps);

Tabs.propTypes = process.env.NODE_ENV !== "production" ? propTypes : {};

class ContentWrapper extends PureComponent {
  render() {
    const {
      id,
      children,
      selected_key: key
    } = this.props;

    if (!children) {
      return React.createElement(React.Fragment, null);
    }

    return React.createElement("div", {
      role: "tabpanel",
      id: "".concat(id, "-content-").concat(key),
      className: "dnb-tabs__content",
      "aria-labelledby": "".concat(id, "-tab-").concat(key)
    }, children);
  }

} // This component is only a dummy component to collect data

/*
  Like:
  <Tabs>
    <Tabs.Content title="first" selected disabled>first</Tabs.Content>
    <Tabs.Content title="second">second</Tabs.Content>
  </Tabs>
 */


ContentWrapper.propTypes = process.env.NODE_ENV !== "production" ? {
  id: PropTypes.string.isRequired,
  selected_key: PropTypes.string.isRequired,
  children: PropTypes.node.isRequired
} : {};

class CustomContent extends PureComponent {
  render() {
    const {
      children
    } = this.props;
    return children;
  }

}

_defineProperty(CustomContent, "defaultProps", {
  displayName: 'CustomContent',
  title: null,
  hash: null,
  selected: null,
  disabled: null
});

CustomContent.propTypes = process.env.NODE_ENV !== "production" ? {
  displayName: PropTypes.string,
  // eslint-disable-line
  title: PropTypes.string,
  // eslint-disable-line
  hash: PropTypes.string,
  // eslint-disable-line
  selected: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  // eslint-disable-line
  disabled: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  // eslint-disable-line
  children: PropTypes.node.isRequired
} : {};

const generateKey = title => {
  const key = String(title).trim().toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-{1,}/g, '-');
  return key;
};

Tabs.Content = CustomContent;
Tabs.generateKey = generateKey;
Tabs.ContentWrapper = ContentWrapper;
export const Dummy = (_ref14) => {
  let {
    children
  } = _ref14;

  /**
   * This is a dummy markup, to define a width of every tab
   * We use "aria-hidden" SPAN to simulate a wider width for each tab
   * There is also a CSS to make this work. Have a look in the styles: & > span[hidden] {...}
   */
  return React.createElement("span", {
    "aria-hidden": true,
    hidden: true
  }, children);
};
Dummy.propTypes = process.env.NODE_ENV !== "production" ? {
  children: PropTypes.node.isRequired
} : {};