lookingglass/hyperglass/ui/components/select/styles.ts

233 lines
7.6 KiB
TypeScript

import { useToken } from '@chakra-ui/react';
import { mergeWith } from '@chakra-ui/utils';
/* eslint-disable react-hooks/exhaustive-deps */
import { useCallback } from 'react';
import {
useMobile,
useOpposingColor,
useOpposingColorCallback,
} from '~/hooks';
import { useSelectContext } from './select';
import * as ReactSelect from 'react-select';
import type { SingleOption } from '~/types';
import type { RSStyleCallbackProps, RSStyleFunction, RSThemeFunction } from './types';
export const useContainerStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'container', Opt, IsMulti> => {
return useCallback((base, state) => {
return { width: '100%' };
}, []);
};
export const useControlStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'control', Opt, IsMulti> => {
const { colorMode } = props;
const { isError } = useSelectContext();
const focusBorder = useToken('colors', 'brand.500');
const invalidBorder = useToken('colors', 'red.500');
const borderHover = useToken('colors', 'gray.300');
return useCallback(
(base, state) => {
const { isFocused } = state;
const styles = {
backgroundColor: useToken('colors', 'white'),
borderRadius: useToken('radii', 'md'),
color: useToken('colors', 'black'),
minHeight: useToken('space', 12),
transition: 'all 0.2s',
borderColor: isError ? invalidBorder : isFocused ? focusBorder : useToken('gray.100'),
boxShadow: isError
? `0 0 0 1px ${invalidBorder}`
: isFocused
? `0 0 0 1px ${focusBorder}`
: undefined,
'&:hover': { borderColor: isFocused ? focusBorder : borderHover },
'&:hover > div > span': { backgroundColor: borderHover },
'&:focus': { borderColor: isError ? invalidBorder : focusBorder },
'&.invalid': { borderColor: invalidBorder, boxShadow: `0 0 0 1px ${invalidBorder}` },
};
return mergeWith({}, base, styles);
},
[colorMode, isError],
);
};
export const useMenuStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'menu', Opt, IsMulti> => {
const { colorMode } = props;
const { isOpen } = useSelectContext();
const styles = { backgroundColor: useToken('white'), zIndex: 1500 };
return useCallback(base => mergeWith({}, base, styles), [colorMode, isOpen]);
};
export const useMenuListStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'menuList', Opt, IsMulti> => {
const { colorMode } = props;
const { isOpen } = useSelectContext();
const scrollbarTrack = useToken('colors', 'blackAlpha.50');
const scrollbarThumb = useToken('colors', 'blackAlpha.300');
const scrollbarThumbHover = useToken('colors', 'blackAlpha.400');
const styles = {
borderRadius: useToken('radii', 'md'),
backgroundColor: useToken('colors', 'white'),
'&::-webkit-scrollbar': { width: '5px' },
'&::-webkit-scrollbar-track': { backgroundColor: scrollbarTrack },
'&::-webkit-scrollbar-thumb': { backgroundColor: scrollbarThumb },
'&::-webkit-scrollbar-thumb:hover': { backgroundColor: scrollbarThumbHover },
'-ms-overflow-style': { display: 'none' },
};
return useCallback(base => mergeWith({}, base, styles), [colorMode, isOpen]);
};
export const useOptionStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'option', Opt, IsMulti> => {
const { colorMode } = props;
const { isOpen } = useSelectContext();
const fontSize = useToken('fontSizes', 'lg');
const disabled = useToken('colors', 'whiteAlpha.400');
const active = useToken('colors', 'brand.600');
const focused = useToken('colors', 'brand.500');
const selected = useToken('colors', 'brand.600');
const color = useToken('colors', 'white');
return useCallback(
(base, state) => {
const { isFocused, isSelected, isDisabled } = state;
let backgroundColor = 'transparent';
switch (true) {
case isDisabled:
backgroundColor = disabled;
break;
case isSelected:
backgroundColor = selected;
break;
case isFocused:
backgroundColor = focused;
break;
}
const styles = {
color: backgroundColor === 'transparent' ? 'currentColor' : color,
'&:active': { backgroundColor: active, color: color },
'&:focus': { backgroundColor: active, color: color },
backgroundColor,
fontSize,
};
return mergeWith({}, base, styles);
},
[isOpen, colorMode],
);
};
export const useIndicatorSeparatorStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'indicatorSeparator', Opt, IsMulti> => {
const { colorMode } = props;
const styles = { backgroundColor: useToken('colors', 'gray.200') };
return useCallback(base => mergeWith({}, base, styles), [colorMode]);
};
export const usePlaceholderStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'placeholder', Opt, IsMulti> => {
const { colorMode } = props;
const styles = {
color: useToken('colors', 'gray.600'),
fontSize: useToken('fontSizes', 'lg')
}
return useCallback(base => mergeWith({}, base, styles), [colorMode]);
};
export const useSingleValueStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'singleValue', Opt, IsMulti> => {
const { colorMode } = props;
const color = useToken('colors', 'black');
const styles = {
color,
fontSize: useToken('fontSizes', 'lg')
};
return useCallback(base => mergeWith({}, base, styles), [color, colorMode]);
};
export const useMultiValueStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'multiValue', Opt, IsMulti> => {
const { colorMode } = props;
const backgroundColor = useToken('colors', 'brand.500');
const styles = {
backgroundColor: backgroundColor,
color: useToken('colors', 'white'),
borderRadius: useToken('radii', 'md')
};
return useCallback(base => mergeWith({}, base, styles), [backgroundColor, colorMode]);
};
export const useMultiValueLabelStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'multiValueLabel', Opt, IsMulti> => {
const { colorMode } = props;
const styles = { color: useToken('colors', 'white') };
return useCallback(base => mergeWith({}, base, styles), [colorMode]);
};
export const useMultiValueRemoveStyle = <Opt extends SingleOption, IsMulti extends boolean>(
props: RSStyleCallbackProps,
): RSStyleFunction<'multiValueRemove', Opt, IsMulti> => {
const { colorMode } = props;
const color = useToken('colors', 'white');
const styles = {
color: color,
'&:hover': { backgroundColor: 'transparent', color, opacity: 0.8 },
};
return useCallback(base => mergeWith({}, base, styles), [colorMode]);
};
export const useRSTheme = (): RSThemeFunction => {
const borderRadius = useToken('radii', 'md') as unknown as number;
return useCallback((t: ReactSelect.Theme): ReactSelect.Theme => ({ ...t, borderRadius }), []);
};
export const useMenuPortal = <Opt extends SingleOption, IsMulti extends boolean>(): RSStyleFunction<
'menuPortal',
Opt,
IsMulti
> => {
const isMobile = useMobile();
const styles = {
zIndex: 1500,
};
return useCallback(base => mergeWith({}, base, styles), [isMobile]);
};