Skip to content

Commit e801ca9

Browse files
TMHSDigitalclaude
andcommitted
feat(gallery): Examples-first and By-category sorts, Back undoes filter changes
Discrete filter changes (chips, pills, kind, category, sort, reset) now push a history entry so the Back button undoes them; typing and density still replace the current entry. Two new sort options keep gallery order within each group. Regenerated docs/gallery. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 1d29cef commit e801ca9

2 files changed

Lines changed: 48 additions & 18 deletions

File tree

‎docs/gallery/index.html‎

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ <h1>Examples and Showcase</h1>
8282
<select id="sort">
8383
<option value="default">Gallery order</option>
8484
<option value="az">Name A&ndash;Z</option>
85+
<option value="kind">Examples first</option>
86+
<option value="cat">By category</option>
8587
</select>
8688
</label>
8789
<div class="density" role="group" aria-label="Card density">
@@ -1791,7 +1793,7 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
17911793
// walks the reader's filtered order instead of the full gallery.
17921794
var SS_HASH = 'bdt-gallery-hash', SS_ORDER = 'bdt-gallery-order';
17931795
var KINDS = ['', 'examples', 'showcase'];
1794-
var SORTS = ['default', 'az'];
1796+
var SORTS = ['default', 'az', 'kind', 'cat'];
17951797
var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
17961798

17971799
// data-search holds title, slug, teaches, tags and category. Each
@@ -1845,15 +1847,19 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
18451847
} catch (e) {}
18461848
}
18471849

1848-
function writeHash() {
1850+
// Discrete changes (chips, pills, kind, category, sort, reset) push a
1851+
// history entry so Back undoes them; typing and density replace it.
1852+
function writeHash(push) {
18491853
var parts = [];
18501854
if (state.q) parts.push('q=' + encodeURIComponent(state.q));
18511855
if (state.tags.length) parts.push('tag=' + state.tags.map(encodeURIComponent).join(','));
18521856
if (state.kind) parts.push('k=' + state.kind);
18531857
if (state.cat) parts.push('c=' + state.cat);
18541858
if (state.sort !== 'default') parts.push('s=' + state.sort);
18551859
parts.push('d=' + state.density);
1856-
history.replaceState(null, '', location.pathname + location.search + '#' + parts.join('&'));
1860+
var url = location.pathname + location.search + '#' + parts.join('&');
1861+
if (push && url !== location.pathname + location.search + location.hash) history.pushState(null, '', url);
1862+
else history.replaceState(null, '', url);
18571863
remember();
18581864
}
18591865
var hashTimer = 0;
@@ -1902,7 +1908,7 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
19021908
b.textContent = it.label + ' \u00d7';
19031909
b.setAttribute('aria-label', 'Remove filter: ' + it.label);
19041910
b.addEventListener('click', function () {
1905-
it.drop(); syncChips(); applyFilters(); writeHash();
1911+
it.drop(); syncChips(); applyFilters(); writeHash(true);
19061912
});
19071913
pillsEl.appendChild(b);
19081914
});
@@ -1928,6 +1934,15 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
19281934
var x = a.getAttribute('data-name'), y = b.getAttribute('data-name');
19291935
return x < y ? -1 : x > y ? 1 : 0;
19301936
});
1937+
} else if (state.sort === 'kind' || state.sort === 'cat') {
1938+
// Stable by gallery order within each group. Examples have no
1939+
// category and sort first; showcase pieces follow CATEGORIES order.
1940+
var rank = function (c) {
1941+
if (c.getAttribute('data-kind') !== 'showcase') return -1;
1942+
return state.sort === 'cat' ? CATS.indexOf(c.getAttribute('data-category')) : 0;
1943+
};
1944+
var pos = new Map(cards.map(function (c, i) { return [c, i]; }));
1945+
order.sort(function (a, b) { return (rank(a) - rank(b)) || (pos.get(a) - pos.get(b)); });
19311946
}
19321947
order.forEach(function (c) { grid.appendChild(c); });
19331948
sortSel.value = state.sort;
@@ -1978,7 +1993,7 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
19781993
var at = state.tags.indexOf(t);
19791994
if (at === -1) state.tags.push(t); else state.tags.splice(at, 1);
19801995
}
1981-
syncChips(); applyFilters(); writeHash();
1996+
syncChips(); applyFilters(); writeHash(true);
19821997
});
19831998
});
19841999
// The chip row is one toolbar tab stop; arrows, Home and End move
@@ -2009,7 +2024,7 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
20092024
kindBtns.forEach(function (b) {
20102025
b.addEventListener('click', function () {
20112026
state.kind = b.getAttribute('data-kind-filter');
2012-
applyFilters(); writeHash();
2027+
applyFilters(); writeHash(true);
20132028
});
20142029
});
20152030
// A category only exists among showcase pieces, so picking one moves
@@ -2018,12 +2033,12 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
20182033
b.addEventListener('click', function () {
20192034
state.cat = b.getAttribute('data-cat-filter');
20202035
if (state.cat) state.kind = 'showcase';
2021-
applyFilters(); writeHash();
2036+
applyFilters(); writeHash(true);
20222037
});
20232038
});
20242039
sortSel.addEventListener('change', function () {
20252040
state.sort = SORTS.indexOf(sortSel.value) !== -1 ? sortSel.value : 'default';
2026-
applySort(); writeHash();
2041+
applySort(); writeHash(true);
20272042
});
20282043
densityBtns.forEach(function (b) {
20292044
b.addEventListener('click', function () {
@@ -2034,7 +2049,7 @@ <h2><a class="stretch" href="sawhorse-plank/">Sawhorses and Plank</a></h2>
20342049
resetFilters.addEventListener('click', function () {
20352050
state.q = ''; state.tags = []; state.kind = ''; state.cat = ''; state.sort = 'default';
20362051
q.value = '';
2037-
syncChips(); applySort(); applyFilters(); writeHash(); q.focus();
2052+
syncChips(); applySort(); applyFilters(); writeHash(true); q.focus();
20382053
});
20392054
filtersToggle.addEventListener('click', function () {
20402055
var open = filtersEl.classList.toggle('open');

‎scripts/build_gallery.py‎

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ def check_categories(entries: list) -> None:
609609
// walks the reader's filtered order instead of the full gallery.
610610
var SS_HASH = 'bdt-gallery-hash', SS_ORDER = 'bdt-gallery-order';
611611
var KINDS = ['', 'examples', 'showcase'];
612-
var SORTS = ['default', 'az'];
612+
var SORTS = ['default', 'az', 'kind', 'cat'];
613613
var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
614614
615615
// data-search holds title, slug, teaches, tags and category. Each
@@ -663,15 +663,19 @@ def check_categories(entries: list) -> None:
663663
} catch (e) {}
664664
}
665665
666-
function writeHash() {
666+
// Discrete changes (chips, pills, kind, category, sort, reset) push a
667+
// history entry so Back undoes them; typing and density replace it.
668+
function writeHash(push) {
667669
var parts = [];
668670
if (state.q) parts.push('q=' + encodeURIComponent(state.q));
669671
if (state.tags.length) parts.push('tag=' + state.tags.map(encodeURIComponent).join(','));
670672
if (state.kind) parts.push('k=' + state.kind);
671673
if (state.cat) parts.push('c=' + state.cat);
672674
if (state.sort !== 'default') parts.push('s=' + state.sort);
673675
parts.push('d=' + state.density);
674-
history.replaceState(null, '', location.pathname + location.search + '#' + parts.join('&'));
676+
var url = location.pathname + location.search + '#' + parts.join('&');
677+
if (push && url !== location.pathname + location.search + location.hash) history.pushState(null, '', url);
678+
else history.replaceState(null, '', url);
675679
remember();
676680
}
677681
var hashTimer = 0;
@@ -720,7 +724,7 @@ def check_categories(entries: list) -> None:
720724
b.textContent = it.label + ' \\u00d7';
721725
b.setAttribute('aria-label', 'Remove filter: ' + it.label);
722726
b.addEventListener('click', function () {
723-
it.drop(); syncChips(); applyFilters(); writeHash();
727+
it.drop(); syncChips(); applyFilters(); writeHash(true);
724728
});
725729
pillsEl.appendChild(b);
726730
});
@@ -746,6 +750,15 @@ def check_categories(entries: list) -> None:
746750
var x = a.getAttribute('data-name'), y = b.getAttribute('data-name');
747751
return x < y ? -1 : x > y ? 1 : 0;
748752
});
753+
} else if (state.sort === 'kind' || state.sort === 'cat') {
754+
// Stable by gallery order within each group. Examples have no
755+
// category and sort first; showcase pieces follow CATEGORIES order.
756+
var rank = function (c) {
757+
if (c.getAttribute('data-kind') !== 'showcase') return -1;
758+
return state.sort === 'cat' ? CATS.indexOf(c.getAttribute('data-category')) : 0;
759+
};
760+
var pos = new Map(cards.map(function (c, i) { return [c, i]; }));
761+
order.sort(function (a, b) { return (rank(a) - rank(b)) || (pos.get(a) - pos.get(b)); });
749762
}
750763
order.forEach(function (c) { grid.appendChild(c); });
751764
sortSel.value = state.sort;
@@ -796,7 +809,7 @@ def check_categories(entries: list) -> None:
796809
var at = state.tags.indexOf(t);
797810
if (at === -1) state.tags.push(t); else state.tags.splice(at, 1);
798811
}
799-
syncChips(); applyFilters(); writeHash();
812+
syncChips(); applyFilters(); writeHash(true);
800813
});
801814
});
802815
// The chip row is one toolbar tab stop; arrows, Home and End move
@@ -827,7 +840,7 @@ def check_categories(entries: list) -> None:
827840
kindBtns.forEach(function (b) {
828841
b.addEventListener('click', function () {
829842
state.kind = b.getAttribute('data-kind-filter');
830-
applyFilters(); writeHash();
843+
applyFilters(); writeHash(true);
831844
});
832845
});
833846
// A category only exists among showcase pieces, so picking one moves
@@ -836,12 +849,12 @@ def check_categories(entries: list) -> None:
836849
b.addEventListener('click', function () {
837850
state.cat = b.getAttribute('data-cat-filter');
838851
if (state.cat) state.kind = 'showcase';
839-
applyFilters(); writeHash();
852+
applyFilters(); writeHash(true);
840853
});
841854
});
842855
sortSel.addEventListener('change', function () {
843856
state.sort = SORTS.indexOf(sortSel.value) !== -1 ? sortSel.value : 'default';
844-
applySort(); writeHash();
857+
applySort(); writeHash(true);
845858
});
846859
densityBtns.forEach(function (b) {
847860
b.addEventListener('click', function () {
@@ -852,7 +865,7 @@ def check_categories(entries: list) -> None:
852865
resetFilters.addEventListener('click', function () {
853866
state.q = ''; state.tags = []; state.kind = ''; state.cat = ''; state.sort = 'default';
854867
q.value = '';
855-
syncChips(); applySort(); applyFilters(); writeHash(); q.focus();
868+
syncChips(); applySort(); applyFilters(); writeHash(true); q.focus();
856869
});
857870
filtersToggle.addEventListener('click', function () {
858871
var open = filtersEl.classList.toggle('open');
@@ -1657,6 +1670,8 @@ def build_index(data: dict, *, base: str, repo_root_url: str, site: str, css_v:
16571670
' <select id="sort">\n'
16581671
' <option value="default">Gallery order</option>\n'
16591672
' <option value="az">Name A&ndash;Z</option>\n'
1673+
' <option value="kind">Examples first</option>\n'
1674+
' <option value="cat">By category</option>\n'
16601675
' </select>\n'
16611676
' </label>\n'
16621677
' <div class="density" role="group" aria-label="Card density">\n'

0 commit comments

Comments
 (0)