Filter by tag, and by more than one library - #209
Closed
abernier wants to merge 2 commits into
Closed
Conversation
`pnpm dlx shadcn@latest add combobox -c apps/website`, `base-maia`. It pulls `button` and `input-group`, both already here, and re-emits them plus `input` and `textarea` with formatting-only differences that prettier takes straight back out -- `add --diff` over all five reports nothing else. Radix has no combobox, which is why this file could not exist before the registry moved: the `radix-*` styles answer that job with `command` and cmdk instead, a different component with a different API. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rail filtered by library and by free text, and the two took turns:
the header showed one or the other, never both, because there was no
room for both. Tags were printed on every card and were decorative --
they reached the free-text haystack and nothing else.
Now there are two controls on one line, and a row of what is picked
underneath.
### Two vocabularies, one rule for both
An option is offered when it partitions the corpus:
2 <= count < examples.length
which is 56 tags and 12 libraries. `@react-three/fiber` is on all 167
examples and leaves by the top; a tag carried by one example leaves by
the bottom. Neither end is named in the code -- the rule reads the
corpus, so it cannot go stale as examples land.
It governs what is *offered*, never what is *accepted*. A value that has
dropped out of a list still filters, so a link shared before it dropped
still narrows to what it said it would.
Every pick narrows, within a vocabulary and across the two. `Cannon` +
`Rapier` is empty and says so; there is one rule to learn and the count
under the field only ever falls as filters are added.
### The URL
`?library=` keeps its name and its labels and becomes a list. A link
written when it held one value parses as the one-element list it now is,
so there is no compatibility layer to write, deprecate or delete.
`?tags=` arrives in the same shape, and `bootNav` learns it -- without
that, a `?tags=` link paints all 167 examples with no skeleton and then
snaps.
### Where the picks are shown
Not inside the field. A chips input grows to two and three lines, and
the pre-paint skeleton standing in front of it on a filtered arrival can
only be one -- and the registry's pill radius on a box three lines tall
reads as a mistake. So the field stays one line at every filter it can
hold, and everything picked sits in a row below it, from both controls
at once. That row is also the only thing that says *which* libraries are
on, the trigger being an icon and a count.
The row is a repeated group of controls, so it takes a roving tabindex
of its own: four active filters would otherwise be four more tab stops
in a site that has six.
### Keyboard
Carried over rather than dropped. Cmd-F/Cmd-K and `/` unchanged, and the
`!libraryOpen` guard stays exactly what it was, because the Select is
still open by exception.
`Escape` goes in two beats: while the popup is open Base UI closes it
and the key never reaches the rail, so the press that dismisses a list
cannot also throw away what the list was filtering. Closed and filtered,
the same key clears.
The field is reached through the row that holds it rather than by a ref:
it is the registry's component, rendered a couple of wrappers down, and
a ref handed that far arrives empty.
### Search
Still every typed word, not the whole phrase. Free text is the only way
to reach the 139 single-example tags, the authors and the descriptions,
none of which are pickable. The dropdown keeps the component's own
filter -- of 68 options exactly one is two words, which is not enough to
justify writing one.
### Checked by hand
`tsc --noEmit`, `pnpm check`, `next build` -> 171 static pages.
In the browser: picking, clearing, both counts, the intersection going
empty and the `Empty` block that catches it, a `?library=Drei` link from
before this change, `?library=Leva,Lamina` after it, the typed query
surviving the popup closing, and the roving tabindex over the picked
row. Card links carry the filter and not the card's own tags. Mobile at
375px.
Two things this browser could not drive: injected key events arrive with
an empty `key`, so the hotkey layer was exercised by dispatching events
with one -- it wants a real keyboard once -- and the popup inside the
mobile sheet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
The rail filtered by library and by free text, and the two took turns:
the header showed one or the other, never both, because there was no
room for both. Tags were printed on every card and were decorative --
they reached the free-text haystack and nothing else.
Now there are two controls on one line, and a row of what is picked
underneath.
Two vocabularies, one rule for both
An option is offered when it partitions the corpus:
which is 56 tags and 12 libraries.
@react-three/fiberis on all 167examples and leaves by the top; a tag carried by one example leaves by
the bottom. Neither end is named in the code -- the rule reads the
corpus, so it cannot go stale as examples land.
It governs what is offered, never what is accepted. A value that has
dropped out of a list still filters, so a link shared before it dropped
still narrows to what it said it would.
Every pick narrows, within a vocabulary and across the two.
Cannon+Rapieris empty and says so; there is one rule to learn and the countunder the field only ever falls as filters are added.
The URL
?library=keeps its name and its labels and becomes a list. A linkwritten when it held one value parses as the one-element list it now is,
so there is no compatibility layer to write, deprecate or delete.
?tags=arrives in the same shape, andbootNavlearns it -- withoutthat, a
?tags=link paints all 167 examples with no skeleton and thensnaps.
Where the picks are shown
Not inside the field. A chips input grows to two and three lines, and
the pre-paint skeleton standing in front of it on a filtered arrival can
only be one -- and the registry's pill radius on a box three lines tall
reads as a mistake. So the field stays one line at every filter it can
hold, and everything picked sits in a row below it, from both controls
at once. That row is also the only thing that says which libraries are
on, the trigger being an icon and a count.
The row is a repeated group of controls, so it takes a roving tabindex
of its own: four active filters would otherwise be four more tab stops
in a site that has six.
Keyboard
Carried over rather than dropped. Cmd-F/Cmd-K and
/unchanged, and the!libraryOpenguard stays exactly what it was, because the Select isstill open by exception.
Escapegoes in two beats: while the popup is open Base UI closes itand the key never reaches the rail, so the press that dismisses a list
cannot also throw away what the list was filtering. Closed and filtered,
the same key clears.
The field is reached through the row that holds it rather than by a ref:
it is the registry's component, rendered a couple of wrappers down, and
a ref handed that far arrives empty.
Search
Still every typed word, not the whole phrase. Free text is the only way
to reach the 139 single-example tags, the authors and the descriptions,
none of which are pickable. The dropdown keeps the component's own
filter -- of 68 options exactly one is two words, which is not enough to
justify writing one.
Checked by hand
tsc --noEmit,pnpm check,next build-> 171 static pages.In the browser: picking, clearing, both counts, the intersection going
empty and the
Emptyblock that catches it, a?library=Dreilink frombefore this change,
?library=Leva,Laminaafter it, the typed querysurviving the popup closing, and the roving tabindex over the picked
row. Card links carry the filter and not the card's own tags. Mobile at
375px.
Two things this browser could not drive: injected key events arrive with
an empty
key, so the hotkey layer was exercised by dispatching eventswith one -- it wants a real keyboard once -- and the popup inside the
mobile sheet.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Closes #206. The clickable tag badges are #207.
馃 Generated with Claude Code