alinea
Version:
Headless git-based CMS
441 lines (437 loc) • 14.7 kB
JavaScript
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 {
atomWithStorage
} from "../../chunks/chunk-WDCPVJJC.js";
import {
useAtom
} from "../../chunks/chunk-TOJF2G3X.js";
import "../../chunks/chunk-WJ67RR7S.js";
import "../../chunks/chunk-NZLE2WMY.js";
// src/field/list/ListField.view.tsx
import { getType } from "alinea/core/Internal";
import { ListRow } from "alinea/core/shape/ListShape";
import { Type } from "alinea/core/Type";
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 { Icon, TextLabel } from "alinea/ui";
import { IcBaselineContentCopy } from "alinea/ui/icons/IcBaselineContentCopy";
import { IcBaselineContentPasteGo } from "alinea/ui/icons/IcBaselineContentPasteGo";
import { IcOutlineList } from "alinea/ui/icons/IcOutlineList";
import { IcRoundAdd } from "alinea/ui/icons/IcRoundAdd";
import { IcRoundArrowDownward } from "alinea/ui/icons/IcRoundArrowDownward";
import { IcRoundArrowUpward } from "alinea/ui/icons/IcRoundArrowUpward";
import { IcRoundClose } from "alinea/ui/icons/IcRoundClose";
import { IcRoundDragHandle } from "alinea/ui/icons/IcRoundDragHandle";
import { IcRoundKeyboardArrowDown } from "alinea/ui/icons/IcRoundKeyboardArrowDown";
import { IcRoundKeyboardArrowUp } from "alinea/ui/icons/IcRoundKeyboardArrowUp";
import { Sink } from "alinea/ui/Sink";
import {
useState
} from "react";
// src/field/list/ListField.module.scss
var ListField_module_default = {
"root": "alinea-ListField",
"root-inner": "alinea-ListField-inner",
"rootInner": "alinea-ListField-inner",
"row": "alinea-ListField-row",
"is-dragging": "alinea-ListField-is-dragging",
"isDragging": "alinea-ListField-is-dragging",
"is-overlay": "alinea-ListField-is-overlay",
"isOverlay": "alinea-ListField-is-overlay",
"row-header-title": "alinea-ListField-row-header-title",
"rowHeaderTitle": "alinea-ListField-row-header-title",
"insert": "alinea-ListField-insert",
"is-first": "alinea-ListField-is-first",
"isFirst": "alinea-ListField-is-first",
"is-open": "alinea-ListField-is-open",
"isOpen": "alinea-ListField-is-open",
"insert-icon": "alinea-ListField-insert-icon",
"insertIcon": "alinea-ListField-insert-icon",
"create": "alinea-ListField-create",
"is-inline": "alinea-ListField-is-inline",
"isInline": "alinea-ListField-is-inline"
};
// src/field/list/ListField.view.tsx
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
var styles = dist_default(ListField_module_default);
var copyAtom = atomWithStorage(
"@alinea/copypaste",
void 0
);
function animateLayoutChanges(args) {
const { isSorting, wasSorting } = args;
if (isSorting || wasSorting) return defaultAnimateLayoutChanges(args);
return true;
}
function ListInputRowSortable(props) {
const { onCreate, onPaste } = props;
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
animateLayoutChanges,
id: props.row._id
});
const style = {
transform: CSS.Transform.toString(transform),
transition: transition || void 0
};
return /* @__PURE__ */ jsx(
ListInputRow,
{
...props,
rootRef: setNodeRef,
style,
handle: listeners,
...attributes,
isDragging,
onCreate,
onPaste
}
);
}
function ListInputRow({
row,
schema,
onMove,
onDelete,
onCopyBlock,
handle,
rootRef,
isDragging,
isDragOverlay,
readOnly,
onCreate,
onPasteBlock,
firstRow,
lastRow,
isFolded,
toggleFold,
...rest
}) {
const type = schema[row[ListRow.type]];
const [showInsert, setShowInsert] = useState(false);
if (!type) return null;
return /* @__PURE__ */ jsxs(
"div",
{
className: styles.row({ dragging: isDragging, overlay: isDragOverlay }),
ref: rootRef,
...rest,
children: [
!readOnly && !isDragOverlay && /* @__PURE__ */ jsx(
ListInsertRow,
{
open: showInsert,
first: Boolean(firstRow),
onInsert: () => setShowInsert(!showInsert)
}
),
showInsert && /* @__PURE__ */ jsx(
ListCreateRow,
{
inline: true,
schema,
onCreate: (type2) => {
onCreate(type2);
setShowInsert(false);
},
onPaste: (data) => {
if (onPasteBlock) onPasteBlock(data);
setShowInsert(false);
}
}
),
/* @__PURE__ */ jsxs(Sink.Header, { children: [
/* @__PURE__ */ jsx(Sink.Options, { style: { zIndex: 1 }, children: /* @__PURE__ */ jsx(
IconButton,
{
icon: getType(type).icon || IcRoundDragHandle,
...handle,
style: { cursor: handle ? "grab" : "grabbing" },
title: "Drag and drop to reorder"
}
) }),
/* @__PURE__ */ jsx(Sink.Title, { children: /* @__PURE__ */ jsx(
TextLabel,
{
label: Type.label(type),
className: styles.row.header.title()
}
) }),
/* @__PURE__ */ jsxs(Sink.Options, { children: [
isFolded !== void 0 && /* @__PURE__ */ jsx(
IconButton,
{
icon: isFolded ? IcRoundKeyboardArrowDown : IcRoundKeyboardArrowUp,
onClick: toggleFold,
title: isFolded ? "Expand" : "Fold",
disabled: isDragOverlay
}
),
onCopyBlock !== void 0 && /* @__PURE__ */ jsx(
IconButton,
{
icon: IcBaselineContentCopy,
onClick: () => onCopyBlock(),
title: "Copy block",
disabled: isDragOverlay
}
),
!readOnly && /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
IconButton,
{
icon: IcRoundArrowUpward,
onClick: () => onMove?.(-1),
title: "Move up one position",
disabled: firstRow || isDragOverlay
}
),
/* @__PURE__ */ jsx(
IconButton,
{
icon: IcRoundArrowDownward,
onClick: () => onMove?.(1),
title: "Move down one position",
disabled: lastRow || isDragOverlay
}
),
/* @__PURE__ */ jsx(
IconButton,
{
icon: IcRoundClose,
onClick: onDelete,
title: "Delete block",
disabled: isDragOverlay
}
)
] })
] })
] }),
!isFolded && /* @__PURE__ */ jsx(Sink.Content, { children: /* @__PURE__ */ jsx(InputForm, { type }) })
]
}
);
}
function ListCreateRow({
schema,
readOnly,
inline,
onCreate,
onPaste
}) {
const [pasted] = useAtom(copyAtom);
const canPaste = pasted && entries(schema).some(([key]) => key === pasted._type);
return /* @__PURE__ */ jsx("div", { className: styles.create({ inline }), children: /* @__PURE__ */ jsxs(Create.Root, { disabled: readOnly, children: [
entries(schema).map(([key, type]) => {
return /* @__PURE__ */ jsx(
Create.Button,
{
icon: getType(type).icon,
onClick: () => onCreate(key),
children: /* @__PURE__ */ jsx(TextLabel, { label: Type.label(type) })
},
key
);
}),
canPaste && /* @__PURE__ */ jsx(
Create.Button,
{
icon: IcBaselineContentPasteGo,
onClick: () => onPaste(pasted),
mod: "paste",
children: /* @__PURE__ */ jsx(TextLabel, { label: "Paste block" })
}
)
] }) });
}
function ListInsertRow({ first, open, onInsert }) {
return /* @__PURE__ */ jsx("div", { className: styles.insert({ open, first }), children: /* @__PURE__ */ jsx(
"button",
{
className: styles.insert.icon(),
onClick: onInsert,
title: "Insert new block",
type: "button",
children: /* @__PURE__ */ jsx(Icon, { icon: open ? IcRoundKeyboardArrowUp : IcRoundAdd })
}
) });
}
var layoutMeasuringConfig = {
strategy: LayoutMeasuringStrategy.Always
};
function ListInput({ field }) {
const { options, value, mutator, error } = useField(field);
const { schema, readOnly } = options;
const rows = value;
const ids = rows.map((row) => row._id);
const [, setPasted] = useAtom(copyAtom);
const [dragging, setDragging] = useState(null);
const [foldedItems, setFoldedItems] = useState(/* @__PURE__ */ new Set());
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates
})
);
function handleDragStart(event) {
const { active } = event;
setDragging(rows.find((row) => row._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 isFolded = rows.every((row) => foldedItems.has(row._id));
const toggleFold = () => setFoldedItems(new Set(isFolded ? [] : rows.map((row) => row._id)));
return /* @__PURE__ */ jsx(
DndContext,
{
sensors,
collisionDetection: closestCenter,
onDragStart: handleDragStart,
onDragEnd: handleDragEnd,
layoutMeasuring: layoutMeasuringConfig,
children: /* @__PURE__ */ jsx(
InputLabel,
{
...options,
error,
icon: IcOutlineList,
isFolded: rows.length === 0 ? void 0 : isFolded,
toggleFold,
children: /* @__PURE__ */ jsx("div", { className: styles.root(), children: /* @__PURE__ */ jsxs("div", { className: styles.root.inner({ inline: options.inline }), children: [
/* @__PURE__ */ jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsxs(Sink.Root, { children: [
rows.map((row, i) => {
const type = options.schema[row[ListRow.type]];
return /* @__PURE__ */ jsx(
FormRow,
{
field,
rowId: row._id,
type,
readOnly,
children: /* @__PURE__ */ jsx(
ListInputRowSortable,
{
isFolded: foldedItems.has(row._id),
row,
schema,
readOnly,
onCopyBlock: () => {
const data = mutator.read(row._id);
setPasted(data);
},
onMove: (direction) => {
if (readOnly) return;
mutator.move(i, i + direction);
},
onDelete: () => {
if (readOnly) return;
mutator.remove(row._id);
},
onCreate: (type2) => {
if (readOnly) return;
mutator.push({ _type: type2 }, i);
},
onPasteBlock: (data) => {
if (readOnly) return;
const { _id, _index, ...rest } = data;
mutator.push(rest, i);
},
toggleFold: () => {
setFoldedItems((current) => {
const result = new Set(current);
if (result.has(row._id)) result.delete(row._id);
else result.add(row._id);
return result;
});
},
firstRow: i === 0,
lastRow: i === rows.length - 1
}
)
},
row._id
);
}),
/* @__PURE__ */ jsx(
ListCreateRow,
{
schema,
readOnly,
onPaste: (data) => {
const { _id, _index, ...rest } = data;
mutator.push(rest);
},
onCreate: (type, data) => {
if (readOnly) return;
mutator.push({ _type: type });
}
}
)
] }) }),
/* @__PURE__ */ jsx(
DragOverlay,
{
dropAnimation: {
...defaultDropAnimation,
dragSourceOpacity: 0.5
},
children: dragging ? /* @__PURE__ */ jsx(
FormRow,
{
field,
rowId: dragging._id,
type: options.schema[dragging[ListRow.type]],
readOnly,
children: /* @__PURE__ */ jsx(
ListInputRow,
{
row: dragging,
schema,
isDragOverlay: true,
isFolded: foldedItems.has(dragging._id),
onCopyBlock: () => {
}
},
"overlay"
)
},
"overlay"
) : null
}
)
] }) })
}
)
}
);
}
export {
ListInput
};