Skip to content

Commit

Permalink
feat: #463 sortable project links
Browse files Browse the repository at this point in the history
  • Loading branch information
dmijatovic committed Sep 30, 2022
1 parent cf8cbbf commit 5f41058
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 61 deletions.
6 changes: 3 additions & 3 deletions frontend/components/layout/SortableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export default function SortableList<T extends RequiredProps>({
useSensor(MouseSensor,{
// required to enable click events
// on draggable items with buttons
activationConstraint: {
distance: 8,
}
// activationConstraint: {
// distance: 8,
// }
})
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,15 @@
import {useState} from 'react'

import Button from '@mui/material/Button'
import List from '@mui/material/List'
import ListItem from '@mui/material/ListItem'
import ListItemText from '@mui/material/ListItemText'
import IconButton from '@mui/material/IconButton'
import DeleteIcon from '@mui/icons-material/Delete'
import EditIcon from '@mui/icons-material/Edit'

import {useSession} from '~/auth'
import useSnackbar from '~/components/snackbar/useSnackbar'
import {ProjectLink} from '~/types/Project'
import EditSectionTitle from '~/components/layout/EditSectionTitle'
import {deleteProjectLink} from '~/utils/editProject'
import {deleteProjectLink, patchProjectLinkPositions} from '~/utils/editProject'
import {projectInformation as config} from './config'
import ProjectLinkModal from './ProjectLinkModal'
import SortableProjectLinksList from './SortableProjectLinksList'

type ProjectLinksProps = {
project_id: string,
Expand All @@ -43,7 +38,7 @@ export default function AutosaveProjectLinks({project_id, url_for_project}: Proj
function addLink() {
const newLink = {
id: null,
position: links.length,
position: links.length + 1,
title: null,
url: null,
project: project_id
Expand All @@ -70,21 +65,39 @@ export default function AutosaveProjectLinks({project_id, url_for_project}: Proj
const item = links[pos]
if (item.id) {
const resp = await deleteProjectLink({
ids: [item.id],
id: item.id,
token
})
if (resp.status === 200) {
const items = [
// remove item
...links.slice(0, pos),
...links.slice(pos+1)
]
setLinks(items)
].map((item, pos) => {
// renumber positions
item.position = pos + 1
return item
})
// update links positions
sortedLinks(items)
} else {
showErrorMessage(`Failed to remove link. ${resp.message}`)
}
}
}

async function sortedLinks(links: ProjectLink[]) {
const resp = await patchProjectLinkPositions({
links,
token
})
if (resp.status === 200) {
setLinks(links)
} else {
showErrorMessage(`Failed to update project link positions. ${resp.message}`)
}
}

function updateLink({data, pos}: { data: ProjectLink, pos?: number }) {
if (typeof pos !== 'undefined') {
const items = links.map((item, i) => {
Expand Down Expand Up @@ -118,45 +131,14 @@ export default function AutosaveProjectLinks({project_id, url_for_project}: Proj
Add
</Button>
</EditSectionTitle>

<section>
<List>
{links.map((item, pos) => {
return(
<ListItem
key={item.id}
disableGutters
secondaryAction={
<>
<IconButton
edge="end"
aria-label="edit"
sx={{marginRight: '1rem'}}
onClick={() => {
editLink(pos)
}}
>
<EditIcon />
</IconButton>
<IconButton
edge="end"
aria-label="delete"
onClick={() => {
deleteLink(pos)
}}
>
<DeleteIcon />
</IconButton>
</>
}
>
<ListItemText
primary={item.title}
secondary={item.url}
/>
</ListItem>
)
})}
</List>
<SortableProjectLinksList
links={links}
onEdit={editLink}
onDelete={deleteLink}
onSorted={sortedLinks}
/>
</section>

<ProjectLinkModal
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import ListItem from '@mui/material/ListItem'
import ListItemText from '@mui/material/ListItemText'
import SortableListItemActions from '~/components/layout/SortableListItemActions'

import {useSortable} from '@dnd-kit/sortable'
import {CSS} from '@dnd-kit/utilities'
import {ProjectLink} from '~/types/Project'

type SortableProjectLinkProps = {
pos: number,
item: ProjectLink,
onEdit: (pos: number) => void,
onDelete: (pos: number) => void,
}


export default function SortableProjectLinksItem({pos, item, onEdit, onDelete}: SortableProjectLinkProps) {
const {
attributes,listeners,setNodeRef,
transform,transition,isDragging
} = useSortable({id: item.id ?? ''})
return(
<ListItem
// draggable
ref={setNodeRef}
{...attributes}
// disableGutters
secondaryAction={
<SortableListItemActions
pos={pos}
listeners={listeners}
onEdit={onEdit}
onDelete={onDelete}
/>
}
sx={{
// this makes space for buttons
paddingRight: '11rem',
// fixed height to avoid deformed item during dragging
// height:'4rem',
'&:hover': {
backgroundColor:'grey.100'
},
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
backgroundColor: isDragging ? 'grey.100' : 'paper',
zIndex: isDragging ? 9:0,
cursor: isDragging ? 'move' : 'default'
}}
>
<ListItemText
disableTypography
primary={
<a href={item.url ?? undefined}
target="_blank"
rel="noreferrer"
>
{item.title}
</a>
}
/>
</ListItem>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import SortableList from '~/components/layout/SortableList'
import {ProjectLink} from '~/types/Project'
import SortableProjectLinksItem from './SortableProjectLinkItem'

type SortableProjectLinksListProps = {
links: ProjectLink[],
onEdit: (pos: number) => void
onDelete: (pos: number) => void
onSorted: (link:ProjectLink[])=>void
}


export default function SortableProjectLinksList({links,onEdit,onDelete,onSorted}:SortableProjectLinksListProps) {

function onRenderItem(item:ProjectLink,index?:number) {
return <SortableProjectLinksItem
key={item.id ?? index}
pos={index ?? 0}
item={item}
onEdit={onEdit}
onDelete={onDelete}
/>
}

return (
<SortableList
items={links}
onSorted={onSorted}
onRenderItem={onRenderItem}
/>
)
}
22 changes: 13 additions & 9 deletions frontend/components/projects/edit/team/SortableTeamMemberItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ContributorAvatar from '~/components/software/ContributorAvatar'
import {combineRoleAndAffiliation, getDisplayInitials, getDisplayName} from '~/utils/getDisplayName'
import SortableListItemActions from '~/components/layout/SortableListItemActions'
import {TeamMember} from '~/types/Project'
import {useMediaQuery} from '@mui/material'

type TeamMemberProps = {
pos: number,
Expand All @@ -18,6 +19,7 @@ type TeamMemberProps = {

// Shell we move this component to separate file?
export default function SortableTeamMemberItem({pos, item, onEdit, onDelete}: TeamMemberProps) {
const smallScreen = useMediaQuery('(max-width:600px)')
const {
attributes,listeners,setNodeRef,
transform,transition,isDragging
Expand All @@ -44,7 +46,8 @@ export default function SortableTeamMemberItem({pos, item, onEdit, onDelete}: Te
}
sx={{
// this makes space for buttons
paddingRight:'11rem',
paddingRight: '11rem',
// height:'5rem',
'&:hover': {
backgroundColor:'grey.100'
},
Expand All @@ -56,14 +59,15 @@ export default function SortableTeamMemberItem({pos, item, onEdit, onDelete}: Te
cursor: isDragging ? 'move' : 'default'
}}
>
<ListItemAvatar
>
<ContributorAvatar
avatarUrl={item.avatar_url ?? ''}
displayName={displayName ?? ''}
displayInitials={displayInitials}
/>
</ListItemAvatar>
{smallScreen ? null :
<ListItemAvatar>
<ContributorAvatar
avatarUrl={item.avatar_url ?? ''}
displayName={displayName ?? ''}
displayInitials={displayInitials}
/>
</ListItemAvatar>
}
<ListItemText primary={primaryText} secondary={combineRoleAndAffiliation(item)} />

</ListItem>
Expand Down

0 comments on commit 5f41058

Please sign in to comment.