Commit 29aa85f3 authored by tom's avatar tom

Initialize the chakra update and ensure the login page does not crash on load.

parent db6708e1
import { Alert as ChakraAlert } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
export interface AlertProps extends Omit<ChakraAlert.RootProps, 'title'> {
startElement?: React.ReactNode;
endElement?: React.ReactNode;
title?: React.ReactNode;
icon?: React.ReactElement;
closable?: boolean;
onClose?: () => void;
}
export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
function Alert(props, ref) {
const {
title,
children,
icon,
closable,
onClose,
startElement,
endElement,
...rest
} = props;
return (
<ChakraAlert.Root ref={ ref } { ...rest }>
{ startElement || <ChakraAlert.Indicator>{ icon }</ChakraAlert.Indicator> }
{ children ? (
<ChakraAlert.Content>
<ChakraAlert.Title>{ title }</ChakraAlert.Title>
<ChakraAlert.Description>{ children }</ChakraAlert.Description>
</ChakraAlert.Content>
) : (
<ChakraAlert.Title flex="1">{ title }</ChakraAlert.Title>
) }
{ endElement }
{ closable && (
<CloseButton
size="sm"
pos="relative"
top="-2"
insetEnd="-2"
alignSelf="flex-start"
onClick={ onClose }
/>
) }
</ChakraAlert.Root>
);
},
);
'use client';
import type { GroupProps, SlotRecipeProps } from '@chakra-ui/react';
import { Avatar as ChakraAvatar, Group } from '@chakra-ui/react';
import * as React from 'react';
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>;
export interface AvatarProps extends ChakraAvatar.RootProps {
name?: string;
src?: string;
srcSet?: string;
loading?: ImageProps['loading'];
icon?: React.ReactElement;
fallback?: React.ReactNode;
}
export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
function Avatar(props, ref) {
const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
props;
return (
<ChakraAvatar.Root ref={ ref } { ...rest }>
<AvatarFallback name={ name } icon={ icon }>
{ fallback }
</AvatarFallback>
<ChakraAvatar.Image src={ src } srcSet={ srcSet } loading={ loading }/>
{ children }
</ChakraAvatar.Root>
);
},
);
interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
name?: string;
icon?: React.ReactElement;
}
const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>(
function AvatarFallback(props, ref) {
const { name, icon, children, ...rest } = props;
return (
<ChakraAvatar.Fallback ref={ ref } { ...rest }>
{ children }
{ name != null && children == null && <>{ getInitials(name) }</> }
{ name == null && children == null && (
<ChakraAvatar.Icon asChild={ Boolean(icon) }>{ icon }</ChakraAvatar.Icon>
) }
</ChakraAvatar.Fallback>
);
},
);
function getInitials(name: string) {
const names = name.trim().split(' ');
const firstName = names[0] != null ? names[0] : '';
const lastName = names.length > 1 ? names[names.length - 1] : '';
return firstName && lastName ?
`${ firstName.charAt(0) }${ lastName.charAt(0) }` :
firstName.charAt(0);
}
interface AvatarGroupProps extends GroupProps, SlotRecipeProps<'avatar'> {}
export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
function AvatarGroup(props, ref) {
const { size, variant, borderless, ...rest } = props;
return (
<ChakraAvatar.PropsProvider value={{ size, variant, borderless }}>
<Group gap="0" spaceX="-3" ref={ ref } { ...rest }/>
</ChakraAvatar.PropsProvider>
);
},
);
import type { ButtonProps as ChakraButtonProps } from '@chakra-ui/react';
import {
AbsoluteCenter,
Button as ChakraButton,
Span,
Spinner,
} from '@chakra-ui/react';
import * as React from 'react';
interface ButtonLoadingProps {
loading?: boolean;
loadingText?: React.ReactNode;
}
export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {}
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
function Button(props, ref) {
const { loading, disabled, loadingText, children, ...rest } = props;
return (
<ChakraButton disabled={ loading || disabled } ref={ ref } { ...rest }>
{ loading && !loadingText ? (
<>
<AbsoluteCenter display="inline-flex">
<Spinner size="inherit" color="inherit"/>
</AbsoluteCenter>
<Span opacity={ 0 }>{ children }</Span>
</>
) : loading && loadingText ? (
<>
<Spinner size="inherit" color="inherit"/>
{ loadingText }
</>
) : (
children
) }
</ChakraButton>
);
},
);
import { Checkbox as ChakraCheckbox } from '@chakra-ui/react';
import * as React from 'react';
export interface CheckboxProps extends ChakraCheckbox.RootProps {
icon?: React.ReactNode;
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
rootRef?: React.Ref<HTMLLabelElement>;
}
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
function Checkbox(props, ref) {
const { icon, children, inputProps, rootRef, ...rest } = props;
return (
<ChakraCheckbox.Root ref={ rootRef } { ...rest }>
<ChakraCheckbox.HiddenInput ref={ ref } { ...inputProps }/>
<ChakraCheckbox.Control>
{ icon || <ChakraCheckbox.Indicator/> }
</ChakraCheckbox.Control>
{ children != null && (
<ChakraCheckbox.Label>{ children }</ChakraCheckbox.Label>
) }
</ChakraCheckbox.Root>
);
},
);
import type { ButtonProps } from '@chakra-ui/react';
import { IconButton as ChakraIconButton } from '@chakra-ui/react';
import * as React from 'react';
import { LuX } from 'react-icons/lu';
export type CloseButtonProps = ButtonProps;
export const CloseButton = React.forwardRef<
HTMLButtonElement,
CloseButtonProps
>(function CloseButton(props, ref) {
return (
<ChakraIconButton variant="ghost" aria-label="Close" ref={ ref } { ...props }>
{ props.children ?? <LuX/> }
</ChakraIconButton>
);
});
'use client';
import { ThemeProvider, useTheme } from 'next-themes';
import type { ThemeProviderProps } from 'next-themes';
import * as React from 'react';
export interface ColorModeProviderProps extends ThemeProviderProps {}
export type ColorMode = 'light' | 'dark';
export function ColorModeProvider(props: ColorModeProviderProps) {
return (
<ThemeProvider attribute="class" scriptProps={{ 'data-cfasync': 'false' }} disableTransitionOnChange { ...props }/>
);
}
export function useColorMode() {
const { resolvedTheme, setTheme } = useTheme();
const toggleColorMode = () => {
setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
};
return {
colorMode: resolvedTheme as ColorMode,
setColorMode: setTheme,
toggleColorMode,
};
}
export function useColorModeValue<T>(light: T, dark: T) {
const { colorMode } = useColorMode();
return colorMode === 'light' ? light : dark;
}
import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
interface DialogContentProps extends ChakraDialog.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
backdrop?: boolean;
}
export const DialogContent = React.forwardRef<
HTMLDivElement,
DialogContentProps
>(function DialogContent(props, ref) {
const {
children,
portalled = true,
portalRef,
backdrop = true,
...rest
} = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
{ backdrop && <ChakraDialog.Backdrop/> }
<ChakraDialog.Positioner>
<ChakraDialog.Content ref={ ref } { ...rest } asChild={ false }>
{ children }
</ChakraDialog.Content>
</ChakraDialog.Positioner>
</Portal>
);
});
export const DialogCloseTrigger = React.forwardRef<
HTMLButtonElement,
ChakraDialog.CloseTriggerProps
>(function DialogCloseTrigger(props, ref) {
return (
<ChakraDialog.CloseTrigger
position="absolute"
top="2"
insetEnd="2"
{ ...props }
asChild
>
<CloseButton size="sm" ref={ ref }>
{ props.children }
</CloseButton>
</ChakraDialog.CloseTrigger>
);
});
export const DialogRoot = ChakraDialog.Root;
export const DialogFooter = ChakraDialog.Footer;
export const DialogHeader = ChakraDialog.Header;
export const DialogBody = ChakraDialog.Body;
export const DialogBackdrop = ChakraDialog.Backdrop;
export const DialogTitle = ChakraDialog.Title;
export const DialogDescription = ChakraDialog.Description;
export const DialogTrigger = ChakraDialog.Trigger;
export const DialogActionTrigger = ChakraDialog.ActionTrigger;
import { Drawer as ChakraDrawer, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
interface DrawerContentProps extends ChakraDrawer.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
offset?: ChakraDrawer.ContentProps['padding'];
}
export const DrawerContent = React.forwardRef<
HTMLDivElement,
DrawerContentProps
>(function DrawerContent(props, ref) {
const { children, portalled = true, portalRef, offset, ...rest } = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraDrawer.Positioner padding={ offset }>
<ChakraDrawer.Content ref={ ref } { ...rest } asChild={ false }>
{ children }
</ChakraDrawer.Content>
</ChakraDrawer.Positioner>
</Portal>
);
});
export const DrawerCloseTrigger = React.forwardRef<
HTMLButtonElement,
ChakraDrawer.CloseTriggerProps
>(function DrawerCloseTrigger(props, ref) {
return (
<ChakraDrawer.CloseTrigger
position="absolute"
top="2"
insetEnd="2"
{ ...props }
asChild
>
<CloseButton size="sm" ref={ ref }/>
</ChakraDrawer.CloseTrigger>
);
});
export const DrawerTrigger = ChakraDrawer.Trigger;
export const DrawerRoot = ChakraDrawer.Root;
export const DrawerFooter = ChakraDrawer.Footer;
export const DrawerHeader = ChakraDrawer.Header;
export const DrawerBody = ChakraDrawer.Body;
export const DrawerBackdrop = ChakraDrawer.Backdrop;
export const DrawerDescription = ChakraDrawer.Description;
export const DrawerTitle = ChakraDrawer.Title;
export const DrawerActionTrigger = ChakraDrawer.ActionTrigger;
import { Field as ChakraField } from '@chakra-ui/react';
import * as React from 'react';
export interface FieldProps extends Omit<ChakraField.RootProps, 'label'> {
label?: React.ReactNode;
helperText?: React.ReactNode;
errorText?: React.ReactNode;
optionalText?: React.ReactNode;
}
export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
function Field(props, ref) {
const { label, children, helperText, errorText, optionalText, ...rest } =
props;
return (
<ChakraField.Root ref={ ref } { ...rest }>
{ label && (
<ChakraField.Label>
{ label }
<ChakraField.RequiredIndicator fallback={ optionalText }/>
</ChakraField.Label>
) }
{ children }
{ helperText && (
<ChakraField.HelperText>{ helperText }</ChakraField.HelperText>
) }
{ errorText && (
<ChakraField.ErrorText>{ errorText }</ChakraField.ErrorText>
) }
</ChakraField.Root>
);
},
);
import type { BoxProps, InputElementProps } from '@chakra-ui/react';
import { Group, InputElement } from '@chakra-ui/react';
import * as React from 'react';
export interface InputGroupProps extends BoxProps {
startElementProps?: InputElementProps;
endElementProps?: InputElementProps;
startElement?: React.ReactNode;
endElement?: React.ReactNode;
children: React.ReactElement<InputElementProps>;
startOffset?: InputElementProps['paddingStart'];
endOffset?: InputElementProps['paddingEnd'];
}
export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
function InputGroup(props, ref) {
const {
startElement,
startElementProps,
endElement,
endElementProps,
children,
startOffset = '6px',
endOffset = '6px',
...rest
} = props;
const child =
React.Children.only<React.ReactElement<InputElementProps>>(children);
return (
<Group ref={ ref } { ...rest }>
{ startElement && (
<InputElement pointerEvents="none" { ...startElementProps }>
{ startElement }
</InputElement>
) }
{ React.cloneElement(child, {
...(startElement && {
ps: `calc(var(--input-height) - ${ startOffset })`,
}),
...(endElement && { pe: `calc(var(--input-height) - ${ endOffset })` }),
...children.props,
}) }
{ endElement && (
<InputElement placement="end" { ...endElementProps }>
{ endElement }
</InputElement>
) }
</Group>
);
},
);
import { Popover as ChakraPopover, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
interface PopoverContentProps extends ChakraPopover.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
}
export const PopoverContent = React.forwardRef<
HTMLDivElement,
PopoverContentProps
>(function PopoverContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraPopover.Positioner>
<ChakraPopover.Content ref={ ref } { ...rest }/>
</ChakraPopover.Positioner>
</Portal>
);
});
export const PopoverArrow = React.forwardRef<
HTMLDivElement,
ChakraPopover.ArrowProps
>(function PopoverArrow(props, ref) {
return (
<ChakraPopover.Arrow { ...props } ref={ ref }>
<ChakraPopover.ArrowTip/>
</ChakraPopover.Arrow>
);
});
export const PopoverCloseTrigger = React.forwardRef<
HTMLButtonElement,
ChakraPopover.CloseTriggerProps
>(function PopoverCloseTrigger(props, ref) {
return (
<ChakraPopover.CloseTrigger
position="absolute"
top="1"
insetEnd="1"
{ ...props }
asChild
ref={ ref }
>
<CloseButton size="sm"/>
</ChakraPopover.CloseTrigger>
);
});
export const PopoverTitle = ChakraPopover.Title;
export const PopoverDescription = ChakraPopover.Description;
export const PopoverFooter = ChakraPopover.Footer;
export const PopoverHeader = ChakraPopover.Header;
export const PopoverRoot = ChakraPopover.Root;
export const PopoverBody = ChakraPopover.Body;
export const PopoverTrigger = ChakraPopover.Trigger;
'use client';
import { ChakraProvider } from '@chakra-ui/react';
import theme from 'chakra/theme/theme';
import {
ColorModeProvider,
type ColorModeProviderProps,
} from './color-mode';
export function Provider(props: ColorModeProviderProps) {
return (
<ChakraProvider value={ theme }>
<ColorModeProvider { ...props }/>
</ChakraProvider>
);
}
import { RadioGroup as ChakraRadioGroup } from '@chakra-ui/react';
import * as React from 'react';
export interface RadioProps extends ChakraRadioGroup.ItemProps {
rootRef?: React.Ref<HTMLDivElement>;
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
}
export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
function Radio(props, ref) {
const { children, inputProps, rootRef, ...rest } = props;
return (
<ChakraRadioGroup.Item ref={ rootRef } { ...rest }>
<ChakraRadioGroup.ItemHiddenInput ref={ ref } { ...inputProps }/>
<ChakraRadioGroup.ItemIndicator/>
{ children && (
<ChakraRadioGroup.ItemText>{ children }</ChakraRadioGroup.ItemText>
) }
</ChakraRadioGroup.Item>
);
},
);
export const RadioGroup = ChakraRadioGroup.Root;
import { Slider as ChakraSlider, For, HStack } from '@chakra-ui/react';
import * as React from 'react';
export interface SliderProps extends ChakraSlider.RootProps {
marks?: Array<number | { value: number; label: React.ReactNode }>;
label?: React.ReactNode;
showValue?: boolean;
}
export const Slider = React.forwardRef<HTMLDivElement, SliderProps>(
function Slider(props, ref) {
const { marks: marksProp, label, showValue, ...rest } = props;
const value = props.defaultValue ?? props.value;
const marks = marksProp?.map((mark) => {
if (typeof mark === 'number') return { value: mark, label: undefined };
return mark;
});
const hasMarkLabel = Boolean(marks?.some((mark) => mark.label));
return (
<ChakraSlider.Root ref={ ref } thumbAlignment="center" { ...rest }>
{ label && !showValue && (
<ChakraSlider.Label>{ label }</ChakraSlider.Label>
) }
{ label && showValue && (
<HStack justify="space-between">
<ChakraSlider.Label>{ label }</ChakraSlider.Label>
<ChakraSlider.ValueText/>
</HStack>
) }
<ChakraSlider.Control data-has-mark-label={ hasMarkLabel || undefined }>
<ChakraSlider.Track>
<ChakraSlider.Range/>
</ChakraSlider.Track>
<SliderThumbs value={ value }/>
<SliderMarks marks={ marks }/>
</ChakraSlider.Control>
</ChakraSlider.Root>
);
},
);
function SliderThumbs(props: { value?: Array<number> }) {
const { value } = props;
return (
<For each={ value }>
{ (_, index) => (
<ChakraSlider.Thumb key={ index } index={ index }>
<ChakraSlider.HiddenInput/>
</ChakraSlider.Thumb>
) }
</For>
);
}
interface SliderMarksProps {
marks?: Array<number | { value: number; label: React.ReactNode }>;
}
const SliderMarks = React.forwardRef<HTMLDivElement, SliderMarksProps>(
function SliderMarks(props, ref) {
const { marks } = props;
if (!marks?.length) return null;
return (
<ChakraSlider.MarkerGroup ref={ ref }>
{ marks.map((mark, index) => {
const value = typeof mark === 'number' ? mark : mark.value;
const label = typeof mark === 'number' ? undefined : mark.label;
return (
<ChakraSlider.Marker key={ index } value={ value }>
<ChakraSlider.MarkerIndicator/>
{ label }
</ChakraSlider.Marker>
);
}) }
</ChakraSlider.MarkerGroup>
);
},
);
'use client';
import {
Toaster as ChakraToaster,
Portal,
Spinner,
Stack,
Toast,
createToaster,
} from '@chakra-ui/react';
import { SECOND } from 'lib/consts';
export const toaster = createToaster({
placement: 'top-end',
pauseOnPageIdle: true,
duration: 5 * SECOND,
});
export const Toaster = () => {
return (
<Portal>
<ChakraToaster toaster={ toaster } insetInline={{ mdDown: '4' }}>
{ (toast) => (
<Toast.Root width={{ md: 'sm' }}>
{ toast.type === 'loading' ? (
<Spinner size="sm" color="blue.solid"/>
) : (
<Toast.Indicator/>
) }
<Stack gap="1" flex="1" maxWidth="100%">
{ toast.title && <Toast.Title>{ toast.title }</Toast.Title> }
{ toast.description && (
<Toast.Description>{ toast.description }</Toast.Description>
) }
</Stack>
{ toast.action && (
<Toast.ActionTrigger>{ toast.action.label }</Toast.ActionTrigger>
) }
{ toast.meta?.closable && <Toast.CloseTrigger/> }
</Toast.Root>
) }
</ChakraToaster>
</Portal>
);
};
import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react';
import * as React from 'react';
export interface TooltipProps extends ChakraTooltip.RootProps {
showArrow?: boolean;
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
content: React.ReactNode;
contentProps?: ChakraTooltip.ContentProps;
disabled?: boolean;
}
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
function Tooltip(props, ref) {
const {
showArrow,
children,
disabled,
portalled,
content,
contentProps,
portalRef,
...rest
} = props;
if (disabled) return children;
return (
<ChakraTooltip.Root { ...rest }>
<ChakraTooltip.Trigger asChild>{ children }</ChakraTooltip.Trigger>
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraTooltip.Positioner>
<ChakraTooltip.Content ref={ ref } { ...contentProps }>
{ showArrow && (
<ChakraTooltip.Arrow>
<ChakraTooltip.ArrowTip/>
</ChakraTooltip.Arrow>
) }
{ content }
</ChakraTooltip.Content>
</ChakraTooltip.Positioner>
</Portal>
</ChakraTooltip.Root>
);
},
);
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
export const radii: TokenDefinition['radii'] = {
none: { value: '0' },
sm: { value: '4px' },
base: { value: '8px' },
md: { value: '12px' },
lg: { value: '16px' },
xl: { value: '24px' },
full: { value: '9999px' },
};
const breakpoints = {
// maybe we need them in future
sm: '415px',
// md: '768px',
lg: '1000px',
xl: '1440px',
'2xl': '1920px',
// these breakpoint are needed just to make others work
'3xl': '3000px',
};
export default breakpoints;
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const colors: TokenDefinition['colors'] = {
green: {
'100': { value: '#C6F6D5' },
'400': { value: '#48BB78' },
'500': { value: '#38A169' },
'600': { value: '#25855A' },
},
blue: {
'50': { value: '#EBF8FF' },
'100': { value: '#BEE3F8' },
'200': { value: '#90CDF4' },
'300': { value: '#63B3ED' },
'400': { value: '#4299E1' },
'500': { value: '#3182CE' },
'600': { value: '#2B6CB0' },
'700': { value: '#2C5282' },
'800': { value: '#2A4365' },
'900': { value: '#1A365D' },
},
red: {
'500': { value: '#E53E3E' },
'100': { value: '#FED7D7' },
},
orange: {
'100': { value: '#FEEBCB' },
},
gray: {
'50': { value: '#F7FAFC' },
'100': { value: '#EDF2F7' },
'200': { value: '#E2E8F0' },
'300': { value: '#CBD5E0' },
'400': { value: '#A0AEC0' },
'500': { value: '#718096' },
'600': { value: '#4A5568' },
'700': { value: '#2D3748' },
'800': { value: '#1A202C' },
'900': { value: '#171923' },
},
black: { value: '#101112' },
white: { value: '#ffffff' },
blackAlpha: {
'50': { value: 'RGBA(16, 17, 18, 0.04)' },
'100': { value: 'RGBA(16, 17, 18, 0.06)' },
'200': { value: 'RGBA(16, 17, 18, 0.08)' },
'300': { value: 'RGBA(16, 17, 18, 0.16)' },
'400': { value: 'RGBA(16, 17, 18, 0.24)' },
'500': { value: 'RGBA(16, 17, 18, 0.36)' },
'600': { value: 'RGBA(16, 17, 18, 0.48)' },
'700': { value: 'RGBA(16, 17, 18, 0.64)' },
'800': { value: 'RGBA(16, 17, 18, 0.80)' },
'900': { value: 'RGBA(16, 17, 18, 0.92)' },
},
github: { value: '#171923' },
telegram: { value: '#2775CA' },
linkedin: { value: '#1564BA' },
discord: { value: '#9747FF' },
slack: { value: '#1BA27A' },
twitter: { value: '#000000' },
opensea: { value: '#2081E2' },
facebook: { value: '#4460A0' },
medium: { value: '#231F20' },
reddit: { value: '#FF4500' },
celo: { value: '#FCFF52' },
};
export default colors;
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const durations: TokenDefinition['durations'] = {
'ultra-fast': { value: '50ms' },
faster: { value: '100ms' },
fast: { value: '150ms' },
normal: { value: '200ms' },
slow: { value: '300ms' },
slower: { value: '400ms' },
'ultra-slow': { value: '500ms' },
};
export default durations;
import { getCSSVar } from '@chakra-ui/styled-system';
import { mode } from '@chakra-ui/theme-tools';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
const scrollbar = (props: StyleFunctionProps) => {
const bgColor = mode('blackAlpha.300', 'whiteAlpha.300')(props);
const resizerUrl = mode('url(/static/resizer_light.png)', 'url(/static/resizer_dark.png)')(props);
return {
'body *::-webkit-scrollbar': {
width: '20px',
},
'body *::-webkit-scrollbar-track': {
backgroundColor: 'transparent',
},
'body *::-webkit-scrollbar-thumb': {
backgroundColor: bgColor,
borderRadius: '20px',
border: `8px solid rgba(0,0,0,0)`,
backgroundClip: 'content-box',
minHeight: '32px',
},
'body *::-webkit-scrollbar-button': {
display: 'none',
},
'body *::-webkit-scrollbar-corner': {
backgroundColor: 'transparent',
},
'body *::-webkit-resizer': {
backgroundImage: resizerUrl,
backgroundSize: '20px',
},
'body *': {
scrollbarWidth: 'thin',
scrollbarColor: `${ getCSSVar(props.theme, 'colors', bgColor) } transparent`,
},
};
};
export default scrollbar;
import type { SemanticTokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const semanticTokens: SemanticTokenDefinition = {
colors: {
divider: {
DEFAULT: { value: '{colors.blackAlpha.200}' },
_dark: { value: '{colors.whiteAlpha.200}' },
},
text: {
DEFAULT: { value: '{colors.blackAlpha.800}' },
_dark: { value: '{colors.whiteAlpha.800}' },
},
text_secondary: {
DEFAULT: { value: '{colors.gray.500}' },
_dark: { value: '{colors.gray.400}' },
},
link: {
DEFAULT: { value: '{colors.blue.600}' },
_dark: { value: '{colors.blue.300}' },
},
link_hovered: {
DEFAULT: { value: '{colors.blue.400}' },
},
icon_link_external: {
DEFAULT: { value: '{colors.gray.300}' },
_dark: { value: '{colors.gray.500}' },
},
icon_info: {
DEFAULT: { value: '{colors.gray.400}' },
_dark: { value: '{colors.gray.500}' },
},
error: {
DEFAULT: { value: '{colors.red.500}' },
_dark: { value: '{colors.red.500}' },
},
dialog_bg: {
DEFAULT: { value: '{colors.white}' },
_dark: { value: '{colors.gray.900}' },
},
},
};
export default semanticTokens;
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const shadows: TokenDefinition['shadows'] = {
action_bar: { value: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)' },
};
export default shadows;
import type { ThemingConfig, TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
import config from 'configs/app';
export const BODY_TYPEFACE = config.UI.fonts.body?.name ?? 'Inter';
export const HEADING_TYPEFACE = config.UI.fonts.heading?.name ?? 'Poppins';
export const fonts: TokenDefinition['fonts'] = {
heading: { value: `${ HEADING_TYPEFACE }, sans-serif` },
body: { value: `${ BODY_TYPEFACE }, sans-serif` },
};
export const textStyles: ThemingConfig['textStyles'] = {
h2: {
fontSize: '32px',
fontWeight: '500',
lineHeight: '40px',
fontFamily: 'heading',
},
h3: {
fontSize: '24px',
fontWeight: '500',
lineHeight: '32px',
fontFamily: 'heading',
},
h4: {
fontSize: 'md',
fontWeight: '500',
lineHeight: '24px',
fontFamily: 'heading',
},
};
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
export const zIndex: TokenDefinition['zIndex'] = {
hide: { value: -1 },
auto: { value: 'auto' },
base: { value: 0 },
docked: { value: 10 },
dropdown: { value: 1000 },
sticky: { value: 1100 },
sticky1: { value: 1101 },
sticky2: { value: 1102 },
banner: { value: 1200 },
overlay: { value: 1300 },
modal: { value: 1400 },
popover: { value: 1500 },
tooltip: { value: 1550 }, // otherwise tooltips will not be visible in modals
skipLink: { value: 1600 },
toast: { value: 1700 },
};
export default zIndex;
import type { StyleFunctionProps, SystemStyleObject } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
import scrollbar from './foundations/scrollbar';
import addressEntity from './globals/address-entity';
import recaptcha from './globals/recaptcha';
// import getDefaultTransitionProps from './utils/getDefaultTransitionProps';
const globalCss: Record<string, SystemStyleObject> = {
body: {
// bg: mode('white', 'black')(props),
// ...getDefaultTransitionProps(),
'-webkit-tap-highlight-color': 'transparent',
'font-variant-ligatures': 'no-contextual',
},
mark: {
// bgColor: mode('green.100', 'green.800')(props),
color: 'inherit',
},
'svg *::selection': {
color: 'none',
background: 'none',
},
form: {
w: '100%',
},
// ...scrollbar(props),
// ...addressEntity(props),
...recaptcha(),
};
export default globalCss;
import { mode } from '@chakra-ui/theme-tools';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
const styles = (props: StyleFunctionProps) => {
return {
'.address-entity': {
'&.address-entity_highlighted': {
_before: {
content: `" "`,
position: 'absolute',
py: 1,
pl: 1,
pr: 0,
top: '-5px',
left: '-5px',
width: `100%`,
height: '100%',
borderRadius: 'base',
borderColor: mode('blue.200', 'blue.600')(props),
borderWidth: '1px',
borderStyle: 'dashed',
bgColor: mode('blue.50', 'blue.900')(props),
zIndex: -1,
},
},
},
'.address-entity_no-copy': {
'&.address-entity_highlighted': {
_before: {
pr: 2,
},
},
},
};
};
export default styles;
const styles = () => {
return {
'.grecaptcha-badge': {
visibility: 'hidden',
},
'div:has(div):has(iframe[title="recaptcha challenge expires in two minutes"])': {
'&::after': {
content: `" "`,
display: 'block',
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
zIndex: 100000,
bgColor: 'blackAlpha.300',
},
},
};
};
export default styles;
import { createSystem, defaultConfig, defineConfig } from '@chakra-ui/react';
// import components from './components/index';
// import config from './config';
import * as borders from './foundations/borders';
import breakpoints from './foundations/breakpoints';
import colors from './foundations/colors';
import durations from './foundations/durations';
import semanticTokens from './foundations/semanticTokens';
import shadows from './foundations/shadows';
import { fonts, textStyles } from './foundations/typography';
import zIndex from './foundations/zIndex';
import globalCss from './globalCss';
const customConfig = defineConfig({
globalCss,
theme: {
breakpoints,
semanticTokens,
textStyles,
tokens: {
...borders,
colors,
durations,
fonts,
shadows,
zIndex,
},
},
// components,
// config,
});
export default createSystem(defaultConfig, customConfig);
......@@ -283,6 +283,7 @@ export default tseslint.config(
'/types/',
[ '/^nextjs/' ],
[
'/^chakra/',
'/^configs/',
'/^data/',
'/^deploy/',
......
import { useBoolean } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query';
import { useToggle } from '@uidotdev/usehooks';
import { useRouter } from 'next/router';
import React, { createContext, useContext, useEffect, useMemo, useCallback } from 'react';
import { useSignMessage } from 'wagmi';
......@@ -13,6 +13,7 @@ import type {
RewardsConfigResponse,
} from 'types/api/rewards';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
......@@ -22,7 +23,6 @@ import * as cookies from 'lib/cookies';
import decodeJWT from 'lib/decodeJWT';
import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import getQueryParamString from 'lib/router/getQueryParamString';
import removeQueryParam from 'lib/router/removeQueryParam';
import useAccount from 'lib/web3/useAccount';
......@@ -114,13 +114,12 @@ export function RewardsContextProvider({ children }: Props) {
const router = useRouter();
const queryClient = useQueryClient();
const apiFetch = useApiFetch();
const toast = useToast();
const { address } = useAccount();
const { signMessageAsync } = useSignMessage();
const profileQuery = useProfileQuery();
const [ isLoginModalOpen, setIsLoginModalOpen ] = useBoolean(false);
const [ isInitialized, setIsInitialized ] = useBoolean(false);
const [ isLoginModalOpen, setIsLoginModalOpen ] = useToggle(false);
const [ isInitialized, setIsInitialized ] = useToggle(false);
const [ apiToken, setApiToken ] = React.useState<string | undefined>();
// Initialize state with the API token from cookies
......@@ -131,7 +130,7 @@ export function RewardsContextProvider({ children }: Props) {
if (registeredAddress === profileQuery.data?.address_hash) {
setApiToken(token);
}
setIsInitialized.on();
setIsInitialized(true);
}
}, [ setIsInitialized, profileQuery ]);
......@@ -187,22 +186,18 @@ export function RewardsContextProvider({ children }: Props) {
cookies.set(cookies.NAMES.REWARDS_REFERRAL_CODE, refCode);
removeQueryParam(router, 'ref');
if (!apiToken) {
setIsLoginModalOpen.on();
setIsLoginModalOpen(true);
}
}
}, [ router, apiToken, isInitialized, setIsLoginModalOpen ]);
const errorToast = useCallback((error: unknown) => {
const apiError = getErrorObjPayload<{ message: string }>(error);
toast({
position: 'top-right',
toaster.error({
title: 'Error',
description: apiError?.message || getErrorMessage(error) || 'Something went wrong. Try again later.',
status: 'error',
variant: 'subtle',
isClosable: true,
});
}, [ toast ]);
}, [ ]);
// Login to the rewards program
const login = useCallback(async(refCode: string) => {
......@@ -254,6 +249,14 @@ export function RewardsContextProvider({ children }: Props) {
}
}, [ apiFetch, errorToast, fetchParams ]);
const openLoginModal = React.useCallback(() => {
setIsLoginModalOpen(true);
}, [ setIsLoginModalOpen ]);
const closeLoginModal = React.useCallback(() => {
setIsLoginModalOpen(false);
}, [ setIsLoginModalOpen ]);
const value = useMemo(() => {
if (!feature.isEnabled) {
return initialState;
......@@ -267,14 +270,15 @@ export function RewardsContextProvider({ children }: Props) {
apiToken,
isInitialized,
isLoginModalOpen,
openLoginModal: setIsLoginModalOpen.on,
closeLoginModal: setIsLoginModalOpen.off,
openLoginModal,
closeLoginModal,
login,
claim,
};
}, [
isLoginModalOpen, setIsLoginModalOpen, balancesQuery, dailyRewardQuery, checkUserQuery,
balancesQuery, dailyRewardQuery, checkUserQuery,
apiToken, login, claim, referralsQuery, rewardsConfigQuery, isInitialized,
isLoginModalOpen, openLoginModal, closeLoginModal,
]);
return (
......
import type { ColorMode } from '@chakra-ui/react';
import { useColorMode } from '@chakra-ui/react';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router';
import React from 'react';
import type { ColorMode } from 'chakra/components/color-mode';
import { useColorMode } from 'chakra/components/color-mode';
import config from 'configs/app';
import * as cookies from 'lib/cookies';
import getQueryParamString from 'lib/router/getQueryParamString';
......
......@@ -70,8 +70,7 @@ export function app(): CspDev.DirectiveDescriptor {
config.app.isDev ? KEY_WORDS.UNSAFE_EVAL : '',
// hash of ColorModeScript: system + dark
'\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'',
'\'sha256-9A7qFFHmxdWjZMQmfzYD2XWaNHLu1ZmQB0Ds4Go764k=\'',
'\'sha256-yYJq8IP5/WhJj6zxyTmujEqBFs/MufRufp2QKJFU76M=\'',
// CapybaraRunner
'\'sha256-5+YTmTcBwCYdJ8Jetbr6kyjGp0Ry/H7ptpoun6CrSwQ=\'',
......
......@@ -40,11 +40,10 @@
"@blockscout/bens-types": "1.4.1",
"@blockscout/stats-types": "2.0.0",
"@blockscout/visualizer-types": "0.2.0",
"@chakra-ui/react": "2.7.1",
"@chakra-ui/react": "3.2.5",
"@chakra-ui/theme-tools": "^2.0.18",
"@cloudnouns/kit": "1.1.6",
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@emotion/react": "11.14.0",
"@growthbook/growthbook-react": "0.21.0",
"@helia/verified-fetch": "2.0.1",
"@hypelab/sdk-react": "^1.0.0",
......@@ -60,15 +59,16 @@
"@opentelemetry/sdk-node": "0.49.1",
"@opentelemetry/sdk-trace-node": "1.22.0",
"@opentelemetry/semantic-conventions": "1.22.0",
"@rollbar/react": "0.12.0-beta",
"@scure/base": "1.1.9",
"@reown/appkit": "1.6.0",
"@reown/appkit-adapter-wagmi": "1.6.0",
"@rollbar/react": "0.12.0-beta",
"@scure/base": "1.1.9",
"@slise/embed-react": "^2.2.0",
"@tanstack/react-query": "5.55.4",
"@tanstack/react-query-devtools": "5.55.4",
"@types/papaparse": "^5.3.5",
"@types/react-scroll": "^1.8.4",
"@uidotdev/usehooks": "2.4.1",
"airtable": "^0.12.2",
"bignumber.js": "^9.1.0",
"blo": "^1.1.1",
......@@ -79,7 +79,6 @@
"dayjs": "^1.11.5",
"dom-to-image": "^2.6.0",
"focus-visible": "^5.2.0",
"framer-motion": "^6.5.1",
"getit-sdk": "^1.0.4",
"gradient-avatar": "git+https://github.com/blockscout/gradient-avatar.git",
"graphiql": "^2.2.0",
......@@ -91,6 +90,7 @@
"mixpanel-browser": "^2.47.0",
"monaco-editor": "^0.34.1",
"next": "15.0.3",
"next-themes": "0.4.4",
"nextjs-routes": "^1.0.8",
"node-fetch": "^3.2.9",
"papaparse": "^5.3.2",
......@@ -105,6 +105,7 @@
"react-dom": "18.3.1",
"react-google-recaptcha": "3.1.0",
"react-hook-form": "7.52.1",
"react-icons": "5.4.0",
"react-identicons": "^1.2.5",
"react-intersection-observer": "^9.5.2",
"react-jazzicon": "^1.0.4",
......@@ -119,6 +120,7 @@
"xss": "^1.0.14"
},
"devDependencies": {
"@chakra-ui/cli": "3.2.5",
"@eslint/compat": "1.2.2",
"@eslint/js": "9.14.0",
"@next/eslint-plugin-next": "15.0.3",
......
import { type ChakraProps } from '@chakra-ui/react';
import type { HTMLChakraProps } from '@chakra-ui/react';
import { GrowthBookProvider } from '@growthbook/growthbook-react';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
......@@ -7,10 +7,11 @@ import React from 'react';
import type { NextPageWithLayout } from 'nextjs/types';
import { Provider as ChakraProvider } from 'chakra/components/provider';
import { Toaster } from 'chakra/components/toaster';
import config from 'configs/app';
import useQueryClientConfig from 'lib/api/useQueryClientConfig';
import { AppContextProvider } from 'lib/contexts/app';
import { ChakraProvider } from 'lib/contexts/chakra';
import { MarketplaceContextProvider } from 'lib/contexts/marketplace';
import { RewardsContextProvider } from 'lib/contexts/rewards';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
......@@ -34,7 +35,7 @@ type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
const ERROR_SCREEN_STYLES: ChakraProps = {
const ERROR_SCREEN_STYLES: HTMLChakraProps<'div'> = {
h: '100vh',
display: 'flex',
flexDirection: 'column',
......@@ -56,7 +57,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>);
return (
<ChakraProvider cookies={ pageProps.cookies }>
<ChakraProvider>
<RollbarProvider config={ rollbarConfig }>
<AppErrorBoundary
{ ...ERROR_SCREEN_STYLES }
......@@ -72,6 +73,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
<MarketplaceContextProvider>
<SettingsContextProvider>
{ getLayout(<Component { ...pageProps }/>) }
<Toaster/>
{ config.features.rewards.isEnabled && <RewardsLoginModal/> }
</SettingsContextProvider>
</MarketplaceContextProvider>
......
import { ColorModeScript } from '@chakra-ui/react';
import type { DocumentContext } from 'next/document';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react';
......@@ -7,7 +6,6 @@ import logRequestFromBot from 'nextjs/utils/logRequestFromBot';
import * as serverTiming from 'nextjs/utils/serverTiming';
import config from 'configs/app';
import theme from 'theme/theme';
import * as svgSprite from 'ui/shared/IconSvg';
class MyDocument extends Document {
......@@ -57,7 +55,6 @@ class MyDocument extends Document {
<link rel="preload" as="image" href={ svgSprite.href }/>
</Head>
<body>
<ColorModeScript initialColorMode={ theme.config.initialColorMode }/>
<Main/>
<NextScript/>
</body>
......
......@@ -8,8 +8,8 @@
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
......
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Code, Flex, Box } from '@chakra-ui/react';
import { VStack, Textarea, Button, Alert, Code, Flex, Box } from '@chakra-ui/react';
import mixpanel from 'mixpanel-browser';
import type { ChangeEvent } from 'react';
import React from 'react';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app';
import * as cookies from 'lib/cookies';
import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useToast from 'lib/hooks/useToast';
import { useRollbar } from 'lib/rollbar';
import PageTitle from 'ui/shared/Page/PageTitle';
const Login = () => {
const rollbar = useRollbar();
const toast = useToast();
const [ num, setNum ] = useGradualIncrement(0);
const testFeature = useFeatureValue('test_value', 'fallback');
......@@ -41,18 +40,18 @@ const Login = () => {
const handleSetTokenClick = React.useCallback(() => {
cookies.set(cookies.NAMES.API_TOKEN, token);
setToken('');
toast({
position: 'top-right',
toaster.create({
placement: 'top-end',
title: 'Success 🥳',
description: 'Successfully set cookie',
status: 'success',
variant: 'subtle',
isClosable: true,
onCloseComplete: () => {
setFormVisibility(false);
type: 'success',
onStatusChange: (details) => {
if (details.status === 'unmounted') {
setFormVisibility(false);
}
},
});
}, [ toast, token ]);
}, [ token ]);
const handleNumIncrement = React.useCallback(() => {
for (let index = 0; index < 5; index++) {
......@@ -65,16 +64,16 @@ const Login = () => {
<PageTitle title="Login page 😂"/>
{ isFormVisible && (
<>
<Alert status="error" flexDirection="column" alignItems="flex-start">
<AlertTitle fontSize="md">
<Alert.Root status="error" flexDirection="column" alignItems="flex-start">
<Alert.Title fontSize="md">
!!! Temporary solution for authentication on localhost !!!
</AlertTitle>
<AlertDescription mt={ 3 }>
</Alert.Title>
<Alert.Description mt={ 3 }>
To Sign in go to production instance first, sign in there, copy obtained API token from cookie
<Code ml={ 1 }>{ cookies.NAMES.API_TOKEN }</Code> and paste it in the form below. After submitting the form you should be successfully
authenticated in current environment
</AlertDescription>
</Alert>
</Alert.Description>
</Alert.Root>
<Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/>
<Button onClick={ handleSetTokenClick }>Set cookie</Button>
</>
......
import { chakra, Flex } from '@chakra-ui/react';
import type { GroupBase, SelectComponentsConfig, SingleValueProps } from 'chakra-react-select';
import { chakraComponents } from 'chakra-react-select';
import _capitalize from 'lodash/capitalize';
import React from 'react';
import { useFormContext } from 'react-hook-form';
......@@ -9,6 +7,8 @@ import type { FormFields } from '../types';
import type { PublicTagType } from 'types/api/addressMetadata';
import type { Option } from 'ui/shared/forms/inputs/select/types';
import { chakraComponents } from 'chakra-react-select';
import type { GroupBase, SelectComponentsConfig, SingleValueProps } from 'chakra-react-select';
import FormFieldFancySelect from 'ui/shared/forms/fields/FormFieldFancySelect';
import IconSvg from 'ui/shared/IconSvg';
......
import { Box, useColorModeValue } from '@chakra-ui/react';
import { Box } from '@chakra-ui/react';
import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
interface Props {
children: React.ReactNode;
}
......
import { Button, Text } from '@chakra-ui/react';
import React from 'react';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app';
import buildUrl from 'lib/api/buildUrl';
import useFetch from 'lib/hooks/useFetch';
import useToast from 'lib/hooks/useToast';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha';
......@@ -12,7 +12,6 @@ import AppErrorIcon from '../AppErrorIcon';
import AppErrorTitle from '../AppErrorTitle';
const AppErrorTooManyRequests = () => {
const toast = useToast();
const fetch = useFetch();
const recaptcha = useReCaptcha();
......@@ -32,16 +31,14 @@ const AppErrorTooManyRequests = () => {
window.location.reload();
} catch (error) {
toast({
position: 'top-right',
toaster.create({
placement: 'top-end',
title: 'Error',
description: 'Unable to get client key.',
status: 'error',
variant: 'subtle',
isClosable: true,
type: 'error',
});
}
}, [ recaptcha, toast, fetch ]);
}, [ recaptcha, fetch ]);
if (!config.services.reCaptchaV2.siteKey) {
throw new Error('reCAPTCHA V2 site key is not set');
......
import { Box, Text, chakra } from '@chakra-ui/react';
import { keyframes } from '@chakra-ui/system';
import React from 'react';
const runnerAnimation = keyframes`
0% { left: 0%; transform: translateX(-1%); }
100% { left: '100%'; transform: translateX(-99%); }
`;
interface Props {
className?: string;
text?: string;
......@@ -24,7 +18,8 @@ const ContentLoader = ({ className, text }: Props) => {
position: 'absolute',
width: '60px',
height: '6px',
animation: `${ runnerAnimation } 700ms ease-in-out infinite alternate`,
// TODO @tom2drum check this animation
animation: `slide-from-left-full 700ms ease-in-out infinite alternate`,
left: '100%',
top: 0,
backgroundColor: 'blue.300',
......
......@@ -15,7 +15,7 @@ import React, { useCallback, useEffect, useRef } from 'react';
import type { FontFace } from 'use-font-face-observer';
import useFontFaceObserver from 'use-font-face-observer';
import { BODY_TYPEFACE, HEADING_TYPEFACE } from 'theme/foundations/typography';
import { BODY_TYPEFACE, HEADING_TYPEFACE } from 'chakra/theme/foundations/typography';
const TAIL_LENGTH = 4;
const HEAD_MIN_LENGTH = 4;
......
import { Button } from '@chakra-ui/react';
import React from 'react';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel/index';
import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain';
import useProvider from 'lib/web3/useProvider';
......@@ -12,7 +12,6 @@ import IconSvg from 'ui/shared/IconSvg';
const feature = config.features.web3Wallet;
const NetworkAddToWallet = () => {
const toast = useToast();
const { provider, wallet } = useProvider();
const addOrSwitchChain = useAddOrSwitchChain();
......@@ -24,13 +23,9 @@ const NetworkAddToWallet = () => {
try {
await addOrSwitchChain();
toast({
position: 'top-right',
toaster.success({
title: 'Success',
description: 'Successfully added network to your wallet',
status: 'success',
variant: 'subtle',
isClosable: true,
});
mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, {
......@@ -39,16 +34,12 @@ const NetworkAddToWallet = () => {
});
} catch (error) {
toast({
position: 'top-right',
toaster.error({
title: 'Error',
description: (error as Error)?.message || 'Something went wrong',
status: 'error',
variant: 'subtle',
isClosable: true,
});
}
}, [ addOrSwitchChain, provider, toast, wallet ]);
}, [ addOrSwitchChain, provider, wallet ]);
if (!provider || !wallet || !config.chain.rpcUrl || !feature.isEnabled) {
return null;
......
import { Heading, Flex, Tooltip, Link, chakra, Skeleton, useDisclosure } from '@chakra-ui/react';
import { Heading, Flex, Link, chakra, Skeleton, useDisclosure } from '@chakra-ui/react';
import _debounce from 'lodash/debounce';
import React from 'react';
import { Tooltip } from 'chakra/components/tooltip';
import useIsMobile from 'lib/hooks/useIsMobile';
import TextAd from 'ui/shared/ad/TextAd';
import IconSvg from 'ui/shared/IconSvg';
......@@ -38,7 +39,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
mr={ 3 }
my={ 2 }
verticalAlign="text-bottom"
isLoaded={ !props.isLoading }
loading={ props.isLoading }
/>
);
}
......@@ -47,7 +48,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
if ('url' in props) {
return (
<Tooltip label={ props.label }>
<Tooltip content={ props.label }>
<LinkInternal display="inline-flex" href={ props.url } h="40px" mr={ 3 }>
{ icon }
</LinkInternal>
......@@ -56,7 +57,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
}
return (
<Tooltip label={ props.label }>
<Tooltip content={ props.label }>
<Link display="inline-flex" onClick={ props.onClick } h="40px" mr={ 3 }>
{ icon }
</Link>
......@@ -64,7 +65,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
);
};
const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading, afterTitle, beforeTitle, secondRow }: Props) => {
const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading = false, afterTitle, beforeTitle, secondRow }: Props) => {
const tooltip = useDisclosure();
const isMobile = useIsMobile();
const [ isTextTruncated, setIsTextTruncated ] = React.useState(false);
......@@ -101,6 +102,14 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
};
}, [ updatedTruncateState ]);
const handleTooltipOpenChange = React.useCallback((details: { open: boolean }) => {
if (details.open) {
tooltip.onOpen();
} else {
tooltip.onClose();
}
}, [ tooltip ]);
return (
<Flex className={ className } flexDir="column" rowGap={ 3 } mb={ 6 }>
<Flex
......@@ -114,16 +123,16 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
{ backLink && <BackLink { ...backLink } isLoading={ isLoading }/> }
{ beforeTitle }
<Skeleton
isLoaded={ !isLoading }
loading={ isLoading }
overflow="hidden"
>
<Tooltip
label={ title }
isOpen={ tooltip.isOpen }
onClose={ tooltip.onClose }
maxW={{ base: 'calc(100vw - 32px)', lg: '500px' }}
content={ title }
open={ tooltip.open }
onOpenChange={ handleTooltipOpenChange }
contentProps={{ maxW: { base: 'calc(100vw - 32px)', lg: '500px' } }}
closeOnScroll={ isMobile ? true : false }
isDisabled={ !isTextTruncated }
disabled={ !isTextTruncated }
>
<Heading
ref={ headingRef }
......@@ -154,7 +163,7 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
{ withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto" w={{ base: '100%', lg: 'auto' }}/> }
</Flex>
{ secondRow && (
<Skeleton isLoaded={ !isLoading } alignItems="center" minH={ 10 } overflow="hidden" display="flex" _empty={{ display: 'none' }}>
<Skeleton loading={ isLoading } alignItems="center" minH={ 10 } overflow="hidden" display="flex" _empty={{ display: 'none' }}>
{ secondRow }
</Skeleton>
) }
......
......@@ -4,7 +4,7 @@ import debounce from 'lodash/debounce';
import React from 'react';
import useFontFaceObserver from 'use-font-face-observer';
import { BODY_TYPEFACE } from 'theme/foundations/typography';
import { BODY_TYPEFACE } from 'chakra/theme/foundations/typography';
interface Props {
children: React.ReactNode;
......
import { useColorMode } from '@chakra-ui/react';
import { createAppKit, useAppKitTheme } from '@reown/appkit/react';
import React from 'react';
import { WagmiProvider } from 'wagmi';
import { useColorMode } from 'chakra/components/color-mode';
import colors from 'chakra/theme/foundations/colors';
import { BODY_TYPEFACE } from 'chakra/theme/foundations/typography';
import zIndex from 'chakra/theme/foundations/zIndex';
import config from 'configs/app';
import currentChain from 'lib/web3/currentChain';
import wagmiConfig from 'lib/web3/wagmiConfig';
import colors from 'theme/foundations/colors';
import { BODY_TYPEFACE } from 'theme/foundations/typography';
import zIndices from 'theme/foundations/zIndices';
const feature = config.features.blockchainInteraction;
......@@ -37,9 +37,9 @@ const init = () => {
},
themeVariables: {
'--w3m-font-family': `${ BODY_TYPEFACE }, sans-serif`,
'--w3m-accent': colors.blue[600],
'--w3m-accent': colors.blue[600].value,
'--w3m-border-radius-master': '2px',
'--w3m-z-index': zIndices.popover,
'--w3m-z-index': zIndex.popover.value,
},
featuredWalletIds: [],
allowUnsupportedChain: true,
......@@ -66,7 +66,7 @@ const Web3ModalProvider = ({ children }: Props) => {
const { setThemeMode } = useAppKitTheme();
React.useEffect(() => {
setThemeMode(colorMode);
setThemeMode(colorMode ?? 'light');
}, [ colorMode, setThemeMode ]);
return (
......
import { FormControl, useToken, useColorMode } from '@chakra-ui/react';
import type { CSSObjectWithLabel, GroupBase, SingleValue, MultiValue, AsyncProps, Props as SelectProps } from 'chakra-react-select';
import { Select, AsyncSelect } from 'chakra-react-select';
import React from 'react';
import type { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form';
import type { Option } from './types';
import { Select, AsyncSelect } from 'chakra-react-select';
import type { CSSObjectWithLabel, GroupBase, SingleValue, MultiValue, AsyncProps, Props as SelectProps } from 'chakra-react-select';
import FormInputPlaceholder from 'ui/shared/forms/inputs/FormInputPlaceholder';
import { getChakraStyles } from 'ui/shared/forms/inputs/select/utils';
......
import type { ColorMode } from '@chakra-ui/react';
import type { Size, ChakraStylesConfig } from 'chakra-react-select';
import type { Option } from './types';
import type { Size, ChakraStylesConfig } from 'chakra-react-select';
import theme from 'theme/theme';
import getFormStyles from 'theme/utils/getFormStyles';
......
......@@ -2,14 +2,12 @@ import type {
PlacementWithLogical } from '@chakra-ui/react';
import {
Box,
DarkMode,
Flex,
Grid,
PopoverBody,
PopoverContent,
PopoverTrigger,
Portal,
useColorModeValue,
} from '@chakra-ui/react';
import React from 'react';
......@@ -17,6 +15,7 @@ import type { HomeStats } from 'types/api/stats';
import { route } from 'nextjs-routes';
import { useColorModeValue } from 'chakra/components/color-mode';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
import Popover from 'ui/shared/chakra/Popover';
......@@ -56,28 +55,27 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr
<Portal>
<PopoverContent bgColor={ tooltipBg } w="auto">
<PopoverBody color="white">
<DarkMode>
<Flex flexDir="column" fontSize="xs" lineHeight={ 4 } rowGap={ 3 }>
{ data.gas_price_updated_at && (
<Flex justifyContent="space-between">
<Box color="text_secondary">Last update</Box>
<Flex color="text_secondary" justifyContent="flex-end" columnGap={ 2 } ml={ 3 }>
{ dayjs(data.gas_price_updated_at).format('MMM DD, HH:mm:ss') }
{ data.gas_prices_update_in !== 0 &&
{ /* TODO @tom2drum add dark mode */ }
<Flex flexDir="column" fontSize="xs" lineHeight={ 4 } rowGap={ 3 }>
{ data.gas_price_updated_at && (
<Flex justifyContent="space-between">
<Box color="text_secondary">Last update</Box>
<Flex color="text_secondary" justifyContent="flex-end" columnGap={ 2 } ml={ 3 }>
{ dayjs(data.gas_price_updated_at).format('MMM DD, HH:mm:ss') }
{ data.gas_prices_update_in !== 0 &&
<GasInfoUpdateTimer key={ dataUpdatedAt } startTime={ dataUpdatedAt } duration={ data.gas_prices_update_in }/> }
</Flex>
</Flex>
) }
<Grid rowGap={ 2 } columnGap="10px" gridTemplateColumns={ `repeat(${ columnNum }, minmax(min-content, auto))` }>
<GasInfoTooltipRow name="Fast" info={ data.gas_prices.fast }/>
<GasInfoTooltipRow name="Normal" info={ data.gas_prices.average }/>
<GasInfoTooltipRow name="Slow" info={ data.gas_prices.slow }/>
</Grid>
<LinkInternal href={ route({ pathname: '/gas-tracker' }) }>
Gas tracker overview
</LinkInternal>
</Flex>
</DarkMode>
</Flex>
) }
<Grid rowGap={ 2 } columnGap="10px" gridTemplateColumns={ `repeat(${ columnNum }, minmax(min-content, auto))` }>
<GasInfoTooltipRow name="Fast" info={ data.gas_prices.fast }/>
<GasInfoTooltipRow name="Normal" info={ data.gas_prices.average }/>
<GasInfoTooltipRow name="Slow" info={ data.gas_prices.slow }/>
</Grid>
<LinkInternal href={ route({ pathname: '/gas-tracker' }) }>
Gas tracker overview
</LinkInternal>
</Flex>
</PopoverBody>
</PopoverContent>
</Portal>
......
......@@ -4,22 +4,23 @@ import type { Props } from './types';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import HeaderAlert from 'ui/snippets/header/HeaderAlert';
import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
import HeaderMobile from 'ui/snippets/header/HeaderMobile';
// TODO @tom2drum fix main layout
// import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
// import HeaderMobile from 'ui/snippets/header/HeaderMobile';
import * as Layout from './components';
const LayoutDefault = ({ children }: Props) => {
return (
<Layout.Container>
<Layout.TopRow/>
<Layout.NavBar/>
<HeaderMobile/>
{ /* <Layout.TopRow/> */ }
{ /* <Layout.NavBar/>
<HeaderMobile/> */ }
<Layout.MainArea>
<Layout.SideBar/>
{ /* <Layout.SideBar/> */ }
<Layout.MainColumn>
<HeaderAlert/>
<HeaderDesktop/>
{ /* <HeaderDesktop/> */ }
<AppErrorBoundary>
<Layout.Content>
{ children }
......@@ -27,7 +28,7 @@ const LayoutDefault = ({ children }: Props) => {
</AppErrorBoundary>
</Layout.MainColumn>
</Layout.MainArea>
<Layout.Footer/>
{ /* <Layout.Footer/> */ }
</Layout.Container>
);
};
......
import { Box, chakra, useColorModeValue } from '@chakra-ui/react';
import { Box, chakra } from '@chakra-ui/react';
import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
interface Props {
children: React.ReactNode;
className?: string;
......
......@@ -5,10 +5,10 @@ import { FormProvider, useForm } from 'react-hook-form';
import type { EmailFormFields, Screen } from '../types';
import { toaster } from 'chakra/components/toaster';
import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel';
import FormFieldEmail from 'ui/shared/forms/fields/FormFieldEmail';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
......@@ -27,7 +27,6 @@ interface Props {
const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
const apiFetch = useApiFetch();
const toast = useToast();
const recaptcha = useReCaptcha();
const formApi = useForm<EmailFormFields>({
......@@ -64,13 +63,12 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
}
onSubmit({ type: 'otp_code', email: formData.email, isAuth });
} catch (error) {
toast({
status: 'error',
toaster.error({
title: 'Error',
description: getErrorObjPayload<{ message: string }>(error)?.message || getErrorMessage(error) || 'Something went wrong',
});
}
}, [ recaptcha, apiFetch, isAuth, onSubmit, mixpanelConfig?.account_link_info.source, toast ]);
}, [ recaptcha, apiFetch, isAuth, onSubmit, mixpanelConfig?.account_link_info.source ]);
return (
<FormProvider { ...formApi }>
......@@ -89,7 +87,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
<Button
mt={ 6 }
type="submit"
isDisabled={ formApi.formState.isSubmitting }
disabled={ formApi.formState.isSubmitting }
isLoading={ formApi.formState.isSubmitting }
loadingText="Send a code"
>
......
......@@ -6,10 +6,10 @@ import { FormProvider, useForm } from 'react-hook-form';
import type { OtpCodeFormFields, ScreenSuccess } from '../types';
import type { UserInfo } from 'types/api/account';
import { toaster } from 'chakra/components/toaster';
import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import IconSvg from 'ui/shared/IconSvg';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha';
......@@ -25,7 +25,6 @@ interface Props {
const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
const apiFetch = useApiFetch();
const toast = useToast();
const recaptcha = useReCaptcha();
const [ isCodeSending, setIsCodeSending ] = React.useState(false);
......@@ -61,13 +60,12 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
return;
}
toast({
status: 'error',
toaster.error({
title: 'Error',
description: getErrorMessage(error) || 'Something went wrong',
});
});
}, [ apiFetch, email, onSuccess, isAuth, toast, formApi ]);
}, [ apiFetch, email, onSuccess, isAuth, formApi ]);
const handleResendCodeClick = React.useCallback(async() => {
try {
......@@ -81,23 +79,21 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
},
});
toast({
status: 'success',
toaster.success({
title: 'Success',
description: 'Code has been sent to your email',
});
} catch (error) {
const apiError = getErrorObjPayload<{ message: string }>(error);
toast({
status: 'error',
toaster.error({
title: 'Error',
description: apiError?.message || getErrorMessage(error) || 'Something went wrong',
});
} finally {
setIsCodeSending(false);
}
}, [ apiFetch, email, formApi, toast, recaptcha ]);
}, [ apiFetch, email, formApi, recaptcha ]);
return (
<FormProvider { ...formApi }>
......
......@@ -4,11 +4,11 @@ import React from 'react';
import type { Route } from 'nextjs-routes';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel';
const PROTECTED_ROUTES: Array<Route['pathname']> = [
......@@ -24,7 +24,6 @@ const PROTECTED_ROUTES: Array<Route['pathname']> = [
export default function useLogout() {
const router = useRouter();
const queryClient = useQueryClient();
const toast = useToast();
const apiFetch = useApiFetch();
return React.useCallback(async() => {
......@@ -62,11 +61,10 @@ export default function useLogout() {
router.push({ pathname: '/' }, undefined, { shallow: true });
}
} catch (error) {
toast({
status: 'error',
toaster.error({
title: 'Logout failed',
description: 'Please try again later',
});
}
}, [ apiFetch, queryClient, router, toast ]);
}, [ apiFetch, queryClient, router ]);
}
......@@ -3,12 +3,12 @@ import { useSignMessage } from 'wagmi';
import type { UserInfo } from 'types/api/account';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObj from 'lib/errors/getErrorObj';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import type * as mixpanel from 'lib/mixpanel';
import useWeb3Wallet from 'lib/web3/useWallet';
......@@ -24,7 +24,6 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login', isAuth }: P
const isConnectingWalletRef = React.useRef(false);
const apiFetch = useApiFetch();
const toast = useToast();
const web3Wallet = useWeb3Wallet({ source });
const { signMessageAsync } = useSignMessage();
......@@ -48,15 +47,14 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login', isAuth }: P
const apiErrorMessage = getErrorObjPayload<{ message: string }>(error)?.message;
const shortMessage = errorObj && 'shortMessage' in errorObj && typeof errorObj.shortMessage === 'string' ? errorObj.shortMessage : undefined;
onError?.();
toast({
status: 'error',
toaster.error({
title: 'Error',
description: apiErrorMessage || shortMessage || getErrorMessage(error) || 'Something went wrong',
});
} finally {
setIsPending(false);
}
}, [ apiFetch, isAuth, onError, onSuccess, signMessageAsync, toast ]);
}, [ apiFetch, isAuth, onError, onSuccess, signMessageAsync ]);
const start = React.useCallback(() => {
setIsPending(true);
......
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useColorModeValue, useDisclosure } from '@chakra-ui/react';
import { Box, Flex, Drawer, useDisclosure } from '@chakra-ui/react';
import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import config from 'configs/app';
import IconSvg from 'ui/shared/IconSvg';
import NavigationMobile from 'ui/snippets/navigation/mobile/NavigationMobile';
......@@ -39,15 +40,15 @@ const Burger = ({ isMarketplaceAppPage }: Props) => {
aria-label="Menu button"
/>
</Box>
<Drawer
<Drawer.Root
isOpen={ isOpen }
placement="left"
onClose={ onClose }
autoFocus={ false }
>
<DrawerOverlay/>
<DrawerContent maxWidth="330px">
<DrawerBody p={ 6 } display="flex" flexDirection="column">
<Drawer.Backdrop/>
<Drawer.Content maxWidth="330px">
<Drawer.Body p={ 6 } display="flex" flexDirection="column">
<TestnetBadge alignSelf="flex-start"/>
<Flex alignItems="center" justifyContent="space-between">
<NetworkLogo onClick={ handleNetworkLogoClick }/>
......@@ -63,9 +64,9 @@ const Burger = ({ isMarketplaceAppPage }: Props) => {
<NetworkMenuContentMobile tabs={ networkMenu.availableTabs } items={ networkMenu.data }/> :
<NavigationMobile onNavLinkClick={ onClose } isMarketplaceAppPage={ isMarketplaceAppPage }/>
}
</DrawerBody>
</DrawerContent>
</Drawer>
</Drawer.Body>
</Drawer.Content>
</Drawer.Root>
</>
);
};
......
import { Box, Flex, Text, VStack, useColorModeValue } from '@chakra-ui/react';
import { animate, motion, useMotionValue } from 'framer-motion';
import { Box, Flex, Text, VStack } from '@chakra-ui/react';
// import { animate, motion, useMotionValue } from 'framer-motion';
import React, { useCallback } from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
......@@ -10,7 +11,7 @@ import NavLink from '../vertical/NavLink';
import NavLinkRewards from '../vertical/NavLinkRewards';
import NavLinkGroup from './NavLinkGroup';
const DRAWER_WIDTH = 330;
// const DRAWER_WIDTH = 330;
interface Props {
onNavLinkClick?: () => void;
......@@ -22,19 +23,20 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
const [ openedGroupIndex, setOpenedGroupIndex ] = React.useState(-1);
const mainX = useMotionValue(0);
const subX = useMotionValue(DRAWER_WIDTH);
// const mainX = useMotionValue(0);
// const subX = useMotionValue(DRAWER_WIDTH);
const onGroupItemOpen = (index: number) => () => {
setOpenedGroupIndex(index);
animate(mainX, -DRAWER_WIDTH, { ease: 'easeInOut' });
animate(subX, 0, { ease: 'easeInOut' });
// TODO @tom2drum animate navigation group menu
// animate(mainX, -DRAWER_WIDTH, { ease: 'easeInOut' });
// animate(subX, 0, { ease: 'easeInOut' });
};
const onGroupItemClose = useCallback(() => {
animate(mainX, 0, { ease: 'easeInOut' });
animate(subX, DRAWER_WIDTH, { ease: 'easeInOut', onComplete: () => setOpenedGroupIndex(-1) });
}, [ mainX, subX ]);
// animate(mainX, 0, { ease: 'easeInOut' });
// animate(subX, DRAWER_WIDTH, { ease: 'easeInOut', onComplete: () => setOpenedGroupIndex(-1) });
}, []);
const isAuth = useIsAuth();
......@@ -50,8 +52,8 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
display="flex"
flexDirection="column"
flexGrow={ 1 }
as={ motion.div }
style={{ x: mainX }}
// as={ motion.div }
// style={{ x: mainX }}
maxHeight={ openedGroupIndex > -1 ? '100vh' : 'unset' }
overflowY={ openedGroupIndex > -1 ? 'hidden' : 'unset' }
>
......@@ -91,12 +93,12 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
</Box>
{ openedGroupIndex >= 0 && (
<Box
as={ motion.nav }
// as={ motion.nav }
w="100%"
mt={ 6 }
position="absolute"
top={ 0 }
style={{ x: subX }}
// style={{ x: subX }}
key="sub"
>
<Flex alignItems="center" px={ 2 } py={ 2.5 } w="100%" h="50px" onClick={ onGroupItemClose } mb={ 1 }>
......
import { useColorModeValue, Button, forwardRef, chakra } from '@chakra-ui/react';
import { Button, chakra } from '@chakra-ui/react';
import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import IconSvg from 'ui/shared/IconSvg';
......@@ -44,4 +45,4 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re
);
};
export default chakra(forwardRef(NetworkMenuButton));
export default chakra(React.forwardRef(NetworkMenuButton));
import { InputGroup, Input, InputLeftElement, chakra, useColorModeValue, forwardRef, InputRightElement, Center } from '@chakra-ui/react';
import { InputGroup, Input, InputLeftElement, chakra, InputRightElement, Center } from '@chakra-ui/react';
import throttle from 'lodash/throttle';
import React from 'react';
import type { ChangeEvent, FormEvent, FocusEvent } from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import { useScrollDirection } from 'lib/contexts/scrollDirection';
import useIsMobile from 'lib/hooks/useIsMobile';
import ClearButton from 'ui/shared/ClearButton';
......@@ -179,4 +180,4 @@ const SearchBarInput = (
);
};
export default React.memo(forwardRef(SearchBarInput));
export default React.memo(React.forwardRef(SearchBarInput));
import { Flex, Divider, useColorModeValue, Box } from '@chakra-ui/react';
import { Flex, Divider, Box } from '@chakra-ui/react';
import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import config from 'configs/app';
import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils';
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment