Tabledrag

The tables whose rows are dragged into order, in the shapes core and contrib build them: a flat list ordered by weight, several tables in one form, a row for adding at the foot, a row of widgets, a multiple value field, a table grouped into regions, and the child order outline of a book. Each names the file it is rebuilt from, and most are shown a second time as a drag leaves them, with the changed mark in each moved row and the warning above the table. Nothing is saved.

A flat list ordered by weight

The simplest table tabledrag is put on: one order group, a sibling relationship, and a label and a weight in each row. The weight column is hidden while the handles are shown, and the "Show row weights" button above the table swaps the one for the other. The text format form gives the table a #title through the form_element wrapper, so it has a label like any field. Once a row has moved, the asterisk goes in the first cell, straight after the handle -- the place the patch for issue 3084910 puts it, where core appends it to the end of the cell -- and the warning above the table opens with the same asterisk.

Limit allowed HTML tags and correct faulty HTML
Convert line breaks into HTML (i.e. <br> and <p>)
Convert URLs into links
Align images
Caption images
Correct faulty and chopped off HTML

After a drag

Limit allowed HTML tags and correct faulty HTML
Convert line breaks into HTML (i.e. <br> and <p>)
Convert URLs into links
Align images
Caption images
Correct faulty and chopped off HTML
  • core/modules/filter/src/FilterFormatFormBase.php — the filter processing order of a text format
  • web/modules/contrib/linkit/src/Form/Matcher/OverviewForm.php — the matchers of a Linkit profile
  • web/modules/contrib/facets/src/FacetListBuilder.php — the facets of a facet source

Several tables in one form

Search API orders its processors once per stage, and builds a table for each stage in a fieldset of its own, every table with its own order group. Each is a tabledrag of its own, so each has its own "Show row weights" button -- and the button is a toggle of the whole page, stored in the browser, so pressing one flips them all. A changed table gets its warning, and the others stay as they were.

Preprocess index
HTML filter
Ignore case
Tokenizer
Stop words
Preprocess query
Ignore case
Tokenizer
Postprocess query
Highlight
  • web/modules/contrib/search_api/src/Form/IndexProcessorsForm.php — the processor order of a search index

A row for adding at the foot

An image style lists its effects with a summary after each label, and ends with a row that is not an effect at all: the select of a new one and the button that adds it, in the same column as the labels and draggable like every other row, so the new effect can be given its place before it is added. That row is the widest thing in the first column, and it is the one with the handle and the mark in front of a form control.

Effect Weight Operations
Scale 480×480 (upscaling allowed)
Crop 320×240
Desaturate

After a drag

Effect Weight Operations
Scale 480×480 (upscaling allowed)
Crop 320×240
Desaturate
  • core/modules/image/src/Form/ImageStyleEditForm.php — the effects of an image style
  • web/modules/contrib/field_validation/src/Form/FieldValidationRuleSetEditForm.php — the rules of a validation rule set, built on the same pattern

A row of widgets

Every cell but the weight is a form control: a text field, a select, another text field, a text area, and a button that removes the row. The first of them shares its cell with the handle, and once the row has moved with the asterisk as well, so a field that fills its cell would push past the edge of it by the width of both -- which is why the theme leaves room for them. Rows of different heights test where the handle sits in a row taller than itself.

  • web/modules/contrib/manage_top_bar/src/Form/PageActionsForm.php — the featured actions of the top bar
  • web/modules/contrib/office_hours/src/Plugin/Field/FieldWidget/OfficeHoursExceptionsWidget.php — the exception days of an office hours field
  • web/modules/contrib/google_tag/src/Form/TagContainerForm.php — the events of a tag container

A multiple value field

The table every field with more than one value renders, built by a preprocess function rather than by the widget: the handle gets a cell of its own, field-multiple-drag, with nothing else in it, the widget is in the second, and the weight is in a last column headed Order. So this is the one table in which the asterisk has a cell to itself and the handle, and the cell has to be wide enough for both without the widget moving over when a row is dropped. The label of the field is a heading in the header row, spanning the first two cells.

  • core/lib/Drupal/Core/Field/FieldPreprocess.php — preprocessFieldMultipleValueForm(), which builds the table
  • core/lib/Drupal/Core/Field/WidgetBase.php — formMultipleElements(), which builds what goes in it
  • web/modules/contrib/social_media_links/modules/social_media_links_field/src/Plugin/Field/FieldType/SocialMediaLinksFieldItem.php — a field that orders its platforms in a table of its own instead

A table grouped into regions

The block layout: for every region a title row with the button that places a block in it, a message row that is there whether the region is empty or not and is hidden by a class when it is filled, and the blocks under it. The region of a block is a visible select, and both the region and the weight carry a subgroup class per region, so a row is ordered among the rows of its own region. The script of the real page, which moves a row when its select is changed and swaps those classes when it is dropped, is not attached: it works on the table with the id "blocks" and on every region select of the page, and would reach into both tables here.

Block Category Region Weight Operations
Header
No blocks in this region
Site branding System
Main navigation Menus
Content
No blocks in this region
Messages System
Page title Core
Main page content System
Sidebar
No blocks in this region

After a drag

Block Category Region Weight Operations
Header
No blocks in this region
Site branding System
Main navigation Menus
Content
No blocks in this region
Messages System
Page title Core
Main page content System
Sidebar
No blocks in this region
  • core/modules/block/src/BlockListBuilder.php — the block layout of admin/structure/block
  • core/modules/field_ui/src/Form/EntityDisplayFormBase.php — the manage display form, regions of fields rather than of blocks -- shown on the hierarchical tables example

A tree: the child order outline of a book

The "Child pages" outline of a book, at node/{id}/outline/children, which is a tree in the other sense: a parent relationship as well as an order, with the field of the parent hidden and the depth held in the indentation in the first cell. The first cell is also where the title is edited, so a page is renamed and moved in one submission, and the cell holds indentation, the handle, the asterisk once the row has moved, and a text field, in that order. A row dragged under another takes on its indentation and is marked, and so is every row it carries with it. The third column, "Hidden Book Information", holds the hidden fields of the row and is hidden itself.

Title Weight Hidden Book Information Operations
 
 
 
 
 
 
 
 
 
 
 

After a drag

Title Weight Hidden Book Information Operations
 
 
 
 
 
 
 
 
 
 
 
  • web/modules/contrib/book/src/Form/BookAdminEditForm.php — bookAdminTable() and bookAdminTableTree(), the child order outline
  • core/modules/menu_ui/src/MenuForm.php — the same tree for the links of a menu -- shown on the menu and taxonomy overview example
  • core/modules/taxonomy/src/Form/OverviewTerms.php — and for the terms of a vocabulary
  • web/modules/contrib/entity_hierarchy/src/Form/HierarchyChildrenForm.php — the children of an entity in a hierarchy, flat rather than a tree
  • web/modules/contrib/webform/modules/webform_ui/src/WebformUiEntityElementsForm.php — the elements of a webform, a tree whose leaves are fields