v2.3.4
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@svar-ui/react-gantt",
|
"name": "@svar-ui/react-gantt",
|
||||||
"version": "2.3.3",
|
"version": "2.3.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@svar-ui/react-gantt",
|
"name": "@svar-ui/react-gantt",
|
||||||
"version": "2.3.3",
|
"version": "2.3.4",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@svar-ui/gantt-data-provider": "2.3.0",
|
"@svar-ui/gantt-data-provider": "2.3.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@svar-ui/react-gantt",
|
"name": "@svar-ui/react-gantt",
|
||||||
"version": "2.3.3",
|
"version": "2.3.4",
|
||||||
"description": "A customizable, high-performance React Gantt chart component for building project timelines.",
|
"description": "A customizable, high-performance React Gantt chart component for building project timelines.",
|
||||||
"productTag": "gantt",
|
"productTag": "gantt",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -50,7 +50,8 @@
|
|||||||
"dist",
|
"dist",
|
||||||
"types",
|
"types",
|
||||||
"package.json",
|
"package.json",
|
||||||
"dist-full/index.css"
|
"dist-full/index.css",
|
||||||
|
"whatsnew.md"
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=18",
|
"react": ">=18",
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
useContext,
|
useContext,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { hotkeys } from '@svar-ui/grid-store';
|
import { hotkeys } from '@svar-ui/grid-store';
|
||||||
import TimeScales from './TimeScale.jsx';
|
|
||||||
import Grid from './grid/Grid.jsx';
|
import Grid from './grid/Grid.jsx';
|
||||||
import Chart from './chart/Chart.jsx';
|
import Chart from './chart/Chart.jsx';
|
||||||
import Resizer from './Resizer.jsx';
|
import Resizer from './Resizer.jsx';
|
||||||
@@ -31,9 +30,7 @@ function Layout(props) {
|
|||||||
const rScales = useStore(api, "_scales");
|
const rScales = useStore(api, "_scales");
|
||||||
const rCellHeight = useStore(api, "cellHeight");
|
const rCellHeight = useStore(api, "cellHeight");
|
||||||
const rColumns = useStore(api, "columns");
|
const rColumns = useStore(api, "columns");
|
||||||
const scrollTop = useStore(api, "scrollTop");
|
|
||||||
const rScrollTask = useStore(api, "_scrollTask");
|
const rScrollTask = useStore(api, "_scrollTask");
|
||||||
|
|
||||||
const [compactMode, setCompactMode] = useState(false);
|
const [compactMode, setCompactMode] = useState(false);
|
||||||
let [gridWidth, setGridWidth] = useState(0);
|
let [gridWidth, setGridWidth] = useState(0);
|
||||||
const [ganttWidth, setGanttWidth] = useState(undefined);
|
const [ganttWidth, setGanttWidth] = useState(undefined);
|
||||||
@@ -131,20 +128,13 @@ function Layout(props) {
|
|||||||
const ganttDivRef = useRef(null);
|
const ganttDivRef = useRef(null);
|
||||||
const pseudoRowsRef = useRef(null);
|
const pseudoRowsRef = useRef(null);
|
||||||
|
|
||||||
const syncScroll = useCallback(() => {
|
|
||||||
const el = ganttDivRef.current;
|
|
||||||
if (el && scrollTop !== el.scrollTop) el.scrollTop = scrollTop;
|
|
||||||
}, [scrollTop]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
syncScroll();
|
|
||||||
}, [scrollTop, syncScroll]);
|
|
||||||
|
|
||||||
const onScroll = useCallback(() => {
|
const onScroll = useCallback(() => {
|
||||||
const el = ganttDivRef.current;
|
const el = ganttDivRef.current;
|
||||||
api.exec('scroll-chart', {
|
if (el) {
|
||||||
top: el ? el.scrollTop : 0,
|
api.exec('scroll-chart', {
|
||||||
});
|
top: el.scrollTop,
|
||||||
|
});
|
||||||
|
}
|
||||||
}, [api]);
|
}, [api]);
|
||||||
|
|
||||||
const latest = useRef({
|
const latest = useRef({
|
||||||
@@ -193,6 +183,7 @@ function Layout(props) {
|
|||||||
}
|
}
|
||||||
if (top !== null) {
|
if (top !== null) {
|
||||||
api.exec('scroll-chart', { top: Math.max(top, 0) });
|
api.exec('scroll-chart', { top: Math.max(top, 0) });
|
||||||
|
ganttDivRef.current.scrollTop = Math.max(top, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -200,7 +191,7 @@ function Layout(props) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
scrollToTask(rScrollTask);
|
scrollToTask(rScrollTask);
|
||||||
}, [rScrollTask]);
|
}, [rScrollTask]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -229,11 +220,14 @@ function Layout(props) {
|
|||||||
}, [pseudoRowsRef.current]);
|
}, [pseudoRowsRef.current]);
|
||||||
|
|
||||||
const layoutRef = useRef(null);
|
const layoutRef = useRef(null);
|
||||||
|
const cleanupRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (cleanupRef.current) return;
|
||||||
const node = layoutRef.current;
|
const node = layoutRef.current;
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
const cleanup = hotkeys(node, {
|
cleanupRef.current = hotkeys(node, {
|
||||||
keys: {
|
keys: {
|
||||||
'ctrl+c': true,
|
'ctrl+c': true,
|
||||||
'ctrl+v': true,
|
'ctrl+v': true,
|
||||||
@@ -246,9 +240,11 @@ function Layout(props) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
return cleanup.destroy;
|
cleanupRef.current?.destroy();
|
||||||
}, [api]);
|
cleanupRef.current = null;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="wx-jlbQoHOz wx-gantt" ref={ganttDivRef} onScroll={onScroll}>
|
<div className="wx-jlbQoHOz wx-gantt" ref={ganttDivRef} onScroll={onScroll}>
|
||||||
@@ -289,8 +285,6 @@ function Layout(props) {
|
|||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="wx-jlbQoHOz wx-content" ref={chartRef}>
|
<div className="wx-jlbQoHOz wx-content" ref={chartRef}>
|
||||||
<TimeScales highlightTime={highlightTime} />
|
|
||||||
|
|
||||||
<Chart
|
<Chart
|
||||||
readonly={readonly}
|
readonly={readonly}
|
||||||
fullWidth={fullWidth}
|
fullWidth={fullWidth}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
.wx-scale.wx-ZkvhDKir {
|
.wx-scale.wx-ZkvhDKir {
|
||||||
position: relative;
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: var(--wx-background);
|
||||||
box-shadow: var(--wx-timescale-shadow);
|
box-shadow: var(--wx-timescale-shadow);
|
||||||
z-index: 1;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wx-row.wx-ZkvhDKir,
|
.wx-row.wx-ZkvhDKir,
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ function TimeScale(props) {
|
|||||||
|
|
||||||
const api = useContext(storeContext);
|
const api = useContext(storeContext);
|
||||||
const scales = useStore(api, "_scales");
|
const scales = useStore(api, "_scales");
|
||||||
const scrollLeft = useStore(api, "scrollLeft");
|
|
||||||
|
|
||||||
const containerStyle = {
|
const containerStyle = {
|
||||||
width: `${(scales && scales.width) != null ? scales.width : 0}px`,
|
width: `${(scales && scales.width) != null ? scales.width : 0}px`,
|
||||||
left: `${-(scrollLeft != null ? scrollLeft : 0)}px`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ function Bars(props) {
|
|||||||
|
|
||||||
const api = useContext(storeContext);
|
const api = useContext(storeContext);
|
||||||
|
|
||||||
const [rTasksValue, rTasksCounter] = useStoreWithCounter(api,"_tasks");
|
const [rTasksValue, rTasksCounter] = useStoreWithCounter(api, "_tasks");
|
||||||
const [rLinksValue, rLinksCounter] = useStoreWithCounter(api,"_links");
|
const [rLinksValue, rLinksCounter] = useStoreWithCounter(api, "_links");
|
||||||
const areaValue = useStore(api,"area");
|
const areaValue = useStore(api, "area");
|
||||||
const scalesValue = useStore(api,"_scales");
|
const scalesValue = useStore(api, "_scales");
|
||||||
const taskTypesValue = useStore(api,"taskTypes");
|
const taskTypesValue = useStore(api, "taskTypes");
|
||||||
const baselinesValue = useStore(api,"baselines");
|
const baselinesValue = useStore(api, "baselines");
|
||||||
const selectedValue = useStore(api,"_selected");
|
const selectedValue = useStore(api, "_selected");
|
||||||
const scrollTaskStore = useStore(api,"_scrollTask" );
|
const scrollTaskStore = useStore(api, "_scrollTask");
|
||||||
|
|
||||||
const tasks = useMemo(() => {
|
const tasks = useMemo(() => {
|
||||||
if (!areaValue || !Array.isArray(rTasksValue)) return [];
|
if (!areaValue || !Array.isArray(rTasksValue)) return [];
|
||||||
@@ -66,21 +66,16 @@ function Bars(props) {
|
|||||||
}, [hasFocus, selectedValue]);
|
}, [hasFocus, selectedValue]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!scrollTaskStore || typeof scrollTaskStore.subscribe !== 'function')
|
if (!scrollTaskStore)
|
||||||
return;
|
return;
|
||||||
const unsub = scrollTaskStore.subscribe((value) => {
|
if (hasFocus && scrollTaskStore) {
|
||||||
if (hasFocus && value) {
|
const { id } = scrollTaskStore;
|
||||||
const { id } = value;
|
const node = containerRef.current?.querySelector(
|
||||||
const node = containerRef.current?.querySelector(
|
`.wx-bar[data-id='${id}']`,
|
||||||
`.wx-bar[data-id='${id}']`,
|
);
|
||||||
);
|
if (node) node.focus({ preventScroll: true });
|
||||||
if (node) node.focus();
|
}
|
||||||
}
|
}, [scrollTaskStore]);
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
if (unsub) unsub();
|
|
||||||
};
|
|
||||||
}, [scrollTaskStore, hasFocus]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = containerRef.current;
|
const el = containerRef.current;
|
||||||
@@ -391,7 +386,7 @@ function Bars(props) {
|
|||||||
setLinkFrom(null);
|
setLinkFrom(null);
|
||||||
}
|
}
|
||||||
}, [linkFrom]);
|
}, [linkFrom]);
|
||||||
|
|
||||||
const onClick = useCallback(
|
const onClick = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (ignoreNextClickRef.current) {
|
if (ignoreNextClickRef.current) {
|
||||||
@@ -496,7 +491,7 @@ function Bars(props) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tasks.map((task) => {
|
{tasks.map((task) => {
|
||||||
if (task.$skip) return null;
|
if (task.$skip && task.$skip_baseline) return null;
|
||||||
const barClass =
|
const barClass =
|
||||||
`wx-bar wx-${taskTypeCss(task.type)}` +
|
`wx-bar wx-${taskTypeCss(task.type)}` +
|
||||||
(touched && taskMove && task.id === taskMove.id ? ' wx-touch' : '') +
|
(touched && taskMove && task.id === taskMove.id ? ' wx-touch' : '') +
|
||||||
@@ -518,7 +513,7 @@ function Bars(props) {
|
|||||||
: '');
|
: '');
|
||||||
return (
|
return (
|
||||||
<Fragment key={task.id}>
|
<Fragment key={task.id}>
|
||||||
<div
|
{!task.$skip && <div
|
||||||
className={'wx-GKbcLEGA ' + barClass}
|
className={'wx-GKbcLEGA ' + barClass}
|
||||||
style={taskStyle(task)}
|
style={taskStyle(task)}
|
||||||
data-tooltip-id={task.id}
|
data-tooltip-id={task.id}
|
||||||
@@ -574,6 +569,7 @@ function Bars(props) {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
{baselinesValue && !task.$skip_baseline ? (
|
{baselinesValue && !task.$skip_baseline ? (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { hotkeys } from '@svar-ui/grid-store';
|
|||||||
import storeContext from '../../context';
|
import storeContext from '../../context';
|
||||||
import { useStore, useStoreWithCounter } from '@svar-ui/lib-react';
|
import { useStore, useStoreWithCounter } from '@svar-ui/lib-react';
|
||||||
import './Chart.css';
|
import './Chart.css';
|
||||||
|
import TimeScales from '../TimeScale.jsx';
|
||||||
|
|
||||||
function Chart(props) {
|
function Chart(props) {
|
||||||
const {
|
const {
|
||||||
@@ -37,17 +38,9 @@ function Chart(props) {
|
|||||||
const zoom = useStore(api, "zoom");
|
const zoom = useStore(api, "zoom");
|
||||||
|
|
||||||
const [chartHeight, setChartHeight] = useState();
|
const [chartHeight, setChartHeight] = useState();
|
||||||
const [scrollLeft, setScrollLeft] = useState();
|
|
||||||
const [scrollTop, setScrollTop] = useState();
|
|
||||||
const chartRef = useRef(null);
|
const chartRef = useRef(null);
|
||||||
|
|
||||||
const extraRows = 1;
|
const extraRows = 1 + (scales?.rows?.length || 0);
|
||||||
useEffect(() => {
|
|
||||||
setScrollLeft(rScrollLeft);
|
|
||||||
setScrollTop(rScrollTop);
|
|
||||||
}, [rScrollLeft, rScrollTop]);
|
|
||||||
|
|
||||||
|
|
||||||
const selectStyle = useMemo(() => {
|
const selectStyle = useMemo(() => {
|
||||||
const t = [];
|
const t = [];
|
||||||
if (selected && selected.length && cellHeight) {
|
if (selected && selected.length && cellHeight) {
|
||||||
@@ -58,10 +51,6 @@ function Chart(props) {
|
|||||||
return t;
|
return t;
|
||||||
}, [selectedCounter, cellHeight]);
|
}, [selectedCounter, cellHeight]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dataRequest();
|
|
||||||
}, [chartHeight]);
|
|
||||||
|
|
||||||
const chartGridHeight = useMemo(
|
const chartGridHeight = useMemo(
|
||||||
() => Math.max(chartHeight || 0, fullHeight),
|
() => Math.max(chartHeight || 0, fullHeight),
|
||||||
[chartHeight, fullHeight],
|
[chartHeight, fullHeight],
|
||||||
@@ -70,25 +59,21 @@ function Chart(props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = chartRef.current;
|
const el = chartRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
if (typeof scrollTop === 'number') el.scrollTop = scrollTop;
|
|
||||||
if (typeof scrollLeft === 'number') el.scrollLeft = scrollLeft;
|
if (typeof rScrollTop === 'number') {
|
||||||
if (typeof scrollTop === 'number' && scrollTop !== el.scrollTop)
|
el.scrollTop = rScrollTop;
|
||||||
setScroll({ top: true });
|
}
|
||||||
if (typeof scrollLeft === 'number' && scrollLeft !== el.scrollLeft)
|
}, [rScrollTop]);
|
||||||
setScroll({ left: true });
|
|
||||||
}, [scrollTop, scrollLeft]);
|
|
||||||
|
|
||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
const scroll = { left: true, top: true };
|
const scroll = { left: true };
|
||||||
setScroll(scroll);
|
setScroll(scroll);
|
||||||
dataRequest();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function setScroll(scroll) {
|
function setScroll(scroll) {
|
||||||
const el = chartRef.current;
|
const el = chartRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const pos = {};
|
const pos = {};
|
||||||
if (scroll.top) pos.top = el.scrollTop;
|
|
||||||
if (scroll.left) pos.left = el.scrollLeft;
|
if (scroll.left) pos.left = el.scrollLeft;
|
||||||
api.exec('scroll-chart', pos);
|
api.exec('scroll-chart', pos);
|
||||||
}
|
}
|
||||||
@@ -108,6 +93,10 @@ function Chart(props) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dataRequest();
|
||||||
|
}, [chartHeight, rScrollTop, rScrollLeft]);
|
||||||
|
|
||||||
const showTask = useCallback(
|
const showTask = useCallback(
|
||||||
(value) => {
|
(value) => {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
@@ -119,14 +108,16 @@ function Chart(props) {
|
|||||||
if (!el) return;
|
if (!el) return;
|
||||||
const { clientWidth } = el;
|
const { clientWidth } = el;
|
||||||
const task = api.getTask(id);
|
const task = api.getTask(id);
|
||||||
if (task.$x + task.$w < (scrollLeft || 0)) {
|
if (task.$x + task.$w < el.scrollLeft) {
|
||||||
setScrollLeft(task.$x - (cellWidth || 0));
|
api.exec('scroll-chart', { left: task.$x - (cellWidth || 0) });
|
||||||
} else if (task.$x >= clientWidth + (scrollLeft || 0)) {
|
el.scrollLeft = task.$x - (cellWidth || 0)
|
||||||
|
} else if (task.$x >= clientWidth + el.scrollLeft) {
|
||||||
const width = clientWidth < task.$w ? cellWidth || 0 : task.$w;
|
const width = clientWidth < task.$w ? cellWidth || 0 : task.$w;
|
||||||
setScrollLeft(task.$x - clientWidth + width);
|
api.exec('scroll-chart', { left: task.$x - clientWidth + width });
|
||||||
|
el.scrollLeft = task.$x - clientWidth + width;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[api, scrollLeft, cellWidth],
|
[api, cellWidth, rScrollLeft],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -165,10 +156,10 @@ function Chart(props) {
|
|||||||
: null;
|
: null;
|
||||||
}, [scales, highlightTime]);
|
}, [scales, highlightTime]);
|
||||||
|
|
||||||
function handleHotkey(ev) {
|
const handleHotkey = useCallback((ev) => {
|
||||||
ev.eventSource = 'chart';
|
ev.eventSource = 'chart';
|
||||||
api.exec('hotkey', ev);
|
api.exec('hotkey', ev);
|
||||||
}
|
}, [api]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = chartRef.current;
|
const el = chartRef.current;
|
||||||
@@ -182,10 +173,13 @@ function Chart(props) {
|
|||||||
};
|
};
|
||||||
}, [chartRef.current]);
|
}, [chartRef.current]);
|
||||||
|
|
||||||
|
const cleanupRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = chartRef.current;
|
const el = chartRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const cleanup = hotkeys(el, {
|
if (cleanupRef.current) return;
|
||||||
|
cleanupRef.current = hotkeys(el, {
|
||||||
keys: {
|
keys: {
|
||||||
arrowup: true,
|
arrowup: true,
|
||||||
arrowdown: true,
|
arrowdown: true,
|
||||||
@@ -193,10 +187,11 @@ function Chart(props) {
|
|||||||
exec: (v) => handleHotkey(v),
|
exec: (v) => handleHotkey(v),
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
if (typeof cleanup === 'function') cleanup();
|
cleanupRef.current?.destroy();
|
||||||
|
cleanupRef.current = null;
|
||||||
};
|
};
|
||||||
}, [chartRef.current]);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const node = chartRef.current;
|
const node = chartRef.current;
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
@@ -215,6 +210,7 @@ function Chart(props) {
|
|||||||
ref={chartRef}
|
ref={chartRef}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
>
|
>
|
||||||
|
<TimeScales highlightTime={highlightTime} scales={scales} />
|
||||||
{markers && markers.length ? (
|
{markers && markers.length ? (
|
||||||
<div
|
<div
|
||||||
className="wx-mR7v2Xag wx-markers"
|
className="wx-mR7v2Xag wx-markers"
|
||||||
@@ -260,15 +256,15 @@ function Chart(props) {
|
|||||||
|
|
||||||
{selected && selected.length
|
{selected && selected.length
|
||||||
? selected.map((obj, index) =>
|
? selected.map((obj, index) =>
|
||||||
obj.$y ? (
|
obj.$y ? (
|
||||||
<div
|
<div
|
||||||
key={obj.id}
|
key={obj.id}
|
||||||
className="wx-mR7v2Xag wx-selected"
|
className="wx-mR7v2Xag wx-selected"
|
||||||
data-id={obj.id}
|
data-id={obj.id}
|
||||||
style={selectStyle[index]}
|
style={selectStyle[index]}
|
||||||
></div>
|
></div>
|
||||||
) : null,
|
) : null,
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
<Links />
|
<Links />
|
||||||
|
|||||||
101
whatsnew.md
Normal file
101
whatsnew.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
## Version 2.3.4
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Performance regression during scrolling and zooming
|
||||||
|
|
||||||
|
## Version 2.3.3
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Fullscreen hotkey property should be optional
|
||||||
|
- Incorrect handling of themes
|
||||||
|
- ContextMenu custom class name cannot be set
|
||||||
|
|
||||||
|
## Version 2.3.2
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Tooltips for bars are not visible
|
||||||
|
|
||||||
|
## Version 2.3
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
- TypeScript definitions
|
||||||
|
- Ability to define "hour" duration unit
|
||||||
|
- Ability to define "minute" length unit
|
||||||
|
- Ability to create custom scale units
|
||||||
|
- `autoScale` property to configure start/end scale behaviour
|
||||||
|
- Standalone sidebar and modal Editor
|
||||||
|
- Ability to validate fields in the Editor
|
||||||
|
- Hotkeys for common actions and navigation
|
||||||
|
- Multi-sorting for table columns via the sort-tasks action
|
||||||
|
- Headermenu to hide table columns
|
||||||
|
- Inline editors for table cells
|
||||||
|
- Ability to expand/collapse table and chart via buttons
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
- The package migrated from wx-react-gantt to @svar-ui/react-gantt
|
||||||
|
|
||||||
|
## Version 1.3.1
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Rendering issues
|
||||||
|
|
||||||
|
## Version 1.3
|
||||||
|
|
||||||
|
- Released under GPLv3
|
||||||
|
|
||||||
|
## Version 1.1
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
- Summary tasks
|
||||||
|
- Sorting Grid columns
|
||||||
|
- Batch mode for RestDataProvider to handle mass operations
|
||||||
|
|
||||||
|
### Updates
|
||||||
|
|
||||||
|
- Common minCellwidth and maxCellWidth for zoom levels
|
||||||
|
- Skipping meaningless actions in Context Menu and Toolbar
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Scroll in Grid is not smooth
|
||||||
|
- Impossible to resize chart bars on the left more than for one cell
|
||||||
|
- When a branch is removed, only links of the parent task are removed
|
||||||
|
- Unstable move down behaviour: tasks are inserted in wrong positions
|
||||||
|
- Resizing columns: horizontal scrollbar does not appear
|
||||||
|
- Text of tasks is higher than a dragged bar during reordering
|
||||||
|
- Last task is misplaced after reordering when there are few tasks
|
||||||
|
- Reordering of tasks with child tasks is broken
|
||||||
|
- Impossible to define cellWidth if default zoom is enabled
|
||||||
|
- Data is removed from the task object after the "update-task" operation
|
||||||
|
- Outdenting does not work for the 3rd-level tasks
|
||||||
|
- Auto scale is calculated incorrectly if the last task is a milestone
|
||||||
|
- Parent task is not always opened after adding a new task
|
||||||
|
- Task start date is not set according to the top-level target task
|
||||||
|
- Zooming in and out between levels does not work correctly
|
||||||
|
|
||||||
|
## Version 1.0
|
||||||
|
|
||||||
|
### Initial functionality
|
||||||
|
|
||||||
|
- Fast behavior and clear API
|
||||||
|
- Configurable Grid columns
|
||||||
|
- Configurable Chart scales and cell sizes
|
||||||
|
- Task types: "project", "task", "milestone" and custom
|
||||||
|
- Baselines
|
||||||
|
- Holiday and custom markers in the Chart area
|
||||||
|
- Configurable Editor panel
|
||||||
|
- Configurable Context Menu and Toolbar
|
||||||
|
- Tooltips for tasks in the Chart area
|
||||||
|
- Readonly mode
|
||||||
|
- Fullscreen mode
|
||||||
|
- Mouse-wheel zooming in the Chart area
|
||||||
|
- Responsive behaviour of the Grid area
|
||||||
|
- Localization of labels and dates
|
||||||
|
- Ready-made DataProvider to integrate with server
|
||||||
Reference in New Issue
Block a user