feat(ui-toolkit): use i18next (#5289)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
bb47747501
commit
61a9eb2f5c
@ -1,3 +1,4 @@
|
|||||||
|
import '../i18n';
|
||||||
import {
|
import {
|
||||||
NetworkLoader,
|
NetworkLoader,
|
||||||
NodeFailure,
|
NodeFailure,
|
||||||
@ -28,20 +29,24 @@ function App() {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<TendermintWebsocketProvider>
|
<TendermintWebsocketProvider>
|
||||||
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
<Suspense fallback={splashLoading}>
|
||||||
<NodeGuard
|
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||||
skeleton={<div>{t('Loading')}</div>}
|
<NodeGuard
|
||||||
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
skeleton={<div>{t('Loading')}</div>}
|
||||||
>
|
failure={
|
||||||
<Suspense fallback={splashLoading}>
|
<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />
|
||||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
}
|
||||||
</Suspense>
|
>
|
||||||
</NodeGuard>
|
<Suspense fallback={splashLoading}>
|
||||||
<NodeSwitcherDialog
|
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||||
open={nodeSwitcherOpen}
|
</Suspense>
|
||||||
setOpen={setNodeSwitcherOpen}
|
</NodeGuard>
|
||||||
/>
|
<NodeSwitcherDialog
|
||||||
</NetworkLoader>
|
open={nodeSwitcherOpen}
|
||||||
|
setOpen={setNodeSwitcherOpen}
|
||||||
|
/>
|
||||||
|
</NetworkLoader>
|
||||||
|
</Suspense>
|
||||||
</TendermintWebsocketProvider>
|
</TendermintWebsocketProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
import { locales } from '@vegaprotocol/i18n';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
Object.defineProperty(window, 'ResizeObserver', {
|
Object.defineProperty(window, 'ResizeObserver', {
|
||||||
writable: false,
|
writable: false,
|
||||||
@ -13,3 +16,14 @@ Object.defineProperty(window, 'ResizeObserver', {
|
|||||||
disconnect: jest.fn(),
|
disconnect: jest.fn(),
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Set up i18n instance so that components have the correct default
|
||||||
|
// en translations
|
||||||
|
i18n.use(initReactI18next).init({
|
||||||
|
// we init with resources
|
||||||
|
resources: locales,
|
||||||
|
fallbackLng: 'en',
|
||||||
|
nsSeparator: false,
|
||||||
|
ns: ['explorer'],
|
||||||
|
defaultNS: 'explorer',
|
||||||
|
});
|
||||||
|
1
apps/explorer/src/assets/locales
Symbolic link
1
apps/explorer/src/assets/locales
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../../libs/i18n/src/locales
|
45
apps/explorer/src/i18n/index.ts
Normal file
45
apps/explorer/src/i18n/index.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import type { Module } from 'i18next';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
import HttpBackend from 'i18next-http-backend';
|
||||||
|
import LocizeBackend from 'i18next-locize-backend';
|
||||||
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
|
const isInDev = process.env.NODE_ENV === 'development';
|
||||||
|
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
|
||||||
|
|
||||||
|
const backend = useLocize
|
||||||
|
? {
|
||||||
|
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
|
||||||
|
apiKey: process.env.NX_LOCIZE_API_KEY,
|
||||||
|
referenceLng: 'en',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
loadPath: '/assets/locales/{{lng}}/{{ns}}.json',
|
||||||
|
};
|
||||||
|
|
||||||
|
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(Backend)
|
||||||
|
.use(LanguageDetector)
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
lng: 'en',
|
||||||
|
fallbackLng: 'en',
|
||||||
|
supportedLngs: ['en'],
|
||||||
|
load: 'languageOnly',
|
||||||
|
debug: isInDev,
|
||||||
|
// have a common namespace used around the full app
|
||||||
|
ns: ['explorer'],
|
||||||
|
defaultNS: 'explorer',
|
||||||
|
keySeparator: false, // we use content as keys
|
||||||
|
nsSeparator: false,
|
||||||
|
backend,
|
||||||
|
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
@ -15,6 +15,7 @@ import en_markets from './locales/en/markets.json';
|
|||||||
import en_web3 from './locales/en/web3.json';
|
import en_web3 from './locales/en/web3.json';
|
||||||
import en_positions from './locales/en/positions.json';
|
import en_positions from './locales/en/positions.json';
|
||||||
import en_trades from './locales/en/trading.json';
|
import en_trades from './locales/en/trading.json';
|
||||||
|
import en_ui_toolkit from './locales/en/ui-toolkit.json';
|
||||||
|
|
||||||
export const locales = {
|
export const locales = {
|
||||||
en: {
|
en: {
|
||||||
@ -33,5 +34,6 @@ export const locales = {
|
|||||||
web3: en_web3,
|
web3: en_web3,
|
||||||
positions: en_positions,
|
positions: en_positions,
|
||||||
trades: en_trades,
|
trades: en_trades,
|
||||||
|
'ui-toolkit': en_ui_toolkit,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
1
libs/i18n/src/locales/en/explorer.json
Normal file
1
libs/i18n/src/locales/en/explorer.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
21
libs/i18n/src/locales/en/ui-toolkit.json
Normal file
21
libs/i18n/src/locales/en/ui-toolkit.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"{{fee}} Fee": "{{fee}} Fee",
|
||||||
|
"Auction Trigger stake {{trigger}}": "Auction Trigger stake {{trigger}}",
|
||||||
|
"Collapse": "Collapse",
|
||||||
|
"Copied": "Copied",
|
||||||
|
"Dark mode": "Dark mode",
|
||||||
|
"Dismiss all toasts": "Dismiss all toasts",
|
||||||
|
"Dismiss all": "Dismiss all",
|
||||||
|
"Exit view as": "Exit view as",
|
||||||
|
"Expand": "Expand",
|
||||||
|
"Light mode": "Light mode",
|
||||||
|
"Loading...": "Loading...",
|
||||||
|
"No data": "No data",
|
||||||
|
"Providers greater than 2x target stake not shown": "Providers greater than 2x target stake not shown",
|
||||||
|
"Show more": "Show more",
|
||||||
|
"Something went wrong: {{errorMessage}}": "Something went wrong: {{errorMessage}}",
|
||||||
|
"Target stake {{target}}": "Target stake {{target}}",
|
||||||
|
"This is an example of a toast notification": "This is an example of a toast notification",
|
||||||
|
"Try again": "Try again",
|
||||||
|
"Viewing as Vega user: {{pubKey}}": "Viewing as Vega user: {{pubKey}}"
|
||||||
|
}
|
14
libs/ui-toolkit/__mocks__/react-i18next.ts
Normal file
14
libs/ui-toolkit/__mocks__/react-i18next.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export const useTranslation = () => ({
|
||||||
|
t: (label: string, replacements?: Record<string, string>) => {
|
||||||
|
let translatedLabel = label;
|
||||||
|
if (typeof replacements === 'object' && replacements !== null) {
|
||||||
|
Object.keys(replacements).forEach((key) => {
|
||||||
|
translatedLabel = translatedLabel.replace(
|
||||||
|
`{{${key}}}`,
|
||||||
|
replacements[key]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return translatedLabel;
|
||||||
|
},
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
import { Splash } from '../splash';
|
import { Splash } from '../splash';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Button } from '../button';
|
import { Button } from '../button';
|
||||||
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
interface AsyncRendererProps<T> {
|
interface AsyncRendererProps<T> {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -28,15 +28,18 @@ export function AsyncRenderer<T = object>({
|
|||||||
render,
|
render,
|
||||||
reload,
|
reload,
|
||||||
}: AsyncRendererProps<T>) {
|
}: AsyncRendererProps<T>) {
|
||||||
|
const t = useT();
|
||||||
if (error) {
|
if (error) {
|
||||||
if (!data || (Array.isArray(data) && !data.length)) {
|
if (!data || (Array.isArray(data) && !data.length)) {
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex items-center justify-center">
|
<div className="flex h-full items-center justify-center">
|
||||||
<div className="h-12 flex flex-col items-center">
|
<div className="flex h-12 flex-col items-center">
|
||||||
<Splash>
|
<Splash>
|
||||||
{errorMessage
|
{errorMessage
|
||||||
? errorMessage
|
? errorMessage
|
||||||
: t(`Something went wrong: ${error.message}`)}
|
: t('Something went wrong: {{errorMessage}}', {
|
||||||
|
errorMessage: error.message,
|
||||||
|
})}
|
||||||
</Splash>
|
</Splash>
|
||||||
{reload && error.message === 'Timeout exceeded' && (
|
{reload && error.message === 'Timeout exceeded' && (
|
||||||
<Button
|
<Button
|
||||||
@ -77,6 +80,7 @@ export function AsyncRendererInline<T>({
|
|||||||
render,
|
render,
|
||||||
reload,
|
reload,
|
||||||
}: AsyncRendererProps<T>) {
|
}: AsyncRendererProps<T>) {
|
||||||
|
const t = useT();
|
||||||
const wrapperClasses = 'text-sm';
|
const wrapperClasses = 'text-sm';
|
||||||
if (error) {
|
if (error) {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@ -85,7 +89,9 @@ export function AsyncRendererInline<T>({
|
|||||||
<p>
|
<p>
|
||||||
{errorMessage
|
{errorMessage
|
||||||
? errorMessage
|
? errorMessage
|
||||||
: t(`Something went wrong: ${error.message}`)}
|
: t('Something went wrong: {{errorMessage}}', {
|
||||||
|
errorMessage: error.message,
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
{reload && error.message === 'Timeout exceeded' && (
|
{reload && error.message === 'Timeout exceeded' && (
|
||||||
<Button
|
<Button
|
||||||
|
@ -5,7 +5,7 @@ import { forwardRef } from 'react';
|
|||||||
import { VegaIcon, VegaIconNames } from '../icon';
|
import { VegaIcon, VegaIconNames } from '../icon';
|
||||||
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
|
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
|
||||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
const itemClass = classNames(
|
const itemClass = classNames(
|
||||||
'relative flex gap-2 items-center rounded-sm p-2 text-sm',
|
'relative flex gap-2 items-center rounded-sm p-2 text-sm',
|
||||||
@ -214,6 +214,7 @@ export const DropdownMenuCopyItem = ({
|
|||||||
value: string;
|
value: string;
|
||||||
text: string;
|
text: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useT();
|
||||||
const [copied, setCopied] = useCopyTimeout();
|
const [copied, setCopied] = useCopyTimeout();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -3,14 +3,14 @@ import {
|
|||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
formatNumberPercentage,
|
formatNumberPercentage,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import { getIntentBackground, Intent } from '../../utils/intent';
|
import { getIntentBackground, Intent } from '../../utils/intent';
|
||||||
import { Indicator } from '../indicator';
|
import { Indicator } from '../indicator';
|
||||||
import { Tooltip } from '../tooltip';
|
import { Tooltip } from '../tooltip';
|
||||||
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
const Remainder = () => (
|
const Remainder = () => (
|
||||||
<div className="bg-greys-light-200 h-[inherit] relative flex-1" />
|
<div className="bg-greys-light-200 relative h-[inherit] flex-1" />
|
||||||
);
|
);
|
||||||
|
|
||||||
const Target = ({
|
const Target = ({
|
||||||
@ -22,6 +22,7 @@ const Target = ({
|
|||||||
target: string;
|
target: string;
|
||||||
decimals: number;
|
decimals: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useT();
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
description={
|
description={
|
||||||
@ -30,20 +31,22 @@ const Target = ({
|
|||||||
<Indicator variant={Intent.None} />
|
<Indicator variant={Intent.None} />
|
||||||
</div>
|
</div>
|
||||||
<span>
|
<span>
|
||||||
{t('Target stake')} {addDecimalsFormatNumber(target, decimals)}
|
{t('Target stake {{target}}', {
|
||||||
|
target: addDecimalsFormatNumber(target, decimals),
|
||||||
|
})}{' '}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5 group'
|
'group absolute left-1/2 top-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5'
|
||||||
)}
|
)}
|
||||||
style={{ left: '50%' }}
|
style={{ left: '50%' }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'health-target w-0.5 bg-vega-dark-100 dark:bg-vega-light-100 group-hover:scale-x-150 group-hover:scale-y-108',
|
'health-target bg-vega-dark-100 dark:bg-vega-light-100 group-hover:scale-y-108 w-0.5 group-hover:scale-x-150',
|
||||||
{
|
{
|
||||||
'h-6': !isLarge,
|
'h-6': !isLarge,
|
||||||
'h-12': isLarge,
|
'h-12': isLarge,
|
||||||
@ -66,6 +69,7 @@ const AuctionTarget = ({
|
|||||||
rangeLimit: number;
|
rangeLimit: number;
|
||||||
decimals: number;
|
decimals: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useT();
|
||||||
const leftPosition = new BigNumber(trigger).div(rangeLimit).multipliedBy(100);
|
const leftPosition = new BigNumber(trigger).div(rangeLimit).multipliedBy(100);
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -75,15 +79,16 @@ const AuctionTarget = ({
|
|||||||
<Indicator variant={Intent.None} />
|
<Indicator variant={Intent.None} />
|
||||||
</div>
|
</div>
|
||||||
<span>
|
<span>
|
||||||
{t('Auction Trigger stake')}{' '}
|
{t('Auction Trigger stake {{trigger}}', {
|
||||||
{addDecimalsFormatNumber(trigger, decimals)}
|
trigger: addDecimalsFormatNumber(trigger, decimals),
|
||||||
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5 group'
|
'group absolute left-1/2 top-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5'
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
left: `${leftPosition}%`,
|
left: `${leftPosition}%`,
|
||||||
@ -91,7 +96,7 @@ const AuctionTarget = ({
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'health-target w-0.5 group-hover:scale-x-150 group-hover:scale-y-108 dashed-background',
|
'health-target group-hover:scale-y-108 dashed-background w-0.5 group-hover:scale-x-150',
|
||||||
{
|
{
|
||||||
'h-6': !isLarge,
|
'h-6': !isLarge,
|
||||||
'h-12': isLarge,
|
'h-12': isLarge,
|
||||||
@ -120,6 +125,7 @@ const Level = ({
|
|||||||
decimals: number;
|
decimals: number;
|
||||||
intent: Intent;
|
intent: Intent;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useT();
|
||||||
const width = new BigNumber(commitmentAmount)
|
const width = new BigNumber(commitmentAmount)
|
||||||
.div(rangeLimit)
|
.div(rangeLimit)
|
||||||
.multipliedBy(100)
|
.multipliedBy(100)
|
||||||
@ -134,9 +140,7 @@ const Level = ({
|
|||||||
<div className="mt-1.5 inline-flex">
|
<div className="mt-1.5 inline-flex">
|
||||||
<Indicator variant={intent} />
|
<Indicator variant={intent} />
|
||||||
</div>
|
</div>
|
||||||
<span>
|
<span>{t('{{fee}} Fee', { fee: formattedFee })}</span>
|
||||||
{formattedFee} {t('Fee')}
|
|
||||||
</span>
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span>
|
<span>
|
||||||
{prevLevel ? addDecimalsFormatNumber(prevLevel, decimals) : '0'} -{' '}
|
{prevLevel ? addDecimalsFormatNumber(prevLevel, decimals) : '0'} -{' '}
|
||||||
@ -149,14 +153,14 @@ const Level = ({
|
|||||||
return (
|
return (
|
||||||
<Tooltip description={tooltipContent}>
|
<Tooltip description={tooltipContent}>
|
||||||
<div
|
<div
|
||||||
className={classNames(`relative h-[inherit] w-full group min-w-[1px]`)}
|
className="group relative h-[inherit] w-full min-w-[1px]"
|
||||||
style={{
|
style={{
|
||||||
width: `${width}%`,
|
width: `${width}%`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'relative w-full h-[inherit] group-hover:scale-y-150',
|
'relative h-[inherit] w-full group-hover:scale-y-150',
|
||||||
getIntentBackground(intent)
|
getIntentBackground(intent)
|
||||||
)}
|
)}
|
||||||
style={{ opacity }}
|
style={{ opacity }}
|
||||||
@ -167,7 +171,7 @@ const Level = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Full = () => (
|
const Full = () => (
|
||||||
<div className="bg-transparent w-full h-[inherit] absolute bottom-0 left-0" />
|
<div className="absolute bottom-0 left-0 h-[inherit] w-full bg-transparent" />
|
||||||
);
|
);
|
||||||
|
|
||||||
interface Levels {
|
interface Levels {
|
||||||
@ -190,6 +194,7 @@ export const HealthBar = ({
|
|||||||
intent: Intent;
|
intent: Intent;
|
||||||
triggerRatio?: string;
|
triggerRatio?: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useT();
|
||||||
const targetNumber = parseInt(target, 10);
|
const targetNumber = parseInt(target, 10);
|
||||||
const rangeLimit = targetNumber * 2;
|
const rangeLimit = targetNumber * 2;
|
||||||
|
|
||||||
@ -220,7 +225,7 @@ export const HealthBar = ({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames('health-inner relative w-full flex', {
|
className={classNames('health-inner relative flex w-full', {
|
||||||
'h-4': !isLarge,
|
'h-4': !isLarge,
|
||||||
'h-8': isLarge,
|
'h-8': isLarge,
|
||||||
})}
|
})}
|
||||||
@ -228,8 +233,8 @@ export const HealthBar = ({
|
|||||||
<Full />
|
<Full />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="health-bars h-[inherit] flex w-full
|
className="health-bars outline-vega-light-200 dark:outline-vega-dark-200 flex
|
||||||
gap-0.5 outline outline-vega-light-200 dark:outline-vega-dark-200"
|
h-[inherit] w-full gap-0.5 outline"
|
||||||
>
|
>
|
||||||
{levels.map((p, index) => {
|
{levels.map((p, index) => {
|
||||||
const { commitmentAmount, fee } = p;
|
const { commitmentAmount, fee } = p;
|
||||||
@ -253,11 +258,11 @@ export const HealthBar = ({
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
description={
|
description={
|
||||||
<div className="text-vega-dark-100 dark:text-vega-light-200">
|
<div className="text-vega-dark-100 dark:text-vega-light-200">
|
||||||
t( 'Providers greater than 2x target stake not shown' )
|
{t('Providers greater than 2x target stake not shown')}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="h-[inherit] relative flex-1 leading-4">...</div>
|
<div className="relative h-[inherit] flex-1 leading-4">...</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useRef, useState, useEffect } from 'react';
|
import { useRef, useState, useEffect } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Button } from '../button';
|
import { Button } from '../button';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
type ShowMoreProps = {
|
type ShowMoreProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -15,6 +15,7 @@ export const ShowMore = ({
|
|||||||
closedMaxHeightPx = 125,
|
closedMaxHeightPx = 125,
|
||||||
overlayColourOverrides,
|
overlayColourOverrides,
|
||||||
}: ShowMoreProps) => {
|
}: ShowMoreProps) => {
|
||||||
|
const t = useT();
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||||
import { SunIcon, MoonIcon } from './icons';
|
import { SunIcon, MoonIcon } from './icons';
|
||||||
import { Toggle } from '../toggle';
|
import { Toggle } from '../toggle';
|
||||||
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
export const ThemeSwitcher = ({
|
export const ThemeSwitcher = ({
|
||||||
className,
|
className,
|
||||||
@ -10,6 +10,7 @@ export const ThemeSwitcher = ({
|
|||||||
className?: string;
|
className?: string;
|
||||||
withMobile?: boolean;
|
withMobile?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useT();
|
||||||
const { theme, setTheme } = useThemeSwitcher();
|
const { theme, setTheme } = useThemeSwitcher();
|
||||||
const button = (
|
const button = (
|
||||||
<button
|
<button
|
||||||
@ -35,7 +36,7 @@ export const ThemeSwitcher = ({
|
|||||||
];
|
];
|
||||||
return withMobile ? (
|
return withMobile ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex grow gap-6 md:hidden whitespace-nowrap justify-between">
|
<div className="flex grow justify-between gap-6 whitespace-nowrap md:hidden">
|
||||||
{button}{' '}
|
{button}{' '}
|
||||||
<Toggle
|
<Toggle
|
||||||
name="theme-switch"
|
name="theme-switch"
|
||||||
|
@ -1,28 +1,27 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { IconNames } from '@blueprintjs/icons';
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
import { Icon } from '../icon';
|
import { Icon } from '../icon';
|
||||||
import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts';
|
import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { Intent } from '../../utils/intent';
|
import { Intent } from '../../utils/intent';
|
||||||
|
import { useT } from '../../use-t';
|
||||||
const TEST_TOAST = {
|
|
||||||
id: 'test-toast',
|
|
||||||
intent: Intent.Primary,
|
|
||||||
content: <>{t('This is an example of a toast notification')}</>,
|
|
||||||
onClose: () => useToasts.getState().remove('test-toast'),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ToastPositionSetter = () => {
|
export const ToastPositionSetter = () => {
|
||||||
|
const t = useT();
|
||||||
const setPostion = useToastsConfiguration((store) => store.setPosition);
|
const setPostion = useToastsConfiguration((store) => store.setPosition);
|
||||||
const position = useToastsConfiguration((store) => store.position);
|
const position = useToastsConfiguration((store) => store.position);
|
||||||
const setToast = useToasts((store) => store.setToast);
|
const setToast = useToasts((store) => store.setToast);
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(position: ToastPosition) => {
|
(position: ToastPosition) => {
|
||||||
setPostion(position);
|
setPostion(position);
|
||||||
setToast(TEST_TOAST);
|
setToast({
|
||||||
|
id: 'test-toast',
|
||||||
|
intent: Intent.Primary,
|
||||||
|
content: <>{t('This is an example of a toast notification')}</>,
|
||||||
|
onClose: () => useToasts.getState().remove('test-toast'),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setToast, setPostion]
|
[setToast, setPostion, t]
|
||||||
);
|
);
|
||||||
const buttonCssClasses =
|
const buttonCssClasses =
|
||||||
'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400';
|
'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400';
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
import { Intent } from '../../utils/intent';
|
import { Intent } from '../../utils/intent';
|
||||||
import { Icon, VegaIcon, VegaIconNames } from '../icon';
|
import { Icon, VegaIcon, VegaIconNames } from '../icon';
|
||||||
import { Loader } from '../loader';
|
import { Loader } from '../loader';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
export type ToastContent = JSX.Element | undefined;
|
export type ToastContent = JSX.Element | undefined;
|
||||||
|
|
||||||
@ -83,6 +83,7 @@ export const CollapsiblePanel = forwardRef<
|
|||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
CollapsiblePanelProps & HTMLAttributes<HTMLDivElement>
|
CollapsiblePanelProps & HTMLAttributes<HTMLDivElement>
|
||||||
>(({ children, className, actions, ...props }, ref) => {
|
>(({ children, className, actions, ...props }, ref) => {
|
||||||
|
const t = useT();
|
||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { usePrevious } from '@vegaprotocol/react-helpers';
|
import { usePrevious } from '@vegaprotocol/react-helpers';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { Ref } from 'react';
|
import type { Ref } from 'react';
|
||||||
@ -9,6 +8,7 @@ import type { Toasts } from './use-toasts';
|
|||||||
import { ToastPosition, useToasts, useToastsConfiguration } from './use-toasts';
|
import { ToastPosition, useToasts, useToastsConfiguration } from './use-toasts';
|
||||||
|
|
||||||
import { Portal } from '@radix-ui/react-portal';
|
import { Portal } from '@radix-ui/react-portal';
|
||||||
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
type ToastsContainerProps = {
|
type ToastsContainerProps = {
|
||||||
toasts: Toasts;
|
toasts: Toasts;
|
||||||
@ -21,6 +21,7 @@ export const ToastsContainer = ({
|
|||||||
order = 'asc',
|
order = 'asc',
|
||||||
showHidden = false,
|
showHidden = false,
|
||||||
}: ToastsContainerProps) => {
|
}: ToastsContainerProps) => {
|
||||||
|
const t = useT();
|
||||||
const ref = useRef<HTMLDivElement>();
|
const ref = useRef<HTMLDivElement>();
|
||||||
const closeAll = useToasts((store) => store.closeAll);
|
const closeAll = useToasts((store) => store.closeAll);
|
||||||
const position = useToastsConfiguration((store) => store.position);
|
const position = useToastsConfiguration((store) => store.position);
|
||||||
@ -55,17 +56,17 @@ export const ToastsContainer = ({
|
|||||||
'absolute z-20',
|
'absolute z-20',
|
||||||
{ 'bottom-0 right-0': position === ToastPosition.BottomRight },
|
{ 'bottom-0 right-0': position === ToastPosition.BottomRight },
|
||||||
{ 'bottom-0 left-0': position === ToastPosition.BottomLeft },
|
{ 'bottom-0 left-0': position === ToastPosition.BottomLeft },
|
||||||
{ 'top-0 left-0': position === ToastPosition.TopLeft },
|
{ 'left-0 top-0': position === ToastPosition.TopLeft },
|
||||||
{ 'top-0 right-0': position === ToastPosition.TopRight },
|
{ 'right-0 top-0': position === ToastPosition.TopRight },
|
||||||
{
|
{
|
||||||
'top-0 left-[50%] translate-x-[-50%]':
|
'left-[50%] top-0 translate-x-[-50%]':
|
||||||
position === ToastPosition.TopCenter,
|
position === ToastPosition.TopCenter,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'bottom-0 left-[50%] translate-x-[-50%]':
|
'bottom-0 left-[50%] translate-x-[-50%]':
|
||||||
position === ToastPosition.BottomCenter,
|
position === ToastPosition.BottomCenter,
|
||||||
},
|
},
|
||||||
'max-w-full max-h-full overflow-x-hidden overflow-y-auto',
|
'max-h-full max-w-full overflow-y-auto overflow-x-hidden',
|
||||||
{
|
{
|
||||||
'p-4': validToasts.length > 0, // only apply padding when toasts showing, otherwise a small section of the screen is covered
|
'p-4': validToasts.length > 0, // only apply padding when toasts showing, otherwise a small section of the screen is covered
|
||||||
hidden: validToasts.length === 0,
|
hidden: validToasts.length === 0,
|
||||||
@ -89,9 +90,9 @@ export const ToastsContainer = ({
|
|||||||
})}
|
})}
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'absolute w-full top-[-38px] right-0 z-20',
|
'absolute right-0 top-[-38px] z-20 w-full',
|
||||||
'transition-opacity',
|
'transition-opacity',
|
||||||
'opacity-0 group-hover:opacity-50 hover:!opacity-100',
|
'opacity-0 hover:!opacity-100 group-hover:opacity-50',
|
||||||
{
|
{
|
||||||
hidden: validToasts.length === 0,
|
hidden: validToasts.length === 0,
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { forwardRef, type ComponentProps, type ReactNode } from 'react';
|
|||||||
import { VegaIcon, VegaIconNames } from '../icon';
|
import { VegaIcon, VegaIconNames } from '../icon';
|
||||||
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
|
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
|
||||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
const itemClass = classNames(
|
const itemClass = classNames(
|
||||||
'relative flex gap-2 items-center rounded-sm p-2 text-sm',
|
'relative flex gap-2 items-center rounded-sm p-2 text-sm',
|
||||||
@ -188,6 +188,7 @@ export const TradingDropdownCopyItem = ({
|
|||||||
value: string;
|
value: string;
|
||||||
text: string;
|
text: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useT();
|
||||||
const [copied, setCopied] = useCopyTimeout();
|
const [copied, setCopied] = useCopyTimeout();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
|
|
||||||
type VegaLogoProps = {
|
type VegaLogoProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
export const VegaLogo = ({ className }: VegaLogoProps) => {
|
export const VegaLogo = ({ className }: VegaLogoProps) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
aria-label={t('Vega logo')}
|
aria-label="Vega"
|
||||||
className={className || 'h-6'}
|
className={className || 'h-6'}
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -23,7 +21,7 @@ export const VegaLogo = ({ className }: VegaLogoProps) => {
|
|||||||
export const VLogo = ({ className }: { className?: string }) => {
|
export const VLogo = ({ className }: { className?: string }) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
aria-label={t('Vega logo')}
|
aria-label="Vega"
|
||||||
width="29"
|
width="29"
|
||||||
height="34"
|
height="34"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { NotificationBanner, SHORT } from '../notification-banner';
|
import { NotificationBanner, SHORT } from '../notification-banner';
|
||||||
import { Intent } from '../../utils/intent';
|
import { Intent } from '../../utils/intent';
|
||||||
import { TradingButton } from '../trading-button';
|
import { TradingButton } from '../trading-button';
|
||||||
|
import { useT } from '../../use-t';
|
||||||
|
|
||||||
export function truncateMiddle(address: string, start = 6, end = 4) {
|
export function truncateMiddle(address: string, start = 6, end = 4) {
|
||||||
if (address.length < 11) return address;
|
if (address.length < 11) return address;
|
||||||
@ -21,15 +21,14 @@ export const ViewingAsBanner = ({
|
|||||||
pubKey,
|
pubKey,
|
||||||
disconnect,
|
disconnect,
|
||||||
}: ViewingAsBannerProps) => {
|
}: ViewingAsBannerProps) => {
|
||||||
|
const t = useT();
|
||||||
return (
|
return (
|
||||||
<NotificationBanner
|
<NotificationBanner intent={Intent.None} className={SHORT}>
|
||||||
data-testid="view-banner"
|
<div className="flex items-baseline justify-between">
|
||||||
intent={Intent.None}
|
<span data-testid="view-banner">
|
||||||
className={SHORT}
|
{t('Viewing as Vega user: {{pubKey}}', {
|
||||||
>
|
pubKey: pubKey && truncateMiddle(pubKey),
|
||||||
<div className="flex justify-between items-baseline">
|
})}
|
||||||
<span>
|
|
||||||
{t('Viewing as Vega user:')} {pubKey && truncateMiddle(pubKey)}{' '}
|
|
||||||
</span>
|
</span>
|
||||||
<TradingButton
|
<TradingButton
|
||||||
intent={Intent.None}
|
intent={Intent.None}
|
||||||
|
3
libs/ui-toolkit/src/use-t.ts
Normal file
3
libs/ui-toolkit/src/use-t.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
export const ns = 'ui-toolkit';
|
||||||
|
export const useT = () => useTranslation(ns).t;
|
Loading…
Reference in New Issue
Block a user