Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/content-settings-sortable-sections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@emdash-cms/admin": minor
---

Adds accessible drag handles for reordering the built-in content settings sections and stores each editor's preferred order per collection in the browser.
27 changes: 25 additions & 2 deletions packages/admin/src/components/ContentEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,7 @@ function MobileSidebarPortalGuard() {
const nestedOverlaySelector =
'[role="dialog"], [role="listbox"], [role="menu"], .kumo-tooltip-popup';
const keepSheetOpen = () => queueMicrotask(() => setOpenMobile(true));
const reopenSheetAfterDismiss = () => setTimeout(setOpenMobile, 0, true);
const promotePortal = (element: Element) => {
const overlay =
element.closest(nestedOverlaySelector) ?? element.querySelector(nestedOverlaySelector);
Expand All @@ -964,7 +965,19 @@ function MobileSidebarPortalGuard() {
const handleFocusOut = (event: FocusEvent) => {
const source = event.target;
const destination = event.relatedTarget;
if (!(source instanceof Element) || !(destination instanceof Element)) return;
if (!(source instanceof Element)) return;

// dnd-kit briefly blurs and then restores the activator after a
// pointer drop. Kumo interprets the null relatedTarget as leaving the
// sheet and closes it before focus is restored. Keep this transient
// sortable-handle blur inside the mobile settings interaction.
if (source.closest("[data-sortable-handle]") && destination === null) {
event.stopPropagation();
keepSheetOpen();
return;
}

if (!(destination instanceof Element)) return;

const sheet = source.closest('nav[data-sidebar="sidebar"][data-mobile="true"]');
if (!sheet || sheet.contains(destination)) return;
Expand All @@ -977,7 +990,17 @@ function MobileSidebarPortalGuard() {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key !== "Escape") return;
const target = event.target;
if (!(target instanceof Element) || !target.closest(nestedOverlaySelector)) return;
if (!(target instanceof Element)) return;

// Escape cancels a keyboard drag, but Kumo also treats it as a request
// to dismiss the mobile sheet. Let dnd-kit receive the key while
// restoring the sheet after its dismissal handler runs.
if (target.closest('[data-sortable-handle][data-sorting="true"]')) {
reopenSheetAfterDismiss();
return;
}

if (!target.closest(nestedOverlaySelector)) return;
keepSheetOpen();
};

Expand Down
Loading
Loading