Przeglądaj źródła

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>
Georges-Antoine Assi 1 rok temu
rodzic
commit
9529553102

+ 1 - 1
.vscode/launch.json

@@ -16,4 +16,4 @@
       }
     }
   ]
-}
+}

+ 55 - 35
src/components/bookmarks/group.jsx

@@ -5,44 +5,64 @@ import { MdKeyboardArrowDown } from "react-icons/md";
 
 import ErrorBoundary from "components/errorboundry";
 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();
   return (
-    <div key={group.name} className="flex-1">
-    <Disclosure defaultOpen>
-    {({ open }) => (
-      <>
-        <Disclosure.Button disabled={disableCollapse} className="flex w-full select-none items-center group">
-          <h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2>
-          <MdKeyboardArrowDown className={classNames(
-            disableCollapse ? 'hidden' : '',
-            'transition-all opacity-0 group-hover:opacity-100 ml-auto text-theme-800 dark:text-theme-300 text-xl',
-            open ? '' : 'rotate-180'
-            )} />
-        </Disclosure.Button>
-        <Transition
-          // Otherwise the transition group does display: none and cancels animation
-          className="!block"
-          unmount={false}
-          beforeLeave={() => {
-            panel.current.style.height = `${panel.current.scrollHeight}px`;
-            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={group.bookmarks} />
-              </ErrorBoundary>
-            </Disclosure.Panel>
-        </Transition>
-      </>
-    )}
-    </Disclosure>
+    <div
+      key={bookmarks.name}
+      className={classNames(
+        layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/4 lg:basis-1/5 xl:basis-1/6",
+        layout?.header === false ? "flex-1 px-1 -my-1" : "flex-1 p-1"
+      )}
+    >
+      <Disclosure defaultOpen>
+        {({ open }) => (
+          <>
+            {layout?.header !== false && (
+              <Disclosure.Button disabled={disableCollapse} className="flex w-full select-none items-center group">
+                {layout?.icon && (
+                  <div className="flex-shrink-0 mr-2 w-7 h-7">
+                    <ResolvedIcon icon={layout.icon} />
+                  </div>
+                )}
+                <h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{bookmarks.name}</h2>
+                <MdKeyboardArrowDown
+                  className={classNames(
+                    disableCollapse ? "hidden" : "",
+                    "transition-all opacity-0 group-hover:opacity-100 ml-auto text-theme-800 dark:text-theme-300 text-xl",
+                    open ? "" : "rotate-180"
+                  )}
+                />
+              </Disclosure.Button>
+            )}
+            <Transition
+              // Otherwise the transition group does display: none and cancels animation
+              className="!block"
+              unmount={false}
+              beforeLeave={() => {
+                panel.current.style.height = `${panel.current.scrollHeight}px`;
+                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>
   );
 }

+ 11 - 2
src/components/bookmarks/list.jsx

@@ -1,8 +1,17 @@
+import classNames from "classnames";
+
+import { columnMap } from "../../utils/layout/columns";
+
 import Item from "components/bookmarks/item";
 
-export default function List({ bookmarks }) {
+export default function List({ bookmarks, layout }) {
   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) => (
         <Item key={`${bookmark.name}-${bookmark.href}`} bookmark={bookmark} />
       ))}

+ 2 - 12
src/components/services/list.jsx

@@ -1,18 +1,8 @@
 import classNames from "classnames";
 
-import Item from "components/services/item";
+import { columnMap } from "../../utils/layout/columns";
 
-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",
-];
+import Item from "components/services/item";
 
 export default function List({ group, services, layout }) {
   return (

+ 25 - 25
src/pages/index.jsx

@@ -209,7 +209,7 @@ function Home({ initialSettings }) {
       searchProvider = searchProviders[searchWidget.options?.provider];
     }
   }
-  const headerStyle = initialSettings?.headerStyle || "underlined";
+  const headerStyle = settings?.headerStyle || "underlined";
 
   useEffect(() => {
     function handleKeyDown(e) {
@@ -233,12 +233,12 @@ function Home({ initialSettings }) {
   return (
     <>
       <Head>
-        <title>{initialSettings.title || "Homepage"}</title>
-        {initialSettings.base && <base href={initialSettings.base} />}
-        {initialSettings.favicon ? (
+        <title>{settings.title || "Homepage"}</title>
+        {settings.base && <base href={settings.base} />}
+        {settings.favicon ? (
           <>
-            <link rel="apple-touch-icon" sizes="180x180" href={initialSettings.favicon} />
-            <link rel="icon" href={initialSettings.favicon} />
+            <link rel="apple-touch-icon" sizes="180x180" href={settings.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" />
           </>
         )}
-        <meta
-          name="msapplication-TileColor"
-          content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]}
-        />
-        <meta name="theme-color" content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]} />
+        <meta name="msapplication-TileColor" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
+        <meta name="theme-color" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
       </Head>
       <div className="relative container m-auto flex flex-col justify-start z-10 h-full">
         <QuickLaunch
@@ -267,7 +264,7 @@ function Home({ initialSettings }) {
           className={classNames(
             "flex flex-row flex-wrap justify-between",
             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 && (
@@ -275,7 +272,7 @@ function Home({ initialSettings }) {
               {widgets
                 .filter((widget) => !rightAlignedWidgets.includes(widget.type))
                 .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(
@@ -285,7 +282,7 @@ function Home({ initialSettings }) {
                 {widgets
                   .filter((widget) => rightAlignedWidgets.includes(widget.type))
                   .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>
             </>
@@ -293,39 +290,42 @@ function Home({ initialSettings }) {
         </div>
 
         {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) => (
-              <ServicesGroup 
+              <ServicesGroup
                 key={group.name}
                 group={group.name}
                 services={group}
-                layout={initialSettings.layout?.[group.name]}
-                fiveColumns={settings.fiveColumns} 
-                disableCollapse={settings.disableCollapse} />
+                layout={settings.layout?.[group.name]}
+                fiveColumns={settings.fiveColumns}
+                disableCollapse={settings.disableCollapse}
+              />
             ))}
           </div>
         )}
 
         {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) => (
               <BookmarksGroup
                 key={group.name}
-                group={group}
-                disableCollapse={settings.disableCollapse} />
+                bookmarks={group}
+                layout={settings.layout?.[group.name]}
+                disableCollapse={settings.disableCollapse}
+              />
             ))}
           </div>
         )}
 
         <div className="flex flex-col mt-auto p-8 w-full">
           <div className="flex w-full justify-end">
-            {!initialSettings?.color && <ColorToggle />}
+            {!settings?.color && <ColorToggle />}
             <Revalidate />
-            {!initialSettings?.theme && <ThemeToggle />}
+            {!settings.theme && <ThemeToggle />}
           </div>
 
           <div className="flex mt-4 w-full justify-end">
-            {!initialSettings?.hideVersion && <Version />}
+            {!settings.hideVersion && <Version />}
           </div>
         </div>
       </div>

+ 25 - 1
src/utils/config/api-response.js

@@ -34,6 +34,16 @@ export async function bookmarksResponse() {
 
   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
   const bookmarksArray = bookmarks.map((group) => ({
     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() {

+ 12 - 0
src/utils/layout/columns.js

@@ -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",
+  ];

+ 11 - 0
tailwind.config.js

@@ -59,5 +59,16 @@ module.exports = {
     'backdrop-brightness-125',
     'backdrop-brightness-150',
     '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',
   ],
 }