diff --git a/webapp/src/components/VaultPage.tsx b/webapp/src/components/VaultPage.tsx index 3819eee..c6c35bf 100644 --- a/webapp/src/components/VaultPage.tsx +++ b/webapp/src/components/VaultPage.tsx @@ -908,6 +908,8 @@ function folderName(id: string | null | undefined): string { selectedCipherId={selectedCipherId} selectedMap={selectedMap} sidebarFilter={sidebarFilter} + isMobileLayout={isMobileLayout} + mobileFabVisible={!isMobileLayout || mobilePanel === 'list'} createMenuOpen={createMenuOpen} createMenuRef={createMenuRef} sortMenuRef={sortMenuRef} diff --git a/webapp/src/components/vault/VaultListPanel.tsx b/webapp/src/components/vault/VaultListPanel.tsx index 0c3fe4a..91dda2d 100644 --- a/webapp/src/components/vault/VaultListPanel.tsx +++ b/webapp/src/components/vault/VaultListPanel.tsx @@ -1,4 +1,5 @@ import type { RefObject } from 'preact'; +import { createPortal } from 'preact/compat'; import { Archive, ArrowUpDown, Check, CheckCheck, FolderInput, Plus, RefreshCw, RotateCcw, Trash2, X } from 'lucide-preact'; import type { Cipher } from '@/lib/types'; import { t } from '@/lib/i18n'; @@ -32,6 +33,8 @@ interface VaultListPanelProps { selectedCipherId: string; selectedMap: Record; sidebarFilter: SidebarFilter; + isMobileLayout: boolean; + mobileFabVisible: boolean; createMenuOpen: boolean; createMenuRef: RefObject; sortMenuRef: RefObject; @@ -60,6 +63,30 @@ interface VaultListPanelProps { } export default function VaultListPanel(props: VaultListPanelProps) { + const createMenu = ( +
+ + {props.createMenuOpen && ( +
+ {CREATE_TYPE_OPTIONS.map((option) => ( + + ))} +
+ )} +
+ ); + return (
@@ -159,27 +186,9 @@ export default function VaultListPanel(props: VaultListPanelProps) { -
- - {props.createMenuOpen && ( -
- {CREATE_TYPE_OPTIONS.map((option) => ( - - ))} -
- )} -
+ {props.isMobileLayout && typeof document !== 'undefined' + ? props.mobileFabVisible ? createPortal(createMenu, document.body) : null + : createMenu}
props.onScroll((event.currentTarget as HTMLDivElement).scrollTop)}> diff --git a/webapp/src/styles/responsive.css b/webapp/src/styles/responsive.css index e082445..41340c2 100644 --- a/webapp/src/styles/responsive.css +++ b/webapp/src/styles/responsive.css @@ -327,11 +327,11 @@ .mobile-fab-wrap { @apply fixed right-3.5 z-[45]; - bottom: calc(14px + var(--mobile-tabbar-height) + env(safe-area-inset-bottom)); + bottom: calc(14px + var(--mobile-tabbar-height, 70px) + env(safe-area-inset-bottom)); } .mobile-fab-trigger { - @apply h-14 w-9 gap-0 rounded-full p-0 text-[0]; + @apply h-9 w-9 gap-0 rounded-full p-0 text-[0]; box-shadow: 0 14px 30px rgba(37, 99, 235, 0.28); transition: transform 180ms var(--ease-spring), box-shadow var(--dur-fast) var(--ease-out-soft); } diff --git a/webapp/src/styles/shell.css b/webapp/src/styles/shell.css index 6537920..670b2e4 100644 --- a/webapp/src/styles/shell.css +++ b/webapp/src/styles/shell.css @@ -184,7 +184,6 @@ .route-stage { @apply h-full min-h-0 overflow-auto; - animation: route-stage-in 220ms var(--ease-out-expo) both; } .mobile-sidebar-mask {