Skip to content

Using Viselect with Vue

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 Vue, install its vue package with:

sh
$ npm install @viselect/vue
sh
$ pnpm install @viselect/vue
sh
$ yarn add @viselect/vue

Usage

You can use Viselect in your Vue project by importing the SelectionArea component from the @viselect/vue package.

TIP

All options are exposed as options prop, events can be passed as props suffixed with on. The options are a one-to-one mapping of the original options describe here!

NOTE

Events are handled using props because you can’t return a value in events synchronously.

vue
<template>
  <SelectionArea class="container"
                 selectionAreaClass="selection-area"
                 :options="{ selectables: '.selectable' }"
                 :onMove="onMove"
                 :onStart="onStart">
    <div v-for="id of 42"
         class="selectable"
         :key="id" 
         :data-key="id"
         :class="{ selected: selected.has(id) }"/>
  </SelectionArea>
</template>

<script lang="ts" setup>
import { SelectionArea, SelectionEvent } from '@viselect/vue';
import { reactive } from 'vue';

const selected = reactive<Set<number>>(new Set());

const extractIds = (els: Element[]): number[] => {
  return els.map(v => v.getAttribute('data-key'))
      .filter(Boolean)
      .map(Number);
};

const onStart = ({ event, selection }: SelectionEvent) => {
  if (!event?.ctrlKey && !event?.metaKey) {
    selection.clearSelection();
    selected.clear();
  }
};

const onMove = ({ store: { changed: { added, removed } } }: SelectionEvent) => {
  extractIds(added).forEach(id => selected.add(id));
  extractIds(removed).forEach(id => selected.delete(id));
};
</script>

<style>
.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;
}

.container.green div.selected {
  background: linear-gradient(45deg, #78b2ff, #218ad9);
}

.container.blue div.selected {
  background: linear-gradient(45deg, #9e91ef, #5c51b4);
}

.selection-area {
  background: rgba(46, 115, 252, 0.11);
  border: 1px solid rgba(98, 155, 255, 0.85);
  border-radius: 0.15em;
}
</style>

API

Props

The Vue component accepts one required prop for the selection options:

PropType
optionsOmit<PartialSelectionOptions, 'boundaries'>

options contains the following optional properties: container: Quantify<string \| HTMLElement>, document: Document, selectables: Quantify<string>, startAreas: Quantify<string \| HTMLElement>, selectionAreaClass: string, selectionContainerClass: string, behaviour: DeepPartial<Behaviour>, and features: DeepPartial<Features>.

The component supplies its root <div> as boundaries, so boundaries cannot be set through options. Standard Vue fallthrough attributes such as class, style, id, ARIA attributes, and native DOM listeners can be placed directly on <SelectionArea>.

Events

The component emits the following events. Each event receives a SelectionEvent, except init, which receives the initialized SelectionArea instance.

EventHandler type
before-start(event: SelectionEvent) => void
before-drag(event: SelectionEvent) => void
start(event: SelectionEvent) => void
move(event: SelectionEvent) => void
stop(event: SelectionEvent) => void
init(selection: SelectionArea) => void

In contrast to the vanilla, React, and Preact integrations, Vue event handlers cannot return a value synchronously. Therefore, returning false from before-start or before-drag does not cancel the selection.

The option-related types (PartialSelectionOptions, Behaviour, Features, DeepPartial, and Quantify) and SelectionEvent are re-exported from @viselect/vue. See the API reference for their definitions.

Composables

It's possible to get the current SelectionArea-instance via template refs.

vue
<template>
  <SelectionArea 
    class="container"
    :options="{selectables: '.selectable'}"
    ref="selectionAreaRef"
  >
    <div v-for="id of 42"
         class="selectable"
         :key="id" 
         :data-key="id"
         :class="{selected: selected.has(id)}"
    />
  </SelectionArea>
</template>

<script lang="ts" setup>
import { SelectionArea } from '@viselect/vue';
import { useTemplateRef, reactive, watchEffect } from 'vue';

const selected = reactive<Set<number>>(new Set());
const selectionAreaRef = useTemplateRef<InstanceType<typeof SelectionArea>>();

watchEffect(() => {
  // log selection instance
  console.log(selectionAreaRef.value?.selection)
});
</script>

Released under the MIT License.