diff --git a/.github/pr-assets/bookmarks/after.png b/.github/pr-assets/bookmarks/after.png new file mode 100644 index 000000000..27d07abb4 Binary files /dev/null and b/.github/pr-assets/bookmarks/after.png differ diff --git a/.github/pr-assets/bookmarks/before.png b/.github/pr-assets/bookmarks/before.png new file mode 100644 index 000000000..06ab10a74 Binary files /dev/null and b/.github/pr-assets/bookmarks/before.png differ diff --git a/apps/docs/docs/widgets/bookmarks/index.mdx b/apps/docs/docs/widgets/bookmarks/index.mdx index d712c4a9d..9c22f1bae 100644 --- a/apps/docs/docs/widgets/bookmarks/index.mdx +++ b/apps/docs/docs/widgets/bookmarks/index.mdx @@ -18,7 +18,7 @@ Paste a URL and press Enter, paste several comma-separated or one-per-line URLs, URLs do not need an App record; Homarr uses the site's favicon and falls back to a link icon when it cannot load one. Bookmarks selected from Apps continue to use the App name, URL, icon, and description. -**Adaptive** layout is the practical default: it changes between icons, compact rows, and tiles as the widget changes shape. Every compact item exposes its destination on hover, while advanced view expands items into descriptive cards. +**Adaptive** layout is the practical default: it changes between icons, compact rows, and tiles as the widget changes shape. **Compact grid** keeps every card to a short row. Enable **Expand cards to fill row** when cards in an incomplete final row should use the remaining width. Every compact item exposes its destination on hover, while advanced view expands items into descriptive cards. ### Screenshots diff --git a/apps/docs/docs/widgets/bookmarks/index.ts b/apps/docs/docs/widgets/bookmarks/index.ts index b1a766ade..b335a70fd 100644 --- a/apps/docs/docs/widgets/bookmarks/index.ts +++ b/apps/docs/docs/widgets/bookmarks/index.ts @@ -56,6 +56,12 @@ export const bookmarksWidget: WidgetDefinition = { values: { type: "boolean" }, defaultValue: "yes", }, + { + name: "Expand cards to fill row", + description: "Whether cards in an incomplete final row expand to use the remaining width.", + values: { type: "boolean" }, + defaultValue: "no", + }, { name: "Bookmarks", description: "URLs and existing Apps shown by the widget.", diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 04dabe9f7..8a37b40d7 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -2272,6 +2272,9 @@ "withBorder": { "label": "Show border" }, + "grow": { + "label": "Expand cards to fill row" + }, "customUrls": { "label": "Custom links" }, diff --git a/packages/widgets/src/bookmarks/bookmarks-widget.tsx b/packages/widgets/src/bookmarks/bookmarks-widget.tsx index cc73a445a..5838af748 100644 --- a/packages/widgets/src/bookmarks/bookmarks-widget.tsx +++ b/packages/widgets/src/bookmarks/bookmarks-widget.tsx @@ -6,6 +6,7 @@ import { Box, Card, Center, + Grid, Group, ScrollArea, SimpleGrid, @@ -156,6 +157,14 @@ export default function BookmarksWidget({ {cards} + ) : options.grow ? ( + + {cards.map((card) => ( + + {card} + + ))} + ) : ( {cards} diff --git a/packages/widgets/src/bookmarks/index.tsx b/packages/widgets/src/bookmarks/index.tsx index 82d171e9d..1fefa08c1 100644 --- a/packages/widgets/src/bookmarks/index.tsx +++ b/packages/widgets/src/bookmarks/index.tsx @@ -49,6 +49,7 @@ export const { definition, componentLoader } = createWidgetDefinition("bookmarks hideHostname: factory.switch({ defaultValue: false }), openNewTab: factory.switch({ defaultValue: true }), withBorder: factory.switch({ defaultValue: false }), + grow: factory.switch({ defaultValue: false }), customUrls: factory.internal({ defaultValue: [] as string[] }), items: factory.sortableItemList({ ItemComponent: ({ item, handle, removeItem, removeLabel, rootAttributes }) => { diff --git a/packages/widgets/src/bookmarks/layout.ts b/packages/widgets/src/bookmarks/layout.ts index ada81edef..0c0d7b32e 100644 --- a/packages/widgets/src/bookmarks/layout.ts +++ b/packages/widgets/src/bookmarks/layout.ts @@ -240,8 +240,20 @@ export const getBookmarkDisplayPlan = ({ }; } - let columns: number = widthSettings.compactColumns; - let orientation: BookmarkOrientation = "horizontal"; + if (layout === "gridHorizontal") { + return { + columns: Math.min(count, widthSettings.compactColumns), + horizontalScroll: false, + itemHeight: 56, + itemWidth: widthSettings.rowItemWidth, + orientation: "horizontal", + showHostname: widthSettings.showHostname && heightSettings.showHostname, + showTitle: widthSettings.showTitle && heightSettings.showTitle, + }; + } + + let columns: number = widthSettings.adaptiveColumns; + let orientation: BookmarkOrientation = widthSettings.adaptiveOrientation; if (layout === "grid") { columns = widthSettings.gridColumns; orientation = "vertical";