FForm Platform
enzh-CN

Designer pointer and touch interactions

Use mouse, touch and pen input safely when inserting, sorting and moving components in the visual Designer.

Pointer and touch interactions in the form designer

Purpose

The form designer supports both direct Pointer Events dragging and an explicit tap-to-insert fallback without changing the persisted form schema.

  • Mouse, touch, and pen users can drag a palette item or an existing component to an insertion target.
  • Keyboard and touch users can select a palette item, then activate an **Add here** target.
  • The same targets and mutation rules are used for the root list, nested containers, panes, and table cells.

Architecture and data flow

`designerPointerDrag.js` owns the single active pointer session. A session records the pointer ID, source kind, cloned component snapshot, start point, current target, and display label. It activates only after an 8-pixel movement threshold, which keeps a normal tap distinct from a drag.

`ComponentPanel.vue`, `DesignArea.vue`, `FormRenderer.vue`, and `FormTable.vue` create sessions for these source kinds:

  • `palette`: create a new component;
  • `component`: move an existing root or nested component while preserving its ID;
  • `table`: move the component from an occupied Form Table cell.

`DesignerTapInsertTarget.vue` supplies one target contract through DOM data attributes. During pointer dragging it renders as a compact insertion bar or table-cell target. During tap insertion it remains a localized, keyboard-accessible button. `DesignArea.vue` owns validation and performs the accepted mutation atomically against the Pinia form tree.

The manager uses `document.elementsFromPoint()` for hit testing, displays a non-interactive drag preview, highlights the accepted target, and scrolls the nearest scrollable ancestor when the pointer approaches an edge. Pointer cancellation, browser-window blur, or Escape ends the session without changing the form. A completed drag suppresses its synthetic follow-up click so it cannot accidentally enter tap insertion mode.

Supported sources and targets

SourceRoot listEditable container or paneEmpty Form Table cell
Palette componentCreateCreateCreate when its type is table-compatible
Root or nested componentMove/reorderMove/reorderMove when its type is table-compatible
Form Table cell componentMove outMove outMove between empty compatible cells

Insertion positions are available before every root or nested component and at the end of each list. Editable destinations include DIV, Form, Form Group, placeholder Custom Block, active Tabs/Pagination panes, and individual Grid Layout panes.

Loaded Custom Block sources remain read-only. An occupied table cell is never overwritten. A container cannot be dropped into itself or any of its descendants, including descendants reached through panes or table cells. A rejected target is not highlighted and releasing over it leaves the source unchanged.

Tap and keyboard fallback

Selecting a palette item by tap, click, Enter, or Space enters one-shot insertion mode. Activating an **Add here** target inserts one component and immediately consumes the pending item. Selecting the same palette item again cancels the mode. Starting a pointer drag also cancels pending tap insertion.

This transient selection is held by `formDesigner.js`; it is not exported or saved in the form schema.

Compatibility and security boundaries

  • The implementation uses Pointer Events instead of browser `DataTransfer`, so one path covers mouse, touch, and pen input.
  • Component IDs are preserved when existing components move; palette components receive the same generated IDs and defaults as before.
  • Viewer rendering, form JSON, server APIs, permissions, and database schema are unchanged.
  • The manager accepts only targets rendered by the designer and revalidates the target against the current component tree immediately before mutation.
  • Table type restrictions, occupied-cell protection, and cyclic-container protection fail closed.
  • No pointer coordinates or form contents are sent to a server.

Manual verification

After rebuilding the client, verify with a mouse and on a real touch device or browser touch emulation:

1. Tap and keyboard-select a palette item, insert it at root/container/pane/table targets, and cancel the mode.

2. Drag palette items to the beginning, middle, and end of root and nested lists.

3. Reorder the first item to the end and the last item to the beginning.

4. Move components between root, nested containers, Tabs/Pagination/Grid panes, and empty compatible table cells.

5. Move a table component to another empty cell and out to a list; confirm its ID is preserved.

6. Confirm occupied cells, unsupported table component types, self-drops, and descendant-drops are rejected without removing the source.

7. Hold a drag near each scrollable edge and confirm scrolling continues while target highlighting follows the content.

8. Cancel with Escape, a system pointer cancellation, and by leaving/focusing away from the window; confirm the form is unchanged.

9. Confirm a short tap still selects a palette item and a completed drag does not trigger an extra click.