@codecademy/gamut

72.2.272.2.3-alpha.83b0a8.0
dist/Form/SelectDropdown/core/styles.js
+dist/Form/SelectDropdown/core/styles.jsNew file
+248
Index: package/dist/Form/SelectDropdown/core/styles.js
===================================================================
--- package/dist/Form/SelectDropdown/core/styles.js
+++ package/dist/Form/SelectDropdown/core/styles.js
@@ -0,0 +1,248 @@
+import { css, states, variant } from '@codecademy/gamut-styles';
+import { dismissSharedStyles, tagBaseStyles, tagLabelFontSize, tagLabelPadding } from '../../../Tag/styles';
+import { formBaseComponentStyles, formBaseFieldStylesObject, formFieldDisabledStyles, formFieldPaddingStyles, InputSelectors } from '../../styles';
+const selectDropdownStyles = css({
+  ...formBaseFieldStylesObject,
+  display: 'flex',
+  zIndex: 3
+});
+const selectFocusStyles = {
+  color: 'primary',
+  borderColor: 'currentColor',
+  boxShadow: `inset 0 0 0 1px currentColor`
+};
+export const conditionalBorderStates = states({
+  isFocused: selectFocusStyles,
+  activated: {
+    borderColor: 'currentColor'
+  },
+  error: {
+    color: 'feedback-error',
+    borderColor: 'currentColor',
+    [InputSelectors.HOVER]: {
+      borderColor: 'currentColor'
+    }
+  },
+  isDisabled: formFieldDisabledStyles
+});
+const sizeVariants = variant({
+  prop: 'size',
+  defaultVariant: 'medium',
+  variants: {
+    medium: formFieldPaddingStyles,
+    mediumIsMultiSelected: {
+      px: 8,
+      py: 8
+    },
+    small: {
+      minHeight: '2rem',
+      px: 8,
+      py: 0
+    }
+  }
+});
+const dropdownBorderStates = states({
+  error: {
+    borderColorTop: 'feedback-error'
+  }
+});
+const dropdownBorderStyles = (zIndex = 2) => css({
+  ...formBaseComponentStyles,
+  border: 1,
+  borderColor: 'currentColor',
+  position: 'absolute',
+  marginTop: 0,
+  borderRadius: 'none',
+  zIndex
+});
+const getOptionBackground = (isSelected, isFocused) => css({
+  bg: isFocused ? 'background-hover' : isSelected ? 'background-selected' : 'transparent'
+});
+const textColor = css({
+  color: 'text'
+});
+const placeholderColor = css({
+  color: 'text-secondary'
+});
+export const getMemoizedStyles = (theme, zIndex) => {
+  return {
+    clearIndicator: provided => ({
+      ...provided
+    }),
+    container: (provided, state) => {
+      const {
+        inputWidth
+      } = state.selectProps;
+      return {
+        ...provided,
+        pointerEvents: 'visible',
+        cursor: state.selectProps.isSearchable ? 'text' : 'pointer',
+        width: inputWidth || '100%',
+        minWidth: '7rem'
+      };
+    },
+    control: (provided, state) => {
+      const {
+        isMulti,
+        size
+      } = state.selectProps;
+      const getSize = size ?? 'medium';
+      const getPadding = isMulti && state.hasValue && size !== 'small' ? `mediumIsMultiSelected` : getSize;
+      return {
+        ...selectDropdownStyles({
+          theme
+        }),
+        ...sizeVariants({
+          size: getPadding,
+          theme
+        }),
+        ...conditionalBorderStates({
+          isFocused: state.isFocused,
+          isDisabled: state.isDisabled,
+          error: state.selectProps.error,
+          activated: state.selectProps.activated,
+          theme
+        })
+      };
+    },
+    dropdownIndicator: () => ({
+      color: 'currentColor',
+      display: 'flex',
+      padding: '0',
+      pointerEvents: 'none'
+    }),
+    groupHeading: provided => ({
+      ...provided,
+      color: theme.colors['text-disabled']
+    }),
+    input: provided => ({
+      ...provided,
+      ...textColor({
+        theme
+      }),
+      margin: '0',
+      padding: '0'
+    }),
+    menu: (provided, state) => {
+      const {
+        dropdownWidth,
+        menuAlignment
+      } = state.selectProps;
+      return {
+        ...provided,
+        ...dropdownBorderStyles(zIndex)({
+          theme
+        }),
+        ...dropdownBorderStates({
+          error: state.selectProps.error,
+          theme
+        }),
+        // Drop react-select's default menu drop shadow; the border above defines the edge.
+        boxShadow: 'none',
+        ...(dropdownWidth ? {
+          minWidth: dropdownWidth,
+          width: dropdownWidth
+        } : {}),
+        ...(menuAlignment === 'right' ? {
+          left: 'auto',
+          right: 0
+        } : {})
+      };
+    },
+    menuList: (provided, state) => {
+      const sizeInteger = state.selectProps.size === 'small' ? 2 : 3;
+      const maxHeight = `${(state.selectProps.shownOptionsLimit ?? 6) * sizeInteger}rem`;
+      return {
+        ...provided,
+        maxHeight
+      };
+    },
+    multiValue: (provided, state) => ({
+      ...provided,
+      ...tagBaseStyles,
+      backgroundColor: 'transparent',
+      color: theme.colors.background,
+      cursor: state.isDisabled ? 'not-allowed' : 'pointer',
+      height: '24px'
+    }),
+    multiValueLabel: provided => ({
+      ...provided,
+      backgroundColor: theme.colors['text-secondary'],
+      borderEndEndRadius: 0,
+      borderEndStartRadius: theme.borderRadii.md,
+      borderStartEndRadius: 0,
+      borderStartStartRadius: theme.borderRadii.md,
+      color: theme.colors.background,
+      fontSize: tagLabelFontSize,
+      height: '100%',
+      padding: `0 ${tagLabelPadding}px`,
+      paddingLeft: `${tagLabelPadding}px`,
+      // default label has an explicit rule for padding left so we need this to override it
+      paddingTop: '2px' // adding to shift the text down to vertically center it
+    }),
+    multiValueRemove: (provided, state) => ({
+      ...provided,
+      ...dismissSharedStyles,
+      backgroundColor: theme.colors['text-secondary'],
+      borderEndEndRadius: theme.borderRadii.md,
+      borderEndStartRadius: 0,
+      borderStartEndRadius: theme.borderRadii.md,
+      borderStartStartRadius: 0,
+      cursor: state.isDisabled ? 'not-allowed' : 'pointer',
+      padding: 0,
+      // default remove has padding left and right that we don't need
+      pointerEvents: state.isDisabled ? 'none' : 'visible',
+      ':hover': {
+        backgroundColor: theme.colors['secondary-hover']
+      }
+    }),
+    noOptionsMessage: provided => ({
+      ...provided,
+      color: theme.colors['text-secondary']
+    }),
+    option: (provided, state) => {
+      const isNew = state.data?.__isNew__;
+      const isSmall = state.selectProps.size === 'small';
+      return {
+        ...getOptionBackground(state.isSelected, state.isFocused)({
+          theme
+        }),
+        alignItems: 'center',
+        color: state.isDisabled ? theme.colors['text-disabled'] : isNew ? theme.colors.primary : theme.colors.text,
+        cursor: state.isDisabled ? 'not-allowed' : 'pointer',
+        display: 'flex',
+        padding: isSmall ? '3px 14px' : '11px 14px',
+        ...(isNew && {
+          // Gradient creates the 1px divider line at the top edge of the option background
+          backgroundImage: `linear-gradient(${theme.colors['text-disabled']} 1px, transparent 1px)`,
+          backgroundPosition: '0 0',
+          backgroundRepeat: 'no-repeat',
+          backgroundSize: '100% 1px',
+          paddingTop: isSmall ? '11px' : '19px'
+        })
+      };
+    },
+    placeholder: provided => ({
+      ...provided,
+      ...placeholderColor({
+        theme
+      }),
+      overflow: 'hidden',
+      textOverflow: 'ellipsis',
+      whiteSpace: 'nowrap'
+    }),
+    singleValue: provided => ({
+      ...provided,
+      ...textColor({
+        theme
+      }),
+      alignItems: 'center',
+      display: 'flex',
+      marginLeft: 0
+    }),
+    valueContainer: provided => ({
+      ...provided,
+      padding: 0
+    })
+  };
+};
\ No newline at end of file