From 0e2df22983259b3c545bd46c138de7d03f998d50 Mon Sep 17 00:00:00 2001 From: sechan100 Date: Mon, 13 Jan 2025 17:45:35 +0900 Subject: [PATCH] =?UTF-8?q?debug:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20nav?= =?UTF-8?q?=EB=A1=9C=20month=20=EB=B3=80=EA=B2=BD=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintignore | 2 +- .eslintrc | 1 + esbuild.config.mjs | 2 +- src/app/ui/DailyRoutineView.tsx | 6 +- src/app/ui/tab-note.tsx | 2 +- .../merge-note/use-routine-mutate-merge.ts | 4 +- .../performance/PerformanceCircle.tsx | 17 +- src/features/task-el/dnd/use-group-dnd.tsx | 4 +- src/features/task-el/dnd/use-task-dnd.tsx | 4 +- src/features/task-el/index.ts | 3 +- .../task-el/model/change-task-state.ts | 2 +- src/features/task-el/ui/BaseTaskFeature.tsx | 12 +- .../task-el/ui/BaseTaskGroupFeature.tsx | 14 +- src/features/task-el/ui/CancelLineName.tsx | 4 +- src/features/task-el/ui/Checkbox.tsx | 140 --------------- src/features/task-el/ui/TaskCheckbox.tsx | 170 ++++++++++++++++++ src/main.ts | 2 +- src/pages/achivement/AchivementPage.tsx | 75 +++++++- src/shared/components/Circle.tsx | 58 ++++-- .../components/obsidian-accent-color.ts | 19 ++ .../components/swipeable-calender/Nav.tsx | 31 ++-- .../swipeable-calender/SwipeableCalendar.tsx | 13 +- src/shared/period/day.ts | 3 +- src/shared/period/month.ts | 6 +- src/shared/tab/initial-tab.ts | 3 +- src/widgets/calendar/ui/CalendarTile.tsx | 22 ++- src/widgets/calendar/ui/CalendarWidget.tsx | 8 - src/widgets/note-achivement/index.ts | 2 +- .../note-achivement/ui/CalendarSlide.tsx | 27 ++- .../ui/NoteAchivementCalendar.tsx | 56 ------ .../ui/NoteAchivementWidget.tsx | 24 +++ src/widgets/routine-achivement/index.ts | 2 + .../model/resolve-routine-tiles.ts | 43 +++++ src/widgets/routine-achivement/model/types.ts | 10 ++ .../routine-achivement/ui/CalendarSlide.tsx | 57 ++++++ .../routine-achivement/ui/CalendarTile.tsx | 76 ++++++++ .../ui/RoutineAchivementWidget.tsx | 49 +++++ src/widgets/routine/create-routine.ts | 2 +- src/widgets/weeks/DayNodeComponent.tsx | 2 +- src/widgets/weeks/WeeksWidget.tsx | 4 +- 40 files changed, 691 insertions(+), 290 deletions(-) delete mode 100644 src/features/task-el/ui/Checkbox.tsx create mode 100644 src/features/task-el/ui/TaskCheckbox.tsx create mode 100644 src/shared/components/obsidian-accent-color.ts delete mode 100644 src/widgets/note-achivement/ui/NoteAchivementCalendar.tsx create mode 100644 src/widgets/note-achivement/ui/NoteAchivementWidget.tsx create mode 100644 src/widgets/routine-achivement/index.ts create mode 100644 src/widgets/routine-achivement/model/resolve-routine-tiles.ts create mode 100644 src/widgets/routine-achivement/model/types.ts create mode 100644 src/widgets/routine-achivement/ui/CalendarSlide.tsx create mode 100644 src/widgets/routine-achivement/ui/CalendarTile.tsx create mode 100644 src/widgets/routine-achivement/ui/RoutineAchivementWidget.tsx diff --git a/.eslintignore b/.eslintignore index e019f3c..b25ab8d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,3 @@ node_modules/ - +**/dist/** main.js diff --git a/.eslintrc b/.eslintrc index 707f4d4..bc25d9f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,6 +17,7 @@ "sourceType": "module" }, "rules": { + "no-warning-comments": ["warn", { "terms": ["TODO", "FIXME"], "location": "start" }], "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": ["off", {}], "@typescript-eslint/ban-ts-comment": "off", diff --git a/esbuild.config.mjs b/esbuild.config.mjs index a8ca37b..7f1be78 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -63,7 +63,7 @@ const context = await esbuild.context({ }, format: "cjs", // CommonJS 포맷 target: "es2018", - logLevel: "info", + logLevel: "warning", sourcemap: prod ? false : "inline", treeShaking: true, outfile: "main.js", // JavaScript 번들 파일 diff --git a/src/app/ui/DailyRoutineView.tsx b/src/app/ui/DailyRoutineView.tsx index 00874cb..0ab61d3 100644 --- a/src/app/ui/DailyRoutineView.tsx +++ b/src/app/ui/DailyRoutineView.tsx @@ -23,12 +23,12 @@ const tabCss = css({ boxShadow: "none !important", backgroundColor: "transparent !important", minHeight: "0 !important", + height: tabsHeight, fontSize: "0.7em", }) export const DailyRoutineView = () => { const { tab, route } = useTabRoute(); - const { view, leafBgColor } = useLeaf(); useEffect(() => { @@ -39,7 +39,7 @@ export const DailyRoutineView = () => { const onTabChange = useCallback((e: React.SyntheticEvent, tab: DrTabType) => { switch(tab){ case "note": - route(tab, { day: Day.now() }); + route(tab, { day: Day.today() }); break; case "achivement": route(tab, { month: Month.now() }); @@ -74,7 +74,7 @@ export const DailyRoutineView = () => { zIndex: 1000, backgroundColor: leafBgColor, position: "fixed", - height: tabsHeight, + minHeight: 0, width: "100%", left: "0", bottom: tabsBottomGap, diff --git a/src/app/ui/tab-note.tsx b/src/app/ui/tab-note.tsx index ba3a5a2..d92568b 100644 --- a/src/app/ui/tab-note.tsx +++ b/src/app/ui/tab-note.tsx @@ -20,7 +20,7 @@ export const RouitneNoteTab = () => { } return params; } else { - return { day: Day.now() }; + return { day: Day.today() }; } }, [routeParams, tab]); diff --git a/src/features/merge-note/use-routine-mutate-merge.ts b/src/features/merge-note/use-routine-mutate-merge.ts index 9286e6d..55665bb 100644 --- a/src/features/merge-note/use-routine-mutate-merge.ts +++ b/src/features/merge-note/use-routine-mutate-merge.ts @@ -35,7 +35,7 @@ export const useRoutineMutationMerge: UseRoutineMutationMerge = () => { setNote(manuallyMutatedNote); await noteRepository.updateIfExist(manuallyMutatedNote); // merge - let notes = await noteRepository.loadBetween(Day.now(), Day.max()); + let notes = await noteRepository.loadBetween(Day.today(), Day.max()); notes = notes.filter(n => !n.day.isSameDay(currentNote.day)); const noteCreator = await RoutineNoteCreator.withLoadFromRepositoryAsync(); for(const note of notes){ @@ -44,7 +44,7 @@ export const useRoutineMutationMerge: UseRoutineMutationMerge = () => { await noteRepository.persist(merged); } } else { - const notes = await noteRepository.loadBetween(Day.now(), Day.max()); + const notes = await noteRepository.loadBetween(Day.today(), Day.max()); const noteCreator = await RoutineNoteCreator.withLoadFromRepositoryAsync(); for(const note of notes){ const merged = mergeNote(note, noteCreator); diff --git a/src/features/performance/PerformanceCircle.tsx b/src/features/performance/PerformanceCircle.tsx index 960b3ae..1408834 100644 --- a/src/features/performance/PerformanceCircle.tsx +++ b/src/features/performance/PerformanceCircle.tsx @@ -1,6 +1,6 @@ import { NotePerformance } from "@entities/note"; import { Circle } from "@shared/components/Circle"; -import clsx from "clsx"; +import { getCustomAccentHSL } from "@shared/components/obsidian-accent-color"; import React from "react"; const cleanPercentage = (percentage: number) => { @@ -10,8 +10,13 @@ const cleanPercentage = (percentage: number) => { }; -const completionCircleColor = "hsla(var(--color-accent-2-hsl), 1)"; -const accomplishmentCircleColor = "hsla(var(--color-accent-hsl), 1)"; +const completionCircleColor = getCustomAccentHSL({ + h: 1, + s: 1, + l: 1.2, + a: 1, +}) +const accomplishmentCircleColor = "hsl(var(--color-accent-hsl))"; interface PercentageCircleProps { @@ -31,11 +36,11 @@ export const PerformanceCircle = React.memo(({ const accomplishment = cleanPercentage(performance.accomplishment); const completion = cleanPercentage(performance.completion); return ( - + - - + + ({ lineHeight: 1 }} > - - + + onOptionMenu(m, task)} /> {indicator} diff --git a/src/features/task-el/ui/BaseTaskGroupFeature.tsx b/src/features/task-el/ui/BaseTaskGroupFeature.tsx index ea1950f..91be0f1 100644 --- a/src/features/task-el/ui/BaseTaskGroupFeature.tsx +++ b/src/features/task-el/ui/BaseTaskGroupFeature.tsx @@ -5,7 +5,7 @@ import { Icon } from '@shared/components/Icon'; import { Touchable } from '@shared/components/Touchable'; import { dr } from '@shared/utils/daily-routine-bem'; import { useLeaf } from '@shared/view/use-leaf'; -import React, { useCallback, useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useGroupDnd } from '../dnd/use-group-dnd'; import { baseHeaderStyle, draggingStyle, dragReadyStyle, elementHeight, pressedStyle } from './base-element-style'; import { DELAY_TOUCH_START } from '../dnd/dnd-context'; @@ -38,7 +38,14 @@ export const BaseTaskGroupFeature = React.memo(({ const groupRef = useRef(null); const [groupMode, setGroupMode] = useState("idle"); const bgColor = useLeaf(s=>s.leafBgColor); - const [open, setOpen] = useState(true); + const isAllSubTasksChecked = group.children.every(t => t.state !== "un-checked") + const [open, setOpen] = useState(isAllSubTasksChecked ? false : true); + + useEffect(() => { + if(isAllSubTasksChecked){ + setOpen(false); + } + }, [isAllSubTasksChecked]) const onElDrop = useCallback((newNote: RoutineNote, dropped: TaskGroup) => { onGroupReorder?.(newNote, dropped); @@ -158,8 +165,7 @@ export const BaseTaskGroupFeature = React.memo(({ > t.state !== "un-checked")} - transparentLine={group.children.length === 0} + cancel={isAllSubTasksChecked} /> diff --git a/src/features/task-el/ui/CancelLineName.tsx b/src/features/task-el/ui/CancelLineName.tsx index 8818712..f23c285 100644 --- a/src/features/task-el/ui/CancelLineName.tsx +++ b/src/features/task-el/ui/CancelLineName.tsx @@ -36,17 +36,15 @@ const cancelLineStyle = css({ interface Props { name: string; cancel: boolean; - transparentLine?: boolean; } export const CancelLineName = ({ name, cancel, - transparentLine }: Props) => { return ( {name} diff --git a/src/features/task-el/ui/Checkbox.tsx b/src/features/task-el/ui/Checkbox.tsx deleted file mode 100644 index 862d49b..0000000 --- a/src/features/task-el/ui/Checkbox.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/** @jsxImportSource @emotion/react */ -import { css } from "@emotion/react"; -import { TaskState } from "@entities/note"; -import { Icon } from "@shared/components/Icon"; -import { dr } from "@shared/utils/daily-routine-bem"; -import { useMemo } from "react"; -import { CSSTransition, SwitchTransition } from "react-transition-group"; - - -const bem = dr("task-cbx") - -const CheckMarkWrapper = ({ children }: { children: React.ReactNode }) => { - return ( - - {children} - - ) -} - -const AccomplishCheckMark = () => { - return ( - - - - ) -} - -const FailedCheckMark = () => { - return ( - - - - - ) -} - - - -interface Props { - state: TaskState; -} -export const Checkbox = ({ state }: Props) => { - const isChecked = useMemo(() => state !== "un-checked", [state]); - return ( - - } - /> - } - /> - - ) -} \ No newline at end of file diff --git a/src/features/task-el/ui/TaskCheckbox.tsx b/src/features/task-el/ui/TaskCheckbox.tsx new file mode 100644 index 0000000..e105ea0 --- /dev/null +++ b/src/features/task-el/ui/TaskCheckbox.tsx @@ -0,0 +1,170 @@ +/** @jsxImportSource @emotion/react */ +import { css } from "@emotion/react"; +import { TaskState } from "@entities/note"; +import { Icon } from "@shared/components/Icon"; +import { getCustomAccentHSL } from "@shared/components/obsidian-accent-color"; +import { dr } from "@shared/utils/daily-routine-bem"; +import { useMemo } from "react"; +import { CSSTransition, SwitchTransition } from "react-transition-group"; + + +const bem = dr("task-cbx") + +const CheckMarkWrapper = ({ children, size }: { children: React.ReactNode, size: number }) => { + return ( + + {children} + + ) +} + +type AccomplishCheckMarkProps = { + size: number; + custom?: React.ReactNode; +} +const AccomplishCheckMark = ({ + size, + custom, +}: AccomplishCheckMarkProps) => { + return ( + + {custom ?? ( + + )} + + ) +} + +type FailedCheckMarkProps = { + size: number; + custom?: React.ReactNode; +} +const FailedCheckMark = ({ + size, + custom, +}: FailedCheckMarkProps) => { + const x = 15; + const y = 100 - x; + const color = "var(--color-accent-2)"; + return ( + + {custom ?? (<> + + + {/* 삼각형은 어떻니 */} + {/* */} + )} + + ) +} + + + +interface Props { + state: TaskState; + className?: string; + size?: number; + customAccomplishedSVG?: React.ReactNode; + customFailedSVG?: React.ReactNode; +} +export const TaskCheckbox = ({ + state, + className, + customAccomplishedSVG, + customFailedSVG, + size = 15, +}: Props) => { + const isChecked = useMemo(() => state !== "un-checked", [state]); + return ( +
+ + } + /> + + } + /> +
+ ) +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 52705c2..f447fdd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -40,7 +40,7 @@ export default class DailyRoutinePlugin extends Plugin { }); devRunner(); - activateView(DailyRoutineObsidianView.VIEW_TYPE, 1); + setTimeout(() => activateView(DailyRoutineObsidianView.VIEW_TYPE, 1), 500); } onunload() { diff --git a/src/pages/achivement/AchivementPage.tsx b/src/pages/achivement/AchivementPage.tsx index bd760cd..48bcf76 100644 --- a/src/pages/achivement/AchivementPage.tsx +++ b/src/pages/achivement/AchivementPage.tsx @@ -1,15 +1,86 @@ +/** @jsxImportSource @emotion/react */ import { Month } from "@shared/period/month"; -import { NoteAchivementCalendar } from "@widgets/note-achivement"; +import { NoteAchivementWidget } from "@widgets/note-achivement"; +import TabNavItem from '@mui/material/Tab'; +import TabNav from '@mui/material/Tabs'; +import { useState } from "react"; +import { useLeaf } from "@shared/view/use-leaf"; +import { css } from "@emotion/react"; +import { Icon } from "@shared/components/Icon"; +import { RoutineAchivementWidget } from "@widgets/routine-achivement"; +type AchivementType = "note" | "routine"; + +const tabCss = css({ + boxShadow: "none !important", + backgroundColor: "transparent !important", + minHeight: "0 !important", + fontSize: "0.7em", + height: "50px", + width: "50%", +}) + export interface AchivementPageProps { month: Month; } export const AchivementPage = ({ month }: AchivementPageProps) => { + // FIXME: routine -> note로 + const [type, setType] = useState("routine"); + const { view, leafBgColor } = useLeaf(); return ( <> - + setType(value)} + css={{ + zIndex: 5, + backgroundColor: leafBgColor, + minHeight: 0, + width: "100%", + }} + > + } + iconPosition="start" + css={tabCss} + /> + } + iconPosition="start" + css={tabCss} + /> + +
+
+ {type === "note" ? + + : + + } +
+
+
) } \ No newline at end of file diff --git a/src/shared/components/Circle.tsx b/src/shared/components/Circle.tsx index 66f49fc..8c176b2 100644 --- a/src/shared/components/Circle.tsx +++ b/src/shared/components/Circle.tsx @@ -1,13 +1,27 @@ -import clsx from "clsx"; +/** @jsxImportSource @emotion/react */ import React, { useEffect, useState } from "react"; interface CircleProps { color: string; percentage: number; - transition: boolean; + transition?: boolean; + width?: string; + strokeWidth?: number; + className?: string; + rotate?: number; + fill?: string; } -export const Circle = ({ color, percentage: propsPercentage, transition=true }: CircleProps) => { +export const Circle = ({ + color, + percentage: propsPercentage, + transition = true, + width, + strokeWidth = 25, + className, + rotate, + fill, +}: CircleProps) => { const [percentage, setPercentage] = useState(transition ? 0 : propsPercentage); const r = 85; @@ -22,20 +36,30 @@ export const Circle = ({ color, percentage: propsPercentage, transition=true }: }, [percentage, propsPercentage, transition]); return ( - + > + + ); }; \ No newline at end of file diff --git a/src/shared/components/obsidian-accent-color.ts b/src/shared/components/obsidian-accent-color.ts new file mode 100644 index 0000000..f2715d9 --- /dev/null +++ b/src/shared/components/obsidian-accent-color.ts @@ -0,0 +1,19 @@ + + + + + + +export const getCustomAccentHSL = ({ + h = 1, + s = 1, + l = 1, + a = 1, +}: { + h?: number; + s?: number; + l?: number; + a?: number; +}) => { + return `hsla(calc(var(--accent-h) * ${a}), calc(var(--accent-s) * ${s}), calc(var(--accent-l) * ${l}), ${a})`; +} \ No newline at end of file diff --git a/src/shared/components/swipeable-calender/Nav.tsx b/src/shared/components/swipeable-calender/Nav.tsx index 5e93749..5bbad49 100644 --- a/src/shared/components/swipeable-calender/Nav.tsx +++ b/src/shared/components/swipeable-calender/Nav.tsx @@ -11,12 +11,12 @@ import { OnArgs } from "react-calendar/dist/cjs/shared/types"; export interface CalendarNavigationProps { month: Month; - onMonthChange?: (month: Month) => void; + setMonth: (month: Month) => void; className?: string; } export const CalendarNavigation = ({ month, - onMonthChange, + setMonth, className, }: CalendarNavigationProps) => { const { leafBgColor } = useLeaf(); @@ -27,14 +27,13 @@ export const CalendarNavigation = ({ // @ts-ignore calendarRef.current.setActiveStartDate(month.startDay.getJsDate()); }, [month]); - - const onActiveStartDateChange = useCallback(({ activeStartDate, view }: OnArgs) => { - if(!activeStartDate || view !== "month") return; - const changedMonth = Month.fromJsDate(activeStartDate); - if(!changedMonth.startDay.isSameMonth(month.startDay)){ - onMonthChange?.(changedMonth); - } - }, [month.startDay, onMonthChange]); + + const lastChangedMonthRef = useRef(month); + const monthChangeHandler = useCallback((month: Month) => { + if(lastChangedMonthRef.current.isSameMonth(month)) return; + setMonth?.(month); + lastChangedMonthRef.current = month; + }, [setMonth]); const calendarStyles = useMemo(() => css({ // 네비게이션 @@ -48,7 +47,7 @@ export const CalendarNavigation = ({ // 캘린더 + 월, 년 선택 컨테이너 ".react-calendar__viewContainer": { position: "relative", - zIndex: 10, + zIndex: 10000, "div": { position: "absolute", @@ -117,7 +116,15 @@ export const CalendarNavigation = ({ css={calendarStyles} defaultValue={month.startDay.getJsDate()} allowPartialRange={false} - onActiveStartDateChange={onActiveStartDateChange} + onActiveStartDateChange={({ activeStartDate, view }: OnArgs) => { + if(!activeStartDate || view !== "month") return; + const newMonth = Month.fromJsDate(activeStartDate); + monthChangeHandler(newMonth); + }} + onClickMonth={value => { + const month = Month.fromJsDate(value); + monthChangeHandler(month); + }} selectRange={false} showNavigation={true} /> diff --git a/src/shared/components/swipeable-calender/SwipeableCalendar.tsx b/src/shared/components/swipeable-calender/SwipeableCalendar.tsx index 056dc35..a29dab8 100644 --- a/src/shared/components/swipeable-calender/SwipeableCalendar.tsx +++ b/src/shared/components/swipeable-calender/SwipeableCalendar.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource @emotion/react */ import { VirtualSwiper } from "@shared/components/VirtualSwiper"; import { Month } from "@shared/period/month"; -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { CalendarNavigation } from "./Nav"; @@ -29,19 +29,27 @@ interface Props { month: Month; children: (month: Month, index?: number) => React.ReactNode; verticalHeight?: number; + maxWidth?: number } export const SwipeableCalendar = ({ month: propsMonth, children, verticalHeight, + maxWidth, }: Props) => { const [months, setMonths] = useState(loadMonths(propsMonth)); const [activeMonth, setActiveMonth] = useState(propsMonth); const resetMonths = useCallback((month: Month) => { + console.log("resetMonths"); setActiveMonth(month); setMonths(loadMonths(month)); }, []); + + useEffect(() => { + console.log(activeMonth.monthNum); + console.log(months.map(m => m.monthNum)); + }, [activeMonth, months]); const onSlideChange = useCallback((month: Month) => { setActiveMonth(month); @@ -51,10 +59,11 @@ export const SwipeableCalendar = ({
m.add(amount, "month")) diff --git a/src/shared/tab/initial-tab.ts b/src/shared/tab/initial-tab.ts index 56be7f8..1c60e69 100644 --- a/src/shared/tab/initial-tab.ts +++ b/src/shared/tab/initial-tab.ts @@ -1,4 +1,5 @@ import { DrTabType } from "@shared/tab/use-tab-route"; -export const INITIAL_TAB: DrTabType = "note"; \ No newline at end of file +// FIXME: achivement변경하기 +export const INITIAL_TAB: DrTabType = "achivement"; \ No newline at end of file diff --git a/src/widgets/calendar/ui/CalendarTile.tsx b/src/widgets/calendar/ui/CalendarTile.tsx index 0cdbde8..8d48efe 100644 --- a/src/widgets/calendar/ui/CalendarTile.tsx +++ b/src/widgets/calendar/ui/CalendarTile.tsx @@ -1,10 +1,10 @@ /** @jsxImportSource @emotion/react */ -import { Day } from "@shared/period/day"; -import { Tile } from "../model/load-tile-map"; +import { Day, DayOfWeek } from "@shared/period/day"; +import { Tile } from "../model/types"; import { TEXT_CSS } from "@shared/components/text-style"; import { Task } from "@entities/note"; import { css } from "@emotion/react"; -import { useContext } from "react"; +import { useContext, useEffect } from "react"; import { useTileHeightInfo } from "./tile-height-info-context"; @@ -15,6 +15,8 @@ const defaultTextStyle = css({ const DateBadge = ({ day }: { day: Day }) => { + const isWeekend = day.isSameDow(DayOfWeek.SAT) || day.isSameDow(DayOfWeek.SUN); + return (
{ borderRadius: '50%', textAlign: 'start', backgroundColor: 'transparent', - // textAlign: day.isToday() ? 'center' : 'start', - // backgroundColor: day.isToday() ? 'var(--color-accent)' : 'transparent', + color: isWeekend ? 'var(--color-accent-1)' : "var(--text-color)", }}> {day.date} @@ -47,7 +48,9 @@ const TaskLineContainer = ({ tasks }: { tasks: Task[] }) => { const { limitedTaskPer } = useTileHeightInfo(); if(tasks.length <= limitedTaskPer){ return ( -
+
{tasks.map(task => ( { } return ( -
+
{tasks.slice(0, limitedTaskPer - 1).map(task => ( - @@ -88,7 +93,6 @@ const TaskLine = ({ task }: { task: Task }) => { borderRadius: '2px', textAlign: 'start', textOverflow: 'ellipsis', - overflow: "hidden", whiteSpace: "nowrap", }]}> {task.name} diff --git a/src/widgets/calendar/ui/CalendarWidget.tsx b/src/widgets/calendar/ui/CalendarWidget.tsx index 849a025..f682e7c 100644 --- a/src/widgets/calendar/ui/CalendarWidget.tsx +++ b/src/widgets/calendar/ui/CalendarWidget.tsx @@ -1,14 +1,6 @@ /** @jsxImportSource @emotion/react */ import { Month } from "@shared/period/month"; -import { useCallback, useMemo, useState } from "react"; -import { useLeaf } from "@shared/view/use-leaf"; import { SwipeableCalendar } from "@shared/components/swipeable-calender/SwipeableCalendar"; -import { useAsync } from "@shared/utils/use-async"; -import { loadTileMap } from "../model/load-tile-map"; -import { useTabRoute } from "@shared/tab/use-tab-route"; -import { Day } from "@shared/period/day"; -import { CalendarTile } from "./CalendarTile"; -import { Task } from "@entities/note"; import { useTabHeight } from "@app/ui/use-tab-height"; import { TileHeightInfoProvider } from "./tile-height-info-context"; import { CalendarSlide } from "./CalendarSlide"; diff --git a/src/widgets/note-achivement/index.ts b/src/widgets/note-achivement/index.ts index 64626d4..832592d 100644 --- a/src/widgets/note-achivement/index.ts +++ b/src/widgets/note-achivement/index.ts @@ -1 +1 @@ -export { NoteAchivementCalendar } from "./ui/NoteAchivementCalendar"; \ No newline at end of file +export { NoteAchivementWidget } from "./ui/NoteAchivementWidget"; \ No newline at end of file diff --git a/src/widgets/note-achivement/ui/CalendarSlide.tsx b/src/widgets/note-achivement/ui/CalendarSlide.tsx index 84cc331..c7281ef 100644 --- a/src/widgets/note-achivement/ui/CalendarSlide.tsx +++ b/src/widgets/note-achivement/ui/CalendarSlide.tsx @@ -7,6 +7,7 @@ import { useAsync } from "@shared/utils/use-async"; import { useCallback, useMemo } from "react"; import { useTabRoute } from "@shared/tab/use-tab-route"; import { PerformanceCircle } from "@features/performance"; +import { Badge } from "@mui/material"; interface CalendarSlideProps { @@ -16,18 +17,33 @@ export const CalendarSlide = ({ month }: CalendarSlideProps) => { const notesAsync = useAsync(async () => await noteRepository.loadBetween(month.startDay, month.endDay), [month]); const route = useTabRoute(s=>s.route); - const tile = useCallback((tileDay: Day) => { + const tile = useCallback((day: Day) => { + if(day.month !== month.monthNum) return <>; let performance = NoteEntity.getEmptyNotePerformance(); if(notesAsync.value){ - const note = notesAsync.value.find(note => note.day.isSameDay(tileDay)); + const note = notesAsync.value.find(note => note.day.isSameDay(day)); if(note){ performance = NoteEntity.getPerformance(note); } } return ( - +
+ .MuiBadge-badge": { + padding: "2px 5px", + height: "fit-content", + backgroundColor: "var(--color-accent)", + clipPath: "inset(-50px)" + } + }}> + + +
) - }, [notesAsync.value]); + }, [month.monthNum, notesAsync.value]); const onTileClick = useCallback((day: Day) => { @@ -54,7 +70,8 @@ export const CalendarSlide = ({ month }: CalendarSlideProps) => { gap: "0", }, tile: { - border: "0" + border: "0", + overflow: "visible !important", } }} /> diff --git a/src/widgets/note-achivement/ui/NoteAchivementCalendar.tsx b/src/widgets/note-achivement/ui/NoteAchivementCalendar.tsx deleted file mode 100644 index 357277f..0000000 --- a/src/widgets/note-achivement/ui/NoteAchivementCalendar.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/** @jsxImportSource @emotion/react */ -import { NoteEntity, noteRepository, RoutineNote } from '@entities/note'; -import { Day } from "@shared/period/day"; -import { BaseCalendar } from '@shared/components/BaseCalendar'; -import { useCallback, useEffect, useState } from "react"; -import { useTabRoute } from '@shared/tab/use-tab-route'; -import { Month } from '@shared/period/month'; -import { PerformanceCircle } from '@features/performance'; -import { SwipeableCalendar } from '@shared/components/swipeable-calender/SwipeableCalendar'; -import { useAsync } from '@shared/utils/use-async'; -import { useTabHeight } from '@app/ui/use-tab-height'; - - - -export interface NoteAchivementCalendarProps { - month: Month; -} -export const NoteAchivementCalendar = ({ month }: NoteAchivementCalendarProps) => { - const notesAsync = useAsync(async () => await noteRepository.loadBetween(month.startDay, month.endDay), [month]); - const route = useTabRoute(s=>s.route); - const tabHeight = useTabHeight(); - - - const tile = useCallback((tileDay: Day) => { - let performance = NoteEntity.getEmptyNotePerformance(); - if(notesAsync.value){ - const note = notesAsync.value.find(note => note.day.isSameDay(tileDay)); - if(note){ - performance = NoteEntity.getPerformance(note); - } - } - return ( - - ) - }, [notesAsync.value]); - - const onTileClick = useCallback((day: Day) => { - route("note", { day }); - }, [route]); - - - if(notesAsync.loading) return
Loading...
; - return ( - - ) -} \ No newline at end of file diff --git a/src/widgets/note-achivement/ui/NoteAchivementWidget.tsx b/src/widgets/note-achivement/ui/NoteAchivementWidget.tsx new file mode 100644 index 0000000..f9ed221 --- /dev/null +++ b/src/widgets/note-achivement/ui/NoteAchivementWidget.tsx @@ -0,0 +1,24 @@ +/** @jsxImportSource @emotion/react */ +import { SwipeableCalendar } from '@shared/components/swipeable-calender/SwipeableCalendar'; +import { Month } from '@shared/period/month'; +import { CalendarSlide } from './CalendarSlide'; + + + +export interface NoteAchivementCalendarProps { + month: Month; + height: number; + maxWidth: number; +} +export const NoteAchivementWidget = ({ month, height, maxWidth }: NoteAchivementCalendarProps) => { + + return ( + + {month => } + + ) +} \ No newline at end of file diff --git a/src/widgets/routine-achivement/index.ts b/src/widgets/routine-achivement/index.ts new file mode 100644 index 0000000..a330a16 --- /dev/null +++ b/src/widgets/routine-achivement/index.ts @@ -0,0 +1,2 @@ + +export { RoutineAchivementWidget } from "./ui/RoutineAchivementWidget"; \ No newline at end of file diff --git a/src/widgets/routine-achivement/model/resolve-routine-tiles.ts b/src/widgets/routine-achivement/model/resolve-routine-tiles.ts new file mode 100644 index 0000000..372a1cd --- /dev/null +++ b/src/widgets/routine-achivement/model/resolve-routine-tiles.ts @@ -0,0 +1,43 @@ +import { NoteEntity, noteRepository, RoutineNote } from "@entities/note"; +import { Tile, TileMap } from "./types"; +import { Month } from "@shared/period/month"; +import { DayFormat } from "@shared/period/day"; + + + +/** + * + * @param routineName + * @param month + */ +export const resolveRoutineTiles = async (routineName: string, month: Month): Promise => { + const notes = await noteRepository.loadBetween(month.startDay, month.endDay); + const tileMap = new Map(); + + const end = month.startDay.daysInMonth(); + for(let d = 1; d <= end; d++) { + const day = month.startDay.clone(m => m.add(d-1, "day")); + const note = notes.find(note => note.day.isSameDay(day)); + + let tile: Tile | null = null; + // 노트가 존재 + if(note){ + const routineTask = NoteEntity.findTask(note, routineName); + // 노트에 routine이 존재 + if(routineTask){ + tile = { + day, + state: routineTask.state + } + } + } + if(!tile){ + tile = { + day, + state: "inactive" + } + } + tileMap.set(day.format(), tile); + } + return tileMap; +} \ No newline at end of file diff --git a/src/widgets/routine-achivement/model/types.ts b/src/widgets/routine-achivement/model/types.ts new file mode 100644 index 0000000..ce71d07 --- /dev/null +++ b/src/widgets/routine-achivement/model/types.ts @@ -0,0 +1,10 @@ +import { NoteEntity, RoutineNote, TaskState } from "@entities/note"; +import { Day, DayFormat } from "@shared/period/day"; +import { Month } from "@shared/period/month"; + + +export type TileMap = Map; +export type Tile = { + day: Day; + state: TaskState | "inactive"; +} \ No newline at end of file diff --git a/src/widgets/routine-achivement/ui/CalendarSlide.tsx b/src/widgets/routine-achivement/ui/CalendarSlide.tsx new file mode 100644 index 0000000..1b9b8f3 --- /dev/null +++ b/src/widgets/routine-achivement/ui/CalendarSlide.tsx @@ -0,0 +1,57 @@ +/** @jsxImportSource @emotion/react */ + +import { noteRepository } from "@entities/note"; +import { BaseCalendar } from "@shared/components/BaseCalendar"; +import { Day } from "@shared/period/day"; +import { Month } from "@shared/period/month"; +import { useAsync } from "@shared/utils/use-async"; +import { useCallback } from "react"; +import { CalendarTile } from "./CalendarTile"; +import { Tile } from "../model/types"; +import { resolveRoutineTiles } from "../model/resolve-routine-tiles"; + + +interface Props { + month: Month; + routineName: string; +} +export const CalendarSlide = ({ routineName, month }: Props) => { + const tilesAsync = useAsync(async () => await resolveRoutineTiles(routineName, month), [month]); + + const tile = useCallback((day: Day) => { + if(day.month !== month.monthNum) return <>; + const tile = tilesAsync.value?.get(day.format()); + if(!tile) throw new Error(`tile not found for ${day.format()}`); + return ; + }, [month.monthNum, tilesAsync.value]); + + const onTileClick = useCallback((day: Day) => { + console.log(day); + }, []); + + const tileDisabled = useCallback((day: Day) => { + return day.month !== month.monthNum; + }, [month]); + + + if(tilesAsync.loading) return
Loading...
; + return ( + {}} + onTileClick={onTileClick} + tile={tile} + showNavigation={false} + tileDisabled={tileDisabled} + styleOptions={{ + tileContainer: { + gap: "0", + }, + tile: { + border: "0", + overflow: "visible !important", + } + }} + /> + ); +}; diff --git a/src/widgets/routine-achivement/ui/CalendarTile.tsx b/src/widgets/routine-achivement/ui/CalendarTile.tsx new file mode 100644 index 0000000..5b90208 --- /dev/null +++ b/src/widgets/routine-achivement/ui/CalendarTile.tsx @@ -0,0 +1,76 @@ +/** @jsxImportSource @emotion/react */ +import { TaskCheckbox } from "@features/task-el"; +import { Tile } from "../model/types" +import { Circle } from "@shared/components/Circle"; +import { getCustomAccentHSL } from "@shared/components/obsidian-accent-color"; +import { Day } from "@shared/period/day"; +import { Badge } from "@mui/material"; + + + + + + + +type Props = { + tile: Tile; +} +export const CalendarTile = ({ + tile, +}: Props) => { + const isActive = tile.state !== "inactive"; + const isTodayOrBefore = tile.day.isSameOrBefore(Day.today()); + const color = isActive && isTodayOrBefore ? "var(--color-accent)" : "#ececec"; + const opacity = isActive && isTodayOrBefore ? 1 : 0.5; + + return ( +
+ .MuiBadge-badge": { + padding: "2px 5px", + height: "fit-content", + } + }}> + + +
+
{tile.day.date}
+
+ { tile.state === "inactive" || !isTodayOrBefore + ? + + : + + } +
+
+
+ ) +} \ No newline at end of file diff --git a/src/widgets/routine-achivement/ui/RoutineAchivementWidget.tsx b/src/widgets/routine-achivement/ui/RoutineAchivementWidget.tsx new file mode 100644 index 0000000..d757baa --- /dev/null +++ b/src/widgets/routine-achivement/ui/RoutineAchivementWidget.tsx @@ -0,0 +1,49 @@ +/** @jsxImportSource @emotion/react */ +import { Month } from '@shared/period/month'; +import { SwipeableCalendar } from '@shared/components/swipeable-calender/SwipeableCalendar'; +import { CalendarSlide } from './CalendarSlide'; +import { TEXT_CSS } from '@shared/components/text-style'; + + + +export interface Props { + month: Month; + routineName: string; + height: number; + maxWidth: number; +} +export const RoutineAchivementWidget = ({ + month, + height, + routineName, + maxWidth +}: Props) => { + + return ( +
+ + {month => } + +
+
{routineName}
+
+
+ ) +} \ No newline at end of file diff --git a/src/widgets/routine/create-routine.ts b/src/widgets/routine/create-routine.ts index 3f47119..775b5dd 100644 --- a/src/widgets/routine/create-routine.ts +++ b/src/widgets/routine/create-routine.ts @@ -12,7 +12,7 @@ export const createNewRoutine = (): Routine => { showOnCalendar: false, activeCriteria: "week", daysOfWeek: Day.getDaysOfWeek(), - daysOfMonth: [Day.now().date], + daysOfMonth: [Day.today().date], enabled: true } } diff --git a/src/widgets/weeks/DayNodeComponent.tsx b/src/widgets/weeks/DayNodeComponent.tsx index d532689..2e8038d 100644 --- a/src/widgets/weeks/DayNodeComponent.tsx +++ b/src/widgets/weeks/DayNodeComponent.tsx @@ -34,7 +34,7 @@ export const DayNodeComponent = React.memo(({ dayNode: { day, performance: _perf }, [isActiveDay, activeNode.performance]); const afterStyle = useMemo(() => { - if(!day.isAfter(Day.now())) return css({}); + if(!day.isAfter(Day.today())) return css({}); return css({ content: "''", position: "absolute", diff --git a/src/widgets/weeks/WeeksWidget.tsx b/src/widgets/weeks/WeeksWidget.tsx index c3982d5..5d7fccb 100644 --- a/src/widgets/weeks/WeeksWidget.tsx +++ b/src/widgets/weeks/WeeksWidget.tsx @@ -21,7 +21,7 @@ export const WeeksWidget = ({ className }: WeeksProps) => { const { note, setNote } = useRoutineNote(); const activeDay = useMemo(() => note.day, [note]); const activeWeek = useMemo(() => new Week(activeDay), [activeDay]); - const currentDayPercentage = useMemo(() => NoteEntity.getPerformance(note), [note]); + const currentNotePerformance = useMemo(() => NoteEntity.getPerformance(note), [note]); const [ weeks, setWeeks ] = useState([]); const { leafBgColor } = useLeaf(); @@ -57,7 +57,7 @@ export const WeeksWidget = ({ className }: WeeksProps) => { return ( store.setState(data)} >