Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/04_build_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@
from _lib.be.region import derive_region as derive_be_region
from _lib.bg.geometry import BGPolygonIndex
from _lib.bg.region import derive_region as derive_bg_region
from _lib.ch.canton import canton_name as ch_canton_name
from _lib.ch.geometry import CHCommuneIndex, GESitgIndex
from _lib.ch.geometry import resolve as ch_resolve_geometry
from _lib.ch.canton import canton_name as ch_canton_name
from _lib.ch.region import derive_region as derive_ch_region
from _lib.cy.geometry import CYPolygonIndex
from _lib.cy.region import derive_region as derive_cy_region
Expand Down
38 changes: 34 additions & 4 deletions scripts/_lib/assets/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
const FACET_STYLES_TREE = __OWM_styles_tree_json__;
const STYLE_DESCENDANTS = __OWM_style_descendants_json__;
const FACET_STYLES_SIMPLE = __OWM_styles_simple_json__;
const FACET_PRINCIPAL = __OWM_principal_json__;

Check warning on line 16 in scripts/_lib/assets/app.js

View workflow job for this annotation

GitHub Actions / eslint

'FACET_PRINCIPAL' is assigned a value but never used
const FACET_ACCESSORY = __OWM_accessory_json__;

Check warning on line 17 in scripts/_lib/assets/app.js

View workflow job for this annotation

GitHub Actions / eslint

'FACET_ACCESSORY' is assigned a value but never used
const FACET_GRAPES_ALL = __OWM_grapes_all_json__;

Check warning on line 18 in scripts/_lib/assets/app.js

View workflow job for this annotation

GitHub Actions / eslint

'FACET_GRAPES_ALL' is assigned a value but never used
const FACET_REGIONS = __OWM_regions_json__;
const STYLE_LABELS = __OWM_style_labels_json__;
const SIMPLE_STYLE_LABELS = __OWM_simple_style_labels_json__;
Expand Down Expand Up @@ -73,7 +73,7 @@
// the single canonical slug under which the facet renders. Cross-narrow
// counts roll up via this map so a record using "malbec" increments the
// canonical "cot" row.
const SLUG_TO_CANONICAL = __OWM_slug_to_canonical_json__;

Check warning on line 76 in scripts/_lib/assets/app.js

View workflow job for this annotation

GitHub Actions / eslint

'SLUG_TO_CANONICAL' is assigned a value but never used
// Synonyms shown inline on each canonical row (e.g. Côt → [malbec,
// auxerrois]). Sorted by global usage; the row's `.name` span includes
// every synonym so the per-facet search input matches any spelling.
Expand All @@ -87,7 +87,7 @@
}
return out;
}
function grapeSynonymsHtml(canonSlug) {

Check warning on line 90 in scripts/_lib/assets/app.js

View workflow job for this annotation

GitHub Actions / eslint

'grapeSynonymsHtml' is defined but never used
const syns = GRAPE_SYNONYMS[canonSlug];
if (!syns || !syns.length) return '';
const labels = syns.map(s => grapeName(s)).join(', ');
Expand Down Expand Up @@ -866,6 +866,24 @@
return 'indeterminate';
}

// A region's slug list mixes two legal tiers: appellations proper and their
// complementary geographic designations (FR DGC / premier-cru climat, ES
// subzona, IT sottozona, DE Einzellage). One combined number implies a legal
// precision it doesn't have — Burgundy reads 869 where the regulator
// recognises 84 appellations carrying 785 complementary designations.
// Written by both the initial paint and the filter-driven refresh, so it
// lives here rather than being duplicated at either call site.
function renderRegionCount(el, parents, subs) {
if (!el) return;
el.innerHTML = subs
? `${parents}<span class="sub-count">+${subs}</span>`
: `${parents}`;
el.setAttribute('title', fmt(
subs ? LABELS.region_count_title : LABELS.region_count_title_flat,
{ p: parents, s: subs },
));
}

function buildAppellationFacet() {
const el = document.getElementById('facet-appellations');
if (!el) return; // defensive null-guard
Expand All @@ -882,15 +900,21 @@
const openLbl = escapeAttr(fmt(LABELS.open_appellation_aria, { name: rec.name || slug }));
return `<label data-slug="${safeSlug}" data-name="${escapeAttr(searchableText(rec))}"><input type="checkbox" data-key="${safeSlug}"${checked}><span class="name">${nameHtml}</span><button type="button" class="open-aoc" data-slug="${safeSlug}" aria-label="${openLbl}" title="${escapeAttr(LABELS.open_appellation_title)}">→</button></label>`;
}).join('');
let parentCount = 0;
for (const s of slugs) if (!AOCS[s].is_sub_denomination) parentCount++;
const subCount = slugs.length - parentCount;
const safeRegion = escapeAttr(region);
// Checkbox lives outside `<summary>` (sibling of `<details>`,
// not a descendant) so the nested-interactive-in-summary
// accessibility warning doesn't fire. Visual layout is restored
// via `.region-group-wrap`'s flex rule — checkbox + disclosure
// sit in the same row.
html.push(`<div class="region-group-wrap" data-region="${safeRegion}"><input type="checkbox" class="region-select" data-region="${safeRegion}" aria-label="${escapeAttr(LABELS.select_all_aria)}"><details class="region-group" data-region="${safeRegion}"><summary><span class="name">${escapeHtml(label)}</span><span class="count">${slugs.length}</span></summary><div class="region-items">${items}</div></details></div>`);
html.push(`<div class="region-group-wrap" data-region="${safeRegion}"><input type="checkbox" class="region-select" data-region="${safeRegion}" aria-label="${escapeAttr(LABELS.select_all_aria)}"><details class="region-group" data-region="${safeRegion}"><summary><span class="name">${escapeHtml(label)}</span><span class="count" data-parents="${parentCount}" data-subs="${subCount}"></span></summary><div class="region-items">${items}</div></details></div>`);
}
el.innerHTML = html.join('');
el.querySelectorAll('.region-group > summary > .count').forEach(c => {
renderRegionCount(c, +c.dataset.parents, +c.dataset.subs);
});
// Reapply current search visibility (so a tree rebuild during a typed
// query keeps the filtered view).
refreshFacetVisibility('facet-appellations', filters.q);
Expand Down Expand Up @@ -1349,17 +1373,23 @@
const except = new Set(['appellations']);
appEl.querySelectorAll('.region-group').forEach(group => {
let visible = 0;
let visibleParents = 0;
group.querySelectorAll('label').forEach(lbl => {
const inp = lbl.querySelector('input[type=checkbox]'); if (!inp) return;
const slug = inp.dataset.key; const rec = AOCS[slug];
const reachable = rec ? matchesExceptFacets(rec, slug, except) : false;
const hide = !reachable && !inp.checked;
lbl.classList.toggle('facet-unavailable', hide);
if (!hide) visible++;
if (!hide) {
visible++;
if (!(rec && rec.is_sub_denomination)) visibleParents++;
}
});
(group.parentElement || group).classList.toggle('facet-unavailable', visible === 0);
const cs = group.querySelector(':scope > summary > .count');
if (cs) cs.textContent = String(visible);
renderRegionCount(
group.querySelector(':scope > summary > .count'),
visibleParents, visible - visibleParents,
);
});
}
}
Expand Down Expand Up @@ -1758,7 +1788,7 @@
return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}

function frMarker() {

Check warning on line 1791 in scripts/_lib/assets/app.js

View workflow job for this annotation

GitHub Actions / eslint

'frMarker' is defined but never used
return LANG === 'fr'
? ''
: ` <span class="fr-marker" title="${escapeAttr(LABELS.fr_marker_aria)}">${escapeHtml(LABELS.fr_marker)}</span>`;
Expand Down
17 changes: 17 additions & 0 deletions scripts/_lib/fr_wine_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@
"roussette-du-bugey",
)

# Two BOURGOGNE-bassin appellations lie outside Burgundy proper. INAO's
# comité régional is an administrative grouping, not a wine region:
# Côtes du Forez sits in the upper Loire beside Côte roannaise and Côtes
# d'Auvergne (which INAO itself files under VAL DE LOIRE), and Coteaux du
# Lyonnais continues the Beaujolais granite south of Lyon.
_LOIRE_SLUGS: frozenset[str] = frozenset({
"cotes-du-forez",
})

_LYONNAIS_SLUGS: frozenset[str] = frozenset({
"coteaux-du-lyonnais",
})


def derive_wine_region(record: dict[str, Any]) -> str:
"""Return the wine-region bucket for an FR record.
Expand All @@ -69,6 +82,10 @@ def derive_wine_region(record: dict[str, Any]) -> str:
return bassin
slug = record.get("slug") or ""
target = record.get("parent_slug") or slug
if target in _LOIRE_SLUGS:
return "VAL DE LOIRE"
if target in _LYONNAIS_SLUGS:
return "BEAUJOLAIS"
if target in _JURA_SLUGS:
return "JURA"
if target.startswith(_BUGEY_PREFIXES):
Expand Down
8 changes: 8 additions & 0 deletions scripts/_lib/map_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ def build_labels(_: Callable[[str], str]) -> dict[str, str]:
"options_h": _("Options"),
"active_filters_aria": _("Filtres actifs"),
"select_all_aria": _("Tout sélectionner"),
"region_count_title": _(
"{p} appellations · {s} dénominations géographiques complémentaires"
),
"region_count_title_flat": _("{p} appellations"),
"open_appellation_aria": _("Ouvrir la fiche de {name}"),
"open_appellation_title": _("Ouvrir la fiche"),
"show_spirits_label": _("Inclure les spiritueux"),
Expand Down Expand Up @@ -1310,6 +1314,9 @@ def _country_name(cc: str) -> str:
"styles", "styles_simple", "classifications",
"grapes_principal", "grapes_accessory", "grapes_all",
"bbox", "bbox_villages", "geom_source",
# buildAppellationFacet splits each region's count into appellations
# vs complementary geographic designations.
"is_sub_denomination",
})


Expand Down Expand Up @@ -1953,6 +1960,7 @@ def _emit(slug: str, meta: dict, ssr: str, has_card: bool) -> None:
underline on name-hover signals the "go to this one" link affordance. */
.facet label[data-slug] > .name:hover {{ text-decoration:underline; text-decoration-color:#a8c08a; text-underline-offset:2px }}
.facet .count {{ color:#666; font-size:11px; margin-left:4px }}
.facet .count .sub-count {{ opacity:0.65; margin-left:3px }}
.facet .syns {{ color:#888; font-size:11px; font-weight:normal }}
/* Keyboard/SR path to open an appellation panel (the WebGL polygons aren't
DOM-reachable). Subtle until the row is hovered or the button is focused. */
Expand Down
Loading