mirror of
https://github.com/karl0ss/homepage.git
synced 2025-04-29 12:03:41 +01:00
Enhancement: apply layout settings to bookmarks (#1902)
* Apply layout settings to bookmarks * merge columnMap const * Remove bookmarksOnTop setting --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
parent
4d55f1fba4
commit
9529553102
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -16,4 +16,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5,44 +5,64 @@ import { MdKeyboardArrowDown } from "react-icons/md";
|
|||||||
|
|
||||||
import ErrorBoundary from "components/errorboundry";
|
import ErrorBoundary from "components/errorboundry";
|
||||||
import List from "components/bookmarks/list";
|
import List from "components/bookmarks/list";
|
||||||
|
import ResolvedIcon from "components/resolvedicon";
|
||||||
|
|
||||||
export default function BookmarksGroup({ group, disableCollapse }) {
|
export default function BookmarksGroup({ bookmarks, layout, disableCollapse }) {
|
||||||
const panel = useRef();
|
const panel = useRef();
|
||||||
return (
|
return (
|
||||||
<div key={group.name} className="flex-1">
|
<div
|
||||||
<Disclosure defaultOpen>
|
key={bookmarks.name}
|
||||||
{({ open }) => (
|
className={classNames(
|
||||||
<>
|
layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/4 lg:basis-1/5 xl:basis-1/6",
|
||||||
<Disclosure.Button disabled={disableCollapse} className="flex w-full select-none items-center group">
|
layout?.header === false ? "flex-1 px-1 -my-1" : "flex-1 p-1"
|
||||||
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2>
|
)}
|
||||||
<MdKeyboardArrowDown className={classNames(
|
>
|
||||||
disableCollapse ? 'hidden' : '',
|
<Disclosure defaultOpen>
|
||||||
'transition-all opacity-0 group-hover:opacity-100 ml-auto text-theme-800 dark:text-theme-300 text-xl',
|
{({ open }) => (
|
||||||
open ? '' : 'rotate-180'
|
<>
|
||||||
)} />
|
{layout?.header !== false && (
|
||||||
</Disclosure.Button>
|
<Disclosure.Button disabled={disableCollapse} className="flex w-full select-none items-center group">
|
||||||
<Transition
|
{layout?.icon && (
|
||||||
// Otherwise the transition group does display: none and cancels animation
|
<div className="flex-shrink-0 mr-2 w-7 h-7">
|
||||||
className="!block"
|
<ResolvedIcon icon={layout.icon} />
|
||||||
unmount={false}
|
</div>
|
||||||
beforeLeave={() => {
|
)}
|
||||||
panel.current.style.height = `${panel.current.scrollHeight}px`;
|
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{bookmarks.name}</h2>
|
||||||
setTimeout(() => {panel.current.style.height = `0`}, 1);
|
<MdKeyboardArrowDown
|
||||||
}}
|
className={classNames(
|
||||||
beforeEnter={() => {
|
disableCollapse ? "hidden" : "",
|
||||||
panel.current.style.height = `0px`;
|
"transition-all opacity-0 group-hover:opacity-100 ml-auto text-theme-800 dark:text-theme-300 text-xl",
|
||||||
setTimeout(() => {panel.current.style.height = `${panel.current.scrollHeight}px`}, 1);
|
open ? "" : "rotate-180"
|
||||||
}}
|
)}
|
||||||
>
|
/>
|
||||||
<Disclosure.Panel className="transition-all overflow-hidden duration-300 ease-out" ref={panel} static>
|
</Disclosure.Button>
|
||||||
<ErrorBoundary>
|
)}
|
||||||
<List bookmarks={group.bookmarks} />
|
<Transition
|
||||||
</ErrorBoundary>
|
// Otherwise the transition group does display: none and cancels animation
|
||||||
</Disclosure.Panel>
|
className="!block"
|
||||||
</Transition>
|
unmount={false}
|
||||||
</>
|
beforeLeave={() => {
|
||||||
)}
|
panel.current.style.height = `${panel.current.scrollHeight}px`;
|
||||||
</Disclosure>
|
setTimeout(() => {
|
||||||
|
panel.current.style.height = `0`;
|
||||||
|
}, 1);
|
||||||
|
}}
|
||||||
|
beforeEnter={() => {
|
||||||
|
panel.current.style.height = `0px`;
|
||||||
|
setTimeout(() => {
|
||||||
|
panel.current.style.height = `${panel.current.scrollHeight}px`;
|
||||||
|
}, 1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Disclosure.Panel className="transition-all overflow-hidden duration-300 ease-out" ref={panel} static>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<List bookmarks={bookmarks.bookmarks} layout={layout} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</Disclosure.Panel>
|
||||||
|
</Transition>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Disclosure>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import { columnMap } from "../../utils/layout/columns";
|
||||||
|
|
||||||
import Item from "components/bookmarks/item";
|
import Item from "components/bookmarks/item";
|
||||||
|
|
||||||
export default function List({ bookmarks }) {
|
export default function List({ bookmarks, layout }) {
|
||||||
return (
|
return (
|
||||||
<ul className="mt-3 flex flex-col">
|
<ul
|
||||||
|
className={classNames(
|
||||||
|
layout?.style === "row" ? `grid ${columnMap[layout?.columns]} gap-x-2` : "flex flex-col",
|
||||||
|
"mt-3"
|
||||||
|
)}
|
||||||
|
>
|
||||||
{bookmarks.map((bookmark) => (
|
{bookmarks.map((bookmark) => (
|
||||||
<Item key={`${bookmark.name}-${bookmark.href}`} bookmark={bookmark} />
|
<Item key={`${bookmark.name}-${bookmark.href}`} bookmark={bookmark} />
|
||||||
))}
|
))}
|
||||||
|
@ -1,18 +1,8 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
import Item from "components/services/item";
|
import { columnMap } from "../../utils/layout/columns";
|
||||||
|
|
||||||
const columnMap = [
|
import Item from "components/services/item";
|
||||||
"grid-cols-1 md:grid-cols-1 lg:grid-cols-1",
|
|
||||||
"grid-cols-1 md:grid-cols-1 lg:grid-cols-1",
|
|
||||||
"grid-cols-1 md:grid-cols-2 lg:grid-cols-2",
|
|
||||||
"grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
|
|
||||||
"grid-cols-1 md:grid-cols-2 lg:grid-cols-4",
|
|
||||||
"grid-cols-1 md:grid-cols-2 lg:grid-cols-5",
|
|
||||||
"grid-cols-1 md:grid-cols-2 lg:grid-cols-6",
|
|
||||||
"grid-cols-1 md:grid-cols-2 lg:grid-cols-7",
|
|
||||||
"grid-cols-1 md:grid-cols-2 lg:grid-cols-8",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function List({ group, services, layout }) {
|
export default function List({ group, services, layout }) {
|
||||||
return (
|
return (
|
||||||
|
@ -209,7 +209,7 @@ function Home({ initialSettings }) {
|
|||||||
searchProvider = searchProviders[searchWidget.options?.provider];
|
searchProvider = searchProviders[searchWidget.options?.provider];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const headerStyle = initialSettings?.headerStyle || "underlined";
|
const headerStyle = settings?.headerStyle || "underlined";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleKeyDown(e) {
|
function handleKeyDown(e) {
|
||||||
@ -233,12 +233,12 @@ function Home({ initialSettings }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{initialSettings.title || "Homepage"}</title>
|
<title>{settings.title || "Homepage"}</title>
|
||||||
{initialSettings.base && <base href={initialSettings.base} />}
|
{settings.base && <base href={settings.base} />}
|
||||||
{initialSettings.favicon ? (
|
{settings.favicon ? (
|
||||||
<>
|
<>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href={initialSettings.favicon} />
|
<link rel="apple-touch-icon" sizes="180x180" href={settings.favicon} />
|
||||||
<link rel="icon" href={initialSettings.favicon} />
|
<link rel="icon" href={settings.favicon} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@ -248,11 +248,8 @@ function Home({ initialSettings }) {
|
|||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=4" />
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=4" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<meta
|
<meta name="msapplication-TileColor" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
|
||||||
name="msapplication-TileColor"
|
<meta name="theme-color" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
|
||||||
content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]}
|
|
||||||
/>
|
|
||||||
<meta name="theme-color" content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]} />
|
|
||||||
</Head>
|
</Head>
|
||||||
<div className="relative container m-auto flex flex-col justify-start z-10 h-full">
|
<div className="relative container m-auto flex flex-col justify-start z-10 h-full">
|
||||||
<QuickLaunch
|
<QuickLaunch
|
||||||
@ -267,7 +264,7 @@ function Home({ initialSettings }) {
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
"flex flex-row flex-wrap justify-between",
|
"flex flex-row flex-wrap justify-between",
|
||||||
headerStyles[headerStyle],
|
headerStyles[headerStyle],
|
||||||
initialSettings.cardBlur !== undefined && headerStyle === "boxed" && `backdrop-blur${initialSettings.cardBlur.length ? '-' : ""}${initialSettings.cardBlur}`
|
settings.cardBlur !== undefined && headerStyle === "boxed" && `backdrop-blur${settings.cardBlur.length ? '-' : ""}${settings.cardBlur}`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{widgets && (
|
{widgets && (
|
||||||
@ -275,7 +272,7 @@ function Home({ initialSettings }) {
|
|||||||
{widgets
|
{widgets
|
||||||
.filter((widget) => !rightAlignedWidgets.includes(widget.type))
|
.filter((widget) => !rightAlignedWidgets.includes(widget.type))
|
||||||
.map((widget, i) => (
|
.map((widget, i) => (
|
||||||
<Widget key={i} widget={widget} style={{ header: headerStyle, isRightAligned: false, cardBlur: initialSettings.cardBlur }} />
|
<Widget key={i} widget={widget} style={{ header: headerStyle, isRightAligned: false, cardBlur: settings.cardBlur }} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
@ -285,7 +282,7 @@ function Home({ initialSettings }) {
|
|||||||
{widgets
|
{widgets
|
||||||
.filter((widget) => rightAlignedWidgets.includes(widget.type))
|
.filter((widget) => rightAlignedWidgets.includes(widget.type))
|
||||||
.map((widget, i) => (
|
.map((widget, i) => (
|
||||||
<Widget key={i} widget={widget} style={{ header: headerStyle, isRightAligned: true, cardBlur: initialSettings.cardBlur }} />
|
<Widget key={i} widget={widget} style={{ header: headerStyle, isRightAligned: true, cardBlur: settings.cardBlur }} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@ -293,39 +290,42 @@ function Home({ initialSettings }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{services?.length > 0 && (
|
{services?.length > 0 && (
|
||||||
<div className="flex flex-wrap p-4 sm:p-8 sm:pt-4 items-start pb-2">
|
<div key="services" className="flex flex-wrap p-4 sm:p-8 sm:pt-4 items-start pb-2">
|
||||||
{services.map((group) => (
|
{services.map((group) => (
|
||||||
<ServicesGroup
|
<ServicesGroup
|
||||||
key={group.name}
|
key={group.name}
|
||||||
group={group.name}
|
group={group.name}
|
||||||
services={group}
|
services={group}
|
||||||
layout={initialSettings.layout?.[group.name]}
|
layout={settings.layout?.[group.name]}
|
||||||
fiveColumns={settings.fiveColumns}
|
fiveColumns={settings.fiveColumns}
|
||||||
disableCollapse={settings.disableCollapse} />
|
disableCollapse={settings.disableCollapse}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{bookmarks?.length > 0 && (
|
{bookmarks?.length > 0 && (
|
||||||
<div className={`grow flex flex-wrap pt-0 p-4 sm:p-8 gap-2 grid-cols-1 lg:grid-cols-2 lg:grid-cols-${Math.min(6, bookmarks.length)}`}>
|
<div key="bookmarks" className="flex flex-wrap p-4 sm:p-8 sm:pt-4 items-start pb-2">
|
||||||
{bookmarks.map((group) => (
|
{bookmarks.map((group) => (
|
||||||
<BookmarksGroup
|
<BookmarksGroup
|
||||||
key={group.name}
|
key={group.name}
|
||||||
group={group}
|
bookmarks={group}
|
||||||
disableCollapse={settings.disableCollapse} />
|
layout={settings.layout?.[group.name]}
|
||||||
|
disableCollapse={settings.disableCollapse}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col mt-auto p-8 w-full">
|
<div className="flex flex-col mt-auto p-8 w-full">
|
||||||
<div className="flex w-full justify-end">
|
<div className="flex w-full justify-end">
|
||||||
{!initialSettings?.color && <ColorToggle />}
|
{!settings?.color && <ColorToggle />}
|
||||||
<Revalidate />
|
<Revalidate />
|
||||||
{!initialSettings?.theme && <ThemeToggle />}
|
{!settings.theme && <ThemeToggle />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex mt-4 w-full justify-end">
|
<div className="flex mt-4 w-full justify-end">
|
||||||
{!initialSettings?.hideVersion && <Version />}
|
{!settings.hideVersion && <Version />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,6 +34,16 @@ export async function bookmarksResponse() {
|
|||||||
|
|
||||||
if (!bookmarks) return [];
|
if (!bookmarks) return [];
|
||||||
|
|
||||||
|
let initialSettings;
|
||||||
|
|
||||||
|
try {
|
||||||
|
initialSettings = await getSettings();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to load settings.yaml, please check for errors");
|
||||||
|
if (e) console.error(e.toString());
|
||||||
|
initialSettings = {};
|
||||||
|
}
|
||||||
|
|
||||||
// map easy to write YAML objects into easy to consume JS arrays
|
// map easy to write YAML objects into easy to consume JS arrays
|
||||||
const bookmarksArray = bookmarks.map((group) => ({
|
const bookmarksArray = bookmarks.map((group) => ({
|
||||||
name: Object.keys(group)[0],
|
name: Object.keys(group)[0],
|
||||||
@ -43,7 +53,21 @@ export async function bookmarksResponse() {
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return bookmarksArray;
|
const sortedGroups = [];
|
||||||
|
const unsortedGroups = [];
|
||||||
|
const definedLayouts = initialSettings.layout ? Object.keys(initialSettings.layout) : null;
|
||||||
|
|
||||||
|
bookmarksArray.forEach((group) => {
|
||||||
|
if (definedLayouts) {
|
||||||
|
const layoutIndex = definedLayouts.findIndex(layout => layout === group.name);
|
||||||
|
if (layoutIndex > -1) sortedGroups[layoutIndex] = group;
|
||||||
|
else unsortedGroups.push(group);
|
||||||
|
} else {
|
||||||
|
unsortedGroups.push(group);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...sortedGroups.filter(g => g), ...unsortedGroups];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function widgetsResponse() {
|
export async function widgetsResponse() {
|
||||||
|
12
src/utils/layout/columns.js
Normal file
12
src/utils/layout/columns.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const columnMap = [
|
||||||
|
"grid-cols-1 md:grid-cols-1 lg:grid-cols-1",
|
||||||
|
"grid-cols-1 md:grid-cols-1 lg:grid-cols-1",
|
||||||
|
"grid-cols-1 md:grid-cols-2 lg:grid-cols-2",
|
||||||
|
"grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
|
||||||
|
"grid-cols-1 md:grid-cols-2 lg:grid-cols-4",
|
||||||
|
"grid-cols-1 md:grid-cols-2 lg:grid-cols-5",
|
||||||
|
"grid-cols-1 md:grid-cols-2 lg:grid-cols-6",
|
||||||
|
"grid-cols-1 md:grid-cols-2 lg:grid-cols-7",
|
||||||
|
"grid-cols-1 md:grid-cols-2 lg:grid-cols-8",
|
||||||
|
];
|
@ -59,5 +59,16 @@ module.exports = {
|
|||||||
'backdrop-brightness-125',
|
'backdrop-brightness-125',
|
||||||
'backdrop-brightness-150',
|
'backdrop-brightness-150',
|
||||||
'backdrop-brightness-200',
|
'backdrop-brightness-200',
|
||||||
|
'grid-cols-1',
|
||||||
|
'md:grid-cols-1',
|
||||||
|
'md:grid-cols-2',
|
||||||
|
'lg:grid-cols-1',
|
||||||
|
'lg:grid-cols-2',
|
||||||
|
'lg:grid-cols-3',
|
||||||
|
'lg:grid-cols-4',
|
||||||
|
'lg:grid-cols-5',
|
||||||
|
'lg:grid-cols-6',
|
||||||
|
'lg:grid-cols-7',
|
||||||
|
'lg:grid-cols-8',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user