Skip to content

feat: add custom document ordering to collections#1305

Open
stevenle wants to merge 4 commits into
mainfrom
claude/manual-collection-sorting-szccnj
Open

feat: add custom document ordering to collections#1305
stevenle wants to merge 4 commits into
mainfrom
claude/manual-collection-sorting-szccnj

Conversation

@stevenle

@stevenle stevenle commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

Adds support for custom document ordering in collections via a new customSorting collection schema option. Editors can now drag documents to reorder them or use "Move to top/bottom" actions, with the order persisted as a fractional-index string in sys.sortKey.

Key Changes

  • Fractional indexing library (packages/root-cms/shared/sort-key.ts): Vendored implementation from rocicorp/fractional-indexing for generating sort keys that maintain order through insertions without rewriting existing keys.

  • CMS UI enhancements:

    • Added "Custom order" sort option to collection pages when customSorting is enabled
    • Implemented drag-and-drop reordering using @hello-pangea/dnd with visual drag handles
    • Added "Assign positions" banner for bulk-initializing sort keys on existing docs (e.g., when enabling custom sorting on a collection with existing docs)
    • Added "Move to top/bottom" actions in the doc actions menu
    • Compact and comfortable list views both support reordering
  • Backend integration:

    • cmsSetDocSortKey(): Updates a single doc's sort key across draft/published/scheduled copies
    • cmsAssignSortKeys(): Batch-assigns sort keys with Firestore batch write chunking (500 writes per batch)
    • fetchMaxSortKey(): Queries the current maximum sort key in a collection
    • Sort key updates intentionally skip sys.modifiedAt to avoid marking docs as edited
  • Client API (RootCMSClient.listDocs()): Automatically orders results by sys.sortKey when customSorting is enabled and no explicit orderBy is provided.

  • Sorting utilities (doc-sort.ts): sortDocsCustomOrder() function that sorts docs by sort key (keyed docs first) with keyless docs at the end (newest first) for visibility.

  • Schema updates: Added customSorting?: boolean option to the Collection type and serialized it in the app context.

Implementation Details

  • Sort keys are base-62 strings that sort correctly by code point, matching Firestore's string field ordering
  • Reordering is non-destructive: moving a doc only writes a new key to that doc, not to neighbors
  • Published/scheduled doc copies are updated best-effort; failures don't block the operation
  • Keyless docs (created before custom sorting was enabled or via imports) are displayed at the end with a banner offering one-click position assignment
  • Comprehensive test coverage for fractional indexing, sort key assignment, and UI rendering

https://claude.ai/code/session_018Xgdd2x1BnTarkWKftet4q

claude added 2 commits July 15, 2026 00:16
Adds a `manualSorting` collection option that lets editors manually
order docs in a collection (e.g. storefronts) with the order preserved
when listing from the API.

- Order is stored as a fractional-index string at `sys.sortKey` on both
  the Drafts and Published copies, so a reorder is a single-doc write
  and applies to live listings immediately without publishing (and
  without marking docs as edited).
- The CMS gets a "Manual order" sort with drag-and-drop rows (both
  densities, keyboard accessible) plus "Move to top/bottom" actions,
  and an "Assign positions" banner that initializes keys for docs
  created before the option was enabled (or by import scripts).
- `listDocs()` defaults to manual order for opted-in collections when
  no `orderBy`/`query` is passed; `orderBy: 'sys.sortKey'` also works
  explicitly. New docs are appended to the end of the manual order.
- The fractional-indexing helpers (`generateKeyBetween`,
  `generateNKeysBetween`, `generateKeyAfter`) are vendored from
  rocicorp/fractional-indexing (MIT) and exported for import scripts.

Closes #1303

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Xgdd2x1BnTarkWKftet4q
Renames the collection option (and the derived identifiers, sort value,
and "Custom order" UI label) from "manual" to "custom" naming. The
stored field remains `sys.sortKey`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Xgdd2x1BnTarkWKftet4q
@stevenle stevenle changed the title Add manual document ordering to collections feat: add custom document ordering to collections Jul 15, 2026
@stevenle

Copy link
Copy Markdown
Member Author

@jeremydw fable kinda nailed this impl pretty well. i just tested and everything looks pretty good to me. for newly added customSorting: true collections, there's a user-friendly message bar at the top of the collection that indicates the collection needs "sortKey" added to the docs and a button to auto assign those values. (that's the only 1-time setup step)

Screenshot 2026-07-15 at 7 38 00 AM Screenshot 2026-07-15 at 7 38 04 AM

lmk if any feedback.

@jeremydw

Copy link
Copy Markdown
Member

Wow, that's great. Two comments:

  1. I wonder if we should have some tooltip or something that explains what "custom order" does?
  2. How does the ordering play with draft/published states? What if you want to reorder something and leave that in draft for review first.

@stevenle

Copy link
Copy Markdown
Member Author

i don't think a tooltip is necessary, it's opt-in at the moment so devs would need to know it exists and would probably have a pre-existing defined purpose for it, hopefully it's clear enough from that.

re: draft/published, there's a comment about it in the PR desc but basically it currently changes the position in draft mode but doesn't change the modifiedAt time (so the "draft" badge wouldn't appear). i think ai did this to avoid having it show the draft badge, but rethinking this now and it'd probably make sense to show the draft badge as an indication that it requires republishing.

Reordering docs now updates sys.modifiedAt/modifiedBy on the draft doc
(like any other edit), so docs show as having unpublished changes and
must be published for the new order to take effect on live listings.
The published/scheduled copies are no longer written directly on
reorder; the sort key propagates on publish, which copies the draft
sys wholesale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Xgdd2x1BnTarkWKftet4q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants