Skip to content

Commit

Permalink
enhance: add animation as switching containers (#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
hudy9x authored Mar 27, 2024
1 parent 287bfda commit c564412
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 94 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"firebase": "^10.7.1",
"firebase-admin": "^12.0.0",
"formik": "^2.4.1",
"framer-motion": "^11.0.20",
"fuse.js": "^7.0.0",
"immer": "^10.0.2",
"ioredis": "^5.3.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/shared-ui/src/components/Timeline/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default function Timeline({
<div
className={`flex items-start bg-white dark:bg-gray-900 border dark:border-gray-700 p-0.5 rounded-md`}>
{/* <TimelineList items={items} height={colHeight} /> */}
<section className="timeline">
<section className="timeline custom-scrollbar">
<header
className="timeline-month-headers grid divide-x"
style={{
Expand Down
12 changes: 11 additions & 1 deletion packages/shared-ui/src/components/Timeline/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,18 @@
}

.timeline {
@apply w-full overflow-x-auto overflow-y-hidden;
@apply w-full overflow-x-hidden overflow-y-auto;
@apply dark:border-gray-700;
@apply relative;
height: calc(100vh - 200px);

.timeline-month-headers {
@apply sticky top-0 left-0 z-10 bg-white dark:bg-gray-900;
}

.timeline-week-headers {
@apply sticky top-[28px] left-0 z-10 bg-white dark:bg-gray-900;
}
}

.timeline-columns {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { ProjectViewType } from '@prisma/client'
import { Loading } from '@shared/ui'
import { useProjectStatusStore } from '@/store/status'
import { projectViewMap } from '@/features/ProjectView/useProjectViewList'
import { AnimatePresence, motion } from 'framer-motion'
import React, { useId } from 'react'

const DynamicTeamView = dynamic(() => import('@/features/Project/Team'), {
loading: () => <ProjectContentLoading />
Expand Down Expand Up @@ -40,6 +42,29 @@ const AutomateMenu = dynamic(
}
)

function AnimateView({
visible,
children
}: {
visible: boolean
children: React.ReactNode
}) {
const id = useId()
return (
<AnimatePresence>
{visible ? (
<motion.div
key={id}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}>
{children}
</motion.div>
) : null}
</AnimatePresence>
)
}

export default function ProjectTabContent() {
const { loading } = useProjectViewStore()
const { statusLoading } = useProjectStatusStore()
Expand All @@ -65,14 +90,34 @@ export default function ProjectTabContent() {
<Loading.Absolute title="Preparing view ..." enabled={true} />
</div>
) : null}
{type === 'NONE' && !isIgnored() && <TaskList />}
{isView(ProjectViewType.BOARD) && <Board />}
{/* {mode === 'board2' && <Board2 />} */}
{isView(ProjectViewType.LIST) && <TaskList />}
{isView(ProjectViewType.DASHBOARD) ? <ProjectOverview /> : null}
{isView(ProjectViewType.CALENDAR) ? <Calendar /> : null}
{isView(ProjectViewType.TEAM) ? <DynamicTeamView /> : null}
{isView(ProjectViewType.GOAL) ? <Vision /> : null}
<AnimateView visible={type === 'NONE' && !isIgnored()}>
<TaskList />
</AnimateView>
<AnimateView visible={isView(ProjectViewType.BOARD)}>
<Board />
</AnimateView>
<AnimateView visible={isView(ProjectViewType.LIST)}>
<TaskList />
</AnimateView>
<AnimateView visible={isView(ProjectViewType.CALENDAR)}>
<Calendar />
</AnimateView>
<AnimateView visible={isView(ProjectViewType.GOAL)}>
<Vision />
</AnimateView>
<AnimateView visible={isView(ProjectViewType.DASHBOARD)}>
<ProjectOverview />
</AnimateView>
<AnimateView visible={isView(ProjectViewType.TEAM)}>
<DynamicTeamView />
</AnimateView>
{/* {type === 'NONE' && !isIgnored() && <TaskList />} */}
{/* {isView(ProjectViewType.BOARD) && <Board />} */}
{/* {isView(ProjectViewType.LIST) && <TaskList />} */}
{/* {isView(ProjectViewType.DASHBOARD) ? <ProjectOverview /> : null} */}
{/* {isView(ProjectViewType.CALENDAR) ? <Calendar /> : null} */}
{/* {isView(ProjectViewType.TEAM) ? <DynamicTeamView /> : null} */}
{/* {isView(ProjectViewType.GOAL) ? <Vision /> : null} */}
{mode === 'setting' && <Settings />}
{mode === 'automation-create' ? <Automation /> : null}
{mode === 'automation' ? <AutomateMenu /> : null}
Expand Down
4 changes: 2 additions & 2 deletions packages/ui-app/app/_features/Project/Vision/VisionCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export default function VisionCreate() {
projectId,
parentId: null,
organizationId: orgID,
startDate: new Date(y, m, 1),
dueDate: new Date(y, m, 3)
startDate: new Date(y, m, 2),
dueDate: new Date(y, m, 5)
}

// ex: goal 12 /start-2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { ITimelineItem } from '@shared/ui'
import { useVisionContext } from '../Vision/context'
import { isSameDay } from 'date-fns'
import { HiOutlineFlag } from 'react-icons/hi2'
import { dateFormat } from '@shared/libs'
import MemberAvatar from '@/components/MemberAvatar'
import ProgressBar from '@/components/ProgressBar'

export default function TimelineItem({ start, end, id, title }: ITimelineItem) {
const { getVisionProgress, getVisionData } = useVisionContext()
// {data => {

const progress = getVisionProgress(id)
const visionData = getVisionData(id)
const assigneeIds = Array.from(new Set(visionData.assigneeIds))

const displayAssignees = assigneeIds.slice(0, 3)
const restAssignees = assigneeIds.slice(3).length
const isSame = isSameDay(start, end)
const hasTask = visionData.done || visionData.total

return (
<div className="flex items-center justify-between">
<div className={`w-full ${isSame ? 'pl-6' : ''}`}>
<section className="flex items-center gap-4 justify-between">
<div>
<span>{title}</span>
<div className="flex items-center gap-1 text-[11px] text-gray-400">
<HiOutlineFlag />
<div>{dateFormat(start, 'MMM dd')}</div>
<span>-</span>
<div>{dateFormat(end, 'MMM dd')}</div>
</div>
</div>
<div className="flex items-center -space-x-3">
{displayAssignees.map(assigneeId => {
return (
<MemberAvatar noName={true} uid={assigneeId} key={assigneeId} />
)
})}

{restAssignees ? (
<div className="h-6 w-6 bg-gray-50 rounded-full text-[11px] flex items-center justify-center">
+3
</div>
) : null}
</div>
</section>
{hasTask ? (
<section className="mt-1 flex items-center gap-2 w-[250px]">
<ProgressBar
color={progress === 100 ? 'green' : 'blue'}
variant="square"
progress={progress || 0}
/>
<div className="text-[11px] dark:text-gray-400 space-x-2">
<span>{visionData.total} Total</span>
<span>{visionData.done} Done</span>
</div>
</section>
) : null}
</div>
</div>
)
// }}
}
118 changes: 37 additions & 81 deletions packages/ui-app/app/_features/Project/VisionTimeline/index.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
import './style.css'
import { useVisionContext } from '../Vision/context'
import VisionViewMode from '../Vision/VisionViewMode'

import { Avatar, Loading, Timeline } from '@shared/ui'
import VisionMonthNavigator from '../Vision/VisionMonthNavigator'
import MemberAvatar from '@/components/MemberAvatar'
import { HiOutlineFlag } from 'react-icons/hi2'
import { dateFormat } from '@shared/libs'
import ProgressBar from '@/components/ProgressBar'
import { isSameDay } from 'date-fns'
import { Timeline } from '@shared/ui'
import VisionCreate from '../Vision/VisionCreate'
import TimelineItem from './TimelineItem'
import { AnimatePresence, motion } from 'framer-motion'
import { useId } from 'react'

export default function VisionTimeline({ visible }: { visible: boolean }) {
const {
visions,
filter,
loading,
getVisionProgress,
getVisionData,
updateVision
} = useVisionContext()
function AnimateView({
visible,
children
}: {
visible: boolean
children: React.ReactNode
}) {
const id = useId()
return (
<AnimatePresence>
{visible ? (
<motion.div
key={id}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}>
{children}
</motion.div>
) : null}
</AnimatePresence>
)
}

export default function VisionTimeline({ visible }: { visible: boolean }) {
const { visions, filter, loading, updateVision } = useVisionContext()
const ed = new Date()

return (
<div
className={`vision-timeline-container w-full px-3 pt-3 relative ${visible ? '' : 'hidden'
}`}>
<Loading.Absolute enabled={loading} border />
{/* <div className="py-3 flex items-center justify-between"> */}
{/* <div> */}
{/* <VisionMonthNavigator /> */}
{/* </div> */}
{/* <VisionViewMode /> */}
{/* </div> */}
className={`vision-timeline-container w-full px-3 pt-3 relative ${
visible ? '' : 'hidden'
}`}>
{/* <Loading.Absolute enabled={loading} border /> */}

<Timeline
height="5.75rem"
Expand All @@ -54,65 +61,14 @@ export default function VisionTimeline({ visible }: { visible: boolean }) {
}
})}>
{data => {
const { start, end } = data
const progress = getVisionProgress(data.id)
const visionData = getVisionData(data.id)
const assigneeIds = Array.from(new Set(visionData.assigneeIds))

const displayAssignees = assigneeIds.slice(0, 3)
const restAssignees = assigneeIds.slice(3).length
const isSame = isSameDay(start, end)
const hasTask = visionData.done || visionData.total

return (
<div className="flex items-center justify-between">
<div className={`w-full ${isSame ? 'pl-6' : ''}`}>
<section className="flex items-center justify-between">
<div>
<span>{data.title}</span>
<div className="flex items-center gap-1 text-[11px] text-gray-400">
<HiOutlineFlag />
<div>{dateFormat(start, 'MMM dd')}</div>
<span>-</span>
<div>{dateFormat(end, 'MMM dd')}</div>
</div>
</div>
<div className="flex items-center -space-x-3">
{displayAssignees.map(assigneeId => {
return (
<MemberAvatar
noName={true}
uid={assigneeId}
key={assigneeId}
/>
)
})}

{restAssignees ? (
<div className="h-6 w-6 bg-gray-50 rounded-full text-[11px] flex items-center justify-center">
+3
</div>
) : null}
</div>
</section>
{hasTask ? (
<section className="mt-1 flex items-center gap-2 w-[250px]">
<ProgressBar
color={progress === 100 ? 'green' : 'blue'}
variant="square"
progress={progress || 0}
/>
<div className="text-[11px] dark:text-gray-400 space-x-2">
<span>{visionData.total} Total</span>
<span>{visionData.done} Done</span>
</div>
</section>
) : null}
</div>
</div>
<AnimateView visible={true}>
<TimelineItem {...data} />
</AnimateView>
)
}}
</Timeline>
<VisionCreate />
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const CalendarFilter = () => {
{ id: ICalendarView.WEEK, title: 'Week view' },
{ id: ICalendarView.MONTH, title: 'Month view' },
]} />
<div className='w-[1px] h-[20px] bg-gray-200 dark:bg-gray-700 mx-3'></div>
{/* <div className='w-[1px] h-[20px] bg-gray-200 dark:bg-gray-700 mx-3'></div> */}
{/* <ListPreset */}
{/* className='no-clear-icon' */}
{/* value={status} */}
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9188,6 +9188,13 @@ fraction.js@^4.2.0:
resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz"
integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==

framer-motion@^11.0.20:
version "11.0.20"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-11.0.20.tgz#6bb72bd0b8020fc417a40453b9247d596acbea78"
integrity sha512-YSDmWznt3hpdERosbE0UAPYWoYhTnmQ0J1qWPsgpCia9NgY8Xsz5IpOiUEGGj/nzCAW29fSrWugeLRkdp5de7g==
dependencies:
tslib "^2.4.0"

[email protected]:
version "0.5.2"
resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
Expand Down

0 comments on commit c564412

Please sign in to comment.