Skip to content

Commit 10093d5

Browse files
committed
feat(web): add sticky bottom action bar for mobile entity detail pages
Add a fixed bottom action bar for mobile viewports with quick access to: - Add to catalogue list - Toggle bookmark - Toggle query bookmark (when applicable) - Rich/Raw view mode toggle This improves mobile UX by making actions always accessible without needing to open the collapsible menu.
1 parent ef12dc8 commit 10093d5

File tree

1 file changed

+82
-1
lines changed

1 file changed

+82
-1
lines changed

apps/web/src/components/entity-detail/EntityDetailLayout.tsx

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { EntityType } from "@bibgraph/types";
22
import { logger } from "@bibgraph/utils";
3-
import { ActionIcon, Badge, Box, Code, Group, Modal, Paper, SegmentedControl, Stack, Text, Title, Tooltip } from "@mantine/core";
3+
import { ActionIcon, Affix, Badge, Box, Code, Group, Modal, Paper, SegmentedControl, Stack, Text, Title, Tooltip } from "@mantine/core";
44
import { IconBookmark, IconBookmarkFilled, IconBookmarkOff, IconCode, IconListCheck, IconMenu2, IconX } from "@tabler/icons-react";
55
import React, { ReactNode, useState } from "react";
66

@@ -93,6 +93,7 @@ export const EntityDetailLayout = ({
9393
return (
9494
<Box
9595
p={isMobile() ? "sm" : "xl"}
96+
pb={isMobile() ? 80 : undefined}
9697
bg="var(--mantine-color-body)"
9798
style={{ minHeight: '100%' }}
9899
data-testid="entity-detail-layout"
@@ -387,6 +388,86 @@ export const EntityDetailLayout = ({
387388
}}
388389
/>
389390
</Modal>
391+
392+
{/* Mobile Sticky Bottom Action Bar */}
393+
{isMobile() && (
394+
<Affix position={{ bottom: 0, left: 0, right: 0 }}>
395+
<Paper
396+
p="sm"
397+
shadow="lg"
398+
style={{
399+
borderTop: '1px solid var(--mantine-color-gray-3)',
400+
borderRadius: 0,
401+
backgroundColor: 'var(--mantine-color-body)',
402+
}}
403+
>
404+
<Group justify="space-around" gap="xs">
405+
<Tooltip label="Add to list" position="top">
406+
<ActionIcon
407+
size="lg"
408+
variant="light"
409+
color="green"
410+
onClick={() => setShowAddToListModal(true)}
411+
aria-label="Add to catalogue list"
412+
>
413+
<IconListCheck size={ICON_SIZE.LG} />
414+
</ActionIcon>
415+
</Tooltip>
416+
417+
<Tooltip
418+
label={userInteractions.isBookmarked ? "Remove bookmark" : "Bookmark"}
419+
position="top"
420+
>
421+
<ActionIcon
422+
size="lg"
423+
variant={userInteractions.isBookmarked ? "filled" : "light"}
424+
color={userInteractions.isBookmarked ? "yellow" : "gray"}
425+
onClick={handleBookmarkToggle}
426+
loading={userInteractions.isLoadingBookmarks}
427+
aria-label={userInteractions.isBookmarked ? "Remove bookmark" : "Add bookmark"}
428+
>
429+
{userInteractions.isBookmarked ? (
430+
<IconBookmark size={ICON_SIZE.LG} fill="currentColor" />
431+
) : (
432+
<IconBookmarkOff size={ICON_SIZE.LG} />
433+
)}
434+
</ActionIcon>
435+
</Tooltip>
436+
437+
{(selectParam || Object.keys(queryBookmarking.currentQueryParams).length > 0) && (
438+
<Tooltip
439+
label={queryBookmarking.isQueryBookmarked ? "Remove query bookmark" : "Bookmark query"}
440+
position="top"
441+
>
442+
<ActionIcon
443+
size="lg"
444+
variant={queryBookmarking.isQueryBookmarked ? "filled" : "light"}
445+
color={queryBookmarking.isQueryBookmarked ? "blue" : "gray"}
446+
onClick={handleQueryBookmarkToggle}
447+
aria-label={queryBookmarking.isQueryBookmarked ? "Remove query bookmark" : "Add query bookmark"}
448+
>
449+
{queryBookmarking.isQueryBookmarked ? (
450+
<IconBookmarkFilled size={ICON_SIZE.LG} />
451+
) : (
452+
<IconBookmark size={ICON_SIZE.LG} />
453+
)}
454+
</ActionIcon>
455+
</Tooltip>
456+
)}
457+
458+
<SegmentedControl
459+
size="xs"
460+
value={viewMode}
461+
onChange={(value) => onViewModeChange(value as DetailViewMode)}
462+
data={[
463+
{ label: 'Rich', value: 'rich' },
464+
{ label: 'Raw', value: 'raw' },
465+
]}
466+
/>
467+
</Group>
468+
</Paper>
469+
</Affix>
470+
)}
390471
</Box>
391472
);
392473
};

0 commit comments

Comments
 (0)