96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
|
import { ListItem as ListItemType } from '@reactive-resume/schema';
|
||
|
import clsx from 'clsx';
|
||
|
import get from 'lodash/get';
|
||
|
import isArray from 'lodash/isArray';
|
||
|
import isEmpty from 'lodash/isEmpty';
|
||
|
import isFunction from 'lodash/isFunction';
|
||
|
import { useTranslation } from 'next-i18next';
|
||
|
import { useCallback } from 'react';
|
||
|
import { DndProvider } from 'react-dnd';
|
||
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||
|
|
||
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||
|
import { deleteItem, setResumeState } from '@/store/resume/resumeSlice';
|
||
|
|
||
|
import styles from './List.module.scss';
|
||
|
import ListItem from './ListItem';
|
||
|
|
||
|
type Props = {
|
||
|
path: string;
|
||
|
titleKey?: string;
|
||
|
subtitleKey?: string;
|
||
|
onEdit?: (item: ListItemType) => void;
|
||
|
onDuplicate?: (item: ListItemType) => void;
|
||
|
className?: string;
|
||
|
};
|
||
|
|
||
|
const List: React.FC<Props> = ({
|
||
|
path,
|
||
|
titleKey = 'title',
|
||
|
subtitleKey = 'subtitle',
|
||
|
onEdit,
|
||
|
onDuplicate,
|
||
|
className,
|
||
|
}) => {
|
||
|
const { t } = useTranslation();
|
||
|
|
||
|
const dispatch = useAppDispatch();
|
||
|
|
||
|
const list: Array<ListItemType> = useAppSelector((state) => get(state.resume, path, []));
|
||
|
|
||
|
const handleEdit = (item: ListItemType) => {
|
||
|
isFunction(onEdit) && onEdit(item);
|
||
|
};
|
||
|
|
||
|
const handleDuplicate = (item: ListItemType) => {
|
||
|
isFunction(onDuplicate) && onDuplicate(item);
|
||
|
};
|
||
|
|
||
|
const handleDelete = (item: ListItemType) => {
|
||
|
dispatch(deleteItem({ path, value: item }));
|
||
|
};
|
||
|
|
||
|
const handleMove = useCallback(
|
||
|
(dragIndex: number, hoverIndex: number) => {
|
||
|
const dragItem = list[dragIndex];
|
||
|
const newList = [...list];
|
||
|
|
||
|
newList.splice(dragIndex, 1);
|
||
|
newList.splice(hoverIndex, 0, dragItem);
|
||
|
|
||
|
dispatch(setResumeState({ path, value: newList }));
|
||
|
},
|
||
|
[list, dispatch, path]
|
||
|
);
|
||
|
|
||
|
return (
|
||
|
<DndProvider backend={HTML5Backend}>
|
||
|
<div className={clsx(styles.container, className)}>
|
||
|
{isEmpty(list) && <div className={styles.empty}>{t('builder.common.list.empty-text')}</div>}
|
||
|
|
||
|
{list.map((item, index) => {
|
||
|
const title = get(item, titleKey, '');
|
||
|
const subtitleObj = get(item, subtitleKey);
|
||
|
const subtitle: string = isArray(subtitleObj) ? subtitleObj.join(', ') : subtitleObj;
|
||
|
|
||
|
return (
|
||
|
<ListItem
|
||
|
key={item.id}
|
||
|
item={item}
|
||
|
index={index}
|
||
|
title={title}
|
||
|
subtitle={subtitle}
|
||
|
onMove={handleMove}
|
||
|
onEdit={handleEdit}
|
||
|
onDelete={handleDelete}
|
||
|
onDuplicate={handleDuplicate}
|
||
|
/>
|
||
|
);
|
||
|
})}
|
||
|
</div>
|
||
|
</DndProvider>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default List;
|