CS-11207: Adopt Adorn overlay treatment for cards / atoms#4930
Draft
lukemelia wants to merge 8 commits into
Draft
CS-11207: Adopt Adorn overlay treatment for cards / atoms#4930lukemelia wants to merge 8 commits into
lukemelia wants to merge 8 commits into
Conversation
Replaces the per-card overlay chrome (floating select / edit / 3-dots cluster) with the Adorn pattern from CS-11207: a hover-only type-label tab at the top-left of the card carrying the per-card menu, and a rounded-square selection chip at the bottom-right. Strokes are now box-shadow outlines that follow the card's own border-radius (2px on hover-unselected, 4px on selected, with the darker accent on hover-on-selected). The stack-header multi-select pill becomes a teal chip echoing the selection count, and its dropdown panel gets a "N Selected" header above the existing actions. Edit moves from a standalone pencil button into the per-card menu; overlay tests are updated to walk the more-options menu to reach it. The base Overlays component grows two hooks (shouldDelayHoverClear, shouldSwallowCardClick) so the chrome stays mounted while a portal'd dropdown is open and so the outside-click that dismisses the dropdown doesn't ALSO open the underlying card. The velcro offset middleware copies the underlying card's computed border-radius onto the overlay so the selection stroke traces the same curve. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 1h 33m 30s ⏱️ -22s Results for commit 4cba7a2. ± Comparison against earlier commit affcbd5. For more details on these errors, see this check. Realm Server Test Results 1 files ±0 1 suites ±0 10m 38s ⏱️ +16s Results for commit 4cba7a2. ± Comparison against earlier commit affcbd5. |
The selection count was previously rendered as a custom header element above the Menu in CardHeader's dropdown content. Visually it didn't align with the menu items below (the icon and label sat in a different horizontal grid than Select All / Deselect All / Delete N items). Promote MenuItem.header (which already existed on the type) into the Menu component's renderer: items with header=true get a teal background, pointer-events disabled on the row content so the row is inert, and the check-icon column suppressed. Because the row is just another MenuItem under the same `<ul>`, it inherits the same row geometry — icon column lines up with the items below. CardHeader no longer carries the headerText / custom header rendering; stack-item.gts inserts a header MenuItem at the top of the utility menu with the dark-circle-with-teal-check artwork as its icon. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the header MenuItem is the first child, it now picks up the
panel's border-top-{left,right}-radius via `inherit` (mirroring the
hover-state rule used by regular menu items at the top/bottom edges).
Without this, the teal background of the row was painting square
corners over the panel's rounded ones, leaving a pair of small white
"ears" at the top of the dropdown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the overlay renders over a FileDef target, the type-label tab was always showing 'Card' (because the helper peeked the store as a CardDef, missing the FileDef registered under file-meta). Switch the peek to consult the correct subtree based on the existing isFileMetaTarget detection, and let cardTypeDisplayName / cardTypeIcon resolve against the FileDef's own constructor (FileDef extends BaseDef, so the helpers already work — they were just being fed undefined). The per-card menu now swaps two labels when the target is a file: 'View card' → 'View file' and 'Copy Card URL' → 'Copy File URL'. Edit and Delete are already suppressed for file targets by the existing isButtonDisplayed gate. Test: extend overlay-menu-items-test with a parent card that linksTo MarkdownDef and a .md file in the realm; hover the rendered file and assert (a) the type label tab shows the FileDef's displayName, (b) the menu has 'View file' / 'Copy File URL' but not their card counterparts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the icon gap for cards/files in the cards-grid (and any other surface using prerendered-card-search). Previously the type-label tab showed text without an icon for prerendered rows because cardTypeIcon needs a runtime constructor reference we don't have until the instance is loaded into the store. prerendered-card-search now stamps `data-card-type-icon-html` with the raw SVG markup the realm server already serves on PrerenderedCardData. The overlay's getCardTypeIcon falls back to that attribute, wrapping it in an htmlComponent (which caches by source string) so it renders through the same `<TypeIcon class='adorn-label-icon' />` invocation the loaded-instance path uses. Applies equally to prerendered FileDef rows — they get a real displayName-derived label and a real FileDef icon, not the generic fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six things were broken by the prior commits — addressing all here:
1. lint:types — SelectionCheckmarkIcon was typed as
TemplateOnlyComponent<{ Element: SVGElement }> but Icon expects
SVGSVGElement. Widen.
2. Menu label mismatch — overlay implementation emitted 'Edit card'
but several existing tests (and the new ones I added) assert on
'Edit'. Rename the menu-item label to 'Edit' to match.
3. workspace-delete-multiple selector — the test's selectCard helper
was waiting for `button.actions-item__button`, a CSS class the
new chrome no longer uses. Switch to `[data-test-overlay-select]`
which exists on the new selection chip and is stable.
4. card-delete test — the per-card menu lives inside the type-label
tab, which only renders on hover. The test was selecting two cards
then immediately clicking more-options on the first; with the new
chrome the first card had been mouseleft and so its menu trigger
was unmounted. Re-trigger mouseenter on the target card before
clicking more-options.
5. Errored-card crash — `cardOrField.constructor.getDisplayName is
not a function` when the store returns an error envelope instead
of a BaseDef. Add a defensive check in peekInstance that requires
constructor.getDisplayName before returning the instance.
6. spec-preview overlay clear — the hover-bridge 100ms delay added in
the prior commit was applied as a static constant on the base
Overlays class, which broke immediate-clear expectations in
spec-preview / playground-panel / preview-panel. Convert the delay
to a `hoverClearDelayMs` getter — base returns 0 (immediate, the
original behavior), OperatorModeOverlays overrides to 100.
Also: FileDef instances always show "FILE" in the type-label tab,
because createFileDef on the client always instantiates the base
FileDef class — so cardTypeDisplayName(instance) returns 'File' even
for `linksTo(MarkdownDef)` targets. The realm server's indexer DOES
record the proper subclass display name in `display_names`, which
prerendered-card-search stamps onto the rendered wrapper. Reorder
getCardTypeName / getCardTypeIcon to prefer the DOM attribute over
the in-memory instance, so a FileDef row's chip says 'Markdown'
(or its actual subclass) instead of 'File'.
Fix the FileDef test's realm-content path (`notes.md` belongs under
`ParentWithFile/` since the relationship is './notes.md').
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CardsGrid file rows still read 'FILE' because the realm-server indexer
populates display_names only for FileDef *subclasses* — getDisplayNames
walks the prototype chain and stops at the base FileDef, so a row that
extracted as the bare FileDef class lands in the index with an empty
display_names array. cardType is then undefined and prerendered-card-
search doesn't stamp the DOM attribute, so my overlay's fallback hit
the generic 'File' label.
Also retract an earlier inaccurate claim — `createFileDef` is not on
the linksTo deserialization path; _createFromSerialized resolves to
the field's declared subclass. So linksTo(MarkdownDef) really does
yield a MarkdownDef instance with displayName 'Markdown'. The bare-
FileDef-only-shows-FILE case is specifically about *standalone*
files in CardsGrid (not linked from a card with linksTo(SubFileDef)).
For those rows the label now falls back to the URL extension ('MD',
'GTS', 'PNG', etc.) so each row reads differently. Still degrades to
'File' if no extension is parseable.
Picking order for the label:
1. The first source (DOM attr or in-store instance) that gives a
*specific* name (anything other than the literal 'File' / 'Card').
2. The URL extension when the target is a file.
3. The raw DOM attr / instance name, even if generic.
4. 'File' / 'Card' final fallback.
Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Summary
box-shadowstrokes that follow the card's actualborder-radius(2px on hover, 4px on selected, darker accent on hover-on-selected).[✓ N ▼]; its dropdown gets a tealN Selectedheader above the existing Select All / Deselect All / Delete N items.shouldDelayHoverClear,shouldSwallowCardClick) so the chrome stays mounted while a portal'd dropdown is open and so the dismiss-click doesn't ALSO open the card underneath.Test plan
N Selectedheader + Select All / Deselect All / Delete N items.Deferred (out of tight scope)
🤖 Generated with Claude Code