mirror of
https://github.com/pawelmalak/flame.git
synced 2025-07-24 21:39:36 +02:00
Sorting and custom ordering for categories
This commit is contained in:
parent
5b900872af
commit
ae3141e37b
11 changed files with 302 additions and 60 deletions
|
@ -1,13 +1,25 @@
|
|||
import { ContentType } from '../Bookmarks';
|
||||
import classes from './BookmarkTable.module.css';
|
||||
import { connect } from 'react-redux';
|
||||
import { pinCategory, deleteCategory, deleteBookmark } from '../../../store/actions';
|
||||
import { KeyboardEvent } from 'react';
|
||||
import { KeyboardEvent, useState, useEffect, Fragment } from 'react';
|
||||
import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// Redux
|
||||
import { connect } from 'react-redux';
|
||||
import { pinCategory, deleteCategory, deleteBookmark, createNotification, reorderCategories } from '../../../store/actions';
|
||||
|
||||
// Typescript
|
||||
import { Bookmark, Category, NewNotification } from '../../../interfaces';
|
||||
import { ContentType } from '../Bookmarks';
|
||||
|
||||
// CSS
|
||||
import classes from './BookmarkTable.module.css';
|
||||
|
||||
// UI
|
||||
import Table from '../../UI/Table/Table';
|
||||
import { Bookmark, Category } from '../../../interfaces';
|
||||
import Icon from '../../UI/Icons/Icon/Icon';
|
||||
|
||||
// Utils
|
||||
import { searchConfig } from '../../../utility';
|
||||
|
||||
interface ComponentProps {
|
||||
contentType: ContentType;
|
||||
categories: Category[];
|
||||
|
@ -15,9 +27,28 @@ interface ComponentProps {
|
|||
deleteCategory: (id: number) => void;
|
||||
updateHandler: (data: Category | Bookmark) => void;
|
||||
deleteBookmark: (bookmarkId: number, categoryId: number) => void;
|
||||
createNotification: (notification: NewNotification) => void;
|
||||
reorderCategories: (categories: Category[]) => void;
|
||||
}
|
||||
|
||||
const BookmarkTable = (props: ComponentProps): JSX.Element => {
|
||||
const [localCategories, setLocalCategories] = useState<Category[]>([]);
|
||||
const [isCustomOrder, setIsCustomOrder] = useState<boolean>(false);
|
||||
|
||||
// Copy categories array
|
||||
useEffect(() => {
|
||||
setLocalCategories([...props.categories]);
|
||||
}, [props.categories])
|
||||
|
||||
// Check ordering
|
||||
useEffect(() => {
|
||||
const order = searchConfig('useOrdering', '');
|
||||
|
||||
if (order === 'orderId') {
|
||||
setIsCustomOrder(true);
|
||||
}
|
||||
})
|
||||
|
||||
const deleteCategoryHandler = (category: Category): void => {
|
||||
const proceed = window.confirm(`Are you sure you want to delete ${category.name}? It will delete ALL assigned bookmarks`);
|
||||
|
||||
|
@ -40,46 +71,100 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
|
|||
}
|
||||
}
|
||||
|
||||
const dragEndHanlder = (result: DropResult): void => {
|
||||
if (!isCustomOrder) {
|
||||
props.createNotification({
|
||||
title: 'Error',
|
||||
message: 'Custom order is disabled'
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tmpCategories = [...localCategories];
|
||||
const [movedApp] = tmpCategories.splice(result.source.index, 1);
|
||||
tmpCategories.splice(result.destination.index, 0, movedApp);
|
||||
|
||||
setLocalCategories(tmpCategories);
|
||||
props.reorderCategories(tmpCategories);
|
||||
}
|
||||
|
||||
if (props.contentType === ContentType.category) {
|
||||
return (
|
||||
<Table headers={[
|
||||
'Name',
|
||||
'Actions'
|
||||
]}>
|
||||
{props.categories.map((category: Category) => {
|
||||
return (
|
||||
<tr key={category.id}>
|
||||
<td>{category.name}</td>
|
||||
<td className={classes.TableActions}>
|
||||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => deleteCategoryHandler(category)}
|
||||
onKeyDown={(e) => keyboardActionHandler(e, category, deleteCategoryHandler)}
|
||||
tabIndex={0}>
|
||||
<Icon icon='mdiDelete' />
|
||||
</div>
|
||||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => props.updateHandler(category)}
|
||||
// onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
|
||||
tabIndex={0}>
|
||||
<Icon icon='mdiPencil' />
|
||||
</div>
|
||||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => props.pinCategory(category)}
|
||||
onKeyDown={(e) => keyboardActionHandler(e, category, props.pinCategory)}
|
||||
tabIndex={0}>
|
||||
{category.isPinned
|
||||
? <Icon icon='mdiPinOff' color='var(--color-accent)' />
|
||||
: <Icon icon='mdiPin' />
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</Table>
|
||||
<Fragment>
|
||||
<div className={classes.Message}>
|
||||
{isCustomOrder
|
||||
? <p>You can drag and drop single rows to reorder categories</p>
|
||||
: <p>Custom order is disabled. You can change it in <Link to='/settings/other'>settings</Link></p>
|
||||
}
|
||||
</div>
|
||||
<DragDropContext onDragEnd={dragEndHanlder}>
|
||||
<Droppable droppableId='categories'>
|
||||
{(provided) => (
|
||||
<Table headers={[
|
||||
'Name',
|
||||
'Actions'
|
||||
]}
|
||||
innerRef={provided.innerRef}>
|
||||
{localCategories.map((category: Category, index): JSX.Element => {
|
||||
return (
|
||||
<Draggable key={category.id} draggableId={category.id.toString()} index={index}>
|
||||
{(provided, snapshot) => {
|
||||
const style = {
|
||||
border: snapshot.isDragging ? '1px solid var(--color-accent)' : 'none',
|
||||
borderRadius: '4px',
|
||||
...provided.draggableProps.style,
|
||||
};
|
||||
|
||||
return (
|
||||
<tr
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
style={style}
|
||||
>
|
||||
<td>{category.name}</td>
|
||||
{!snapshot.isDragging && (
|
||||
<td className={classes.TableActions}>
|
||||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => deleteCategoryHandler(category)}
|
||||
onKeyDown={(e) => keyboardActionHandler(e, category, deleteCategoryHandler)}
|
||||
tabIndex={0}>
|
||||
<Icon icon='mdiDelete' />
|
||||
</div>
|
||||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => props.updateHandler(category)}
|
||||
tabIndex={0}>
|
||||
<Icon icon='mdiPencil' />
|
||||
</div>
|
||||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => props.pinCategory(category)}
|
||||
onKeyDown={(e) => keyboardActionHandler(e, category, props.pinCategory)}
|
||||
tabIndex={0}>
|
||||
{category.isPinned
|
||||
? <Icon icon='mdiPinOff' color='var(--color-accent)' />
|
||||
: <Icon icon='mdiPin' />
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
)
|
||||
}}
|
||||
</Draggable>
|
||||
)
|
||||
})}
|
||||
</Table>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</Fragment>
|
||||
)
|
||||
} else {
|
||||
const bookmarks: {bookmark: Bookmark, categoryName: string}[] = [];
|
||||
|
@ -111,14 +196,12 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
|
|||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => deleteBookmarkHandler(bookmark.bookmark)}
|
||||
// onKeyDown={(e) => keyboardActionHandler(e, app, deleteAppHandler)}
|
||||
tabIndex={0}>
|
||||
<Icon icon='mdiDelete' />
|
||||
</div>
|
||||
<div
|
||||
className={classes.TableAction}
|
||||
onClick={() => props.updateHandler(bookmark.bookmark)}
|
||||
// onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
|
||||
tabIndex={0}>
|
||||
<Icon icon='mdiPencil' />
|
||||
</div>
|
||||
|
@ -131,4 +214,12 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
|
|||
}
|
||||
}
|
||||
|
||||
export default connect(null, { pinCategory, deleteCategory, deleteBookmark })(BookmarkTable);
|
||||
const actions = {
|
||||
pinCategory,
|
||||
deleteCategory,
|
||||
deleteBookmark,
|
||||
createNotification,
|
||||
reorderCategories
|
||||
}
|
||||
|
||||
export default connect(null, actions)(BookmarkTable);
|
Loading…
Add table
Add a link
Reference in a new issue