Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
f7a81296
Commit
f7a81296
authored
Jan 29, 2026
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
style: enhance ActivityCalendar components with improved styling and layout adjustments
parent
fcb9e377
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
53 additions
and
44 deletions
+53
-44
CalendarCell.tsx
web/src/components/ActivityCalendar/CalendarCell.tsx
+3
-3
MonthCalendar.tsx
web/src/components/ActivityCalendar/MonthCalendar.tsx
+4
-5
YearCalendar.tsx
web/src/components/ActivityCalendar/YearCalendar.tsx
+15
-11
constants.ts
web/src/components/ActivityCalendar/constants.ts
+11
-11
InsertMenu.tsx
web/src/components/MemoEditor/Toolbar/InsertMenu.tsx
+7
-4
MonthNavigator.tsx
web/src/components/StatisticsView/MonthNavigator.tsx
+13
-10
No files found.
web/src/components/ActivityCalendar/CalendarCell.tsx
View file @
f7a81296
...
@@ -26,7 +26,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => {
...
@@ -26,7 +26,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => {
const
smallExtraClasses
=
size
===
"small"
?
`
${
SMALL_CELL_SIZE
.
dimensions
}
min-h-0`
:
""
;
const
smallExtraClasses
=
size
===
"small"
?
`
${
SMALL_CELL_SIZE
.
dimensions
}
min-h-0`
:
""
;
const
baseClasses
=
cn
(
const
baseClasses
=
cn
(
"aspect-square w-full flex items-center justify-center text-center transition-all duration-
200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-2 select-none
"
,
"aspect-square w-full flex items-center justify-center text-center transition-all duration-
150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 focus-visible:ring-offset-2 select-none border border-border/10 bg-muted/20
"
,
sizeConfig
.
font
,
sizeConfig
.
font
,
sizeConfig
.
borderRadius
,
sizeConfig
.
borderRadius
,
smallExtraClasses
,
smallExtraClasses
,
...
@@ -35,7 +35,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => {
...
@@ -35,7 +35,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => {
const
ariaLabel
=
day
.
isSelected
?
`
${
tooltipText
}
(selected)`
:
tooltipText
;
const
ariaLabel
=
day
.
isSelected
?
`
${
tooltipText
}
(selected)`
:
tooltipText
;
if
(
!
day
.
isCurrentMonth
)
{
if
(
!
day
.
isCurrentMonth
)
{
return
<
div
className=
{
cn
(
baseClasses
,
"text-muted-foreground/30 bg-transparent cursor-default"
)
}
>
{
day
.
label
}
</
div
>;
return
<
div
className=
{
cn
(
baseClasses
,
"text-muted-foreground/30 bg-transparent
border-transparent
cursor-default"
)
}
>
{
day
.
label
}
</
div
>;
}
}
const
intensityClass
=
getCellIntensityClass
(
day
,
maxCount
);
const
intensityClass
=
getCellIntensityClass
(
day
,
maxCount
);
...
@@ -45,7 +45,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => {
...
@@ -45,7 +45,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => {
intensityClass
,
intensityClass
,
day
.
isToday
&&
"ring-2 ring-primary/30 ring-offset-1 font-semibold z-10"
,
day
.
isToday
&&
"ring-2 ring-primary/30 ring-offset-1 font-semibold z-10"
,
day
.
isSelected
&&
"ring-2 ring-primary ring-offset-1 font-bold z-10"
,
day
.
isSelected
&&
"ring-2 ring-primary ring-offset-1 font-bold z-10"
,
isInteractive
?
"cursor-pointer hover:
scale-110 hover:shadow-md hover:z-2
0"
:
"cursor-default"
,
isInteractive
?
"cursor-pointer hover:
bg-muted/40 hover:border-border/3
0"
:
"cursor-default"
,
);
);
const
button
=
(
const
button
=
(
...
...
web/src/components/ActivityCalendar/MonthCalendar.tsx
View file @
f7a81296
...
@@ -3,7 +3,6 @@ import { useInstance } from "@/contexts/InstanceContext";
...
@@ -3,7 +3,6 @@ import { useInstance } from "@/contexts/InstanceContext";
import
{
cn
}
from
"@/lib/utils"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
CalendarCell
}
from
"./CalendarCell"
;
import
{
CalendarCell
}
from
"./CalendarCell"
;
import
{
DEFAULT_CELL_SIZE
,
SMALL_CELL_SIZE
}
from
"./constants"
;
import
{
useTodayDate
,
useWeekdayLabels
}
from
"./hooks"
;
import
{
useTodayDate
,
useWeekdayLabels
}
from
"./hooks"
;
import
type
{
MonthCalendarProps
}
from
"./types"
;
import
type
{
MonthCalendarProps
}
from
"./types"
;
import
{
useCalendarMatrix
}
from
"./useCalendar"
;
import
{
useCalendarMatrix
}
from
"./useCalendar"
;
...
@@ -28,19 +27,19 @@ export const MonthCalendar = memo((props: MonthCalendarProps) => {
...
@@ -28,19 +27,19 @@ export const MonthCalendar = memo((props: MonthCalendarProps) => {
selectedDate
:
""
,
selectedDate
:
""
,
});
});
const
sizeConfig
=
size
===
"small"
?
SMALL_CELL_SIZE
:
DEFAULT_CELL_SIZE
;
const
gridGap
=
size
===
"small"
?
"gap-x-3 gap-y-3"
:
"gap-x-3.5 gap-y-3.5"
;
return
(
return
(
<
div
className=
{
cn
(
"flex flex-col gap-2"
,
className
)
}
>
<
div
className=
{
cn
(
"flex flex-col gap-2"
,
className
)
}
>
<
div
className=
{
cn
(
"grid grid-cols-7"
,
sizeConfig
.
g
ap
,
"text-muted-foreground mb-1"
,
size
===
"small"
?
"text-[10px]"
:
"text-xs"
)
}
>
<
div
className=
{
cn
(
"grid grid-cols-7"
,
gridG
ap
,
"text-muted-foreground mb-1"
,
size
===
"small"
?
"text-[10px]"
:
"text-xs"
)
}
>
{
rotatedWeekDays
.
map
((
label
,
index
)
=>
(
{
rotatedWeekDays
.
map
((
label
,
index
)
=>
(
<
div
key=
{
index
}
className=
"flex h-4 items-center justify-center text-muted-foreground/
60 font-medium
"
>
<
div
key=
{
index
}
className=
"flex h-4 items-center justify-center text-muted-foreground/
70 font-medium uppercase tracking-wide
"
>
{
label
}
{
label
}
</
div
>
</
div
>
))
}
))
}
</
div
>
</
div
>
<
div
className=
{
cn
(
"grid grid-cols-7
"
,
sizeConfig
.
g
ap
)
}
>
<
div
className=
{
cn
(
"grid grid-cols-7
px-2"
,
gridG
ap
)
}
>
{
weeks
.
map
((
week
,
weekIndex
)
=>
{
weeks
.
map
((
week
,
weekIndex
)
=>
week
.
days
.
map
((
day
,
dayIndex
)
=>
{
week
.
days
.
map
((
day
,
dayIndex
)
=>
{
const
tooltipText
=
getTooltipText
(
day
.
count
,
day
.
date
,
t
);
const
tooltipText
=
getTooltipText
(
day
.
count
,
day
.
date
,
t
);
...
...
web/src/components/ActivityCalendar/YearCalendar.tsx
View file @
f7a81296
...
@@ -30,18 +30,20 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
...
@@ -30,18 +30,20 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
const
handleToday
=
()
=>
onYearChange
(
currentYear
);
const
handleToday
=
()
=>
onYearChange
(
currentYear
);
return
(
return
(
<
div
className=
{
cn
(
"w-full flex flex-col gap-6 p-2 md:p-0 select-none"
,
className
)
}
>
<
div
className=
{
cn
(
"w-full flex flex-col gap-6 px-4 sm:px-0 py-4 select-none"
,
className
)
}
>
<
div
className=
"flex items-center justify-between pb-4 px-2 pt-2"
>
<
div
className=
"flex items-center justify-between px-1"
>
<
h2
className=
"text-3xl font-bold text-foreground tracking-tight leading-none"
>
{
selectedYear
}
</
h2
>
<
div
className=
"flex items-baseline gap-3"
>
<
h2
className=
"text-2xl md:text-3xl font-semibold text-foreground tracking-tight leading-none"
>
{
selectedYear
}
</
h2
>
</
div
>
<
div
className=
"inline-flex items-center gap-1 shrink-0"
>
<
div
className=
"inline-flex items-center gap-1 shrink-0
rounded-lg border border-border/20 bg-muted/20 p-1
"
>
<
Button
<
Button
variant=
"ghost"
variant=
"ghost"
size=
"sm"
size=
"sm"
onClick=
{
handlePrevYear
}
onClick=
{
handlePrevYear
}
disabled=
{
!
canGoPrev
}
disabled=
{
!
canGoPrev
}
aria
-
label=
"Previous year"
aria
-
label=
"Previous year"
className=
"h-
9 w-9 p-0 rounded-md hover:bg-secondary/8
0 text-muted-foreground hover:text-foreground"
className=
"h-
8 w-8 p-0 rounded-md hover:bg-muted/3
0 text-muted-foreground hover:text-foreground"
>
>
<
ChevronLeftIcon
className=
"w-5 h-5"
/>
<
ChevronLeftIcon
className=
"w-5 h-5"
/>
</
Button
>
</
Button
>
...
@@ -53,8 +55,8 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
...
@@ -53,8 +55,8 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
disabled=
{
isCurrentYear
}
disabled=
{
isCurrentYear
}
aria
-
label=
{
t
(
"common.today"
)
}
aria
-
label=
{
t
(
"common.today"
)
}
className=
{
cn
(
className=
{
cn
(
"h-
9 px-4 rounded-md text-sm font-medium
transition-colors"
,
"h-
8 px-3 rounded-md text-[11px] font-semibold uppercase tracking-[0.18em]
transition-colors"
,
isCurrentYear
?
"bg-
secondary/50 text-muted-foreground cursor-default"
:
"hover:bg-secondary/8
0 text-foreground"
,
isCurrentYear
?
"bg-
muted/30 text-muted-foreground cursor-default"
:
"hover:bg-muted/3
0 text-foreground"
,
)
}
)
}
>
>
{
t
(
"common.today"
)
}
{
t
(
"common.today"
)
}
...
@@ -66,7 +68,7 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
...
@@ -66,7 +68,7 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
onClick=
{
handleNextYear
}
onClick=
{
handleNextYear
}
disabled=
{
!
canGoNext
}
disabled=
{
!
canGoNext
}
aria
-
label=
"Next year"
aria
-
label=
"Next year"
className=
"h-
9 w-9 p-0 rounded-md hover:bg-secondary/8
0 text-muted-foreground hover:text-foreground"
className=
"h-
8 w-8 p-0 rounded-md hover:bg-muted/3
0 text-muted-foreground hover:text-foreground"
>
>
<
ChevronRightIcon
className=
"w-5 h-5"
/>
<
ChevronRightIcon
className=
"w-5 h-5"
/>
</
Button
>
</
Button
>
...
@@ -75,13 +77,15 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
...
@@ -75,13 +77,15 @@ export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, cl
<
TooltipProvider
>
<
TooltipProvider
>
<
div
className=
"w-full animate-fade-in"
>
<
div
className=
"w-full animate-fade-in"
>
<
div
className=
"grid gap-6
grid-cols-1 sm:grid-cols-2 md:grid-cols-3
"
>
<
div
className=
"grid gap-6
md:gap-7 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4
"
>
{
months
.
map
((
month
)
=>
(
{
months
.
map
((
month
)
=>
(
<
div
<
div
key=
{
month
}
key=
{
month
}
className=
"flex flex-col gap-3 rounded-
lg p-3 hover:bg-secondary/40 transition-colors cursor-default border border-transparent hover:border-border/50
"
className=
"flex flex-col gap-3 rounded-
2xl border border-border/20 bg-muted/10 p-4 shadow-sm hover:shadow-md transition-shadow cursor-default
"
>
>
<
div
className=
"text-xs font-bold text-foreground/80 uppercase tracking-widest pl-1"
>
{
getMonthLabel
(
month
)
}
</
div
>
<
div
className=
"text-[11px] font-semibold text-muted-foreground uppercase tracking-[0.22em] pl-1"
>
{
getMonthLabel
(
month
)
}
</
div
>
<
MonthCalendar
month=
{
month
}
data=
{
yearData
}
maxCount=
{
yearMaxCount
}
size=
"small"
onClick=
{
onDateClick
}
/>
<
MonthCalendar
month=
{
month
}
data=
{
yearData
}
maxCount=
{
yearMaxCount
}
size=
"small"
onClick=
{
onDateClick
}
/>
</
div
>
</
div
>
))
}
))
}
...
...
web/src/components/ActivityCalendar/constants.ts
View file @
f7a81296
...
@@ -14,22 +14,22 @@ export const INTENSITY_THRESHOLDS = {
...
@@ -14,22 +14,22 @@ export const INTENSITY_THRESHOLDS = {
}
as
const
;
}
as
const
;
export
const
CELL_STYLES
=
{
export
const
CELL_STYLES
=
{
HIGH
:
"bg-primary text-primary-foreground shadow-sm"
,
HIGH
:
"bg-primary text-primary-foreground shadow-sm
border-transparent
"
,
MEDIUM
:
"bg-primary/8
0 text-primary-foreground shadow-sm
"
,
MEDIUM
:
"bg-primary/8
5 text-primary-foreground shadow-sm border-transparent
"
,
LOW
:
"bg-primary/
60 text-primary-foreground shadow-sm
"
,
LOW
:
"bg-primary/
70 text-primary-foreground border-transparent
"
,
MINIMAL
:
"bg-primary/
40 text-foreground
"
,
MINIMAL
:
"bg-primary/
50 text-foreground border-transparent
"
,
EMPTY
:
"bg-
secondary/30 text-muted-foreground hover:bg-secondary/5
0"
,
EMPTY
:
"bg-
muted/20 text-muted-foreground hover:bg-muted/30 border-border/1
0"
,
}
as
const
;
}
as
const
;
export
const
SMALL_CELL_SIZE
=
{
export
const
SMALL_CELL_SIZE
=
{
font
:
"text-
xs
"
,
font
:
"text-
[11px]
"
,
dimensions
:
"w-
8 h-8 mx-auto
"
,
dimensions
:
"w-
full h-full
"
,
borderRadius
:
"rounded-
md
"
,
borderRadius
:
"rounded-
lg
"
,
gap
:
"gap-1"
,
gap
:
"gap-1
.5
"
,
}
as
const
;
}
as
const
;
export
const
DEFAULT_CELL_SIZE
=
{
export
const
DEFAULT_CELL_SIZE
=
{
font
:
"text-xs"
,
font
:
"text-xs"
,
borderRadius
:
"rounded-
md
"
,
borderRadius
:
"rounded-
lg
"
,
gap
:
"gap-
1.5
"
,
gap
:
"gap-
2
"
,
}
as
const
;
}
as
const
;
web/src/components/MemoEditor/Toolbar/InsertMenu.tsx
View file @
f7a81296
import
{
LatLng
}
from
"leaflet"
;
import
{
LatLng
}
from
"leaflet"
;
import
{
uniqBy
}
from
"lodash-es"
;
import
{
uniqBy
}
from
"lodash-es"
;
import
{
FileIcon
,
LinkIcon
,
LoaderIcon
,
MapPinIcon
,
Maximize2Icon
,
MoreHorizontalIcon
,
PlusIcon
,
type
Lucide
Icon
}
from
"lucide-react"
;
import
{
FileIcon
,
LinkIcon
,
LoaderIcon
,
type
LucideIcon
,
MapPinIcon
,
Maximize2Icon
,
MoreHorizontalIcon
,
Plus
Icon
}
from
"lucide-react"
;
import
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
"react"
;
import
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
"react"
;
import
{
useDebounce
}
from
"react-use"
;
import
{
useDebounce
}
from
"react-use"
;
import
{
useReverseGeocoding
}
from
"@/components/map"
;
import
{
useReverseGeocoding
}
from
"@/components/map"
;
...
@@ -106,9 +106,12 @@ const InsertMenu = (props: InsertMenuProps) => {
...
@@ -106,9 +106,12 @@ const InsertMenu = (props: InsertMenuProps) => {
setLocationDialogOpen
(
false
);
setLocationDialogOpen
(
false
);
},
[
location
]);
},
[
location
]);
const
handlePositionChange
=
useCallback
((
position
:
LatLng
)
=>
{
const
handlePositionChange
=
useCallback
(
location
.
handlePositionChange
(
position
);
(
position
:
LatLng
)
=>
{
},
[
location
]);
location
.
handlePositionChange
(
position
);
},
[
location
],
);
const
handleToggleFocusMode
=
useCallback
(()
=>
{
const
handleToggleFocusMode
=
useCallback
(()
=>
{
onToggleFocusMode
?.();
onToggleFocusMode
?.();
...
...
web/src/components/StatisticsView/MonthNavigator.tsx
View file @
f7a81296
import
dayjs
from
"dayjs"
;
import
dayjs
from
"dayjs"
;
import
{
Chevron
DownIcon
,
Chevron
LeftIcon
,
ChevronRightIcon
}
from
"lucide-react"
;
import
{
ChevronLeftIcon
,
ChevronRightIcon
}
from
"lucide-react"
;
import
{
useState
}
from
"react"
;
import
{
useState
}
from
"react"
;
import
{
YearCalendar
}
from
"@/components/ActivityCalendar"
;
import
{
YearCalendar
}
from
"@/components/ActivityCalendar"
;
import
{
Dialog
,
DialogContent
,
DialogTitle
,
DialogTrigger
}
from
"@/components/ui/dialog"
;
import
{
Dialog
,
DialogContent
,
DialogTitle
,
DialogTrigger
}
from
"@/components/ui/dialog"
;
...
@@ -31,33 +31,36 @@ export const MonthNavigator = ({ visibleMonth, onMonthChange, activityStats }: M
...
@@ -31,33 +31,36 @@ export const MonthNavigator = ({ visibleMonth, onMonthChange, activityStats }: M
};
};
return
(
return
(
<
div
className=
"w-full mb-2 flex flex-row justify-between items-center gap-
1
"
>
<
div
className=
"w-full mb-2 flex flex-row justify-between items-center gap-
2
"
>
<
Dialog
open=
{
isOpen
}
onOpenChange=
{
setIsOpen
}
>
<
Dialog
open=
{
isOpen
}
onOpenChange=
{
setIsOpen
}
>
<
DialogTrigger
asChild
>
<
DialogTrigger
asChild
>
<
button
className=
"p
x-2 py-1 -ml-2 rounded-md hover:bg-secondary/50 text-sm text-foreground font-semibold transition-colors flex items-center gap-1 select-none group
"
>
<
button
className=
"p
y-1 text-sm text-foreground font-medium transition-colors flex items-center select-none
"
>
{
currentMonth
.
toLocaleString
(
i18n
.
language
,
{
year
:
"numeric"
,
month
:
"long"
})
}
{
currentMonth
.
toLocaleString
(
i18n
.
language
,
{
year
:
"numeric"
,
month
:
"long"
})
}
<
ChevronDownIcon
className=
"w-3.5 h-3.5 text-muted-foreground group-hover:text-foreground transition-colors"
/>
</
button
>
</
button
>
</
DialogTrigger
>
</
DialogTrigger
>
<
DialogContent
className=
"p-0 border-none bg-background md:max-w-4xl"
size=
"2xl"
showCloseButton=
{
false
}
>
<
DialogContent
className=
"p-0 border border-border/20 bg-background md:max-w-6xl w-[min(100vw-24px,1200px)] max-h-[85vh] overflow-auto rounded-2xl shadow-2xl"
size=
"2xl"
showCloseButton=
{
false
}
>
<
DialogTitle
className=
"sr-only"
>
Select Month
</
DialogTitle
>
<
DialogTitle
className=
"sr-only"
>
Select Month
</
DialogTitle
>
<
YearCalendar
selectedYear=
{
currentYear
}
data=
{
activityStats
}
onYearChange=
{
handleYearChange
}
onDateClick=
{
handleDateClick
}
/>
<
YearCalendar
selectedYear=
{
currentYear
}
data=
{
activityStats
}
onYearChange=
{
handleYearChange
}
onDateClick=
{
handleDateClick
}
/>
</
DialogContent
>
</
DialogContent
>
</
Dialog
>
</
Dialog
>
<
div
className=
"flex justify-end items-center shrink-0
gap-0.5
"
>
<
div
className=
"flex justify-end items-center shrink-0"
>
<
button
<
button
className=
"
p-1 rounded-md hover:bg-secondary/5
0 text-muted-foreground hover:text-foreground transition-all"
className=
"
h-8 w-8 rounded-lg hover:border-border/40 hover:bg-muted/3
0 text-muted-foreground hover:text-foreground transition-all"
onClick=
{
handlePrevMonth
}
onClick=
{
handlePrevMonth
}
aria
-
label=
"Previous month"
aria
-
label=
"Previous month"
>
>
<
ChevronLeftIcon
className=
"w-4 h-4"
/>
<
ChevronLeftIcon
className=
"w-4 h-4
mx-auto
"
/>
</
button
>
</
button
>
<
button
<
button
className=
"
p-1 rounded-md hover:bg-secondary/5
0 text-muted-foreground hover:text-foreground transition-all"
className=
"
h-8 w-8 rounded-lg hover:border-border/40 hover:bg-muted/3
0 text-muted-foreground hover:text-foreground transition-all"
onClick=
{
handleNextMonth
}
onClick=
{
handleNextMonth
}
aria
-
label=
"Next month"
aria
-
label=
"Next month"
>
>
<
ChevronRightIcon
className=
"w-4 h-4"
/>
<
ChevronRightIcon
className=
"w-4 h-4
mx-auto
"
/>
</
button
>
</
button
>
</
div
>
</
div
>
</
div
>
</
div
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment