UNPKG

alinea

Version:
374 lines (370 loc) 14.1 kB
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 };