Using Viselect with Preact
TIP
This is merely a convenience wrapper around the core library. If you want to have full control over it, you can roll out your own wrapper in your app.
Source Code
You can find the source code for this component here. You can use it as template for your own implementation.
Installation
To use Viselect with Preact, install its preact package with:
$ npm install @viselect/preact$ pnpm install @viselect/preact$ yarn add @viselect/preactUsage
You can use Viselect in your Preact project by importing the SelectionArea component from the @viselect/preact package.
TIP
All options are exposed as props. They're a one-to-one mapping of the original options describe here!
NOTE
Changing the props won't do anything as React doesn't deep-compare props. Trigger a re-render by adding and changing the key of the SelectionArea component. See #226.
import { SelectionArea, SelectionEvent } from '@viselect/preact';
import { FunctionalComponent } from 'preact';
import { useState } from 'preact/hooks';
import './styles.css';
const App: FunctionComponent = () => {
const [selected, setSelected] = useState<Set<number>>(() => new Set());
const extractIds = (els: Element[]): number[] =>
els.map(v => v.getAttribute('data-key'))
.filter(Boolean)
.map(Number);
const onStart = ({event, selection}: SelectionEvent) => {
if (!event?.ctrlKey && !event?.metaKey) {
selection.clearSelection();
setSelected(() => new Set());
}
};
const onMove = ({store: {changed: {added, removed}}}: SelectionEvent) => {
setSelected(prev => {
const next = new Set(prev);
extractIds(added).forEach(id => next.add(id));
extractIds(removed).forEach(id => next.delete(id));
return next;
});
};
return (
<>
<SelectionArea className="container"
selectionAreaClass="selection-area"
onStart={onStart}
onMove={onMove}
selectables=".selectable">
{new Array(42).fill(0).map((_, index) => (
<div className={selected.has(index) ? 'selected selectable' : 'selectable'}
data-key={index}
key={index}/>
))}
</SelectionArea>
</>
);
}.container {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
border: 1px dashed #4f5276;
border-radius: 15px;
padding: 15px;
margin: 15px 0;
user-select: none;
}
.container div {
height: 50px;
width: 50px;
margin: 3px;
background: rgba(66, 68, 90, 0.075);
border-radius: 10px;
cursor: pointer;
}
div.selected {
background: linear-gradient(45deg, #78b2ff, #218ad9);
}
.selection-area {
background: rgba(46, 115, 252, 0.11);
border: 1px solid rgba(98, 155, 255, 0.85);
border-radius: 0.15em;
}API
Props
SelectionArea accepts all options from PartialSelectionOptions as optional props, in addition to the standard Preact HTMLAttributes<HTMLDivElement> props.
Selection options
| Prop | Type |
|---|---|
boundaries | Quantify<string | HTMLElement> |
container | Quantify<string | HTMLElement> |
document | Document |
selectables | Quantify<string> |
startAreas | Quantify<string | HTMLElement> |
selectionAreaClass | string |
selectionContainerClass | string |
behaviour | DeepPartial<Behaviour> |
features | DeepPartial<Features> |
All selection option props are optional. boundaries defaults to the component's root <div>. When boundaries is provided, the component does not render that wrapper and the supplied element(s) act as the boundaries instead.
Event callbacks
| Prop | Type |
|---|---|
onBeforeStart | (event: SelectionEvent) => boolean | void |
onBeforeDrag | (event: SelectionEvent) => boolean | void |
onStart | (event: SelectionEvent) => void |
onMove | (event: SelectionEvent) => void |
onStop | (event: SelectionEvent) => void |
Returning false from onBeforeStart or onBeforeDrag cancels the selection.
Element props
The component also accepts standard Preact <div> attributes, including id, className, style, children, ARIA attributes, and event handlers such as onClick.
The option-related types (PartialSelectionOptions, Behaviour, Features, DeepPartial, and Quantify) and SelectionEvent are re-exported from @viselect/preact. See the API reference for their definitions.
Hooks
To access the SelectionArea instance, you can use the useSelection hook provided by the @viselect/preact package. The context is provided by the SelectionArea component, so make sure to use it within the component tree where the SelectionArea is rendered. It contains a SelectionArea instance.
import { useSelection } from '@viselect/preact';