mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Merge branch 'main' of github.com:blockscout/frontend into release/v2-0-0
This commit is contained in:
@@ -51,6 +51,65 @@ jobs:
|
||||
- name: Compile TypeScript
|
||||
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:
|
||||
name: ENV variables validation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -90,6 +90,13 @@ jobs:
|
||||
needs: publish_image
|
||||
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
|
||||
# There is an issue with building web3modal deps
|
||||
upload_source_maps:
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
name: Publish Chakra theme package to NPM
|
||||
name: Publish toolkit package to NPM
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -34,19 +34,19 @@ jobs:
|
||||
|
||||
- name: Update package version
|
||||
run: |
|
||||
cd ./theme
|
||||
cd ./toolkit/package
|
||||
npm version ${{ inputs.version }}
|
||||
|
||||
- name: Build the package
|
||||
run: |
|
||||
yarn
|
||||
cd ./theme
|
||||
cd ./toolkit/package
|
||||
yarn
|
||||
yarn build
|
||||
|
||||
- name: Publish to NPM registry
|
||||
run: |
|
||||
cd ./theme
|
||||
cd ./toolkit/package
|
||||
npm publish --provenance --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -31,6 +31,7 @@
|
||||
*.pem
|
||||
.tools
|
||||
grafana
|
||||
.cursor
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
@@ -60,3 +61,4 @@ yarn-error.log*
|
||||
|
||||
# build outputs
|
||||
/tools/preset-sync/index.js
|
||||
/toolkit/package/dist
|
||||
Vendored
+9
-1
@@ -1,3 +1,11 @@
|
||||
{
|
||||
"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",
|
||||
]
|
||||
}
|
||||
+3
-1
@@ -14,7 +14,8 @@ COPY types ./types
|
||||
COPY lib ./lib
|
||||
COPY configs/app ./configs/app
|
||||
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 yarn --frozen-lockfile --network-timeout 100000
|
||||
|
||||
@@ -79,6 +80,7 @@ RUN set -a && \
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# Build app for production
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
RUN yarn build
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import stripTrailingSlash from 'lib/stripTrailingSlash';
|
||||
import { stripTrailingSlash } from 'toolkit/utils/url';
|
||||
|
||||
import { getEnvValue } from './utils';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { RollupType } from 'types/client/rollup';
|
||||
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';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Feature } from './types';
|
||||
import type { ParentChain, RollupType } 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';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Feature } from './types';
|
||||
|
||||
import stripTrailingSlash from 'lib/stripTrailingSlash';
|
||||
import { stripTrailingSlash } from 'toolkit/utils/url';
|
||||
|
||||
import { getEnvValue } from '../utils';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Feature } from './types';
|
||||
|
||||
import stripTrailingSlash from 'lib/stripTrailingSlash';
|
||||
import { stripTrailingSlash } from 'toolkit/utils/url';
|
||||
|
||||
import { getEnvValue } from '../utils';
|
||||
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import type { NetworkExplorer } from 'types/networks';
|
||||
import type { ColorThemeId } from 'types/settings';
|
||||
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 views from './ui/views';
|
||||
@@ -49,7 +49,7 @@ const highlightedRoutes = (() => {
|
||||
|
||||
const defaultColorTheme = (() => {
|
||||
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({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import isBrowser from 'lib/isBrowser';
|
||||
import * as regexp from 'lib/regexp';
|
||||
import { isBrowser } from 'toolkit/utils/isBrowser';
|
||||
import * as regexp from 'toolkit/utils/regexp';
|
||||
|
||||
export const replaceQuotes = (value: string | undefined) => value?.replaceAll('\'', '"');
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import type { VerifiedContractsFilter } from '../../../types/api/contracts';
|
||||
import type { TxExternalTxsConfig } from '../../../types/client/externalTxsConfig';
|
||||
|
||||
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';
|
||||
|
||||
const protocols = [ 'http', 'https' ];
|
||||
|
||||
+4
-4
@@ -29,7 +29,7 @@ const RESTRICTED_MODULES = {
|
||||
name: '@chakra-ui/react',
|
||||
importNames: [
|
||||
'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',
|
||||
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
|
||||
'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
|
||||
@@ -37,7 +37,7 @@ const RESTRICTED_MODULES = {
|
||||
'Heading', 'Badge', 'Tabs', 'Show', 'Hide', 'Checkbox', 'CheckboxGroup',
|
||||
'Table', 'TableRoot', 'TableBody', 'TableHeader', 'TableRow', 'TableCell',
|
||||
'Menu', 'MenuRoot', 'MenuTrigger', 'MenuContent', 'MenuItem', 'MenuTriggerItem', 'MenuRadioItemGroup', 'MenuContextTrigger',
|
||||
'Rating', 'RatingGroup',
|
||||
'Rating', 'RatingGroup', 'Textarea',
|
||||
],
|
||||
message: 'Please use corresponding component or hook from "toolkit" instead',
|
||||
},
|
||||
@@ -65,9 +65,7 @@ export default tseslint.config(
|
||||
{ ignores: [
|
||||
'deploy/tools/',
|
||||
'public/',
|
||||
'theme/dist/',
|
||||
'.git/',
|
||||
'theme/webpack.config.js',
|
||||
'next.config.js',
|
||||
] },
|
||||
|
||||
@@ -455,6 +453,8 @@ export default tseslint.config(
|
||||
{
|
||||
files: [
|
||||
'toolkit/chakra/**',
|
||||
'toolkit/components/**',
|
||||
'toolkit/package/**',
|
||||
],
|
||||
rules: {
|
||||
// for toolkit components allow to import @chakra-ui/react directly
|
||||
|
||||
@@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js';
|
||||
|
||||
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) {
|
||||
const totalReward = block.rewards
|
||||
|
||||
@@ -11,7 +11,6 @@ import config from 'configs/app';
|
||||
import type { ResourceError } from 'lib/api/resources';
|
||||
import useApiFetch from 'lib/api/useApiFetch';
|
||||
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
|
||||
import { YEAR } from 'lib/consts';
|
||||
import * as cookies from 'lib/cookies';
|
||||
import decodeJWT from 'lib/decodeJWT';
|
||||
import getErrorMessage from 'lib/errors/getErrorMessage';
|
||||
@@ -20,6 +19,7 @@ import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import removeQueryParam from 'lib/router/removeQueryParam';
|
||||
import useAccount from 'lib/web3/useAccount';
|
||||
import { toaster } from 'toolkit/chakra/toaster';
|
||||
import { YEAR } from 'toolkit/utils/consts';
|
||||
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
|
||||
|
||||
const feature = config.features.rewards;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { throttle, clamp } from 'es-toolkit';
|
||||
import React from 'react';
|
||||
|
||||
const ScrollDirectionContext = React.createContext<'up' | 'down' | null>(null);
|
||||
import isBrowser from 'lib/isBrowser';
|
||||
import { isBrowser } from 'toolkit/utils/isBrowser';
|
||||
|
||||
const SCROLL_DIFF_THRESHOLD = 20;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import isBrowser from './isBrowser';
|
||||
import { isBrowser } from 'toolkit/utils/isBrowser';
|
||||
|
||||
export enum NAMES {
|
||||
NAV_BAR_COLLAPSED = 'nav_bar_collapsed',
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import updateLocale from 'dayjs/plugin/updateLocale';
|
||||
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||
|
||||
import { nbsp } from 'lib/html-entities';
|
||||
import { nbsp } from 'toolkit/utils/htmlEntities';
|
||||
|
||||
const relativeTimeConfig = {
|
||||
thresholds: [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { ZERO } from 'lib/consts';
|
||||
import { ZERO } from 'toolkit/utils/consts';
|
||||
|
||||
interface Params {
|
||||
value: string;
|
||||
|
||||
@@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js';
|
||||
|
||||
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') {
|
||||
let unitBn: BigNumber.Value;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SECOND } from 'lib/consts';
|
||||
import { SECOND } from 'toolkit/utils/consts';
|
||||
|
||||
import { initGrowthBook } from './init';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { AdBannerProviders } from 'types/client/adProviders';
|
||||
import config from 'configs/app';
|
||||
import { useAppContext } from 'lib/contexts/app';
|
||||
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';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from 'react';
|
||||
import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/navigation';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { rightLineArrow } from 'lib/html-entities';
|
||||
import { rightLineArrow } from 'toolkit/utils/htmlEntities';
|
||||
|
||||
interface ReturnType {
|
||||
mainNavItems: Array<NavItem | NavGroupItem>;
|
||||
|
||||
@@ -5,8 +5,8 @@ import type { PreSubmitTransactionResponse, PreVerifyContractResponse } from '@b
|
||||
import config from 'configs/app';
|
||||
import useApiFetch from 'lib/api/useApiFetch';
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import { MINUTE } from 'lib/consts';
|
||||
import { useRewardsContext } from 'lib/contexts/rewards';
|
||||
import { MINUTE } from 'toolkit/utils/consts';
|
||||
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
|
||||
|
||||
const feature = config.features.rewards;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { DAY, HOUR, MINUTE, SECOND } from 'lib/consts';
|
||||
import dayjs from 'lib/date/dayjs';
|
||||
import { DAY, HOUR, MINUTE, SECOND } from 'toolkit/utils/consts';
|
||||
|
||||
function getUnits(diff: number) {
|
||||
if (diff < MINUTE) {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
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) {}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { uniq } from 'es-toolkit';
|
||||
|
||||
import isBrowser from './isBrowser';
|
||||
import { isBrowser } from 'toolkit/utils/isBrowser';
|
||||
|
||||
const RECENT_KEYWORDS_LS_KEY = 'recent_search_keywords';
|
||||
const MAX_KEYWORDS_NUMBER = 10;
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ColorThemeId } from 'types/settings';
|
||||
|
||||
import type { ColorMode } from 'toolkit/chakra/color-mode';
|
||||
|
||||
interface ColorTheme {
|
||||
export interface ColorTheme {
|
||||
id: ColorThemeId;
|
||||
label: string;
|
||||
colorMode: ColorMode;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
const stripLeadingSlash = (str: string) => str[0] === '/' ? str.slice(1) : str;
|
||||
|
||||
export default stripLeadingSlash;
|
||||
@@ -1,3 +0,0 @@
|
||||
const stripTrailingSlash = (str: string) => str[str.length - 1] === '/' ? str.slice(0, -1) : str;
|
||||
|
||||
export default stripTrailingSlash;
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as regexp from 'lib/regexp';
|
||||
import * as regexp from 'toolkit/utils/regexp';
|
||||
|
||||
export default function urlParser(maybeUrl: string): URL | undefined {
|
||||
try {
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from 'react';
|
||||
import type { AddEthereumChainParameter } from 'viem';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { SECOND } from 'toolkit/utils/consts';
|
||||
|
||||
import { SECOND } from '../consts';
|
||||
import useRewardsActivity from '../hooks/useRewardsActivity';
|
||||
import useProvider from './useProvider';
|
||||
import { getHexadecimalChainId } from './utils';
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { RpcBlock } from 'viem';
|
||||
|
||||
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 tokenMock from '../tokens/tokenInfo';
|
||||
|
||||
@@ -5,8 +5,8 @@ import { httpLogger } from 'nextjs/utils/logger';
|
||||
|
||||
import { RESOURCES } 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 { SECOND } from 'toolkit/utils/consts';
|
||||
|
||||
type Params<R extends ResourceName> = (
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Accordion } from '@chakra-ui/react';
|
||||
import { Accordion, Icon } from '@chakra-ui/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 {
|
||||
indicatorPlacement?: 'start' | 'end';
|
||||
@@ -57,7 +57,7 @@ export const AccordionItemTrigger = React.forwardRef<
|
||||
</Accordion.ItemIndicator>
|
||||
) : (
|
||||
<Accordion.ItemIndicator rotate={{ base: '180deg', _open: '270deg' }} display="flex">
|
||||
<IconSvg name="arrows/east-mini"/>
|
||||
<Icon boxSize={ 5 }><IndicatorIcon/></Icon>
|
||||
</Accordion.ItemIndicator>
|
||||
);
|
||||
|
||||
@@ -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<
|
||||
HTMLDivElement,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 IconSvg from 'ui/shared/IconSvg';
|
||||
import IndicatorIcon from 'icons/info_filled.svg';
|
||||
|
||||
import { CloseButton } from './close-button';
|
||||
import { Skeleton } from './skeleton';
|
||||
@@ -37,7 +37,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
||||
|
||||
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 = (() => {
|
||||
if (startElement !== undefined) {
|
||||
|
||||
@@ -2,22 +2,19 @@ import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react';
|
||||
import { chakra, Badge as ChakraBadge } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { IconName } from 'ui/shared/IconSvg';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
|
||||
|
||||
import { TruncatedTextTooltip } from '../components/truncation/TruncatedTextTooltip';
|
||||
import { Skeleton } from './skeleton';
|
||||
|
||||
export interface BadgeProps extends Omit<ChakraBadgeProps, 'colorScheme'> {
|
||||
loading?: boolean;
|
||||
iconStart?: IconName;
|
||||
startElement?: React.ReactNode;
|
||||
endElement?: React.ReactNode;
|
||||
truncated?: boolean;
|
||||
}
|
||||
|
||||
export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
||||
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>;
|
||||
|
||||
@@ -30,7 +27,7 @@ export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
||||
return (
|
||||
<Skeleton loading={ loading } asChild={ asChild } ref={ ref }>
|
||||
<ChakraBadge display="inline-flex" alignItems="center" whiteSpace="nowrap" { ...rest }>
|
||||
{ iconStart && <IconSvg name={ iconStart } boxSize="10px"/> }
|
||||
{ startElement }
|
||||
{ childrenElement }
|
||||
{ endElement }
|
||||
</ChakraBadge>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 IconSvg from 'ui/shared/IconSvg';
|
||||
import CloseIcon from 'icons/close.svg';
|
||||
|
||||
import { recipe as closeButtonRecipe } from '../theme/recipes/close-button.recipe';
|
||||
import { IconButton } from './icon-button';
|
||||
@@ -21,7 +21,7 @@ export const CloseButton = React.forwardRef<
|
||||
|
||||
return (
|
||||
<IconButton aria-label="Close" ref={ ref } css={ styles } { ...restProps }>
|
||||
{ props.children ?? <IconSvg name="close"/> }
|
||||
{ props.children ?? <Icon boxSize={ 5 }><CloseIcon/></Icon> }
|
||||
</IconButton>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,8 +2,7 @@ import { Flex, type FlexProps } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { scroller, Element } from 'react-scroll';
|
||||
|
||||
import useUpdateEffect from 'lib/hooks/useUpdateEffect';
|
||||
|
||||
import { useUpdateEffect } from '../hooks/useUpdateEffect';
|
||||
import type { LinkProps } from './link';
|
||||
import { Link } from './link';
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react';
|
||||
import * as React from 'react';
|
||||
|
||||
import ButtonBackTo from 'ui/shared/buttons/ButtonBackTo';
|
||||
|
||||
import { BackToButton } from '../components/buttons/BackToButton';
|
||||
import { CloseButton } from './close-button';
|
||||
|
||||
interface DialogContentProps extends ChakraDialog.ContentProps {
|
||||
@@ -62,7 +61,7 @@ export const DialogHeader = React.forwardRef<
|
||||
>(function DialogHeader(props, ref) {
|
||||
const { startElement: startElementProp, onBackToClick, ...rest } = props;
|
||||
|
||||
const startElement = startElementProp ?? (onBackToClick && <ButtonBackTo onClick={ onBackToClick }/>);
|
||||
const startElement = startElementProp ?? (onBackToClick && <BackToButton onClick={ onBackToClick }/>);
|
||||
|
||||
return (
|
||||
<ChakraDialog.Header ref={ ref } { ...rest }>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Field as ChakraField } from '@chakra-ui/react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { space } from 'lib/html-entities';
|
||||
import { space } from 'toolkit/utils/htmlEntities';
|
||||
|
||||
import getComponentDisplayName from '../utils/getComponentDisplayName';
|
||||
import type { InputProps } from './input';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import getComponentDisplayName from '../utils/getComponentDisplayName';
|
||||
import { Button, type ButtonProps } from './button';
|
||||
|
||||
export interface IconButtonProps extends Omit<ButtonProps, 'size'> {
|
||||
@@ -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
|
||||
// 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 clonedChildren = size && getComponentDisplayName(child.type) === 'IconSvg' ?
|
||||
React.cloneElement(child, { boxSize: 5 }) :
|
||||
child;
|
||||
const clonedChildren = size ? React.cloneElement(child, { boxSize: 5 }) : child;
|
||||
|
||||
const sizeStyle = (() => {
|
||||
switch (size) {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
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 type { LinkProps as NextLinkProps } from 'next/link';
|
||||
import React from 'react';
|
||||
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import ArrowIcon from 'icons/link_external.svg';
|
||||
|
||||
import { Skeleton } from './skeleton';
|
||||
|
||||
export const LinkExternalIcon = ({ color }: { color?: ChakraLinkProps['color'] }) => (
|
||||
<IconSvg
|
||||
name="link_external"
|
||||
<Icon
|
||||
boxSize={ 3 }
|
||||
verticalAlign="middle"
|
||||
color={ color ?? 'icon.externalLink' }
|
||||
@@ -18,7 +17,9 @@ export const LinkExternalIcon = ({ color }: { color?: ChakraLinkProps['color'] }
|
||||
color: 'inherit',
|
||||
}}
|
||||
flexShrink={ 0 }
|
||||
/>
|
||||
>
|
||||
<ArrowIcon/>
|
||||
</Icon>
|
||||
);
|
||||
|
||||
interface LinkPropsChakra extends ChakraLinkProps {
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as React from 'react';
|
||||
|
||||
import { CloseButton } from './close-button';
|
||||
|
||||
interface PopoverContentProps extends ChakraPopover.ContentProps {
|
||||
export interface PopoverContentProps extends ChakraPopover.ContentProps {
|
||||
portalled?: boolean;
|
||||
portalRef?: React.RefObject<HTMLElement>;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import theme from 'toolkit/theme/theme';
|
||||
|
||||
import theme from '../theme/theme';
|
||||
import {
|
||||
ColorModeProvider,
|
||||
type ColorModeProviderProps,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { RatingGroup, useRatingGroup } from '@chakra-ui/react';
|
||||
import { Icon, RatingGroup, useRatingGroup } from '@chakra-ui/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'> {
|
||||
count?: number;
|
||||
@@ -24,7 +25,9 @@ export const Rating = React.forwardRef<HTMLDivElement, RatingProps>(
|
||||
<RatingGroup.HiddenInput/>
|
||||
<RatingGroup.Control>
|
||||
{ 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 (
|
||||
<RatingGroup.Item key={ index } index={ index + 1 }>
|
||||
|
||||
+14
-24
@@ -1,21 +1,21 @@
|
||||
'use client';
|
||||
|
||||
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 * as React from 'react';
|
||||
|
||||
import FilterInput from 'ui/shared/filters/FilterInput';
|
||||
import type { IconName } from 'ui/shared/IconSvg';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import ArrowIcon from 'icons/arrows/east-mini.svg';
|
||||
import CheckIcon from 'icons/check.svg';
|
||||
|
||||
import { FilterInput } from '../components/filters/FilterInput';
|
||||
import { CloseButton } from './close-button';
|
||||
import { Skeleton } from './skeleton';
|
||||
|
||||
export interface SelectOption<Value extends string = string> {
|
||||
value: Value;
|
||||
label: string;
|
||||
icon?: IconName | React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface SelectControlProps extends ChakraSelect.ControlProps {
|
||||
@@ -46,7 +46,7 @@ export const SelectControl = React.forwardRef<
|
||||
_open={{ transform: 'rotate(90deg)' }}
|
||||
flexShrink={ 0 }
|
||||
>
|
||||
<IconSvg name="arrows/east-mini"/>
|
||||
<Icon boxSize={ 5 }><ArrowIcon/></Icon>
|
||||
</ChakraSelect.Indicator>
|
||||
) }
|
||||
</ChakraSelect.IndicatorGroup>
|
||||
@@ -88,26 +88,24 @@ export const SelectContent = React.forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
export interface SelectItemProps extends ChakraSelect.ItemProps {
|
||||
item: SelectOption;
|
||||
}
|
||||
|
||||
export const SelectItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
ChakraSelect.ItemProps
|
||||
SelectItemProps
|
||||
>(function SelectItem(props, ref) {
|
||||
const { item, children, ...rest } = props;
|
||||
|
||||
const startElement = (() => {
|
||||
if (item.icon) {
|
||||
return typeof item.icon === 'string' ? <IconSvg name={ item.icon } boxSize={ 5 } flexShrink={ 0 }/> : item.icon;
|
||||
}
|
||||
|
||||
return null;
|
||||
})();
|
||||
const startElement = item.icon;
|
||||
|
||||
return (
|
||||
<ChakraSelect.Item key={ item.value } item={ item } { ...rest } ref={ ref }>
|
||||
{ startElement }
|
||||
{ children }
|
||||
<ChakraSelect.ItemIndicator asChild>
|
||||
<IconSvg name="check" boxSize={ 5 } flexShrink={ 0 } ml="auto"/>
|
||||
<Icon boxSize={ 5 } flexShrink={ 0 } ml="auto"><CheckIcon/></Icon>
|
||||
</ChakraSelect.ItemIndicator>
|
||||
</ChakraSelect.Item>
|
||||
);
|
||||
@@ -142,14 +140,6 @@ export const SelectValueText = React.forwardRef<
|
||||
|
||||
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' ? (
|
||||
<Box
|
||||
textStyle="xs"
|
||||
@@ -164,7 +154,7 @@ export const SelectValueText = React.forwardRef<
|
||||
<>
|
||||
{ label }
|
||||
<Flex display="inline-flex" alignItems="center" flexWrap="nowrap" gap={ 1 }>
|
||||
{ icon }
|
||||
{ item.icon }
|
||||
<span style={{
|
||||
WebkitLineClamp: 1,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Table as ChakraTable } from '@chakra-ui/react';
|
||||
import { Table as ChakraTable, Icon } from '@chakra-ui/react';
|
||||
import { throttle } from 'es-toolkit';
|
||||
import * as React from 'react';
|
||||
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import ArrowIcon from 'icons/arrows/east.svg';
|
||||
|
||||
import { Link } from './link';
|
||||
|
||||
@@ -50,8 +50,7 @@ export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHe
|
||||
<TableColumnHeader { ...rest }>
|
||||
<Link onClick={ disabled ? undefined : handleSortToggle } position="relative">
|
||||
{ sortValue.includes(sortField) && (
|
||||
<IconSvg
|
||||
name="arrows/east"
|
||||
<Icon
|
||||
w={ 4 }
|
||||
h="100%"
|
||||
transform={ sortValue.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)' }
|
||||
@@ -59,7 +58,9 @@ export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHe
|
||||
left={ indicatorPosition === 'left' ? -5 : undefined }
|
||||
right={ indicatorPosition === 'right' ? -5 : undefined }
|
||||
top={ 0 }
|
||||
/>
|
||||
>
|
||||
<ArrowIcon/>
|
||||
</Icon>
|
||||
) }
|
||||
{ children }
|
||||
</Link>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { chakra, Tag as ChakraTag } from '@chakra-ui/react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { nbsp } from 'lib/html-entities';
|
||||
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
|
||||
import { nbsp } from 'toolkit/utils/htmlEntities';
|
||||
|
||||
import { TruncatedTextTooltip } from '../components/truncation/TruncatedTextTooltip';
|
||||
import { CloseButton } from './close-button';
|
||||
import { Skeleton } from './skeleton';
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
createToaster,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { SECOND } from 'lib/consts';
|
||||
import { SECOND } from 'toolkit/utils/consts';
|
||||
|
||||
import { CloseButton } from './close-button';
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { TabsProps } from 'toolkit/chakra/tabs';
|
||||
import { TabsContent, TabsRoot } from 'toolkit/chakra/tabs';
|
||||
import useViewportSize from 'toolkit/hooks/useViewportSize';
|
||||
|
||||
import type { TabsProps } from '../../chakra/tabs';
|
||||
import { TabsContent, TabsRoot } from '../../chakra/tabs';
|
||||
import { useViewportSize } from '../../hooks/useViewportSize';
|
||||
import AdaptiveTabsList, { type BaseProps as AdaptiveTabsListProps } from './AdaptiveTabsList';
|
||||
import { getTabValue } from './utils';
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import type { TabItemRegular } from './types';
|
||||
|
||||
import { useScrollDirection } from 'lib/contexts/scrollDirection';
|
||||
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 useAdaptiveTabs from './useAdaptiveTabs';
|
||||
import useScrollToActiveTab from './useScrollToActiveTab';
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Icon } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { TabItem } from './types';
|
||||
|
||||
import { PopoverBody, PopoverCloseTriggerWrapper, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
|
||||
import { TabsCounter, TabsTrigger } from 'toolkit/chakra/tabs';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import DotsIcon from 'icons/dots.svg';
|
||||
|
||||
import { IconButton } 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';
|
||||
|
||||
interface Props extends IconButtonProps {
|
||||
@@ -38,7 +39,7 @@ const AdaptiveTabsMenu = ({ tabs, tabsCut, isActive, ...props }: Props, ref: Rea
|
||||
px="18px"
|
||||
{ ...props }
|
||||
>
|
||||
<IconSvg name="dots" boxSize={ 5 }/>
|
||||
<Icon boxSize={ 5 }><DotsIcon/></Icon>
|
||||
</IconButton>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export type { TabItemRegular, TabItemMenu, SubTabItem } from './types';
|
||||
export type { Props } from './AdaptiveTabs';
|
||||
|
||||
export { default } from './AdaptiveTabs';
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { TabItem, TabItemMenu } from './types';
|
||||
|
||||
import { middot } from 'lib/html-entities';
|
||||
import { middot } from 'toolkit/utils/htmlEntities';
|
||||
|
||||
export const menuButton: TabItemMenu = {
|
||||
id: 'menu',
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Icon } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import InfoIcon from 'icons/info.svg';
|
||||
|
||||
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;
|
||||
tooltipProps?: Partial<TooltipProps>;
|
||||
isLoading?: boolean;
|
||||
as?: React.ElementType;
|
||||
}
|
||||
|
||||
export const Hint = React.memo(({ label, tooltipProps, isLoading, boxSize = 5, ...rest }: Props) => {
|
||||
return (
|
||||
<Tooltip
|
||||
content={ label }
|
||||
positioning={{ placement: 'top' }}
|
||||
{ ...tooltipProps }
|
||||
>
|
||||
<IconButton
|
||||
aria-label="hint"
|
||||
boxSize={ boxSize }
|
||||
loadingSkeleton={ isLoading }
|
||||
borderRadius="sm"
|
||||
color="icon.info"
|
||||
_hover={{ color: 'link.primary.hover' }}
|
||||
{ ...rest }
|
||||
>
|
||||
<Icon boxSize={ boxSize }>
|
||||
<InfoIcon/>
|
||||
</Icon>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
@@ -23,7 +23,7 @@ const RoutedTabs = (props: Props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const queryForPathname = pickBy(router.query, (value, key) => router.pathname.includes(`[${ key }]`));
|
||||
const queryForPathname = pickBy(router.query, (_, key) => router.pathname.includes(`[${ key }]`));
|
||||
router.push(
|
||||
{ pathname: router.pathname, query: { ...queryForPathname, tab: value } },
|
||||
undefined,
|
||||
|
||||
@@ -3,9 +3,8 @@ import React from 'react';
|
||||
|
||||
import type { TabItemRegular } from '../AdaptiveTabs/types';
|
||||
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import type { TabsProps } from 'toolkit/chakra/tabs';
|
||||
|
||||
import { Skeleton } from '../../chakra/skeleton';
|
||||
import type { TabsProps } from '../../chakra/tabs';
|
||||
import useActiveTabFromQuery from './useActiveTabFromQuery';
|
||||
|
||||
const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabItemRegular['title'] }) => (
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
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';
|
||||
|
||||
import type { TabItem } from '../AdaptiveTabs/types';
|
||||
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { castToString } from '../../utils/guards';
|
||||
|
||||
export default function useActiveTabFromQuery(tabs: Array<TabItem>) {
|
||||
const router = useRouter();
|
||||
const tabFromQuery = getQueryParamString(router.query.tab);
|
||||
const tabFromQuery = castToString(router.query.tab);
|
||||
|
||||
if (!tabFromQuery) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Icon } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import ArrowIcon from 'icons/arrows/east.svg';
|
||||
|
||||
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;
|
||||
hint?: string;
|
||||
}
|
||||
|
||||
export const BackToButton = ({ href, hint, boxSize = 6, ...rest }: BackToButtonProps) => {
|
||||
|
||||
const button = (
|
||||
<IconButton { ...rest } boxSize={ boxSize }>
|
||||
<Icon
|
||||
transform="rotate(180deg)"
|
||||
color="icon.backTo"
|
||||
_hover={{ color: 'link.primary.hover' }}
|
||||
boxSize={ boxSize }
|
||||
>
|
||||
<ArrowIcon/>
|
||||
</Icon>
|
||||
</IconButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip content={ hint } disabled={ !hint }>
|
||||
{ href ? <Link href={ href } asChild>{ button }</Link> : button }
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
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 }
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,15 +1,17 @@
|
||||
import { Icon } from '@chakra-ui/react';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import type { InputProps } from 'toolkit/chakra/input';
|
||||
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';
|
||||
import SearchIcon from 'icons/search.svg';
|
||||
|
||||
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;
|
||||
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
||||
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
||||
@@ -22,7 +24,19 @@ interface Props extends Omit<SkeletonProps, 'onChange' | 'loading'> {
|
||||
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 inputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -39,9 +53,8 @@ const FilterInput = ({ onChange, size = 'sm', placeholder, initialValue, type, n
|
||||
inputRef?.current?.focus();
|
||||
}, [ onChange ]);
|
||||
|
||||
const startElement = <IconSvg name="search" boxSize={ 5 }/>;
|
||||
|
||||
const endElement = <ClearButton onClick={ handleFilterQueryClear } isVisible={ filterQuery.length > 0 }/>;
|
||||
const startElement = <Icon boxSize={ 5 }><SearchIcon/></Icon>;
|
||||
const endElement = <ClearButton onClick={ handleFilterQueryClear } visible={ filterQuery.length > 0 }/>;
|
||||
|
||||
return (
|
||||
<Skeleton
|
||||
@@ -75,5 +88,3 @@ const FilterInput = ({ onChange, size = 'sm', placeholder, initialValue, type, n
|
||||
</Skeleton>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilterInput;
|
||||
+2
-4
@@ -6,8 +6,6 @@ interface Props {
|
||||
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>;
|
||||
};
|
||||
|
||||
export default chakra(FieldError);
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export * from './FormFieldError';
|
||||
+3
-3
@@ -5,9 +5,9 @@ import type { FormFieldPropsBase } from './types';
|
||||
import type { PartialBy } from 'types/utils';
|
||||
|
||||
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'>,
|
||||
) => {
|
||||
const rules = React.useMemo(
|
||||
@@ -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;
|
||||
+4
-4
@@ -3,8 +3,8 @@ import { useController, useFormContext, type FieldValues, type Path } from 'reac
|
||||
|
||||
import type { FormFieldPropsBase } from './types';
|
||||
|
||||
import type { CheckboxProps } from 'toolkit/chakra/checkbox';
|
||||
import { Checkbox } from 'toolkit/chakra/checkbox';
|
||||
import { Checkbox } from '../../../chakra/checkbox';
|
||||
import type { CheckboxProps } from '../../../chakra/checkbox';
|
||||
|
||||
interface Props<
|
||||
FormFields extends FieldValues,
|
||||
@@ -13,7 +13,7 @@ interface Props<
|
||||
label: string;
|
||||
}
|
||||
|
||||
const FormFieldCheckbox = <
|
||||
const FormFieldCheckboxContent = <
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields> = Path<FormFields>,
|
||||
>({
|
||||
@@ -52,4 +52,4 @@ const FormFieldCheckbox = <
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FormFieldCheckbox) as typeof FormFieldCheckbox;
|
||||
export const FormFieldCheckbox = React.memo(FormFieldCheckboxContent) as typeof FormFieldCheckboxContent;
|
||||
+9
-10
@@ -6,13 +6,12 @@ import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { FormFieldPropsBase } from './types';
|
||||
|
||||
import { Field } from 'toolkit/chakra/field';
|
||||
import type { InputProps } from 'toolkit/chakra/input';
|
||||
import { Input } from 'toolkit/chakra/input';
|
||||
import { InputGroup } from 'toolkit/chakra/input-group';
|
||||
import { validator as colorValidator } from 'ui/shared/forms/validators/color';
|
||||
|
||||
import getFieldErrorText from '../utils/getFieldErrorText';
|
||||
import { Field } from '../../../chakra/field';
|
||||
import type { InputProps } from '../../../chakra/input';
|
||||
import { Input } from '../../../chakra/input';
|
||||
import { InputGroup } from '../../../chakra/input-group';
|
||||
import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
|
||||
import { colorValidator } from '../validators/color';
|
||||
|
||||
interface Props<
|
||||
FormFields extends FieldValues,
|
||||
@@ -21,7 +20,7 @@ interface Props<
|
||||
sampleDefaultBgColor?: BoxProps['bgColor'];
|
||||
}
|
||||
|
||||
const FormFieldColor = <
|
||||
const FormFieldColorContent = <
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields> = Path<FormFields>,
|
||||
>({
|
||||
@@ -82,7 +81,7 @@ const FormFieldColor = <
|
||||
return (
|
||||
<Field
|
||||
label={ placeholder }
|
||||
errorText={ getFieldErrorText(fieldState.error) }
|
||||
errorText={ getFormFieldErrorText(fieldState.error) }
|
||||
invalid={ Boolean(fieldState.error) }
|
||||
disabled={ formState.isSubmitting || disabled }
|
||||
size={ size }
|
||||
@@ -106,4 +105,4 @@ const FormFieldColor = <
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FormFieldColor) as typeof FormFieldColor;
|
||||
export const FormFieldColor = React.memo(FormFieldColorContent) as typeof FormFieldColorContent;
|
||||
+3
-3
@@ -5,9 +5,9 @@ import type { FormFieldPropsBase } from './types';
|
||||
import type { PartialBy } from 'types/utils';
|
||||
|
||||
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'>,
|
||||
) => {
|
||||
const rules = React.useMemo(
|
||||
@@ -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;
|
||||
+6
-7
@@ -4,17 +4,16 @@ import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { FormFieldPropsBase } from './types';
|
||||
|
||||
import type { SelectProps } from 'toolkit/chakra/select';
|
||||
import { Select } from 'toolkit/chakra/select';
|
||||
|
||||
import getFieldErrorText from '../utils/getFieldErrorText';
|
||||
import type { SelectProps } from '../../../chakra/select';
|
||||
import { Select } from '../../../chakra/select';
|
||||
import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
|
||||
|
||||
type Props<
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
> = FormFieldPropsBase<FormFields, Name> & SelectProps;
|
||||
|
||||
const FormFieldSelect = <
|
||||
const FormFieldSelectContent = <
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
>(props: Props<FormFields, Name>) => {
|
||||
@@ -47,7 +46,7 @@ const FormFieldSelect = <
|
||||
onInteractOutside={ handleBlur }
|
||||
disabled={ isDisabled }
|
||||
invalid={ Boolean(fieldState.error) }
|
||||
errorText={ getFieldErrorText(fieldState.error) }
|
||||
errorText={ getFormFieldErrorText(fieldState.error) }
|
||||
size={ size }
|
||||
positioning={{ sameWidth: true }}
|
||||
{ ...rest }
|
||||
@@ -55,4 +54,4 @@ const FormFieldSelect = <
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FormFieldSelect) as typeof FormFieldSelect;
|
||||
export const FormFieldSelect = React.memo(FormFieldSelectContent) as typeof FormFieldSelectContent;
|
||||
+6
-7
@@ -4,17 +4,16 @@ import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { FormFieldPropsBase } from './types';
|
||||
|
||||
import type { SelectAsyncProps } from 'toolkit/chakra/select';
|
||||
import { SelectAsync } from 'toolkit/chakra/select';
|
||||
|
||||
import getFieldErrorText from '../utils/getFieldErrorText';
|
||||
import type { SelectAsyncProps } from '../../../chakra/select';
|
||||
import { SelectAsync } from '../../../chakra/select';
|
||||
import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
|
||||
|
||||
type Props<
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
> = FormFieldPropsBase<FormFields, Name> & SelectAsyncProps;
|
||||
|
||||
const FormFieldSelectAsync = <
|
||||
const FormFieldSelectAsyncContent = <
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
>(props: Props<FormFields, Name>) => {
|
||||
@@ -47,7 +46,7 @@ const FormFieldSelectAsync = <
|
||||
onInteractOutside={ handleBlur }
|
||||
disabled={ isDisabled }
|
||||
invalid={ Boolean(fieldState.error) }
|
||||
errorText={ getFieldErrorText(fieldState.error) }
|
||||
errorText={ getFormFieldErrorText(fieldState.error) }
|
||||
size={ size }
|
||||
positioning={{ sameWidth: true }}
|
||||
{ ...rest }
|
||||
@@ -55,4 +54,4 @@ const FormFieldSelectAsync = <
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FormFieldSelectAsync) as typeof FormFieldSelectAsync;
|
||||
export const FormFieldSelectAsync = React.memo(FormFieldSelectAsyncContent) as typeof FormFieldSelectAsyncContent;
|
||||
+10
-11
@@ -4,14 +4,13 @@ import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { FormFieldPropsBase } from './types';
|
||||
|
||||
import { Field } from 'toolkit/chakra/field';
|
||||
import type { InputProps } from 'toolkit/chakra/input';
|
||||
import { Input } from 'toolkit/chakra/input';
|
||||
import { InputGroup } from 'toolkit/chakra/input-group';
|
||||
import type { TextareaProps } from 'toolkit/chakra/textarea';
|
||||
import { Textarea } from 'toolkit/chakra/textarea';
|
||||
|
||||
import getFieldErrorText from '../utils/getFieldErrorText';
|
||||
import { Field } from '../../../chakra/field';
|
||||
import type { InputProps } from '../../../chakra/input';
|
||||
import { Input } from '../../../chakra/input';
|
||||
import { InputGroup } from '../../../chakra/input-group';
|
||||
import type { TextareaProps } from '../../../chakra/textarea';
|
||||
import { Textarea } from '../../../chakra/textarea';
|
||||
import { getFormFieldErrorText } from '../utils/getFormFieldErrorText';
|
||||
|
||||
interface Props<
|
||||
FormFields extends FieldValues,
|
||||
@@ -20,7 +19,7 @@ interface Props<
|
||||
asComponent?: 'Input' | 'Textarea';
|
||||
}
|
||||
|
||||
const FormFieldText = <
|
||||
const FormFieldTextContent = <
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields> = Path<FormFields>,
|
||||
>({
|
||||
@@ -84,7 +83,7 @@ const FormFieldText = <
|
||||
<Field
|
||||
// for floating field label, we pass placeholder value to the label
|
||||
label={ floating ? placeholder : undefined }
|
||||
errorText={ getFieldErrorText(fieldState.error) }
|
||||
errorText={ getFormFieldErrorText(fieldState.error) }
|
||||
invalid={ Boolean(fieldState.error) }
|
||||
disabled={ formState.isSubmitting || disabled }
|
||||
size={ size }
|
||||
@@ -96,4 +95,4 @@ const FormFieldText = <
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FormFieldText) as typeof FormFieldText;
|
||||
export const FormFieldText = React.memo(FormFieldTextContent) as typeof FormFieldTextContent;
|
||||
+3
-3
@@ -4,9 +4,9 @@ import type { FieldValues } from 'react-hook-form';
|
||||
import type { FormFieldPropsBase } from './types';
|
||||
|
||||
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>,
|
||||
) => {
|
||||
const rules = React.useMemo(
|
||||
@@ -23,4 +23,4 @@ const FormFieldUrl = <FormFields extends FieldValues>(
|
||||
return <FormFieldText { ...props } rules={ rules }/>;
|
||||
};
|
||||
|
||||
export default React.memo(FormFieldUrl) as typeof FormFieldUrl;
|
||||
export const FormFieldUrl = React.memo(FormFieldUrlContent) as typeof FormFieldUrlContent;
|
||||
+5
-7
@@ -1,9 +1,9 @@
|
||||
import { chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { ColorMode } from 'toolkit/chakra/color-mode';
|
||||
import { Image } from 'toolkit/chakra/image';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import type { ColorMode } from '../../../../chakra/color-mode';
|
||||
import { Image } from '../../../../chakra/image';
|
||||
import { Skeleton } from '../../../../chakra/skeleton';
|
||||
|
||||
interface Props {
|
||||
src: string | undefined;
|
||||
@@ -15,7 +15,7 @@ interface Props {
|
||||
colorMode?: ColorMode;
|
||||
}
|
||||
|
||||
const ImageUrlPreview = ({
|
||||
export const FormFieldImagePreview = chakra(React.memo(({
|
||||
src,
|
||||
isInvalid,
|
||||
onError,
|
||||
@@ -45,6 +45,4 @@ const ImageUrlPreview = ({
|
||||
onLoad={ onLoad }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default chakra(React.memo(ImageUrlPreview));
|
||||
}));
|
||||
+2
-2
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import type { FieldValues, Path } from 'react-hook-form';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
|
||||
import { urlValidator } from '../validators/url';
|
||||
import { urlValidator } from '../../validators/url';
|
||||
|
||||
interface Params<
|
||||
FormFields extends FieldValues,
|
||||
@@ -31,7 +31,7 @@ interface ReturnType {
|
||||
};
|
||||
}
|
||||
|
||||
export default function useFieldWithImagePreview<
|
||||
export function useImageField<
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
>({
|
||||
@@ -0,0 +1,10 @@
|
||||
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';
|
||||
@@ -1,10 +1,10 @@
|
||||
import type React from 'react';
|
||||
import type { ControllerRenderProps, FieldValues, Path, RegisterOptions } from 'react-hook-form';
|
||||
|
||||
import type { FieldProps } from 'toolkit/chakra/field';
|
||||
import type { InputProps } from 'toolkit/chakra/input';
|
||||
import type { InputGroupProps } from 'toolkit/chakra/input-group';
|
||||
import type { TextareaProps } from 'toolkit/chakra/textarea';
|
||||
import type { FieldProps } from '../../../chakra/field';
|
||||
import type { InputProps } from '../../../chakra/input';
|
||||
import type { InputGroupProps } from '../../../chakra/input-group';
|
||||
import type { TextareaProps } from '../../../chakra/textarea';
|
||||
|
||||
export interface FormFieldPropsBase<
|
||||
FormFields extends FieldValues,
|
||||
+2
-4
@@ -13,7 +13,7 @@ interface Props {
|
||||
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 handleDrop = React.useCallback(async(event: DragEvent<HTMLDivElement>) => {
|
||||
@@ -77,6 +77,4 @@ const DragAndDropArea = ({ onDrop, children, className, isDisabled, fullFilePath
|
||||
{ children }
|
||||
</Center>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(chakra(DragAndDropArea));
|
||||
});
|
||||
+3
-3
@@ -3,7 +3,7 @@ import type { ChangeEvent } from 'react';
|
||||
import React from 'react';
|
||||
import type { ControllerRenderProps, FieldValues, Path } from 'react-hook-form';
|
||||
|
||||
import { Input } from 'toolkit/chakra/input';
|
||||
import { Input } from '../../../../chakra/input';
|
||||
|
||||
interface InjectedProps {
|
||||
onChange: (files: Array<File>) => void;
|
||||
@@ -16,7 +16,7 @@ interface Props<V extends FieldValues, N extends Path<V>> {
|
||||
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);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -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;
|
||||
+18
-19
@@ -1,15 +1,18 @@
|
||||
import { Box, Flex, Text, chakra } from '@chakra-ui/react';
|
||||
import { Box, Flex, Icon, Text, chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { CloseButton } from 'toolkit/chakra/close-button';
|
||||
import Hint from 'ui/shared/Hint';
|
||||
import type { IconName } from 'ui/shared/IconSvg';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import JsonFileIcon from 'icons/files/json.svg';
|
||||
import PlaceholderFileIcon from 'icons/files/placeholder.svg';
|
||||
import SolFileIcon from 'icons/files/sol.svg';
|
||||
import YulFileIcon from 'icons/files/yul.svg';
|
||||
|
||||
const FILE_ICONS: Record<string, IconName> = {
|
||||
'.json': 'files/json',
|
||||
'.sol': 'files/sol',
|
||||
'.yul': 'files/yul',
|
||||
import { CloseButton } from '../../../../chakra/close-button';
|
||||
import { Hint } from '../../../../components/Hint/Hint';
|
||||
|
||||
const FILE_ICONS: Record<string, React.ReactNode> = {
|
||||
'.json': <JsonFileIcon/>,
|
||||
'.sol': <SolFileIcon/>,
|
||||
'.yul': <YulFileIcon/>,
|
||||
};
|
||||
|
||||
function getFileExtension(fileName: string) {
|
||||
@@ -30,14 +33,14 @@ interface Props {
|
||||
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) => {
|
||||
event.stopPropagation();
|
||||
onRemove?.(index);
|
||||
}, [ index, onRemove ]);
|
||||
|
||||
const fileExtension = getFileExtension(file.name);
|
||||
const fileIcon = FILE_ICONS[fileExtension] || 'files/placeholder';
|
||||
const fileIcon = FILE_ICONS[fileExtension] || <PlaceholderFileIcon/>;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -48,11 +51,9 @@ const FileSnippet = ({ file, className, index, onRemove, isDisabled, error }: Pr
|
||||
textAlign="left"
|
||||
columnGap={ 2 }
|
||||
>
|
||||
<IconSvg
|
||||
name={ fileIcon }
|
||||
boxSize="48px"
|
||||
color={ error ? 'text.error' : 'initial' }
|
||||
/>
|
||||
<Icon boxSize="48px" color={ error ? 'text.error' : 'initial' }>
|
||||
{ fileIcon }
|
||||
</Icon>
|
||||
<Box maxW="calc(100% - 58px - 24px)">
|
||||
<Flex alignItems="center">
|
||||
<Text
|
||||
@@ -78,6 +79,4 @@ const FileSnippet = ({ file, className, index, onRemove, isDisabled, error }: Pr
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(chakra(FileSnippet));
|
||||
});
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import stripLeadingSlash from 'lib/stripLeadingSlash';
|
||||
import { stripLeadingSlash } from '../../../../utils/url';
|
||||
|
||||
// Function to get all files in drop directory
|
||||
export async function getAllFileEntries(dataTransferItemList: DataTransferItemList): Promise<Array<FileSystemFileEntry>> {
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './file/DragAndDropArea';
|
||||
export * from './file/FileInput';
|
||||
export * from './file/FileSnippet';
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
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') {
|
||||
return 'Invalid format';
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './getFormFieldErrorText';
|
||||
@@ -1,6 +1,6 @@
|
||||
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') {
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export const EMAIL_REGEXP = /^[\w.%+-]+@[a-z\d-]+(?:\.[a-z\d-]+)+$/i;
|
||||
|
||||
export const emailValidator = (value: string) => EMAIL_REGEXP.test(value) ? true : 'Invalid email';
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from './address';
|
||||
export * from './color';
|
||||
export * from './email';
|
||||
export * from './signature';
|
||||
export * from './text';
|
||||
export * from './transaction';
|
||||
export * from './url';
|
||||
+6
-8
@@ -3,17 +3,17 @@ import { debounce } from 'es-toolkit';
|
||||
import React from 'react';
|
||||
import useFontFaceObserver from 'use-font-face-observer';
|
||||
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
|
||||
import { BODY_TYPEFACE } from 'toolkit/theme/foundations/typography';
|
||||
import { Tooltip } from '../../chakra/tooltip';
|
||||
import { useDisclosure } from '../../hooks/useDisclosure';
|
||||
import { BODY_TYPEFACE } from '../../theme/foundations/typography';
|
||||
|
||||
interface Props {
|
||||
export interface TruncatedTextTooltipProps {
|
||||
children: React.ReactNode;
|
||||
label: React.ReactNode;
|
||||
placement?: Placement;
|
||||
}
|
||||
|
||||
const TruncatedTextTooltip = ({ children, label, placement }: Props) => {
|
||||
export const TruncatedTextTooltip = React.memo(({ children, label, placement }: TruncatedTextTooltipProps) => {
|
||||
const childRef = React.useRef<HTMLElement>(null);
|
||||
const [ isTruncated, setTruncated ] = React.useState(false);
|
||||
const { open, onToggle, onOpen, onClose } = useDisclosure();
|
||||
@@ -90,6 +90,4 @@ const TruncatedTextTooltip = ({ children, label, placement }: Props) => {
|
||||
}
|
||||
|
||||
return modifiedChildren;
|
||||
};
|
||||
|
||||
export default React.memo(TruncatedTextTooltip);
|
||||
});
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useCopyToClipboard } from '@uidotdev/usehooks';
|
||||
import React from 'react';
|
||||
|
||||
import { SECOND } from 'lib/consts';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import { SECOND } from 'toolkit/utils/consts';
|
||||
|
||||
import { useDisclosure } from './useDisclosure';
|
||||
|
||||
// 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 disclosureTimeoutRef = React.useRef<number | null>(null);
|
||||
const [ hasCopied, setHasCopied ] = React.useState(false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { throttle } from 'es-toolkit';
|
||||
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 handleScroll = React.useCallback(() => {
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
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.
|
||||
const useUpdateEffect: typeof React.useEffect = (effect, deps) => {
|
||||
export const useUpdateEffect: typeof React.useEffect = (effect, deps) => {
|
||||
const isFirstMount = useFirstMountState();
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -13,5 +13,3 @@ const useUpdateEffect: typeof React.useEffect = (effect, deps) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, deps);
|
||||
};
|
||||
|
||||
export default useUpdateEffect;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { debounce } from 'es-toolkit';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function useViewportSize(debounceTime = 100) {
|
||||
export function useViewportSize(debounceTime = 100) {
|
||||
const [ viewportSize, setViewportSize ] = useState({ width: 0, height: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user