v2.3.4
This commit is contained in:
+16
-22
@@ -7,7 +7,6 @@ import {
|
||||
useContext,
|
||||
} from 'react';
|
||||
import { hotkeys } from '@svar-ui/grid-store';
|
||||
import TimeScales from './TimeScale.jsx';
|
||||
import Grid from './grid/Grid.jsx';
|
||||
import Chart from './chart/Chart.jsx';
|
||||
import Resizer from './Resizer.jsx';
|
||||
@@ -31,9 +30,7 @@ function Layout(props) {
|
||||
const rScales = useStore(api, "_scales");
|
||||
const rCellHeight = useStore(api, "cellHeight");
|
||||
const rColumns = useStore(api, "columns");
|
||||
const scrollTop = useStore(api, "scrollTop");
|
||||
const rScrollTask = useStore(api, "_scrollTask");
|
||||
|
||||
const [compactMode, setCompactMode] = useState(false);
|
||||
let [gridWidth, setGridWidth] = useState(0);
|
||||
const [ganttWidth, setGanttWidth] = useState(undefined);
|
||||
@@ -131,20 +128,13 @@ function Layout(props) {
|
||||
const ganttDivRef = 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 el = ganttDivRef.current;
|
||||
api.exec('scroll-chart', {
|
||||
top: el ? el.scrollTop : 0,
|
||||
});
|
||||
if (el) {
|
||||
api.exec('scroll-chart', {
|
||||
top: el.scrollTop,
|
||||
});
|
||||
}
|
||||
}, [api]);
|
||||
|
||||
const latest = useRef({
|
||||
@@ -193,6 +183,7 @@ function Layout(props) {
|
||||
}
|
||||
if (top !== null) {
|
||||
api.exec('scroll-chart', { top: Math.max(top, 0) });
|
||||
ganttDivRef.current.scrollTop = Math.max(top, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -200,7 +191,7 @@ function Layout(props) {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
scrollToTask(rScrollTask);
|
||||
scrollToTask(rScrollTask);
|
||||
}, [rScrollTask]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -229,11 +220,14 @@ function Layout(props) {
|
||||
}, [pseudoRowsRef.current]);
|
||||
|
||||
const layoutRef = useRef(null);
|
||||
const cleanupRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (cleanupRef.current) return;
|
||||
const node = layoutRef.current;
|
||||
if (!node) return;
|
||||
|
||||
const cleanup = hotkeys(node, {
|
||||
cleanupRef.current = hotkeys(node, {
|
||||
keys: {
|
||||
'ctrl+c': true,
|
||||
'ctrl+v': true,
|
||||
@@ -246,9 +240,11 @@ function Layout(props) {
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
return cleanup.destroy;
|
||||
}, [api]);
|
||||
return () => {
|
||||
cleanupRef.current?.destroy();
|
||||
cleanupRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="wx-jlbQoHOz wx-gantt" ref={ganttDivRef} onScroll={onScroll}>
|
||||
@@ -289,8 +285,6 @@ function Layout(props) {
|
||||
) : null}
|
||||
|
||||
<div className="wx-jlbQoHOz wx-content" ref={chartRef}>
|
||||
<TimeScales highlightTime={highlightTime} />
|
||||
|
||||
<Chart
|
||||
readonly={readonly}
|
||||
fullWidth={fullWidth}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
.wx-scale.wx-ZkvhDKir {
|
||||
position: relative;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: var(--wx-background);
|
||||
box-shadow: var(--wx-timescale-shadow);
|
||||
z-index: 1;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.wx-row.wx-ZkvhDKir,
|
||||
|
||||
@@ -8,11 +8,9 @@ function TimeScale(props) {
|
||||
|
||||
const api = useContext(storeContext);
|
||||
const scales = useStore(api, "_scales");
|
||||
const scrollLeft = useStore(api, "scrollLeft");
|
||||
|
||||
const containerStyle = {
|
||||
width: `${(scales && scales.width) != null ? scales.width : 0}px`,
|
||||
left: `${-(scrollLeft != null ? scrollLeft : 0)}px`,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,14 +18,14 @@ function Bars(props) {
|
||||
|
||||
const api = useContext(storeContext);
|
||||
|
||||
const [rTasksValue, rTasksCounter] = useStoreWithCounter(api,"_tasks");
|
||||
const [rLinksValue, rLinksCounter] = useStoreWithCounter(api,"_links");
|
||||
const areaValue = useStore(api,"area");
|
||||
const scalesValue = useStore(api,"_scales");
|
||||
const taskTypesValue = useStore(api,"taskTypes");
|
||||
const baselinesValue = useStore(api,"baselines");
|
||||
const selectedValue = useStore(api,"_selected");
|
||||
const scrollTaskStore = useStore(api,"_scrollTask" );
|
||||
const [rTasksValue, rTasksCounter] = useStoreWithCounter(api, "_tasks");
|
||||
const [rLinksValue, rLinksCounter] = useStoreWithCounter(api, "_links");
|
||||
const areaValue = useStore(api, "area");
|
||||
const scalesValue = useStore(api, "_scales");
|
||||
const taskTypesValue = useStore(api, "taskTypes");
|
||||
const baselinesValue = useStore(api, "baselines");
|
||||
const selectedValue = useStore(api, "_selected");
|
||||
const scrollTaskStore = useStore(api, "_scrollTask");
|
||||
|
||||
const tasks = useMemo(() => {
|
||||
if (!areaValue || !Array.isArray(rTasksValue)) return [];
|
||||
@@ -66,21 +66,16 @@ function Bars(props) {
|
||||
}, [hasFocus, selectedValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!scrollTaskStore || typeof scrollTaskStore.subscribe !== 'function')
|
||||
if (!scrollTaskStore)
|
||||
return;
|
||||
const unsub = scrollTaskStore.subscribe((value) => {
|
||||
if (hasFocus && value) {
|
||||
const { id } = value;
|
||||
const node = containerRef.current?.querySelector(
|
||||
`.wx-bar[data-id='${id}']`,
|
||||
);
|
||||
if (node) node.focus();
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
if (unsub) unsub();
|
||||
};
|
||||
}, [scrollTaskStore, hasFocus]);
|
||||
if (hasFocus && scrollTaskStore) {
|
||||
const { id } = scrollTaskStore;
|
||||
const node = containerRef.current?.querySelector(
|
||||
`.wx-bar[data-id='${id}']`,
|
||||
);
|
||||
if (node) node.focus({ preventScroll: true });
|
||||
}
|
||||
}, [scrollTaskStore]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = containerRef.current;
|
||||
@@ -391,7 +386,7 @@ function Bars(props) {
|
||||
setLinkFrom(null);
|
||||
}
|
||||
}, [linkFrom]);
|
||||
|
||||
|
||||
const onClick = useCallback(
|
||||
(e) => {
|
||||
if (ignoreNextClickRef.current) {
|
||||
@@ -496,7 +491,7 @@ function Bars(props) {
|
||||
}}
|
||||
>
|
||||
{tasks.map((task) => {
|
||||
if (task.$skip) return null;
|
||||
if (task.$skip && task.$skip_baseline) return null;
|
||||
const barClass =
|
||||
`wx-bar wx-${taskTypeCss(task.type)}` +
|
||||
(touched && taskMove && task.id === taskMove.id ? ' wx-touch' : '') +
|
||||
@@ -518,7 +513,7 @@ function Bars(props) {
|
||||
: '');
|
||||
return (
|
||||
<Fragment key={task.id}>
|
||||
<div
|
||||
{!task.$skip && <div
|
||||
className={'wx-GKbcLEGA ' + barClass}
|
||||
style={taskStyle(task)}
|
||||
data-tooltip-id={task.id}
|
||||
@@ -574,6 +569,7 @@ function Bars(props) {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
{baselinesValue && !task.$skip_baseline ? (
|
||||
<div
|
||||
className={
|
||||
|
||||
@@ -13,6 +13,7 @@ import { hotkeys } from '@svar-ui/grid-store';
|
||||
import storeContext from '../../context';
|
||||
import { useStore, useStoreWithCounter } from '@svar-ui/lib-react';
|
||||
import './Chart.css';
|
||||
import TimeScales from '../TimeScale.jsx';
|
||||
|
||||
function Chart(props) {
|
||||
const {
|
||||
@@ -37,17 +38,9 @@ function Chart(props) {
|
||||
const zoom = useStore(api, "zoom");
|
||||
|
||||
const [chartHeight, setChartHeight] = useState();
|
||||
const [scrollLeft, setScrollLeft] = useState();
|
||||
const [scrollTop, setScrollTop] = useState();
|
||||
const chartRef = useRef(null);
|
||||
|
||||
const extraRows = 1;
|
||||
useEffect(() => {
|
||||
setScrollLeft(rScrollLeft);
|
||||
setScrollTop(rScrollTop);
|
||||
}, [rScrollLeft, rScrollTop]);
|
||||
|
||||
|
||||
const extraRows = 1 + (scales?.rows?.length || 0);
|
||||
const selectStyle = useMemo(() => {
|
||||
const t = [];
|
||||
if (selected && selected.length && cellHeight) {
|
||||
@@ -58,10 +51,6 @@ function Chart(props) {
|
||||
return t;
|
||||
}, [selectedCounter, cellHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
dataRequest();
|
||||
}, [chartHeight]);
|
||||
|
||||
const chartGridHeight = useMemo(
|
||||
() => Math.max(chartHeight || 0, fullHeight),
|
||||
[chartHeight, fullHeight],
|
||||
@@ -70,25 +59,21 @@ function Chart(props) {
|
||||
useEffect(() => {
|
||||
const el = chartRef.current;
|
||||
if (!el) return;
|
||||
if (typeof scrollTop === 'number') el.scrollTop = scrollTop;
|
||||
if (typeof scrollLeft === 'number') el.scrollLeft = scrollLeft;
|
||||
if (typeof scrollTop === 'number' && scrollTop !== el.scrollTop)
|
||||
setScroll({ top: true });
|
||||
if (typeof scrollLeft === 'number' && scrollLeft !== el.scrollLeft)
|
||||
setScroll({ left: true });
|
||||
}, [scrollTop, scrollLeft]);
|
||||
|
||||
if (typeof rScrollTop === 'number') {
|
||||
el.scrollTop = rScrollTop;
|
||||
}
|
||||
}, [rScrollTop]);
|
||||
|
||||
const onScroll = () => {
|
||||
const scroll = { left: true, top: true };
|
||||
const scroll = { left: true };
|
||||
setScroll(scroll);
|
||||
dataRequest();
|
||||
};
|
||||
|
||||
function setScroll(scroll) {
|
||||
const el = chartRef.current;
|
||||
if (!el) return;
|
||||
const pos = {};
|
||||
if (scroll.top) pos.top = el.scrollTop;
|
||||
if (scroll.left) pos.left = el.scrollLeft;
|
||||
api.exec('scroll-chart', pos);
|
||||
}
|
||||
@@ -108,6 +93,10 @@ function Chart(props) {
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dataRequest();
|
||||
}, [chartHeight, rScrollTop, rScrollLeft]);
|
||||
|
||||
const showTask = useCallback(
|
||||
(value) => {
|
||||
if (!value) return;
|
||||
@@ -119,14 +108,16 @@ function Chart(props) {
|
||||
if (!el) return;
|
||||
const { clientWidth } = el;
|
||||
const task = api.getTask(id);
|
||||
if (task.$x + task.$w < (scrollLeft || 0)) {
|
||||
setScrollLeft(task.$x - (cellWidth || 0));
|
||||
} else if (task.$x >= clientWidth + (scrollLeft || 0)) {
|
||||
if (task.$x + task.$w < el.scrollLeft) {
|
||||
api.exec('scroll-chart', { left: task.$x - (cellWidth || 0) });
|
||||
el.scrollLeft = task.$x - (cellWidth || 0)
|
||||
} else if (task.$x >= clientWidth + el.scrollLeft) {
|
||||
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(() => {
|
||||
@@ -165,10 +156,10 @@ function Chart(props) {
|
||||
: null;
|
||||
}, [scales, highlightTime]);
|
||||
|
||||
function handleHotkey(ev) {
|
||||
const handleHotkey = useCallback((ev) => {
|
||||
ev.eventSource = 'chart';
|
||||
api.exec('hotkey', ev);
|
||||
}
|
||||
}, [api]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = chartRef.current;
|
||||
@@ -182,10 +173,13 @@ function Chart(props) {
|
||||
};
|
||||
}, [chartRef.current]);
|
||||
|
||||
const cleanupRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = chartRef.current;
|
||||
if (!el) return;
|
||||
const cleanup = hotkeys(el, {
|
||||
if (cleanupRef.current) return;
|
||||
cleanupRef.current = hotkeys(el, {
|
||||
keys: {
|
||||
arrowup: true,
|
||||
arrowdown: true,
|
||||
@@ -193,10 +187,11 @@ function Chart(props) {
|
||||
exec: (v) => handleHotkey(v),
|
||||
});
|
||||
return () => {
|
||||
if (typeof cleanup === 'function') cleanup();
|
||||
cleanupRef.current?.destroy();
|
||||
cleanupRef.current = null;
|
||||
};
|
||||
}, [chartRef.current]);
|
||||
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const node = chartRef.current;
|
||||
if (!node) return;
|
||||
@@ -215,6 +210,7 @@ function Chart(props) {
|
||||
ref={chartRef}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
<TimeScales highlightTime={highlightTime} scales={scales} />
|
||||
{markers && markers.length ? (
|
||||
<div
|
||||
className="wx-mR7v2Xag wx-markers"
|
||||
@@ -260,15 +256,15 @@ function Chart(props) {
|
||||
|
||||
{selected && selected.length
|
||||
? selected.map((obj, index) =>
|
||||
obj.$y ? (
|
||||
<div
|
||||
key={obj.id}
|
||||
className="wx-mR7v2Xag wx-selected"
|
||||
data-id={obj.id}
|
||||
style={selectStyle[index]}
|
||||
></div>
|
||||
) : null,
|
||||
)
|
||||
obj.$y ? (
|
||||
<div
|
||||
key={obj.id}
|
||||
className="wx-mR7v2Xag wx-selected"
|
||||
data-id={obj.id}
|
||||
style={selectStyle[index]}
|
||||
></div>
|
||||
) : null,
|
||||
)
|
||||
: null}
|
||||
|
||||
<Links />
|
||||
|
||||
Reference in New Issue
Block a user