Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: (section-list): Allow user to drag and drop to sort the sections in the sidebar #231

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ yarn-error.log*
# swiftlatex's postinstall files
/public/*.js
/public/*.wasm

#vscode
.vscode
.history/**
33 changes: 33 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"polished": "^4.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-easy-sort": "^1.6.0",
"react-hook-form": "^7.31.3",
"react-icons": "^4.4.0",
"react-pdf": "^5.7.2",
Expand Down
110 changes: 77 additions & 33 deletions src/components/generator/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import styled from 'styled-components'
import { MdDragIndicator } from 'react-icons/md'
import arrayMove from 'array-move'
import SortableList, { SortableItem, SortableKnob } from 'react-easy-sort'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid the following warning, shall we load SortableList dynamically with ssr disabled?

Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.
    at SortableList (/home/ducaale/resumake.io/node_modules/react-easy-sort/index.js:375:21)

Something like this should do the trick

const SortableList = dynamic(
  async () => await import('react-easy-sort'),
  { ssr: false }
)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, I havent got time to checkout nextjs related things yet. So its your call.


import { colors } from '../../theme'
import { PrimaryButton, IconButton } from '../core/Button'
Expand All @@ -12,16 +15,16 @@ const Aside = styled.aside`
padding: 24px 36px;
`

const Nav = styled.nav`
// div targets the items inside the drag sort library
const NavList = styled.nav`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 24px;
margin-bottom: 28px;

button {
cursor: grab;
.sort-list-wrapper {
display: flex;
flex-direction: column;
flex-grow: 1;
gap: 24px;
}
`

Expand All @@ -33,38 +36,79 @@ const StyledLink = styled(Link)<{ $active: boolean }>`
${(props) => props.$active && `color: ${colors.primary};`}
`

const DragIconButton = styled(IconButton)`
opacity: ${(props) => (props.disabled ? 0 : 1)};
cursor: ${(props) => (props.disabled ? 'default' : 'grab')};
`

const sections = [
{ label: 'Templates', section: 'templates', isSortble: false },
{ label: 'Profile', section: 'basics', isSortble: false },
{ label: 'Education', section: 'education', isSortble: true },
{ label: 'Work Experience', section: 'work', isSortble: true },
{ label: 'Skills', section: 'skills', isSortble: true },
{ label: 'Projects', section: 'projects', isSortble: true },
{ label: 'Awards', section: 'awards', isSortble: true }
]

const NavItem = ({
label,
section,
currSection,
draggable
}: {
label: string
section: string
currSection: string
draggable?: boolean
}) => (
<div key={section} style={{ display: 'flex', gap: 8 }}>
<SortableKnob>
<DragIconButton type="button" disabled={!Boolean(draggable)}>
<MdDragIndicator />
</DragIconButton>
</SortableKnob>
<StyledLink
href={`/generator?section=${section}`}
$active={section === currSection}
>
{label}
</StyledLink>
</div>
)

export function Sidebar() {
const router = useRouter()
const { section: currSection = 'basics' } = router.query
const currSection = (router.query.section || 'basics') as string
const [sortedSections, updateSectionOrder] = useState(sections)

const sectionLinks = [
{ label: 'Templates', section: 'templates' },
{ label: 'Profile', section: 'basics' },
{ label: 'Education', section: 'education' },
{ label: 'Work Experience', section: 'work' },
{ label: 'Skills', section: 'skills' },
{ label: 'Projects', section: 'projects' },
{ label: 'Awards', section: 'awards' }
]
const onSortEnd = (oldIndex: number, newIndex: number) => {
updateSectionOrder((array) => arrayMove(array, oldIndex, newIndex))
}

return (
<Aside>
<Nav>
{sectionLinks.map(({ label, section }) => (
<div key={section} style={{ display: 'flex', gap: 8 }}>
<IconButton type="button">
<MdDragIndicator />
</IconButton>
<StyledLink
href={`/generator?section=${section}`}
$active={section === currSection}
>
{label}
</StyledLink>
</div>
))}
</Nav>

<NavList>
<SortableList
lockAxis="y"
onSortEnd={onSortEnd}
className="sort-list-wrapper"
>
{sortedSections.map(({ label, section, isSortble }) => (
<SortableItem key={section}>
<div className="sort-item-wrapper">
<NavItem
draggable={isSortble}
key={section}
label={label}
section={section}
currSection={currSection}
/>
</div>
</SortableItem>
))}
</SortableList>
</NavList>
<PrimaryButton form="resume-form">MAKE</PrimaryButton>
</Aside>
)
Expand Down
Loading