From f37bb1bf4c0714a1b51fd3b363665ae0b3a85649 Mon Sep 17 00:00:00 2001 From: alter-eggo Date: Mon, 6 May 2024 17:03:44 +0400 Subject: [PATCH] feat: hide asset list unsupported tokens in accordion, closes #16 --- package.json | 1 + pnpm-lock.yaml | 110 ++++++++++++++---- src/app/features/asset-list/asset-list.tsx | 4 + .../stacks-fungible-token-asset-list.tsx | 10 +- .../stacks-unsupported-token-asset-list.tsx | 39 +++++++ .../balance/stacks-ft-balances.hooks.ts | 52 ++++++++- .../accordion/accordion.stories.tsx | 31 +++++ src/app/ui/components/accordion/accordion.tsx | 69 +++++++++++ theme/keyframes.ts | 8 ++ 9 files changed, 295 insertions(+), 29 deletions(-) create mode 100644 src/app/features/asset-list/components/stacks-unsupported-token-asset-list.tsx create mode 100644 src/app/ui/components/accordion/accordion.stories.tsx create mode 100644 src/app/ui/components/accordion/accordion.tsx diff --git a/package.json b/package.json index 9d73df55176..3c670d84347 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "@octokit/types": "12.4.0", "@radix-ui/colors": "3.0.0", "@radix-ui/react-accessible-icon": "1.0.3", + "@radix-ui/react-accordion": "1.1.2", "@radix-ui/react-avatar": "1.0.4", "@radix-ui/react-dialog": "1.0.5", "@radix-ui/react-dropdown-menu": "2.0.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 063bf34f70e..d146ebca8e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ dependencies: '@radix-ui/react-accessible-icon': specifier: 1.0.3 version: 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-accordion': + specifier: 1.1.2 + version: 1.1.2(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-avatar': specifier: 1.0.4 version: 1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) @@ -2263,7 +2266,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 - dev: true /@babel/template@7.24.0: resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} @@ -4382,7 +4384,7 @@ packages: /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 dev: false /@radix-ui/react-accessible-icon@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0): @@ -4406,6 +4408,35 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.19)(@types/react@18.2.57)(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.24.4 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@types/react': 18.2.57 + '@types/react-dom': 18.2.19 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==} peerDependencies: @@ -4445,7 +4476,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.57 '@types/react-dom': 18.2.19 @@ -4526,6 +4557,34 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} + 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.24.4 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.57)(react@18.2.0) + '@types/react': 18.2.57 + '@types/react-dom': 18.2.19 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: @@ -4539,7 +4598,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.57)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.57)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) @@ -4559,7 +4618,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@types/react': 18.2.57 react: 18.2.0 @@ -4598,7 +4657,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@types/react': 18.2.57 react: 18.2.0 dev: false @@ -4646,7 +4705,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@types/react': 18.2.57 react: 18.2.0 dev: false @@ -4804,7 +4863,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.57)(react@18.2.0) '@types/react': 18.2.57 react: 18.2.0 @@ -4823,7 +4882,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.57 '@types/react-dom': 18.2.19 @@ -4990,7 +5049,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-slot': 1.0.2(@types/react@18.2.57)(react@18.2.0) '@types/react': 18.2.57 '@types/react-dom': 18.2.19 @@ -5335,7 +5394,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.57)(react@18.2.0) '@types/react': 18.2.57 react: 18.2.0 @@ -5350,7 +5409,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.57)(react@18.2.0) '@types/react': 18.2.57 react: 18.2.0 @@ -5393,7 +5452,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/rect': 1.0.1 '@types/react': 18.2.57 react: 18.2.0 @@ -5408,7 +5467,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.57)(react@18.2.0) '@types/react': 18.2.57 react: 18.2.0 @@ -5438,7 +5497,7 @@ packages: /@radix-ui/rect@1.0.1: resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 dev: false /@radix-ui/themes@2.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0): @@ -11377,7 +11436,7 @@ packages: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 cosmiconfig: 7.1.0 resolve: 1.22.8 dev: true @@ -13268,7 +13327,7 @@ packages: '@types/d3': ^7.0.0 d3: ^7.0.0 dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@types/d3': 7.4.3 d3: 7.8.5 dev: true @@ -13755,7 +13814,7 @@ packages: /dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 csstype: 3.1.3 dev: true @@ -18015,7 +18074,7 @@ packages: /map2tree@3.0.0: resolution: {integrity: sha512-TOLkmFFiuMc5QwjoXPgbDtAuQXEIEmEyPr6K71qNmHEsxqZWBTVXHaRVWWe2qm2ZXztSbi+VhZ1ktRA2G42dBw==} dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 lodash: 4.17.21 dev: true @@ -20902,7 +20961,7 @@ packages: /react-base16-styling@0.9.1: resolution: {integrity: sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==} dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@types/base16': 1.0.5 '@types/lodash': 4.14.202 base16: 1.0.0 @@ -21090,7 +21149,7 @@ packages: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@types/lodash': 4.14.202 '@types/react': 18.2.57 react: 18.2.0 @@ -21246,7 +21305,7 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 '@emotion/cache': 11.11.0 '@emotion/react': 11.11.3(@types/react@18.2.57)(react@18.2.0) '@floating-ui/dom': 1.6.3 @@ -21284,7 +21343,7 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -21534,7 +21593,7 @@ packages: /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.4 dev: true /regexp-tree@0.1.27: @@ -22597,6 +22656,9 @@ packages: /sqlite3@5.1.6: resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} requiresBuild: true + peerDependenciesMeta: + node-gyp: + optional: true dependencies: '@mapbox/node-pre-gyp': 1.0.11 node-addon-api: 4.3.0 diff --git a/src/app/features/asset-list/asset-list.tsx b/src/app/features/asset-list/asset-list.tsx index 0324823ece5..190afd133de 100644 --- a/src/app/features/asset-list/asset-list.tsx +++ b/src/app/features/asset-list/asset-list.tsx @@ -31,6 +31,7 @@ import { AddStacksLedgerKeysItem } from './components/add-stacks-ledger-keys-ite import { ConnectLedgerAssetBtn } from './components/connect-ledger-asset-button'; import { StacksBalanceListItem } from './components/stacks-balance-list-item'; import { StacksFungibleTokenAssetList } from './components/stacks-fungible-token-asset-list'; +import { StacksUnsupportedTokenAssetList } from './components/stacks-unsupported-token-asset-list'; export function AssetsList() { const hasBitcoinLedgerKeys = useHasBitcoinLedgerKeychain(); @@ -108,6 +109,9 @@ export function AssetsList() { )} + }> + {account => } + diff --git a/src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx b/src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx index d3b1ea5d4ea..99e82df34a2 100644 --- a/src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx +++ b/src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx @@ -1,4 +1,4 @@ -import { useStacksFungibleTokenAssetBalancesWithMetadata } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; +import { useFilteredStacksFungibleTokenList } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; import { StacksFungibleTokenAssetListLayout } from './stacks-fungible-token-asset-list.layout'; @@ -6,6 +6,10 @@ interface StacksFungibleTokenAssetListProps { address: string; } export function StacksFungibleTokenAssetList({ address }: StacksFungibleTokenAssetListProps) { - const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesWithMetadata(address); - return ; + const stacksFilteredFtAssetBalances = useFilteredStacksFungibleTokenList({ + address, + filter: 'supported', + }); + + return ; } diff --git a/src/app/features/asset-list/components/stacks-unsupported-token-asset-list.tsx b/src/app/features/asset-list/components/stacks-unsupported-token-asset-list.tsx new file mode 100644 index 00000000000..ce10286792a --- /dev/null +++ b/src/app/features/asset-list/components/stacks-unsupported-token-asset-list.tsx @@ -0,0 +1,39 @@ +import { useState } from 'react'; + +import { styled } from 'leather-styles/jsx'; + +import { useFilteredStacksFungibleTokenList } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; +import { Accordion } from '@app/ui/components/accordion/accordion'; + +import { StacksFungibleTokenAssetListLayout } from './stacks-fungible-token-asset-list.layout'; + +const accordionValue = 'accordion-unsupported-token-asset-list'; + +export function StacksUnsupportedTokenAssetList({ address }: { address: string }) { + const stacksFilteredFtAssetBalances = useFilteredStacksFungibleTokenList({ + address, + filter: 'unsupported', + }); + + const [isOpen, setIsOpen] = useState(false); + function onValueChange(value: string) { + setIsOpen(value === accordionValue); + } + + if (stacksFilteredFtAssetBalances.length === 0) { + return null; + } + + return ( + + + + View {isOpen ? 'fewer' : 'more'} + + + + + + + ); +} diff --git a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts b/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts index b5feb7cec5a..2316671b187 100644 --- a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts +++ b/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts @@ -7,7 +7,11 @@ import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asse import { formatContractId } from '@app/common/utils'; import { useToast } from '@app/features/toasts/use-toast'; -import { useAlexCurrencyPriceAsMarketData } from '@app/query/common/alex-sdk/alex-sdk.hooks'; +import { + type SwapAsset, + useAlexCurrencyPriceAsMarketData, + useAlexSwappableAssets, +} from '@app/query/common/alex-sdk/alex-sdk.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useGetFungibleTokenMetadataListQuery } from '../tokens/fungible-tokens/fungible-token-metadata.query'; @@ -34,7 +38,7 @@ function useStacksFungibleTokenAssetBalances(address: string) { }); } -export function useStacksFungibleTokenAssetBalancesWithMetadata(address: string) { +function useStacksFungibleTokenAssetBalancesWithMetadata(address: string) { const { data: initializedAssetBalances = [] } = useStacksFungibleTokenAssetBalances(address); const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); @@ -90,3 +94,47 @@ export function useTransferableStacksFungibleTokenAssetBalances( [assetBalances] ); } + +function filterStacksFungibleTokens( + assetBalances: StacksFungibleTokenAssetBalance[], + swapAssets: SwapAsset[], + filter: StacksFtTokensFilter +) { + if (filter === 'supported') { + return assetBalances.filter(assetBalance => + swapAssets.some(swapAsset => swapAsset.principal.includes(assetBalance.asset.contractAddress)) + ); + } + + if (filter === 'unsupported') { + return assetBalances.filter( + assetBalance => + !swapAssets.some(swapAsset => + swapAsset.principal.includes(assetBalance.asset.contractAddress) + ) + ); + } + + return assetBalances; +} + +/** + * @see https://github.com/leather-wallet/issues/issues/16 + */ +type StacksFtTokensFilter = 'all' | 'supported' | 'unsupported'; + +interface useFilteredStacksFungibleTokenListArgs { + address: string; + filter?: StacksFtTokensFilter; +} +export function useFilteredStacksFungibleTokenList({ + address, + filter = 'all', +}: useFilteredStacksFungibleTokenListArgs) { + const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesWithMetadata(address); + const { data: swapAssets = [] } = useAlexSwappableAssets(); + + return useMemo(() => { + return filterStacksFungibleTokens(stacksFtAssetBalances, swapAssets, filter); + }, [stacksFtAssetBalances, swapAssets, filter]); +} diff --git a/src/app/ui/components/accordion/accordion.stories.tsx b/src/app/ui/components/accordion/accordion.stories.tsx new file mode 100644 index 00000000000..2f933fb9ae7 --- /dev/null +++ b/src/app/ui/components/accordion/accordion.stories.tsx @@ -0,0 +1,31 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Box, styled } from 'leather-styles/jsx'; + +import { Accordion as Component } from './accordion'; + +const meta: Meta = { + component: Component.Root, + tags: ['autodocs'], + title: 'Accordion', +}; + +export default meta; + +type Story = StoryObj; + +export const Accordion: Story = { + render: () => ( + + + + + View more + + + Content + + + + + ), +}; diff --git a/src/app/ui/components/accordion/accordion.tsx b/src/app/ui/components/accordion/accordion.tsx new file mode 100644 index 00000000000..63a17c0ac86 --- /dev/null +++ b/src/app/ui/components/accordion/accordion.tsx @@ -0,0 +1,69 @@ +import { forwardRef } from 'react'; + +import * as RadixAccordion from '@radix-ui/react-accordion'; +import { css } from 'leather-styles/css'; + +import { ChevronDownIcon } from '@app/ui/icons'; + +const accordionTriggerStyles = css({ + position: 'relative', + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + py: 'space.04', + textStyle: 'label.02', + transition: 'all 0.2s', + "&[data-state='open']": { + '& > .accordion-icon': { + transform: 'rotate(180deg)', + }, + }, + '& > .accordion-icon': { + transition: 'transform 0.2s', + }, + + _hover: { + color: 'ink.action-primary-hover', + }, +}); + +const Trigger: typeof RadixAccordion.Trigger = forwardRef((props, ref) => ( + + + {props.children} + + + +)); + +const accordionContentStyles = css({ + willChange: 'max-height', + py: 'space.03', + overflowY: 'hidden', + "&[data-state='open']": { + animation: 'slideDown 300ms ease-in', + }, + "&[data-state='closed']": { + animation: 'slideUp 150ms', + }, +}); + +const Content: typeof RadixAccordion.Content = forwardRef(({ className, ...props }, ref) => ( + +)); + +const Item: typeof RadixAccordion.Item = forwardRef((props, ref) => ( + +)); + +export const Accordion = { + Root: RadixAccordion.Root, + Item, + Content, + Trigger, +}; diff --git a/theme/keyframes.ts b/theme/keyframes.ts index 23225294b17..1119bf45051 100644 --- a/theme/keyframes.ts +++ b/theme/keyframes.ts @@ -8,6 +8,14 @@ export const keyframes: CssKeyframes = { from: { opacity: 1, transform: 'translateY(0)' }, to: { opacity: 0, transform: 'translateY(4px)' }, }, + slideDown: { + from: { maxHeight: 0 }, + to: { maxHeight: '1000px' }, + }, + slideUp: { + from: { maxHeight: '1000px' }, + to: { maxHeight: 0 }, + }, toastAppear: { from: { opacity: 0, transform: 'translateY(-12px) scale(0.9)' }, to: { opacity: 1, transform: 'translateY(0) scale(1)' },