improve bookmarks page layout

This commit is contained in:
2025-05-08 17:36:26 +01:00
parent 9b47b806d5
commit 79a17a714e
2 changed files with 133 additions and 64 deletions

View File

@@ -34,7 +34,9 @@ interface BookmarkPageState {
bookmarkToBeDeleted: LinkBookmark | null bookmarkToBeDeleted: LinkBookmark | null
layoutMode: LayoutMode layoutMode: LayoutMode
activeDialog: ActiveDialog activeDialog: ActiveDialog
actionBarHeight: number
setActionBarHeight: (height: number) => void
setActiveDialog: (dialog: ActiveDialog) => void setActiveDialog: (dialog: ActiveDialog) => void
setBookmarkItemExpanded: (isExpanded: boolean) => void setBookmarkItemExpanded: (isExpanded: boolean) => void
setBookmarkPreviewOpened: (isOpened: boolean) => void setBookmarkPreviewOpened: (isOpened: boolean) => void
@@ -54,6 +56,11 @@ const useBookmarkPageStore = create<BookmarkPageState>()((set, get) => ({
bookmarkToBeDeleted: null, bookmarkToBeDeleted: null,
layoutMode: LAYOUT_MODE.popup, layoutMode: LAYOUT_MODE.popup,
activeDialog: ActiveDialog.None, activeDialog: ActiveDialog.None,
actionBarHeight: 0,
setActionBarHeight(height: number) {
set({ actionBarHeight: height })
},
setActiveDialog(dialog: ActiveDialog) { setActiveDialog(dialog: ActiveDialog) {
set({ activeDialog: dialog }) set({ activeDialog: dialog })
@@ -77,16 +84,21 @@ const useBookmarkPageStore = create<BookmarkPageState>()((set, get) => ({
reconcileSelection(bookmarks: LinkBookmark[]) { reconcileSelection(bookmarks: LinkBookmark[]) {
const { selectedBookmarkId, selectedBookmarkIndex } = get() const { selectedBookmarkId, selectedBookmarkIndex } = get()
const newIndex = bookmarks.findIndex((bookmark) => bookmark.id === selectedBookmarkId)
if (newIndex !== selectedBookmarkIndex) { if (!selectedBookmarkId) {
if (newIndex >= 0) { set({ selectedBookmarkId: bookmarks[selectedBookmarkIndex].id })
set({ selectedBookmarkIndex: newIndex }) } else {
} else if (selectedBookmarkIndex >= bookmarks.length - 1) { const newIndex = bookmarks.findIndex((bookmark) => bookmark.id === selectedBookmarkId)
set({ selectedBookmarkIndex: bookmarks.length - 1 }) if (newIndex !== selectedBookmarkIndex) {
} else if (selectedBookmarkIndex === 0) { if (newIndex >= 0) {
set({ selectedBookmarkIndex: 0 }) set({ selectedBookmarkIndex: newIndex })
} else { } else if (selectedBookmarkIndex >= bookmarks.length - 1) {
set({ selectedBookmarkIndex: selectedBookmarkIndex + 1 }) set({ selectedBookmarkIndex: bookmarks.length - 1 })
} else if (selectedBookmarkIndex === 0) {
set({ selectedBookmarkIndex: 0 })
} else {
set({ selectedBookmarkIndex: selectedBookmarkIndex + 1 })
}
} }
} }
}, },
@@ -121,15 +133,7 @@ function Page() {
return ( return (
<div className="relative"> <div className="relative">
<Main> <Main>
<div className="flex flex-col md:flex-row justify-center py-16 lg:py-32"> <BookmarkListPane />
<header className="mb-4 md:mb-0 md:mr-16 text-start">
<h1 className="font-bold text-start">
<span className="invisible md:hidden">&gt;&nbsp;</span>
YOUR BOOKMARKS
</h1>
</header>
<BookmarkListSection />
</div>
<BookmarkPreview /> <BookmarkPreview />
<ActionBar /> <ActionBar />
</Main> </Main>
@@ -145,8 +149,8 @@ function Main({ children }: React.PropsWithChildren) {
return ( return (
<main <main
className={clsx( className={clsx(
"px-4 lg:px-8 2xl:px-0 grid flex justify-center relative w-full", "grid flex justify-center relative w-full",
isPreviewOpened && layoutMode === LAYOUT_MODE.sideBySide ? "grid-cols-2" : "grid-cols-1", isPreviewOpened && layoutMode === LAYOUT_MODE.sideBySide ? "grid-cols-3" : "grid-cols-1",
)} )}
> >
{children} {children}
@@ -154,6 +158,28 @@ function Main({ children }: React.PropsWithChildren) {
) )
} }
function BookmarkListPane() {
const isBookmarkPreviewOpened = useBookmarkPageStore((state) => state.isBookmarkPreviewOpened)
return (
<div className={clsx("flex flex-col py-16", { "md:flex-row lg:py-32 justify-center ": !isBookmarkPreviewOpened })}>
<header className="mb-4 md:mb-0 md:mr-16 text-start">
<h1 className={clsx("font-bold text-start", { "mb-4": isBookmarkPreviewOpened })}>
<span
className={clsx("invisible", {
"md:hidden": !isBookmarkPreviewOpened,
})}
>
&nbsp;&gt;&nbsp;
</span>
YOUR BOOKMARKS
</h1>
</header>
<BookmarkListSection />
</div>
)
}
function PageDialog() { function PageDialog() {
const dialog = useBookmarkPageStore((state) => state.activeDialog) const dialog = useBookmarkPageStore((state) => state.activeDialog)
switch (dialog) { switch (dialog) {
@@ -385,42 +411,57 @@ function BookmarkList({ bookmarks }: { bookmarks: LinkBookmark[] }) {
reconcileSelection(bookmarks) reconcileSelection(bookmarks)
}, [bookmarks, reconcileSelection]) }, [bookmarks, reconcileSelection])
useEffect(() => { useMnemonics(
function onKeyDown(event: KeyboardEvent) { {
const state = useBookmarkPageStore.getState() j: selectNextItem,
ArrowDown: selectNextItem,
switch (event.key) { k: selectPrevItem,
case "ArrowDown": { ArrowUp: selectPrevItem,
const nextIndex = state.selectedBookmarkIndex + 1
if (nextIndex < bookmarks.length) { h: collapseItem,
state.selectBookmark(bookmarks[nextIndex], nextIndex) ArrowLeft: collapseItem,
}
break l: expandItem,
} ArrowRight: expandItem,
case "ArrowUp": {
const prevIndex = state.selectedBookmarkIndex - 1 Enter: () => {
if (prevIndex >= 0) { const state = useBookmarkPageStore.getState()
state.selectBookmark(bookmarks[prevIndex], prevIndex) expandItem()
} state.setBookmarkPreviewOpened(true)
break },
} },
case "ArrowLeft": {
state.setBookmarkItemExpanded(false) ignore: useCallback(() => useBookmarkPageStore.getState().activeDialog !== ActiveDialog.None, []),
break },
case "ArrowRight": )
state.setBookmarkItemExpanded(true)
break function selectPrevItem() {
default: const state = useBookmarkPageStore.getState()
break const prevIndex = state.selectedBookmarkIndex - 1
} if (prevIndex >= 0) {
state.selectBookmark(bookmarks[prevIndex], prevIndex)
} }
}
window.addEventListener("keydown", onKeyDown) function selectNextItem() {
const state = useBookmarkPageStore.getState()
return () => { const nextIndex = state.selectedBookmarkIndex + 1
window.removeEventListener("keydown", onKeyDown) if (nextIndex < bookmarks.length) {
state.selectBookmark(bookmarks[nextIndex], nextIndex)
} }
}, [bookmarks]) }
function expandItem() {
const state = useBookmarkPageStore.getState()
state.setBookmarkItemExpanded(true)
}
function collapseItem() {
const state = useBookmarkPageStore.getState()
state.setBookmarkItemExpanded(false)
state.setBookmarkPreviewOpened(false)
}
return ( return (
<div className="flex flex-col container max-w-2xl -mt-2"> <div className="flex flex-col container max-w-2xl -mt-2">
@@ -444,7 +485,7 @@ function BookmarkPreview() {
return ( return (
<div <div
className={clsx( className={clsx(
"h-screen flex justify-center items-center border-l border-stone-700 dark:border-stone-300 flex dark:bg-stone-900", "h-screen col-span-2 flex justify-center items-center border-l border-stone-700 dark:border-stone-300 flex dark:bg-stone-900",
{ {
"absolute inset-0 border-l-0": layoutMode === LAYOUT_MODE.popup, "absolute inset-0 border-l-0": layoutMode === LAYOUT_MODE.popup,
}, },
@@ -457,6 +498,7 @@ function BookmarkPreview() {
function BookmarkPreviewFrame() { function BookmarkPreviewFrame() {
const selectedBookmarkId = useBookmarkPageStore((state) => state.selectedBookmarkId) const selectedBookmarkId = useBookmarkPageStore((state) => state.selectedBookmarkId)
const actionBarHeight = useBookmarkPageStore((state) => state.actionBarHeight)
const { data, status } = useAuthenticatedQuery(["bookmarks", selectedBookmarkId], () => const { data, status } = useAuthenticatedQuery(["bookmarks", selectedBookmarkId], () =>
fetchApi(`/bookmark/${selectedBookmarkId}`, { fetchApi(`/bookmark/${selectedBookmarkId}`, {
headers: { headers: {
@@ -473,7 +515,13 @@ function BookmarkPreviewFrame() {
</p> </p>
) )
case "success": case "success":
return <iframe className="bg-stone-200 dark:bg-stone-900 w-full h-full pb-20" srcDoc={data} /> return (
<iframe
className="prose bg-stone-200 dark:bg-stone-900 w-full h-full"
style={{ paddingBottom: actionBarHeight }}
srcDoc={data}
/>
)
default: default:
return null return null
@@ -513,7 +561,6 @@ function BookmarkListItem({ bookmark, index }: { bookmark: LinkBookmark; index:
function expandOrOpenPreview() { function expandOrOpenPreview() {
setBookmarkItemExpanded(true) setBookmarkItemExpanded(true)
if (useBookmarkPageStore.getState().layoutMode === LAYOUT_MODE.sideBySide) { if (useBookmarkPageStore.getState().layoutMode === LAYOUT_MODE.sideBySide) {
console.log(useBookmarkPageStore.getState().layoutMode)
setBookmarkPreviewOpened(true) setBookmarkPreviewOpened(true)
} }
} }
@@ -628,6 +675,10 @@ function OpenBookmarkPreviewButton() {
function ActionBar() { function ActionBar() {
const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog) const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
const setActionBarHeight = useBookmarkPageStore((state) => state.setActionBarHeight)
const layoutMode = useBookmarkPageStore((state) => state.layoutMode)
const isBookmarkPreviewOpened = useBookmarkPageStore((state) => state.isBookmarkPreviewOpened)
const setBookmarkPreviewOpened = useBookmarkPageStore((state) => state.setBookmarkPreviewOpened)
useMnemonics( useMnemonics(
{ {
@@ -640,15 +691,32 @@ function ActionBar() {
setActiveDialog(ActiveDialog.AddBookmark) setActiveDialog(ActiveDialog.AddBookmark)
} }
function closePreview() {
setBookmarkPreviewOpened(false)
}
return ( return (
<div className="fixed z-10 bottom-0 left-0 right-0 bg-stone-200 dark:bg-stone-900 border-t-1 flex flex-row justify-center py-4 space-x-4"> <div
<Button onClick={addBookmark}> ref={(el) => {
<span className="underline">A</span>DD if (el) {
</Button> setActionBarHeight(el.clientHeight)
<Button> }
<span className="underline">S</span>EARCH }}
</Button> className="fixed z-10 bottom-0 left-0 right-0 bg-stone-200 dark:bg-stone-900 border-t-1 flex flex-row justify-center py-4 space-x-4"
<LogOutButton /> >
{layoutMode === LAYOUT_MODE.popup && isBookmarkPreviewOpened ? (
<Button onClick={closePreview}>CLOSE</Button>
) : (
<>
<Button onClick={addBookmark}>
<span className="underline">A</span>DD
</Button>
<Button>
<span className="underline">S</span>EARCH
</Button>
<LogOutButton />
</>
)}
</div> </div>
) )
} }

View File

@@ -6,6 +6,7 @@ function useMnemonics(
) { ) {
useEffect(() => { useEffect(() => {
function onKeyDown(event: KeyboardEvent) { function onKeyDown(event: KeyboardEvent) {
console.log(event.key)
if (!ignore(event)) { if (!ignore(event)) {
mnemonicMap[event.key]?.(event) mnemonicMap[event.key]?.(event)
} }