diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e45291f..862f4d8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,6 +17,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: + registry-url: https://registry.npmjs.org node-version: 18 cache: 'pnpm' diff --git a/packages/react/demo/index.css b/packages/react/demo/index.css index e547f35..902fa15 100644 --- a/packages/react/demo/index.css +++ b/packages/react/demo/index.css @@ -4,12 +4,6 @@ box-sizing: border-box; } -html, -body, -#root { - height: 100%; -} - #root { width: 100%; max-width: 50em; @@ -49,6 +43,18 @@ body, margin-bottom: 3em; } +#root .viewport { + height: 150px; + overflow: auto; + margin-bottom: 3em; +} + +#root .viewport .container { + max-height: unset; + margin-bottom: 0; + overflow: initial; +} + #root .container.red > div { margin: 0; } diff --git a/packages/react/demo/index.tsx b/packages/react/demo/index.tsx index 7d78d37..f48ea84 100644 --- a/packages/react/demo/index.tsx +++ b/packages/react/demo/index.tsx @@ -3,12 +3,14 @@ import {createRoot} from 'react-dom/client'; import SelectionArea, {SelectionEvent} from '../src'; import './index.css'; -function SelectableArea({boxes, offset, className}: { +function SelectableArea({boxes, offset, className, withCustomViewport}: { boxes: number; offset: number; className: string; + withCustomViewport?: boolean; }) { const [selected, setSelected] = useState>(() => new Set()); + const viewportRef = React.useRef(null); const extractIds = (els: Element[]): number[] => els.map(v => v.getAttribute('data-key')) @@ -31,20 +33,37 @@ function SelectableArea({boxes, offset, className}: { }); }; - return ( - - {new Array(boxes).fill(0).map((_, index) => ( -
- ))} - + const content = ( + + {new Array(boxes).fill(0).map((_, index) => ( +
+ ))} + + ); + + return withCustomViewport ? ( +
+ {content} +
+ ) : ( + content ); -} +} const root = createRoot(document.getElementById('root') as HTMLElement); @@ -54,5 +73,6 @@ root.render( + , ); diff --git a/packages/react/src/SelectionArea.tsx b/packages/react/src/SelectionArea.tsx index 39f5f85..e227b4b 100644 --- a/packages/react/src/SelectionArea.tsx +++ b/packages/react/src/SelectionArea.tsx @@ -1,9 +1,9 @@ /* eslint-disable no-use-before-define */ import VanillaSelectionArea from '@viselect/vanilla'; -import {SelectionEvents, SelectionOptions} from '@viselect/vanilla'; +import {SelectionEvents, PartialSelectionOptions} from '@viselect/vanilla'; import React, {createRef, useEffect, createContext, useContext, useState} from 'react'; -export interface SelectionAreaProps extends Omit, 'boundaries'>, React.HTMLAttributes { +export interface SelectionAreaProps extends Omit, React.HTMLAttributes { id?: string; className?: string; onBeforeStart?: SelectionEvents['beforestart']; @@ -11,6 +11,7 @@ export interface SelectionAreaProps extends Omit, 'bou onStart?: SelectionEvents['start']; onMove?: SelectionEvents['move']; onStop?: SelectionEvents['stop']; + viewportRef?: React.RefObject; } const SelectionContext = createContext(undefined); @@ -19,11 +20,15 @@ export const useSelection = () => useContext(SelectionContext); export const SelectionArea: React.FunctionComponent = props => { const [selectionState, setSelection] = useState(undefined); - const root = createRef(); + const root = props.viewportRef ?? createRef(); useEffect(() => { - const {onBeforeStart, onBeforeDrag, onStart, onMove, onStop, ...opt} = props; - const areaBoundaries = root.current as HTMLElement; + const {onBeforeStart, onBeforeDrag, onStart, onMove, onStop, viewportRef, ...opt} = props; + const areaBoundaries = root.current; + + if (!areaBoundaries) { + return; + } const selection = new VanillaSelectionArea({ boundaries: areaBoundaries, @@ -42,13 +47,17 @@ export const SelectionArea: React.FunctionComponent = props selection.destroy(); setSelection(undefined); }; - }, []); + }, [root.current]); return ( - -
- {props.children} -
-
+ +
+ {props.children} +
+
); };