diff --git a/src/components/HorizontalNavigator/index.tsx b/src/components/HorizontalNavigator/index.tsx index 068418f8..a83d0474 100644 --- a/src/components/HorizontalNavigator/index.tsx +++ b/src/components/HorizontalNavigator/index.tsx @@ -3,9 +3,12 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import clsx from 'clsx'; import { nanoid } from 'nanoid'; -import useColors, { ChayaColorType } from '../../hooks/useColors'; +import { cva } from '../../utils/cva'; +import { + colorVariantMapper, ChayaColorType, EMPTY_COLOR_MAP, BORDER_COLOR_MAP, SOLID_BG_COLOR_MAP, +} from '../../utils/classMaps/colors'; -import HorizontalNavigatorItem, { HorizontalNavigatorItemType } from './item'; +import HorizontalNavigatorItem, { HorizontalNavigatorItemType, HorizontalNavigatorVariantType } from './item'; export type HorizontalNavigatorProps = { // id of the navigator @@ -19,13 +22,31 @@ export type HorizontalNavigatorProps = { // key of the active item. If null, no item will be active. activeItem?: string | null, // variant of the navigator. Can be 'pill' or 'line', defaults to 'pill' - variant?: 'pill' | 'line', + variant?: HorizontalNavigatorVariantType, // color of the navigator. color?: ChayaColorType, // callback when an item is clicked. Passes the key and the item as arguments. onClickItem?: (key: string, item: HorizontalNavigatorItemType) => void, }; +const activeMarkerClassName = cva({ + base: [ + 'dsr-absolute dsr-left-0 dsr-rounded-lg', + 'dsr-transition-all dsr-ease-in-out', + ], + variants: { + variant: { + line: 'horizontal-navigator-underline dsr-border-2 dsr-w-full dsr-bottom-0', + pill: 'horizontal-navigator-pill dsr-shadow-lg dsr-z-[500] dsr-top-0', + }, + color: EMPTY_COLOR_MAP, + }, + compoundVariants: [ + ...colorVariantMapper([BORDER_COLOR_MAP], 'line'), + ...colorVariantMapper([SOLID_BG_COLOR_MAP], 'pill'), + ], +}); + const HorizontalNavigator = ({ id, items, variant = 'pill', color = 'primary', className, itemClassName, activeItem, onClickItem = () => {}, @@ -34,8 +55,6 @@ const HorizontalNavigator = ({ const navigatorID = useMemo(() => id || `horizontal-navigator-${nanoid()}`, [id]); const tabRef = useRef(null); - const { backgroundColor } = useColors('solid', color); - const [indicatorStyle, setIndicatorStyle] = useState<{ width: number | null, height: number | null, @@ -64,37 +83,6 @@ const HorizontalNavigator = ({ updateIndicator(); } }, [activeItem]); - - const underlineRenderer = ( -
- ); - - const highlightRenderer = ( -
- ); return (
))} - {variant === 'line' ? underlineRenderer : highlightRenderer} +
); diff --git a/src/components/HorizontalNavigator/item.tsx b/src/components/HorizontalNavigator/item.tsx index 8261812b..67200dfc 100644 --- a/src/components/HorizontalNavigator/item.tsx +++ b/src/components/HorizontalNavigator/item.tsx @@ -6,7 +6,10 @@ import { cva } from '../../utils/cva'; import { LinkWrapper } from '../../utils/misc'; import Icon, { IconInputType } from '../Icon'; import Badge, { BaseBadgeProps } from '../Badge'; -import { colorVariantMapper, ChayaColorType, MINIMAL_BG_COLOR_MAP, TEXT_COLOR_MAP } from '../../utils/classMaps/colors'; +import { + colorVariantMapper, ChayaColorType, + MINIMAL_BG_COLOR_MAP, TEXT_COLOR_MAP, EMPTY_COLOR_MAP, +} from '../../utils/classMaps/colors'; export type HorizontalNavigatorItemType = { key: string, @@ -35,6 +38,15 @@ export type HorizontalNavigatorItemProps = { onClickItem?: (key: string, item: HorizontalNavigatorItemType) => void, }; +const buttonClassNames = cva({ + variants: { + variant: { + pill: 'dsr-px-5 dsr-py-2', + line: 'dsr-py-1 dsr-px-3', + }, + }, +}); + const HorizontalNavigatorItem = ({ item, activeItem, badgeProps, variant = 'pill', className, navigatorID, onClickItem = () => {}, color = 'primary', }: HorizontalNavigatorItemProps) => { @@ -42,28 +54,18 @@ const HorizontalNavigatorItem = ({ const liClassNames = cva({ base: [ - 'dsr-outline-1 focus-visible:dsr-outline dsr-duration-200 dsr-transition', + 'dsr-outline-1 focus-visible:dsr-outline dsr-duration-200 dsr-transition dsr-text-color', 'dsr-rounded-lg dsr-transition-background dsr-outline-2 dsr-no-underline', item?.isDisabled && 'dsr-opacity-60 dsr-cursor-not-allowed', activeItem === item.key && 'active dsr-font-semibold', ], variants: { - color: { - primary: '', - secondary: '', - success: '', - warning: '', - danger: '', - shade: '', - contrast: '', - white: '', - black: '', - }, + color: EMPTY_COLOR_MAP, variant: { pill: [ 'border border-neutral-300/20 ', activeItem === item.key && 'dsr-text-primaryTextColor', - activeItem !== item.key && !item?.isDisabled && 'hover:dsr-bg-neutral-50/80 dark:hover:dsr-bg-neutral-800/80', + activeItem !== item.key && !item?.isDisabled && 'hover:dsr-bg-neutral-50/80 dark:hover:dsr-bg-neutral-500/80', ], line: [ 'dsr-transition-all dsr-rounded-lg dsr-gap-2 dsr-border-0 dsr-mb-2', @@ -74,20 +76,19 @@ const HorizontalNavigatorItem = ({ compoundVariants: [ ...(activeItem === item.key ? colorVariantMapper([MINIMAL_BG_COLOR_MAP, TEXT_COLOR_MAP], 'line') : []), { - variant: 'pill', - color: 'white', - class: activeItem === item.key ? 'dsr-text-neutral-900 dsr-bg-neutral-50/80 dark:dsr-bg-neutral-800/80' : '', + variant: 'pill', color: 'white', + className: [ + 'dark:dsr-text-neutral-100', + activeItem === item.key && 'dsr-text-neutral-900 dsr-bg-neutral-50/80 dark:dsr-bg-neutral-100/80 dark:dsr-text-neutral-900', + ], }, - ], - }); - - const buttonClassNames = cva({ - variants: { - variant: { - pill: 'dsr-px-5 dsr-py-2', - line: 'dsr-py-1 dsr-px-3', + { + variant: 'pill', color: 'contrast', + className: [ + activeItem === item.key && 'dsr-text-neutral-100 dark:dsr-text-neutral-900', + ], }, - }, + ], }); const renderOption = (item: HorizontalNavigatorItemType) => ( @@ -138,7 +139,6 @@ const HorizontalNavigatorItem = ({ key={item?.key ? `tab_selector_${item?.key}` : nanoid()} role="presentation" className={clsx([ - color === 'white' && variant === 'pill' ? 'dsr-text-neutral-900' : 'dsr-text-color', liClassNames({ color, variant }), className, item.className, ])} diff --git a/src/components/VerticalNavigator/Item.tsx b/src/components/VerticalNavigator/Item.tsx index d4b40000..0df32013 100644 --- a/src/components/VerticalNavigator/Item.tsx +++ b/src/components/VerticalNavigator/Item.tsx @@ -6,7 +6,11 @@ import { LinkWrapper } from '../../utils/misc'; import Icon, { IconInputType } from '../Icon'; import Badge, { BaseBadgeProps } from '../Badge'; import ChevronUp from '../../utils/icons/chevron-up'; -import useColors, { ChayaColorType } from '../../hooks/useColors'; +import { cva } from '../../utils/cva'; +import { + ChayaColorType, colorMapper, + EMPTY_COLOR_MAP, MINIMAL_BG_COLOR_MAP, SOLID_TEXT_COLOR_MAP, TEXT_COLOR_MAP, +} from '../../utils/classMaps/colors'; export type VerticalNavigatorItemBaseType = { key: string, @@ -26,6 +30,8 @@ export type VerticalNavigatorItemType = VerticalNavigatorItemBaseType & { items?: VerticalNavigatorItemBaseType[] }; +export type VerticalNavigatorVariantType = 'pill' | 'line'; + export type VerticalNavigatorItemProps = { item: VerticalNavigatorItemType, className?: string, @@ -46,9 +52,6 @@ const VerticalNavigatorItem = ({ const [height, setHeight] = useState(undefined); const dropdownContentRef = useRef(null); - const { backgroundColor, activeColor } = useColors('minimal', color); - const { textColor } = useColors('solid', color); - const [dropdownVisibility, setDropdownVisibility] = useState(defaultExpansion); useEffect(() => setDropdownVisibility(!isCollapsed), [isCollapsed]); @@ -66,18 +69,39 @@ const VerticalNavigatorItem = ({ 'dsr-flex dsr-justify-between dsr-items-center dsr-transition dsr-w-full', className, ]); - const innerContent = (item: VerticalNavigatorItemBaseType, hasChildren: boolean = false, isChild: boolean = false) => ( + const innerContentClassName = cva({ + base: [ + 'dsr-flex dsr-w-full dsr-items-center dsr-gap-2 dsr-text-color dsr-py-1.5 dsr-px-1', + isCollapsed ? 'dsr-justify-center' : 'dsr-justify-between', + ], + variants: { + variant: { + line: 'dsr-transition-all dsr-rounded-r-lg dsr-gap-2', + pill: '', + }, + color: EMPTY_COLOR_MAP, + state: { + active: '', + inactive: '', + }, + }, + compoundVariants: [ + ...colorMapper<{ variant: VerticalNavigatorVariantType, state: 'active' | 'inactive' }>([SOLID_TEXT_COLOR_MAP], { variant: 'pill', state: 'active' }), + ...colorMapper<{ variant: VerticalNavigatorVariantType, state: 'active' | 'inactive' }>([MINIMAL_BG_COLOR_MAP, TEXT_COLOR_MAP], { variant: 'line', state: 'active' }), + { + variant: 'line', + color: 'white', + className: activeItem === item.key ? 'dark:dsr-bg-neutral-100/80' : '', + }, + ], + }); + + const innerContent = (item: VerticalNavigatorItemBaseType, isChild: boolean = false) => (
{item.icon && ( @@ -108,7 +132,7 @@ const VerticalNavigatorItem = ({ ]); const contentRenderer = (item: VerticalNavigatorItemBaseType, isChild: boolean = false) => item?.link ? - LinkWrapper(item.link, innerContent(item, false, isChild), { + LinkWrapper(item.link, innerContent(item, isChild), { role: item.role ?? 'tab', className: clsx([ commonClasses, @@ -129,7 +153,7 @@ const VerticalNavigatorItem = ({ (isChild && variant === 'line') && 'dsr-my-0.5', ])} > - {innerContent(item, false, isChild)} + {innerContent(item, isChild)} ); @@ -151,11 +175,11 @@ const VerticalNavigatorItem = ({ ])} onClick={() => setDropdownVisibility(!dropdownVisibility)} > - {innerContent(item, true, false)} + {innerContent(item)} {!isCollapsed && ( @@ -188,13 +212,7 @@ const VerticalNavigatorItem = ({
  • {contentRenderer(subItem, true)} @@ -216,9 +234,6 @@ const VerticalNavigatorItem = ({ variant === 'pill' && 'hover:dsr-bg-neutral-900/30', ]) : 'hover:dsr-bg-neutral-300/20', ])} - style={{ - color: (activeItem === item.key && variant === 'pill') ? textColor : undefined, - }} key={item.key} > {contentRenderer(item)} diff --git a/src/components/VerticalNavigator/index.tsx b/src/components/VerticalNavigator/index.tsx index ddf69055..ec5353e4 100644 --- a/src/components/VerticalNavigator/index.tsx +++ b/src/components/VerticalNavigator/index.tsx @@ -2,13 +2,18 @@ import React, { useEffect, useRef, useState } from 'react'; import clsx from 'clsx'; -import useColors, { ChayaColorType } from '../../hooks/useColors'; +import { cva } from '../../utils/cva'; +import { + colorVariantMapper, ChayaColorType, + EMPTY_COLOR_MAP, SOLID_BG_COLOR_MAP, BORDER_COLOR_MAP, +} from '../../utils/classMaps/colors'; + +import VerticalNavigatorItem, { VerticalNavigatorItemType, VerticalNavigatorVariantType } from './Item'; -import VerticalNavigatorItem, { VerticalNavigatorItemType } from './Item'; export type VerticalNavigatorProps = { items: VerticalNavigatorItemType[], - variant?: 'pill' | 'line', + variant?: VerticalNavigatorVariantType, color?: ChayaColorType, activeItem?: string | null, className?: string, @@ -20,6 +25,21 @@ export type VerticalNavigatorProps = { onClickItem?: (key: string, item: VerticalNavigatorItemType) => void, }; +const activeMarkerClassNames = cva({ + base: 'vertical-navigator-active-marker dsr-transition-all dsr-ease-in-out dsr-absolute dsr-top-0 dsr-left-0', + variants: { + variant: { + pill: 'dsr-shadow-lg dsr-z-[500]', + line: 'dsr-border-2 dsr-z-[1000]', + }, + color: EMPTY_COLOR_MAP, + }, + compoundVariants: [ + ...colorVariantMapper([SOLID_BG_COLOR_MAP], 'pill'), + ...colorVariantMapper([BORDER_COLOR_MAP], 'line'), + ], +}); + const VerticalNavigator = ({ items, className, itemClassName, variant = 'pill', color = 'primary', role = 'tablist', itemRole, id, isCollapsed, activeItem, onClickItem = () => {}, }: VerticalNavigatorProps) => { @@ -31,8 +51,6 @@ const VerticalNavigator = ({ translateY: number | null, }>(({ width: null, height: null, translateX: null, translateY: null })); - const { backgroundColor } = useColors('solid', color); - const updateIndicator = () => { if (wrapperRef.current) { const tab = wrapperRef.current.querySelector('.active'); @@ -101,20 +119,15 @@ const VerticalNavigator = ({ ) && (
    item.key === activeItem) ? 'dsr-rounded-lg' : 'dsr-rounded-l-0 dsr-rounded-r-lg', - 'dsr-shadow-lg dsr-z-[500]', ]), - variant == 'line' && 'dsr-border-2 dsr-z-[1000]', ])} style={{ transform: `${indicatorStyle?.translateY ? `translateY(${indicatorStyle?.translateY}px)` : ''} ${indicatorStyle?.translateX ? `translateX(${indicatorStyle?.translateX}px)` : ''}`, width: indicatorStyle?.width || 0, height: indicatorStyle?.height || 0, - borderColor: variant === 'line' ? backgroundColor : undefined, - backgroundColor: variant === 'pill' ? backgroundColor : undefined, }} /> )} diff --git a/src/utils/classMaps/colors.ts b/src/utils/classMaps/colors.ts index 0e5a33af..55c6f737 100644 --- a/src/utils/classMaps/colors.ts +++ b/src/utils/classMaps/colors.ts @@ -1,6 +1,18 @@ export type ChayaColorType = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'contrast' | 'shade' | 'white' | 'black'; type ColorClassMap = { [color in ChayaColorType]: string }; +export const EMPTY_COLOR_MAP: ColorClassMap = { + primary: '', + secondary: '', + success: '', + danger: '', + warning: '', + contrast: '', + shade: '', + white: '', + black: '', +}; + export const SOLID_BG_COLOR_MAP: ColorClassMap = { primary: 'dsr-bg-primary', @@ -69,14 +81,18 @@ export const BORDER_COLOR_MAP: ColorClassMap = { black: 'dsr-border-black', }; -export const colorVariantMapper = (maps: ColorClassMap[], variant: Type) => { +export const colorMapper = (maps: ColorClassMap[], object: Type) => { return maps.map((map) => { return Object.keys(map).map((color) => { return { - variant: variant as Type, color: color as ChayaColorType, className: map[color as ChayaColorType], + ...object, }; }); }).flat(); }; + +export const colorVariantMapper = (maps: ColorClassMap[], variant: Type) => { + return colorMapper(maps, { variant }); +};