import React from 'react' import nanoid from 'nanoid' import {SortableHandle, SortableContainer, SortableElement} from 'react-sortable-hoc' import arrayMove from 'array-move' import {isFunctionalUpdate, cssRule, useStyle} from '@karma.run/react' import {MaterialIconDeleteOutlined, MaterialIconDragIndicator} from '@karma.run/icons' import {IconButton} from '../buttons/iconButton' import {Box} from '../layout/box' import {Spacing, ZIndex} from '../style/helpers' import {Card} from '../data/card' import {AddBlockButton} from '../buttons/addBlockButton' export interface FieldProps { readonly value: V readonly onChange: React.Dispatch> } export type FieldConstructorFn = (props: FieldProps) => JSX.Element export interface ListFieldProps extends FieldProps[]> { readonly label?: string readonly defaultValue: T | (() => T) readonly disabled?: boolean readonly children: (props: FieldProps) => JSX.Element } export interface ListValue { readonly id: string readonly value: T } export interface ListItemProps { readonly value: ListValue readonly itemIndex: number readonly itemDisabled?: boolean readonly onChange: (index: number, value: React.SetStateAction>) => void readonly onRemove: (index: number) => void readonly children: (props: FieldProps) => JSX.Element } const DragHandle = SortableHandle(({disabled}: {disabled?: boolean}) => ( )) const ListItem = SortableElement( ({value, itemIndex, itemDisabled, onChange, onRemove, children}: ListItemProps) => { function handleValueChange(fieldValue: React.SetStateAction) { onChange(itemIndex, value => ({ ...value, value: isFunctionalUpdate(fieldValue) ? fieldValue(value.value) : fieldValue })) } function handleRemove() { onRemove(itemIndex) } return ( {children({value: value.value, onChange: handleValueChange})} ) } ) const SortableList = SortableContainer( ({value, defaultValue, disabled, children, onChange}: ListFieldProps) => { function handleItemChange(itemIndex: number, itemValue: React.SetStateAction) { onChange(value => Object.assign([], value, { [itemIndex]: isFunctionalUpdate(itemValue) ? itemValue(value[itemIndex]) : itemValue }) ) } function handleAdd() { onChange(value => [...value, {id: nanoid(), value: defaultValue}]) } function handleRemove(itemIndex: number) { onChange(value => value.filter((_, index) => index !== itemIndex)) } return ( {value.map((value: any, index: number) => ( {children} ))} ) } ) const ListItemHelperStyle = cssRule({ zIndex: ZIndex.DragHelper }) export function ListInput({ value, label, defaultValue, disabled, children, onChange }: ListFieldProps) { const css = useStyle() const onSortEnd = ({oldIndex, newIndex}: {oldIndex: number; newIndex: number}) => { onChange(arrayMove(value, oldIndex, newIndex)) } return ( {label && } ) }