Skip to content

Commit 6d8044e

Browse files
committed
feat: Add Korean date expression support with Jira-style Today arrow
- Add getWeekOfMonth function for monthly week calculation - Add Korean locale support (locale='kor') in calendar component - Year view: Display as '2024년 1월' - Month view: Show month names in Korean - Week view: Display as '1월 1주' format - Day view: Display as '1월 1일 (월)' format - Hour/PartOfDay views: Korean date format support - Add Jira-style Today indicator with orange arrow - Replace simple rect with SVG containing vertical line and triangle arrow - Orange color (#fea362) for better visibility - Improve tooltip date format - Separate task name and date information - Use 'YYYY-MM-DD ~ YYYY-MM-DD' format - Simplify project visual style - Remove triangular decorations from project bars - Clean rectangular design only - Add fontSize={12} to task labels for better readability - Add Korean locale examples in demo app (locale='kor') - Update calendar styling with divider lines and center alignment Based on PR MaTeMaTuK#254 from MaTeMaTuK/gantt-task-react by ohshinyeop Korean date expressions are fundamentally different from English formats
1 parent f7becb1 commit 6d8044e

File tree

7 files changed

+235
-117
lines changed

7 files changed

+235
-117
lines changed

example/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const App = () => {
7878
<Gantt
7979
tasks={tasks}
8080
viewMode={view}
81+
locale="kor"
8182
onDateChange={handleTaskChange}
8283
onDelete={handleTaskDelete}
8384
onProgressChange={handleProgressChange}
@@ -92,6 +93,7 @@ const App = () => {
9293
<Gantt
9394
tasks={tasks}
9495
viewMode={view}
96+
locale="kor"
9597
onDateChange={handleTaskChange}
9698
onDelete={handleTaskDelete}
9799
onProgressChange={handleProgressChange}

src/components/calendar/calendar.tsx

Lines changed: 161 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
getDaysInMonth,
77
getLocalDayOfWeek,
88
getLocaleMonth,
9-
getWeekNumberISO8601,
9+
getWeekOfMonth,
1010
} from "../../helpers/date-helper";
1111
import { DateSetup } from "../../types/date-setup";
1212
import styles from "./calendar.module.css";
@@ -40,14 +40,24 @@ export const Calendar: React.FC<CalendarProps> = ({
4040
const date = dateSetup.dates[i];
4141
const bottomValue = date.getFullYear();
4242
bottomValues.push(
43-
<text
44-
key={date.getTime()}
45-
y={headerHeight * 0.8}
46-
x={columnWidth * i + columnWidth * 0.5}
47-
className={styles.calendarBottomText}
48-
>
49-
{bottomValue}
50-
</text>
43+
<g key={date.getTime()}>
44+
<line
45+
x1={columnWidth * i}
46+
y1={headerHeight * 0.6}
47+
x2={columnWidth * i}
48+
y2={headerHeight}
49+
style={{ stroke: "black", strokeWidth: 1 }}
50+
/>
51+
<text
52+
y={headerHeight * 0.8}
53+
x={columnWidth * i + columnWidth * 0.5}
54+
textAnchor="middle"
55+
alignmentBaseline="middle"
56+
className={styles.calendarBottomText}
57+
>
58+
{bottomValue}
59+
</text>
60+
</g>
5161
);
5262
if (
5363
i === 0 ||
@@ -129,14 +139,24 @@ export const Calendar: React.FC<CalendarProps> = ({
129139
const date = dateSetup.dates[i];
130140
const bottomValue = getLocaleMonth(date, locale);
131141
bottomValues.push(
132-
<text
133-
key={bottomValue + date.getFullYear()}
134-
y={headerHeight * 0.8}
135-
x={columnWidth * i + columnWidth * 0.5}
136-
className={styles.calendarBottomText}
137-
>
138-
{bottomValue}
139-
</text>
142+
<g key={date.getTime()}>
143+
<line
144+
x1={columnWidth * i}
145+
y1={headerHeight * 0.6}
146+
x2={columnWidth * i}
147+
y2={headerHeight}
148+
style={{ stroke: "black", strokeWidth: 1 }}
149+
/>
150+
<text
151+
y={headerHeight * 0.8}
152+
x={columnWidth * i + columnWidth * 0.5}
153+
textAnchor="middle"
154+
alignmentBaseline="middle"
155+
className={styles.calendarBottomText}
156+
>
157+
{bottomValue}
158+
</text>
159+
</g>
140160
);
141161
if (
142162
i === 0 ||
@@ -176,20 +196,37 @@ export const Calendar: React.FC<CalendarProps> = ({
176196
let topValue = "";
177197
if (i === 0 || date.getMonth() !== dates[i - 1].getMonth()) {
178198
// top
179-
topValue = `${getLocaleMonth(date, locale)}, ${date.getFullYear()}`;
199+
if (locale === "kor") {
200+
topValue = `${date.getFullYear()}${getLocaleMonth(date, locale)}`;
201+
} else {
202+
topValue = `${getLocaleMonth(date, locale)}, ${date.getFullYear()}`;
203+
}
180204
}
181205
// bottom
182-
const bottomValue = `W${getWeekNumberISO8601(date)}`;
206+
const bottomValue =
207+
locale === "kor"
208+
? `${getLocaleMonth(date, locale)} ${getWeekOfMonth(date)}주`
209+
: `W${getWeekOfMonth(date)}`;
183210

184211
bottomValues.push(
185-
<text
186-
key={date.getTime()}
187-
y={headerHeight * 0.8}
188-
x={columnWidth * (i + +rtl)}
189-
className={styles.calendarBottomText}
190-
>
191-
{bottomValue}
192-
</text>
212+
<g key={date.getTime()}>
213+
<line
214+
x1={columnWidth * i}
215+
y1={headerHeight * 0.6}
216+
x2={columnWidth * i}
217+
y2={headerHeight}
218+
style={{ stroke: "black", strokeWidth: 1 }}
219+
/>
220+
<text
221+
y={headerHeight * 0.8}
222+
x={columnWidth * i + columnWidth * 0.5}
223+
textAnchor="middle"
224+
alignmentBaseline="middle"
225+
className={styles.calendarBottomText}
226+
>
227+
{bottomValue}
228+
</text>
229+
</g>
193230
);
194231

195232
if (topValue) {
@@ -221,19 +258,39 @@ export const Calendar: React.FC<CalendarProps> = ({
221258
const dates = dateSetup.dates;
222259
for (let i = 0; i < dates.length; i++) {
223260
const date = dates[i];
224-
const bottomValue = `${getLocalDayOfWeek(date, locale, "short")}, ${date
225-
.getDate()
226-
.toString()}`;
261+
const bottomValue =
262+
locale === "kor"
263+
? `${getLocaleMonth(
264+
date,
265+
locale
266+
)} ${date.getDate()}일 (${getLocalDayOfWeek(
267+
date,
268+
locale,
269+
"short"
270+
)})`
271+
: `${getLocalDayOfWeek(date, locale, "short")}, ${date
272+
.getDate()
273+
.toString()}`;
227274

228275
bottomValues.push(
229-
<text
230-
key={date.getTime()}
231-
y={headerHeight * 0.8}
232-
x={columnWidth * i + columnWidth * 0.5}
233-
className={styles.calendarBottomText}
234-
>
235-
{bottomValue}
236-
</text>
276+
<g key={date.getTime()}>
277+
<line
278+
x1={columnWidth * i}
279+
y1={headerHeight * 0.6}
280+
x2={columnWidth * i}
281+
y2={headerHeight}
282+
style={{ stroke: "black", strokeWidth: 1 }}
283+
/>
284+
<text
285+
y={headerHeight * 0.8}
286+
x={columnWidth * i + columnWidth * 0.5}
287+
textAnchor="middle"
288+
alignmentBaseline="middle"
289+
className={styles.calendarBottomText}
290+
>
291+
{bottomValue}
292+
</text>
293+
</g>
237294
);
238295
if (
239296
i + 1 !== dates.length &&
@@ -275,22 +332,41 @@ export const Calendar: React.FC<CalendarProps> = ({
275332
}).format(date);
276333

277334
bottomValues.push(
278-
<text
279-
key={date.getTime()}
280-
y={headerHeight * 0.8}
281-
x={columnWidth * (i + +rtl)}
282-
className={styles.calendarBottomText}
283-
fontFamily={fontFamily}
284-
>
285-
{bottomValue}
286-
</text>
335+
<g key={date.getTime()}>
336+
<line
337+
x1={columnWidth * i}
338+
y1={headerHeight * 0.6}
339+
x2={columnWidth * i}
340+
y2={headerHeight}
341+
style={{ stroke: "black", strokeWidth: 1 }}
342+
/>
343+
<text
344+
y={headerHeight * 0.8}
345+
x={columnWidth * i + columnWidth * 0.5}
346+
textAnchor="middle"
347+
alignmentBaseline="middle"
348+
className={styles.calendarBottomText}
349+
>
350+
{bottomValue}
351+
</text>
352+
</g>
287353
);
288354
if (i === 0 || date.getDate() !== dates[i - 1].getDate()) {
289-
const topValue = `${getLocalDayOfWeek(
290-
date,
291-
locale,
292-
"short"
293-
)}, ${date.getDate()} ${getLocaleMonth(date, locale)}`;
355+
const topValue =
356+
locale === "kor"
357+
? `${getLocaleMonth(
358+
date,
359+
locale
360+
)} ${date.getDate()}일 (${getLocalDayOfWeek(
361+
date,
362+
locale,
363+
"short"
364+
)})`
365+
: `${getLocalDayOfWeek(
366+
date,
367+
locale,
368+
"short"
369+
)}, ${date.getDate()} ${getLocaleMonth(date, locale)}`;
294370
topValues.push(
295371
<TopPartOfCalendar
296372
key={topValue + date.getFullYear()}
@@ -320,23 +396,42 @@ export const Calendar: React.FC<CalendarProps> = ({
320396
}).format(date);
321397

322398
bottomValues.push(
323-
<text
324-
key={date.getTime()}
325-
y={headerHeight * 0.8}
326-
x={columnWidth * (i + +rtl)}
327-
className={styles.calendarBottomText}
328-
fontFamily={fontFamily}
329-
>
330-
{bottomValue}
331-
</text>
399+
<g key={date.getTime()}>
400+
<line
401+
x1={columnWidth * i}
402+
y1={headerHeight * 0.6}
403+
x2={columnWidth * i}
404+
y2={headerHeight}
405+
style={{ stroke: "black", strokeWidth: 1 }}
406+
/>
407+
<text
408+
y={headerHeight * 0.8}
409+
x={columnWidth * i + columnWidth * 0.5}
410+
textAnchor="middle"
411+
alignmentBaseline="middle"
412+
className={styles.calendarBottomText}
413+
>
414+
{bottomValue}
415+
</text>
416+
</g>
332417
);
333418
if (i !== 0 && date.getDate() !== dates[i - 1].getDate()) {
334419
const displayDate = dates[i - 1];
335-
const topValue = `${getLocalDayOfWeek(
336-
displayDate,
337-
locale,
338-
"long"
339-
)}, ${displayDate.getDate()} ${getLocaleMonth(displayDate, locale)}`;
420+
const topValue =
421+
locale === "kor"
422+
? `${getLocaleMonth(
423+
displayDate,
424+
locale
425+
)} ${displayDate.getDate()}일 (${getLocalDayOfWeek(
426+
displayDate,
427+
locale,
428+
"long"
429+
)})`
430+
: `${getLocalDayOfWeek(
431+
date,
432+
locale,
433+
"long"
434+
)}, ${date.getDate()} ${getLocaleMonth(date, locale)}`;
340435
const topPosition = (date.getHours() - 24) / 2;
341436
topValues.push(
342437
<TopPartOfCalendar

src/components/grid/grid-body.tsx

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,41 @@ export const GridBody: React.FC<GridBodyProps> = ({
8888
).getTime() >= now.getTime())
8989
) {
9090
today = (
91-
<rect
92-
x={tickX}
93-
y={0}
94-
width={columnWidth}
95-
height={y}
96-
fill={todayColor}
97-
/>
91+
<svg z={1000}>
92+
{/* 사각형 (배경) */}
93+
<rect
94+
x={tickX}
95+
y={0}
96+
width={columnWidth}
97+
height={y}
98+
fill={todayColor}
99+
/>
100+
101+
{/* 중앙 세로선 */}
102+
<rect
103+
x={tickX + columnWidth / 2 - 0.5}
104+
y={5}
105+
width={1}
106+
height={y - 5}
107+
style={{
108+
fill: "#fea362",
109+
stroke: "#fea362",
110+
strokeWidth: 1,
111+
}}
112+
/>
113+
114+
{/* 세로로 긴 역삼각형 화살표 머리 */}
115+
<polygon
116+
points={`${tickX + columnWidth / 2 - 5},0 ${
117+
tickX + columnWidth / 2 + 5
118+
},0 ${tickX + columnWidth / 2},5 `}
119+
style={{
120+
fill: "#fea362",
121+
stroke: "#fea362",
122+
strokeWidth: 1,
123+
}}
124+
/>
125+
</svg>
98126
);
99127
}
100128
// rtl for today

src/components/other/tooltip.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,18 @@ export const StandardTooltipContent: React.FC<{
123123
};
124124
return (
125125
<div className={styles.tooltipDefaultContainer} style={style}>
126-
<b style={{ fontSize: fontSize + 6 }}>{`${
127-
task.name
128-
}: ${task.start.getDate()}-${
129-
task.start.getMonth() + 1
130-
}-${task.start.getFullYear()} - ${task.end.getDate()}-${
131-
task.end.getMonth() + 1
132-
}-${task.end.getFullYear()}`}</b>
126+
<b style={{ fontSize: fontSize + 6 }}>{`${task.name}`}</b>
127+
<p></p>
128+
<p
129+
className={styles.tooltipDefaultContainerParagraph}
130+
style={{ marginTop: 4 }}
131+
>
132+
{`date : ${task.start.getFullYear()}-${
133+
task.start.getMonth() + 1
134+
}-${task.start.getDate()} ~ ${task.end.getFullYear()}-${
135+
task.end.getMonth() + 1
136+
}-${task.end.getDate()}`}
137+
</p>
133138
{task.end.getTime() - task.start.getTime() !== 0 && (
134139
<p className={styles.tooltipDefaultContainerParagraph}>{`Duration: ${~~(
135140
(task.end.getTime() - task.start.getTime()) /

0 commit comments

Comments
 (0)