Skip to content

Commit

Permalink
- port Banner & VisualPicker to use CVA
Browse files Browse the repository at this point in the history
- remove useColors hook and complete transition to class based colors
  • Loading branch information
aswinshenoy committed Dec 24, 2023
1 parent ff4036c commit 9f5dd18
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 149 deletions.
141 changes: 78 additions & 63 deletions src/components/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from 'react';
import clsx from 'clsx';

import useColors, { ChayaColorType } from '../hooks/useColors';
import { cva } from '../utils/cva';
import {
colorVariantMapper, ChayaColorType,
SOLID_BG_COLOR_MAP, SOLID_TEXT_COLOR_MAP, EMPTY_COLOR_MAP,
} from '../utils/classMaps/colors';

import Icon, { IconInputType } from './Icon';

Expand All @@ -22,98 +26,109 @@ export type BannerProps = {
onClose?: () => void,
};

const wrapperClassName = cva({
base: '',
variants: {
variant: {
'full-width': '',
'float': 'dsr-p-4',
'card': 'dsr-max-w-[700px]',
},
position: {
'top': 'dsr-absolute dsr-top-0',
'bottom': 'dsr-absolute dsr-bottom-0',
'inline': '',
},
},
compoundVariants: [
{
variant: ['full-width', 'float'],
className: 'dsr-w-full dsr-left-0 dsr-right-0',
},
{
variant: ['float', 'card'],
className: 'dsr-rounded-lg dsr-shadow-lg',
},
],
});

const containerClassName = cva({
variants: {
variant: {
'full-width': '',
'float': '',
'card': '',
},
color: EMPTY_COLOR_MAP,
},
compoundVariants: [
...colorVariantMapper([SOLID_BG_COLOR_MAP, SOLID_TEXT_COLOR_MAP], ['float', 'card', 'full-width']),
],
});

const Banner = ({
id, className, variant, onClose, position = 'top', text, color = 'primary', icon,
allowDismissal, children, learnMore,
}: BannerProps) => {

const { activeColor, textColor } = useColors('solid', color);
const contentRenderer = (
<React.Fragment>
<div className="dsr-w-full dsr-flex dsr-gap-4 dsr-place-items-center">
{icon && <Icon icon={icon} size={20} />}
<p>
{text}
{learnMore && <a href={learnMore.link} className="dsr-whitespace-nowrap hover:dsr-underline dsr-inline">{learnMore.text}</a>}
</p>
</div>
<div className="dsr-flex dsr-gap-4 dsr-items-center dsr-flex-shrink-0">
{children}
{allowDismissal && (
<button type="button" className="dsr-flex dsr-place-items-center" onClick={onClose}>
<Icon icon="times" size={20} />
</button>
)}
</div>
</React.Fragment>
);

const cardRenderer = (
<div
className={clsx([
'dsr-p-4 dsr-max-w-[700px] dsr-right-0',
position === 'top' ? 'dsr-top-0 dsr-fixed dsr-flex dsr-justify-end' : position === 'bottom' ? 'dsr-bottom-0 dsr-fixed dsr-flex dsr-justify-end' : '',
'dsr-p-5 dsr-flex dsr-flex-col dsr-gap-4 dsr-flex-wrap md:dsr-flex-nowrap dsr-text-center md:dsr-text-left dsr-items-center dsr-justify-center md:dsr-justify-between',
])}
>
<div
id={id}
className={clsx([
'dsr-p-5 dsr-flex dsr-flex-col dsr-gap-4 dsr-flex-wrap md:dsr-flex-nowrap dsr-text-center md:dsr-text-left dsr-items-center dsr-justify-center md:dsr-justify-between',
variant !== 'full-width' ? 'dsr-rounded-lg' : '',
className,
variant !== 'full-width' && 'dsr-shadow-lg',
])}
style={{ backgroundColor: activeColor, color: textColor }}
>
<div className="dsr-w-full dsr-flex dsr-justify-end">
<div
className={clsx([
'dsr-flex dsr-justify-between',
icon && 'dsr-w-full',
])}
>
{icon && <Icon icon={icon} size={20} />}
{allowDismissal && (
<button type="button" className="dsr-flex dsr-place-items-center" onClick={onClose}>
<Icon icon="times" size={20} />
</button>
)}
</div>
</div>
<p>
{text || 'Banner text'}
' '
{learnMore && <a href={learnMore.link} className="dsr-whitespace-nowrap hover:dsr-underline dsr-inline">{learnMore.text}</a>}
</p>
<div className="dsr-flex dsr-w-full dsr-justify-end">
<div>{children}</div>
</div>
</div>
{contentRenderer}
</div>
);

const bannerRenderer = (
<div
className={clsx([
'dsr-w-full dsr-left-0 dsr-right-0',
position === 'top' ? 'dsr-top-0' : position === 'bottom' ? 'dsr-bottom-0' : '',
variant === 'float' ? 'dsr-p-4' : variant === 'full-width' ? 'dsr-sticky' : '',
position !== 'inline' ? 'dsr-fixed' : '',
'dsr-w-full dsr-p-5 dsr-flex dsr-gap-4 dsr-flex-wrap md:dsr-flex-nowrap dsr-text-center md:dsr-text-left dsr-items-center dsr-justify-center md:dsr-justify-between',
])}
>
{contentRenderer}
</div>
);

return (
<div
className={clsx([
wrapperClassName({ variant, position }),
])}
>
<div
id={id}
className={clsx([
'dsr-w-full dsr-p-5 dsr-flex dsr-gap-4 dsr-flex-wrap md:dsr-flex-nowrap dsr-text-center md:dsr-text-left dsr-items-center dsr-justify-center md:dsr-justify-between',
variant !== 'full-width' ? 'dsr-rounded-lg dsr-shadow-lg' : '',
containerClassName({ variant, color }),
className,
])}
style={{ backgroundColor: activeColor, color: textColor }}
>
<div className="dsr-w-full dsr-flex dsr-gap-4 dsr-place-items-center">
{icon && <Icon icon={icon} size={20} />}
<p>
{text || 'Banner text'}
' '
{learnMore && <a href={learnMore.link} className="dsr-whitespace-nowrap hover:dsr-underline dsr-inline">{learnMore.text}</a>}
</p>
</div>
<div className="dsr-flex dsr-gap-4 dsr-items-center dsr-flex-shrink-0">
{children}
{allowDismissal && (
<button type="button" className="dsr-flex dsr-place-items-center" onClick={onClose}>
<Icon icon="times" size={20} />
</button>
)}
</div>
{(variant === 'card') ? cardRenderer : bannerRenderer}
</div>
</div>
);

return (variant === 'card') ? cardRenderer : bannerRenderer;

};

export default Banner;
51 changes: 40 additions & 11 deletions src/components/VisualPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import React, { useEffect, useMemo } from 'react';
import { nanoid } from 'nanoid';
import clsx from 'clsx';

import useColors, { ChayaColorType } from '../hooks/useColors';
import { cva } from '../utils/cva';
import {
colorMapper, ChayaColorType,
EMPTY_COLOR_MAP, SOLID_BG_COLOR_MAP, SOLID_TEXT_COLOR_MAP, BORDER_COLOR_MAP,
} from '../utils/classMaps/colors';

import Label from './Label';
import Icon, { IconInputType } from './Icon';
Expand Down Expand Up @@ -33,14 +37,41 @@ export type VisualPickerProps<Type> = {
colMinWidth?: number
};

const buttonClassNames = cva({
base: [
'dsr-w-full dsr-rounded-lg dsr-border',
'dsr-transition dsr-flex',
],
variants: {
direction: {
vertical: 'dsr-items-center dsr-justify-start dsr-p-3 dsr-text-left dsr-gap-3',
horizontal: 'dsr-flex-col dsr-gap-1 dsr-items-center dsr-justify-center dsr-px-3 dsr-py-4 dsr-min-h-[220px] dsr-text-center',
},
color: EMPTY_COLOR_MAP,
variant: {
solid: '',
},
state: {
active: '',
inactive: '',
},
},
compoundVariants: [
...colorMapper<{ state: 'active' | 'inactive', variant: 'solid' }>([
SOLID_BG_COLOR_MAP, SOLID_TEXT_COLOR_MAP, BORDER_COLOR_MAP,
], {
state: 'active',
variant: 'solid',
}),
],
});

const VisualPicker = <Type extends VisualPickerValueType | VisualPickerValueType[]>({
items, isVertical, className, value, label, onChange, id, itemClassName, isRequired = false,
isDisabled = false, fitHorizontal = true, color = 'primary', isMulti = false, colMinWidth = 200,
}: VisualPickerProps<Type>) => {
const generatedID = useMemo(() => id ?? `vp-${nanoid()}`, [id]);

const { backgroundColor, textColor } = useColors('solid', color);

const onSelect = (item: VisualPickerValueType) => {
if (isMulti && Array.isArray(value)) {
if (value.includes(item)) onChange(value.filter(v => v !== item) as Type);
Expand Down Expand Up @@ -76,18 +107,16 @@ const VisualPicker = <Type extends VisualPickerValueType | VisualPickerValueType
aria-disabled={isDisabled || item.isDisabled}
disabled={isDisabled || item.isDisabled}
className={clsx([
'dsr-w-full dsr-rounded-lg dsr-border',
'dsr-transition dsr-flex',
buttonClassNames({
direction: isVertical ? 'vertical' : 'horizontal',
color,
variant: 'solid',
state: isSelected(item.value) ? 'active' : 'inactive',
}),
!isSelected(item.value) && !(isDisabled || item.isDisabled) && 'hover:dsr-border-gray-400/80',
isDisabled || item.isDisabled ? 'dsr-opacity-90 dark:dsr-border-neutral-500/50 dsr-border-neutral-500/10' : 'dsr-bg-background-lighten-1 dark:dsr-bg-background-lighten-2 dark:dsr-border-neutral-500/70 dsr-border-neutral-500/20',
isVertical ? 'dsr-items-center dsr-justify-start dsr-p-3 dsr-text-left dsr-gap-3' : 'dsr-flex-col dsr-gap-1 dsr-items-center dsr-justify-center dsr-px-3 dsr-py-4 dsr-min-h-[220px] dsr-text-center',
itemClassName,
])}
style={isSelected(item.value) ? {
backgroundColor,
color: textColor,
borderColor: backgroundColor,
} : undefined}
onClick={() => onSelect(item.value)}
>
{(item.icon) && (
Expand Down
67 changes: 0 additions & 67 deletions src/hooks/useColors.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/utils/classMaps/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,18 @@ export const BORDER_COLOR_MAP: ColorClassMap = {
black: 'dsr-border-black',
};

export const colorMapper = <Type extends { [key: string]: string }>(maps: ColorClassMap[], object: Type) => {
export const colorMapper = <Type extends { [key: string]: string | string[] }>(maps: ColorClassMap[], object: Type) => {
return maps.map((map) => {
return Object.keys(map).map((color) => {
return {
color: color as ChayaColorType,
className: map[color as ChayaColorType],
...object,
...object as { [key: string]: string | string[] },
};
});
}).flat();
};

export const colorVariantMapper = <Type extends string>(maps: ColorClassMap[], variant: Type) => {
export const colorVariantMapper = <Type extends string | string[]>(maps: ColorClassMap[], variant: Type) => {
return colorMapper(maps, { variant });
};
18 changes: 16 additions & 2 deletions stories/components/display/Banner.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Meta, Primary, Title, Controls, } from '@storybook/blocks';
import {Meta, Primary, Title, Controls, Canvas,} from '@storybook/blocks';
import * as BannerStories from "./Banner.stories";

<Meta
Expand All @@ -14,4 +14,18 @@ import * as BannerStories from "./Banner.stories";

## Props

<Controls />
<Controls />

## Variants

## Float (default)

<Primary />

### Card

<Canvas story={BannerStories.CardVariant} withToolbar />

### Full Width

<Canvas story={BannerStories.FullWidthVariant} withToolbar />
Loading

0 comments on commit 9f5dd18

Please sign in to comment.