@coveord/plasma-mantine
Version:
A Plasma flavoured Mantine theme
205 lines (204 loc) • 8.31 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers';
import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { Box, Input, Stack, useProps, useStyles } from '@mantine/core';
import { useDidUpdate } from '@mantine/hooks';
import { identity } from '../../utils/createFactoryComponent.js';
import classes from './Collection.module.css';
import { CollectionAddButton } from './CollectionAddButton.js';
import { CollectionProvider } from './CollectionContext.js';
import { CollectionItem } from './CollectionItem.js';
import { CollectionLayouts } from './layouts/CollectionLayouts.js';
const defaultProps = {
draggable: false,
addLabel: 'Add item',
addDisabledTooltip: 'There is already an empty item',
disabled: false,
readOnly: false,
gap: 'md',
required: false,
getItemId: ({ id })=>id
};
export const Collection = (props)=>{
const { value, onChange, onRemoveItem, onReorderItem, onInsertItem, disabled, readOnly, draggable, children, columns, layout, gap, required, newItem, addLabel, addDisabledTooltip, allowAdd, label, labelProps, withAsterisk, description, descriptionProps, error, errorProps, getItemId, ref, // Style props
style, className, classNames, styles, unstyled, ...others } = useProps('Collection', defaultProps, props);
// Runtime validation: ensure columns and children are mutually exclusive
if (columns && children) {
throw new Error('Collection: Cannot use both "columns" and "children" props.');
}
if (layout && !columns) {
throw new Error('Collection: "layout" prop can only be used with "columns" prop.');
}
const getStyles = useStyles({
name: 'Collection',
classes,
props,
className,
style,
classNames,
styles,
unstyled
});
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates
}));
const canEdit = !disabled && !readOnly;
const items = value ?? [];
const hasOnlyOneItem = items.length === 1;
/**
* Enforcing onChange when the value is modified will make sure the errors are carried through.
*/ useDidUpdate(()=>{
onChange?.(items);
}, [
JSON.stringify(items)
]);
const isRequired = typeof withAsterisk === 'boolean' ? withAsterisk : required;
const _label = label ? /*#__PURE__*/ _jsx(Input.Label, {
required: isRequired,
...labelProps,
children: label
}) : null;
const _description = description ? /*#__PURE__*/ _jsx(Input.Description, {
...descriptionProps,
children: description
}) : null;
const _error = error ? /*#__PURE__*/ _jsx(Input.Error, {
...errorProps,
pt: "xs",
children: error
}) : null;
const _header = _label || _description ? /*#__PURE__*/ _jsxs(Stack, {
gap: "xxs",
pb: "xs",
children: [
_label,
_description
]
}) : null;
const standardizedItems = items.map((item, index)=>({
id: getItemId?.(item, index) ?? String(index),
data: item
}));
const getIndex = (id)=>standardizedItems.findIndex((item)=>item.id === id);
const handleDragEnd = ({ over, active })=>{
if (over) {
const activeIndex = getIndex(String(active.id));
const overIndex = getIndex(String(over.id));
if (activeIndex !== overIndex) {
onReorderItem?.({
from: activeIndex,
to: overIndex
});
}
}
};
const addAllowed = typeof allowAdd === 'boolean' ? allowAdd : allowAdd?.(items) ?? true;
const handleAdd = ()=>{
const newItemValue = typeof newItem === 'function' ? newItem() : newItem;
onInsertItem?.(newItemValue, items?.length ?? 0);
};
const _addButton = canEdit ? /*#__PURE__*/ _jsx(CollectionAddButton, {
addLabel: addLabel,
addDisabledTooltip: addDisabledTooltip,
addAllowed: addAllowed,
onAdd: handleAdd
}) : null;
// Column-based layout pattern
if (columns) {
const Layout = layout || CollectionLayouts.Horizontal;
return /*#__PURE__*/ _jsx(CollectionProvider, {
value: {
getStyles,
columns: columns
},
children: /*#__PURE__*/ _jsx(DndContext, {
onDragEnd: handleDragEnd,
sensors: sensors,
modifiers: [
restrictToVerticalAxis,
restrictToParentElement
],
children: /*#__PURE__*/ _jsx(SortableContext, {
items: standardizedItems,
strategy: verticalListSortingStrategy,
children: /*#__PURE__*/ _jsxs(Box, {
ref: ref,
...others,
...getStyles('root'),
children: [
_header,
/*#__PURE__*/ _jsxs(Layout, {
children: [
/*#__PURE__*/ _jsx(Layout.Header, {
draggable: draggable && canEdit,
removable: canEdit && !(isRequired && hasOnlyOneItem)
}),
/*#__PURE__*/ _jsx(Layout.Body, {
items: items,
onRemove: canEdit ? onRemoveItem : undefined,
removable: canEdit && !(isRequired && hasOnlyOneItem),
draggable: draggable && canEdit,
disabled: disabled,
readOnly: readOnly,
getItemId: getItemId,
gap: gap
})
]
}),
_addButton,
_error
]
})
})
})
});
}
// Legacy children render prop pattern
const renderedItems = standardizedItems.map((item, index)=>/*#__PURE__*/ _jsx(CollectionItem, {
id: item.id,
disabled: !canEdit,
draggable: draggable,
onRemove: ()=>onRemoveItem?.(index),
removable: !(isRequired && hasOnlyOneItem),
children: children(item.data, index)
}, item.id));
return /*#__PURE__*/ _jsx(CollectionProvider, {
value: {
getStyles
},
children: /*#__PURE__*/ _jsx(DndContext, {
onDragEnd: handleDragEnd,
sensors: sensors,
modifiers: [
restrictToVerticalAxis,
restrictToParentElement
],
children: /*#__PURE__*/ _jsx(SortableContext, {
items: standardizedItems,
strategy: verticalListSortingStrategy,
children: /*#__PURE__*/ _jsxs(Box, {
ref: ref,
...others,
...getStyles('root'),
children: [
_header,
/*#__PURE__*/ _jsxs(Stack, {
gap: gap,
...getStyles('items'),
children: [
renderedItems,
_addButton
]
}),
_error
]
})
})
})
});
};
Collection.displayName = 'Collection';
Collection.extend = identity;
Collection.Layouts = CollectionLayouts;
//# sourceMappingURL=Collection.js.map