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)

359 lines (355 loc) 12.2 kB
import { CSS, DndContext, DragOverlay, KeyboardSensor, LayoutMeasuringStrategy, PointerSensor, SortableContext, closestCenter, defaultAnimateLayoutChanges, defaultDropAnimation, sortableKeyboardCoordinates, useSensor, useSensors, useSortable, verticalListSortingStrategy } from "../../chunks/chunk-XMYBRQEU.js"; import "../../chunks/chunk-U5RRZUYZ.js"; // src/input/link/LinkField.browser.tsx import { Field } from "alinea/core"; 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 { Create } from "alinea/dashboard/view/Create"; import { IconButton } from "alinea/dashboard/view/IconButton"; import { InputLabel } from "alinea/dashboard/view/InputLabel"; import { TextLabel, fromModule } from "alinea/ui"; import { Sink } from "alinea/ui/Sink"; 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 { Suspense, useState } from "react"; import { createLink as createLinkField, createLinks as createLinksField } from "./LinkField.js"; // src/input/link/LinkInput.module.scss var LinkInput_module_default = { "root": "alinea-LinkInput", "root-inner": "alinea-LinkInput-inner", "rootInner": "alinea-LinkInput-inner", "row": "alinea-LinkInput-row", "is-dragging": "alinea-LinkInput-is-dragging", "isDragging": "alinea-LinkInput-is-dragging", "is-overlay": "alinea-LinkInput-is-overlay", "isOverlay": "alinea-LinkInput-is-overlay", "row-header": "alinea-LinkInput-row-header", "rowHeader": "alinea-LinkInput-row-header", "row-staticHandle": "alinea-LinkInput-row-staticHandle", "rowStaticHandle": "alinea-LinkInput-row-staticHandle", "create": "alinea-LinkInput-create" }; // src/input/link/LinkField.browser.tsx import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var styles = fromModule(LinkInput_module_default); var createLink = Field.provideView(LinkInput, createLinkField); var createLinks = Field.provideView(LinksInput, createLinksField); function LinkInput({ field }) { const { options, value, mutator, label } = useField(field); const { width, inline, optional, help } = options; const [pickFrom, setPickFrom] = useState(); const picker = pickFrom ? options.pickers[pickFrom] : void 0; function handleConfirm(link) { const selected = link[0]; if (!pickFrom || !picker || !selected) return; mutator.replace(selected); setPickFrom(void 0); } const PickerView = picker && picker.view; return /* @__PURE__ */ jsxs(Fragment, { children: [ PickerView && /* @__PURE__ */ jsx( PickerView, { type: pickFrom, options: picker.options, selection: [value], onConfirm: handleConfirm, onCancel: () => setPickFrom(void 0) } ), /* @__PURE__ */ jsx( InputLabel, { label, help, optional, inline, width, 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.type] ? /* @__PURE__ */ jsx( LinkInputRow, { field, rowId: value.id, fields: options.pickers[value.type].fields, picker: options.pickers[value.type], reference: value, onRemove: () => mutator.replace(void 0), onEdit: () => setPickFrom(value.type) } ) : /* @__PURE__ */ jsx("div", { className: styles.create(), children: /* @__PURE__ */ jsx(Create.Root, { children: entries(options.pickers).map(([name, picker2]) => { return /* @__PURE__ */ jsx( Create.Button, { onClick: () => setPickFrom(name), children: /* @__PURE__ */ jsx(TextLabel, { label: picker2.label }) }, name ); }) }) }) }) }) }) } ) ] }); } var layoutMeasuringConfig = { strategy: LayoutMeasuringStrategy.Always }; function LinksInput({ field }) { const { options, value, mutator, label } = useField(field); const { width, inline, optional, help } = options; const [pickFrom, setPickFrom] = useState(); const picker = pickFrom ? options.pickers[pickFrom.type] : void 0; function handleConfirm(links) { if (!pickFrom || !picker || !links) return; const seen = /* @__PURE__ */ new Set(); for (const link of links) { if (link.type !== pickFrom.type) continue; seen.add(link.id); const index = value.findIndex((v) => v.id === link.id); if (index > -1) mutator.replace(link.id, link); else mutator.push(link); } if (picker.handlesMultiple) for (const link of value) { if (link.type !== pickFrom.type) continue; if (seen.has(link.id)) continue; mutator.remove(link.id); } setPickFrom(void 0); } const ids = value.map((row) => row.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.id === active.id) || null); } function handleDragEnd(event) { const { active, over } = event; if (!over || active.id === over.id) return; 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( PickerView, { type: pickFrom.type, options: picker.options, selection: value.filter((ref) => { if (ref.id === pickFrom.id) return true; if (picker.handlesMultiple) return ref.type === pickFrom.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, { label, help, optional, inline, width, 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.type]) return null; const type = options.pickers[reference.type].fields; return /* @__PURE__ */ jsx( LinkInputRowSortable, { rowId: reference.id, field, fields: type, picker: options.pickers[reference.type], reference, onRemove: () => mutator.remove(reference.id), onEdit: () => setPickFrom(reference), isSortable: options.max !== 1 }, reference.id ); }), showLinkPicker && /* @__PURE__ */ jsx("div", { className: styles.create(), children: /* @__PURE__ */ jsx(Create.Root, { children: entries(options.pickers).map(([name, picker2]) => { return /* @__PURE__ */ jsx( Create.Button, { onClick: () => setPickFrom({ type: name }), children: /* @__PURE__ */ jsx(TextLabel, { label: picker2.label }) }, name ); }) }) }) ] }) } ), /* @__PURE__ */ jsx( DragOverlay, { dropAnimation: { ...defaultDropAnimation, dragSourceOpacity: 0.5 }, children: dragging && options.pickers[dragging.type] ? /* @__PURE__ */ jsx( LinkInputRow, { field, rowId: dragging.id, fields: options.pickers[dragging.type].fields, picker: options.pickers[dragging.type], reference: dragging, onRemove: () => mutator.remove(dragging.id), onEdit: () => setPickFrom(dragging), isDragOverlay: true, isSortable: options.max !== 1 } ) : 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, ...rest }) { 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" } } ) : /* @__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 }) }) }), /* @__PURE__ */ jsxs(Sink.Options, { children: [ /* @__PURE__ */ jsx(IconButton, { icon: IcRoundEdit, onClick: onEdit }), /* @__PURE__ */ jsx(IconButton, { icon: IcRoundClose, onClick: onRemove }) ] }) ] }), fields && /* @__PURE__ */ jsx(Sink.Content, { children: /* @__PURE__ */ jsx(InputForm, { type: fields }) }) ] } ); if (!fields) return inner; return /* @__PURE__ */ jsx(FormRow, { field, type: fields, children: inner }); } export { createLink, createLinks };