Skip to content

Commit

Permalink
feat: add cancel method (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
linxianxi authored Jan 24, 2024
1 parent a21427a commit 212911c
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 21 deletions.
73 changes: 73 additions & 0 deletions docs/examples/cancel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useEffect, useRef, useState } from 'react';
import Selectable, { SelectableRef, 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={{
width: 50,
height: 50,
borderRadius: 4,
border: isAdding ? '1px solid #1677ff' : undefined,
background: isRemoving ? 'red' : isSelected ? '#1677ff' : '#ccc',
}}
/>
);
};

export default () => {
const [value, setValue] = useState<string[]>([]);
const selectableRef = useRef<SelectableRef>(null);

useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.code === 'Escape') {
selectableRef.current?.cancel();
}
};

document.addEventListener('keydown', onKeyDown);

return () => {
document.removeEventListener('keydown', onKeyDown);
};
}, []);

return (
<Selectable
value={value}
ref={selectableRef}
dragContainer={() => document.getElementById('drag-container') as HTMLElement}
onEnd={(selectingValue, { added, removed }) => {
console.log('onEnd');
const result = value.concat(added).filter((i) => !removed.includes(i));
setValue(result);
}}
>
<div
id="drag-container"
style={{
display: 'flex',
flexWrap: 'wrap',
gap: 20,
padding: 20,
border: '1px solid #ccc',
}}
>
{list.map((i) => (
<Item key={i} value={i} />
))}
</div>
</Selectable>
);
};
10 changes: 8 additions & 2 deletions docs/guides/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,23 @@ const { setNodeRef, isSelected, isAdding, isRemoving, isSelecting, isDragging }
});
```

| 参数 | 说明 | 类型 | 默认值 |
| Property | Description | Type | Default |
| -------- | ------------------------------------------- | -------------------------- | ----------- |
| value | The value of the current selectable element | string \| number | - |
| disabled | Whether to disable | boolean | false |
| rule | Selection rule | `collision` \| `inclusion` | `collision` |

| 参数 | 说明 | 类型 |
| Property | 说明 | 类型 |
| ----------- | -------------------------------------- | -------------------------------------- |
| setNodeRef | Set the selectable element | (element: HTMLElement \| null) => void |
| isDragging | Whether it is currently dragging | boolean |
| isSelected | Whether it has been selected | boolean |
| isAdding | Whether it is currently being added | boolean |
| isRemoving | Whether it is currently being removed | boolean |
| isSelecting | Whether it is currently being selected | boolean |

### Methods

| Name | Description | Type |
| ------ | ---------------- | ---------- |
| cancel | cancel selection | () => void |
6 changes: 6 additions & 0 deletions docs/guides/api.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,9 @@ const { setNodeRef, isSelected, isAdding, isRemoving, isSelecting, isDragging }
| isAdding | 当前是否正在添加 | boolean |
| isRemoving | 当前是否正在删除 | boolean |
| isSelecting | 当前是否被框选 | boolean |

### 方法

| 名称 | 说明 | 类型 |
| ------ | -------- | ---------- |
| cancel | 取消选择 | () => void |
11 changes: 11 additions & 0 deletions docs/guides/cancel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: cancel
group:
title: examples
order: 1
order: 9
---

### Press Esc to cancel

<code src="../examples/cancel.tsx"></code>
11 changes: 11 additions & 0 deletions docs/guides/cancel.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: 取消
group:
title: 示例
order: 1
order: 9
---

### 按下 Esc 取消

<code src="../examples/cancel.tsx"></code>
54 changes: 36 additions & 18 deletions src/Selectable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,28 @@ export interface SelectableProps<T> {
getContainer?: () => HTMLElement;
}

function Selectable<T extends string | number>({
defaultValue,
value: propsValue,
disabled,
mode = 'add',
children,
selectStartRange = 'all',
getContainer,
scrollContainer: propsScrollContainer,
dragContainer: propsDragContainer,
boxStyle,
boxClassName,
onStart,
onEnd,
}: SelectableProps<T>) {
export interface SelectableRef {
cancel: () => void;
}

function Selectable<T extends string | number>(
{
defaultValue,
value: propsValue,
disabled,
mode = 'add',
children,
selectStartRange = 'all',
getContainer,
scrollContainer: propsScrollContainer,
dragContainer: propsDragContainer,
boxStyle,
boxClassName,
onStart,
onEnd,
}: SelectableProps<T>,
ref: React.ForwardedRef<SelectableRef>,
) {
const [isDragging, setIsDragging] = useState(false);
const [startCoords, setStartCoords] = useState({ x: 0, y: 0 });
const [moveCoords, setMoveCoords] = useState({ x: 0, y: 0 });
Expand All @@ -52,6 +59,7 @@ function Selectable<T extends string | number>({
const [value, setValue] = useMergedState(defaultValue || [], {
value: propsValue,
});
const [isCanceled, setIsCanceled] = useState(false);

const scrollContainer = useContainer(propsScrollContainer || getContainer);
const dragContainer = useContainer(propsDragContainer || propsScrollContainer || getContainer);
Expand All @@ -73,6 +81,13 @@ function Selectable<T extends string | number>({
);
}

React.useImperativeHandle(ref, () => ({
cancel: () => {
setIsCanceled(true);
setIsDragging(false);
},
}));

const onScroll = () => {
if (isDraggingRef.current && scrollContainer) {
const containerRect = scrollContainer.getBoundingClientRect();
Expand Down Expand Up @@ -114,13 +129,14 @@ function Selectable<T extends string | number>({

const reset = () => {
setIsDragging(false);
setIsCanceled(false);
setStartTarget(null);
isMouseDowning = false;
startInside.current = false;
selectingValue.current = [];
};

if (disabled || !scrollContainer || !dragContainer) {
if (disabled || !scrollContainer || !dragContainer || isCanceled) {
reset();
return;
}
Expand Down Expand Up @@ -210,7 +226,7 @@ function Selectable<T extends string | number>({
window.removeEventListener('touchmove', onMouseMove);
window.removeEventListener('touchend', onMouseUp);
};
}, [disabled, scrollContainer, dragContainer]);
}, [disabled, scrollContainer, dragContainer, isCanceled]);

const contextValue = useMemo(
() => ({
Expand Down Expand Up @@ -252,4 +268,6 @@ function Selectable<T extends string | number>({
);
}

export default Selectable;
export default React.forwardRef(Selectable) as <T>(
props: SelectableProps<T> & React.RefAttributes<SelectableRef>,
) => React.ReactElement;
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Selectable from './Selectable';
import useSelectable from './hooks/useSelectable';

export type { SelectableProps } from './Selectable';
export type { SelectableProps, SelectableRef } from './Selectable';

export { useSelectable };

Expand Down

0 comments on commit 212911c

Please sign in to comment.