Skip to content

Commit

Permalink
fix: start box selection when the area exceeds 1 (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
linxianxi authored Aug 18, 2023
1 parent 4c26aaf commit 220f7b7
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pnpm i react-selectable-box

#### 📝 License

Copyright © 2020 - present [Arvin Xu][profile-url]. <br />
Copyright © 2023 - present [linxianxi][profile-url]. <br />
This project is [MIT](./LICENSE) licensed.

<!-- LINK GROUP -->
Expand Down
3 changes: 3 additions & 0 deletions docs/examples/click-to-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export default () => {
return (
<Selectable
value={value}
onStart={() => {
console.log('start');
}}
onEnd={(selectingValue, { added, removed }) => {
const result = value.concat(added).filter((i) => !removed.includes(i));
setValue(result);
Expand Down
60 changes: 60 additions & 0 deletions docs/examples/reset-at-start.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useState } from 'react';
import Selectable, { useSelectable } from 'react-selectable-box';

const list: string[] = [];
for (let i = 0; i < 200; i++) {
list.push(String(i));
}

const Item = ({ value }: { value: string }) => {
const { setNodeRef, isSelected, isAdding, isRemoving } = useSelectable({
value,
});

return (
<div
ref={setNodeRef}
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: 50,
height: 50,
border: isAdding ? '1px solid #1677ff' : undefined,
background: isRemoving ? 'red' : isSelected ? '#1677ff' : '#ccc',
}}
>
{value}
</div>
);
};

export default () => {
const [value, setValue] = useState<React.Key[]>([]);

return (
<Selectable
value={value}
onStart={() => {
setValue([]);
}}
onEnd={(newValue) => {
setValue(newValue);
}}
>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
gap: 20,
padding: 20,
border: '1px solid #ccc',
}}
>
{list.map((i) => (
<Item key={i} value={i} />
))}
</div>
</Selectable>
);
};
2 changes: 1 addition & 1 deletion docs/guides/click-to-select.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: click to select
group:
title: examples
order: 1
order: 4
order: 5
---

### click to select
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/reset-at-end.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: reset at end
group:
title: examples
order: 1
order: 1
order: 2
---

### reset at end
Expand Down
11 changes: 11 additions & 0 deletions docs/guides/reset-at-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: reset at start
group:
title: examples
order: 1
order: 1
---

### reset at start

<code src="../examples/reset-at-start.tsx"></code>
4 changes: 2 additions & 2 deletions docs/guides/scroll-container.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: scrollContainer
title: scroll-container
group:
title: examples
order: 1
order: 2
order: 3
---

### scrollContainer
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/shift-remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: hold shift to remove
group:
title: examples
order: 1
order: 3
order: 4
---

### hold shift to remove
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/virtual-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: virtual-list
group:
title: examples
order: 1
order: 5
order: 6
---

### virtual list
Expand Down
37 changes: 23 additions & 14 deletions src/Selectable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ const Selectable = forwardRef<SelectableRef, SelectableProps>(
ref,
) => {
const [isDragging, setIsDragging] = useState(false);
const [startCoords, setStartCoords] = useState({ x: 0, y: 0 });
const [moveCoords, setMoveCoords] = useState({ x: 0, y: 0 });
const isStart = useRef(false);
const selectingValue = useRef([]);
const [startTarget, setStartTarget] = useState<HTMLElement | null>(null);
const startInside = useRef(false);
const moveClient = useRef({ x: 0, y: 0 });
const [value, setValue] = useMergedState(defaultValue || [], {
value: propsValue,
onChange: (newValue) => {
Expand All @@ -74,17 +81,18 @@ const Selectable = forwardRef<SelectableRef, SelectableProps>(
}
},
});
const [startCoords, setStartCoords] = useState({ x: 0, y: 0 });
const [moveCoords, setMoveCoords] = useState({ x: 0, y: 0 });
const isStart = useRef(false);
const selectingValue = useRef([]);
const [startTarget, setStartTarget] = useState<HTMLElement | null>(null);
const startInside = useRef(false);
const moveClient = useRef({ x: 0, y: 0 });

const startCoordsRef = useRef(startCoords);
startCoordsRef.current = startCoords;
const isDraggingRef = useRef(isDragging);
isDraggingRef.current = isDragging;

const top = Math.min(startCoords.y, moveCoords.y);
const left = Math.min(startCoords.x, moveCoords.x);
const width = isDragging ? Math.abs(startCoords.x - moveCoords.x) : 0;
const height = isDragging ? Math.abs(startCoords.y - moveCoords.y) : 0;
const boxRect = { top, left, width, height };

const checkScroll = () => {
if (isDraggingRef.current) {
const scrollContainer = getContainer();
Expand Down Expand Up @@ -122,17 +130,23 @@ const Selectable = forwardRef<SelectableRef, SelectableProps>(
const onMouseMove = (e: MouseEvent) => {
const shouldContinue = selectFromInside ? true : !startInside.current;
if (isStart.current && shouldContinue) {
handleStart();
const { clientX, clientY } = e;
moveClient.current = { x: clientX, y: clientY };
setIsDragging(true);
const { left, top } = container.getBoundingClientRect();
const x = clientX - left + container.scrollLeft;
const y = clientY - top + container.scrollTop;
setMoveCoords({
x: Math.min(x, container.scrollWidth),
y: Math.min(y, container.scrollHeight),
});
const width = Math.abs(startCoordsRef.current.x - x);
const height = Math.abs(startCoordsRef.current.y - y);
// prevent trigger when click too fast
// https://github.com/linxianxi/react-selectable-box/issues/5
if (width * height > 1) {
setIsDragging(true);
handleStart();
}
} else {
setStartTarget(null);
}
Expand Down Expand Up @@ -184,11 +198,6 @@ const Selectable = forwardRef<SelectableRef, SelectableProps>(
}, [disabled, selectFromInside]);

const container = getContainer();
const top = Math.min(startCoords.y, moveCoords.y);
const left = Math.min(startCoords.x, moveCoords.x);
const width = isDragging ? Math.abs(startCoords.x - moveCoords.x) : 0;
const height = isDragging ? Math.abs(startCoords.y - moveCoords.y) : 0;
const boxRect = { top, left, width, height };

const contextValue = useMemo(
() => ({
Expand Down

0 comments on commit 220f7b7

Please sign in to comment.