refactored view dropdown
This commit is contained in:
parent
642374c2e5
commit
9ae9c7c81a
|
@ -23,6 +23,7 @@ import { useCollections } from "@/hooks/store/collections";
|
||||||
import { useUser } from "@/hooks/store/user";
|
import { useUser } from "@/hooks/store/user";
|
||||||
import { useGetLink, useLinks } from "@/hooks/store/links";
|
import { useGetLink, useLinks } from "@/hooks/store/links";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import useLocalSettingsStore from "@/store/localSettings";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
link: LinkIncludingShortenedCollectionAndTags;
|
link: LinkIncludingShortenedCollectionAndTags;
|
||||||
|
@ -41,6 +42,10 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
|
||||||
|
|
||||||
const { setSelectedLinks, selectedLinks } = useLinkStore();
|
const { setSelectedLinks, selectedLinks } = useLinkStore();
|
||||||
|
|
||||||
|
const {
|
||||||
|
settings: { show },
|
||||||
|
} = useLocalSettingsStore();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { data: links = [] },
|
data: { data: links = [] },
|
||||||
} = useLinks();
|
} = useLinks();
|
||||||
|
@ -166,11 +171,9 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
|
||||||
) : (
|
) : (
|
||||||
<div className="duration-100 h-40 bg-opacity-80 skeleton rounded-none"></div>
|
<div className="duration-100 h-40 bg-opacity-80 skeleton rounded-none"></div>
|
||||||
)}
|
)}
|
||||||
{link.type !== "image" && (
|
<div className="absolute top-0 left-0 right-0 bottom-0 rounded-t-2xl flex items-center justify-center shadow rounded-md">
|
||||||
<div className="absolute top-0 left-0 right-0 bottom-0 rounded-t-2xl flex items-center justify-center shadow rounded-md">
|
<LinkIcon link={link} />
|
||||||
<LinkIcon link={link} />
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<hr className="divider my-0 border-t border-neutral-content h-[1px]" />
|
<hr className="divider my-0 border-t border-neutral-content h-[1px]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -155,11 +155,9 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
|
||||||
) : link.preview === "unavailable" ? null : (
|
) : link.preview === "unavailable" ? null : (
|
||||||
<div className="duration-100 h-40 bg-opacity-80 skeleton rounded-none"></div>
|
<div className="duration-100 h-40 bg-opacity-80 skeleton rounded-none"></div>
|
||||||
)}
|
)}
|
||||||
{link.type !== "image" && (
|
<div className="absolute top-0 left-0 right-0 bottom-0 rounded-t-2xl flex items-center justify-center shadow rounded-md">
|
||||||
<div className="absolute top-0 left-0 right-0 bottom-0 rounded-t-2xl flex items-center justify-center shadow rounded-md">
|
<LinkIcon link={link} />
|
||||||
<LinkIcon link={link} />
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{link.preview !== "unavailable" && (
|
{link.preview !== "unavailable" && (
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { Dispatch, SetStateAction, useEffect } from "react";
|
import React, { Dispatch, SetStateAction, useEffect } from "react";
|
||||||
import useLocalSettingsStore from "@/store/localSettings";
|
import useLocalSettingsStore from "@/store/localSettings";
|
||||||
|
|
||||||
import { ViewMode } from "@/types/global";
|
import { ViewMode } from "@/types/global";
|
||||||
|
import { dropdownTriggerer } from "@/lib/client/utils";
|
||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
viewMode: ViewMode;
|
viewMode: ViewMode;
|
||||||
|
@ -9,53 +10,111 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ViewDropdown({ viewMode, setViewMode }: Props) {
|
export default function ViewDropdown({ viewMode, setViewMode }: Props) {
|
||||||
const { updateSettings } = useLocalSettingsStore();
|
const { settings, updateSettings } = useLocalSettingsStore((state) => state);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const onChangeViewMode = (
|
const onChangeViewMode = (
|
||||||
e: React.MouseEvent<HTMLButtonElement>,
|
e: React.MouseEvent<HTMLButtonElement>,
|
||||||
viewMode: ViewMode
|
mode: ViewMode
|
||||||
) => {
|
) => {
|
||||||
setViewMode(viewMode);
|
setViewMode(mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleShowSetting = (setting: keyof typeof settings.show) => {
|
||||||
|
const newShowSettings = {
|
||||||
|
...settings.show,
|
||||||
|
[setting]: !settings.show[setting],
|
||||||
|
};
|
||||||
|
updateSettings({ show: newShowSettings });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateSettings({ viewMode });
|
updateSettings({ viewMode });
|
||||||
}, [viewMode]);
|
}, [viewMode, updateSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-1 flex flex-row gap-1 border border-neutral-content rounded-[0.625rem]">
|
<div className="dropdown dropdown-bottom dropdown-end">
|
||||||
<button
|
<div
|
||||||
onClick={(e) => onChangeViewMode(e, ViewMode.Card)}
|
tabIndex={0}
|
||||||
className={`btn btn-square btn-sm btn-ghost ${
|
role="button"
|
||||||
viewMode == ViewMode.Card
|
onMouseDown={dropdownTriggerer}
|
||||||
? "bg-primary/20 hover:bg-primary/20"
|
className="btn btn-sm btn-square btn-ghost border-none"
|
||||||
: "hover:bg-neutral/20"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<i className="bi-grid w-4 h-4 text-neutral"></i>
|
{viewMode === ViewMode.Card ? (
|
||||||
</button>
|
<i className="bi-grid w-4 h-4 text-neutral"></i>
|
||||||
|
) : viewMode === ViewMode.Masonry ? (
|
||||||
<button
|
<i className="bi-columns-gap w-4 h-4 text-neutral"></i>
|
||||||
onClick={(e) => onChangeViewMode(e, ViewMode.Masonry)}
|
) : (
|
||||||
className={`btn btn-square btn-sm btn-ghost ${
|
<i className="bi-view-stacked w-4 h-4 text-neutral"></i>
|
||||||
viewMode == ViewMode.Masonry
|
)}
|
||||||
? "bg-primary/20 hover:bg-primary/20"
|
</div>
|
||||||
: "hover:bg-neutral/20"
|
<ul className="dropdown-content z-[30] menu shadow bg-base-200 min-w-52 border border-neutral-content rounded-xl mt-1">
|
||||||
}`}
|
<p className="mb-1 text-sm text-neutral">{t("view")}</p>
|
||||||
>
|
<div
|
||||||
<i className="bi bi-columns-gap w-4 h-4 text-neutral"></i>
|
className="p-1 flex w-full justify-between gap-1 border border-neutral-content rounded-[0.625rem]"
|
||||||
</button>
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
<button
|
>
|
||||||
onClick={(e) => onChangeViewMode(e, ViewMode.List)}
|
<button
|
||||||
className={`btn btn-square btn-sm btn-ghost ${
|
onClick={(e) => onChangeViewMode(e, ViewMode.Card)}
|
||||||
viewMode == ViewMode.List
|
className={`btn btn-square btn-sm btn-ghost ${
|
||||||
? "bg-primary/20 hover:bg-primary/20"
|
viewMode === ViewMode.Card
|
||||||
: "hover:bg-neutral/20"
|
? "bg-primary/20 hover:bg-primary/20"
|
||||||
}`}
|
: "hover:bg-neutral/20"
|
||||||
>
|
}`}
|
||||||
<i className="bi bi-view-stacked w-4 h-4 text-neutral"></i>
|
>
|
||||||
</button>
|
<i className="bi-grid text-lg text-neutral"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={(e) => onChangeViewMode(e, ViewMode.Masonry)}
|
||||||
|
className={`btn btn-square btn-sm btn-ghost ${
|
||||||
|
viewMode === ViewMode.Masonry
|
||||||
|
? "bg-primary/20 hover:bg-primary/20"
|
||||||
|
: "hover:bg-neutral/20"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<i className="bi-columns-gap text-lg text-neutral"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={(e) => onChangeViewMode(e, ViewMode.List)}
|
||||||
|
className={`btn btn-square btn-sm btn-ghost ${
|
||||||
|
viewMode === ViewMode.List
|
||||||
|
? "bg-primary/20 hover:bg-primary/20"
|
||||||
|
: "hover:bg-neutral/20"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<i className="bi-view-stacked text-lg text-neutral"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p className="my-1 text-sm text-neutral">{t("show")}</p>
|
||||||
|
{Object.entries(settings.show)
|
||||||
|
.filter((e) =>
|
||||||
|
settings.viewMode === ViewMode.List // Hide tags and image checkbox in list view
|
||||||
|
? e[0] !== "tags" && e[0] !== "image"
|
||||||
|
: settings.viewMode === ViewMode.Card // Hide tags checkbox in card view
|
||||||
|
? e[0] !== "tags"
|
||||||
|
: true
|
||||||
|
)
|
||||||
|
.map(([key, value]) => (
|
||||||
|
<li key={key}>
|
||||||
|
<label
|
||||||
|
className="label cursor-pointer flex justify-start"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="checkbox checkbox-primary"
|
||||||
|
checked={value}
|
||||||
|
onChange={() =>
|
||||||
|
toggleShowSetting(key as keyof typeof settings.show)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="label-text whitespace-nowrap">{t(key)}</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,8 @@ export const icons: ReadonlyArray<IconEntry> = iconData.map((entry) => ({
|
||||||
Icon: Icons[entry.pascal_name as keyof typeof Icons] as Icons.Icon,
|
Icon: Icons[entry.pascal_name as keyof typeof Icons] as Icons.Icon,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
// if (process.env.NODE_ENV === "development") {
|
||||||
console.log(`${icons.length} icons`);
|
// console.log(`${icons.length} icons`);
|
||||||
}
|
// }
|
||||||
|
|
||||||
export const iconCount = Intl.NumberFormat("en-US").format(icons.length * 6);
|
export const iconCount = Intl.NumberFormat("en-US").format(icons.length * 6);
|
||||||
|
|
|
@ -380,5 +380,10 @@
|
||||||
"duotone": "Duotone",
|
"duotone": "Duotone",
|
||||||
"light_icon": "Light",
|
"light_icon": "Light",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
"set_custom_icon": "Set Custom Icon"
|
"set_custom_icon": "Set Custom Icon",
|
||||||
|
"view": "View",
|
||||||
|
"show": "Show",
|
||||||
|
"image": "Image",
|
||||||
|
"icon": "Icon",
|
||||||
|
"date": "Date"
|
||||||
}
|
}
|
|
@ -2,14 +2,24 @@ import { Sort } from "@/types/global";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
type LocalSettings = {
|
type LocalSettings = {
|
||||||
theme?: string;
|
theme: string;
|
||||||
viewMode?: string;
|
viewMode: string;
|
||||||
|
show: {
|
||||||
|
link: boolean;
|
||||||
|
title: boolean;
|
||||||
|
description: boolean;
|
||||||
|
image: boolean;
|
||||||
|
tags: boolean;
|
||||||
|
icon: boolean;
|
||||||
|
collection: boolean;
|
||||||
|
date: boolean;
|
||||||
|
};
|
||||||
sortBy?: Sort;
|
sortBy?: Sort;
|
||||||
};
|
};
|
||||||
|
|
||||||
type LocalSettingsStore = {
|
type LocalSettingsStore = {
|
||||||
settings: LocalSettings;
|
settings: LocalSettings;
|
||||||
updateSettings: (settings: LocalSettings) => void;
|
updateSettings: (settings: Partial<LocalSettings>) => void;
|
||||||
setSettings: () => void;
|
setSettings: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,50 +27,84 @@ const useLocalSettingsStore = create<LocalSettingsStore>((set) => ({
|
||||||
settings: {
|
settings: {
|
||||||
theme: "",
|
theme: "",
|
||||||
viewMode: "",
|
viewMode: "",
|
||||||
|
show: {
|
||||||
|
link: true,
|
||||||
|
title: true,
|
||||||
|
description: true,
|
||||||
|
image: true,
|
||||||
|
tags: true,
|
||||||
|
icon: true,
|
||||||
|
collection: true,
|
||||||
|
date: true,
|
||||||
|
},
|
||||||
sortBy: Sort.DateNewestFirst,
|
sortBy: Sort.DateNewestFirst,
|
||||||
},
|
},
|
||||||
updateSettings: async (newSettings) => {
|
updateSettings: (newSettings) => {
|
||||||
if (
|
const { theme, viewMode, sortBy, show } = newSettings;
|
||||||
newSettings.theme !== undefined &&
|
|
||||||
newSettings.theme !== localStorage.getItem("theme")
|
|
||||||
) {
|
|
||||||
localStorage.setItem("theme", newSettings.theme);
|
|
||||||
|
|
||||||
const localTheme = localStorage.getItem("theme") || "";
|
if (theme !== undefined && theme !== localStorage.getItem("theme")) {
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
document.querySelector("html")?.setAttribute("data-theme", localTheme);
|
document.querySelector("html")?.setAttribute("data-theme", theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
newSettings.viewMode !== undefined &&
|
viewMode !== undefined &&
|
||||||
newSettings.viewMode !== localStorage.getItem("viewMode")
|
viewMode !== localStorage.getItem("viewMode")
|
||||||
) {
|
) {
|
||||||
localStorage.setItem("viewMode", newSettings.viewMode);
|
localStorage.setItem("viewMode", viewMode);
|
||||||
|
|
||||||
// const localTheme = localStorage.getItem("viewMode") || "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (sortBy !== undefined) {
|
||||||
newSettings.sortBy !== undefined &&
|
localStorage.setItem("sortBy", sortBy.toString());
|
||||||
newSettings.sortBy !== Number(localStorage.getItem("sortBy"))
|
|
||||||
) {
|
|
||||||
localStorage.setItem("sortBy", newSettings.sortBy.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set((state) => ({ settings: { ...state.settings, ...newSettings } }));
|
const currentShowString = localStorage.getItem("show");
|
||||||
},
|
const newShowString = show ? JSON.stringify(show) : currentShowString;
|
||||||
setSettings: async () => {
|
|
||||||
if (!localStorage.getItem("theme")) {
|
|
||||||
localStorage.setItem("theme", "dark");
|
|
||||||
}
|
|
||||||
|
|
||||||
const localTheme = localStorage.getItem("theme") || "";
|
if (newShowString !== currentShowString) {
|
||||||
|
localStorage.setItem("show", newShowString || "");
|
||||||
|
}
|
||||||
|
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
settings: { ...state.settings, theme: localTheme },
|
settings: {
|
||||||
|
...state.settings,
|
||||||
|
...newSettings,
|
||||||
|
show: show ? { ...state.settings.show, ...show } : state.settings.show,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
},
|
||||||
|
setSettings: () => {
|
||||||
|
const theme = localStorage.getItem("theme") || "dark";
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
|
||||||
document.querySelector("html")?.setAttribute("data-theme", localTheme);
|
const viewMode = localStorage.getItem("viewMode") || "card";
|
||||||
|
localStorage.setItem("viewMode", viewMode);
|
||||||
|
|
||||||
|
const storedShow = localStorage.getItem("show");
|
||||||
|
const show = storedShow
|
||||||
|
? JSON.parse(storedShow)
|
||||||
|
: {
|
||||||
|
link: true,
|
||||||
|
name: true,
|
||||||
|
description: true,
|
||||||
|
image: true,
|
||||||
|
tags: true,
|
||||||
|
icon: true,
|
||||||
|
collection: true,
|
||||||
|
date: true,
|
||||||
|
};
|
||||||
|
localStorage.setItem("show", JSON.stringify(show));
|
||||||
|
|
||||||
|
set({
|
||||||
|
settings: {
|
||||||
|
theme,
|
||||||
|
viewMode,
|
||||||
|
show,
|
||||||
|
sortBy: useLocalSettingsStore.getState().settings.sortBy,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector("html")?.setAttribute("data-theme", theme);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue