Selection: Elixir-Powered Data Selection Mastery

Selection

Visual dom-selection library

Features 🤘

  • 🌟 Modern bundle
  • 🔩 Ultra tiny (~4kb)
  • 👌 Simple usage
  • ⚡ Highly optimized
  • ✔ Zero dependencies
  • 📱 Mobile / touch support
  • 🖱 Vertical and horizontal scroll support
  • 💪 Hardened (over 3 years old and used in many apps)
  • 🖼 Support for major frameworks (WIP)

Getting started

Check out the documentation for the package you want to use:

Check out recipes for commonly asked questions and how to solve them using the standart library! For information about events and more check out the vanilla readme!

Installation

Via package manager

$ npm install @viselect/vanilla
# or 
$ yarn add @viselect/vanilla

Via script tags

<script src="https://cdn.jsdelivr.net/npm/@viselect/vanilla/lib/viselect.cjs.js"></script>

Via ES6 import

import SelectionArea from "https://cdn.jsdelivr.net/npm/@viselect/vanilla/lib/viselect.esm.js"

Getting started

Last but not least you'll need to add some basic styles to make your selection-area visible:

.selection-area {
    background: rgba(46, 115, 252, 0.11);
    border: 2px solid rgba(98, 155, 255, 0.81);
    border-radius: 0.1em;
}

Additionally, to not interfere with text-selection, selection-js won't prevent any default events anymore (as of v2.0.3). This however can cause problems with the actual selection ("introduced" by #99, reported in #103). If you don't care about text-selection, add the following to the container where all your selectables are located:

.container {
    user-select: none;
}

Configuration

const selection = new SelectionArea({

    // Class for the selection-area itself (the element).
    selectionAreaClass: 'selection-area',

    // Class for the selection-area container.
    selectionContainerClass: 'selection-area-container',

    // Query selector or dom-node to set up container for the selection-area element.
    container: 'body',

    // document object - if you want to use it within an embed document (or iframe).
    document: window.document,

    // Query selectors for elements which can be selected.
    selectables: [],

    // Query selectors for elements from where a selection can be started from.
    startareas: ['html'],

    // Query selectors for elements which will be used as boundaries for the selection.
    boundaries: ['html'],

    // Behaviour related options.
    behaviour: {

        // Specifies what should be done if already selected elements get selected again.
        //   invert: Invert selection for elements which were already selected
        //   keep: Keep selected elements (use clearSelection() to remove those)
        //   drop: Remove stored elements after they have been touched
        overlap: 'invert',

        // On which point an element should be selected.
        // Available modes are cover (cover the entire element), center (touch the center) or
        // the default mode is touch (just touching it).
        intersect: 'touch',

        // px, how many pixels the point should move before starting the selection (combined distance).
        // Or specifiy the threshold for each axis by passing an object like {x: <number>, y: <number>}.
        startThreshold: 10,

        // Scroll configuration.
        scrolling: {

            // On scrollable areas the number on px per frame is devided by this amount.
            // Default is 10 to provide a enjoyable scroll experience.
            speedDivider: 10,

            // Browsers handle mouse-wheel events differently, this number will be used as 
            // numerator to calculate the mount of px while scrolling manually: manualScrollSpeed / scrollSpeedDivider.
            manualSpeed: 750,

            // This property defines the virtual inset margins from the borders of the container
            // component that, when crossed by the mouse/touch, trigger the scrolling. Useful for
            // fullscreen containers.
            startScrollMargins: {x: 0, y: 0}
        }
    },

    // Features.
    features: {

        // Enable / disable touch support.
        touch: true,

        // Range selection.
        range: true,

        // Configuration in case a selectable gets just clicked.
        singleTap: {

            // Enable single-click selection (Also disables range-selection via shift + ctrl).
            allow: true,

            // 'native' (element was mouse-event target) or 'touch' (element visually touched).
            intersect: 'native'
        }
    }
});

Events

Use the on(event, cb) and off(event, cb) functions to bind / unbind event-listener.

EventDescription
beforestartThe user tapped one of the areas within the specified boundaries. Return false to cancel selection immediatly.
beforedragSame as beforestart but before the user starts selecting by dragging the mouse. Can be used to conditionally allow a selection by dragging. Return false to cancel the selection.
startSelection started, here you can decide if you want to keep your previously selected elements.
moveSelection is active, user is moving the pointer around.
stopSelection has stopped.

Functions

FunctionDescription
trigger(evt: MouseEvent / TouchEvent, silent = true): voidManually trigger a selection.
resolveSelectables(): voidUpdates the list of selectables, useful if new elements have been added during a selection.
clearSelection(includeStored = true): voidClears the selection, pass false to keep previously selected elements.
getSelection(): Element[]Returns currently selected element. Use it in the stop event to collect selected elements.
getSelectionArea(): HTMLElementReturns the selection area element.
cancel(keepEvent = false): voidCancel the currently active selection, pass true to trigger the stop event afterwards.
destroy(): voidDestroy the SelectionArea-instance, removes all event-listeners and the selection-area element from the DOM.
disable(): voidDisables the selection-area temporarily.
enable(): voidEnables the selection-area.
select(query: SelectAllSelectors, quiet = false): Element[]Manually select elements, if quiet is set to true this will not fire the move & stop event.
deselect(query: SelectAllSelectors, quiet = false): Element[]Manually deselect elements, if quiet is set to true this will not fire the move & stop event.

Example

selection.on('beforestart', evt => {

    // Use this event to decide whether a selection should take place or not.
    // For example if the user should be able to normally interact with input-elements you 
    // may want to prevent a selection if the user clicks such a element:
    // selection.on('beforestart', ({event}) => {
    //   return event.target.tagName !== 'INPUT'; // Returning false prevents a selection
    // });

    console.log('beforestart', evt);
}).on('beforedrag', evt => {

    // Same as 'beforestart' but before a selection via dragging happens.
    console.log('beforedrag', evt);
}).on('start', evt => {

    // A selection got initiated, you could now clear the previous selection or
    // keep it if in case of multi-selection.
    console.log('start', evt);
}).on('move', evt => {

    // Here you can update elements based on their state.
    console.log('move', evt);
}).on('stop', evt => {

    // Do something with the selected elements.
    console.log('stop', evt);
});

Event properties

Every event comes with the following properties:

{
    selection: SelectionArea // Current instance
    event: TouchEvent | MouseEvent | null // TouchEvent, MouseEvent or `null` if triggered manually
    store: {
        touched: Element[] // Touched elements
        selected: Element[] // Elements from the currently active selection (each click, drag counts as a single "selection") 
        stored: Element[] // Elements currently selected (in total, not just an instant)
        changed: {
            added: Element[] // Added elements since last change
            removed: Element[] // Removed elements since last change
        }
    }
}

Common recipes can be found under recipes.

Browser support

This library will always have the previous year as its target. For 2021 for example the target will be ES2020. It always provides both a UMD (.js) and .mjs version. If you want to support legacy browsers, please use the feature of your bundler to transpile dependencie. In case of webpack and babel (give vite a try, it's awesome) you'll have to install corresponding plugins such as babel-plugin-proposal-optional-chaining and include the dependency from node_modules which is normally entirely excluded from being processed.

I do this to provide maximum flexibility and give those who target ESNext a chance to make full use of how this library is bundled. Everything else is just a matter of configuration :)

Is this library the right choice for me?

Viselect primarily focuses on being a high-performant engine to select elements with various boundaries, behaviours and modes in your browser. Viselect is to "full-blown libraries" what is popper.js to tippy.js - the core of your feature / of another library.

Development

Use the following commands to work on this locally (we use lerna to manage this):

  • npm run dev - Spawns a dev-server for all packages. Every framework-dependend package is bundled with the vanilla version.
  • npm run build - Builds all packages in parallel.
  • npm run lint:fix - Lints and fixes all errors in all packages.

For the development servers vite is used. It's superb, you should give it a try. To bundle it we use rollup (which is btw also used by vite behind the scenes) to have full control over how the bundle looks like.

Releasing a new version

This project is managed via lerna. To bump the version and publish a new one run the following commands:

  • lerna version
  • lerna publish from-package

You want to contribute?

That's awesome! Check out the contribution guidelines to get started :)


Download Details:

Author: Simonwep
Source Code: https://github.com/Simonwep/selection 
License: MIT license

#elixir #data 

Selection: Elixir-Powered Data Selection Mastery
1.30 GEEK