diff --git a/.changeset/khaki-poets-double.md b/.changeset/khaki-poets-double.md new file mode 100644 index 000000000..b61c0b9fd --- /dev/null +++ b/.changeset/khaki-poets-double.md @@ -0,0 +1,5 @@ +--- +'@siafoundation/design-system': minor +--- + +Added dockedControls to AppAuthedLayout. diff --git a/.changeset/selfish-parrots-applaud.md b/.changeset/selfish-parrots-applaud.md index dac1240e5..a55269424 100644 --- a/.changeset/selfish-parrots-applaud.md +++ b/.changeset/selfish-parrots-applaud.md @@ -3,4 +3,4 @@ 'renterd': minor --- -The onboarding wizard is now bottom right aligned. +The onboarding wizard now animates in and out. diff --git a/.changeset/spotty-readers-shake.md b/.changeset/spotty-readers-shake.md new file mode 100644 index 000000000..2f3f057ef --- /dev/null +++ b/.changeset/spotty-readers-shake.md @@ -0,0 +1,5 @@ +--- +'renterd': minor +--- + +The transfers bar now animates in and out. diff --git a/apps/hostd/components/DockedControls.tsx b/apps/hostd/components/DockedControls.tsx new file mode 100644 index 000000000..6f3bed20f --- /dev/null +++ b/apps/hostd/components/DockedControls.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import { OnboardingBar } from './OnboardingBar' + +export function DockedControls({ children }: { children?: React.ReactNode }) { + return ( +
+ {children} + +
+ ) +} diff --git a/apps/hostd/components/HostdAuthedLayout.tsx b/apps/hostd/components/HostdAuthedLayout.tsx index 7dd9f1bdd..42f928935 100644 --- a/apps/hostd/components/HostdAuthedLayout.tsx +++ b/apps/hostd/components/HostdAuthedLayout.tsx @@ -5,21 +5,22 @@ import { connectivityRoute } from '../config/routes' import { useSyncStatus } from '../hooks/useSyncStatus' import { HostdTestnetWarningBanner } from './HostdTestnetWarningBanner' import { Profile } from './Profile' +import { DockedControls } from './DockedControls' type Props = Omit< React.ComponentProps, 'appName' | 'connectivityRoute' | 'walletBalance' | 'profile' | 'isSynced' > -export function HostdAuthedLayout(props: Props) { +export function HostdAuthedLayout({ dockedControls, ...props }: Props) { const wallet = useWallet() const { isSynced } = useSyncStatus() return ( } profile={} + banner={} isSynced={isSynced} walletBalanceSc={ wallet.data && { @@ -29,6 +30,7 @@ export function HostdAuthedLayout(props: Props) { unconfirmed: new BigNumber(wallet.data.unconfirmed), } } + dockedControls={{dockedControls}} {...props} /> ) diff --git a/apps/hostd/components/OnboardingBar.tsx b/apps/hostd/components/OnboardingBar.tsx index 21b9a54f2..4ec23724b 100644 --- a/apps/hostd/components/OnboardingBar.tsx +++ b/apps/hostd/components/OnboardingBar.tsx @@ -23,6 +23,7 @@ import { toHastings } from '@siafoundation/units' import { useAppSettings } from '@siafoundation/react-core' import { useVolumes } from '../contexts/volumes' import useLocalStorageState from 'use-local-storage-state' +import { AnimatePresence, motion } from 'framer-motion' export function OnboardingBar() { const { isUnlockedAndAuthedRoute } = useAppSettings() @@ -61,13 +62,13 @@ export function OnboardingBar() { const totalSteps = steps.length const completedSteps = steps.filter((step) => step).length - if (totalSteps === completedSteps) { - return null - } + let el = null - if (maximized) { - return ( -
+ if (totalSteps === completedSteps) { + el = null + } else if (maximized) { + el = ( +
@@ -230,20 +231,37 @@ export function OnboardingBar() {
) + } else { + el = ( +
+ +
+ ) } + return ( -
- -
+ + {el && ( + + {el} + + )} + ) } diff --git a/apps/hostd/pages/_app.tsx b/apps/hostd/pages/_app.tsx index 3bcc5f40d..5bd615516 100644 --- a/apps/hostd/pages/_app.tsx +++ b/apps/hostd/pages/_app.tsx @@ -33,6 +33,7 @@ export default function App(props: AppPropsWithLayout) { ) } + function AppCore({ Component, pageProps }: AppPropsWithLayout) { const Layout = Component.Layout const layoutProps = Component.useLayoutProps() diff --git a/apps/renterd/components/DockedControls.tsx b/apps/renterd/components/DockedControls.tsx new file mode 100644 index 000000000..81f381999 --- /dev/null +++ b/apps/renterd/components/DockedControls.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { OnboardingBar } from './OnboardingBar' +import { TransfersBar } from './TransfersBar' + +export function DockedControls({ children }: { children?: React.ReactNode }) { + return ( +
+ {children} + + +
+ ) +} diff --git a/apps/renterd/components/Keys/KeysDockedControls.tsx b/apps/renterd/components/Keys/KeysDockedControls.tsx new file mode 100644 index 000000000..f2b5adfaa --- /dev/null +++ b/apps/renterd/components/Keys/KeysDockedControls.tsx @@ -0,0 +1,5 @@ +import { KeysBatchMenu } from './KeysBatchMenu' + +export function KeysDockedControls() { + return +} diff --git a/apps/renterd/components/Keys/Layout.tsx b/apps/renterd/components/Keys/Layout.tsx index 83e3b1a5b..6f367c814 100644 --- a/apps/renterd/components/Keys/Layout.tsx +++ b/apps/renterd/components/Keys/Layout.tsx @@ -7,6 +7,7 @@ import { } from '../RenterdAuthedLayout' import { KeysActionsMenu } from './KeysActionsMenu' import { KeysStatsMenu } from './KeysStatsMenu' +import { KeysDockedControls } from './KeysDockedControls' export const Layout = RenterdAuthedLayout export function useLayoutProps(): RenterdAuthedPageLayoutProps { @@ -18,5 +19,6 @@ export function useLayoutProps(): RenterdAuthedPageLayoutProps { openSettings: () => openDialog('settings'), actions: , stats: , + dockedControls: , } } diff --git a/apps/renterd/components/Keys/index.tsx b/apps/renterd/components/Keys/index.tsx index 59b88784c..e4dcc0758 100644 --- a/apps/renterd/components/Keys/index.tsx +++ b/apps/renterd/components/Keys/index.tsx @@ -3,7 +3,6 @@ import { StateNoneMatching } from './StateNoneMatching' import { StateNoneYet } from './StateNoneYet' import { StateError } from './StateError' import { useKeys } from '../../contexts/keys' -import { KeysBatchMenu } from './KeysBatchMenu' export function Keys() { const { @@ -20,7 +19,6 @@ export function Keys() { return (
- step).length - if (totalSteps === completedSteps) { - return null - } + let el = null - if (maximized) { - return ( -
+ if (totalSteps === completedSteps) { + el = null + } else if (maximized) { + el = ( +
@@ -237,20 +238,37 @@ export function OnboardingBar() {
) + } else { + el = ( +
+ +
+ ) } + return ( -
- -
+ + {el && ( + + {el} + + )} + ) } diff --git a/apps/renterd/components/RenterdAuthedLayout.tsx b/apps/renterd/components/RenterdAuthedLayout.tsx index c9d6a21b1..735ccd777 100644 --- a/apps/renterd/components/RenterdAuthedLayout.tsx +++ b/apps/renterd/components/RenterdAuthedLayout.tsx @@ -5,30 +5,32 @@ import { connectivityRoute } from '../config/routes' import { useSyncStatus } from '../hooks/useSyncStatus' import { Profile } from './Profile' import { RenterdTestnetWarningBanner } from './RenterdTestnetWarningBanner' +import { DockedControls } from './DockedControls' type Props = Omit< React.ComponentProps, 'appName' | 'connectivityRoute' | 'walletBalance' | 'profile' | 'isSynced' > -export function RenterdAuthedLayout(props: Props) { +export function RenterdAuthedLayout({ dockedControls, ...props }: Props) { const wallet = useWallet() const { isSynced } = useSyncStatus() return ( } banner={} - connectivityRoute={connectivityRoute} isSynced={isSynced} walletBalanceSc={ wallet.data && { spendable: new BigNumber(wallet.data.spendable), confirmed: new BigNumber(wallet.data.confirmed), - unconfirmed: new BigNumber(wallet.data.unconfirmed), immature: new BigNumber(wallet.data.immature), + unconfirmed: new BigNumber(wallet.data.unconfirmed), } } + dockedControls={{dockedControls}} {...props} /> ) diff --git a/apps/renterd/components/TransfersBar.tsx b/apps/renterd/components/TransfersBar.tsx index 8ff48bfaa..030d28c40 100644 --- a/apps/renterd/components/TransfersBar.tsx +++ b/apps/renterd/components/TransfersBar.tsx @@ -5,6 +5,7 @@ import { useFilesManager } from '../contexts/filesManager' import { useAppSettings } from '@siafoundation/react-core' import { TransfersBarItem } from './TransfersBarItem' import { useUploads } from '../contexts/uploads' +import { AnimatePresence, motion } from 'framer-motion' export function TransfersBar() { const { isUnlockedAndAuthedRoute } = useAppSettings() @@ -17,80 +18,92 @@ export function TransfersBar() { const downloadCount = downloadsList.length const isActiveDownloads = !!downloadCount - if (!isUnlockedAndAuthedRoute) { - return null - } - - if (!isActiveUploads && !isActiveDownloads) { - return null - } + let el = null - const controls = ( -
- {isActiveUploads && !isViewingUploads ? ( - - ) : null} - {isActiveDownloads ? ( - - ) : null} -
- ) - - if (isActiveDownloads && maximized) { - return ( -
- - - {isActiveDownloads ? ( - <> -
- - Active downloads ({downloadCount}) - - -
- {downloadsList.map((download) => ( - downloadCancel(download)} - abortTip="Cancel download" - /> - ))} - - ) : null} -
-
- {controls} + if (!isUnlockedAndAuthedRoute) { + el = null + } else if (!isActiveUploads && !isActiveDownloads) { + el = null + } else { + const controls = ( +
+ {isActiveUploads && !isViewingUploads ? ( + + ) : null} + {isActiveDownloads ? ( + + ) : null}
) + + el = controls + + if (isActiveDownloads && maximized) { + el = ( +
+ + + {isActiveDownloads ? ( + <> +
+ + Active downloads ({downloadCount}) + + +
+ {downloadsList.map((download) => ( + downloadCancel(download)} + abortTip="Cancel download" + /> + ))} + + ) : null} +
+
+ {controls} +
+ ) + } } + return ( -
- {controls} -
+ + {el && ( + + {el} + + )} + ) } diff --git a/apps/renterd/config/providers.tsx b/apps/renterd/config/providers.tsx index 413a68929..87fd107f3 100644 --- a/apps/renterd/config/providers.tsx +++ b/apps/renterd/config/providers.tsx @@ -4,8 +4,6 @@ import { ContractsProvider } from '../contexts/contracts' import { HostsProvider } from '../contexts/hosts' import { AppProvider } from '../contexts/app' import { ConfigProvider } from '../contexts/config' -import { OnboardingBar } from '../components/OnboardingBar' -import { TransfersBar } from '../components/TransfersBar' import { TransactionsProvider } from '../contexts/transactions' import { KeysProvider } from '../contexts/keys' import { FilesFlatProvider } from '../contexts/filesFlat' @@ -34,8 +32,6 @@ export function Providers({ children }: Props) { {/* this is here so that dialogs can use all the other providers, and the other providers can trigger dialogs */} - - {children} diff --git a/apps/renterd/pages/_app.tsx b/apps/renterd/pages/_app.tsx index 57173c7dc..426600a61 100644 --- a/apps/renterd/pages/_app.tsx +++ b/apps/renterd/pages/_app.tsx @@ -33,6 +33,7 @@ export default function App(props: AppPropsWithLayout) { ) } + function AppCore({ Component, pageProps }: AppPropsWithLayout) { const Layout = Component.Layout const layoutProps = Component.useLayoutProps() diff --git a/apps/walletd/components/DockedControls.tsx b/apps/walletd/components/DockedControls.tsx new file mode 100644 index 000000000..275daebd7 --- /dev/null +++ b/apps/walletd/components/DockedControls.tsx @@ -0,0 +1,5 @@ +import React from 'react' + +export function DockedControls({ children }: { children?: React.ReactNode }) { + return
{children}
+} diff --git a/apps/walletd/components/WalletdAuthedLayout.tsx b/apps/walletd/components/WalletdAuthedLayout.tsx index 128143fcb..b1f46a230 100644 --- a/apps/walletd/components/WalletdAuthedLayout.tsx +++ b/apps/walletd/components/WalletdAuthedLayout.tsx @@ -3,13 +3,14 @@ import { connectivityRoute } from '../config/routes' import { useSyncStatus } from '../hooks/useSyncStatus' import { Profile } from './Profile' import { WalletdTestnetWarningBanner } from './WalletdTestnetWarningBanner' +import { DockedControls } from './DockedControls' type Props = Omit< React.ComponentProps, 'appName' | 'connectivityRoute' | 'walletBalance' | 'profile' | 'isSynced' > -export function WalletdAuthedLayout(props: Props) { +export function WalletdAuthedLayout({ dockedControls, ...props }: Props) { const { isSynced } = useSyncStatus() return ( } showWallet={false} isSynced={isSynced} + dockedControls={{dockedControls}} {...props} /> ) diff --git a/apps/walletd/pages/_app.tsx b/apps/walletd/pages/_app.tsx index 02790f645..a937c00c7 100644 --- a/apps/walletd/pages/_app.tsx +++ b/apps/walletd/pages/_app.tsx @@ -31,6 +31,7 @@ export default function App(props: AppPropsWithLayout) { ) } + function AppCore({ Component, pageProps }: AppPropsWithLayout) { const Layout = Component.Layout const layoutProps = Component.useLayoutProps() diff --git a/libs/design-system/src/app/AppAuthedLayout/index.tsx b/libs/design-system/src/app/AppAuthedLayout/index.tsx index ac99ea8ae..e56e8d6e4 100644 --- a/libs/design-system/src/app/AppAuthedLayout/index.tsx +++ b/libs/design-system/src/app/AppAuthedLayout/index.tsx @@ -21,6 +21,7 @@ type Props = { nav?: React.ReactNode banner?: React.ReactNode actions?: React.ReactNode + dockedControls?: React.ReactNode sidenav?: React.ReactNode stats?: React.ReactNode after?: React.ReactNode @@ -59,6 +60,7 @@ export function AppAuthedLayout({ actions, stats, after, + dockedControls, children, sidenav, connectivityRoute, @@ -123,6 +125,9 @@ export function AppAuthedLayout({ {children} )} +
+ {dockedControls} +
diff --git a/libs/design-system/src/multi/MultiSelectionMenu.tsx b/libs/design-system/src/multi/MultiSelectionMenu.tsx index cf772a385..2ef80b17c 100644 --- a/libs/design-system/src/multi/MultiSelectionMenu.tsx +++ b/libs/design-system/src/multi/MultiSelectionMenu.tsx @@ -21,50 +21,49 @@ export function MultiSelectionMenu({ }) { const isVisible = multiSelect.selectionCount > 0 return ( -
- - {isVisible && ( - + {isVisible && ( + + - + {`${pluralize(multiSelect.selectionCount, entityWord, { + plural: entityWordPlural, + })} selected${ + multiSelect.someSelectedItemsOutsideCurrentPage && + multiSelect.someSelectedOnCurrentPage + ? ' on this and other pages' + : !multiSelect.someSelectedItemsOutsideCurrentPage && + multiSelect.someSelectedOnCurrentPage + ? '' + : multiSelect.someSelectedItemsOutsideCurrentPage && + !multiSelect.someSelectedOnCurrentPage + ? ' on other pages' + : '' + }`} + + )} +
+ {children} + - - - )} - -
+ + +
+
+ )} +
) }