UNPKG

alinea

Version:
402 lines (398 loc) 14.3 kB
import { CSS, DndContext, DragOverlay, KeyboardSensor, LayoutMeasuringStrategy, PointerSensor, SortableContext, closestCenter, defaultAnimateLayoutChanges, defaultDropAnimation, sortableKeyboardCoordinates, useSensor, useSensors, useSortable, verticalListSortingStrategy } from "../../chunks/chunk-TPUTTEHF.js"; import { dist_default } from "../../chunks/chunk-A5O3N2GS.js"; import "../../chunks/chunk-NZLE2WMY.js"; // src/field/link/LinkField.view.tsx import { Entry } from "alinea/core/Entry"; import { Reference } from "alinea/core/Reference"; import { ListRow } from "alinea/core/shape/ListShape"; import { entries } from "alinea/core/util/Objects"; import { FormRow } from "alinea/dashboard/atoms/FormAtoms"; import { InputForm } from "alinea/dashboard/editor/InputForm"; import { useField } from "alinea/dashboard/editor/UseField"; import { useGraph } from "alinea/dashboard/hook/UseGraph"; import { useLocale } from "alinea/dashboard/hook/UseLocale"; import { useNav } from "alinea/dashboard/hook/UseNav"; import { SuspenseBoundary } from "alinea/dashboard/util/SuspenseBoundary"; import { Create } from "alinea/dashboard/view/Create"; import { IconButton } from "alinea/dashboard/view/IconButton"; import { InputLabel } from "alinea/dashboard/view/InputLabel"; import { EntryReference } from "alinea/picker/entry/EntryReference"; import { UrlReference } from "alinea/picker/url/UrlPicker"; import { TextLabel } from "alinea/ui"; import { IcRoundClose } from "alinea/ui/icons/IcRoundClose"; import IcRoundDragHandle from "alinea/ui/icons/IcRoundDragHandle"; import { IcRoundEdit } from "alinea/ui/icons/IcRoundEdit"; import { IcRoundLink } from "alinea/ui/icons/IcRoundLink"; import { IcRoundOpenInNew } from "alinea/ui/icons/IcRoundOpenInNew"; import { Sink } from "alinea/ui/Sink"; import { Suspense, useState } from "react"; // src/field/link/LinkField.module.scss var LinkField_module_default = { "root": "alinea-LinkField", "root-inner": "alinea-LinkField-inner", "rootInner": "alinea-LinkField-inner", "row": "alinea-LinkField-row", "is-dragging": "alinea-LinkField-is-dragging", "isDragging": "alinea-LinkField-is-dragging", "is-overlay": "alinea-LinkField-is-overlay", "isOverlay": "alinea-LinkField-is-overlay", "row-header": "alinea-LinkField-row-header", "rowHeader": "alinea-LinkField-row-header", "row-staticHandle": "alinea-LinkField-row-staticHandle", "rowStaticHandle": "alinea-LinkField-row-staticHandle", "create": "alinea-LinkField-create" }; // src/field/link/LinkField.view.tsx import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var styles = dist_default(LinkField_module_default); function SingleLinkInput({ field }) { const { options, value, mutator, error } = useField(field); const { readOnly } = options; const [pickFrom, setPickFrom] = useState(); const picker = pickFrom ? options.pickers[pickFrom] : void 0; function handleConfirm(link) { if (readOnly) return; const selected = link[0]; if (!pickFrom || !picker || !selected) return; if (selected[Reference.type] !== pickFrom) return; mutator.replace(selected); setPickFrom(void 0); } const PickerView = picker && picker.view; return /* @__PURE__ */ jsxs(Fragment, { children: [ PickerView && /* @__PURE__ */ jsx(SuspenseBoundary, { name: "picker", children: /* @__PURE__ */ jsx( PickerView, { type: pickFrom, options: picker.options, selection: [value], onConfirm: handleConfirm, onCancel: () => setPickFrom(void 0) } ) }), /* @__PURE__ */ jsx(InputLabel, { ...options, error, icon: IcRoundLink, children: /* @__PURE__ */ jsx("div", { className: styles.root(), children: /* @__PURE__ */ jsx("div", { className: styles.root.inner(), children: /* @__PURE__ */ jsx(Sink.Root, { children: value && options.pickers[value[Reference.type]] ? /* @__PURE__ */ jsx( LinkInputRow, { readOnly, field, rowId: value[Reference.id], fields: options.pickers[value[Reference.type]].fields, picker: options.pickers[value[Reference.type]], reference: value, onRemove: () => mutator.replace(void 0), onEdit: () => setPickFrom(value[Reference.type]) } ) : /* @__PURE__ */ jsx("div", { className: styles.create(), children: /* @__PURE__ */ jsx(Create.Root, { disabled: readOnly, children: entries(options.pickers).map(([name, picker2]) => { return /* @__PURE__ */ jsx( Create.Button, { onClick: () => { if (readOnly) return; setPickFrom(name); }, children: /* @__PURE__ */ jsx(TextLabel, { label: picker2.label }) }, name ); }) }) }) }) }) }) }) ] }); } var layoutMeasuringConfig = { strategy: LayoutMeasuringStrategy.Always }; function MultipleLinksInput({ field }) { const { options, value, mutator, error } = useField(field); const { readOnly } = options; const [pickFrom, setPickFrom] = useState(); const picker = pickFrom ? options.pickers[pickFrom[Reference.type]] : void 0; function handleConfirm(links) { if (!pickFrom || !picker || !links) return; const seen = /* @__PURE__ */ new Set(); for (const link of links) { if (link[ListRow.type] !== pickFrom[Reference.type]) continue; seen.add(link[ListRow.id]); const index = value.findIndex((v) => v[ListRow.id] === link[ListRow.id]); if (index > -1) mutator.replace(link[ListRow.id], link); else mutator.push(link); } if (picker.handlesMultiple) for (const link of value) { if (link[ListRow.type] !== pickFrom[Reference.type]) continue; if (seen.has(link[ListRow.id])) continue; mutator.remove(link[ListRow.id]); } setPickFrom(void 0); } const ids = value.map((row) => row[ListRow.id]); const [dragging, setDragging] = useState(null); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); function handleDragStart(event) { const { active } = event; setDragging(value.find((row) => row[ListRow.id] === active.id) || null); } function handleDragEnd(event) { const { active, over } = event; if (!over || active.id === over.id) return; if (!readOnly) mutator.move(ids.indexOf(active.id), ids.indexOf(over.id)); setDragging(null); } const showLinkPicker = options.max ? value.length < options.max : true; const PickerView = picker && picker.view; return /* @__PURE__ */ jsxs(Fragment, { children: [ pickFrom && PickerView && /* @__PURE__ */ jsx(SuspenseBoundary, { name: "picker", children: /* @__PURE__ */ jsx( PickerView, { type: pickFrom[Reference.type], options: picker.options, selection: value.filter((ref) => { if (ref[ListRow.id] === pickFrom[Reference.id]) return true; if (picker.handlesMultiple) return ref[ListRow.type] === pickFrom[Reference.type]; return false; }), onConfirm: handleConfirm, onCancel: () => setPickFrom(void 0) } ) }), /* @__PURE__ */ jsx( DndContext, { sensors, collisionDetection: closestCenter, onDragStart: handleDragStart, onDragEnd: handleDragEnd, layoutMeasuring: layoutMeasuringConfig, children: /* @__PURE__ */ jsx(InputLabel, { ...options, error, icon: IcRoundLink, children: /* @__PURE__ */ jsx("div", { className: styles.root(), children: /* @__PURE__ */ jsxs("div", { className: styles.root.inner(), children: [ /* @__PURE__ */ jsx( SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsxs(Sink.Root, { children: [ value.map((reference) => { if (!options.pickers[reference[ListRow.type]]) return null; const picker2 = options.pickers[reference[ListRow.type]]; const type = picker2.fields; return /* @__PURE__ */ jsx( LinkInputRowSortable, { rowId: reference[ListRow.id], field, fields: type, picker: picker2, reference, onRemove: () => mutator.remove(reference[ListRow.id]), onEdit: () => setPickFrom(reference), isSortable: options.max !== 1, readOnly, multiple: true }, reference[ListRow.id] ); }), showLinkPicker && /* @__PURE__ */ jsx("div", { className: styles.create(), children: /* @__PURE__ */ jsx(Create.Root, { disabled: readOnly, children: entries(options.pickers).map(([name, picker2]) => { return /* @__PURE__ */ jsx( Create.Button, { onClick: () => setPickFrom({ [Reference.type]: name }), children: /* @__PURE__ */ jsx(TextLabel, { label: picker2.label }) }, name ); }) }) }) ] }) } ), /* @__PURE__ */ jsx( DragOverlay, { dropAnimation: { ...defaultDropAnimation, dragSourceOpacity: 0.5 }, children: dragging && options.pickers[dragging[Reference.type]] ? /* @__PURE__ */ jsx( LinkInputRow, { field, rowId: dragging._id, fields: options.pickers[dragging[Reference.type]].fields, picker: options.pickers[dragging[Reference.type]], reference: dragging, onRemove: () => mutator.remove(dragging._id), onEdit: () => setPickFrom(dragging), isDragOverlay: true, isSortable: options.max !== 1, readOnly, multiple: true } ) : null } ) ] }) }) }) } ) ] }); } function animateLayoutChanges(args) { const { isSorting, wasSorting } = args; if (isSorting || wasSorting) return defaultAnimateLayoutChanges(args); return true; } function LinkInputRowSortable(props) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ animateLayoutChanges, id: props.reference._id }); const style = { transform: CSS.Transform.toString(transform), transition: transition || void 0 }; return /* @__PURE__ */ jsx( LinkInputRow, { ...props, rootRef: setNodeRef, style, handle: listeners, ...attributes, isDragging } ); } function LinkInputRow({ field, rowId, picker, fields, reference, onEdit, onRemove, handle, rootRef, isDragging, isDragOverlay, isSortable, readOnly, multiple, ...rest }) { const onView = useReferenceViewer(); const RowView = picker.viewRow; const inner = /* @__PURE__ */ jsxs( "div", { className: styles.row({ dragging: isDragging, overlay: isDragOverlay }), ref: rootRef, ...rest, children: [ /* @__PURE__ */ jsxs(Sink.Header, { children: [ /* @__PURE__ */ jsx(Sink.Options, { children: isSortable ? /* @__PURE__ */ jsx( IconButton, { icon: IcRoundDragHandle, ...handle, style: { cursor: handle ? "grab" : "grabbing" }, title: "Drag and drop to reorder" } ) : /* @__PURE__ */ jsx("div", { className: styles.row.staticHandle(), children: /* @__PURE__ */ jsx(IcRoundLink, {}) }) }), /* @__PURE__ */ jsx("div", { style: { flexGrow: 1, minWidth: 0 }, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(RowView, { reference }) }) }), !readOnly && /* @__PURE__ */ jsxs(Sink.Options, { children: [ /* @__PURE__ */ jsx( IconButton, { icon: IcRoundOpenInNew, onClick: () => onView(reference), title: reference?._type === "image" ? "Open media file in new tab" : "Open link in new tab" } ), /* @__PURE__ */ jsx( IconButton, { icon: IcRoundEdit, onClick: onEdit, title: reference?._type === "image" ? "Change image" : "Edit link" } ), /* @__PURE__ */ jsx( IconButton, { icon: IcRoundClose, onClick: onRemove, title: reference?._type === "image" ? "Delete image" : "Delete link" } ) ] }) ] }), fields && /* @__PURE__ */ jsx(Sink.Content, { children: /* @__PURE__ */ jsx(InputForm, { type: fields }) }) ] } ); if (!fields) return inner; return /* @__PURE__ */ jsx( FormRow, { field, type: fields, readOnly, rowId: multiple ? rowId : void 0, children: inner } ); } function useReferenceViewer() { const nav = useNav(); const locale = useLocale(); const graph = useGraph(); return (reference) => { if (UrlReference.isUrl(reference)) { window.open(reference[UrlReference.url], "_blank"); } else if (EntryReference.isEntryReference(reference)) { graph.first({ id: reference[EntryReference.entry], locale: reference[Reference.type] === "entry" ? locale : null, select: { id: Entry.id, workspace: Entry.workspace, root: Entry.root }, status: "preferDraft" }).then((entry) => { if (!entry) return; window.open(`#${nav.entry(entry)}`, "_blank"); }); } }; } export { MultipleLinksInput, SingleLinkInput };