merge newui branch
This commit is contained in:
258
ui/src/collections/collectionsSidebar.js
Normal file
258
ui/src/collections/collectionsSidebar.js
Normal file
@@ -0,0 +1,258 @@
|
||||
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" }),
|
||||
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 }),
|
||||
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",
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user