Repository navigation
Conversation
Port of the from-scratch rebuild (platform-planning#1161) onto current main. Supersedes #238. Snapshots refreshed for the Base UI upgrade. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ported from #238, with the group change-set nested under changeSets so a member named isEmpty can't overwrite the rolled-up flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt can't show through Demo: right-align the toolbar, match Bulk add to the combobox height, drop hover-expand on the product column. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Demo: add a 1,000-line dataset toggle to the PO page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
useLineItems takes selectionActions (DataTable's SelectionAction shape); LineItems.Table shows the shared bar at its foot while lines are selected. The bar view and pending-action runner move to a shared selection-actions module that DataTable now uses too, unchanged. Removes the unshipped LineItems.SelectionBar and BulkActions parts and exposes removeLines for bulk handlers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also fixes the docs, which listed maxBodyHeight as maxHeight with a stale 480 default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sticky header and totals row cast a soft shadow over the lines while more are hidden beyond them, and sit flush at the start and end of the list. The totals row also gets a top rule; the last line drops its bottom border so the two never double up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Controlled lines: an echo of the hook's own onLinesChange no longer re-seeds and wipes the baseline, so adds/removes stay in the change-set. - Tab steps across editable cells only and releases focus at the grid's edges instead of trapping it; focus retries for rows just scrolled in. - Cells mirror the store until the user types, so a paste into the focused cell isn't reverted on blur, and Enter/Tab commit once. - Typing in flight is committed when its row unmounts or turns read-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- No injected <style> or inline style objects: scrollbars and fullscreen use utility classes, the DirtyBar shake uses the Web Animations API. lineItemsFloatingBarStyles is removed from the public API. - English and Japanese labels via defineI18nLabels for both components. - Every cell editor is named by column and line; custom editors get ariaLabel. Row counts/indexes include header and totals rows. - Fullscreen is a labelled modal dialog with a focus wrap, scroll lock and scoped Escape, and no longer remounts the document when toggled. - DirtyBar: count announced, blocked navigation announced and focused, warnOnNav scoped to its own document/card and ignores modified clicks. - Keyboard fill-down (Cmd/Ctrl+D), keyboard reorder announcements, reduced-motion support, O(1) selection checks. - Exports aligned between folder and package root; types referenced by public types are exported. Shared selection bar moved to internals/. - BulkItemPicker: className, ReactNode ctaLabel, cleaner row names. - Docs synced with the types (amend mode, emptyMessage, Table props); dead boolean trueLabel/falseLabel options removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- useLineItems: `data` is now `defaultLines` (defaultX / x / onXChange). - LineItems.Table: `rowActions` is now `renderRowActions`, so it isn't confused with DataTable's RowAction[]; the `totalsRow` prop is removed in favour of <LineItems.TotalsRow>. - Field `hoverExpandWidth` is removed. - Docs and demo present in-card SaveActions as the default save surface; DirtyBar is the floating alternative for long pages. - Fill drag is dropped on pointercancel; the virtualizer measures before paint so the first frame isn't empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
16 tasks
- A paste, fill or revert now wins over typing already in the cell, and the cell shows the stored value after a commit that normalizes back. - Escape in a closed select cell cancels instead of clearing the value. - Tab reaches custom editors (focus falls back to their own input). - Number cells read pasted "1,234", "1,5", currency and full-width digits; unreadable input keeps the current value. Date cells accept YYYY/MM/DD and reject invalid or out-of-range values. - A blocked link focuses Save (or the bar), never Discard. - FloatingDock and fullscreen use the z-index token scale, so dialogs and popups are never covered. - Keyboard scroll-into-view clears the sticky header and totals rows. - Cells get one stable handlers object, so memoized cells skip re-renders on unrelated keypresses. - Last English sr-only labels localized; tests assert data attributes, not class names; new tests for each fix and for fullscreen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It is a generic tree-select dialog, not specific to line items. Component, props/node types, labels, data-slots, docs and demo renamed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pinned cells stay opaque so scrolled content can't show through, which also hid the row's hover background. They now switch to the same tint (muted at 50% over the card), with the row's colour transition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nested leaves no longer reserve the caret's space; the indent step is one caret wide instead, so a child's checkbox sits under its parent's caret and labels line up. A top-level leaf keeps the space only when siblings expand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The row is the hit target, with a pointer cursor and hover tint. Clicks on the checkbox, caret, or anything interactive the consumer renders keep their own behaviour, so nothing toggles twice. Space still toggles from the keyboard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…apse A small hook watches the rows' size and animates the list's height with the native Web Animations API (no animation library), so the dialog grows and shrinks smoothly instead of jumping. Revealed child rows fade in. Both are skipped under prefers-reduced-motion. Open/close keep Dialog's shared fade + zoom. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Group rows expand/collapse on click; their checkbox still selects every child. Item rows keep click-to-tick. Documented under Mouse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`function TreePicker` + a trailing `export { TreePicker }` made the
declaration bundler emit the displayName namespace twice (exported and
local): TS2395 for consumers with skipLibCheck: false. This is what failed
check-dts in CI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- useLineItems: `selection: true | { actions }` replaces the separate
`selectionActions` option; `lines` now requires `onLinesChange` at the
type level (and can't be combined with `defaultLines`).
- DirtyBar warnOnNav uses React Router's useBlocker (links, navigate(),
back/forward; same-route search-param changes pass) plus
useBeforeUnload, replacing document click interception. Mounted only
while dirty, so tables without it need no router.
- Custom table scrollbars dropped in favour of the browser's.
- TreePicker callbacks take one object: renderRow({ node, depth }),
renderMetric({ node, depth }), matchesSearch({ node, query }).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TreePicker now lands in its own PR; this branch builds on it. The LineItems pairing section and link return to the TreePicker docs here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 12, 2026
# Conflicts: # docs-manifest.json
Contributor
Author
|
@IzumiSy, a quick check on a DataTable refactor before this goes further in review. LineItems uses the same bulk-action footer bar as DataTable ("3 of 5 selected · actions · Clear"). Rather than copy it, I moved the bar's view and the pending-action runner into a shared internal module,
Is this refactor something we can do? If you'd rather see it as its own small PR first, I'll split it out. For context, TreePicker is now its own PR (#597), and this PR is stacked on it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
LineItems, a spreadsheet-grade editor for the lines of a document (purchase orders, invoices, journals, work orders).main.feat/tree-picker, so this diff is LineItems only. It will be retargeted tomainonce feat(tree-picker): TreePicker, a tree-select dialog for picking many items #597 merges.API
useLineItems()→<LineItems.Root value>→ compound parts.getChangeSet()returns a transport-agnostic{ isEmpty, lineChanges }withadd/update/remove/reorderops, so only real edits get submitted.useLineItemsGroup({ debits, credits })puts several collections under one Save / Discard.SelectionActionshape and the same footer bar.SaveActionsin the card is the default.DirtyBaris the floating alternative for long pages; withwarnOnNav, it holds in-app navigation through React Router'suseBlocker.Behaviour
1,234,1,5, currency symbols and full-width digits. Dates acceptYYYY/MM/DDand reject out-of-range values.Why it's built this way
renderRowActionsis a render function, not DataTable'sRowAction[]. Document lines often need per-row controls that aren't menu actions: a link to the item, inline remove, a status chip. The name is deliberately different from DataTable'srowActionsso the two aren't confused. Bulk actions do use DataTable'sSelectionActionshape.Dialog.Dialogportals its content, which would remount the grid and lose half-typed cells and focus. The overlay keeps the same element tree and addsrole="dialog",aria-modal, a focus wrap, a scroll lock, and an Escape scoped to unhandled keys.modeis"edit" | "view" | "amend", notreadOnly/disabledflags. These are stages of a document's life: draft, posted, and confirmed-but-partly-editable. A field opts into the modes it's editable in.fields, notcolumns. A field carries editing semantics (editor type, normalize/equals, commit scope, amend rules), not just display.virtual-window.test.ts.Shared selection bar: needs a maintainer's OK
To reuse DataTable's bulk-action bar, its view and pending-action runner moved into
components/internals/selection-actions/, and DataTable now uses the shared copy (data-table.tsx,selection-bar.tsx,use-data-table.ts).Known follow-ups
import "@/contexts/appshell-context"sits in two i18n modules and the shared bar. It works around a pre-existing@/hooks/i18nmodule cycle and will be removed when that cycle is fixed separately.Test plan
vitest: full suite green, including the controlled round-trip, Tab/focus, paste-after-typing, fill-down, selection actions,warnOnNav(memory router), fullscreen, and number/date parsingtsc,oxlint(0 warnings),pnpm docs:check,check-dts/showcase/line-items(PO, with a 1,000-line toggle) and/showcase/line-items-journal(group)🤖 Generated with Claude Code