Skip to content

Add item card display variants#27

Open
mattiapearl wants to merge 2 commits into
deadlock-api:mainfrom
mattiapearl:feat/item-card-display-variants-only
Open

Add item card display variants#27
mattiapearl wants to merge 2 commits into
deadlock-api:mainfrom
mattiapearl:feat/item-card-display-variants-only

Conversation

@mattiapearl
Copy link
Copy Markdown
Contributor

@mattiapearl mattiapearl commented May 29, 2026

Summary

  • add official dl-item-card variants for image-only, image+name, inline image+text, inline text, and inline image triggers
  • keep default card behavior while making the card image area explicitly square and aligning name typography with the shop card styling
  • render visible loading/error states for the new auto-sized and image-only variants
  • lazily mount/resolve tooltip relationship data only when a tooltip is opened
  • expose ItemCardVariant through core types so generated React/Vue wrappers get typed variants
  • keep tooltip behavior on the existing tooltip-trigger prop; variants still open tooltips unless disabled with tooltip-trigger="none"
  • note: variant="icon" now defaults to square corners to match the square source item art; consumers can override with --dl-item-icon-border-radius

Verification

  • npm run build (Stencil core + React wrapper + Vue wrapper)

@mattiapearl
Copy link
Copy Markdown
Contributor Author

Correction to the PR body formatting: this adds official dl-item-card variants for image-only, image+name, inline image+text, inline text, and inline image triggers. It also exposes ItemCardVariant through core types, keeps tooltip behavior on the existing tooltip-trigger prop, and was verified with npm run build for Stencil core, React, and Vue.

Copy link
Copy Markdown
Member

@raimannma raimannma left a comment

Choose a reason for hiding this comment

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

Automated /code-review (high effort, 7 finder angles → verified). 9 findings below as inline comments — 2 user-visible state bugs on the new variants, 1 clean reuse fix, plus behavior/visual regressions and cleanup. The fetchItems-duplication and wrapper-regeneration candidates were refuted (caching is present; React/Vue wrappers consume the widened type transitively from dist).

}

private renderInlineItem(item: Item | undefined, isClickMode: boolean, parts: { image: boolean; name: boolean }) {
if (this._loading || !item) return <span class="item-inline-trigger loading"></span>;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Loading skeleton is invisible for the inline / image-name variants. When variant is inline/inline-text/inline-image/image-name and the item is fetched via class-name/item-id, _loading is true and this returns an empty <span>/<div class="... loading">. But the host and .item-inline-trigger/.item-image-name are width:auto;height:auto with no content and no min-size, so the #1a1a2e shimmer paints a 0×0 box — no loading feedback at all. (The fixed-square image/icon variants are fine since the host pins 48px.) Consider a min-width/min-height or a placeholder glyph for the auto-sized loading state.


private renderImageOnly(item: Item | undefined, isClickMode: boolean) {
if (this._loading || !item) return <div class="item-image-only loading"></div>;
if (this._error) return <div class="item-image-only error" title={this._error}></div>;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Error state is effectively invisible for variant="image" and variant="inline-image". On fetch failure renderImageOnly returns a blank square (no child) and renderInlineItem with parts.name=false renders {null} — an empty 0-width span. There is no .error CSS rule, so the only signal is the hover-only title attr, which is inaccessible on touch and invisible otherwise. (image-name/inline/inline-text do show the error text.) Consider an error glyph/fallback for the image-only paths.

}
}

private getImageSrc(item: Item): string | undefined {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice extraction — but the identical fallback chain is still inlined in resolveComponentItems (line ~213) and resolveParentItems (line ~240): x.shop_image_webp || x.shop_image || x.image_webp || x.image || undefined. Both comp and other are typed Item, so this.getImageSrc(comp) / this.getImageSrc(other) are drop-in replacements. Otherwise the precedence now lives in 3 places and a future change updates the cards but silently leaves the tooltip thumbnails on the old chain.

];
}

private renderVariant(item: Item | undefined, isClickMode: boolean) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Altitude: the new lightweight variants still pay the full card cost. fetchItemData() always runs resolveComponentItems + resolveParentItems (the latter does an O(n) scan over the entire item catalog) regardless of variant, and render() always mounts dl-item-tooltip unless trigger='none'. A page with many inline-image chips therefore does N catalog scans + N hidden tooltip mounts for purely decorative glyphs. (fetchItems is cached so network is bounded, but the per-card scan + DOM mount are not.) Worth gating the parent/component resolution and tooltip mount on whether they're actually needed.

height: 100%;
position: relative;
border-radius: 4px;
border-radius: 0;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

.icon-box border-radius changed 4px → 0, which silently squares the corners of the existing variant="icon" usage. If this is intentional (to match the new square image variant), worth calling out in the PR description / changelog; otherwise it's an unannounced visual regression for current consumers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional.

}

:host(.custom-trigger) {
:host(.custom-trigger),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Altitude: variant classification is enumerated by name in 2–3 separate CSS selector lists (this :host sizing block, the .trigger block below, and the icon/image group) plus the renderVariant switch. Adding a variant means editing the type, the switch, and every CSS list in lockstep — miss one and the variant sizes wrong. The existing custom-trigger marker class (toggled once in detectCustomTrigger) is the pattern that would collapse these into a single rule (e.g. an auto-size host class).

return item?.item_slot_type ?? 'neutral';
}

private renderImageOnly(item: Item | undefined, isClickMode: boolean) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Simplification: renderImageOnly / renderImageName / renderInlineItem (and renderIcon) repeat near-identical if (_loading||!item) return loading; if (_error) return error; + getImageSrc + clickable/slot-class boilerplate, differing only in tag and which parts render. A small shared helper for the guard + class object would mean a change to the loading/error contract is made once instead of 4×.

width: 100%;
}

.item-image-only.clickable,
Copy link
Copy Markdown
Member

@raimannma raimannma Jun 1, 2026

Choose a reason for hiding this comment

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

Duplication (minor): the clickable/loading/cursor trio is now repeated in three blocks (.mod-box, .icon-box, and this new .item-image-only/.item-image-name/.item-inline-trigger group) — a shared base selector would keep the shimmer timing/cursor behavior from drifting. Also the --item-accent-color default + color declaration is duplicated verbatim in both the .item-image-name and .item-inline-trigger base rules. (Note: the per-slot weapon/vitality/spirit accent rules are already comma-grouped across the two selectors, so those are fine.)

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