Problem/Motivation

The only way to add a component, a block or a preset from the library is to drag it onto the canvas. That is slow on a long page, hard on a small screen, and fails WCAG 2.2 2.5.7 Dragging Movements, which asks for a single-pointer alternative.

Proposed resolution

A click on a library item inserts it, as Drupal Canvas, Gutenberg, Webflow and GrapesJS do. Enter or Space on a focused item does the same. Dragging still works.

  • With an element selected, the item lands right after it, in the same slot.
  • With nothing selected, the item lands at the end of the display.
  • The new element opens in the settings sidebar, the next step after adding it. Selected, it also keeps a series of clicks in order. A preset opens nothing: it is a ready-made arrangement, not an element to set up.
  • When the slot of the selection is full, the item lands right after the component owning that slot, one level up, and so on up to the root. Drag never sends a drop into a full dropzone, and the click must not either: the server answers it with the generic "refresh the page" error.
  • When no dropzone has room, the root included, a click does nothing: the row shows no "+" and the grab hand before the click, since a drag may still find room, and the click is announced as "There is no room left here."

A plain text row does not say that a click inserts it, nor where. So:

  • Signifier: a pointer cursor instead of the grab hand, and a "+" at the end of the row under the pointer or the keyboard focus.
  • Feedforward: a line on the Canvas where the item will land, as the Gutenberg inserter draws on hover.
  • Feedback: the new element is scrolled into view, and announced with Drupal.announce().

A first draft offered a ⋮ menu per item with four options: at the beginning, at the end, before and after the selection. It was dropped: four lines of text for one click, and a second Tab stop on every library row. "Before" and "at the beginning" are an insert plus a move, which #3624637: Plan: Builder accessibility and UX plans without dragging.

Implementation:

  • Library items get role="button", so a row stays one Tab stop.
  • The click posts to the existing api_root_attach or api_slot_attach route, with the payload and position a drop sends. The server cannot tell it from a drop.
  • The selection is resolved with the visible-only lookup of keyboard.js, moved to a shared helper, so a stale copy in a hidden panel is never the target.
  • A locked dropzone, full for now, is detected with the check the drag already uses, isDropzoneLocked() of dropzone.js, shared rather than copied. Locks still to come, expected or a lock panel, will hold for the click too.

Remaining tasks

  • Review.

User interface changes

A click on a library item inserts it. A pointer cursor and a "+" on the row, and a line on the Canvas, show it before the click. The new element opens in the settings sidebar, except a preset.

API changes

None. The click uses the existing attach routes.

Data model changes

None.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mogtofu33 created an issue. See original summary.

mogtofu33’s picture

Issue summary: View changes

mogtofu33’s picture

Issue summary: View changes

mogtofu33 changed the visibility of the branch 1.0.x to hidden.

  • mogtofu33 committed 4fff0a3e on 1.0.x
    feat: #3628802 Insert from the library without dragging
    
    By: mogtofu33
    
mogtofu33’s picture

Assigned: mogtofu33 » Unassigned
Status: Needs work » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.