/* eslint-disable react/no-array-index-key */
import { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import useResizeObserver from "../../../hooks/useResizeObserver/index.js";
import Span from './Span';
import classes from './styles.module.scss';

/**
 * A bit of text that will responsively swap in different versions based
 * on avaialable space. This component comes with a few caveats.
 *
 * First, the text is rendered inside a `<span>`, so only inline content is
 * allowed (i.e., no paragraphs or other block content). Second, the text is
 * not allowed to wrap onto additional lines; it must all be displayed on one
 * single line.
 *
 * Third, the responsive text is rendered inside a `<div>` that sets its width
 * to 100%, making it take up all the available space that its parent can
 * provide; it is up to the parent of this component to restrict the horizontal
 * space that it is willing to allocate for the text. You likely won't have to
 * worry about that if you are allocating the full width of a line to the text,
 * but if you're using this component to render a label that should only take
 * up a certain percentage of the line you'll have to handle that explicitly.
 *
 * Finally, note that if the shortest bit of text is _still_ too long for the
 * space available, it will be shown anyways.
 */
const ResponsiveText = function ResponsiveText(props) {
  const {
    text,
    optional
  } = props;
  const [ref, [width]] = useResizeObserver();
  const [childWidths, setChildWidths] = useState({});
  const registerWidth = useCallback((index, childWidth) => {
    setChildWidths(old => {
      if (old[index] === childWidth) return old;
      return {
        ...old,
        [index]: childWidth
      };
    });
  }, []);
  const fittingIndex = text.reduce((idx, _, i) => {
    const w = childWidths[i];
    if (w <= width) return i;
    return idx;
  }, undefined);
  const activeIndex = optional ? fittingIndex : fittingIndex || 0;
  return <div ref={ref} className={classes.container}>
      {text.map((t, i) => <Span key={t.key || t} registerWidth={registerWidth} index={i} active={i === activeIndex}>
          {t.text || t}
        </Span>)}
    </div>;
};
ResponsiveText.defaultProps = {
  optional: false
};
ResponsiveText.propTypes = {
  /**
   * An array of text alternatives
   */
  text: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.shape({
    key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    text: PropTypes.node
  })])).isRequired,
  /**
   * Whether to bail on the text if the space is too small even for the
   * smallest child.
   */
  optional: PropTypes.bool
};
export default ResponsiveText;