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 { useGetLink, useLinks } from "@/hooks/store/links";
|
||||
import { useRouter } from "next/router";
|
||||
import useLocalSettingsStore from "@/store/localSettings";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
|
@ -41,6 +42,10 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
|
|||
|
||||
const { setSelectedLinks, selectedLinks } = useLinkStore();
|
||||
|
||||
const {
|
||||
settings: { show },
|
||||
} = useLocalSettingsStore();
|
||||
|
||||
const {
|
||||
data: { data: links = [] },
|
||||
} = 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>
|
||||
)}
|
||||
{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">
|
||||
<LinkIcon link={link} />
|
||||
</div>
|
||||
)}
|
||||
<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} />
|
||||
</div>
|
||||
</div>
|
||||
<hr className="divider my-0 border-t border-neutral-content h-[1px]" />
|
||||
</div>
|
||||
|
|
|
@ -155,11 +155,9 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
|
|||
) : link.preview === "unavailable" ? null : (
|
||||
<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">
|
||||
<LinkIcon link={link} />
|
||||
</div>
|
||||
)}
|
||||
<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} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{link.preview !== "unavailable" && (
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, { Dispatch, SetStateAction, useEffect } from "react";
|
||||
import useLocalSettingsStore from "@/store/localSettings";
|
||||
|
||||
import { ViewMode } from "@/types/global";
|
||||
import { dropdownTriggerer } from "@/lib/client/utils";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
type Props = {
|
||||
viewMode: ViewMode;
|
||||
|
@ -9,53 +10,111 @@ type Props = {
|
|||
};
|
||||
|
||||
export default function ViewDropdown({ viewMode, setViewMode }: Props) {
|
||||
const { updateSettings } = useLocalSettingsStore();
|
||||
const { settings, updateSettings } = useLocalSettingsStore((state) => state);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onChangeViewMode = (
|
||||
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(() => {
|
||||
updateSettings({ viewMode });
|
||||
}, [viewMode]);
|
||||
}, [viewMode, updateSettings]);
|
||||
|
||||
return (
|
||||
<div className="p-1 flex flex-row gap-1 border border-neutral-content rounded-[0.625rem]">
|
||||
<button
|
||||
onClick={(e) => onChangeViewMode(e, ViewMode.Card)}
|
||||
className={`btn btn-square btn-sm btn-ghost ${
|
||||
viewMode == ViewMode.Card
|
||||
? "bg-primary/20 hover:bg-primary/20"
|
||||
: "hover:bg-neutral/20"
|
||||
}`}
|
||||
<div className="dropdown dropdown-bottom dropdown-end">
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onMouseDown={dropdownTriggerer}
|
||||
className="btn btn-sm btn-square btn-ghost border-none"
|
||||
>
|
||||
<i className="bi-grid w-4 h-4 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 bi-columns-gap w-4 h-4 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 bi-view-stacked w-4 h-4 text-neutral"></i>
|
||||
</button>
|
||||
{viewMode === ViewMode.Card ? (
|
||||
<i className="bi-grid w-4 h-4 text-neutral"></i>
|
||||
) : viewMode === ViewMode.Masonry ? (
|
||||
<i className="bi-columns-gap w-4 h-4 text-neutral"></i>
|
||||
) : (
|
||||
<i className="bi-view-stacked w-4 h-4 text-neutral"></i>
|
||||
)}
|
||||
</div>
|
||||
<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
|
||||
className="p-1 flex w-full justify-between gap-1 border border-neutral-content rounded-[0.625rem]"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
>
|
||||
<button
|
||||
onClick={(e) => onChangeViewMode(e, ViewMode.Card)}
|
||||
className={`btn btn-square btn-sm btn-ghost ${
|
||||
viewMode === ViewMode.Card
|
||||
? "bg-primary/20 hover:bg-primary/20"
|
||||
: "hover:bg-neutral/20"
|
||||
}`}
|
||||
>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ export const icons: ReadonlyArray<IconEntry> = iconData.map((entry) => ({
|
|||
Icon: Icons[entry.pascal_name as keyof typeof Icons] as Icons.Icon,
|
||||
}));
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log(`${icons.length} icons`);
|
||||
}
|
||||
// if (process.env.NODE_ENV === "development") {
|
||||
// console.log(`${icons.length} icons`);
|
||||
// }
|
||||
|
||||
export const iconCount = Intl.NumberFormat("en-US").format(icons.length * 6);
|
||||
|
|
|
@ -380,5 +380,10 @@
|
|||
"duotone": "Duotone",
|
||||
"light_icon": "Light",
|
||||
"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";
|
||||
|
||||
type LocalSettings = {
|
||||
theme?: string;
|
||||
viewMode?: string;
|
||||
theme: string;
|
||||
viewMode: string;
|
||||
show: {
|
||||
link: boolean;
|
||||
title: boolean;
|
||||
description: boolean;
|
||||
image: boolean;
|
||||
tags: boolean;
|
||||
icon: boolean;
|
||||
collection: boolean;
|
||||
date: boolean;
|
||||
};
|
||||
sortBy?: Sort;
|
||||
};
|
||||
|
||||
type LocalSettingsStore = {
|
||||
settings: LocalSettings;
|
||||
updateSettings: (settings: LocalSettings) => void;
|
||||
updateSettings: (settings: Partial<LocalSettings>) => void;
|
||||
setSettings: () => void;
|
||||
};
|
||||
|
||||
|
@ -17,50 +27,84 @@ const useLocalSettingsStore = create<LocalSettingsStore>((set) => ({
|
|||
settings: {
|
||||
theme: "",
|
||||
viewMode: "",
|
||||
show: {
|
||||
link: true,
|
||||
title: true,
|
||||
description: true,
|
||||
image: true,
|
||||
tags: true,
|
||||
icon: true,
|
||||
collection: true,
|
||||
date: true,
|
||||
},
|
||||
sortBy: Sort.DateNewestFirst,
|
||||
},
|
||||
updateSettings: async (newSettings) => {
|
||||
if (
|
||||
newSettings.theme !== undefined &&
|
||||
newSettings.theme !== localStorage.getItem("theme")
|
||||
) {
|
||||
localStorage.setItem("theme", newSettings.theme);
|
||||
updateSettings: (newSettings) => {
|
||||
const { theme, viewMode, sortBy, show } = newSettings;
|
||||
|
||||
const localTheme = localStorage.getItem("theme") || "";
|
||||
|
||||
document.querySelector("html")?.setAttribute("data-theme", localTheme);
|
||||
if (theme !== undefined && theme !== localStorage.getItem("theme")) {
|
||||
localStorage.setItem("theme", theme);
|
||||
document.querySelector("html")?.setAttribute("data-theme", theme);
|
||||
}
|
||||
|
||||
if (
|
||||
newSettings.viewMode !== undefined &&
|
||||
newSettings.viewMode !== localStorage.getItem("viewMode")
|
||||
viewMode !== undefined &&
|
||||
viewMode !== localStorage.getItem("viewMode")
|
||||
) {
|
||||
localStorage.setItem("viewMode", newSettings.viewMode);
|
||||
|
||||
// const localTheme = localStorage.getItem("viewMode") || "";
|
||||
localStorage.setItem("viewMode", viewMode);
|
||||
}
|
||||
|
||||
if (
|
||||
newSettings.sortBy !== undefined &&
|
||||
newSettings.sortBy !== Number(localStorage.getItem("sortBy"))
|
||||
) {
|
||||
localStorage.setItem("sortBy", newSettings.sortBy.toString());
|
||||
if (sortBy !== undefined) {
|
||||
localStorage.setItem("sortBy", sortBy.toString());
|
||||
}
|
||||
|
||||
set((state) => ({ settings: { ...state.settings, ...newSettings } }));
|
||||
},
|
||||
setSettings: async () => {
|
||||
if (!localStorage.getItem("theme")) {
|
||||
localStorage.setItem("theme", "dark");
|
||||
}
|
||||
const currentShowString = localStorage.getItem("show");
|
||||
const newShowString = show ? JSON.stringify(show) : currentShowString;
|
||||
|
||||
const localTheme = localStorage.getItem("theme") || "";
|
||||
if (newShowString !== currentShowString) {
|
||||
localStorage.setItem("show", newShowString || "");
|
||||
}
|
||||
|
||||
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