UNPKG

alinea

Version:

[![npm](https://img.shields.io/npm/v/alinea.svg)](https://npmjs.org/package/alinea) [![install size](https://packagephobia.com/badge?p=alinea)](https://packagephobia.com/result?p=alinea)

332 lines (328 loc) 12.5 kB
import { useQuery } from "../../chunks/chunk-DJKGEOOC.js"; import "../../chunks/chunk-U5RRZUYZ.js"; // src/picker/entry/EntryPicker.browser.tsx import { Picker, Root, createId } from "alinea/core"; import { Entry } from "alinea/core/Entry"; import { workspaceMediaDir } from "alinea/core/EntryFilenames"; import { isMediaRoot } from "alinea/core/media/MediaRoot"; import { and } from "alinea/core/pages/Expr"; import { entries } from "alinea/core/util/Objects"; import { useConfig } from "alinea/dashboard/hook/UseConfig"; 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, fromModule } 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 = Picker.withView(createEntryPicker, { view: EntryPickerModal, viewRow: EntryPickerRow }); var styles = fromModule(EntryPicker_module_default); function mediaRoot(workspace) { for (const [name, root] of entries(workspace.roots)) if (isMediaRoot(root)) return name; throw new Error(`Workspace ${workspace.name} has no media root`); } function EntryPickerModal({ type, options, selection, onConfirm, onCancel }) { const config = useConfig(); const graph = useGraph(); const { title, defaultView, max, condition, withNavigation = true, showMedia } = options; const [search, setSearch] = useState(""); const list = useFocusList({ onClear: () => setSearch("") }); const [selected, setSelected] = useState( () => selection || [] ); const workspace = useWorkspace(); const { name: root } = useRoot(); const locale = useLocale(); const [destination, setDestination] = useState({ workspace: workspace.name, root: showMedia ? mediaRoot(workspace) : root, locale }); const destinationRoot = Root.data(workspace.roots[destination.root]); const destinationLocale = !destinationRoot.i18n ? void 0 : destination.locale ?? destinationRoot.i18n.locales[0]; const { data: parentEntries } = useQuery( ["picker-parents", destination, destinationLocale], async () => { if (!destination.parentId) return []; const drafts = graph.preferDraft; const query = destinationLocale ? drafts.locale(destinationLocale) : drafts; const res = await query.get( Entry({ entryId: destination.parentId }).select({ title: Entry.title, parents({ parents }) { return parents().select({ id: Entry.entryId, title: Entry.title }); } }) ); return res?.parents.concat({ id: destination.parentId, title: res.title }); } ); const cursor = useMemo(() => { const terms = search.replace(/,/g, " ").split(" ").filter(Boolean); if (!withNavigation && condition) return Entry().where(condition).search(...terms); const rootCondition = and( Entry.workspace.is(destination.workspace), Entry.root.is(destination.root) ); const destinationCondition = terms.length === 0 ? and(rootCondition, Entry.parent.is(destination.parentId ?? null)) : rootCondition; const translatedCondition = destinationLocale ? and(destinationCondition, Entry.locale.is(destinationLocale)) : destinationCondition; return Entry().where(translatedCondition).search(...terms); }, [destination, destinationLocale, search, condition]); const [view, setView] = useState(defaultView || "row"); const handleSelect = useCallback( (entry) => { setSelected((selected2) => { const index = selected2.findIndex( (ref) => EntryReference.isEntryReference(ref) && ref.entry === entry.entryId ); let res = selected2.slice(); if (index === -1) { res = res.concat({ id: createId(), type, entry: entry.entryId }).slice(-(max || 0)); } else { res.splice(index, 1); res = res.slice(-(max || 0)); } if (max === 1 && res.length === 1) onConfirm(res); return res; }); }, [setSelected, 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", { onClick: toRoot, children: workspace.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.roots[destination.root]), /* @__PURE__ */ jsx(Icon, { icon: IcRoundUnfoldMore }) ] }) }), /* @__PURE__ */ jsx(DropdownMenu.Items, { children: entries(workspace.roots).map(([name, root2]) => { return /* @__PURE__ */ jsx( DropdownMenu.Item, { onClick: () => { setDestination({ workspace: destination.workspace, root: name }); }, children: Root.label(root2) }, name ); }) }) ] }), destinationRoot.i18n && /* @__PURE__ */ jsx( Langswitch, { inline: true, selected: destinationLocale, locales: destinationRoot.i18n.locales, onChange: (locale2) => { setDestination({ ...destination, parentId: void 0, locale: locale2 }); } } ) ] }), !search && parentEntries?.map(({ id, title: title2 }) => { return /* @__PURE__ */ jsx(BreadcrumbsItem, { children: /* @__PURE__ */ jsx( "button", { 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, autoFocus: true } ), /* @__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, cursor, type: view, selectable: showMedia ? ["MediaFile"] : true, selection: selected, toggleSelect: handleSelect, showMedia, onNavigate: search ? void 0 : (entryId) => { setDestination({ ...destination, parentId: entryId }); } } ) }) }), showMedia && /* @__PURE__ */ jsx( FileUploader, { position: "left", destination: { ...destination, directory: workspaceMediaDir(config, destination.workspace) }, max, toggleSelect: handleSelect } ) ] }), /* @__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 };