Commit 443d1853 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into release/v2-0-0

parents 2aa46b22 5e808067
...@@ -51,6 +51,65 @@ jobs: ...@@ -51,6 +51,65 @@ jobs:
- name: Compile TypeScript - name: Compile TypeScript
run: yarn lint:tsc run: yarn lint:tsc
toolkit_build_check:
name: Toolkit build check
needs: [ code_quality ]
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 22.11.0
cache: 'yarn'
- name: Cache node_modules
uses: actions/cache@v4
id: cache-node-modules
with:
path: |
node_modules
key: node_modules-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
- name: Install project dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile
- name: Install package dependencies
run: |
cd ./toolkit/package
yarn --frozen-lockfile
- name: Type check the package
run: |
cd ./toolkit/package
yarn typecheck
- name: Build the package
run: |
cd ./toolkit/package
yarn build
- name: Verify build output
run: |
cd ./toolkit/package
if [ ! -d "dist" ]; then
echo "Build failed: dist directory not found"
exit 1
fi
if [ ! -f "dist/index.js" ]; then
echo "Build failed: dist/index.js not found"
exit 1
fi
if [ ! -f "dist/index.d.ts" ]; then
echo "Build failed: dist/index.d.ts not found"
exit 1
fi
envs_validation: envs_validation:
name: ENV variables validation name: ENV variables validation
runs-on: ubuntu-latest runs-on: ubuntu-latest
......
...@@ -90,6 +90,13 @@ jobs: ...@@ -90,6 +90,13 @@ jobs:
needs: publish_image needs: publish_image
secrets: inherit secrets: inherit
publish_toolkit:
name: Publish toolkit package to NPM
uses: './.github/workflows/toolkit-npm-publisher.yml'
secrets: inherit
with:
version: ${{ github.ref_name }}
# Temporary disable this step because it is broken # Temporary disable this step because it is broken
# There is an issue with building web3modal deps # There is an issue with building web3modal deps
upload_source_maps: upload_source_maps:
......
name: Publish Chakra theme package to NPM name: Publish toolkit package to NPM
on: on:
workflow_dispatch: workflow_dispatch:
...@@ -34,19 +34,19 @@ jobs: ...@@ -34,19 +34,19 @@ jobs:
- name: Update package version - name: Update package version
run: | run: |
cd ./theme cd ./toolkit/package
npm version ${{ inputs.version }} npm version ${{ inputs.version }}
- name: Build the package - name: Build the package
run: | run: |
yarn yarn
cd ./theme cd ./toolkit/package
yarn yarn
yarn build yarn build
- name: Publish to NPM registry - name: Publish to NPM registry
run: | run: |
cd ./theme cd ./toolkit/package
npm publish --provenance --access public npm publish --provenance --access public
env: env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
*.pem *.pem
.tools .tools
grafana grafana
.cursor
# debug # debug
npm-debug.log* npm-debug.log*
...@@ -60,3 +61,4 @@ yarn-error.log* ...@@ -60,3 +61,4 @@ yarn-error.log*
# build outputs # build outputs
/tools/preset-sync/index.js /tools/preset-sync/index.js
/toolkit/package/dist
\ No newline at end of file
{ {
"typescript.tsdk": "node_modules/typescript/lib" "typescript.tsdk": "node_modules/typescript/lib",
"javascript.preferences.autoImportFileExcludePatterns": [
"./toolkit/package/**",
"./toolkit/components/**/index.ts",
],
"typescript.preferences.autoImportFileExcludePatterns": [
"./toolkit/package/**",
"./toolkit/components/**/index.ts",
]
} }
\ No newline at end of file
...@@ -14,7 +14,8 @@ COPY types ./types ...@@ -14,7 +14,8 @@ COPY types ./types
COPY lib ./lib COPY lib ./lib
COPY configs/app ./configs/app COPY configs/app ./configs/app
COPY toolkit/theme ./toolkit/theme COPY toolkit/theme ./toolkit/theme
COPY ui/shared/forms/validators/url.ts ./ui/shared/forms/validators/url.ts COPY toolkit/utils ./toolkit/utils
COPY toolkit/components/forms/validators/url.ts ./toolkit/components/forms/validators/url.ts
RUN apk add git RUN apk add git
RUN yarn --frozen-lockfile --network-timeout 100000 RUN yarn --frozen-lockfile --network-timeout 100000
...@@ -79,6 +80,7 @@ RUN set -a && \ ...@@ -79,6 +80,7 @@ RUN set -a && \
# ENV NEXT_TELEMETRY_DISABLED 1 # ENV NEXT_TELEMETRY_DISABLED 1
# Build app for production # Build app for production
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN yarn build RUN yarn build
......
import stripTrailingSlash from 'lib/stripTrailingSlash'; import { stripTrailingSlash } from 'toolkit/utils/url';
import { getEnvValue } from './utils'; import { getEnvValue } from './utils';
......
import type { RollupType } from 'types/client/rollup'; import type { RollupType } from 'types/client/rollup';
import type { NetworkVerificationType, NetworkVerificationTypeEnvs } from 'types/networks'; import type { NetworkVerificationType, NetworkVerificationTypeEnvs } from 'types/networks';
import { urlValidator } from 'ui/shared/forms/validators/url'; import { urlValidator } from 'toolkit/components/forms/validators/url';
import { getEnvValue, parseEnvJson } from './utils'; import { getEnvValue, parseEnvJson } from './utils';
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import type { ParentChain, RollupType } from 'types/client/rollup'; import type { ParentChain, RollupType } from 'types/client/rollup';
import { ROLLUP_TYPES } from 'types/client/rollup'; import { ROLLUP_TYPES } from 'types/client/rollup';
import stripTrailingSlash from 'lib/stripTrailingSlash'; import { stripTrailingSlash } from 'toolkit/utils/url';
import { getEnvValue, parseEnvJson } from '../utils'; import { getEnvValue, parseEnvJson } from '../utils';
......
import type { Feature } from './types'; import type { Feature } from './types';
import stripTrailingSlash from 'lib/stripTrailingSlash'; import { stripTrailingSlash } from 'toolkit/utils/url';
import { getEnvValue } from '../utils'; import { getEnvValue } from '../utils';
......
import type { Feature } from './types'; import type { Feature } from './types';
import stripTrailingSlash from 'lib/stripTrailingSlash'; import { stripTrailingSlash } from 'toolkit/utils/url';
import { getEnvValue } from '../utils'; import { getEnvValue } from '../utils';
......
...@@ -5,7 +5,7 @@ import type { NetworkExplorer } from 'types/networks'; ...@@ -5,7 +5,7 @@ import type { NetworkExplorer } from 'types/networks';
import type { ColorThemeId } from 'types/settings'; import type { ColorThemeId } from 'types/settings';
import type { FontFamily } from 'types/ui'; import type { FontFamily } from 'types/ui';
import { COLOR_THEMES } from 'lib/settings/colorTheme'; import { COLOR_THEMES, type ColorTheme } from 'lib/settings/colorTheme';
import * as features from './features'; import * as features from './features';
import * as views from './ui/views'; import * as views from './ui/views';
...@@ -49,7 +49,7 @@ const highlightedRoutes = (() => { ...@@ -49,7 +49,7 @@ const highlightedRoutes = (() => {
const defaultColorTheme = (() => { const defaultColorTheme = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_COLOR_THEME_DEFAULT') as ColorThemeId | undefined; const envValue = getEnvValue('NEXT_PUBLIC_COLOR_THEME_DEFAULT') as ColorThemeId | undefined;
return COLOR_THEMES.find((theme) => theme.id === envValue); return COLOR_THEMES.find((theme) => theme.id === envValue) as ColorTheme | undefined;
})(); })();
const UI = Object.freeze({ const UI = Object.freeze({
......
import isBrowser from 'lib/isBrowser'; import { isBrowser } from 'toolkit/utils/isBrowser';
import * as regexp from 'lib/regexp'; import * as regexp from 'toolkit/utils/regexp';
export const replaceQuotes = (value: string | undefined) => value?.replaceAll('\'', '"'); export const replaceQuotes = (value: string | undefined) => value?.replaceAll('\'', '"');
......
...@@ -46,7 +46,7 @@ import type { VerifiedContractsFilter } from '../../../types/api/contracts'; ...@@ -46,7 +46,7 @@ import type { VerifiedContractsFilter } from '../../../types/api/contracts';
import type { TxExternalTxsConfig } from '../../../types/client/externalTxsConfig'; import type { TxExternalTxsConfig } from '../../../types/client/externalTxsConfig';
import { replaceQuotes } from '../../../configs/app/utils'; import { replaceQuotes } from '../../../configs/app/utils';
import * as regexp from '../../../lib/regexp'; import * as regexp from '../../../toolkit/utils/regexp';
import type { IconName } from '../../../ui/shared/IconSvg'; import type { IconName } from '../../../ui/shared/IconSvg';
const protocols = [ 'http', 'https' ]; const protocols = [ 'http', 'https' ];
......
...@@ -29,7 +29,7 @@ const RESTRICTED_MODULES = { ...@@ -29,7 +29,7 @@ const RESTRICTED_MODULES = {
name: '@chakra-ui/react', name: '@chakra-ui/react',
importNames: [ importNames: [
'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button', 'ButtonGroup', 'Link', 'LinkBox', 'LinkOverlay', 'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button', 'ButtonGroup', 'Link', 'LinkBox', 'LinkOverlay',
'Dialog', 'DialogRoot', 'DialogContent', 'DialogHeader', 'DialogCloseTrigger', 'Dialog', 'DialogRoot', 'DialogContent', 'DialogHeader', 'DialogCloseTrigger', 'DialogBody',
'Tag', 'Switch', 'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter', 'Tag', 'Switch', 'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter',
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer', 'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription', 'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
...@@ -37,7 +37,7 @@ const RESTRICTED_MODULES = { ...@@ -37,7 +37,7 @@ const RESTRICTED_MODULES = {
'Heading', 'Badge', 'Tabs', 'Show', 'Hide', 'Checkbox', 'CheckboxGroup', 'Heading', 'Badge', 'Tabs', 'Show', 'Hide', 'Checkbox', 'CheckboxGroup',
'Table', 'TableRoot', 'TableBody', 'TableHeader', 'TableRow', 'TableCell', 'Table', 'TableRoot', 'TableBody', 'TableHeader', 'TableRow', 'TableCell',
'Menu', 'MenuRoot', 'MenuTrigger', 'MenuContent', 'MenuItem', 'MenuTriggerItem', 'MenuRadioItemGroup', 'MenuContextTrigger', 'Menu', 'MenuRoot', 'MenuTrigger', 'MenuContent', 'MenuItem', 'MenuTriggerItem', 'MenuRadioItemGroup', 'MenuContextTrigger',
'Rating', 'RatingGroup', 'Rating', 'RatingGroup', 'Textarea',
], ],
message: 'Please use corresponding component or hook from "toolkit" instead', message: 'Please use corresponding component or hook from "toolkit" instead',
}, },
...@@ -65,9 +65,7 @@ export default tseslint.config( ...@@ -65,9 +65,7 @@ export default tseslint.config(
{ ignores: [ { ignores: [
'deploy/tools/', 'deploy/tools/',
'public/', 'public/',
'theme/dist/',
'.git/', '.git/',
'theme/webpack.config.js',
'next.config.js', 'next.config.js',
] }, ] },
...@@ -455,6 +453,8 @@ export default tseslint.config( ...@@ -455,6 +453,8 @@ export default tseslint.config(
{ {
files: [ files: [
'toolkit/chakra/**', 'toolkit/chakra/**',
'toolkit/components/**',
'toolkit/package/**',
], ],
rules: { rules: {
// for toolkit components allow to import @chakra-ui/react directly // for toolkit components allow to import @chakra-ui/react directly
......
...@@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'; ...@@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import { WEI, ZERO } from 'lib/consts'; import { WEI, ZERO } from 'toolkit/utils/consts';
export default function getBlockTotalReward(block: Block) { export default function getBlockTotalReward(block: Block) {
const totalReward = block.rewards const totalReward = block.rewards
......
...@@ -11,7 +11,6 @@ import config from 'configs/app'; ...@@ -11,7 +11,6 @@ import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { YEAR } from 'lib/consts';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import decodeJWT from 'lib/decodeJWT'; import decodeJWT from 'lib/decodeJWT';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
...@@ -20,6 +19,7 @@ import getQueryParamString from 'lib/router/getQueryParamString'; ...@@ -20,6 +19,7 @@ import getQueryParamString from 'lib/router/getQueryParamString';
import removeQueryParam from 'lib/router/removeQueryParam'; import removeQueryParam from 'lib/router/removeQueryParam';
import useAccount from 'lib/web3/useAccount'; import useAccount from 'lib/web3/useAccount';
import { toaster } from 'toolkit/chakra/toaster'; import { toaster } from 'toolkit/chakra/toaster';
import { YEAR } from 'toolkit/utils/consts';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
const feature = config.features.rewards; const feature = config.features.rewards;
......
...@@ -2,7 +2,7 @@ import { throttle, clamp } from 'es-toolkit'; ...@@ -2,7 +2,7 @@ import { throttle, clamp } from 'es-toolkit';
import React from 'react'; import React from 'react';
const ScrollDirectionContext = React.createContext<'up' | 'down' | null>(null); const ScrollDirectionContext = React.createContext<'up' | 'down' | null>(null);
import isBrowser from 'lib/isBrowser'; import { isBrowser } from 'toolkit/utils/isBrowser';
const SCROLL_DIFF_THRESHOLD = 20; const SCROLL_DIFF_THRESHOLD = 20;
......
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import isBrowser from './isBrowser'; import { isBrowser } from 'toolkit/utils/isBrowser';
export enum NAMES { export enum NAMES {
NAV_BAR_COLLAPSED = 'nav_bar_collapsed', NAV_BAR_COLLAPSED = 'nav_bar_collapsed',
......
...@@ -7,7 +7,7 @@ import relativeTime from 'dayjs/plugin/relativeTime'; ...@@ -7,7 +7,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
import updateLocale from 'dayjs/plugin/updateLocale'; import updateLocale from 'dayjs/plugin/updateLocale';
import weekOfYear from 'dayjs/plugin/weekOfYear'; import weekOfYear from 'dayjs/plugin/weekOfYear';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'toolkit/utils/htmlEntities';
const relativeTimeConfig = { const relativeTimeConfig = {
thresholds: [ thresholds: [
......
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { ZERO } from 'lib/consts'; import { ZERO } from 'toolkit/utils/consts';
interface Params { interface Params {
value: string; value: string;
......
...@@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'; ...@@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js';
import type { Unit } from 'types/unit'; import type { Unit } from 'types/unit';
import { WEI, GWEI } from 'lib/consts'; import { WEI, GWEI } from 'toolkit/utils/consts';
export default function getValueWithUnit(value: string | number, unit: Unit = 'wei') { export default function getValueWithUnit(value: string | number, unit: Unit = 'wei') {
let unitBn: BigNumber.Value; let unitBn: BigNumber.Value;
......
import React from 'react'; import React from 'react';
import { SECOND } from 'lib/consts'; import { SECOND } from 'toolkit/utils/consts';
import { initGrowthBook } from './init'; import { initGrowthBook } from './init';
......
...@@ -5,7 +5,7 @@ import type { AdBannerProviders } from 'types/client/adProviders'; ...@@ -5,7 +5,7 @@ import type { AdBannerProviders } from 'types/client/adProviders';
import config from 'configs/app'; import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import isBrowser from 'lib/isBrowser'; import { isBrowser } from 'toolkit/utils/isBrowser';
const DEFAULT_URL = 'https://request-global.czilladx.com'; const DEFAULT_URL = 'https://request-global.czilladx.com';
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/navigation'; import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/navigation';
import config from 'configs/app'; import config from 'configs/app';
import { rightLineArrow } from 'lib/html-entities'; import { rightLineArrow } from 'toolkit/utils/htmlEntities';
interface ReturnType { interface ReturnType {
mainNavItems: Array<NavItem | NavGroupItem>; mainNavItems: Array<NavItem | NavGroupItem>;
......
...@@ -5,8 +5,8 @@ import type { PreSubmitTransactionResponse, PreVerifyContractResponse } from '@b ...@@ -5,8 +5,8 @@ import type { PreSubmitTransactionResponse, PreVerifyContractResponse } from '@b
import config from 'configs/app'; import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { MINUTE } from 'lib/consts';
import { useRewardsContext } from 'lib/contexts/rewards'; import { useRewardsContext } from 'lib/contexts/rewards';
import { MINUTE } from 'toolkit/utils/consts';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
const feature = config.features.rewards; const feature = config.features.rewards;
......
import React from 'react'; import React from 'react';
import { DAY, HOUR, MINUTE, SECOND } from 'lib/consts';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import { DAY, HOUR, MINUTE, SECOND } from 'toolkit/utils/consts';
function getUnits(diff: number) { function getUnits(diff: number) {
if (diff < MINUTE) { if (diff < MINUTE) {
......
export default function makePrettyLink(url: string | undefined): { url: string; domain: string } | undefined {
try {
const urlObj = new URL(url ?? '');
return {
url: urlObj.href,
domain: urlObj.hostname,
};
} catch (error) {}
}
import { uniq } from 'es-toolkit'; import { uniq } from 'es-toolkit';
import isBrowser from './isBrowser'; import { isBrowser } from 'toolkit/utils/isBrowser';
const RECENT_KEYWORDS_LS_KEY = 'recent_search_keywords'; const RECENT_KEYWORDS_LS_KEY = 'recent_search_keywords';
const MAX_KEYWORDS_NUMBER = 10; const MAX_KEYWORDS_NUMBER = 10;
......
...@@ -2,7 +2,7 @@ import type { ColorThemeId } from 'types/settings'; ...@@ -2,7 +2,7 @@ import type { ColorThemeId } from 'types/settings';
import type { ColorMode } from 'toolkit/chakra/color-mode'; import type { ColorMode } from 'toolkit/chakra/color-mode';
interface ColorTheme { export interface ColorTheme {
id: ColorThemeId; id: ColorThemeId;
label: string; label: string;
colorMode: ColorMode; colorMode: ColorMode;
......
const stripLeadingSlash = (str: string) => str[0] === '/' ? str.slice(1) : str;
export default stripLeadingSlash;
const stripTrailingSlash = (str: string) => str[str.length - 1] === '/' ? str.slice(0, -1) : str;
export default stripTrailingSlash;
import * as regexp from 'lib/regexp'; import * as regexp from 'toolkit/utils/regexp';
export default function urlParser(maybeUrl: string): URL | undefined { export default function urlParser(maybeUrl: string): URL | undefined {
try { try {
......
...@@ -2,8 +2,8 @@ import React from 'react'; ...@@ -2,8 +2,8 @@ import React from 'react';
import type { AddEthereumChainParameter } from 'viem'; import type { AddEthereumChainParameter } from 'viem';
import config from 'configs/app'; import config from 'configs/app';
import { SECOND } from 'toolkit/utils/consts';
import { SECOND } from '../consts';
import useRewardsActivity from '../hooks/useRewardsActivity'; import useRewardsActivity from '../hooks/useRewardsActivity';
import useProvider from './useProvider'; import useProvider from './useProvider';
import { getHexadecimalChainId } from './utils'; import { getHexadecimalChainId } from './utils';
......
...@@ -3,7 +3,7 @@ import type { RpcBlock } from 'viem'; ...@@ -3,7 +3,7 @@ import type { RpcBlock } from 'viem';
import type { Block, BlocksResponse, ZilliqaBlockData } from 'types/api/block'; import type { Block, BlocksResponse, ZilliqaBlockData } from 'types/api/block';
import { ZERO_ADDRESS } from 'lib/consts'; import { ZERO_ADDRESS } from 'toolkit/utils/consts';
import * as addressMock from '../address/address'; import * as addressMock from '../address/address';
import * as tokenMock from '../tokens/tokenInfo'; import * as tokenMock from '../tokens/tokenInfo';
......
...@@ -5,8 +5,8 @@ import { httpLogger } from 'nextjs/utils/logger'; ...@@ -5,8 +5,8 @@ import { httpLogger } from 'nextjs/utils/logger';
import { RESOURCES } from 'lib/api/resources'; import { RESOURCES } from 'lib/api/resources';
import type { ResourceName, ResourcePathParams, ResourcePayload } from 'lib/api/resources'; import type { ResourceName, ResourcePathParams, ResourcePayload } from 'lib/api/resources';
import { SECOND } from 'lib/consts';
import metrics from 'lib/monitoring/metrics'; import metrics from 'lib/monitoring/metrics';
import { SECOND } from 'toolkit/utils/consts';
type Params<R extends ResourceName> = ( type Params<R extends ResourceName> = (
{ {
......
import { Accordion } from '@chakra-ui/react'; import { Accordion, Icon } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IndicatorIcon from 'icons/arrows/east-mini.svg';
interface AccordionItemTriggerProps extends Accordion.ItemTriggerProps { interface AccordionItemTriggerProps extends Accordion.ItemTriggerProps {
indicatorPlacement?: 'start' | 'end'; indicatorPlacement?: 'start' | 'end';
...@@ -57,7 +57,7 @@ export const AccordionItemTrigger = React.forwardRef< ...@@ -57,7 +57,7 @@ export const AccordionItemTrigger = React.forwardRef<
</Accordion.ItemIndicator> </Accordion.ItemIndicator>
) : ( ) : (
<Accordion.ItemIndicator rotate={{ base: '180deg', _open: '270deg' }} display="flex"> <Accordion.ItemIndicator rotate={{ base: '180deg', _open: '270deg' }} display="flex">
<IconSvg name="arrows/east-mini"/> <Icon boxSize={ 5 }><IndicatorIcon/></Icon>
</Accordion.ItemIndicator> </Accordion.ItemIndicator>
); );
...@@ -70,7 +70,7 @@ export const AccordionItemTrigger = React.forwardRef< ...@@ -70,7 +70,7 @@ export const AccordionItemTrigger = React.forwardRef<
); );
}); });
interface AccordionItemContentProps extends Accordion.ItemContentProps {} export interface AccordionItemContentProps extends Accordion.ItemContentProps {}
export const AccordionItemContent = React.forwardRef< export const AccordionItemContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
......
import type { AlertDescriptionProps } from '@chakra-ui/react'; import type { AlertDescriptionProps } from '@chakra-ui/react';
import { Alert as ChakraAlert } from '@chakra-ui/react'; import { Alert as ChakraAlert, Icon } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IndicatorIcon from 'icons/info_filled.svg';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
...@@ -37,7 +37,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>( ...@@ -37,7 +37,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
const [ isOpen, setIsOpen ] = React.useState(true); const [ isOpen, setIsOpen ] = React.useState(true);
const defaultIcon = <IconSvg name="info_filled" w="100%" h="100%"/>; const defaultIcon = <Icon boxSize={ 5 }><IndicatorIcon/></Icon>;
const iconElement = (() => { const iconElement = (() => {
if (startElement !== undefined) { if (startElement !== undefined) {
......
...@@ -2,22 +2,19 @@ import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react'; ...@@ -2,22 +2,19 @@ import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react';
import { chakra, Badge as ChakraBadge } from '@chakra-ui/react'; import { chakra, Badge as ChakraBadge } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { IconName } from 'ui/shared/IconSvg'; import { TruncatedTextTooltip } from '../components/truncation/TruncatedTextTooltip';
import IconSvg from 'ui/shared/IconSvg';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
export interface BadgeProps extends Omit<ChakraBadgeProps, 'colorScheme'> { export interface BadgeProps extends Omit<ChakraBadgeProps, 'colorScheme'> {
loading?: boolean; loading?: boolean;
iconStart?: IconName; startElement?: React.ReactNode;
endElement?: React.ReactNode; endElement?: React.ReactNode;
truncated?: boolean; truncated?: boolean;
} }
export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>( export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
function Badge(props, ref) { function Badge(props, ref) {
const { loading, iconStart, children, asChild = true, truncated = false, endElement, ...rest } = props; const { loading, startElement, children, asChild = true, truncated = false, endElement, ...rest } = props;
const child = <chakra.span overflow="hidden" textOverflow="ellipsis">{ children }</chakra.span>; const child = <chakra.span overflow="hidden" textOverflow="ellipsis">{ children }</chakra.span>;
...@@ -30,7 +27,7 @@ export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>( ...@@ -30,7 +27,7 @@ export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
return ( return (
<Skeleton loading={ loading } asChild={ asChild } ref={ ref }> <Skeleton loading={ loading } asChild={ asChild } ref={ ref }>
<ChakraBadge display="inline-flex" alignItems="center" whiteSpace="nowrap" { ...rest }> <ChakraBadge display="inline-flex" alignItems="center" whiteSpace="nowrap" { ...rest }>
{ iconStart && <IconSvg name={ iconStart } boxSize="10px"/> } { startElement }
{ childrenElement } { childrenElement }
{ endElement } { endElement }
</ChakraBadge> </ChakraBadge>
......
import type { ButtonProps } from '@chakra-ui/react'; import type { ButtonProps } from '@chakra-ui/react';
import { useRecipe } from '@chakra-ui/react'; import { Icon, useRecipe } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import CloseIcon from 'icons/close.svg';
import { recipe as closeButtonRecipe } from '../theme/recipes/close-button.recipe'; import { recipe as closeButtonRecipe } from '../theme/recipes/close-button.recipe';
import { IconButton } from './icon-button'; import { IconButton } from './icon-button';
...@@ -21,7 +21,7 @@ export const CloseButton = React.forwardRef< ...@@ -21,7 +21,7 @@ export const CloseButton = React.forwardRef<
return ( return (
<IconButton aria-label="Close" ref={ ref } css={ styles } { ...restProps }> <IconButton aria-label="Close" ref={ ref } css={ styles } { ...restProps }>
{ props.children ?? <IconSvg name="close"/> } { props.children ?? <Icon boxSize={ 5 }><CloseIcon/></Icon> }
</IconButton> </IconButton>
); );
}); });
...@@ -2,8 +2,7 @@ import { Flex, type FlexProps } from '@chakra-ui/react'; ...@@ -2,8 +2,7 @@ import { Flex, type FlexProps } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
import useUpdateEffect from 'lib/hooks/useUpdateEffect'; import { useUpdateEffect } from '../hooks/useUpdateEffect';
import type { LinkProps } from './link'; import type { LinkProps } from './link';
import { Link } from './link'; import { Link } from './link';
......
import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react'; import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import ButtonBackTo from 'ui/shared/buttons/ButtonBackTo'; import { BackToButton } from '../components/buttons/BackToButton';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
interface DialogContentProps extends ChakraDialog.ContentProps { interface DialogContentProps extends ChakraDialog.ContentProps {
...@@ -62,7 +61,7 @@ export const DialogHeader = React.forwardRef< ...@@ -62,7 +61,7 @@ export const DialogHeader = React.forwardRef<
>(function DialogHeader(props, ref) { >(function DialogHeader(props, ref) {
const { startElement: startElementProp, onBackToClick, ...rest } = props; const { startElement: startElementProp, onBackToClick, ...rest } = props;
const startElement = startElementProp ?? (onBackToClick && <ButtonBackTo onClick={ onBackToClick }/>); const startElement = startElementProp ?? (onBackToClick && <BackToButton onClick={ onBackToClick }/>);
return ( return (
<ChakraDialog.Header ref={ ref } { ...rest }> <ChakraDialog.Header ref={ ref } { ...rest }>
......
import { Field as ChakraField } from '@chakra-ui/react'; import { Field as ChakraField } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import { space } from 'lib/html-entities'; import { space } from 'toolkit/utils/htmlEntities';
import getComponentDisplayName from '../utils/getComponentDisplayName'; import getComponentDisplayName from '../utils/getComponentDisplayName';
import type { InputProps } from './input'; import type { InputProps } from './input';
......
import React from 'react'; import React from 'react';
import getComponentDisplayName from '../utils/getComponentDisplayName';
import { Button, type ButtonProps } from './button'; import { Button, type ButtonProps } from './button';
export interface IconButtonProps extends Omit<ButtonProps, 'size'> { export interface IconButtonProps extends Omit<ButtonProps, 'size'> {
...@@ -14,9 +13,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>( ...@@ -14,9 +13,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
// FIXME: I have to clone the children instead of using _icon props because of style overrides // FIXME: I have to clone the children instead of using _icon props because of style overrides
// in some pw tests for some reason the _icon style will be applied before the style of child (IconSvg component) // in some pw tests for some reason the _icon style will be applied before the style of child (IconSvg component)
const child = React.Children.only<React.ReactElement>(children as React.ReactElement); const child = React.Children.only<React.ReactElement>(children as React.ReactElement);
const clonedChildren = size && getComponentDisplayName(child.type) === 'IconSvg' ? const clonedChildren = size ? React.cloneElement(child, { boxSize: 5 }) : child;
React.cloneElement(child, { boxSize: 5 }) :
child;
const sizeStyle = (() => { const sizeStyle = (() => {
switch (size) { switch (size) {
......
import type { LinkProps as ChakraLinkProps } from '@chakra-ui/react'; import type { LinkProps as ChakraLinkProps } from '@chakra-ui/react';
import { Link as ChakraLink, LinkBox as ChakraLinkBox, LinkOverlay as ChakraLinkOverlay } from '@chakra-ui/react'; import { Link as ChakraLink, LinkBox as ChakraLinkBox, LinkOverlay as ChakraLinkOverlay, Icon } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import type { LinkProps as NextLinkProps } from 'next/link'; import type { LinkProps as NextLinkProps } from 'next/link';
import React from 'react'; import React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import ArrowIcon from 'icons/link_external.svg';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
export const LinkExternalIcon = ({ color }: { color?: ChakraLinkProps['color'] }) => ( export const LinkExternalIcon = ({ color }: { color?: ChakraLinkProps['color'] }) => (
<IconSvg <Icon
name="link_external"
boxSize={ 3 } boxSize={ 3 }
verticalAlign="middle" verticalAlign="middle"
color={ color ?? 'icon.externalLink' } color={ color ?? 'icon.externalLink' }
...@@ -18,7 +17,9 @@ export const LinkExternalIcon = ({ color }: { color?: ChakraLinkProps['color'] } ...@@ -18,7 +17,9 @@ export const LinkExternalIcon = ({ color }: { color?: ChakraLinkProps['color'] }
color: 'inherit', color: 'inherit',
}} }}
flexShrink={ 0 } flexShrink={ 0 }
/> >
<ArrowIcon/>
</Icon>
); );
interface LinkPropsChakra extends ChakraLinkProps { interface LinkPropsChakra extends ChakraLinkProps {
......
...@@ -3,7 +3,7 @@ import * as React from 'react'; ...@@ -3,7 +3,7 @@ import * as React from 'react';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
interface PopoverContentProps extends ChakraPopover.ContentProps { export interface PopoverContentProps extends ChakraPopover.ContentProps {
portalled?: boolean; portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>; portalRef?: React.RefObject<HTMLElement>;
} }
......
...@@ -3,8 +3,7 @@ ...@@ -3,8 +3,7 @@
import { ChakraProvider } from '@chakra-ui/react'; import { ChakraProvider } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import theme from 'toolkit/theme/theme'; import theme from '../theme/theme';
import { import {
ColorModeProvider, ColorModeProvider,
type ColorModeProviderProps, type ColorModeProviderProps,
......
import { RatingGroup, useRatingGroup } from '@chakra-ui/react'; import { Icon, RatingGroup, useRatingGroup } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import StarFilledIcon from 'icons/star_filled.svg';
import StarOutlineIcon from 'icons/star_outline.svg';
export interface RatingProps extends Omit<RatingGroup.RootProviderProps, 'value'> { export interface RatingProps extends Omit<RatingGroup.RootProviderProps, 'value'> {
count?: number; count?: number;
...@@ -24,7 +25,9 @@ export const Rating = React.forwardRef<HTMLDivElement, RatingProps>( ...@@ -24,7 +25,9 @@ export const Rating = React.forwardRef<HTMLDivElement, RatingProps>(
<RatingGroup.HiddenInput/> <RatingGroup.HiddenInput/>
<RatingGroup.Control> <RatingGroup.Control>
{ Array.from({ length: count }).map((_, index) => { { Array.from({ length: count }).map((_, index) => {
const icon = index < highlightedIndex ? <IconSvg name="star_filled"/> : <IconSvg name="star_outline"/>; const icon = index < highlightedIndex ?
<Icon boxSize={ 5 }><StarFilledIcon/></Icon> :
<Icon boxSize={ 5 }><StarOutlineIcon/></Icon>;
return ( return (
<RatingGroup.Item key={ index } index={ index + 1 }> <RatingGroup.Item key={ index } index={ index + 1 }>
......
'use client'; 'use client';
import type { ListCollection } from '@chakra-ui/react'; import type { ListCollection } from '@chakra-ui/react';
import { Box, Select as ChakraSelect, createListCollection, Flex, Portal, useSelectContext } from '@chakra-ui/react'; import { Box, Select as ChakraSelect, createListCollection, Flex, Portal, Icon, useSelectContext } from '@chakra-ui/react';
import { useDebounce } from '@uidotdev/usehooks'; import { useDebounce } from '@uidotdev/usehooks';
import * as React from 'react'; import * as React from 'react';
import FilterInput from 'ui/shared/filters/FilterInput'; import ArrowIcon from 'icons/arrows/east-mini.svg';
import type { IconName } from 'ui/shared/IconSvg'; import CheckIcon from 'icons/check.svg';
import IconSvg from 'ui/shared/IconSvg';
import { FilterInput } from '../components/filters/FilterInput';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
export interface SelectOption<Value extends string = string> { export interface SelectOption<Value extends string = string> {
value: Value; value: Value;
label: string; label: string;
icon?: IconName | React.ReactNode; icon?: React.ReactNode;
} }
export interface SelectControlProps extends ChakraSelect.ControlProps { export interface SelectControlProps extends ChakraSelect.ControlProps {
...@@ -46,7 +46,7 @@ export const SelectControl = React.forwardRef< ...@@ -46,7 +46,7 @@ export const SelectControl = React.forwardRef<
_open={{ transform: 'rotate(90deg)' }} _open={{ transform: 'rotate(90deg)' }}
flexShrink={ 0 } flexShrink={ 0 }
> >
<IconSvg name="arrows/east-mini"/> <Icon boxSize={ 5 }><ArrowIcon/></Icon>
</ChakraSelect.Indicator> </ChakraSelect.Indicator>
) } ) }
</ChakraSelect.IndicatorGroup> </ChakraSelect.IndicatorGroup>
...@@ -88,26 +88,24 @@ export const SelectContent = React.forwardRef< ...@@ -88,26 +88,24 @@ export const SelectContent = React.forwardRef<
); );
}); });
export interface SelectItemProps extends ChakraSelect.ItemProps {
item: SelectOption;
}
export const SelectItem = React.forwardRef< export const SelectItem = React.forwardRef<
HTMLDivElement, HTMLDivElement,
ChakraSelect.ItemProps SelectItemProps
>(function SelectItem(props, ref) { >(function SelectItem(props, ref) {
const { item, children, ...rest } = props; const { item, children, ...rest } = props;
const startElement = (() => { const startElement = item.icon;
if (item.icon) {
return typeof item.icon === 'string' ? <IconSvg name={ item.icon } boxSize={ 5 } flexShrink={ 0 }/> : item.icon;
}
return null;
})();
return ( return (
<ChakraSelect.Item key={ item.value } item={ item } { ...rest } ref={ ref }> <ChakraSelect.Item key={ item.value } item={ item } { ...rest } ref={ ref }>
{ startElement } { startElement }
{ children } { children }
<ChakraSelect.ItemIndicator asChild> <ChakraSelect.ItemIndicator asChild>
<IconSvg name="check" boxSize={ 5 } flexShrink={ 0 } ml="auto"/> <Icon boxSize={ 5 } flexShrink={ 0 } ml="auto"><CheckIcon/></Icon>
</ChakraSelect.ItemIndicator> </ChakraSelect.ItemIndicator>
</ChakraSelect.Item> </ChakraSelect.Item>
); );
...@@ -142,14 +140,6 @@ export const SelectValueText = React.forwardRef< ...@@ -142,14 +140,6 @@ export const SelectValueText = React.forwardRef<
if (!item) return placeholder; if (!item) return placeholder;
const icon = (() => {
if (item.icon) {
return typeof item.icon === 'string' ? <IconSvg name={ item.icon as IconName } boxSize={ 5 } flexShrink={ 0 }/> : item.icon;
}
return null;
})();
const label = size === 'lg' ? ( const label = size === 'lg' ? (
<Box <Box
textStyle="xs" textStyle="xs"
...@@ -164,7 +154,7 @@ export const SelectValueText = React.forwardRef< ...@@ -164,7 +154,7 @@ export const SelectValueText = React.forwardRef<
<> <>
{ label } { label }
<Flex display="inline-flex" alignItems="center" flexWrap="nowrap" gap={ 1 }> <Flex display="inline-flex" alignItems="center" flexWrap="nowrap" gap={ 1 }>
{ icon } { item.icon }
<span style={{ <span style={{
WebkitLineClamp: 1, WebkitLineClamp: 1,
WebkitBoxOrient: 'vertical', WebkitBoxOrient: 'vertical',
......
import { Table as ChakraTable } from '@chakra-ui/react'; import { Table as ChakraTable, Icon } from '@chakra-ui/react';
import { throttle } from 'es-toolkit'; import { throttle } from 'es-toolkit';
import * as React from 'react'; import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import ArrowIcon from 'icons/arrows/east.svg';
import { Link } from './link'; import { Link } from './link';
...@@ -50,8 +50,7 @@ export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHe ...@@ -50,8 +50,7 @@ export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHe
<TableColumnHeader { ...rest }> <TableColumnHeader { ...rest }>
<Link onClick={ disabled ? undefined : handleSortToggle } position="relative"> <Link onClick={ disabled ? undefined : handleSortToggle } position="relative">
{ sortValue.includes(sortField) && ( { sortValue.includes(sortField) && (
<IconSvg <Icon
name="arrows/east"
w={ 4 } w={ 4 }
h="100%" h="100%"
transform={ sortValue.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)' } transform={ sortValue.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)' }
...@@ -59,7 +58,9 @@ export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHe ...@@ -59,7 +58,9 @@ export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHe
left={ indicatorPosition === 'left' ? -5 : undefined } left={ indicatorPosition === 'left' ? -5 : undefined }
right={ indicatorPosition === 'right' ? -5 : undefined } right={ indicatorPosition === 'right' ? -5 : undefined }
top={ 0 } top={ 0 }
/> >
<ArrowIcon/>
</Icon>
) } ) }
{ children } { children }
</Link> </Link>
......
import { chakra, Tag as ChakraTag } from '@chakra-ui/react'; import { chakra, Tag as ChakraTag } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'toolkit/utils/htmlEntities';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import { TruncatedTextTooltip } from '../components/truncation/TruncatedTextTooltip';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
......
...@@ -9,7 +9,7 @@ import { ...@@ -9,7 +9,7 @@ import {
createToaster, createToaster,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { SECOND } from 'lib/consts'; import { SECOND } from 'toolkit/utils/consts';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
......
import React from 'react'; import React from 'react';
import type { TabsProps } from 'toolkit/chakra/tabs'; import type { TabsProps } from '../../chakra/tabs';
import { TabsContent, TabsRoot } from 'toolkit/chakra/tabs'; import { TabsContent, TabsRoot } from '../../chakra/tabs';
import useViewportSize from 'toolkit/hooks/useViewportSize'; import { useViewportSize } from '../../hooks/useViewportSize';
import AdaptiveTabsList, { type BaseProps as AdaptiveTabsListProps } from './AdaptiveTabsList'; import AdaptiveTabsList, { type BaseProps as AdaptiveTabsListProps } from './AdaptiveTabsList';
import { getTabValue } from './utils'; import { getTabValue } from './utils';
......
...@@ -6,10 +6,10 @@ import type { TabItemRegular } from './types'; ...@@ -6,10 +6,10 @@ import type { TabItemRegular } from './types';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useIsSticky from 'lib/hooks/useIsSticky';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TabsCounter, TabsList, TabsTrigger } from 'toolkit/chakra/tabs';
import { useIsSticky } from '../..//hooks/useIsSticky';
import { Skeleton } from '../../chakra/skeleton';
import { TabsCounter, TabsList, TabsTrigger } from '../../chakra/tabs';
import AdaptiveTabsMenu from './AdaptiveTabsMenu'; import AdaptiveTabsMenu from './AdaptiveTabsMenu';
import useAdaptiveTabs from './useAdaptiveTabs'; import useAdaptiveTabs from './useAdaptiveTabs';
import useScrollToActiveTab from './useScrollToActiveTab'; import useScrollToActiveTab from './useScrollToActiveTab';
......
import { Icon } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TabItem } from './types'; import type { TabItem } from './types';
import { PopoverBody, PopoverCloseTriggerWrapper, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; import DotsIcon from 'icons/dots.svg';
import { TabsCounter, TabsTrigger } from 'toolkit/chakra/tabs';
import IconSvg from 'ui/shared/IconSvg';
import { IconButton } from '../../chakra/icon-button'; import { IconButton } from '../../chakra/icon-button';
import type { IconButtonProps } from '../../chakra/icon-button'; import type { IconButtonProps } from '../../chakra/icon-button';
import { PopoverBody, PopoverCloseTriggerWrapper, PopoverContent, PopoverRoot, PopoverTrigger } from '../../chakra/popover';
import { TabsCounter, TabsTrigger } from '../../chakra/tabs';
import { getTabValue } from './utils'; import { getTabValue } from './utils';
interface Props extends IconButtonProps { interface Props extends IconButtonProps {
...@@ -38,7 +39,7 @@ const AdaptiveTabsMenu = ({ tabs, tabsCut, isActive, ...props }: Props, ref: Rea ...@@ -38,7 +39,7 @@ const AdaptiveTabsMenu = ({ tabs, tabsCut, isActive, ...props }: Props, ref: Rea
px="18px" px="18px"
{ ...props } { ...props }
> >
<IconSvg name="dots" boxSize={ 5 }/> <Icon boxSize={ 5 }><DotsIcon/></Icon>
</IconButton> </IconButton>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent> <PopoverContent>
......
export type { TabItemRegular, TabItemMenu, SubTabItem } from './types';
export type { Props } from './AdaptiveTabs';
export { default } from './AdaptiveTabs';
import type { TabItem, TabItemMenu } from './types'; import type { TabItem, TabItemMenu } from './types';
import { middot } from 'lib/html-entities'; import { middot } from 'toolkit/utils/htmlEntities';
export const menuButton: TabItemMenu = { export const menuButton: TabItemMenu = {
id: 'menu', id: 'menu',
......
import { chakra } from '@chakra-ui/react'; import { Icon } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { IconButton } from 'toolkit/chakra/icon-button'; import InfoIcon from 'icons/info.svg';
import type { TooltipProps } from 'toolkit/chakra/tooltip';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
interface Props { import type { IconButtonProps } from '../../chakra/icon-button';
import { IconButton } from '../../chakra/icon-button';
import type { TooltipProps } from '../../chakra/tooltip';
import { Tooltip } from '../../chakra/tooltip';
interface Props extends IconButtonProps {
label: string | React.ReactNode; label: string | React.ReactNode;
className?: string;
tooltipProps?: Partial<TooltipProps>; tooltipProps?: Partial<TooltipProps>;
isLoading?: boolean; isLoading?: boolean;
as?: React.ElementType; as?: React.ElementType;
} }
const Hint = ({ label, className, tooltipProps, isLoading, as }: Props) => { export const Hint = React.memo(({ label, tooltipProps, isLoading, boxSize = 5, ...rest }: Props) => {
return ( return (
<Tooltip <Tooltip
content={ label } content={ label }
positioning={{ placement: 'top' }} positioning={{ placement: 'top' }}
interactive
{ ...tooltipProps } { ...tooltipProps }
> >
<IconButton <IconButton
aria-label="hint" aria-label="hint"
boxSize={ 5 } boxSize={ boxSize }
className={ className }
loadingSkeleton={ isLoading } loadingSkeleton={ isLoading }
borderRadius="sm" borderRadius="sm"
as={ as }
color="icon.info" color="icon.info"
_hover={{ color: 'link.primary.hover' }} _hover={{ color: 'link.primary.hover' }}
{ ...rest }
> >
<IconSvg name="info" w="100%" h="100%"/> <Icon boxSize={ boxSize }>
<InfoIcon/>
</Icon>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
); );
}; });
export default React.memo(chakra(Hint));
...@@ -23,7 +23,7 @@ const RoutedTabs = (props: Props) => { ...@@ -23,7 +23,7 @@ const RoutedTabs = (props: Props) => {
return; return;
} }
const queryForPathname = pickBy(router.query, (value, key) => router.pathname.includes(`[${ key }]`)); const queryForPathname = pickBy(router.query, (_, key) => router.pathname.includes(`[${ key }]`));
router.push( router.push(
{ pathname: router.pathname, query: { ...queryForPathname, tab: value } }, { pathname: router.pathname, query: { ...queryForPathname, tab: value } },
undefined, undefined,
......
...@@ -3,9 +3,8 @@ import React from 'react'; ...@@ -3,9 +3,8 @@ import React from 'react';
import type { TabItemRegular } from '../AdaptiveTabs/types'; import type { TabItemRegular } from '../AdaptiveTabs/types';
import { Skeleton } from 'toolkit/chakra/skeleton'; import { Skeleton } from '../../chakra/skeleton';
import type { TabsProps } from 'toolkit/chakra/tabs'; import type { TabsProps } from '../../chakra/tabs';
import useActiveTabFromQuery from './useActiveTabFromQuery'; import useActiveTabFromQuery from './useActiveTabFromQuery';
const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabItemRegular['title'] }) => ( const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabItemRegular['title'] }) => (
......
export { default as RoutedTabs } from './RoutedTabs';
export { default as RoutedTabsSkeleton } from './RoutedTabsSkeleton';
export { default as useActiveTabFromQuery } from './useActiveTabFromQuery';
...@@ -2,11 +2,11 @@ import { useRouter } from 'next/router'; ...@@ -2,11 +2,11 @@ import { useRouter } from 'next/router';
import type { TabItem } from '../AdaptiveTabs/types'; import type { TabItem } from '../AdaptiveTabs/types';
import getQueryParamString from 'lib/router/getQueryParamString'; import { castToString } from '../../utils/guards';
export default function useActiveTabFromQuery(tabs: Array<TabItem>) { export default function useActiveTabFromQuery(tabs: Array<TabItem>) {
const router = useRouter(); const router = useRouter();
const tabFromQuery = getQueryParamString(router.query.tab); const tabFromQuery = castToString(router.query.tab);
if (!tabFromQuery) { if (!tabFromQuery) {
return; return;
......
import { Icon } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { IconButtonProps } from 'toolkit/chakra/icon-button'; import ArrowIcon from 'icons/arrows/east.svg';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Link } from 'toolkit/chakra/link';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
interface Props extends IconButtonProps { import type { IconButtonProps } from '../../chakra/icon-button';
import { IconButton } from '../../chakra/icon-button';
import { Link } from '../../chakra/link';
import { Tooltip } from '../../chakra/tooltip';
export interface BackToButtonProps extends IconButtonProps {
href?: string; href?: string;
hint?: string; hint?: string;
} }
const ButtonBackTo = ({ href, hint, ...rest }: Props) => { export const BackToButton = ({ href, hint, boxSize = 6, ...rest }: BackToButtonProps) => {
const button = ( const button = (
<IconButton { ...rest } boxSize={ 6 }> <IconButton { ...rest } boxSize={ boxSize }>
<IconSvg <Icon
name="arrows/east"
transform="rotate(180deg)" transform="rotate(180deg)"
color="icon.backTo" color="icon.backTo"
_hover={{ color: 'link.primary.hover' }} _hover={{ color: 'link.primary.hover' }}
/> boxSize={ boxSize }
>
<ArrowIcon/>
</Icon>
</IconButton> </IconButton>
); );
...@@ -30,5 +34,3 @@ const ButtonBackTo = ({ href, hint, ...rest }: Props) => { ...@@ -30,5 +34,3 @@ const ButtonBackTo = ({ href, hint, ...rest }: Props) => {
</Tooltip> </Tooltip>
); );
}; };
export default React.memo(ButtonBackTo);
import React from 'react';
import type { CloseButtonProps } from '../../chakra/close-button';
import { CloseButton } from '../../chakra/close-button';
export interface ClearButtonProps extends CloseButtonProps {
visible?: boolean;
}
export const ClearButton = ({ disabled, visible = true, ...rest }: ClearButtonProps) => {
return (
<CloseButton
disabled={ disabled || !visible }
aria-label="Clear"
title="Clear"
opacity={ visible ? 1 : 0 }
visibility={ visible ? 'visible' : 'hidden' }
{ ...rest }
/>
);
};
import { Icon } from '@chakra-ui/react';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { InputProps } from 'toolkit/chakra/input'; import SearchIcon from 'icons/search.svg';
import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group';
import type { SkeletonProps } from 'toolkit/chakra/skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import ClearButton from 'ui/shared/ClearButton';
import IconSvg from 'ui/shared/IconSvg';
interface Props extends Omit<SkeletonProps, 'onChange' | 'loading'> { import type { InputProps } from '../../chakra/input';
import { Input } from '../../chakra/input';
import { InputGroup } from '../../chakra/input-group';
import type { SkeletonProps } from '../../chakra/skeleton';
import { Skeleton } from '../../chakra/skeleton';
import { ClearButton } from '../buttons/ClearButton';
export interface FilterInputProps extends Omit<SkeletonProps, 'onChange' | 'loading'> {
onChange?: (searchTerm: string) => void; onChange?: (searchTerm: string) => void;
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void; onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void; onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
...@@ -22,7 +24,19 @@ interface Props extends Omit<SkeletonProps, 'onChange' | 'loading'> { ...@@ -22,7 +24,19 @@ interface Props extends Omit<SkeletonProps, 'onChange' | 'loading'> {
inputProps?: InputProps; inputProps?: InputProps;
}; };
const FilterInput = ({ onChange, size = 'sm', placeholder, initialValue, type, name, loading = false, onFocus, onBlur, inputProps, ...rest }: Props) => { export const FilterInput = ({
onChange,
size = 'sm',
placeholder,
initialValue,
type,
name,
loading = false,
onFocus,
onBlur,
inputProps,
...rest
}: FilterInputProps) => {
const [ filterQuery, setFilterQuery ] = useState(initialValue || ''); const [ filterQuery, setFilterQuery ] = useState(initialValue || '');
const inputRef = React.useRef<HTMLInputElement>(null); const inputRef = React.useRef<HTMLInputElement>(null);
...@@ -39,9 +53,8 @@ const FilterInput = ({ onChange, size = 'sm', placeholder, initialValue, type, n ...@@ -39,9 +53,8 @@ const FilterInput = ({ onChange, size = 'sm', placeholder, initialValue, type, n
inputRef?.current?.focus(); inputRef?.current?.focus();
}, [ onChange ]); }, [ onChange ]);
const startElement = <IconSvg name="search" boxSize={ 5 }/>; const startElement = <Icon boxSize={ 5 }><SearchIcon/></Icon>;
const endElement = <ClearButton onClick={ handleFilterQueryClear } visible={ filterQuery.length > 0 }/>;
const endElement = <ClearButton onClick={ handleFilterQueryClear } isVisible={ filterQuery.length > 0 }/>;
return ( return (
<Skeleton <Skeleton
...@@ -75,5 +88,3 @@ const FilterInput = ({ onChange, size = 'sm', placeholder, initialValue, type, n ...@@ -75,5 +88,3 @@ const FilterInput = ({ onChange, size = 'sm', placeholder, initialValue, type, n
</Skeleton> </Skeleton>
); );
}; };
export default FilterInput;
...@@ -6,8 +6,6 @@ interface Props { ...@@ -6,8 +6,6 @@ interface Props {
className?: string; className?: string;
} }
const FieldError = ({ message, className }: Props) => { export const FormFieldError = chakra(({ message, className }: Props) => {
return <Box className={ className } color="text.error" textStyle="sm" mt={ 2 } wordBreak="break-word">{ message }</Box>; return <Box className={ className } color="text.error" textStyle="sm" mt={ 2 } wordBreak="break-word">{ message }</Box>;
}; });
export default chakra(FieldError);
export * from './FormFieldError';
...@@ -5,9 +5,9 @@ import type { FormFieldPropsBase } from './types'; ...@@ -5,9 +5,9 @@ import type { FormFieldPropsBase } from './types';
import type { PartialBy } from 'types/utils'; import type { PartialBy } from 'types/utils';
import { addressValidator } from '../validators/address'; import { addressValidator } from '../validators/address';
import FormFieldText from './FormFieldText'; import { FormFieldText } from './FormFieldText';
const FormFieldAddress = <FormFields extends FieldValues>( const FormFieldAddressContent = <FormFields extends FieldValues>(
props: PartialBy<FormFieldPropsBase<FormFields>, 'placeholder'>, props: PartialBy<FormFieldPropsBase<FormFields>, 'placeholder'>,
) => { ) => {
const rules = React.useMemo( const rules = React.useMemo(
...@@ -30,4 +30,4 @@ const FormFieldAddress = <FormFields extends FieldValues>( ...@@ -30,4 +30,4 @@ const FormFieldAddress = <FormFields extends FieldValues>(
); );
}; };
export default React.memo(FormFieldAddress) as typeof FormFieldAddress; export const FormFieldAddress = React.memo(FormFieldAddressContent) as typeof FormFieldAddressContent;
...@@ -3,8 +3,8 @@ import { useController, useFormContext, type FieldValues, type Path } from 'reac ...@@ -3,8 +3,8 @@ import { useController, useFormContext, type FieldValues, type Path } from 'reac
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import type { CheckboxProps } from 'toolkit/chakra/checkbox'; import { Checkbox } from '../../../chakra/checkbox';
import { Checkbox } from 'toolkit/chakra/checkbox'; import type { CheckboxProps } from '../../../chakra/checkbox';
interface Props< interface Props<
FormFields extends FieldValues, FormFields extends FieldValues,
...@@ -13,7 +13,7 @@ interface Props< ...@@ -13,7 +13,7 @@ interface Props<
label: string; label: string;
} }
const FormFieldCheckbox = < const FormFieldCheckboxContent = <
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields> = Path<FormFields>, Name extends Path<FormFields> = Path<FormFields>,
>({ >({
...@@ -52,4 +52,4 @@ const FormFieldCheckbox = < ...@@ -52,4 +52,4 @@ const FormFieldCheckbox = <
); );
}; };
export default React.memo(FormFieldCheckbox) as typeof FormFieldCheckbox; export const FormFieldCheckbox = React.memo(FormFieldCheckboxContent) as typeof FormFieldCheckboxContent;
...@@ -6,13 +6,12 @@ import { useController, useFormContext } from 'react-hook-form'; ...@@ -6,13 +6,12 @@ import { useController, useFormContext } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import { Field } from 'toolkit/chakra/field'; import { Field } from '../../../chakra/field';
import type { InputProps } from 'toolkit/chakra/input'; import type { InputProps } from '../../../chakra/input';
import { Input } from 'toolkit/chakra/input'; import { Input } from '../../../chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group'; import { InputGroup } from '../../../chakra/input-group';
import { validator as colorValidator } from 'ui/shared/forms/validators/color'; import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
import { colorValidator } from '../validators/color';
import getFieldErrorText from '../utils/getFieldErrorText';
interface Props< interface Props<
FormFields extends FieldValues, FormFields extends FieldValues,
...@@ -21,7 +20,7 @@ interface Props< ...@@ -21,7 +20,7 @@ interface Props<
sampleDefaultBgColor?: BoxProps['bgColor']; sampleDefaultBgColor?: BoxProps['bgColor'];
} }
const FormFieldColor = < const FormFieldColorContent = <
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields> = Path<FormFields>, Name extends Path<FormFields> = Path<FormFields>,
>({ >({
...@@ -82,7 +81,7 @@ const FormFieldColor = < ...@@ -82,7 +81,7 @@ const FormFieldColor = <
return ( return (
<Field <Field
label={ placeholder } label={ placeholder }
errorText={ getFieldErrorText(fieldState.error) } errorText={ getFormFieldErrorText(fieldState.error) }
invalid={ Boolean(fieldState.error) } invalid={ Boolean(fieldState.error) }
disabled={ formState.isSubmitting || disabled } disabled={ formState.isSubmitting || disabled }
size={ size } size={ size }
...@@ -106,4 +105,4 @@ const FormFieldColor = < ...@@ -106,4 +105,4 @@ const FormFieldColor = <
); );
}; };
export default React.memo(FormFieldColor) as typeof FormFieldColor; export const FormFieldColor = React.memo(FormFieldColorContent) as typeof FormFieldColorContent;
...@@ -5,9 +5,9 @@ import type { FormFieldPropsBase } from './types'; ...@@ -5,9 +5,9 @@ import type { FormFieldPropsBase } from './types';
import type { PartialBy } from 'types/utils'; import type { PartialBy } from 'types/utils';
import { EMAIL_REGEXP } from '../validators/email'; import { EMAIL_REGEXP } from '../validators/email';
import FormFieldText from './FormFieldText'; import { FormFieldText } from './FormFieldText';
const FormFieldEmail = <FormFields extends FieldValues>( const FormFieldEmailContent = <FormFields extends FieldValues>(
props: PartialBy<FormFieldPropsBase<FormFields>, 'placeholder'>, props: PartialBy<FormFieldPropsBase<FormFields>, 'placeholder'>,
) => { ) => {
const rules = React.useMemo( const rules = React.useMemo(
...@@ -27,4 +27,4 @@ const FormFieldEmail = <FormFields extends FieldValues>( ...@@ -27,4 +27,4 @@ const FormFieldEmail = <FormFields extends FieldValues>(
); );
}; };
export default React.memo(FormFieldEmail) as typeof FormFieldEmail; export const FormFieldEmail = React.memo(FormFieldEmailContent) as typeof FormFieldEmailContent;
...@@ -4,17 +4,16 @@ import { useController, useFormContext } from 'react-hook-form'; ...@@ -4,17 +4,16 @@ import { useController, useFormContext } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import type { SelectProps } from 'toolkit/chakra/select'; import type { SelectProps } from '../../../chakra/select';
import { Select } from 'toolkit/chakra/select'; import { Select } from '../../../chakra/select';
import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
import getFieldErrorText from '../utils/getFieldErrorText';
type Props< type Props<
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields>, Name extends Path<FormFields>,
> = FormFieldPropsBase<FormFields, Name> & SelectProps; > = FormFieldPropsBase<FormFields, Name> & SelectProps;
const FormFieldSelect = < const FormFieldSelectContent = <
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields>, Name extends Path<FormFields>,
>(props: Props<FormFields, Name>) => { >(props: Props<FormFields, Name>) => {
...@@ -47,7 +46,7 @@ const FormFieldSelect = < ...@@ -47,7 +46,7 @@ const FormFieldSelect = <
onInteractOutside={ handleBlur } onInteractOutside={ handleBlur }
disabled={ isDisabled } disabled={ isDisabled }
invalid={ Boolean(fieldState.error) } invalid={ Boolean(fieldState.error) }
errorText={ getFieldErrorText(fieldState.error) } errorText={ getFormFieldErrorText(fieldState.error) }
size={ size } size={ size }
positioning={{ sameWidth: true }} positioning={{ sameWidth: true }}
{ ...rest } { ...rest }
...@@ -55,4 +54,4 @@ const FormFieldSelect = < ...@@ -55,4 +54,4 @@ const FormFieldSelect = <
); );
}; };
export default React.memo(FormFieldSelect) as typeof FormFieldSelect; export const FormFieldSelect = React.memo(FormFieldSelectContent) as typeof FormFieldSelectContent;
...@@ -4,17 +4,16 @@ import { useController, useFormContext } from 'react-hook-form'; ...@@ -4,17 +4,16 @@ import { useController, useFormContext } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import type { SelectAsyncProps } from 'toolkit/chakra/select'; import type { SelectAsyncProps } from '../../../chakra/select';
import { SelectAsync } from 'toolkit/chakra/select'; import { SelectAsync } from '../../../chakra/select';
import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
import getFieldErrorText from '../utils/getFieldErrorText';
type Props< type Props<
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields>, Name extends Path<FormFields>,
> = FormFieldPropsBase<FormFields, Name> & SelectAsyncProps; > = FormFieldPropsBase<FormFields, Name> & SelectAsyncProps;
const FormFieldSelectAsync = < const FormFieldSelectAsyncContent = <
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields>, Name extends Path<FormFields>,
>(props: Props<FormFields, Name>) => { >(props: Props<FormFields, Name>) => {
...@@ -47,7 +46,7 @@ const FormFieldSelectAsync = < ...@@ -47,7 +46,7 @@ const FormFieldSelectAsync = <
onInteractOutside={ handleBlur } onInteractOutside={ handleBlur }
disabled={ isDisabled } disabled={ isDisabled }
invalid={ Boolean(fieldState.error) } invalid={ Boolean(fieldState.error) }
errorText={ getFieldErrorText(fieldState.error) } errorText={ getFormFieldErrorText(fieldState.error) }
size={ size } size={ size }
positioning={{ sameWidth: true }} positioning={{ sameWidth: true }}
{ ...rest } { ...rest }
...@@ -55,4 +54,4 @@ const FormFieldSelectAsync = < ...@@ -55,4 +54,4 @@ const FormFieldSelectAsync = <
); );
}; };
export default React.memo(FormFieldSelectAsync) as typeof FormFieldSelectAsync; export const FormFieldSelectAsync = React.memo(FormFieldSelectAsyncContent) as typeof FormFieldSelectAsyncContent;
...@@ -4,14 +4,13 @@ import { useController, useFormContext } from 'react-hook-form'; ...@@ -4,14 +4,13 @@ import { useController, useFormContext } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import { Field } from 'toolkit/chakra/field'; import { Field } from '../../../chakra/field';
import type { InputProps } from 'toolkit/chakra/input'; import type { InputProps } from '../../../chakra/input';
import { Input } from 'toolkit/chakra/input'; import { Input } from '../../../chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group'; import { InputGroup } from '../../../chakra/input-group';
import type { TextareaProps } from 'toolkit/chakra/textarea'; import type { TextareaProps } from '../../../chakra/textarea';
import { Textarea } from 'toolkit/chakra/textarea'; import { Textarea } from '../../../chakra/textarea';
import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
import getFieldErrorText from '../utils/getFieldErrorText';
interface Props< interface Props<
FormFields extends FieldValues, FormFields extends FieldValues,
...@@ -20,7 +19,7 @@ interface Props< ...@@ -20,7 +19,7 @@ interface Props<
asComponent?: 'Input' | 'Textarea'; asComponent?: 'Input' | 'Textarea';
} }
const FormFieldText = < const FormFieldTextContent = <
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields> = Path<FormFields>, Name extends Path<FormFields> = Path<FormFields>,
>({ >({
...@@ -84,7 +83,7 @@ const FormFieldText = < ...@@ -84,7 +83,7 @@ const FormFieldText = <
<Field <Field
// for floating field label, we pass placeholder value to the label // for floating field label, we pass placeholder value to the label
label={ floating ? placeholder : undefined } label={ floating ? placeholder : undefined }
errorText={ getFieldErrorText(fieldState.error) } errorText={ getFormFieldErrorText(fieldState.error) }
invalid={ Boolean(fieldState.error) } invalid={ Boolean(fieldState.error) }
disabled={ formState.isSubmitting || disabled } disabled={ formState.isSubmitting || disabled }
size={ size } size={ size }
...@@ -96,4 +95,4 @@ const FormFieldText = < ...@@ -96,4 +95,4 @@ const FormFieldText = <
); );
}; };
export default React.memo(FormFieldText) as typeof FormFieldText; export const FormFieldText = React.memo(FormFieldTextContent) as typeof FormFieldTextContent;
...@@ -4,9 +4,9 @@ import type { FieldValues } from 'react-hook-form'; ...@@ -4,9 +4,9 @@ import type { FieldValues } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import { urlValidator } from '../validators/url'; import { urlValidator } from '../validators/url';
import FormFieldText from './FormFieldText'; import { FormFieldText } from './FormFieldText';
const FormFieldUrl = <FormFields extends FieldValues>( const FormFieldUrlContent = <FormFields extends FieldValues>(
props: FormFieldPropsBase<FormFields>, props: FormFieldPropsBase<FormFields>,
) => { ) => {
const rules = React.useMemo( const rules = React.useMemo(
...@@ -23,4 +23,4 @@ const FormFieldUrl = <FormFields extends FieldValues>( ...@@ -23,4 +23,4 @@ const FormFieldUrl = <FormFields extends FieldValues>(
return <FormFieldText { ...props } rules={ rules }/>; return <FormFieldText { ...props } rules={ rules }/>;
}; };
export default React.memo(FormFieldUrl) as typeof FormFieldUrl; export const FormFieldUrl = React.memo(FormFieldUrlContent) as typeof FormFieldUrlContent;
import { chakra } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ColorMode } from 'toolkit/chakra/color-mode'; import type { ColorMode } from '../../../../chakra/color-mode';
import { Image } from 'toolkit/chakra/image'; import { Image } from '../../../../chakra/image';
import { Skeleton } from 'toolkit/chakra/skeleton'; import { Skeleton } from '../../../../chakra/skeleton';
interface Props { interface Props {
src: string | undefined; src: string | undefined;
...@@ -15,7 +15,7 @@ interface Props { ...@@ -15,7 +15,7 @@ interface Props {
colorMode?: ColorMode; colorMode?: ColorMode;
} }
const ImageUrlPreview = ({ export const FormFieldImagePreview = chakra(React.memo(({
src, src,
isInvalid, isInvalid,
onError, onError,
...@@ -45,6 +45,4 @@ const ImageUrlPreview = ({ ...@@ -45,6 +45,4 @@ const ImageUrlPreview = ({
onLoad={ onLoad } onLoad={ onLoad }
/> />
); );
}; }));
export default chakra(React.memo(ImageUrlPreview));
...@@ -2,7 +2,7 @@ import React from 'react'; ...@@ -2,7 +2,7 @@ import React from 'react';
import type { FieldValues, Path } from 'react-hook-form'; import type { FieldValues, Path } from 'react-hook-form';
import { useFormContext, useWatch } from 'react-hook-form'; import { useFormContext, useWatch } from 'react-hook-form';
import { urlValidator } from '../validators/url'; import { urlValidator } from '../../validators/url';
interface Params< interface Params<
FormFields extends FieldValues, FormFields extends FieldValues,
...@@ -31,7 +31,7 @@ interface ReturnType { ...@@ -31,7 +31,7 @@ interface ReturnType {
}; };
} }
export default function useFieldWithImagePreview< export function useImageField<
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields>, Name extends Path<FormFields>,
>({ >({
......
export * from './image/FormFieldImagePreview';
export * from './image/useImageField';
export * from './FormFieldAddress';
export * from './FormFieldCheckbox';
export * from './FormFieldColor';
export * from './FormFieldEmail';
export * from './FormFieldSelect';
export * from './FormFieldSelectAsync';
export * from './FormFieldText';
export * from './FormFieldUrl';
import type React from 'react'; import type React from 'react';
import type { ControllerRenderProps, FieldValues, Path, RegisterOptions } from 'react-hook-form'; import type { ControllerRenderProps, FieldValues, Path, RegisterOptions } from 'react-hook-form';
import type { FieldProps } from 'toolkit/chakra/field'; import type { FieldProps } from '../../../chakra/field';
import type { InputProps } from 'toolkit/chakra/input'; import type { InputProps } from '../../../chakra/input';
import type { InputGroupProps } from 'toolkit/chakra/input-group'; import type { InputGroupProps } from '../../../chakra/input-group';
import type { TextareaProps } from 'toolkit/chakra/textarea'; import type { TextareaProps } from '../../../chakra/textarea';
export interface FormFieldPropsBase< export interface FormFieldPropsBase<
FormFields extends FieldValues, FormFields extends FieldValues,
......
...@@ -13,7 +13,7 @@ interface Props { ...@@ -13,7 +13,7 @@ interface Props {
fullFilePath?: boolean; fullFilePath?: boolean;
} }
const DragAndDropArea = ({ onDrop, children, className, isDisabled, fullFilePath, isInvalid }: Props) => { export const DragAndDropArea = chakra(({ onDrop, children, className, isDisabled, fullFilePath, isInvalid }: Props) => {
const [ isDragOver, setIsDragOver ] = React.useState(false); const [ isDragOver, setIsDragOver ] = React.useState(false);
const handleDrop = React.useCallback(async(event: DragEvent<HTMLDivElement>) => { const handleDrop = React.useCallback(async(event: DragEvent<HTMLDivElement>) => {
...@@ -77,6 +77,4 @@ const DragAndDropArea = ({ onDrop, children, className, isDisabled, fullFilePath ...@@ -77,6 +77,4 @@ const DragAndDropArea = ({ onDrop, children, className, isDisabled, fullFilePath
{ children } { children }
</Center> </Center>
); );
}; });
export default React.memo(chakra(DragAndDropArea));
...@@ -3,7 +3,7 @@ import type { ChangeEvent } from 'react'; ...@@ -3,7 +3,7 @@ import type { ChangeEvent } from 'react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, FieldValues, Path } from 'react-hook-form'; import type { ControllerRenderProps, FieldValues, Path } from 'react-hook-form';
import { Input } from 'toolkit/chakra/input'; import { Input } from '../../../../chakra/input';
interface InjectedProps { interface InjectedProps {
onChange: (files: Array<File>) => void; onChange: (files: Array<File>) => void;
...@@ -16,7 +16,7 @@ interface Props<V extends FieldValues, N extends Path<V>> { ...@@ -16,7 +16,7 @@ interface Props<V extends FieldValues, N extends Path<V>> {
multiple?: boolean; multiple?: boolean;
} }
const FileInput = <Values extends FieldValues, Names extends Path<Values>>({ children, accept, multiple, field }: Props<Values, Names>) => { const FileInputContent = <Values extends FieldValues, Names extends Path<Values>>({ children, accept, multiple, field }: Props<Values, Names>) => {
const ref = React.useRef<HTMLInputElement>(null); const ref = React.useRef<HTMLInputElement>(null);
React.useEffect(() => { React.useEffect(() => {
...@@ -64,4 +64,4 @@ const FileInput = <Values extends FieldValues, Names extends Path<Values>>({ chi ...@@ -64,4 +64,4 @@ const FileInput = <Values extends FieldValues, Names extends Path<Values>>({ chi
); );
}; };
export default FileInput; export const FileInput = React.memo(FileInputContent) as typeof FileInputContent;
import { Box, Flex, Text, chakra } from '@chakra-ui/react'; import { Box, Flex, Icon, Text, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { CloseButton } from 'toolkit/chakra/close-button'; import JsonFileIcon from 'icons/files/json.svg';
import Hint from 'ui/shared/Hint'; import PlaceholderFileIcon from 'icons/files/placeholder.svg';
import type { IconName } from 'ui/shared/IconSvg'; import SolFileIcon from 'icons/files/sol.svg';
import IconSvg from 'ui/shared/IconSvg'; import YulFileIcon from 'icons/files/yul.svg';
const FILE_ICONS: Record<string, IconName> = { import { CloseButton } from '../../../../chakra/close-button';
'.json': 'files/json', import { Hint } from '../../../../components/Hint/Hint';
'.sol': 'files/sol',
'.yul': 'files/yul', const FILE_ICONS: Record<string, React.ReactNode> = {
'.json': <JsonFileIcon/>,
'.sol': <SolFileIcon/>,
'.yul': <YulFileIcon/>,
}; };
function getFileExtension(fileName: string) { function getFileExtension(fileName: string) {
...@@ -30,14 +33,14 @@ interface Props { ...@@ -30,14 +33,14 @@ interface Props {
error?: string; error?: string;
} }
const FileSnippet = ({ file, className, index, onRemove, isDisabled, error }: Props) => { export const FileSnippet = chakra(({ file, className, index, onRemove, isDisabled, error }: Props) => {
const handleRemove = React.useCallback((event: React.MouseEvent) => { const handleRemove = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation(); event.stopPropagation();
onRemove?.(index); onRemove?.(index);
}, [ index, onRemove ]); }, [ index, onRemove ]);
const fileExtension = getFileExtension(file.name); const fileExtension = getFileExtension(file.name);
const fileIcon = FILE_ICONS[fileExtension] || 'files/placeholder'; const fileIcon = FILE_ICONS[fileExtension] || <PlaceholderFileIcon/>;
return ( return (
<Flex <Flex
...@@ -48,11 +51,9 @@ const FileSnippet = ({ file, className, index, onRemove, isDisabled, error }: Pr ...@@ -48,11 +51,9 @@ const FileSnippet = ({ file, className, index, onRemove, isDisabled, error }: Pr
textAlign="left" textAlign="left"
columnGap={ 2 } columnGap={ 2 }
> >
<IconSvg <Icon boxSize="48px" color={ error ? 'text.error' : 'initial' }>
name={ fileIcon } { fileIcon }
boxSize="48px" </Icon>
color={ error ? 'text.error' : 'initial' }
/>
<Box maxW="calc(100% - 58px - 24px)"> <Box maxW="calc(100% - 58px - 24px)">
<Flex alignItems="center"> <Flex alignItems="center">
<Text <Text
...@@ -78,6 +79,4 @@ const FileSnippet = ({ file, className, index, onRemove, isDisabled, error }: Pr ...@@ -78,6 +79,4 @@ const FileSnippet = ({ file, className, index, onRemove, isDisabled, error }: Pr
</Box> </Box>
</Flex> </Flex>
); );
}; });
export default React.memo(chakra(FileSnippet));
import stripLeadingSlash from 'lib/stripLeadingSlash'; import { stripLeadingSlash } from '../../../../utils/url';
// Function to get all files in drop directory // Function to get all files in drop directory
export async function getAllFileEntries(dataTransferItemList: DataTransferItemList): Promise<Array<FileSystemFileEntry>> { export async function getAllFileEntries(dataTransferItemList: DataTransferItemList): Promise<Array<FileSystemFileEntry>> {
......
export * from './file/DragAndDropArea';
export * from './file/FileInput';
export * from './file/FileSnippet';
import type { FieldError } from 'react-hook-form'; import type { FieldError } from 'react-hook-form';
export default function getFieldErrorText(error: FieldError | undefined) { export function getFormFieldErrorText(error: FieldError | undefined) {
if (!error?.message && error?.type === 'pattern') { if (!error?.message && error?.type === 'pattern') {
return 'Invalid format'; return 'Invalid format';
} }
......
export * from './getFormFieldErrorText';
export const COLOR_HEX_REGEXP = /^#[a-f\d]{3,6}$/i; export const COLOR_HEX_REGEXP = /^#[a-f\d]{3,6}$/i;
export const validator = (value: unknown) => { export const colorValidator = (value: unknown) => {
if (typeof value !== 'string') { if (typeof value !== 'string') {
return true; return true;
} }
......
export const EMAIL_REGEXP = /^[\w.%+-]+@[a-z\d-]+(?:\.[a-z\d-]+)+$/i; export const EMAIL_REGEXP = /^[\w.%+-]+@[a-z\d-]+(?:\.[a-z\d-]+)+$/i;
export const validator = (value: string) => EMAIL_REGEXP.test(value) ? true : 'Invalid email'; export const emailValidator = (value: string) => EMAIL_REGEXP.test(value) ? true : 'Invalid email';
export * from './address';
export * from './color';
export * from './email';
export * from './signature';
export * from './text';
export * from './transaction';
export * from './url';
...@@ -3,17 +3,17 @@ import { debounce } from 'es-toolkit'; ...@@ -3,17 +3,17 @@ import { debounce } from 'es-toolkit';
import React from 'react'; import React from 'react';
import useFontFaceObserver from 'use-font-face-observer'; import useFontFaceObserver from 'use-font-face-observer';
import { Tooltip } from 'toolkit/chakra/tooltip'; import { Tooltip } from '../../chakra/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import { useDisclosure } from '../../hooks/useDisclosure';
import { BODY_TYPEFACE } from 'toolkit/theme/foundations/typography'; import { BODY_TYPEFACE } from '../../theme/foundations/typography';
interface Props { export interface TruncatedTextTooltipProps {
children: React.ReactNode; children: React.ReactNode;
label: React.ReactNode; label: React.ReactNode;
placement?: Placement; placement?: Placement;
} }
const TruncatedTextTooltip = ({ children, label, placement }: Props) => { export const TruncatedTextTooltip = React.memo(({ children, label, placement }: TruncatedTextTooltipProps) => {
const childRef = React.useRef<HTMLElement>(null); const childRef = React.useRef<HTMLElement>(null);
const [ isTruncated, setTruncated ] = React.useState(false); const [ isTruncated, setTruncated ] = React.useState(false);
const { open, onToggle, onOpen, onClose } = useDisclosure(); const { open, onToggle, onOpen, onClose } = useDisclosure();
...@@ -90,6 +90,4 @@ const TruncatedTextTooltip = ({ children, label, placement }: Props) => { ...@@ -90,6 +90,4 @@ const TruncatedTextTooltip = ({ children, label, placement }: Props) => {
} }
return modifiedChildren; return modifiedChildren;
}; });
export default React.memo(TruncatedTextTooltip);
import { useCopyToClipboard } from '@uidotdev/usehooks'; import { useCopyToClipboard } from '@uidotdev/usehooks';
import React from 'react'; import React from 'react';
import { SECOND } from 'lib/consts';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { SECOND } from 'toolkit/utils/consts';
import { useDisclosure } from './useDisclosure'; import { useDisclosure } from './useDisclosure';
// NOTE: If you don't need the disclosure and the timeout features, please use the useCopyToClipboard hook directly // NOTE: If you don't need the disclosure and the timeout features, please use the useCopyToClipboard hook directly
export default function useClipboard(text: string, timeout = SECOND) { export function useClipboard(text: string, timeout = SECOND) {
const flagTimeoutRef = React.useRef<number | null>(null); const flagTimeoutRef = React.useRef<number | null>(null);
const disclosureTimeoutRef = React.useRef<number | null>(null); const disclosureTimeoutRef = React.useRef<number | null>(null);
const [ hasCopied, setHasCopied ] = React.useState(false); const [ hasCopied, setHasCopied ] = React.useState(false);
......
import { throttle } from 'es-toolkit'; import { throttle } from 'es-toolkit';
import React from 'react'; import React from 'react';
export default function useIsSticky(ref: React.RefObject<HTMLDivElement>, offset = 0, isEnabled = true) { export function useIsSticky(ref: React.RefObject<HTMLDivElement>, offset = 0, isEnabled = true) {
const [ isSticky, setIsSticky ] = React.useState(false); const [ isSticky, setIsSticky ] = React.useState(false);
const handleScroll = React.useCallback(() => { const handleScroll = React.useCallback(() => {
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import { useFirstMountState } from './useFirstMountState'; import { useFirstMountState } from './useFirstMountState';
// React effect hook that ignores the first invocation (e.g. on mount). The signature is exactly the same as the useEffect hook. // React effect hook that ignores the first invocation (e.g. on mount). The signature is exactly the same as the useEffect hook.
const useUpdateEffect: typeof React.useEffect = (effect, deps) => { export const useUpdateEffect: typeof React.useEffect = (effect, deps) => {
const isFirstMount = useFirstMountState(); const isFirstMount = useFirstMountState();
React.useEffect(() => { React.useEffect(() => {
...@@ -13,5 +13,3 @@ const useUpdateEffect: typeof React.useEffect = (effect, deps) => { ...@@ -13,5 +13,3 @@ const useUpdateEffect: typeof React.useEffect = (effect, deps) => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, deps); }, deps);
}; };
export default useUpdateEffect;
import { debounce } from 'es-toolkit'; import { debounce } from 'es-toolkit';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
export default function useViewportSize(debounceTime = 100) { export function useViewportSize(debounceTime = 100) {
const [ viewportSize, setViewportSize ] = useState({ width: 0, height: 0 }); const [ viewportSize, setViewportSize ] = useState({ width: 0, height: 0 });
useEffect(() => { useEffect(() => {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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