Deck builder: sticky section bar for jumping between sections
A bar below the toolbar pins under the top bar and jumps between the card sections (Available / In other decks / Not owned, To pull / In deck, or All cards, plus Maybeboard), marking the one in view. Where it fits it shows inline tabs; on a narrow bar, one button names the current section and opens a menu of them all, so nothing scrolls sideways. The switch follows the bar's own width via a container query. It also holds the deck filter and an up button back to the overview. Group headers and column headers pin below the bar. Closes #23 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,12 @@ const ICON_ARROW_LEFT = icon(
|
|||||||
<polyline points="12 19 5 12 12 5" />
|
<polyline points="12 19 5 12 12 5" />
|
||||||
</>,
|
</>,
|
||||||
);
|
);
|
||||||
|
const ICON_ARROW_UP = icon(
|
||||||
|
<>
|
||||||
|
<line x1="12" y1="19" x2="12" y2="5" />
|
||||||
|
<polyline points="5 12 12 5 19 12" />
|
||||||
|
</>,
|
||||||
|
);
|
||||||
const ICON_PLUS = icon(
|
const ICON_PLUS = icon(
|
||||||
<>
|
<>
|
||||||
<line x1="12" y1="5" x2="12" y2="19" />
|
<line x1="12" y1="5" x2="12" y2="19" />
|
||||||
@@ -194,6 +200,7 @@ export const Icons = {
|
|||||||
share: ICON_SHARE,
|
share: ICON_SHARE,
|
||||||
check: ICON_CHECK,
|
check: ICON_CHECK,
|
||||||
arrowLeft: ICON_ARROW_LEFT,
|
arrowLeft: ICON_ARROW_LEFT,
|
||||||
|
arrowUp: ICON_ARROW_UP,
|
||||||
plus: ICON_PLUS,
|
plus: ICON_PLUS,
|
||||||
more: ICON_MORE,
|
more: ICON_MORE,
|
||||||
image: ICON_IMAGE,
|
image: ICON_IMAGE,
|
||||||
|
|||||||
116
src/styles.css
116
src/styles.css
@@ -779,6 +779,7 @@ input[type="checkbox"] {
|
|||||||
scrolled-past rows still show. */
|
scrolled-past rows still show. */
|
||||||
--content-pad-top: var(--space-5);
|
--content-pad-top: var(--space-5);
|
||||||
--group-header-h: 40px;
|
--group-header-h: 40px;
|
||||||
|
--section-nav-h: 48px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: var(--content-pad-top) var(--space-6);
|
padding: var(--content-pad-top) var(--space-6);
|
||||||
@@ -965,7 +966,7 @@ h4 {
|
|||||||
* tag) since h3 is also used for .dialog-title, which must not be sticky. */
|
* tag) since h3 is also used for .dialog-title, which must not be sticky. */
|
||||||
.group-header {
|
.group-header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: calc(-1 * var(--content-pad-top, 0px));
|
top: calc(var(--sticky-offset, 0px) - var(--content-pad-top, 0px));
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
margin: var(--space-4) 0 var(--space-2);
|
margin: var(--space-4) 0 var(--space-2);
|
||||||
/* A fixed height, so a grouped table's column headers can pin just below
|
/* A fixed height, so a grouped table's column headers can pin just below
|
||||||
@@ -1582,7 +1583,7 @@ th {
|
|||||||
letter-spacing: var(--tracking-label);
|
letter-spacing: var(--tracking-label);
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: calc(-1 * var(--content-pad-top, 0px));
|
top: calc(var(--sticky-offset, 0px) - var(--content-pad-top, 0px));
|
||||||
background: var(--bg-content);
|
background: var(--bg-content);
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@@ -1594,7 +1595,7 @@ td {
|
|||||||
}
|
}
|
||||||
/* Under a sticky group header, pin below it instead of behind it. */
|
/* Under a sticky group header, pin below it instead of behind it. */
|
||||||
.group-header + table th {
|
.group-header + table th {
|
||||||
top: calc(var(--group-header-h) - var(--content-pad-top, 0px));
|
top: calc(var(--sticky-offset, 0px) + var(--group-header-h) - var(--content-pad-top, 0px));
|
||||||
}
|
}
|
||||||
tbody tr {
|
tbody tr {
|
||||||
transition: background 0.08s;
|
transition: background 0.08s;
|
||||||
@@ -3139,6 +3140,115 @@ button.pull-action {
|
|||||||
margin: 4px 0 18px;
|
margin: 4px 0 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Section bar (see `SectionNav`): pins under the top bar, so group headers
|
||||||
|
and column headers below it pin under it in turn via --sticky-offset. */
|
||||||
|
.deck-builder {
|
||||||
|
--sticky-offset: var(--section-nav-h);
|
||||||
|
}
|
||||||
|
.deck-section {
|
||||||
|
scroll-margin-top: calc(var(--section-nav-h) + var(--space-2));
|
||||||
|
}
|
||||||
|
.section-nav {
|
||||||
|
position: sticky;
|
||||||
|
top: calc(-1 * var(--content-pad-top, 0px));
|
||||||
|
z-index: 6;
|
||||||
|
height: var(--section-nav-h);
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-top: var(--space-3);
|
||||||
|
background: var(--bg-content);
|
||||||
|
border-bottom: 1px solid var(--border-soft);
|
||||||
|
/* Tabs or jump menu is decided by the bar's own width (the nav rail eats
|
||||||
|
into the window). Not on .deck-builder: containment there would trap
|
||||||
|
its inline position: fixed dialogs. */
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
.section-nav button {
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
/* The bar's own buttons (not the jump menu's items) share one height. */
|
||||||
|
.section-nav > button,
|
||||||
|
.section-nav-tabs button,
|
||||||
|
.section-nav-jump > button {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.section-nav .section-nav-top {
|
||||||
|
flex-shrink: 0;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.section-nav-top svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
.section-nav-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-1);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.section-nav-tabs button {
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
.section-nav-tabs button:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.section-nav-tabs button.active {
|
||||||
|
background: var(--accent-subtle);
|
||||||
|
border-color: var(--accent-glow);
|
||||||
|
color: var(--text-strong);
|
||||||
|
}
|
||||||
|
.section-nav-tabs button.empty:not(.active) {
|
||||||
|
color: var(--faint);
|
||||||
|
}
|
||||||
|
.section-nav-jump {
|
||||||
|
position: relative;
|
||||||
|
display: none;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
.section-nav-jump > button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.section-nav-jump-label {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.section-nav-menu {
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
.section-nav-filter {
|
||||||
|
height: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
margin-left: auto;
|
||||||
|
min-width: 140px;
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
/* Below this the longest tab set (Available / In other decks / Not owned /
|
||||||
|
Maybeboard, with counts) plus a minimum-width filter no longer fits. */
|
||||||
|
@container (max-width: 740px) {
|
||||||
|
.section-nav-tabs {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.section-nav-jump {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.section-nav-filter {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* The four top-of-page info panels (commander, meta, analysis, legality).
|
/* The four top-of-page info panels (commander, meta, analysis, legality).
|
||||||
Single column matches the existing top-to-bottom reading order on
|
Single column matches the existing top-to-bottom reading order on
|
||||||
narrow/portrait viewports; on wide viewports the compact panels move
|
narrow/portrait viewports; on wide viewports the compact panels move
|
||||||
|
|||||||
@@ -206,6 +206,103 @@ const STATUS_HINTS: Record<DeckStatus, string> = {
|
|||||||
|
|
||||||
const STARTING_HAND_SIZE = 7;
|
const STARTING_HAND_SIZE = 7;
|
||||||
|
|
||||||
|
interface NavSection {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The deck builder's sticky section bar: jumps between its card sections,
|
||||||
|
* marks the one in view, and holds the filter. Inline tabs where they fit;
|
||||||
|
* on a narrow bar, one button naming the current section opens a menu of
|
||||||
|
* them all instead (the CSS picks by the bar's own width).
|
||||||
|
*/
|
||||||
|
function SectionNav({
|
||||||
|
sections,
|
||||||
|
active,
|
||||||
|
onJump,
|
||||||
|
onTop,
|
||||||
|
filter,
|
||||||
|
onFilterChange,
|
||||||
|
}: {
|
||||||
|
sections: NavSection[];
|
||||||
|
active: string;
|
||||||
|
onJump: (id: string) => void;
|
||||||
|
onTop: () => void;
|
||||||
|
filter: string;
|
||||||
|
onFilterChange: (q: string) => void;
|
||||||
|
}) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const jumpRef = useRef<HTMLDivElement>(null);
|
||||||
|
const current = sections.find((s) => s.id === active) ?? sections[0];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
const onDown = (e: MouseEvent) => {
|
||||||
|
if (jumpRef.current && !jumpRef.current.contains(e.target as Node)) setOpen(false);
|
||||||
|
};
|
||||||
|
const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
|
||||||
|
document.addEventListener("mousedown", onDown);
|
||||||
|
window.addEventListener("keydown", onKey);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", onDown);
|
||||||
|
window.removeEventListener("keydown", onKey);
|
||||||
|
};
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="section-nav">
|
||||||
|
<button className="section-nav-top" onClick={onTop} title="Back to the deck overview" aria-label="Back to the deck overview">
|
||||||
|
{Icons.arrowUp}
|
||||||
|
</button>
|
||||||
|
<nav className="section-nav-tabs" aria-label="Deck sections">
|
||||||
|
{sections.map((s) => (
|
||||||
|
<button
|
||||||
|
key={s.id}
|
||||||
|
className={[s.id === active ? "active" : "", s.count === 0 ? "empty" : ""].filter(Boolean).join(" ")}
|
||||||
|
aria-current={s.id === active ? "location" : undefined}
|
||||||
|
onClick={() => onJump(s.id)}
|
||||||
|
>
|
||||||
|
{s.label} <span className="badge">{s.count}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
<div className="section-nav-jump" ref={jumpRef}>
|
||||||
|
<button onClick={() => setOpen(!open)} aria-haspopup="menu" aria-expanded={open} title="Jump to a section">
|
||||||
|
<span className="section-nav-jump-label">{current.label}</span>
|
||||||
|
<span className="badge">{current.count}</span> ▾
|
||||||
|
</button>
|
||||||
|
{open && (
|
||||||
|
<div className="library-menu section-nav-menu" role="menu">
|
||||||
|
{sections.map((s) => (
|
||||||
|
<button
|
||||||
|
key={s.id}
|
||||||
|
role="menuitemradio"
|
||||||
|
aria-checked={s.id === active}
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false);
|
||||||
|
onJump(s.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="library-menu-check">{s.id === active ? "✓" : ""}</span>
|
||||||
|
{s.label}
|
||||||
|
<span className="hint">{s.count}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
className="section-nav-filter"
|
||||||
|
value={filter}
|
||||||
|
onChange={(e) => onFilterChange(e.target.value)}
|
||||||
|
placeholder="Filter cards in deck…"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deck editor. A deck's mainboard cards are split into two groups — "In your
|
* Deck editor. A deck's mainboard cards are split into two groups — "In your
|
||||||
* library" (owned) and "Not owned" — each with its own search box and each
|
* library" (owned) and "Not owned" — each with its own search box and each
|
||||||
@@ -692,6 +789,69 @@ function DeckBuilder({
|
|||||||
const deckStats = useMemo(() => computeDeckStats(contents), [contents]);
|
const deckStats = useMemo(() => computeDeckStats(contents), [contents]);
|
||||||
const deckLegality = useMemo(() => computeDeckLegality(deck, contents), [deck, contents]);
|
const deckLegality = useMemo(() => computeDeckLegality(deck, contents), [deck, contents]);
|
||||||
|
|
||||||
|
// The card sections below, in order, for the section bar to jump between.
|
||||||
|
// Mirrors the branches that render them.
|
||||||
|
const sections: NavSection[] = [
|
||||||
|
...(combineSections
|
||||||
|
? [{ id: "all", label: "All cards", count: mainboard.length }]
|
||||||
|
: physical
|
||||||
|
? [
|
||||||
|
{ id: "pull", label: "To pull", count: toPull.length },
|
||||||
|
{ id: "in-deck", label: "In deck", count: inDeck.length },
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{ id: "available", label: "Available", count: available.length },
|
||||||
|
...(elsewhere.length > 0 ? [{ id: "elsewhere", label: "In other decks", count: elsewhere.length }] : []),
|
||||||
|
{ id: "unowned", label: "Not owned", count: unowned.length },
|
||||||
|
]),
|
||||||
|
{ id: "maybe", label: "Maybeboard", count: maybeboard.length },
|
||||||
|
];
|
||||||
|
const rootRef = useRef<HTMLDivElement>(null);
|
||||||
|
const sectionRefs = useRef<Record<string, HTMLElement | null>>({});
|
||||||
|
const sectionRef = (id: string) => (el: HTMLElement | null) => {
|
||||||
|
sectionRefs.current[id] = el;
|
||||||
|
};
|
||||||
|
const [activeSection, setActiveSection] = useState(sections[0].id);
|
||||||
|
const sectionIds = sections.map((s) => s.id).join(",");
|
||||||
|
|
||||||
|
// Scroll-spy: the current section is the last one whose top has reached
|
||||||
|
// where a jump lands it (just under the bar, per its scroll-margin) — or
|
||||||
|
// the last one outright once scrolled to the bottom, so a short
|
||||||
|
// Maybeboard can still become current.
|
||||||
|
useEffect(() => {
|
||||||
|
const scroller = rootRef.current?.closest(".content");
|
||||||
|
if (!scroller) return;
|
||||||
|
let frame = 0;
|
||||||
|
const update = () => {
|
||||||
|
frame = 0;
|
||||||
|
const ids = sectionIds.split(",");
|
||||||
|
const top = scroller.getBoundingClientRect().top;
|
||||||
|
let current = ids[0];
|
||||||
|
for (const id of ids) {
|
||||||
|
const el = sectionRefs.current[id];
|
||||||
|
if (el && el.getBoundingClientRect().top <= top + parseFloat(getComputedStyle(el).scrollMarginTop) + 1) {
|
||||||
|
current = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 2) current = ids[ids.length - 1];
|
||||||
|
setActiveSection(current);
|
||||||
|
};
|
||||||
|
const onScroll = () => {
|
||||||
|
if (!frame) frame = requestAnimationFrame(update);
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
scroller.addEventListener("scroll", onScroll, { passive: true });
|
||||||
|
return () => {
|
||||||
|
scroller.removeEventListener("scroll", onScroll);
|
||||||
|
cancelAnimationFrame(frame);
|
||||||
|
};
|
||||||
|
}, [sectionIds]);
|
||||||
|
|
||||||
|
const scrollBehavior = (): ScrollBehavior =>
|
||||||
|
window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth";
|
||||||
|
const jumpTo = (id: string) => sectionRefs.current[id]?.scrollIntoView({ behavior: scrollBehavior(), block: "start" });
|
||||||
|
const jumpToTop = () => rootRef.current?.closest(".content")?.scrollTo({ top: 0, behavior: scrollBehavior() });
|
||||||
|
|
||||||
// Lets the click preview track copies and choose each slot's printing.
|
// Lets the click preview track copies and choose each slot's printing.
|
||||||
const preview: PreviewContext = {
|
const preview: PreviewContext = {
|
||||||
deckId,
|
deckId,
|
||||||
@@ -913,7 +1073,7 @@ function DeckBuilder({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="deck-builder">
|
<div className="deck-builder" ref={rootRef}>
|
||||||
<button className="deck-builder-back" onClick={onBack}>
|
<button className="deck-builder-back" onClick={onBack}>
|
||||||
{Icons.arrowLeft}
|
{Icons.arrowLeft}
|
||||||
All decks
|
All decks
|
||||||
@@ -1140,12 +1300,6 @@ function DeckBuilder({
|
|||||||
Combine sections
|
Combine sections
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
|
||||||
value={filterQuery}
|
|
||||||
onChange={(e) => setFilterQuery(e.target.value)}
|
|
||||||
placeholder="Filter cards in deck…"
|
|
||||||
style={{ width: 200 }}
|
|
||||||
/>
|
|
||||||
<span className="spacer" />
|
<span className="spacer" />
|
||||||
<GroupBySelect value={groupBy} onChange={setGroupBy} />
|
<GroupBySelect value={groupBy} onChange={setGroupBy} />
|
||||||
<ViewToggle mode={view} onChange={setView} />
|
<ViewToggle mode={view} onChange={setView} />
|
||||||
@@ -1153,6 +1307,15 @@ function DeckBuilder({
|
|||||||
{view === "tiles" && <TileColumnsControl tiles={tiles} />}
|
{view === "tiles" && <TileColumnsControl tiles={tiles} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<SectionNav
|
||||||
|
sections={sections}
|
||||||
|
active={activeSection}
|
||||||
|
onJump={jumpTo}
|
||||||
|
onTop={jumpToTop}
|
||||||
|
filter={filterQuery}
|
||||||
|
onFilterChange={setFilterQuery}
|
||||||
|
/>
|
||||||
|
|
||||||
{selectedCards.size > 0 && (
|
{selectedCards.size > 0 && (
|
||||||
<div className="bulk-bar">
|
<div className="bulk-bar">
|
||||||
<span className="bulk-bar-count">
|
<span className="bulk-bar-count">
|
||||||
@@ -1208,7 +1371,7 @@ function DeckBuilder({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{combineSections ? (
|
{combineSections ? (
|
||||||
<section style={{ marginTop: 10 }}>
|
<section className="deck-section" ref={sectionRef("all")} style={{ marginTop: 10 }}>
|
||||||
<h3>
|
<h3>
|
||||||
All cards <span className="badge">{mainboard.length}</span>
|
All cards <span className="badge">{mainboard.length}</span>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1223,7 +1386,7 @@ function DeckBuilder({
|
|||||||
</section>
|
</section>
|
||||||
) : physical ? (
|
) : physical ? (
|
||||||
<>
|
<>
|
||||||
<section style={{ marginTop: 10 }}>
|
<section className="deck-section" ref={sectionRef("pull")} style={{ marginTop: 10 }}>
|
||||||
<h3>
|
<h3>
|
||||||
To pull <span className="badge">{toPull.length}</span>
|
To pull <span className="badge">{toPull.length}</span>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1249,7 +1412,7 @@ function DeckBuilder({
|
|||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section style={{ marginTop: 26 }}>
|
<section className="deck-section" ref={sectionRef("in-deck")} style={{ marginTop: 26 }}>
|
||||||
<h3>
|
<h3>
|
||||||
In deck <span className="badge">{inDeck.length}</span>
|
In deck <span className="badge">{inDeck.length}</span>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1258,7 +1421,7 @@ function DeckBuilder({
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<section style={{ marginTop: 10 }}>
|
<section className="deck-section" ref={sectionRef("available")} style={{ marginTop: 10 }}>
|
||||||
<h3>
|
<h3>
|
||||||
Available <span className="badge">{available.length}</span>
|
Available <span className="badge">{available.length}</span>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1276,7 +1439,7 @@ function DeckBuilder({
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{elsewhere.length > 0 && (
|
{elsewhere.length > 0 && (
|
||||||
<section style={{ marginTop: 26 }}>
|
<section className="deck-section" ref={sectionRef("elsewhere")} style={{ marginTop: 26 }}>
|
||||||
<h3>
|
<h3>
|
||||||
In other decks <span className="badge">{elsewhere.length}</span>
|
In other decks <span className="badge">{elsewhere.length}</span>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1287,7 +1450,7 @@ function DeckBuilder({
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section style={{ marginTop: 26 }}>
|
<section className="deck-section" ref={sectionRef("unowned")} style={{ marginTop: 26 }}>
|
||||||
<h3>
|
<h3>
|
||||||
Not owned <span className="badge">{unowned.length}</span>
|
Not owned <span className="badge">{unowned.length}</span>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1303,7 +1466,7 @@ function DeckBuilder({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section style={{ marginTop: 26 }}>
|
<section className="deck-section" ref={sectionRef("maybe")} style={{ marginTop: 26 }}>
|
||||||
<h3>
|
<h3>
|
||||||
Maybeboard <span className="badge">{maybeboard.length}</span>
|
Maybeboard <span className="badge">{maybeboard.length}</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
Reference in New Issue
Block a user