From fa10e28639539d7828d0f122f6ef04d9cb689ee4 Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Sat, 11 Nov 2023 21:59:25 +0330 Subject: [PATCH 1/8] Fix rtl support --- example/src/App.tsx | 16 ++ src/components/gantt/gantt.tsx | 3 +- src/components/task-list/task-list-table.tsx | 153 ++++++++++--------- src/components/task-list/task-list.tsx | 3 + src/helpers/bar-helper.ts | 5 +- 5 files changed, 102 insertions(+), 78 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index c2ab602eb..8a6f09647 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -103,6 +103,22 @@ const App = () => { ganttHeight={300} columnWidth={columnWidth} /> +

RTL Gantt

+ ); }; diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index b90483f3d..e0cbdd462 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -438,6 +438,7 @@ export const Gantt: React.FunctionComponent = ({ fontSize, tasks: barTasks, locale, + rtl, headerHeight, scrollY, ganttHeight, @@ -450,7 +451,7 @@ export const Gantt: React.FunctionComponent = ({ TaskListTable, }; return ( -
+
- (date: Date, dateTimeOptions: Intl.DateTimeFormatOptions) => { - const key = date.toString(); - let lds = localeDateStringCache[key]; - if (!lds) { - lds = date.toLocaleDateString(locale, dateTimeOptions); - localeDateStringCache[key] = lds; - } - return lds; - }; + (date: Date, dateTimeOptions: Intl.DateTimeFormatOptions) => { + const key = date.toString(); + let lds = localeDateStringCache[key]; + if (!lds) { + lds = date.toLocaleDateString(locale, dateTimeOptions); + localeDateStringCache[key] = lds; + } + return lds; + }; const dateTimeOptions: Intl.DateTimeFormatOptions = { weekday: "short", year: "numeric", @@ -28,6 +28,7 @@ export const TaskListTableDefault: React.FC<{ fontSize: string; locale: string; tasks: Task[]; + rtl?: boolean; selectedTaskId: string; setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; @@ -38,78 +39,80 @@ export const TaskListTableDefault: React.FC<{ fontFamily, fontSize, locale, + rtl, onExpanderClick, }) => { - const toLocaleDateString = useMemo( - () => toLocaleDateStringFactory(locale), - [locale] - ); + const toLocaleDateString = useMemo( + () => toLocaleDateStringFactory(locale), + [locale] + ); + + return ( +
+ {tasks.map(t => { + let expanderSymbol = ""; + if (t.hideChildren === false) { + expanderSymbol = "▼"; + } else if (t.hideChildren === true) { - return ( -
- {tasks.map(t => { - let expanderSymbol = ""; - if (t.hideChildren === false) { - expanderSymbol = "▼"; - } else if (t.hideChildren === true) { - expanderSymbol = "▶"; - } + expanderSymbol = rtl?"◀":"▶"; + } - return ( -
+ return (
-
-
onExpanderClick(t)} - > - {expanderSymbol} +
+
+
onExpanderClick(t)} + > + {expanderSymbol} +
+
{t.name}
-
{t.name}
+
+
+  {toLocaleDateString(t.start, dateTimeOptions)} +
+
+  {toLocaleDateString(t.end, dateTimeOptions)}
-
-  {toLocaleDateString(t.start, dateTimeOptions)} -
-
-  {toLocaleDateString(t.end, dateTimeOptions)} -
-
- ); - })} -
- ); -}; + ); + })} +
+ ); + }; diff --git a/src/components/task-list/task-list.tsx b/src/components/task-list/task-list.tsx index bbfed4365..c6cad11ff 100644 --- a/src/components/task-list/task-list.tsx +++ b/src/components/task-list/task-list.tsx @@ -11,6 +11,7 @@ export type TaskListProps = { ganttHeight: number; scrollY: number; locale: string; + rtl?:boolean; tasks: Task[]; taskListRef: React.RefObject; horizontalContainerClass?: string; @@ -48,6 +49,7 @@ export const TaskList: React.FC = ({ setSelectedTask, onExpanderClick, locale, + rtl, ganttHeight, taskListRef, horizontalContainerClass, @@ -75,6 +77,7 @@ export const TaskList: React.FC = ({ fontSize, tasks, locale, + rtl, selectedTaskId: selectedTaskId, setSelectedTask, onExpanderClick, diff --git a/src/helpers/bar-helper.ts b/src/helpers/bar-helper.ts index ba5f987c9..7b08b61df 100644 --- a/src/helpers/bar-helper.ts +++ b/src/helpers/bar-helper.ts @@ -247,8 +247,9 @@ const convertToMilestone = ( }; const taskXCoordinate = (xDate: Date, dates: Date[], columnWidth: number) => { - const index = dates.findIndex(d => d.getTime() >= xDate.getTime()) - 1; - + let index = dates.findIndex(d => d.getTime() >= xDate.getTime()) - 1; +if(index===-1) +index=0; const remainderMillis = xDate.getTime() - dates[index].getTime(); const percentOfInterval = remainderMillis / (dates[index + 1].getTime() - dates[index].getTime()); From 02a0cc79d342f9938e6bb724c33a0a241211d741 Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Sat, 2 Dec 2023 14:52:11 +0330 Subject: [PATCH 2/8] Fix tooltip position problem in rtl mode, Fix date locale formats in rtl mode --- src/components/other/tooltip.tsx | 2 +- src/components/task-list/task-list-table.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/other/tooltip.tsx b/src/components/other/tooltip.tsx index 7542a14eb..1cdd7589d 100644 --- a/src/components/other/tooltip.tsx +++ b/src/components/other/tooltip.tsx @@ -49,7 +49,7 @@ export const Tooltip: React.FC = ({ let newRelatedY = task.index * rowHeight - scrollY + headerHeight; let newRelatedX: number; if (rtl) { - newRelatedX = task.x1 - arrowIndent * 1.5 - tooltipWidth - scrollX; + newRelatedX = task.x1 - tooltipWidth - scrollX; if (newRelatedX < 0) { newRelatedX = task.x2 + arrowIndent * 1.5 - scrollX; } diff --git a/src/components/task-list/task-list-table.tsx b/src/components/task-list/task-list-table.tsx index 9e1931dfd..3938c059c 100644 --- a/src/components/task-list/task-list-table.tsx +++ b/src/components/task-list/task-list-table.tsx @@ -6,10 +6,10 @@ const localeDateStringCache = {}; const toLocaleDateStringFactory = (locale: string) => (date: Date, dateTimeOptions: Intl.DateTimeFormatOptions) => { - const key = date.toString(); + const key = date.toString()+'_'+locale; let lds = localeDateStringCache[key]; if (!lds) { - lds = date.toLocaleDateString(locale, dateTimeOptions); + lds = date.toLocaleDateString(locale, locale!=="fa"?dateTimeOptions:{}); localeDateStringCache[key] = lds; } return lds; From cde0a130a92b85ce773070888646d8e2fcb298b1 Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Sun, 3 Dec 2023 12:04:10 +0330 Subject: [PATCH 3/8] Add dark & light mode switch --- example/src/App.tsx | 6 +++ example/src/components/view-switcher.tsx | 14 ++++++ example/src/index.css | 47 ++++++++++++++++--- src/components/calendar/calendar.module.css | 10 ++-- src/components/gantt/gantt.module.css | 2 + src/components/grid/grid.module.css | 8 ++-- .../other/horizontal-scroll.module.css | 8 ++-- src/components/other/tooltip.module.css | 6 +-- .../other/vertical-scroll.module.css | 8 ++-- src/components/task-item/bar/bar.module.css | 2 +- src/components/task-item/task-list.module.css | 4 +- .../task-list/task-list-header.module.css | 8 ++-- .../task-list/task-list-table.module.css | 8 ++-- src/index.tsx | 1 + src/palette.css | 19 ++++++++ 15 files changed, 114 insertions(+), 37 deletions(-) create mode 100644 src/palette.css diff --git a/example/src/App.tsx b/example/src/App.tsx index 8a6f09647..d572b29ea 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -9,6 +9,7 @@ const App = () => { const [view, setView] = React.useState(ViewMode.Day); const [tasks, setTasks] = React.useState(initTasks()); const [isChecked, setIsChecked] = React.useState(true); + const [isDark, setDark] = React.useState(true); let columnWidth = 65; if (view === ViewMode.Year) { columnWidth = 350; @@ -68,13 +69,17 @@ const App = () => { }; return ( +
setView(viewMode)} onViewListChange={setIsChecked} isChecked={isChecked} + setDark={setDark} + isDark={isDark} />

Gantt With Unlimited Height

+ { rtl />
+
); }; diff --git a/example/src/components/view-switcher.tsx b/example/src/components/view-switcher.tsx index 42f736016..152136a34 100644 --- a/example/src/components/view-switcher.tsx +++ b/example/src/components/view-switcher.tsx @@ -4,12 +4,15 @@ import { ViewMode } from "gantt-task-react"; type ViewSwitcherProps = { isChecked: boolean; onViewListChange: (isChecked: boolean) => void; + isDark: boolean; + setDark: (isDark: boolean) => void; onViewModeChange: (viewMode: ViewMode) => void; }; export const ViewSwitcher: React.FC = ({ onViewModeChange, onViewListChange, isChecked, + isDark,setDark }) => { return (
@@ -69,6 +72,17 @@ export const ViewSwitcher: React.FC = ({ Show Task List
+
+ + Dark Mode +
); }; diff --git a/example/src/index.css b/example/src/index.css index 9fd1ea03d..e5c1259f9 100644 --- a/example/src/index.css +++ b/example/src/index.css @@ -1,5 +1,39 @@ +:root { + --back: #fff; + --button-back: #e7e7e7; + --fore:#000; + --gray:#ccc; + --inputSlider: #2196f3; +} + +.dark { + --back: #000; + --button-back: #181818; + --fore:#fff; + --gray:#333; + --inputSlider: #2196f3; + --gtr-back: #000; + --gtr-even-back: #0a0a0a; + --gtr-grid-row-line: #14100d; + --gtr-border: #191b1b; + --gtr-label: #aaa; + --gtr-calendar-bottom-text:#ccc; + --gtr-calendar-stroke: #1f1f1f; + --gtr-tooltip-default-container-paragraph: #999999; + --gtr-bar-handle: #222222; + + --gtr-scroll-wrapper: rgba(255,255,255, 0.2); + --gtr-scroll-wrapper-hover: rgba(255,255,255, 0.3); + --gtr-tooltip-default-container-shadow-first: rgba(255,255,255, 0.16); + --gtr-tooltip-default-container-shadow-second: rgba(255,255,255, 0.23); + + --gtr-gantt-header-separator:rgb(59, 59, 59); + --gtr-task-list-expander: rgb(169 169 169); +} .Wrapper { margin-bottom: 2rem; + background-color: var(--back); + color: var(--fore) } .ViewContainer { list-style: none; @@ -11,8 +45,8 @@ } .Button { - background-color: #e7e7e7; - color: black; + background-color: var(--button-back); + color: var(--fore); border: none; padding: 7px 16px; text-decoration: none; @@ -50,7 +84,7 @@ left: 0; right: 0; bottom: 0; - background-color: #ccc; + background-color: var(--gray); -webkit-transition: 0.4s; transition: 0.4s; } @@ -62,17 +96,17 @@ width: 21px; left: 6px; bottom: 4px; - background-color: white; + background-color: var(--back); -webkit-transition: 0.4s; transition: 0.4s; } input:checked + .Slider { - background-color: #2196f3; + background-color: var(--inputSlider); } input:focus + .Slider { - box-shadow: 0 0 1px #2196f3; + box-shadow: 0 0 1px var(--inputSlider); } input:checked + .Slider:before { @@ -80,3 +114,4 @@ input:checked + .Slider:before { -ms-transform: translateX(26px); transform: translateX(26px); } + diff --git a/src/components/calendar/calendar.module.css b/src/components/calendar/calendar.module.css index 1be99d3f6..379473982 100644 --- a/src/components/calendar/calendar.module.css +++ b/src/components/calendar/calendar.module.css @@ -1,6 +1,6 @@ .calendarBottomText { text-anchor: middle; - fill: #333; + fill: var(--gtr-calendar-bottom-text); -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; @@ -10,12 +10,12 @@ } .calendarTopTick { - stroke: #e6e4e4; + stroke: var(--gtr-border); } .calendarTopText { text-anchor: middle; - fill: #555; + fill: var(--gtr-label); -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; @@ -25,7 +25,7 @@ } .calendarHeader { - fill: #ffffff; - stroke: #e0e0e0; + fill: var(--gtr-back); + stroke: var(--gtr-calendar-stroke); stroke-width: 1.4; } diff --git a/src/components/gantt/gantt.module.css b/src/components/gantt/gantt.module.css index 8169a19ce..416cf3d60 100644 --- a/src/components/gantt/gantt.module.css +++ b/src/components/gantt/gantt.module.css @@ -1,3 +1,5 @@ + + .ganttVerticalContainer { overflow: hidden; font-size: 0; diff --git a/src/components/grid/grid.module.css b/src/components/grid/grid.module.css index 964303f22..7614096f1 100644 --- a/src/components/grid/grid.module.css +++ b/src/components/grid/grid.module.css @@ -1,15 +1,15 @@ .gridRow { - fill: #fff; + fill: var(--gtr-back); } .gridRow:nth-child(even) { - fill: #f5f5f5; + fill: var(--gtr-even-back); } .gridRowLine { - stroke: #ebeff2; + stroke: var(--gtr-grid-row-line); } .gridTick { - stroke: #e6e4e4; + stroke: var(--gtr-border); } diff --git a/src/components/other/horizontal-scroll.module.css b/src/components/other/horizontal-scroll.module.css index dcf787e52..8d87adbf3 100644 --- a/src/components/other/horizontal-scroll.module.css +++ b/src/components/other/horizontal-scroll.module.css @@ -15,15 +15,15 @@ } .scrollWrapper::-webkit-scrollbar-thumb { border: 6px solid transparent; - background: rgba(0, 0, 0, 0.2); - background: var(--palette-black-alpha-20, rgba(0, 0, 0, 0.2)); + background: var(--gtr-scroll-wrapper); + background: var(--gtr-palette-black-alpha-20, --scroll-wrapper); border-radius: 10px; background-clip: padding-box; } .scrollWrapper::-webkit-scrollbar-thumb:hover { border: 4px solid transparent; - background: rgba(0, 0, 0, 0.3); - background: var(--palette-black-alpha-30, rgba(0, 0, 0, 0.3)); + background: var(--gtr-scroll-wrapper-hover); + background: var(--gtr-palette-black-alpha-30, --scroll-wrapper-hover); background-clip: padding-box; } @media only screen and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) { diff --git a/src/components/other/tooltip.module.css b/src/components/other/tooltip.module.css index d5793ef14..551b5dfea 100644 --- a/src/components/other/tooltip.module.css +++ b/src/components/other/tooltip.module.css @@ -1,13 +1,13 @@ .tooltipDefaultContainer { - background: #fff; + background: var(--gtr-back); padding: 12px; - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); + box-shadow: 0 3px 6px var(--gtr-tooltip-default-container-shadow-first), 0 3px 6px var(--gtr-tooltip-default-container-shadow-second); } .tooltipDefaultContainerParagraph { font-size: 12px; margin-bottom: 6px; - color: #666; + color: var(--gtr-tooltip-default-container-paragraph); } .tooltipDetailsContainer { diff --git a/src/components/other/vertical-scroll.module.css b/src/components/other/vertical-scroll.module.css index da55a2e2e..3f836242a 100644 --- a/src/components/other/vertical-scroll.module.css +++ b/src/components/other/vertical-scroll.module.css @@ -14,14 +14,14 @@ } .scroll::-webkit-scrollbar-thumb { border: 6px solid transparent; - background: rgba(0, 0, 0, 0.2); - background: var(--palette-black-alpha-20, rgba(0, 0, 0, 0.2)); + background: var(--gtr-scroll-wrapper); + background: var(--gtr-palette-black-alpha-20, --scroll-wrapper); border-radius: 10px; background-clip: padding-box; } .scroll::-webkit-scrollbar-thumb:hover { border: 4px solid transparent; - background: rgba(0, 0, 0, 0.3); - background: var(--palette-black-alpha-30, rgba(0, 0, 0, 0.3)); + background: var(--gtr-scroll-wrapper-hover); + background: var(--gtr-palette-black-alpha-30, --scroll-wrapper-hover); background-clip: padding-box; } diff --git a/src/components/task-item/bar/bar.module.css b/src/components/task-item/bar/bar.module.css index 7ff4926f8..f45836173 100644 --- a/src/components/task-item/bar/bar.module.css +++ b/src/components/task-item/bar/bar.module.css @@ -9,7 +9,7 @@ } .barHandle { - fill: #ddd; + fill: var(--gtr-bar-handle); cursor: ew-resize; opacity: 0; visibility: hidden; diff --git a/src/components/task-item/task-list.module.css b/src/components/task-item/task-list.module.css index 2eec4204a..9ef7f644c 100644 --- a/src/components/task-item/task-list.module.css +++ b/src/components/task-item/task-list.module.css @@ -1,5 +1,5 @@ .barLabel { - fill: #fff; + fill: var(--gtr-back); text-anchor: middle; font-weight: lighter; dominant-baseline: central; @@ -12,7 +12,7 @@ } .barLabelOutside { - fill: #555; + fill: var(--gtr-label); text-anchor: start; -webkit-touch-callout: none; -webkit-user-select: none; diff --git a/src/components/task-list/task-list-header.module.css b/src/components/task-list/task-list-header.module.css index c25035401..d6a79260e 100644 --- a/src/components/task-list/task-list-header.module.css +++ b/src/components/task-list/task-list-header.module.css @@ -1,8 +1,8 @@ .ganttTable { display: table; - border-bottom: #e6e4e4 1px solid; - border-top: #e6e4e4 1px solid; - border-left: #e6e4e4 1px solid; + border-bottom: var(--gtr-border) 1px solid; + border-top: var(--gtr-border) 1px solid; + border-left: var(--gtr-border) 1px solid; } .ganttTable_Header { @@ -11,7 +11,7 @@ } .ganttTable_HeaderSeparator { - border-right: 1px solid rgb(196, 196, 196); + border-right: 1px solid var(--gtr-gantt-header-separator); opacity: 1; margin-left: -2px; } diff --git a/src/components/task-list/task-list-table.module.css b/src/components/task-list/task-list-table.module.css index 7f57268e8..936d883bd 100644 --- a/src/components/task-list/task-list-table.module.css +++ b/src/components/task-list/task-list-table.module.css @@ -1,7 +1,7 @@ .taskListWrapper { display: table; - border-bottom: #e6e4e4 1px solid; - border-left: #e6e4e4 1px solid; + border-bottom: var(--gtr-border) 1px solid; + border-left: var(--gtr-border) 1px solid; } .taskListTableRow { @@ -10,7 +10,7 @@ } .taskListTableRow:nth-of-type(even) { - background-color: #f5f5f5; + background-color: var(--gtr-even-back); } .taskListCell { @@ -25,7 +25,7 @@ } .taskListExpander { - color: rgb(86 86 86); + color: var(--gtr-task-list-expander); font-size: 0.6rem; padding: 0.15rem 0.2rem 0rem 0.2rem; user-select: none; diff --git a/src/index.tsx b/src/index.tsx index 47e8b63a4..1358ae19d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,3 +1,4 @@ +import './palette.css' export { Gantt } from "./components/gantt/gantt"; export { ViewMode } from "./types/public-types"; export type { diff --git a/src/palette.css b/src/palette.css new file mode 100644 index 000000000..79326b2ef --- /dev/null +++ b/src/palette.css @@ -0,0 +1,19 @@ +:root { + --gtr-back: #fff; + --gtr-even-back: #f5f5f5; + --gtr-grid-row-line: #ebeff2; + --gtr-border: #e6e4e4; + --gtr-label: #555; + --gtr-calendar-bottom-text:#333; + --gtr-calendar-stroke: #e0e0e0; + --gtr-tooltip-default-container-paragraph: #666; + --gtr-bar-handle: #ddd; + + --gtr-scroll-wrapper: rgba(0, 0, 0, 0.2); + --gtr-scroll-wrapper-hover: rgba(0, 0, 0, 0.3); + --gtr-tooltip-default-container-shadow-first: rgba(0, 0, 0, 0.16); + --gtr-tooltip-default-container-shadow-second: rgba(0, 0, 0, 0.23); + + --gtr-gantt-header-separator:rgb(196, 196, 196); + --gtr-task-list-expander: rgb(86 86 86); + } \ No newline at end of file From 9b247af961497261b00654aaf73dd964202b8546 Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Mon, 4 Dec 2023 13:35:31 +0330 Subject: [PATCH 4/8] Fix problem : invisible scrollbar --- src/components/other/horizontal-scroll.module.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/other/horizontal-scroll.module.css b/src/components/other/horizontal-scroll.module.css index 8d87adbf3..755394671 100644 --- a/src/components/other/horizontal-scroll.module.css +++ b/src/components/other/horizontal-scroll.module.css @@ -16,14 +16,14 @@ .scrollWrapper::-webkit-scrollbar-thumb { border: 6px solid transparent; background: var(--gtr-scroll-wrapper); - background: var(--gtr-palette-black-alpha-20, --scroll-wrapper); + /* background: var(--gtr-palette-black-alpha-20, --scroll-wrapper); */ border-radius: 10px; background-clip: padding-box; } .scrollWrapper::-webkit-scrollbar-thumb:hover { border: 4px solid transparent; background: var(--gtr-scroll-wrapper-hover); - background: var(--gtr-palette-black-alpha-30, --scroll-wrapper-hover); + /* background: var(--gtr-palette-black-alpha-30, --scroll-wrapper-hover); */ background-clip: padding-box; } @media only screen and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) { From b7b6fd5f3371a8446b006d581328a640bcbafb62 Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Tue, 5 Dec 2023 10:06:20 +0330 Subject: [PATCH 5/8] Update readme --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9191d87ba..71dc1c048 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,31 @@ You may handle actions /> ``` +You can override colors to make it compatible with your theme + +```css + +.dark { + --gtr-back: #000; + --gtr-even-back: #0a0a0a; + --gtr-grid-row-line: #14100d; + --gtr-border: #191b1b; + --gtr-label: #aaa; + --gtr-calendar-bottom-text:#ccc; + --gtr-calendar-stroke: #1f1f1f; + --gtr-tooltip-default-container-paragraph: #999999; + --gtr-bar-handle: #222222; + + --gtr-scroll-wrapper: rgba(255,255,255, 0.2); + --gtr-scroll-wrapper-hover: rgba(255,255,255, 0.3); + --gtr-tooltip-default-container-shadow-first: rgba(255,255,255, 0.16); + --gtr-tooltip-default-container-shadow-second: rgba(255,255,255, 0.23); + + --gtr-gantt-header-separator:rgb(59, 59, 59); + --gtr-task-list-expander: rgb(169 169 169); +} +``` + ## How to run example ``` @@ -115,7 +140,8 @@ npm start | todayColor | string | Specifies the current period column fill color. | | TooltipContent | | Specifies the Tooltip view for selected taskbar. | | TaskListHeader | | Specifies the task list Header view | -| TaskListTable | | Specifies the task list Table view | +| TaskListTable | | Specifies the task list Table view + - TooltipContent: [`React.FC<{ task: Task; fontSize: string; fontFamily: string; }>;`](https://github.com/MaTeMaTuK/gantt-task-react/blob/main/src/components/other/tooltip.tsx#L56) - TaskListHeader: `React.FC<{ headerHeight: number; rowWidth: string; fontFamily: string; fontSize: string;}>;` From c0d119e3d82a25d1a97f43cb6dfa75120de83951 Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Tue, 5 Dec 2023 23:18:50 +0330 Subject: [PATCH 6/8] Fix some borders, Fix calculating dates and years in other locales --- example/src/App.tsx | 118 +++++++++--------- src/components/calendar/calendar.tsx | 31 +++-- src/components/gantt/gantt.module.css | 5 +- src/components/gantt/gantt.tsx | 3 + src/components/task-list/task-list-header.tsx | 20 ++- .../task-list/task-list-table.module.css | 9 +- src/components/task-list/task-list-table.tsx | 10 +- src/components/task-list/task-list.tsx | 17 ++- src/helpers/other-helper.ts | 11 +- src/types/public-types.ts | 6 + 10 files changed, 148 insertions(+), 82 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index d572b29ea..89eb7986e 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -69,62 +69,68 @@ const App = () => { }; return ( -
-
- setView(viewMode)} - onViewListChange={setIsChecked} - isChecked={isChecked} - setDark={setDark} - isDark={isDark} - /> -

Gantt With Unlimited Height

- - -

Gantt With Limited Height

- -

RTL Gantt

- -
+
+
+ setView(viewMode)} + onViewListChange={setIsChecked} + isChecked={isChecked} + setDark={setDark} + isDark={isDark} + /> +

Gantt With Unlimited Height

+ + +

Gantt With Limited Height

+ +

RTL Gantt

+ +
); }; diff --git a/src/components/calendar/calendar.tsx b/src/components/calendar/calendar.tsx index a5860db52..aca6bc145 100644 --- a/src/components/calendar/calendar.tsx +++ b/src/components/calendar/calendar.tsx @@ -6,10 +6,10 @@ import { getDaysInMonth, getLocalDayOfWeek, getLocaleMonth, - getWeekNumberISO8601, } from "../../helpers/date-helper"; import { DateSetup } from "../../types/date-setup"; import styles from "./calendar.module.css"; +import { farsiDigitToEnglish } from "../../helpers/other-helper"; export type CalendarProps = { dateSetup: DateSetup; @@ -38,7 +38,7 @@ export const Calendar: React.FC = ({ const topDefaultHeight = headerHeight * 0.5; for (let i = 0; i < dateSetup.dates.length; i++) { const date = dateSetup.dates[i]; - const bottomValue = date.getFullYear(); + const bottomValue = date.toLocaleDateString(locale, { year: 'numeric' }); bottomValues.push( = ({ for (let i = 0; i < dateSetup.dates.length; i++) { const date = dateSetup.dates[i]; // const bottomValue = getLocaleMonth(date, locale); - const quarter = "Q" + Math.floor((date.getMonth() + 3) / 3); + const qNumber = Math.floor((date.getMonth() + 3) / 3); + + let quarter; + if (locale === 'fa') { + const seasons = ['بهار', 'تابستان', 'پاییز', 'زمستان']; + quarter = seasons[qNumber - 1]; + } else + quarter = 'Q' + qNumber; bottomValues.push( = ({ i === 0 || date.getFullYear() !== dateSetup.dates[i - 1].getFullYear() ) { - const topValue = date.getFullYear().toString(); + const topValue = date.toLocaleDateString(locale, { year: 'numeric' }); let xText: number; if (rtl) { xText = (6 + i + date.getMonth() + 1) * columnWidth; @@ -142,7 +149,7 @@ export const Calendar: React.FC = ({ i === 0 || date.getFullYear() !== dateSetup.dates[i - 1].getFullYear() ) { - const topValue = date.getFullYear().toString(); + const topValue = date.toLocaleDateString(locale, { year: 'numeric' }); let xText: number; if (rtl) { xText = (6 + i + date.getMonth() + 1) * columnWidth; @@ -176,10 +183,12 @@ export const Calendar: React.FC = ({ let topValue = ""; if (i === 0 || date.getMonth() !== dates[i - 1].getMonth()) { // top - topValue = `${getLocaleMonth(date, locale)}, ${date.getFullYear()}`; + topValue = `${getLocaleMonth(date, locale)}, ${date.toLocaleDateString(locale, { year: 'numeric' })}`; } + const weekNumber = Math.floor(farsiDigitToEnglish(date.toLocaleDateString(locale, { day: 'numeric' })) / 7) + 1; + // bottom - const bottomValue = `W${getWeekNumberISO8601(date)}`; + const bottomValue = `${locale === 'fa' ? 'هفته' : 'Week'} ${weekNumber}`; bottomValues.push( = ({ const dates = dateSetup.dates; for (let i = 0; i < dates.length; i++) { const date = dates[i]; - const bottomValue = `${getLocalDayOfWeek(date, locale, "short")}, ${date + const bottomValue = `${getLocalDayOfWeek(date, locale, (rtl ? "narrow" : "short"))} (${date .getDate() - .toString()}`; + .toString()})`; bottomValues.push( = ({ xText={ columnWidth * (i + 1) - getDaysInMonth(date.getMonth(), date.getFullYear()) * - columnWidth * - 0.5 + columnWidth * + 0.5 } yText={topDefaultHeight * 0.9} /> diff --git a/src/components/gantt/gantt.module.css b/src/components/gantt/gantt.module.css index 416cf3d60..06b2a2eae 100644 --- a/src/components/gantt/gantt.module.css +++ b/src/components/gantt/gantt.module.css @@ -1,10 +1,10 @@ - - .ganttVerticalContainer { overflow: hidden; font-size: 0; margin: 0; padding: 0; + border: 1px solid var(--gtr-border); + border-width: 0 1px; } .horizontalContainer { @@ -20,4 +20,5 @@ list-style: none; outline: none; position: relative; + border-bottom: 1px solid var(--gtr-border); } diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index e0cbdd462..141b30f71 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -58,6 +58,8 @@ export const Gantt: React.FunctionComponent = ({ TooltipContent = StandardTooltipContent, TaskListHeader = TaskListHeaderDefault, TaskListTable = TaskListTableDefault, + headerLabels, + headerTextAlign, onDateChange, onProgressChange, onDoubleClick, @@ -445,6 +447,7 @@ export const Gantt: React.FunctionComponent = ({ horizontalContainerClass: styles.horizontalContainer, selectedTask, taskListRef, + headerLabels,headerTextAlign, setSelectedTask: handleSelectedTask, onExpanderClick: handleExpanderClick, TaskListHeader, diff --git a/src/components/task-list/task-list-header.tsx b/src/components/task-list/task-list-header.tsx index 4e8cdb66b..9d31c8c20 100644 --- a/src/components/task-list/task-list-header.tsx +++ b/src/components/task-list/task-list-header.tsx @@ -6,13 +6,19 @@ export const TaskListHeaderDefault: React.FC<{ rowWidth: string; fontFamily: string; fontSize: string; -}> = ({ headerHeight, fontFamily, fontSize, rowWidth }) => { + textAlign?: 'left' | 'center' | 'right'; + labels?: { + name?: string, + from?: string, + to?: string; + }; +}> = ({ headerHeight, fontFamily, fontSize, rowWidth, textAlign, labels }) => { return (
-  Name +  {labels?.name ?? 'Name'}
-  From +  {labels?.from ?? 'From'}
-  To +  {labels?.to ?? 'To'}
diff --git a/src/components/task-list/task-list-table.module.css b/src/components/task-list/task-list-table.module.css index 936d883bd..7cbde8196 100644 --- a/src/components/task-list/task-list-table.module.css +++ b/src/components/task-list/task-list-table.module.css @@ -1,7 +1,10 @@ .taskListWrapper { display: table; - border-bottom: var(--gtr-border) 1px solid; - border-left: var(--gtr-border) 1px solid; + border-color:var(--gtr-border); + border-style: solid; + border-width: 0; + /* border-bottom-width: 1px; */ + /* border-left: var(--gtr-border) 1px solid; */ } .taskListTableRow { @@ -33,6 +36,6 @@ } .taskListEmptyExpander { font-size: 0.6rem; - padding-left: 1rem; + /* padding-left: 1rem; */ user-select: none; } diff --git a/src/components/task-list/task-list-table.tsx b/src/components/task-list/task-list-table.tsx index 3938c059c..e1cf498a6 100644 --- a/src/components/task-list/task-list-table.tsx +++ b/src/components/task-list/task-list-table.tsx @@ -6,10 +6,10 @@ const localeDateStringCache = {}; const toLocaleDateStringFactory = (locale: string) => (date: Date, dateTimeOptions: Intl.DateTimeFormatOptions) => { - const key = date.toString()+'_'+locale; + const key = date.toString() + '_' + locale; let lds = localeDateStringCache[key]; if (!lds) { - lds = date.toLocaleDateString(locale, locale!=="fa"?dateTimeOptions:{}); + lds = date.toLocaleDateString(locale, locale !== "fa" ? dateTimeOptions : {}); localeDateStringCache[key] = lds; } return lds; @@ -53,6 +53,7 @@ export const TaskListTableDefault: React.FC<{ style={{ fontFamily: fontFamily, fontSize: fontSize, + [`border-${rtl ? 'right' : 'left'}-width`]: '1px' }} > {tasks.map(t => { @@ -61,7 +62,7 @@ export const TaskListTableDefault: React.FC<{ expanderSymbol = "▼"; } else if (t.hideChildren === true) { - expanderSymbol = rtl?"◀":"▶"; + expanderSymbol = rtl ? "◀" : "▶"; } return ( @@ -85,6 +86,9 @@ export const TaskListTableDefault: React.FC<{ ? styles.taskListExpander : styles.taskListEmptyExpander } + style={!expanderSymbol ? { + [`padding-${rtl ? 'right' : 'left'}`]: '1rem' + } : {}} onClick={() => onExpanderClick(t)} > {expanderSymbol} diff --git a/src/components/task-list/task-list.tsx b/src/components/task-list/task-list.tsx index c6cad11ff..9e562198a 100644 --- a/src/components/task-list/task-list.tsx +++ b/src/components/task-list/task-list.tsx @@ -11,11 +11,17 @@ export type TaskListProps = { ganttHeight: number; scrollY: number; locale: string; - rtl?:boolean; + rtl?: boolean; tasks: Task[]; taskListRef: React.RefObject; horizontalContainerClass?: string; selectedTask: BarTask | undefined; + headerTextAlign?: 'left' | 'center' | 'right'; + headerLabels?: { + name?: string, + from?: string, + to?: string; + }; setSelectedTask: (task: string) => void; onExpanderClick: (task: Task) => void; TaskListHeader: React.FC<{ @@ -23,6 +29,12 @@ export type TaskListProps = { rowWidth: string; fontFamily: string; fontSize: string; + textAlign?: 'left' | 'center' | 'right'; + labels?: { + name?: string, + from?: string, + to?: string; + }; }>; TaskListTable: React.FC<{ rowHeight: number; @@ -50,6 +62,7 @@ export const TaskList: React.FC = ({ onExpanderClick, locale, rtl, + headerLabels, headerTextAlign, ganttHeight, taskListRef, horizontalContainerClass, @@ -68,6 +81,8 @@ export const TaskList: React.FC = ({ fontFamily, fontSize, rowWidth, + textAlign: headerTextAlign, + labels: headerLabels }; const selectedTaskId = selectedTask ? selectedTask.id : ""; const tableProps = { diff --git a/src/helpers/other-helper.ts b/src/helpers/other-helper.ts index afd5a362e..5b3a070b1 100644 --- a/src/helpers/other-helper.ts +++ b/src/helpers/other-helper.ts @@ -43,7 +43,7 @@ function getChildren(taskList: Task[], task: Task) { var taskChildren: Task[] = []; tasks.forEach(t => { taskChildren.push(...getChildren(taskList, t)); - }) + }); tasks = tasks.concat(tasks, taskChildren); return tasks; } @@ -59,3 +59,12 @@ export const sortTasks = (taskA: Task, taskB: Task) => { return 0; } }; + +export const farsiDigitToEnglish = (farsiNumber: string): number => { + const farsiDigits = ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"]; + const convertedNumber = farsiNumber.replace(/[۰-۹]/g, match => + farsiDigits.indexOf(match).toString() + ); + if (/\d+/.test(convertedNumber)) return Number(convertedNumber); + else return 0; +}; diff --git a/src/types/public-types.ts b/src/types/public-types.ts index cc44ff17c..0e36e9109 100644 --- a/src/types/public-types.ts +++ b/src/types/public-types.ts @@ -138,6 +138,12 @@ export interface StylingOption { setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; }>; + headerTextAlign?: 'left' | 'center' | 'right'; + headerLabels?: { + name?: string, + from?: string, + to?: string; + }; } export interface GanttProps extends EventOption, DisplayOption, StylingOption { From 5c44c4079fa6cbcf6b54ccf69cad0c0eba2be692 Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Fri, 8 Dec 2023 15:54:16 +0330 Subject: [PATCH 7/8] Fix vertical scroll problem --- .../other/vertical-scroll.module.css | 4 +- src/components/other/vertical-scroll.tsx | 42 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/other/vertical-scroll.module.css b/src/components/other/vertical-scroll.module.css index 3f836242a..45a95711a 100644 --- a/src/components/other/vertical-scroll.module.css +++ b/src/components/other/vertical-scroll.module.css @@ -15,13 +15,13 @@ .scroll::-webkit-scrollbar-thumb { border: 6px solid transparent; background: var(--gtr-scroll-wrapper); - background: var(--gtr-palette-black-alpha-20, --scroll-wrapper); + /* background: var(--gtr-palette-black-alpha-20, --scroll-wrapper); */ border-radius: 10px; background-clip: padding-box; } .scroll::-webkit-scrollbar-thumb:hover { border: 4px solid transparent; background: var(--gtr-scroll-wrapper-hover); - background: var(--gtr-palette-black-alpha-30, --scroll-wrapper-hover); + /* background: var(--gtr-palette-black-alpha-30, --scroll-wrapper-hover); */ background-clip: padding-box; } diff --git a/src/components/other/vertical-scroll.tsx b/src/components/other/vertical-scroll.tsx index d01d46ec9..171b18cad 100644 --- a/src/components/other/vertical-scroll.tsx +++ b/src/components/other/vertical-scroll.tsx @@ -16,26 +16,26 @@ export const VerticalScroll: React.FC<{ rtl, onScroll, }) => { - const scrollRef = useRef(null); + const scrollRef = useRef(null); - useEffect(() => { - if (scrollRef.current) { - scrollRef.current.scrollTop = scroll; - } - }, [scroll]); + useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTop = scroll; + } + }, [scroll]); - return ( -
-
-
- ); -}; + return ( +
+
+
+ ); + }; From 92ca7c4bcc9d332e538153c4d89e64a115d7303c Mon Sep 17 00:00:00 2001 From: Farzad Mousavi Date: Fri, 8 Dec 2023 16:09:41 +0330 Subject: [PATCH 8/8] Fix side border in header --- src/components/task-list/task-list-header.module.css | 2 +- src/components/task-list/task-list-header.tsx | 6 ++++-- src/components/task-list/task-list.tsx | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/task-list/task-list-header.module.css b/src/components/task-list/task-list-header.module.css index d6a79260e..5ce98a881 100644 --- a/src/components/task-list/task-list-header.module.css +++ b/src/components/task-list/task-list-header.module.css @@ -2,7 +2,7 @@ display: table; border-bottom: var(--gtr-border) 1px solid; border-top: var(--gtr-border) 1px solid; - border-left: var(--gtr-border) 1px solid; + /* border-left: var(--gtr-border) 1px solid; */ } .ganttTable_Header { diff --git a/src/components/task-list/task-list-header.tsx b/src/components/task-list/task-list-header.tsx index 9d31c8c20..91aa3c03a 100644 --- a/src/components/task-list/task-list-header.tsx +++ b/src/components/task-list/task-list-header.tsx @@ -6,19 +6,21 @@ export const TaskListHeaderDefault: React.FC<{ rowWidth: string; fontFamily: string; fontSize: string; + rtl?: boolean; textAlign?: 'left' | 'center' | 'right'; labels?: { name?: string, from?: string, to?: string; }; -}> = ({ headerHeight, fontFamily, fontSize, rowWidth, textAlign, labels }) => { +}> = ({ headerHeight, fontFamily, fontSize, rowWidth, textAlign, labels, rtl }) => { return (
= ({ fontFamily, fontSize, rowWidth, + rtl, textAlign: headerTextAlign, labels: headerLabels };