Skip to content

Scope the Type List header checkbox to the displayed rows - #1871

Merged
BryonLewis merged 12 commits into
mainfrom
type-list-header-checkbox
Aug 29, 2026
Merged

Scope the Type List header checkbox to the displayed rows#1871
BryonLewis merged 12 commits into
mainfrom
type-list-header-checkbox

Conversation

@PaulHax

@PaulHax PaulHax commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Scope the Type List header checkbox to the displayed rows

Follow-up to Bryon's #1867 review.

With Filter Types by Frame on, the list showed only the current frame's types but the header
checkbox still toggled hidden types. Those selections also feed the delete button.

Changes

  • Scope the header checkbox to the rows actually displayed — after search, frame filtering,
    collapsed branches, and Compact Parents.
  • Scope the delete button the same way. It read every checked type, so it deleted annotations of
    types the list was not showing, and stayed enabled when the only checked types were hidden.
  • Keep parent-row checkboxes subtree-wide: parent means "this branch"; header means "what I can
    see."
  • Show an empty list as unchecked and disable the header checkbox, and say whether the list is
    empty because nothing is defined or because the filters excluded everything.
  • Add the missing default for Filter Types by Frame, making the setting reactive on fresh
    profiles.
  • Hide hierarchy branches with no annotations while Show Empty is off. The candidate set took
    every type with a child, so an unannotated branch held a row whose own leaves stayed hidden.
  • Cover flat and hierarchical lists with regression tests.

Frame-filtered rows disappear when unchecked; turning the frame filter off brings them back. This
matches existing per-row behavior.

A parent's tri-state still rolls up its complete subtree, so an ancestor of a used type can read
indeterminate against a hidden checked descendant. That is the row's documented meaning.

PaulHax added 11 commits August 28, 2026 18:06
The header bulk toggle read `actionableTypes`, which honored Show Empty and
the search box but not Filter Types by Frame. It also reached hierarchy types
hidden under collapsed branches or folded into the shared-lineage breadcrumb.
A user could see a few rows, check the header, and act on types the list did
not show, including through the delete button.

Build the final row model and intersect it with the filtered candidates. This
excludes frame-filtered types, context-only ancestors, breadcrumbed ancestors,
and collapsed descendants. A parent row's checkbox is unchanged and still
owns its complete subtree.

An empty list also reported the header as checked, because a difference
against nothing is empty. Ordering the two state checks the other way round
reports unchecked instead, and the control is disabled.

Filter Types by Frame had no key in the default settings, so on a profile
that never stored one the switch wrote a non-reactive property and the list
did not respond until reload. Give it a default.

The Type List builds its model the same way with and without a hierarchy, so
this changes flat datasets too.
The candidate set admitted every type with a child, so a branch whose whole
subtree is unannotated took a row while Show Empty was off. Its own leaves
stayed hidden, leaving a parent that led nowhere and, once the header cleared
the visible rows, rendered indeterminate against a descendant the list never
showed.

Build the set from the ancestors of used types instead. Paths back to each
root survive because an ancestor of a used type is one of them; branches with
nothing annotated do not.

A parent's tri-state still rolls up its complete subtree, so an ancestor of a
used type can still read indeterminate against a hidden checked descendant.
That is the row's documented meaning and is left alone.
An empty list looked the same whether the dataset defined no types or the
filters excluded every one it does define, leaving nothing to act on and no
account of why.

Name the two cases apart, and use the list's own noun so the group panel reads
correctly.
The button read every checked type, so it deleted annotations of types the
list was not showing: a type hidden by Show Empty, a frame filter, a search,
a collapsed branch, or the shared-lineage breadcrumb was still deleted, and
the button stayed enabled when the only checked types were hidden ones.

Read the same rows the header acts on. The confirmation lists exactly what
will be deleted, and an empty intersection disables the button.
The empty-state text re-derived "does this dataset define anything" from the
raw type sources, which is the union the model already builds, and read it off
the per-frame row projection rather than the row model itself. Report it from
the model and read the rows.

The candidate set walked ancestors by hand next to the helper that does it,
and the delete set hand-rolled an ordered intersection beside a sibling that
uses lodash for the same pair of lists.

The empty text says "types" in both panels, matching the heading the panel
already shows.
The Type List header checkbox and the delete button both act on "checked
and displayed". That rule was expressed three ways in the component: an
intersection for delete, a difference for the header tri-state, and the
model's own row/actionable intersection. buildTypeListModel now returns
actionableCheckedTypes alongside actionableTypes, and the component reads
both.
Both branches of the header toggle rebuilt the same "checked but not
displayed" difference; hoist it to one computed and let the toggle read
as the union or the remainder.
Scoping the header and the delete button to displayed rows dropped the
descendants a collapsed row hides, even though that row's own checkbox
owns them. Delete matches a track's displayed type and does not cascade,
so deleting a collapsed parent removed nothing while still prompting;
the header likewise checked the parent alone and left its row showing
the mixed state it had just resolved.

actionableTypes now expands a collapsed row to its subtree and an
expanded row to itself, then keeps only what the filters allowed, so a
frame-filtered or searched-out type is still left alone either way.
The header acts on every candidate the filters allowed, minus the
ancestors a breadcrumb hides. Deriving that from the row list instead
restated the same set in terms of what flatten happened to emit, which
is why a collapsed row's branch had to be added back by hand.
@PaulHax
PaulHax marked this pull request as ready for review August 29, 2026 17:08

@BryonLewis BryonLewis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just pulled and tested, made a small PR to align with the other vitest PR about the jsdom #1872. Thanks for doing this.

@BryonLewis
BryonLewis merged commit be70fba into main Aug 29, 2026
3 checks passed
@BryonLewis
BryonLewis deleted the type-list-header-checkbox branch August 29, 2026 19:00
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.

2 participants