alinea
Version:
Headless git-based CMS
374 lines (370 loc) • 14.1 kB
JavaScript
import {
dist_default
} from "../../chunks/chunk-A5O3N2GS.js";
import {
useQuery
} from "../../chunks/chunk-YWCPLD22.js";
import "../../chunks/chunk-NZLE2WMY.js";
// src/picker/entry/EntryPicker.browser.tsx
import { Entry } from "alinea/core/Entry";
import { createId } from "alinea/core/Id";
import { pickerWithView } from "alinea/core/Picker";
import { Reference } from "alinea/core/Reference";
import { Root } from "alinea/core/Root";
import { Workspace } from "alinea/core/Workspace";
import { workspaceMediaDir } from "alinea/core/util/EntryFilenames";
import { entries } from "alinea/core/util/Objects";
import { useConfig } from "alinea/dashboard/hook/UseConfig";
import { useEntryEditor } from "alinea/dashboard/hook/UseEntryEditor";
import { useFocusList } from "alinea/dashboard/hook/UseFocusList";
import { useGraph } from "alinea/dashboard/hook/UseGraph";
import { useLocale } from "alinea/dashboard/hook/UseLocale";
import { useRoot } from "alinea/dashboard/hook/UseRoot";
import { useWorkspace } from "alinea/dashboard/hook/UseWorkspace";
import { Breadcrumbs, BreadcrumbsItem } from "alinea/dashboard/view/Breadcrumbs";
import { IconButton } from "alinea/dashboard/view/IconButton";
import { Modal } from "alinea/dashboard/view/Modal";
import { Langswitch } from "alinea/dashboard/view/entry/LangSwitch";
import {
Explorer
} from "alinea/dashboard/view/explorer/Explorer";
import { FileUploader } from "alinea/dashboard/view/media/FileUploader";
import { Button, HStack, Icon, Loader, Stack, TextLabel, VStack } from "alinea/ui";
import { DropdownMenu } from "alinea/ui/DropdownMenu";
import { IcOutlineGridView } from "alinea/ui/icons/IcOutlineGridView";
import { IcOutlineList } from "alinea/ui/icons/IcOutlineList";
import { IcRoundArrowBack } from "alinea/ui/icons/IcRoundArrowBack";
import { IcRoundSearch } from "alinea/ui/icons/IcRoundSearch";
import { IcRoundUnfoldMore } from "alinea/ui/icons/IcRoundUnfoldMore";
import { Suspense, useCallback, useMemo, useState } from "react";
import {
entryPicker as createEntryPicker
} from "./EntryPicker.js";
// src/picker/entry/EntryPicker.module.scss
var EntryPicker_module_default = {
"root": "alinea-EntryPicker",
"root-footer": "alinea-EntryPicker-footer",
"rootFooter": "alinea-EntryPicker-footer",
"root-header": "alinea-EntryPicker-header",
"rootHeader": "alinea-EntryPicker-header",
"root-search": "alinea-EntryPicker-search",
"rootSearch": "alinea-EntryPicker-search",
"root-search-icon": "alinea-EntryPicker-search-icon",
"rootSearchIcon": "alinea-EntryPicker-search-icon",
"root-search-input": "alinea-EntryPicker-search-input",
"rootSearchInput": "alinea-EntryPicker-search-input",
"root-results": "alinea-EntryPicker-results",
"rootResults": "alinea-EntryPicker-results",
"root-results-list": "alinea-EntryPicker-results-list",
"rootResultsList": "alinea-EntryPicker-results-list",
"root-results-list-item": "alinea-EntryPicker-results-list-item",
"rootResultsListItem": "alinea-EntryPicker-results-list-item"
};
// src/picker/entry/EntryPicker.browser.tsx
import { EntryPickerRow } from "./EntryPickerRow.js";
import { EntryReference } from "./EntryReference.js";
export * from "./EntryPicker.js";
import { jsx, jsxs } from "react/jsx-runtime";
var entryPicker = pickerWithView(createEntryPicker, {
view: EntryPickerModal,
viewRow: EntryPickerRow
});
var styles = dist_default(EntryPicker_module_default);
function EntryPickerModal({
type,
options,
selection,
onConfirm,
onCancel
}) {
const config = useConfig();
const graph = useGraph();
const editor = useEntryEditor();
const { title, defaultView, max, pickChildren, showMedia } = options;
const [search, setSearch] = useState("");
const list = useFocusList({
onClear: () => setSearch("")
});
const [selected, setSelected] = useState(
() => selection || []
);
const { name: currentWorkspace } = useWorkspace();
const { name: currentRoot } = useRoot();
const locale = useLocale();
const entry = useMemo(() => {
if (!editor) return;
const { id, type: type2, workspace: workspace2, root, parentId, locale: locale2 } = editor.activeVersion;
return { id, type: type2, workspace: workspace2, root, parentId, locale: locale2 };
}, [editor?.activeVersion]);
const locationQuery = useQuery(
["entry-location", graph, entry, options.location],
async () => {
if (!entry) return void 0;
return typeof options.location === "function" ? options.location({ graph, entry }) : options.location;
},
{ suspense: true }
);
const location = locationQuery.data;
const [destination, setDestination] = useState({
workspace: currentWorkspace,
parentId: pickChildren ? editor?.entryId : void 0,
root: showMedia ? Workspace.defaultMediaRoot(config.workspaces[currentWorkspace]) : currentRoot,
...location,
locale
});
const workspace = config.workspaces[destination.workspace];
const workspaceData = Workspace.data(workspace);
const destinationRoot = Root.data(workspace[destination.root]);
const locales = destinationRoot.i18n?.locales;
const destinationLocale = !destinationRoot.i18n ? void 0 : locales && destination.locale && locales.includes(destination.locale) ? destination.locale : destinationRoot.i18n.locales[0];
const { data: parentEntries } = useQuery(
["picker-parents", destination, destinationLocale],
async () => {
if (!destination.parentId) return [];
const res = await graph.get({
locale: destinationLocale,
select: {
title: Entry.title,
parents: {
edge: "parents",
select: {
id: Entry.id,
title: Entry.title
}
}
},
id: destination.parentId,
status: "preferDraft"
});
return res?.parents.concat({
id: destination.parentId,
title: res.title
});
}
);
const withNavigation = options.enableNavigation || !options.condition && !options.pickChildren;
const conditionQuery = useQuery(
["entry-condition", graph, entry],
() => {
if (!entry) return void 0;
return typeof options.condition === "function" ? options.condition({ graph, entry }) : options.condition;
},
{ suspense: true }
);
const query = useMemo(() => {
const terms = search.replace(/,/g, " ").split(" ").filter(Boolean);
const condition = conditionQuery.data;
const parentId = withNavigation && !search || pickChildren ? destination.parentId ?? null : void 0;
const filter = {
and: [
condition,
{
_workspace: destination.workspace,
_root: destination.root,
_parentId: parentId,
_locale: destinationLocale
}
]
};
return {
select: Entry,
filter,
search: terms
};
}, [
withNavigation,
destination,
destinationLocale,
search,
conditionQuery.data
]);
const [view, setView] = useState(defaultView || "row");
const handleSelect = useCallback(
(entry2) => {
setSelected((selected2) => {
const index = selected2.findIndex(
(ref) => EntryReference.isEntryReference(ref) && ref[EntryReference.entry] === entry2.id
);
let res = selected2.slice();
if (index === -1) {
res = res.concat({
[Reference.id]: createId(),
[Reference.type]: type,
[EntryReference.entry]: entry2.id
}).slice(-(max || 0));
} else {
res.splice(index, 1);
res = res.slice(-(max || 0));
}
if (max === 1 && res.length === 1) onConfirm(res);
return res;
});
},
[onConfirm, type, max]
);
function handleConfirm() {
onConfirm(selected);
}
function toRoot() {
setDestination({ ...destination, parentId: void 0 });
}
function goUp() {
if (!destination.parentId) return onCancel();
const parentIndex = parentEntries?.findIndex(
({ id }) => id === destination.parentId
);
if (parentIndex === void 0) return;
const parent = parentEntries?.[parentIndex - 1];
if (!parent) return toRoot();
setDestination({ ...destination, parentId: parent.id });
}
return /* @__PURE__ */ jsx(Modal, { open: true, onClose: onCancel, className: styles.root(), children: /* @__PURE__ */ jsxs(Suspense, { fallback: /* @__PURE__ */ jsx(Loader, { absolute: true }), children: [
/* @__PURE__ */ jsx("div", { className: styles.root.header(), children: /* @__PURE__ */ jsxs(VStack, { gap: 24, children: [
/* @__PURE__ */ jsxs(HStack, { align: "flex-end", gap: 18, children: [
/* @__PURE__ */ jsx(IconButton, { icon: IcRoundArrowBack, onClick: goUp }),
/* @__PURE__ */ jsxs(VStack, { children: [
withNavigation && /* @__PURE__ */ jsxs(Breadcrumbs, { children: [
/* @__PURE__ */ jsx(BreadcrumbsItem, { children: /* @__PURE__ */ jsx(
"button",
{
type: "button",
style: { cursor: "pointer" },
onClick: toRoot,
children: workspaceData.label
}
) }),
/* @__PURE__ */ jsxs(BreadcrumbsItem, { children: [
/* @__PURE__ */ jsxs(DropdownMenu.Root, { bottom: true, children: [
/* @__PURE__ */ jsx(DropdownMenu.Trigger, { children: /* @__PURE__ */ jsxs(HStack, { center: true, gap: 4, children: [
Root.label(workspace[destination.root]),
/* @__PURE__ */ jsx(Icon, { icon: IcRoundUnfoldMore })
] }) }),
/* @__PURE__ */ jsx(DropdownMenu.Items, { children: entries(workspace).map(([name, root]) => {
return /* @__PURE__ */ jsx(
DropdownMenu.Item,
{
onClick: () => {
setDestination({
workspace: destination.workspace,
root: name
});
},
children: Root.label(root)
},
name
);
}) })
] }),
destinationRoot.i18n && /* @__PURE__ */ jsx(
Langswitch,
{
inline: true,
selected: destinationLocale,
locales: destinationRoot.i18n.locales,
onChange: (locale2) => {
setDestination({
...destination,
parentId: void 0,
locale: locale2
});
}
}
)
] }),
parentEntries?.map(({ id, title: title2 }) => {
return /* @__PURE__ */ jsx(BreadcrumbsItem, { children: /* @__PURE__ */ jsx(
"button",
{
type: "button",
style: { cursor: "pointer" },
onClick: () => {
setDestination({
...destination,
parentId: id
});
},
children: title2
}
) }, id);
})
] }),
/* @__PURE__ */ jsx("h2", { children: title ? /* @__PURE__ */ jsx(TextLabel, { label: title }) : "Select a reference" })
] })
] }),
/* @__PURE__ */ jsxs("label", { className: styles.root.search(), children: [
/* @__PURE__ */ jsx(IcRoundSearch, { className: styles.root.search.icon() }),
/* @__PURE__ */ jsx(
"input",
{
type: "text",
placeholder: "Search",
value: search,
onChange: (event) => setSearch(event.target.value),
className: styles.root.search.input(),
...list.focusProps
}
),
/* @__PURE__ */ jsx(Stack.Right, { children: /* @__PURE__ */ jsxs(HStack, { gap: 16, children: [
/* @__PURE__ */ jsx(
IconButton,
{
icon: IcOutlineList,
active: view === "row",
onClick: () => setView("row")
}
),
/* @__PURE__ */ jsx(
IconButton,
{
icon: IcOutlineGridView,
active: view === "thumb",
onClick: () => setView("thumb")
}
)
] }) })
] })
] }) }),
/* @__PURE__ */ jsxs(VStack, { style: { flexGrow: 1, minHeight: 0 }, children: [
/* @__PURE__ */ jsx(list.Container, { children: /* @__PURE__ */ jsx("div", { className: styles.root.results(), children: /* @__PURE__ */ jsx(
Explorer,
{
virtualized: true,
query,
type: view,
selectable: showMedia ? ["MediaFile"] : true,
selection: selected,
toggleSelect: handleSelect,
showMedia,
onNavigate: search && !showMedia ? void 0 : (entryId) => {
if (showMedia && search) {
setSearch("");
}
setDestination({ ...destination, parentId: entryId });
},
withNavigation
}
) }) }),
showMedia && /* @__PURE__ */ jsx(
FileUploader,
{
position: "left",
destination: {
...destination,
directory: workspaceMediaDir(config, destination.workspace)
},
max,
toggleSelect: handleSelect,
onlyImages: type === "image"
}
)
] }),
/* @__PURE__ */ jsx(HStack, { as: "footer", className: styles.root.footer(), children: /* @__PURE__ */ jsx(Stack.Right, { children: /* @__PURE__ */ jsxs(HStack, { gap: 16, children: [
/* @__PURE__ */ jsx(Button, { outline: true, type: "button", onClick: onCancel, children: "Cancel" }),
/* @__PURE__ */ jsx(Button, { onClick: handleConfirm, children: "Confirm" })
] }) }) })
] }) });
}
export {
EntryPickerModal,
entryPicker
};