All Projects → schne324 → Dragon Drop

schne324 / Dragon Drop

Licence: mit
Accessible drag and drop list reorder module

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Dragon Drop

tobii
An accessible, open-source lightbox with no dependencies
Stars: ✭ 132 (-65.71%)
Mutual labels:  a11y, accessible
Reakit
Toolkit for building accessible rich web apps with React
Stars: ✭ 5,265 (+1267.53%)
Mutual labels:  a11y, accessible
Chakra Ui
⚡️Simple, Modular & Accessible UI Components for your React Applications
Stars: ✭ 295 (-23.38%)
Mutual labels:  a11y, accessible
Bootstrap Vue
BootstrapVue provides one of the most comprehensive implementations of Bootstrap v4 for Vue.js. With extensive and automated WAI-ARIA accessibility markup.
Stars: ✭ 13,603 (+3433.25%)
Mutual labels:  a11y, accessible
Handorgel
Accessible W3C conform accordion written in ES6.
Stars: ✭ 239 (-37.92%)
Mutual labels:  a11y, accessible
Tobi
An accessible, open-source lightbox with no dependencies.
Stars: ✭ 195 (-49.35%)
Mutual labels:  a11y, accessible
A11y accordions
ES5 ARIA Accordion Component
Stars: ✭ 108 (-71.95%)
Mutual labels:  a11y, accessible
a11y-react-emoji
⚛️ An accessible Emoji component for React applications
Stars: ✭ 69 (-82.08%)
Mutual labels:  a11y, accessible
Chakra Ui
⚡️ Simple, Modular & Accessible UI Components for your React Applications
Stars: ✭ 22,745 (+5807.79%)
Mutual labels:  a11y, accessible
Angular Editor Fabric Js
Drag-and-drop editor based on Fabricjs for Angular.io
Stars: ✭ 295 (-23.38%)
Mutual labels:  drag-and-drop
A11y styled form controls
Various styled accessible form controls
Stars: ✭ 335 (-12.99%)
Mutual labels:  a11y
Pa11y Ci
Pa11y CI is a CI-centric accessibility test runner, built using Pa11y
Stars: ✭ 291 (-24.42%)
Mutual labels:  a11y
Brick Design
全场景流式布局,可视化拖拽、随意嵌套组合、实时渲染、实时辅助线展示,实时组件间距展示、实时拖拽排序、状态域管理,可视化属性配置、可视化样式配置、多设备适配展示,支持逻辑渲染、模板字符变量、表达式、自定义方法、自定义状态
Stars: ✭ 4,048 (+951.43%)
Mutual labels:  drag-and-drop
Vue Form Builder
Super Form Builder built on top of Vue with Drag & Drop functionality, savable-form-schema and easy to maintain/upgrade your form.
Stars: ✭ 292 (-24.16%)
Mutual labels:  drag-and-drop
Craft.js
🚀 A React Framework for building extensible drag and drop page editors
Stars: ✭ 4,512 (+1071.95%)
Mutual labels:  drag-and-drop
Air Light
WordPress starter theme - designed to be minimal, lightweight and easy for all kinds of WordPress projects. Public Roadmap: https://favro.com/organization/3b45e73eaf083f68fefef368/c1dd2d4a99d6723904d2e763
Stars: ✭ 285 (-25.97%)
Mutual labels:  accessible
A11y Courses
Courses offered in web accessibility (and other learning opportunities)
Stars: ✭ 288 (-25.19%)
Mutual labels:  a11y
React Uploady
Modern file uploading - components & hooks for React
Stars: ✭ 372 (-3.38%)
Mutual labels:  drag-and-drop
Cxjs
CxJS - Advanced JavaScript UI framework for admin and dashboard applications with ready to use grid, form and chart components.
Stars: ✭ 345 (-10.39%)
Mutual labels:  drag-and-drop
A11yproject.com
The A11Y Project is a community-driven effort to make digital accessibility easier.
Stars: ✭ 3,436 (+792.47%)
Mutual labels:  a11y

Dragon Drop

CircleCI

Keyboard/assistive technology accessible drag-and-drop reorder list.

Dragon Drop

Demo

http://schne324.github.io/dragon-drop/demo/

Enter the dragon...

For an in-depth look at dragon drop see the smashing magazine article on dragon drop

Installation

npm

$ npm install drag-on-drop

bower

$ bower install drag-on-drop

Usage

Browserify/Webpack

import DragonDrop from 'drag-on-drop';

const dragon = new DragonDrop(container, options);

In the browser

const DragonDrop = window.DragonDrop;
const dragon = new DragonDrop(container, options);

React

Although a DragonDrop react component doesn't exist (yet), it can be used with react:

class App extends Component {
  componentDidMount() {
    this.setState({
      dragonDrop: new DragonDrop(this.dragon)
    });
  }

  componentDidUpdate() {
    const { dragonDrop } = this.state;
    // this public method allows dragon drop to
    // reassess the updated items and handles
    dragonDrop.initElements(this.dragon);
  }

  render() {
    return (
      <ul className='dragon' ref={el => this.dragon = el}>
        <li>
          <button type='button' aria-label='Reorder' />
          <span>Item 1</span>
        </li>
        <li>
          <button type='button' aria-label='Reorder' />
          <span>Item 2</span>
        </li>
      </ul>
    );
  }
}

Full example

NOTE usage with react is not exactly ideal because DragonDrop uses normal DOM events not picked up by react (react doesn't know about the reordering).

CDN (unpkg)

https://unpkg.com/drag-on-drop

API

new DragonDrop(container, [options])

container HTMLElement|Array (required)

Either a single container element or an array of container elements.

options Object (optional)

item String

The selector for the drag items (qualified within container). Defaults to

'li'
handle String

The selector for the keyboard handle (qualified within the container and the selector provided for item). If set to false, the entire item will be used as the handle. Defaults to

'button'
activeClass String

The class to be added to the item being dragged. Defaults to

'dragon-active'
inactiveClass String

The class to be added to all of the other items when an item is being dragged. Defaults

'dragon-inactive'
nested Boolean

Set to true if nested lists are being used (click and keydown events will not bubble up (e.stopPropagation() will be applied)). For nested lists, you MUST pass DragonDrop an array of containers as the 1st parameter (see example below).

NOTE: there is a 99% chance that you'll need to use :scope selectors to target only a given list's items (because dragon drop would otherwise include the sub list's items for example). Using :scope selectors will allow you to target direct descendant children (example: :scope > li).

const lists = Array.from(document.querySelectorAll('.dragon-list'));
const dragons = new DragonDrop(lists, {
  nested: true,
  handle: false,
  item: ':scope > li' // IMPORTANT! a selector that targets only a single list's items
});
const [ topLevel, sublist1, sublist2 ] = dragons;

topLevel.on('grabbed', () => console.log('top-most container item grabbed'));
sublist1.on('grabbed', () => console.log('sublist 1 item grabbed'));
sublist2.on('grabbed', () => console.log('sublist 1 item grabbed'));
dragulaOptions Object

An options object passed through to dragula.

NOTE: dragulaOptions.moves will be ignored given a DragonDrop instance with nested: false and a truthy handle

NOTE: dragulaOptions.moves AND dragulaOptions.accepts will be ignored given a DragonDrop instance with nested: true

announcement Object

The live region announcement configuration object containing the following properties:

grabbed Function

The function called when an item is picked up. The currently grabbed element along with an array of all items are passed as arguments respectively. The function should return a string of text to be announced in the live region. Defaults to

el => `Item ${el.innerText} grabbed`
dropped Function

The function called when an item is dropped. The newly dropped item along with an array of all items are passed as arguments respectively. The function should return a string of text to be announced in the live region. Defaults to

el => `Item ${el.innerText} dropped`
reorder Function

The function called when the list has been reordered. The newly dropped item along with an array of items are passed as arguments respectively. The function should return a string of text to be announced in the live region. Defaults to

(el, items) => {
  const pos = items.indexOf(el) + 1;
  const text = el.innerText;
  return `The list has been reordered, ${text} is now item ${pos} of ${items.length}`;
}
cancel Function

The function called when the reorder is cancelled (via ESC). No arguments passed in. Defaults to

() => 'Reordering cancelled'

Properties

const dragonDrop = new DragonDrop(container);

dragonDrop.items Array

An array of each of the sortable item element references.

dragonDrop.handles Array

An array of each of the handle item element references. If instance doesn't have handles, this will be identical to dragonDrop.items.

dragonDrop.dragula

A direct handle on the dragula instance created by dragonDrop

Example with options

const list = document.getElementById('dragon-list');
const dragonDrop = new DragonDrop(list, {
  item: 'li',
  handle: '.handle',
  announcement: {
    grabbed: el => `The dragon has grabbed ${el.innerText}`,
    dropped: el => `The dragon has dropped ${el.innerText}`,
    reorder: (el, items) => {
      const pos = items.indexOf(el) + 1;
      const text = el.innerText;
      return `The dragon's list has been reordered, ${text} is now item ${pos} of ${items.length}`;
    },
    cancel: 'The dragon cancelled the reorder'
  }
});

Events

Dragon drop emit events when important stuff happens.

dragonDrop.on('grabbed', callback)

Fires when an item is grabbed (with keyboard or mouse). The callback is passed the container along with the grabbed item.

dragonDrop.on('dropped', callback)

Fires when an item is dropped (with keyboard or mouse). The callback is passed the container and the grabbed item.

dragonDrop.on('reorder', callback)

Fires when an list is reordered. The callback is passed the container along with the item.

Example use of events

dragonDrop
  .on('grabbed', (container, item) => console.log(`Item ${item.innerText} grabbed`))
  .on('dropped', (container, item) => console.log(`Item ${item.innerText} dropped`))
  .on('reorder', (container, item) => console.log(`Reorder: ${item.innerText} has moved`))
  .on('cancel', () => console.log('Reordering cancelled'));

NOTE for mouse drag/drop event hooks the dragula property is exposed for dragula's events

dragonDrop.dragula.on('drop', ...);

Methods

dragonDrop.initElements(container)

Reinitialises the list, so that newly added items can be dragged. You can do this automatically with a MutationObserver:

const observer = new MutationObserver(() => dragonDrop.initElements(container));
observer.observe(container, {childList: true});

Debugging

Set the following localStorage option to debug dragonDrop

localStorage.debug = 'drag-on-drop:*';

Notes on accessibility

There are certain things that are left up to the discretion of the implementer. This is to keep DragonDrop less opinionated on some of the gray areas of a11y. The demos show a few different approaches on associating help text with DragonDrop:

  1. Recommended aria-describedby on each control (item, or if applicable, handle). This is the safest/test approach because it guarantees that AT users will receive the instructions. Demo of this approach
  2. Recommended aria-labelledby on the container/list element. With this approach, where supported, will announce the instructions whenever the AT users enters the list (which is less verbose than the above). Demo of this approach
  3. Not recommendedaria-describedby on the container/list element. This approach, where supported, will only announce the instructions if the screen reader user traverses to the actual list element. Demo of this approach

For more information regarding accessibility you can read an accessibility review of dragon drop initiated by Drupal.

Thanks!

A special thanks to Aaron Pearlman for the logo.

Another special thanks to contributors/maintainers of dragula which is used for all of the mouse behavior/interaction for dragon drop!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].