mirror of
https://github.com/zoriya/flood.git
synced 2026-06-06 03:56:42 +00:00
TableHeading: sync scroll with list on cell focus
This commit is contained in:
@@ -15,140 +15,144 @@ const pointerDownStyles = `
|
||||
|
||||
interface TableHeadingProps {
|
||||
onCellClick: (column: TorrentListColumn) => void;
|
||||
onCellFocus: () => void;
|
||||
onWidthsChange: (column: TorrentListColumn, width: number) => void;
|
||||
}
|
||||
|
||||
const TableHeading = observer(
|
||||
forwardRef<HTMLDivElement, TableHeadingProps>(({onCellClick, onWidthsChange}: TableHeadingProps, ref) => {
|
||||
const [isPointerDown, setIsPointerDown] = useState<boolean>(false);
|
||||
forwardRef<HTMLDivElement, TableHeadingProps>(
|
||||
({onCellClick, onCellFocus, onWidthsChange}: TableHeadingProps, ref) => {
|
||||
const [isPointerDown, setIsPointerDown] = useState<boolean>(false);
|
||||
|
||||
const focusedCell = useRef<TorrentListColumn>();
|
||||
const focusedCellWidth = useRef<number>();
|
||||
const lastPointerX = useRef<number>();
|
||||
const tableHeading = useEnsuredForwardedRef<HTMLDivElement>(ref as MutableRefObject<HTMLDivElement>);
|
||||
const resizeLine = useRef<HTMLDivElement>(null);
|
||||
const focusedCell = useRef<TorrentListColumn>();
|
||||
const focusedCellWidth = useRef<number>();
|
||||
const lastPointerX = useRef<number>();
|
||||
const tableHeading = useEnsuredForwardedRef<HTMLDivElement>(ref as MutableRefObject<HTMLDivElement>);
|
||||
const resizeLine = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {i18n} = useLingui();
|
||||
const {i18n} = useLingui();
|
||||
|
||||
const handlePointerMove = (event: PointerEvent) => {
|
||||
let widthDelta = 0;
|
||||
if (lastPointerX.current != null) {
|
||||
widthDelta = event.clientX - lastPointerX.current;
|
||||
}
|
||||
|
||||
let nextCellWidth = 20;
|
||||
if (focusedCellWidth.current != null) {
|
||||
nextCellWidth = focusedCellWidth.current + widthDelta;
|
||||
}
|
||||
|
||||
if (nextCellWidth > 20) {
|
||||
focusedCellWidth.current = nextCellWidth;
|
||||
lastPointerX.current = event.clientX;
|
||||
if (resizeLine.current != null && tableHeading.current != null) {
|
||||
resizeLine.current.style.transform = `translate(${Math.max(0, event.clientX)}px, ${
|
||||
tableHeading.current.getBoundingClientRect().top
|
||||
}px)`;
|
||||
const handlePointerMove = (event: PointerEvent) => {
|
||||
let widthDelta = 0;
|
||||
if (lastPointerX.current != null) {
|
||||
widthDelta = event.clientX - lastPointerX.current;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerUp = () => {
|
||||
UIStore.removeGlobalStyle(pointerDownStyles);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
let nextCellWidth = 20;
|
||||
if (focusedCellWidth.current != null) {
|
||||
nextCellWidth = focusedCellWidth.current + widthDelta;
|
||||
}
|
||||
|
||||
setIsPointerDown(false);
|
||||
lastPointerX.current = undefined;
|
||||
|
||||
if (resizeLine.current != null) {
|
||||
resizeLine.current.style.opacity = '0';
|
||||
}
|
||||
|
||||
if (focusedCell.current != null && focusedCellWidth.current != null) {
|
||||
onWidthsChange(focusedCell.current, focusedCellWidth.current);
|
||||
}
|
||||
|
||||
focusedCell.current = undefined;
|
||||
focusedCellWidth.current = undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="table__row table__row--heading" role="rowheader" ref={tableHeading}>
|
||||
{SettingStore.floodSettings.torrentListColumns.reduce((accumulator: ReactNodeArray, {id, visible}) => {
|
||||
if (!visible) {
|
||||
return accumulator;
|
||||
if (nextCellWidth > 20) {
|
||||
focusedCellWidth.current = nextCellWidth;
|
||||
lastPointerX.current = event.clientX;
|
||||
if (resizeLine.current != null && tableHeading.current != null) {
|
||||
resizeLine.current.style.transform = `translate(${Math.max(0, event.clientX)}px, ${
|
||||
tableHeading.current.getBoundingClientRect().top
|
||||
}px)`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const labelID = TorrentListColumns[id];
|
||||
if (labelID == null) {
|
||||
return accumulator;
|
||||
}
|
||||
const handlePointerUp = () => {
|
||||
UIStore.removeGlobalStyle(pointerDownStyles);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
|
||||
let handle = null;
|
||||
const width = SettingStore.floodSettings.torrentListColumnWidths[id] || 100;
|
||||
setIsPointerDown(false);
|
||||
lastPointerX.current = undefined;
|
||||
|
||||
if (!isPointerDown) {
|
||||
handle = (
|
||||
<span
|
||||
className="table__heading__handle"
|
||||
onPointerDown={(event) => {
|
||||
if (!isPointerDown && resizeLine.current != null && tableHeading.current != null) {
|
||||
setIsPointerDown(true);
|
||||
if (resizeLine.current != null) {
|
||||
resizeLine.current.style.opacity = '0';
|
||||
}
|
||||
|
||||
focusedCell.current = id;
|
||||
focusedCellWidth.current = width;
|
||||
lastPointerX.current = event.clientX;
|
||||
if (focusedCell.current != null && focusedCellWidth.current != null) {
|
||||
onWidthsChange(focusedCell.current, focusedCellWidth.current);
|
||||
}
|
||||
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
UIStore.addGlobalStyle(pointerDownStyles);
|
||||
focusedCell.current = undefined;
|
||||
focusedCellWidth.current = undefined;
|
||||
};
|
||||
|
||||
resizeLine.current.style.transform = `translate(${Math.max(0, event.clientX)}px, ${
|
||||
tableHeading.current.getBoundingClientRect().top
|
||||
}px)`;
|
||||
resizeLine.current.style.opacity = '1';
|
||||
}
|
||||
return (
|
||||
<div className="table__row table__row--heading" role="rowheader" ref={tableHeading}>
|
||||
{SettingStore.floodSettings.torrentListColumns.reduce((accumulator: ReactNodeArray, {id, visible}) => {
|
||||
if (!visible) {
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
const labelID = TorrentListColumns[id];
|
||||
if (labelID == null) {
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
let handle = null;
|
||||
const width = SettingStore.floodSettings.torrentListColumnWidths[id] || 100;
|
||||
|
||||
if (!isPointerDown) {
|
||||
handle = (
|
||||
<span
|
||||
className="table__heading__handle"
|
||||
onPointerDown={(event) => {
|
||||
if (!isPointerDown && resizeLine.current != null && tableHeading.current != null) {
|
||||
setIsPointerDown(true);
|
||||
|
||||
focusedCell.current = id;
|
||||
focusedCellWidth.current = width;
|
||||
lastPointerX.current = event.clientX;
|
||||
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
UIStore.addGlobalStyle(pointerDownStyles);
|
||||
|
||||
resizeLine.current.style.transform = `translate(${Math.max(0, event.clientX)}px, ${
|
||||
tableHeading.current.getBoundingClientRect().top
|
||||
}px)`;
|
||||
resizeLine.current.style.opacity = '1';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const isSortActive = id === SettingStore.floodSettings.sortTorrents.property;
|
||||
const classes = classnames('table__cell table__heading', {
|
||||
'table__heading--is-sorted': isSortActive,
|
||||
[`table__heading--direction--${SettingStore.floodSettings.sortTorrents.direction}`]: isSortActive,
|
||||
});
|
||||
|
||||
accumulator.push(
|
||||
<button
|
||||
className={classes}
|
||||
css={{
|
||||
textAlign: 'left',
|
||||
':focus': {
|
||||
outline: 'none',
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
type="button"
|
||||
key={id}
|
||||
onClick={() => onCellClick(id)}
|
||||
onFocus={() => onCellFocus()}
|
||||
style={{
|
||||
width: `${width}px`,
|
||||
}}>
|
||||
<span className="table__heading__label" title={i18n._(labelID)}>
|
||||
<Trans id={labelID} />
|
||||
</span>
|
||||
{handle}
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
|
||||
const isSortActive = id === SettingStore.floodSettings.sortTorrents.property;
|
||||
const classes = classnames('table__cell table__heading', {
|
||||
'table__heading--is-sorted': isSortActive,
|
||||
[`table__heading--direction--${SettingStore.floodSettings.sortTorrents.direction}`]: isSortActive,
|
||||
});
|
||||
|
||||
accumulator.push(
|
||||
<button
|
||||
className={classes}
|
||||
css={{
|
||||
textAlign: 'left',
|
||||
':focus': {
|
||||
outline: 'none',
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
},
|
||||
}}
|
||||
type="button"
|
||||
key={id}
|
||||
onClick={() => onCellClick(id)}
|
||||
style={{
|
||||
width: `${width}px`,
|
||||
}}>
|
||||
<span className="table__heading__label" title={i18n._(labelID)}>
|
||||
<Trans id={labelID} />
|
||||
</span>
|
||||
{handle}
|
||||
</button>,
|
||||
);
|
||||
|
||||
return accumulator;
|
||||
}, [])}
|
||||
<div className="table__cell table__heading table__heading--fill" />
|
||||
<div className="table__heading__resize-line" ref={resizeLine} />
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
return accumulator;
|
||||
}, [])}
|
||||
<div className="table__cell table__heading table__heading--fill" />
|
||||
<div className="table__heading__resize-line" ref={resizeLine} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
export default TableHeading;
|
||||
|
||||
@@ -86,6 +86,11 @@ const TorrentList: FC = observer(() => {
|
||||
if (isCondensed) {
|
||||
torrentListHeading = (
|
||||
<TableHeading
|
||||
onCellFocus={() => {
|
||||
if (listViewportOuterRef.current != null && listHeaderRef.current != null) {
|
||||
listViewportOuterRef.current.scrollLeft = listHeaderRef.current.scrollLeft;
|
||||
}
|
||||
}}
|
||||
onCellClick={(property: TorrentListColumn) => {
|
||||
const currentSort = SettingStore.floodSettings.sortTorrents;
|
||||
|
||||
@@ -101,10 +106,6 @@ const TorrentList: FC = observer(() => {
|
||||
};
|
||||
|
||||
SettingActions.saveSetting('sortTorrents', sortBy);
|
||||
|
||||
if (listViewportOuterRef.current != null && listHeaderRef.current != null) {
|
||||
listViewportOuterRef.current.scrollLeft = listHeaderRef.current.scrollLeft;
|
||||
}
|
||||
}}
|
||||
onWidthsChange={(column: TorrentListColumn, width: number) => {
|
||||
const {torrentListColumnWidths = defaultFloodSettings.torrentListColumnWidths} = SettingStore.floodSettings;
|
||||
|
||||
Reference in New Issue
Block a user