Skip to content

Commit

Permalink
feat: select multiple elements
Browse files Browse the repository at this point in the history
  • Loading branch information
sasza2 committed Jan 16, 2024
1 parent 5b38a01 commit 82a1aca
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 26 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-grid-panzoom",
"version": "1.2.0",
"version": "1.3.0",
"description": "React grid component for pan and zoom with possibility to moving and selecting elements inside",
"keywords": [
"react",
Expand Down Expand Up @@ -66,7 +66,7 @@
"vitest": "0.34.6"
},
"dependencies": {
"@sasza/react-panzoom": "1.11.4"
"@sasza/react-panzoom": "1.12.0"
},
"peerDependencies": {
"react": ">=16.14.0",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/Element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ElementWrapperProps = {
elementsNodes: Record<string | number, HTMLDivElement>,
elementResizerWidth?: number,
rowHeight: Pixels,
onStartResizing?: ElementProps['onStartResizing'],
onAfterResize?: ElementProps['onAfterResize'],
onClick?: ElementProps['onClick'],
onContextMenu?: ElementProps['onContextMenu'],
Expand All @@ -38,6 +39,7 @@ const ElementWrapper: React.FC<ElementWrapperProps> = ({
elementResizerWidth,
fullHeight = true,
rowHeight,
onStartResizing,
onAfterResize,
onClick,
onContextMenu,
Expand Down Expand Up @@ -113,6 +115,7 @@ const ElementWrapper: React.FC<ElementWrapperProps> = ({
family={element.family}
x={x}
y={y}
onStartResizing={onStartResizing}
onAfterResize={onAfterResize}
onClick={disabled ? undefined : onClick}
onContextMenu={disabled ? undefined : onContextMenu}
Expand All @@ -129,6 +132,7 @@ const ElementWrapper: React.FC<ElementWrapperProps> = ({
<div style={{ pointerEvents: 'none', height: fullHeight ? '100%' : undefined }}>
<LinesContainer />
{renderElement}
<div className="react-grid-panzoom-element-selection" />
</div>
</div>
</Element>
Expand Down
3 changes: 3 additions & 0 deletions src/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const Grid: React.FC<React.PropsWithChildren> = ({ children }) => {
onContainerContextMenu,
onContainerChange,
onElementContextMenu,
onElementStartResizing,
paddingLeft,
panZoomRef,
selectedElements,
Expand All @@ -68,6 +69,7 @@ const Grid: React.FC<React.PropsWithChildren> = ({ children }) => {

const onAfterResize = useOnAfterResize();
const onAfterResizeMemo = useMemoRef(onAfterResize);
const onElementStartResizingMemo = useMemoRef(onElementStartResizing);

const onElementMouseUp = useOnElementMouseUp();
const onElementMouseUpMemo = useMemoRef(onElementMouseUp);
Expand Down Expand Up @@ -154,6 +156,7 @@ const Grid: React.FC<React.PropsWithChildren> = ({ children }) => {
onClick={onClickMemo}
onContextMenu={onElementContextMenuMemo}
onMouseUp={onElementMouseUpMemo}
onStartResizing={onElementStartResizingMemo}
disabled={element.disabled}
fullHeight={element.fullHeight}
{...elementProps}
Expand Down
39 changes: 26 additions & 13 deletions src/Styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,37 @@ const Styles = () => (
<Style>
{`
@keyframes react-panzoom-element-animation {
0% { outline: solid 2px rgba(0, 0, 0, 0.05); }
50% { outline: solid 2px rgba(0, 0, 0, 0.15); }
100% { outline: solid 2px rgba(0, 0, 0, 0.05); }
0% { outline: solid 4px rgba(0, 0, 0, 0.05); }
50% { outline: solid 4px rgba(0, 0, 0, 0.15); }
100% { outline: solid 4px rgba(0, 0, 0, 0.05); }
}
.react-panzoom--element-moving .react-panzoom-element:not(.react-panzoom-element--disabled) {
.react-grid-panzoom .react-panzoom__in {
overflow: visible !important;
}
.react-panzoom--element-moving .react-panzoom-element:not(.react-panzoom-element--disabled) .react-grid-panzoom-element-selection {
outline-offset: -2px;
outline: solid 2px rgba(0, 0, 0, 0.1);
outline: solid 4px rgba(0, 0, 0, 0.1);
}
.react-panzoom--element-moving .react-panzoom-element-is-shadow {
z-index: 1;
}
.react-panzoom--element-moving .react-panzoom-element-is-shadow .react-grid-panzoom-element-selection {
outline: none;
}
.react-grid-panzoom .react-panzoom-element:hover {
.react-grid-panzoom .react-panzoom-element:hover .react-grid-panzoom-element-selection {
outline-offset: -2px;
animation: react-panzoom-element-animation 1s infinite;
}
.react-grid-panzoom .react-panzoom--element-moving .react-panzoom-element,
.react-grid-panzoom .react-panzoom--element-resizing .react-panzoom-element,
.react-grid-panzoom .react-panzoom--element-moving .react-panzoom-element:hover,
.react-grid-panzoom .react-panzoom--element-resizing .react-panzoom-element:hover {
.react-grid-panzoom .react-panzoom--element-moving .react-panzoom-element .react-grid-panzoom-element-selection,
.react-grid-panzoom .react-panzoom--element-resizing .react-panzoom-element .react-grid-panzoom-element-selection,
.react-grid-panzoom .react-panzoom--element-moving .react-panzoom-element:hover .react-grid-panzoom-element-selection,
.react-grid-panzoom .react-panzoom--element-resizing .react-panzoom-element:hover .react-grid-panzoom-element-selection {
outline-offset: -1px;
outline: solid 1px rgba(0, 0, 0, 0.1);
outline: solid 2px rgba(0, 0, 0, 0.1);
animation: none;
}
.react-grid-panzoom .react-panzoom--element-moving .react-panzoom-element.react-panzoom-element--disabled,
.react-grid-panzoom .react-panzoom--element-resizing .react-panzoom-element.react-panzoom-element--disabled {
.react-grid-panzoom .react-panzoom--element-moving .react-panzoom-element.react-panzoom-element--disabled .react-grid-panzoom-element-selection,
.react-grid-panzoom .react-panzoom--element-resizing .react-panzoom-element.react-panzoom-element--disabled .react-grid-panzoom-element-selection {
outline: none;
}
.react-grid-panzoom .react-panzoom-element {
Expand All @@ -53,6 +58,14 @@ const Styles = () => (
.react-grid-panzoom-lines-container .react-grid-panzoom-line--left {
text-align: right;
}
.react-grid-panzoom-element-selection {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
`}
</Style>
);
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useElementsChecksum.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import useMeasureElementHeight from './useMeasureElementHeight';
import useMemoRef from './useMemoRef';

export const useElementsChecksum = () => {
const { elements } = useGrid();
const { elements, selectedElements } = useGrid();
const measureElementHeight = useMeasureElementHeight();
const measureElementHeightMemo = useMemoRef(measureElementHeight);

return useMemo(() => {
const sortedElements = sortElements(elements);
return sortedElements.reduce((checksum, element) => `${checksum}--${element.id}-${measureElementHeightMemo(element)}`, '');
}, [elements]);
}, [elements, selectedElements]);
};
2 changes: 2 additions & 0 deletions src/hooks/useGrabElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const useGrabElement = () => {
const onPointerMove = (e: PointerEvent) => {
if (e.buttons) return;

setSelectedElements([]);

window.removeEventListener('pointermove', onPointerMove, true);

target.style.transition = '0.3s all';
Expand Down
12 changes: 8 additions & 4 deletions src/hooks/useOnAfterResize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const useOnAfterResize = () => {
rows,
rowHeight,
setElements,
setSelectedElements,
} = useGrid();

const calculateCellPositionByPixels = useCalculateCellPositionByPixels();
Expand All @@ -34,6 +35,7 @@ const useOnAfterResize = () => {
const onAfterResize: ElementProps['onAfterResize'] = useCallback(({ id }) => {
const panZoomElementsRef = panZoomRef.current.getElements();
const zoom = panZoomRef.current.getZoom();
const { node: elementNode } = panZoomElementsRef[id];

const startingElements = elements.map((originalElement) => {
if (originalElement.id !== id) return originalElement;
Expand Down Expand Up @@ -61,9 +63,8 @@ const useOnAfterResize = () => {
const selectedElement = getElementById(startingElements, id);

const updateElement = (element: GridElement) => {
const { node } = panZoomElementsRef[id];

node.current.style.width = isLengthAuto(colWidth)
elementNode.current.style.transition = '0.3s all';
elementNode.current.style.width = isLengthAuto(colWidth)
? undefined
: `${colWidth * element.w + gapHorizontal * (element.w - 1)}px`;

Expand All @@ -72,6 +73,9 @@ const useOnAfterResize = () => {
}));
};

elementNode.current.style.transition = 'none';
elementsHeightRef.current = {};

const nextElements = organizeGridElements({
startingElements,
cols,
Expand All @@ -88,7 +92,7 @@ const useOnAfterResize = () => {

updateElement(selectedElement);
setElements(nextElements, { type: 'user' });

setSelectedElements([]);
elementsHeightRef.current = {};
}, [cols, colWidth, elements, gapHorizontal, gapVertical, paddingLeft, rows, rowHeight]);

Expand Down
3 changes: 2 additions & 1 deletion src/measureElementHeightInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const measureElementHeightInit = (

const { height } = node.getBoundingClientRect();

return Math.ceil(height / rowHeight / zoom) || 1;
const originalHeight = Math.round((height / zoom) * 10) / 10; // px
return Math.ceil(originalHeight / rowHeight) || 1;
};

export default measureElementHeightInit;
1 change: 1 addition & 0 deletions types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type GridProps = PropsWithChildren<{
gapVertical?: number,
onContainerChange?: PanZoomOptions['onContainerChange'],
onContainerContextMenu?: PanZoomOptions['onContextMenu'],
onElementStartResizing?: ElementOptions['onStartResizing'],
onElementClick?: (element: GridElement) => void,
onElementContextMenu?: ElementOptions['onContextMenu'],
onElementsMeasureUpdate?: (elementsHeight: Record<number | string, number>) => void,
Expand Down

0 comments on commit 82a1aca

Please sign in to comment.