grommet/grommet

View on GitHub
src/js/components/Select/utils.js

Summary

Maintainability
C
7 hrs
Test Coverage
import { useCallback } from 'react';
import { normalizeColor } from '../../utils';

export const applyKey = (option, key) => {
  if (option === undefined || option === null) return undefined;
  if (typeof key === 'object') return applyKey(option, key.key);
  if (typeof key === 'function') return key(option);
  if (key !== undefined && typeof option === 'object') return option[key];
  if (typeof option === 'object' && Object.keys(option))
    return option[Object.keys(option)[0]];
  return option;
};

export const getOptionLabel = (index, options, labelKey) =>
  applyKey(options[index], labelKey);

export const getOptionValue = (index, options, valueKey) =>
  applyKey(options[index], valueKey);

export const getOptionIndex = (options, i, valueKey) => {
  if (options) {
    if (typeof i === 'object')
      return options.findIndex(
        (x) => applyKey(x, valueKey) === applyKey(i, valueKey),
      );
    return options.indexOf(i);
  }
  return undefined;
};

export const arrayIncludes = (arr, i, valueKey) => {
  if (arr) {
    if (typeof i === 'object')
      return arr.some((x) => applyKey(x, valueKey) === applyKey(i, valueKey));
    return arr.includes(i);
  }
  return undefined;
};

export const useDisabled = (disabled, disabledKey, options, valueKey) =>
  useCallback(
    (index) => {
      const option = options[index];
      let result;
      if (disabledKey) {
        result = applyKey(option, disabledKey);
      } else if (Array.isArray(disabled)) {
        if (typeof disabled[0] === 'number') {
          result = disabled.indexOf(index) !== -1;
        } else {
          const optionVal = getOptionValue(index, options, valueKey);
          result =
            getOptionIndex(disabled, options[index], valueKey) !== -1 ||
            getOptionIndex(disabled, optionVal, valueKey) !== -1;
        }
      }
      return result;
    },
    [disabled, disabledKey, options, valueKey],
  );

export const getNormalizedValue = (value, valueKey) => {
  if (Array.isArray(value))
    return value.map((v) =>
      valueKey && valueKey.reduce ? v : applyKey(v, valueKey),
    );
  return valueKey && valueKey.reduce ? value : applyKey(value, valueKey);
};

export const changeEvent = (inputRef, nextValue) => {
  // Calling set value function directly on input because React library
  // overrides setter `event.target.value =` and loses original event
  // target fidelity.
  // https://stackoverflow.com/a/46012210
  const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
    window.HTMLInputElement.prototype,
    'value',
  ).set;
  nativeInputValueSetter.call(inputRef.current, nextValue);
  const event = new Event('input', { bubbles: true });
  inputRef.current.dispatchEvent(event);
};

export const getSelectIcon = (icon, theme, open) => {
  let SelectIcon;
  switch (icon) {
    case false:
      break;
    case true:
    case undefined:
      SelectIcon =
        open && theme.select.icons.up
          ? theme.select.icons.up
          : theme.select.icons.down;
      break;
    default:
      SelectIcon = icon;
  }
  return SelectIcon;
};

// if labelKey is a function and valueLabel is not defined
// we should use the labelKey function to display the
// selected value
export const getDisplayLabelKey = (
  labelKey,
  allOptions,
  optionIndexesInValue,
  selectValue,
) => {
  const optionLabelKey = applyKey(
    allOptions[optionIndexesInValue[0]],
    labelKey,
  );
  if (
    !selectValue &&
    optionIndexesInValue.length === 1 &&
    typeof optionLabelKey === 'object'
  )
    return optionLabelKey;
  return undefined;
};

export const getIconColor = (theme) =>
  normalizeColor(theme.select.icons.color || 'control', theme);