Skip to content

Commit

Permalink
feat(agenda3): ✨ custom project color
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenull committed Nov 6, 2023
1 parent 496f967 commit 70e9ae1
Show file tree
Hide file tree
Showing 17 changed files with 187 additions and 32 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"sortablejs": "^1.15.0",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"tinycolor2": "^1.6.0",
"tui-calendar": "^1.15.3",
"valibot": "^0.17.0"
},
Expand Down
9 changes: 6 additions & 3 deletions pnpm-lock.yaml

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

23 changes: 23 additions & 0 deletions src/constants/agenda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,26 @@ export const SHOW_DATETIME_FORMATTER = 'MM-DD ddd HH:mm'

export const AGENDA_DRAWER_REGEX = /\n:AGENDA:([\s\S]*?)\n:END:/s
export const LOGBOOK_REGEX = /\n:LOGBOOK:([\s\S]*?)\n:END:/s

export const BACKGROUND_COLOR = {
// blue: '#3688d8',
blue: '#2196f3',
green: '#4caf50',
yellow: '#f6bf25',
purple: '#9c27b0',
orange: '#ff9800',
pink: '#e91e63',
teal: '#009688',
red: '#f44336',
indigo: '#3f51b5',
cyan: '#00bcd4',
lime: '#c0ca33',
amber: '#ffc107',
deepPurple: '#673ab7',
lightGreen: '#8bc34a',
deepOrange: '#ff5722',
brown: '#795548',
grey: '#9e9e9e',
blueGrey: '#607d8b',
}
export const DEFAULT_BG_COLOR_NAME = 'blue'
10 changes: 6 additions & 4 deletions src/helper/autoTextColor.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
const getTextColor = (bgColor: number[]) => {
// 当color值大于128时,color值偏向255,即#ffffff,此时字体颜色应为#333333
// 当color值小于128时,color值偏向0,即#000000,此时字体颜色应为#ffffff
const isWhiteBg = 0.213 * bgColor[0] + 0.715 * bgColor[1] + 0.072 * bgColor[2] > 255 / 2;
return isWhiteBg ? '#333333': '#ffffff'
const isWhiteBg = 0.299 * bgColor[0] + 0.587 * bgColor[1] + 0.114 * bgColor[2] > 128
return isWhiteBg ? '#333333' : '#ffffff'
}

/**
* get text color base on background color
* @param rbgColor rgba(11, 22, 33, 1)
*/
export const autoTextColor = (rbgColor: string) => {
const [red, green, blue, alpha] = rbgColor.replace(/(?:\(|\)|rgba)*/g, "").split(",")
const [red, green, blue, alpha] = rbgColor.replace(/[rgba()]/g, '').split(',')
if (rbgColor === 'rgb(149, 239, 181)') console.log('xxxxx', getTextColor([Number(red), Number(green), Number(blue)]))
if (rbgColor === 'rgb(149, 239, 181)') console.log('yyyyy', [red, Number(green), Number(blue)])
return getTextColor([Number(red), Number(green), Number(blue)])
}
}
17 changes: 17 additions & 0 deletions src/hooks/useNewProjects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useAtom } from 'jotai'

import { getAllProjects } from '@/newHelper/project'
import { allProjectsAtom } from '@/newModel/projects'

const useNewProjects = () => {
const [, setProjects] = useAtom(allProjectsAtom)
const refreshProjects = () => {
getAllProjects().then((projects) => setProjects(projects))
}

return {
refreshProjects,
}
}

export default useNewProjects
11 changes: 9 additions & 2 deletions src/newHelper/__tests__/task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import type { AgendaTaskWithStart } from '@/types/task'
import { type BlockFromQuery, transformBlockToAgendaTask, separateTasksInDay } from '../task'

export const DEMO_BLOCK = {
properties: {},
properties: {
'agenda-color': 'blue',
},
scheduled: 20230924,
parent: {
id: 137,
Expand All @@ -30,6 +32,7 @@ export const DEMO_BLOCK = {
name: 'sep 24th, 2023',
'original-name': 'Sep 24th, 2023',
id: 137,
uuid: '123123123-sep-24th-2023a',
originalName: 'Sep 24th, 2023',
journalDay: 20230924,
},
Expand Down Expand Up @@ -65,7 +68,11 @@ export const DEMO_TASK = {
'journal-day': 20230924,
name: 'sep 24th, 2023',
'original-name': 'Sep 24th, 2023',
id: 137,
id: '123123123-sep-24th-2023a',
uuid: '123123123-sep-24th-2023a',
bgColor: '#2196f3',
isFavorite: false,
isJournal: true,
},
timeLogs: [
{
Expand Down
1 change: 1 addition & 0 deletions src/newHelper/fullCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const transformAgendaTaskToCalendarEvent = (task: AgendaTaskWithStart): C
extendedProps: task,
rrule: task.rrule,
editable: !(task.recurringPast || task.rrule),
color: task.project.bgColor,
}
}

Expand Down
28 changes: 28 additions & 0 deletions src/newHelper/project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import tinycolor from 'tinycolor2'

import { BACKGROUND_COLOR, DEFAULT_BG_COLOR_NAME } from '@/constants/agenda'
import { autoTextColor } from '@/helper/autoTextColor'
import type { AgendaProject } from '@/types/project'
import type { AgendaTaskPage } from '@/types/task'

export const getAllProjects = async () => {
const pages = (await logseq.Editor.getAllPages()) || []
return pages?.map((page) => {
return transformPageToProject({
...page,
isJournal: page['journal?'],
})
})
}

export const transformPageToProject = (page: AgendaTaskPage): AgendaProject => {
const colorName = page.properties?.['agenda-color'] || DEFAULT_BG_COLOR_NAME
const bgColor = BACKGROUND_COLOR[colorName] || BACKGROUND_COLOR[DEFAULT_BG_COLOR_NAME]
return {
...page,
id: page.uuid,
isJournal: page['journal?'],
isFavorite: page.properties?.['agenda-favorite'] === 'yes',
bgColor,
}
}
12 changes: 8 additions & 4 deletions src/newHelper/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { AgendaTask, AgendaTaskWithStart, AgendaTaskPage } from '@/types/ta
import { genDays } from '@/util/util'

import { parseAgendaDrawer } from './block'
import { transformPageToProject } from './project'

export type BlockFromQuery = BlockEntity & {
marker: 'TODO' | 'DOING' | 'NOW' | 'LATER' | 'WAITING' | 'DONE' | 'CANCELED'
Expand Down Expand Up @@ -44,32 +45,35 @@ export const getAgendaTasks = async () => {
:block/file
:block/heading-level
{:block/page
[:db/id :block/name :block/original-name :block/journal-day :block/journal?]}
[:db/id :block/uuid :block/name :block/original-name :block/journal-day :block/journal? :block/properties]}
{:block/refs
[:db/id :block/name :block/original-name :block/journal-day :block/journal?]}])
[:db/id :block/uuid :block/name :block/original-name :block/journal-day :block/journal? :block/properties]}])
:where
[?block :block/marker ?marker]
[(contains? #{"TODO" "DOING" "NOW" "LATER" "WAITING" "DONE"} ?marker)]]
`)) as BlockFromQuery[]
if (!blocks || blocks?.length === 0) return []
blocks = blocks.flat()

const promiseList: Promise<AgendaTask[]>[] = blocks.map(async (block) => {
const _block = {
...block,
uuid: typeof block.uuid === 'string' ? block.uuid : block.uuid?.['$uuid$'],
repeated: block['repeated?'],
page: {
// ...block.page,
uuid: block.page?.['uuid'],
originalName: block.page?.['original-name'],
journalDay: block.page?.['journal-day'],
isJournal: block.page?.['journal?'],
properties: block.page?.['properties'],
},
refs: block.refs?.map((_page) => ({
// ..._page,
uuid: _page?.['uuid'],
journalDay: _page?.['journal-day'],
originalName: _page?.['original-name'],
isJournal: _page?.['journal?'],
properties: _page?.['properties'],
})),
}

Expand Down Expand Up @@ -200,7 +204,7 @@ export const transformBlockToAgendaTask = async (block: BlockFromQuery): Promise
deadline,
estimatedTime,
actualTime,
project: page,
project: transformPageToProject(page),
timeLogs,
// TODO: read from logseq
// label: page,
Expand Down
20 changes: 20 additions & 0 deletions src/newModel/projects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { atom } from 'jotai'

import type { AgendaProject } from '@/types/project'

export const allProjectsAtom = atom<AgendaProject[]>([])

export const favoriteProjectsAtom = atom<AgendaProject[]>((get) => {
const allProjects = get(allProjectsAtom)
return allProjects.filter((project) => project.isFavorite)
})

export const journalProjectsAtom = atom<AgendaProject[]>((get) => {
const allProjects = get(allProjectsAtom)
return allProjects.filter((project) => project.isJournal && !project.isFavorite)
})

export const normalProjectsAtom = atom<AgendaProject[]>((get) => {
const allProjects = get(allProjectsAtom)
return allProjects.filter((project) => !project.isJournal && !project.isFavorite)
})
15 changes: 12 additions & 3 deletions src/pages/NewDashboard/components/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,18 @@ const Calendar = ({ onCalendarTitleChange }: CalendarProps, ref) => {
})}
title={info.event.title}
>
{info.event.allDay ? null : <span className="w-1.5 h-1.5 rounded-full bg-blue-400" />}
{info.event.allDay ? null : (
<span
className="w-1.5 h-1.5 rounded-full"
style={{ backgroundColor: info.event.backgroundColor }}
/>
)}
<span className="truncate flex-1">{info.event.title}</span>
{isDone ? <IoIosCheckmarkCircle className="text-green-500 absolute right-0" /> : null}
{isDone ? (
<IoIosCheckmarkCircle
className={cn('absolute right-0', info.event.allDay ? 'text-white' : 'text-green-500')}
/>
) : null}
</div>
)
break
Expand All @@ -235,7 +244,7 @@ const Calendar = ({ onCalendarTitleChange }: CalendarProps, ref) => {
{info.event.title}
</div>
{isShowTimeText ? <div className="text-xs text-gray-200">{info.timeText}</div> : null}
{isDone ? <IoIosCheckmarkCircle className="text-green-500 absolute right-0 top-0.5" /> : null}
{isDone ? <IoIosCheckmarkCircle className="absolute right-0 top-0.5" /> : null}
</div>
)
break
Expand Down
29 changes: 19 additions & 10 deletions src/pages/NewDashboard/components/KanBan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,12 @@ const KanBan = (props, ref) => {
const isToday = day.isSame(today, 'day')
const isFuture = day.isAfter(today, 'day')
const doneTasks = columnTasks.filter((task) => task.status === 'done')
const estimatedTime = columnTasks.reduce((acc, task) => {
const undoneTasks = columnTasks.filter((task) => task.status !== 'done')
const _dayTasks = undoneTasks.concat(doneTasks)
const estimatedTime = _dayTasks.reduce((acc, task) => {
return acc + (task.estimatedTime ?? DEFAULT_ESTIMATED_TIME)
}, 0)
const actualTime = columnTasks.reduce((acc, task) => {
const actualTime = _dayTasks.reduce((acc, task) => {
return acc + (task.actualTime ?? task.estimatedTime ?? DEFAULT_ESTIMATED_TIME)
}, 0)
return (
Expand Down Expand Up @@ -131,7 +133,7 @@ const KanBan = (props, ref) => {
status="success"
className="!m-0"
showInfo={false}
percent={(doneTasks.length / columnTasks.length) * 100}
percent={(doneTasks.length / _dayTasks.length) * 100}
/>
) : (
<div className="h-[24px]"></div>
Expand All @@ -152,11 +154,11 @@ const KanBan = (props, ref) => {
{/* ========= Tasks List ========= */}
<ReactSortable
forceFallback // 该属性如果不加就无法与 fullcalendar 交互
className={cn('flex flex-col gap-2 flex-1 overflow-y-auto', { 'pb-28': columnTasks.length === 0 })}
className={cn('flex flex-col gap-2 flex-1 overflow-y-auto', { 'pb-28': _dayTasks.length === 0 })}
group="shared"
dragClass="dragged-mirror-element"
draggable=".droppable-task-element"
list={columnTasks}
list={_dayTasks}
// onMove={(event, originalEvent) => {
// console.log('[faiz:] === xxx onMove', event)
// console.log('[faiz:] === xxx originalEvent', originalEvent)
Expand All @@ -182,7 +184,7 @@ const KanBan = (props, ref) => {
})
}}
>
{columnTasks.map((task) => {
{_dayTasks.map((task) => {
const editDisabled = task.rrule || task.recurringPast
const estimatedTime = task.estimatedTime ?? DEFAULT_ESTIMATED_TIME
return (
Expand All @@ -204,7 +206,7 @@ const KanBan = (props, ref) => {
<div className="flex gap-1 items-center">
{task.status === 'done' ? (
<IoIosCheckmarkCircle
className="text-green-500 text-xl cursor-pointer"
className="text-xl cursor-pointer text-gray-300"
onClick={(e) => onClickTaskMark(e, task, 'todo')}
/>
) : (
Expand All @@ -214,7 +216,12 @@ const KanBan = (props, ref) => {
/>
)}
{task.allDay ? null : (
<span className="text-[10px] bg-blue-300 rounded px-1 py-0.5 text-white">
<span
className="text-[10px] rounded px-1 py-0.5 text-white opacity-70"
style={{
backgroundColor: task.project.bgColor,
}}
>
{task.start.format('HH:mm')}
</span>
)}
Expand All @@ -226,10 +233,12 @@ const KanBan = (props, ref) => {
{minutesToHHmm(estimatedTime)}
</div>
</div>
<div className={cn('text-gray-600', { 'line-through': task.status === 'done' })}>{task.title}</div>
<div className={cn('text-gray-600 my-0.5', { 'line-through': task.status === 'done' })}>
{task.title}
</div>
{task.project.isJournal ? null : (
<div className="text-gray-400 text-xs flex gap-1 items-center">
<span className="w-2 h-2 rounded-full bg-blue-400" />
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: task.project.bgColor }} />
<span>{task.project?.originalName}</span>
</div>
)}
Expand Down
Loading

0 comments on commit 70e9ae1

Please sign in to comment.