diff --git a/package.json b/package.json index c271bf8..d99fab9 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@dydxprotocol/v4-localization": "^1.1.5", "@ethersproject/providers": "^5.7.2", "@js-joda/core": "^5.5.3", + "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-dialog": "^1.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 552ea95..67939f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - dependencies: '@0xsquid/sdk': specifier: ^1.10.0 @@ -41,6 +37,9 @@ dependencies: '@js-joda/core': specifier: ^5.5.3 version: 5.5.3 + '@radix-ui/react-accordion': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-checkbox': specifier: ^1.0.4 version: 1.0.4(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) @@ -2320,6 +2319,35 @@ packages: '@babel/runtime': 7.22.10 dev: false + /@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.10 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.14)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.14)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.14)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.14)(react@18.2.0) + '@types/react': 18.2.14 + '@types/react-dom': 18.2.6 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: @@ -11748,8 +11776,8 @@ packages: engines: {node: '>=10.5.0'} dev: true - /node-fetch-native@1.4.1: - resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==} + /node-fetch-native@1.6.1: + resolution: {integrity: sha512-bW9T/uJDPAJB2YNYEpWzE54U5O3MQidXsOyTfnbKYtTtFexRvGzb1waphBN4ZwP6EcIvYYEOwW0b72BpAqydTw==} dev: false /node-fetch-native@1.6.1: @@ -11899,7 +11927,7 @@ packages: resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==} dependencies: destr: 2.0.2 - node-fetch-native: 1.4.1 + node-fetch-native: 1.6.1 ufo: 1.3.2 dev: false @@ -13787,7 +13815,7 @@ packages: consola: 3.2.3 defu: 6.1.3 mime: 3.0.0 - node-fetch-native: 1.4.1 + node-fetch-native: 1.6.1 pathe: 1.1.1 dev: false @@ -13910,7 +13938,7 @@ packages: listhen: 1.5.5 lru-cache: 10.1.0 mri: 1.2.0 - node-fetch-native: 1.4.1 + node-fetch-native: 1.6.1 ofetch: 1.3.3 ufo: 1.3.2 transitivePeerDependencies: @@ -14726,3 +14754,7 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: true + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/src/components/Accordion.tsx b/src/components/Accordion.tsx new file mode 100644 index 0000000..b65c90e --- /dev/null +++ b/src/components/Accordion.tsx @@ -0,0 +1,119 @@ +import styled, { keyframes, type AnyStyledComponent } from 'styled-components'; + +import { Root, Item, Header, Trigger, Content } from '@radix-ui/react-accordion'; + +import { layoutMixins } from '@/styles/layoutMixins'; +import { PlusIcon } from '@/icons'; + +export type AccordionItem = { + header: React.ReactNode; + content: React.ReactNode; +}; + +type AccordionProps = { + items: AccordionItem[]; + className?: string; +}; + +export const Accordion = ({ items, className }: AccordionProps) => ( + + {items.map(({ header, content }, idx) => ( + +
+ + {header} + + + + +
+ {content} +
+ ))} +
+); + +const Styled: Record = {}; + +Styled.Root = styled(Root)` + > *:not(:last-child) { + border-bottom: var(--border-width) solid var(--border-color); + } +`; + +Styled.Item = styled(Item)``; + +Styled.Icon = styled.div` + display: inline-flex; + justify-content: center; + align-items: center; + + width: 2.25rem; + height: 2.25rem; + + --color-border: var(--color-layer-6); + color: var(--color-text-1); + background-color: var(--color-layer-5); + border: solid var(--border-width) var(--color-border); + border-radius: 50%; + font: var(--font-small-book); + + svg { + height: 1.125em; + width: 1.125em; + } +`; + +Styled.Trigger = styled(Trigger)` + ${layoutMixins.spacedRow} + width: 100%; + padding: 1rem 0.75rem; + + color: var(--color-text-1); + text-align: start; + + &:hover { + ${Styled.Icon} { + color: var(--color-text-2); + filter: brightness(1.1); + } + } + + svg { + color: var(--color-text-0); + transition: transform 0.3s var(--ease-out-expo); + } + + &[data-state='open'] svg { + transform: rotate(45deg); + } +`; + +Styled.Content = styled(Content)` + overflow: hidden; + margin: 0 0.75rem 1rem; + + color: var(--color-text-0); + + &[data-state='open'] { + animation: ${keyframes` + from { + height: 0; + } + to { + height: var(--radix-accordion-content-height); + } + `} 0.3s var(--ease-out-expo); + } + + &[data-state='closed'] { + animation: ${keyframes` + from { + height: var(--radix-accordion-content-height); + } + to { + height: 0; + } + `} 0.1s var(--ease-in-expo); + } +`; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index d5f5d64..d8db86f 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -55,6 +55,7 @@ import { OverviewIcon, PencilIcon, PlayIcon, + PlusIcon, PositionsIcon, PriceChartIcon, PrivacyIcon, @@ -130,6 +131,7 @@ export enum IconName { Overview = 'Overview', Pencil = 'Pencil', Play = 'Play', + Plus = 'Plus', Positions = 'Positions', PriceChart = 'PriceChart', Privacy = 'Privacy', @@ -204,6 +206,7 @@ const icons = { [IconName.Overview]: OverviewIcon, [IconName.Pencil]: PencilIcon, [IconName.Play]: PlayIcon, + [IconName.Plus]: PlusIcon, [IconName.Positions]: PositionsIcon, [IconName.PriceChart]: PriceChartIcon, [IconName.Privacy]: PrivacyIcon, diff --git a/src/icons/index.ts b/src/icons/index.ts index a32913e..bd3b347 100644 --- a/src/icons/index.ts +++ b/src/icons/index.ts @@ -44,6 +44,7 @@ export { default as OrderbookIcon } from './orderbook.svg'; export { default as OverviewIcon } from './overview.svg'; export { default as PencilIcon } from './pencil.svg'; export { default as PlayIcon } from './play.svg'; +export { default as PlusIcon } from './plus.svg'; export { default as PortfolioIcon } from './portfolio.svg'; export { default as PositionsIcon } from './positions.svg'; export { default as PriceChartIcon } from './price-chart.svg'; diff --git a/src/icons/plus.svg b/src/icons/plus.svg new file mode 100644 index 0000000..c74751a --- /dev/null +++ b/src/icons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/pages/rewards/RewardsHelpPanel.tsx b/src/pages/rewards/RewardsHelpPanel.tsx new file mode 100644 index 0000000..e7c23d6 --- /dev/null +++ b/src/pages/rewards/RewardsHelpPanel.tsx @@ -0,0 +1,80 @@ +import styled, { AnyStyledComponent } from 'styled-components'; + +import { STRING_KEYS } from '@/constants/localization'; +import { layoutMixins } from '@/styles/layoutMixins'; +import { useStringGetter } from '@/hooks'; + +import { Accordion } from '@/components/Accordion'; +import { Link } from '@/components/Link'; +import { Panel } from '@/components/Panel'; + +const REWARDS_LEARN_MORE_LINK = ''; // to be configured + +export const RewardsHelpPanel = () => { + const stringGetter = useStringGetter(); + + return ( + +

{stringGetter({ key: STRING_KEYS.HELP })}

+ {REWARDS_LEARN_MORE_LINK && ( + + {stringGetter({ key: STRING_KEYS.LEARN_MORE })} + + )} + + } + > + +
+ ); +}; + +const Styled: Record = {}; + +Styled.HelpCard = styled(Panel)` + --panel-content-paddingY: 0; + width: 100%; + height: max-content; + padding: 0; + gap: 0; + + text-align: start; +`; + +Styled.Header = styled.div` + ${layoutMixins.spacedRow} + gap: 1ch; + + padding: 1.25rem 1.5rem; + border-bottom: var(--border-width) solid var(--border-color); + + font: var(--font-small-book); + + h3 { + font: var(--font-medium-book); + color: var(--color-text-2); + } +`; + +Styled.Link = styled(Link)` + display: inline-flex; +`;