Skip to content

feat(list): drag and drop reordering (#DS-4454) - #1911

Open
lskramarov wants to merge 5 commits into
mainfrom
feat/DS-4454
Open

feat(list): drag and drop reordering (#DS-4454)#1911
lskramarov wants to merge 5 commits into
mainfrom
feat/DS-4454

Conversation

@lskramarov

Copy link
Copy Markdown
Contributor

Options of kbq-list-selection can be reordered by dragging or with Alt + arrow keys once draggable is set, and moved between lists connected via connectedTo.

The list never mutates the projected data: it reports the move through the dropped event and the consumer applies it. The new position is announced to assistive tech only once that move has actually been applied, and aria-keyshortcuts advertises the keyboard alternative to dragging.

Dragging inside kbq-optgroup or cdk-virtual-scroll-viewport reports indices that do not address the backing array, so both now warn in development mode.

Options of `kbq-list-selection` can be reordered by dragging or with `Alt` + arrow
keys once `draggable` is set, and moved between lists connected via `connectedTo`.

The list never mutates the projected data: it reports the move through the
`dropped` event and the consumer applies it. The new position is announced to
assistive tech only once that move has actually been applied, and
`aria-keyshortcuts` advertises the keyboard alternative to dragging.

Dragging inside `kbq-optgroup` or `cdk-virtual-scroll-viewport` reports indices
that do not address the backing array, so both now warn in development mode.
@lskramarov lskramarov self-assigned this Aug 17, 2026
Copilot AI lite review requested due to automatic review settings August 17, 2026 18:33
@lskramarov lskramarov added the enhancement New feature or request label Aug 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in drag-and-drop (and keyboard) reordering/transfer for kbq-list-selection, including accessible announcements and documentation/examples, while keeping the list “data-immutable” (consumer applies reorders based on a dropped event).

Changes:

  • Introduces draggable, connectedTo, and dropped on KbqListSelection, plus keyboard reordering/transfer via Alt + arrow keys and live-region announcements.
  • Adds styling for drag preview/placeholder/animations and updates a11y locale strings to support move announcements.
  • Adds docs examples, dev-app demos, and unit/e2e coverage for drag/drop + keyboard flows.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/public_api_guard/components/list.api.md Public API snapshot updated for new list drag/drop inputs/outputs and types.
tools/public_api_guard/components/core.api.md Public API snapshot updated for new a11y locale key (listOptionMoved).
packages/e2e/routes.ts Registers new list drag-and-drop E2E route/component.
packages/docs-examples/example-module.ts Adds new live examples entries for draggable list variants.
packages/docs-examples/components/list/list-draggable/list-draggable-example.ts New docs example: in-list reordering via dropped.
packages/docs-examples/components/list/list-draggable-connected/list-draggable-connected-example.ts New docs example: transferring options between connected lists.
packages/docs-examples/components/list/index.ts Exports and registers the new list draggable examples module-side.
packages/components/list/list.scss Adds drag/drop interaction styling (placeholder, preview, cursor, transitions).
packages/components/list/list.ru.md Documents draggable lists + keyboard shortcuts (RU).
packages/components/list/list.en.md Documents draggable lists + keyboard shortcuts (EN).
packages/components/list/list-tokens.scss Adds tokens for drag preview surface/shadow; ensures preview inherits tokens in <body>.
packages/components/list/list-selection.component.ts Core implementation: CDK drag/drop wiring, keyboard move/transfer, dropped event, live-region announcements, dev warnings.
packages/components/list/list-selection.component.spec.ts Unit + axe coverage for drag/drop opt-in, keyboard behavior, announcements, and shortcuts.
packages/components/list/e2e.ts Adds E2E fixture component with two connected draggable lists and consumer-applied reorder/transfer.
packages/components/list/e2e.playwright-spec.ts Adds Playwright coverage for pointer drag, keyboard reorder/transfer, and transition settling behavior.
packages/components/core/locales/types.ts Extends KbqA11yLocaleConfiguration with listOptionMoved announcement template.
packages/components/core/locales/tk-TM.ts Adds listOptionMoved locale string (tk-TM).
packages/components/core/locales/ru-RU.ts Adds listOptionMoved locale string (ru-RU).
packages/components/core/locales/pt-BR.ts Adds listOptionMoved locale string (pt-BR).
packages/components/core/locales/es-LA.ts Adds listOptionMoved locale string (es-LA).
packages/components/core/locales/en-US.ts Adds listOptionMoved locale string (en-US).
packages/components-dev/list/template.html Adds dev-app demos for draggable reorder and connected transfer.
packages/components-dev/list/module.ts Wires demo data + handlers for the new draggable behaviors in dev app.
Suppressed comments (1)

packages/components/list/list-selection.component.ts:814

  • To ensure unsupported-container warnings are emitted when draggable becomes enabled after initial render (e.g. dynamic bindings), call warnOnUnsupportedDragContainer() from syncDraggableState(), which already runs when draggable/disabled change.
    /** Keeps the underlying CDK directives in sync with the resolved `draggable` state. */
    private syncDraggableState(): void {
        this.dropList.disabled = !this.draggable;
        this.options?.forEach((option) => option.syncDraggableState());
        this.changeDetectorRef.markForCheck();
    }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/components/list/list-selection.component.ts
Comment thread packages/components/list/list-selection.component.ts
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit c09a563):

https://koobiq-next--prs-1911-o55jwo31.web.app

(expires Fri, 21 Aug 2026 15:30:15 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c

@lskramarov
lskramarov marked this pull request as draft August 18, 2026 08:19
`KbqListOptionDragData` is only read inside the component file, so it no longer
leaves the package and no longer widens the public API surface.

`warnOnUnsupportedDragContainer()` now also runs from `syncDraggableState()`, so
enabling `draggable` after the initial render still warns. It tolerates being
called before the content children exist and reports each list only once.
Dragging an option no longer opens a gap. The surrounding options stay where they
are, the dragged one keeps its slot as a faded row, and a line with a dot marks the
position the option would land in — in the connected list too, and along the main
axis of a horizontal list.

CDK's sorting is switched off to keep the list still, which also makes the index it
reports on drop unusable, so the target is derived from the pointer instead. The
dragged option is left out of that measurement, which makes the resulting gap index
the very index `moveItemInArray` and `transferArrayItem` expect.

Three CDK behaviours had to be handled: `enter()` ignores `sortingDisabled` and
pushes the placeholder into whichever list is hovered, so a foreign placeholder is
hidden; `hasAnchor` keeps the faded row in the origin list during a transfer; and
the drop animation is dropped, because it would fly the preview back to the slot the
drag started from and read as a rejected drop.
The indicator is a plain line now. Removes the cap pseudo-element in both
orientations and the size token that only fed it.
@lskramarov
lskramarov marked this pull request as ready for review August 18, 2026 11:41
event.preventDefault();
}

if (this.draggable && isAltOnly(event) && [UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW].includes(keyCode)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не работает на MacOS - происходит скролл страницы

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В гайде не было ничего про шорткаты, альт и стрелки — это опасные шорткаты, не рекомендую.

Альтернатива не продумана и пока никому не требовалась. Будет что-то более сложное. Или даже вход в режим перемещения по шорткатам.

Потенциально список может быть связан с инпутом как комбобокс, традиционно шорткаты с Alt должны управлять позицией каретки в ипнуте.

В Сафари вот вообще нельзя перезаписывать «быстрый скролл» по ALT+стрелки

image

…e-flows

An overlay opened from inside a toast — a select panel, a dropdown menu — stayed
where it was when another toast was dismissed, while its trigger slid up with the
rest of the stack.

Toasts are ordinary flow children, so a removal moves the others for as long as the
leaving toast animates its height away, and CDK re-measures an overlay's origin only
on a scroll or a viewport resize. The container already bridged that gap by faking a
scroll event, but it only did so on the animation's start and done events: the panel
was adrift for the 300ms in between, and a template toast, which carries no animation
at all, never reported anything.

The container now watches its own box instead, which tracks the reflow exactly, and
merges that into `elementScrolled()` so `ScrollDispatcher` re-broadcasts it without a
synthetic DOM event. `dispatchScrollEvent` is left in place, deprecated, because it is
part of the public surface.
@rmnturov

Copy link
Copy Markdown
Contributor

При сортировке перетаскванием нужно запретить ховер-эффекты на элементах списка. Тащить и видишь только полоску — позицию вставки и превью. Фон элементов списка под курсором не подсвечивается.

Реагировать должны только элементы, в которые можно дропнуть и что-то будет. В данном примере дропнуть можно только в список или в соседний список. Подстветка может быть особенная, если дроп запрещен.

(Возможно, что вообще ховеры в приложении надо отключать. Для тех жлементов на которые дроп не влиет. Но это я не уверен, можно обсудить)

@rmnturov

Copy link
Copy Markdown
Contributor

Вот еще для обсуждения.

Полоска-индикатор должна показывает только позицию, которая изменит порядок элементов.

Если перетаскиваемый элемент остаётся на прежнем месте, полоску не показываем.

Пример: перетаскиваем элемент №2.

перед №1 — показываем;
между №1 и №2 — не показываем;
между №2 и №3 — не показываем;
между №3 и №4 — показываем;
после №4 — показываем.

Итого: если после отпускания порядок элементов не изменится, полоски быть не должно.

====

Так не везде, но я нашел такую реализацию https://atlassian.design/components/pragmatic-drag-and-drop/examples#list

@rmnturov

Copy link
Copy Markdown
Contributor

@rmnturov

rmnturov commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Нужна возможность запретить перетаскивание (смену порядка) для элемента. Не disabled, а просто обычное состояние, но именно перетаскивать нельзя. Такая возможность есть?

@rmnturov

Copy link
Copy Markdown
Contributor

Предлагаю доработать пример с драг сортом. Добавить туда прокрутку, заголовки, разделители, элементы, которые нельзя таскать. Курсор pointer надо сделать также

https://www.figma.com/design/8dBFabYg2hAuCH1muiUM3z/Tree-Drag-Sort?node-id=463-6655&t=1hRROXvCAaLBsxrk-4

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants