262 lines
10 KiB
JavaScript
262 lines
10 KiB
JavaScript
const PINNED_STORAGE_KEY = "pbPinnedCollections";
|
|
|
|
const compactThreshold = 12;
|
|
|
|
export function collectionsSidebar() {
|
|
const data = store({
|
|
search: "",
|
|
pinned: app.utils.getLocalHistory(PINNED_STORAGE_KEY, []),
|
|
get filteredCollections() {
|
|
if (!data.search.length) {
|
|
return app.store.collections;
|
|
}
|
|
|
|
const normalizedSearch = data.search.replaceAll(" ", "").toLowerCase();
|
|
|
|
return app.store.collections.filter((c) => {
|
|
return (c.name + c.id + c.type).toLowerCase().includes(normalizedSearch);
|
|
});
|
|
},
|
|
get systemCollections() {
|
|
return data.filteredCollections.filter((c) => c.system && !data.pinned.includes(c.id));
|
|
},
|
|
get regularCollections() {
|
|
return data.filteredCollections.filter((c) => !c.system && !data.pinned.includes(c.id));
|
|
},
|
|
get pinnedCollections() {
|
|
if (!data.pinned.length) {
|
|
return [];
|
|
}
|
|
|
|
return data.filteredCollections.filter((c) => data.pinned.includes(c.id));
|
|
},
|
|
});
|
|
|
|
function clearSearch() {
|
|
data.search = "";
|
|
}
|
|
|
|
const watchers = [];
|
|
|
|
return app.components.pageSidebar(
|
|
{
|
|
className: () => `collections-sidebar ${data.responsiveShow ? "active" : ""}`,
|
|
onmount: (el) => {
|
|
// init and persist pinned changes
|
|
watchers.push(watch(() => {
|
|
app.utils.saveLocalHistory(PINNED_STORAGE_KEY, JSON.stringify(data.pinned));
|
|
}));
|
|
|
|
// scroll to the active item
|
|
watchers.push(watch(
|
|
() => app.store.activeCollection?.id,
|
|
async () => {
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
const activeNavItem = el?.querySelector(".nav-item.active");
|
|
const details = activeNavItem?.closest("details");
|
|
if (details) {
|
|
details.open = true;
|
|
activeNavItem?.scrollIntoView({ block: "nearest" });
|
|
}
|
|
},
|
|
));
|
|
},
|
|
onunmount: () => {
|
|
watchers.forEach((w) => w?.unwatch());
|
|
},
|
|
},
|
|
t.div(
|
|
{ className: "sidebar-search" },
|
|
t.div(
|
|
{ className: "fields" },
|
|
t.div(
|
|
{ className: "field" },
|
|
t.input({
|
|
className: "p-r-5",
|
|
type: "text",
|
|
placeholder: "Search collections...",
|
|
value: () => data.search,
|
|
oninput: (e) => data.search = e.target.value,
|
|
}),
|
|
),
|
|
t.div(
|
|
{ className: "field addon p-l-0 p-r-5 gap-0" },
|
|
t.button(
|
|
{
|
|
hidden: () => !data.search.length,
|
|
type: "button",
|
|
className: "btn sm circle transparent secondary",
|
|
ariaDescription: app.attrs.tooltip("Clear", "left"),
|
|
onclick: clearSearch,
|
|
},
|
|
t.i({ className: "ri-close-line", ariaHidden: true }),
|
|
),
|
|
t.button(
|
|
{
|
|
hidden: () => app.store.isLoadingCollections,
|
|
type: "button",
|
|
className: "btn sm circle transparent secondary link-faded",
|
|
ariaDescription: app.attrs.tooltip("Collections overview", "left"),
|
|
onclick: () => app.modals.openCollectionsOverview(),
|
|
},
|
|
t.i({ className: "ri-organization-chart", ariaHidden: true }),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
() => {
|
|
if (
|
|
!data.search.length
|
|
|| !!data.filteredCollections.length
|
|
|| app.store.isLoadingCollections
|
|
) {
|
|
return;
|
|
}
|
|
|
|
return t.div(
|
|
{ className: "block p-t-base txt-center txt-hint" },
|
|
t.p(null, "No collections found."),
|
|
t.button({
|
|
type: "button",
|
|
className: "btn sm secondary",
|
|
textContent: "Clear search",
|
|
onclick: () => clearSearch(),
|
|
}),
|
|
);
|
|
},
|
|
() => {
|
|
if (app.store.isLoadingCollections) {
|
|
return t.div({ className: "sidebar-content txt-center" }, t.span({ className: "loader sm" }));
|
|
}
|
|
|
|
return [
|
|
t.nav(
|
|
{
|
|
className: () =>
|
|
`sidebar-content collections-list scrollable ${
|
|
data.regularCollections.length + data.pinnedCollections >= compactThreshold
|
|
? "compact"
|
|
: ""
|
|
}`,
|
|
},
|
|
t.details(
|
|
{
|
|
hidden: () => !data.pinnedCollections.length,
|
|
className: () => `nav-group nav-group-pinned-collections`,
|
|
open: true,
|
|
},
|
|
t.summary(
|
|
{ tabIndex: -1, onfocusout: () => false, onclick: () => false, onkeyup: () => false },
|
|
"Pinned",
|
|
),
|
|
() => data.pinnedCollections.map((c) => collectionItem(c, data)),
|
|
),
|
|
t.details(
|
|
{
|
|
hidden: () => !data.regularCollections.length,
|
|
className: "nav-group nav-group-regular-collections",
|
|
open: true,
|
|
},
|
|
t.summary(
|
|
{ tabIndex: -1, onfocusout: () => false, onclick: () => false, onkeyup: () => false },
|
|
() => data.pinnedCollections.length ? "Others" : "Collections",
|
|
),
|
|
() => data.regularCollections.map((c) => collectionItem(c, data)),
|
|
),
|
|
t.details(
|
|
{
|
|
hidden: () => !data.systemCollections.length,
|
|
className: "nav-group nav-group-system-collections",
|
|
open: () => data.search.length,
|
|
},
|
|
t.summary(null, "System"),
|
|
() => data.systemCollections.map((c) => collectionItem(c, data)),
|
|
),
|
|
),
|
|
t.div(
|
|
{
|
|
hidden: () => data.search.length && !data.filteredCollections.length,
|
|
className: "sidebar-content new-collection",
|
|
},
|
|
t.button(
|
|
{
|
|
type: "button",
|
|
className: "btn outline block",
|
|
onclick: () => {
|
|
app.modals.openCollectionUpsert({}, {
|
|
onsave: (newCollection) => {
|
|
app.store.activeCollection = newCollection.id;
|
|
},
|
|
});
|
|
},
|
|
},
|
|
t.i({ className: "ri-add-line", ariaHidden: true }),
|
|
t.span({ textContent: "New collection" }),
|
|
),
|
|
),
|
|
];
|
|
},
|
|
);
|
|
}
|
|
|
|
function collectionItem(collection, data) {
|
|
return t.button(
|
|
{
|
|
"html-data-collection-id": () => collection.id,
|
|
type: "button",
|
|
className: () =>
|
|
`nav-item responsive-close ${collection.id == app.store.activeCollection?.id ? "active" : ""}`,
|
|
title: () => collection.name,
|
|
onclick: () => app.store.activeCollection = collection.name,
|
|
},
|
|
t.i({
|
|
className: () => app.collectionTypes[collection.type]?.icon || app.utils.fallbackCollectionIcon,
|
|
ariaHidden: true,
|
|
}),
|
|
t.span({ className: "txt" }, () => collection.name),
|
|
() => {
|
|
if (
|
|
collection.type != "auth"
|
|
|| !collection.oauth2?.enabled
|
|
|| collection.oauth2?.providers?.length > 0
|
|
) {
|
|
return;
|
|
}
|
|
|
|
return t.i({
|
|
ariaHidden: true,
|
|
className: "ri-alert-line txt-hint txt-sm",
|
|
ariaDescription: app.attrs.tooltip(
|
|
"OAuth2 auth is enabled but the collection doesn't have any registered providers",
|
|
),
|
|
});
|
|
},
|
|
() => {
|
|
const pinnedIndex = data.pinned.indexOf(collection.id);
|
|
|
|
return t.span(
|
|
{
|
|
tabIndex: -1,
|
|
role: "button",
|
|
className: "pin",
|
|
title: () => pinnedIndex >= 0 ? "Unpin" : "Pin",
|
|
onclick: (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (pinnedIndex >= 0) {
|
|
data.pinned.splice(pinnedIndex, 1);
|
|
} else {
|
|
data.pinned.push(collection.id);
|
|
}
|
|
},
|
|
},
|
|
t.i({
|
|
ariaHidden: false,
|
|
className: () => pinnedIndex >= 0 ? "ri-unpin-line" : "ri-pushpin-line",
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
}
|