Skip to content

Commit

Permalink
TW-1360: EVM Design. Home page. Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-tsx committed May 14, 2024
1 parent b30f879 commit 8bde9d9
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 297 deletions.
2 changes: 1 addition & 1 deletion src/app/atoms/AccountName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const AccountName = memo<Props>(({ account, searchValue, smaller }) => {
strategy="fixed"
popup={props => (
<ActionsDropdownPopup
title={() => 'Select Address to copy'}
title="Select Address to copy"
opened={props.opened}
lowering={smaller ? 1 : 3}
style={{ minWidth: 173 }}
Expand Down
6 changes: 3 additions & 3 deletions src/app/atoms/ActionsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { FC, PropsWithChildren, CSSProperties } from 'react';
import React, { FC, CSSProperties } from 'react';

import DropdownWrapper from './DropdownWrapper';

interface Props {
title: () => string;
title: React.ReactNode;
opened: boolean;
lowering?: 1 | 2 | 3;
style?: CSSProperties;
Expand All @@ -17,7 +17,7 @@ export const ActionsDropdownPopup: FC<PropsWithChildren<Props>> = ({
children
}) => (
<DropdownWrapper opened={opened} design="day" className={`p-2 flex flex-col mt-${lowering}`} style={style}>
<div className="py-2.5 px-2 text-font-small-bold text-grey-1">{title()}</div>
<div className="py-2.5 px-2 text-font-small-bold text-grey-1">{title}</div>

{children}
</DropdownWrapper>
Expand Down
183 changes: 87 additions & 96 deletions src/app/atoms/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,101 +20,92 @@ interface Props extends CheckboxProps {
overrideClassNames?: string;
}

const Checkbox = forwardRef<HTMLInputElement, Props>(
(
{
overrideClassNames,
errored = false,
className,
checked,
onChange,
onFocus,
onBlur,
testID,
testIDProperties,
disabled,
...rest
},
ref
) => {
const [localChecked, setLocalChecked] = useState(() => checked ?? false);

const { trackEvent } = useAnalytics();

useEffect(() => {
setLocalChecked(prevChecked => checked ?? prevChecked);
}, [setLocalChecked, checked]);

const handleChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const { checked: toChecked } = event.target;
checkedHandler(event, onChange && (() => onChange(toChecked, event)), setLocalChecked);

testID && trackEvent(testID, AnalyticsEventCategory.CheckboxChange, { toChecked, ...testIDProperties });
},
[onChange, setLocalChecked, trackEvent, testID, testIDProperties]
);

/**
* Focus handling
*/
const [localFocused, setLocalFocused] = useState(false);

const handleFocus = useCallback(
(e: React.FocusEvent<HTMLInputElement>) => focusHandler(e, onFocus!, setLocalFocused),
[onFocus, setLocalFocused]
);
const handleBlur = useCallback(
(e: React.FocusEvent<HTMLInputElement>) => blurHandler(e, onBlur!, setLocalFocused),
[onBlur, setLocalFocused]
);

const classNameMemo = useMemo(
() =>
clsx(
'flex justify-center items-center flex-shrink-0',
'text-white border overflow-hidden',
'transition ease-in-out duration-200 disable-outline-for-click',
localChecked ? 'bg-primary-orange' : 'bg-black-40',
localFocused && 'shadow-outline',
(() => {
switch (true) {
case localChecked:
return 'border-primary-orange-dark';
case localFocused:
return 'border-primary-orange';
case errored:
return 'border-red-400';
default:
return 'border-gray-400';
}
})(),
disabled && 'opacity-75 pointer-events-none',
overrideClassNames || 'h-6 w-6 rounded-md'
),
[localChecked, localFocused, disabled, overrideClassNames, errored]
);

return (
<div className={classNameMemo} {...setTestID(testID)}>
<input
disabled={disabled}
ref={ref}
type="checkbox"
className={clsx('sr-only', className)}
checked={localChecked}
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
{...rest}
/>

<OkIcon
className={clsx('h-4/6 w-4/6 stroke-2 stroke-current pointer-events-none', localChecked ? 'block' : 'hidden')}
/>
</div>
);
}
);
const Checkbox = forwardRef<HTMLInputElement, Props>((props, ref) => {
const { overrideClassNames, errored = false, className, testID, disabled, ...rest } = props;

const { localChecked, localFocused, handleChange, handleFocus, handleBlur } = useCheckboxHooks(props);

const classNameMemo = useMemo(
() =>
clsx(
'flex justify-center items-center flex-shrink-0',
'text-white border overflow-hidden',
'transition ease-in-out duration-200 disable-outline-for-click',
localChecked ? 'bg-primary-orange' : 'bg-black-40',
localFocused && 'shadow-outline',
(() => {
switch (true) {
case localChecked:
return 'border-primary-orange-dark';
case localFocused:
return 'border-primary-orange';
case errored:
return 'border-red-400';
default:
return 'border-gray-400';
}
})(),
disabled && 'opacity-75 pointer-events-none',
overrideClassNames || 'h-6 w-6 rounded-md'
),
[localChecked, localFocused, disabled, overrideClassNames, errored]
);

return (
<div className={classNameMemo} {...setTestID(testID)}>
<input
{...rest}
disabled={disabled}
ref={ref}
type="checkbox"
className={clsx('sr-only', className)}
checked={localChecked}
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
/>

<OkIcon
className={clsx('h-4/6 w-4/6 stroke-2 stroke-current pointer-events-none', localChecked ? 'block' : 'hidden')}
/>
</div>
);
});

export default Checkbox;

export const useCheckboxHooks = ({ checked, onChange, onFocus, onBlur, testID, testIDProperties }: CheckboxProps) => {
const [localChecked, setLocalChecked] = useState(() => checked ?? false);

const { trackEvent } = useAnalytics();

useEffect(() => {
setLocalChecked(prevChecked => checked ?? prevChecked);
}, [setLocalChecked, checked]);

const handleChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const { checked: toChecked } = event.target;
checkedHandler(event, onChange && (() => onChange(toChecked, event)), setLocalChecked);

testID && trackEvent(testID, AnalyticsEventCategory.CheckboxChange, { toChecked, ...testIDProperties });
},
[onChange, setLocalChecked, trackEvent, testID, testIDProperties]
);

/**
* Focus handling
*/
const [localFocused, setLocalFocused] = useState(false);

const handleFocus = useCallback(
(e: React.FocusEvent<HTMLInputElement>) => focusHandler(e, onFocus!, setLocalFocused),
[onFocus, setLocalFocused]
);
const handleBlur = useCallback(
(e: React.FocusEvent<HTMLInputElement>) => blurHandler(e, onBlur!, setLocalFocused),
[onBlur, setLocalFocused]
);

return { localChecked, localFocused, handleChange, handleFocus, handleBlur };
};
41 changes: 22 additions & 19 deletions src/app/atoms/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@ import { combineRefs } from 'lib/ui/utils';

import { Button } from './Button';
import { IconBase } from './IconBase';
import {
StyledButtonColor,
ACTIVE_STYLED_BUTTON_COLORS_CLASSNAME,
getStyledButtonColorsClassNames
} from './StyledButton';

type Color = 'blue' | 'orange' | 'red';

interface Props extends TestIDProps {
Icon: ImportedSVGComponent;
design?: 'blue' | 'orange' | 'red';
color?: Color;
active?: boolean;
tooltip?: string;
onClick?: EmptyFn;
}

export const IconButton = memo(
forwardRef<HTMLButtonElement, Props>(({ Icon, design, active, tooltip, onClick, testID, testIDProperties }, ref) => {
forwardRef<HTMLButtonElement, Props>(({ Icon, color, active, tooltip, onClick, testID, testIDProperties }, ref) => {
const disabled = !onClick;

const tippyProps = useMemo(
Expand All @@ -35,28 +42,18 @@ export const IconButton = memo(

const finalRef = useMemo(() => combineRefs<HTMLButtonElement>(ref, tippyRef), [ref, tippyRef]);

const designClassName = useMemo(() => {
if (active) return 'bg-grey-4 text-grey-1 shadow-none';
const colorClassName = useMemo(() => {
if (active) return clsx(ACTIVE_STYLED_BUTTON_COLORS_CLASSNAME, 'shadow-none');

switch (design) {
case 'blue':
return clsx('bg-secondary-low text-secondary', 'hover:bg-secondary-hover-low hover:text-secondary-hover');
case 'orange':
return clsx('bg-primary-low text-primary', 'hover:bg-primary-hover-low hover:text-primary-hover');
case 'red':
return clsx('bg-error text-error-low', 'hover:bg-error-low-hover hover:text-error-hover');
default:
return clsx(
'bg-white text-primary shadow-bottom',
'hover:bg-grey-4 hover:shadow-none hover:text-primary-hover'
);
}
}, [active, design]);
return color
? getStyledButtonColorsClassNames(MAP_TO_STYLED_BUTTON_COLORS[color])
: 'bg-white text-primary shadow-bottom hover:bg-grey-4 hover:shadow-none hover:text-primary-hover';
}, [active, color]);

return (
<Button
ref={finalRef}
className={clsx('p-1 rounded-md', designClassName)}
className={clsx('p-1 rounded-md', colorClassName)}
disabled={disabled || active}
onClick={onClick}
testID={testID}
Expand All @@ -67,3 +64,9 @@ export const IconButton = memo(
);
})
);

const MAP_TO_STYLED_BUTTON_COLORS: Record<Color, StyledButtonColor> = {
blue: 'secondary-low',
orange: 'primary-low',
red: 'red-low'
};
50 changes: 29 additions & 21 deletions src/app/atoms/StyledButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,50 @@ import clsx from 'clsx';
import { Button, ButtonProps } from './Button';

type Size = 'L' | 'M' | 'S';
type Color = 'primary' | 'primary-low' | 'secondary' | 'secondary-low' | 'red' | 'red-low';

export type StyledButtonColor = 'primary' | 'primary-low' | 'secondary' | 'secondary-low' | 'red' | 'red-low';

interface Props extends ButtonProps {
size: Size;
color: Color;
color: StyledButtonColor;
active?: boolean;
}

export const StyledButton = React.forwardRef<HTMLButtonElement, Props>(
({ size, color, active, disabled, ...otherProps }, ref) => {
const colorClassName = useMemo(() => {
if (active) return 'bg-grey-4 text-grey-1';
if (disabled) return 'bg-disable text-grey-3';

switch (color) {
case 'primary':
return 'bg-primary hover:bg-primary-hover text-white';
case 'primary-low':
return 'bg-primary-low hover:bg-primary-hover-low text-primary hover:text-primary-hover';
case 'secondary':
return 'bg-secondary hover:bg-secondary-hover text-white';
case 'secondary-low':
return 'bg-secondary-low hover:bg-secondary-hover-low text-secondary hover:text-secondary-hover';
case 'red':
return 'bg-red hover:bg-red-hover text-white';
case 'red-low':
return 'bg-error-low hover:bg-error-low-hover text-error hover:text-error-hover';
}
}, [active, disabled, color]);
const colorClassName = useMemo(
() => getStyledButtonColorsClassNames(color, active, disabled),
[active, disabled, color]
);

return (
<Button ref={ref} className={clsx(SIZE_CLASSNAME[size], colorClassName)} disabled={disabled} {...otherProps} />
);
}
);

export const ACTIVE_STYLED_BUTTON_COLORS_CLASSNAME = 'bg-grey-4 text-grey-1';

export function getStyledButtonColorsClassNames(color: StyledButtonColor, active = false, disabled = false) {
if (active) return ACTIVE_STYLED_BUTTON_COLORS_CLASSNAME;
if (disabled) return 'bg-disable text-grey-3';

switch (color) {
case 'primary':
return 'bg-primary hover:bg-primary-hover text-white';
case 'primary-low':
return 'bg-primary-low hover:bg-primary-hover-low text-primary hover:text-primary-hover';
case 'secondary':
return 'bg-secondary hover:bg-secondary-hover text-white';
case 'secondary-low':
return 'bg-secondary-low hover:bg-secondary-hover-low text-secondary hover:text-secondary-hover';
case 'red':
return 'bg-red hover:bg-red-hover text-white';
case 'red-low':
return 'bg-error-low hover:bg-error-low-hover text-error hover:text-error-hover';
}
}

const SIZE_CLASSNAME: Record<Size, string> = {
L: 'py-2 px-3 rounded-lg text-font-regular-bold',
M: 'py-1 px-2 rounded-lg text-font-medium-bold',
Expand Down
Loading

0 comments on commit 8bde9d9

Please sign in to comment.