alinea
Version:
[](https://npmjs.org/package/alinea) [](https://packagephobia.com/result?p=alinea)
359 lines (355 loc) • 12.2 kB
JavaScript
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
};