doxa-maps/documentation/architecture/components.md
doxa-maps-88655c70 9dc4dda8cf
Some checks are pending
build-gate / build (push) Waiting to run
consolidate docs into documentation/ and deployment shells into deployment/
Driver 2026-09-21: 'a folder called deployment which will store the capacitor
folder, wordpress folder and tauri folder', and 'a folder called documentation
to store the contributing folder so docs are in one place'.

docs/ + contributing/ + CONTRIBUTING.md -> documentation/ (three doc homes into
one). capacitor/ + wordpress/ + src-tauri/ -> deployment/.

The public/ -> dist/ rename the Driver asked about is NOT here: after seeing the
measured blast radius (855 tracked files, 54 source refs, and public/ is the
served web-root four consumers point at) the Driver withdrew it - 'keep the
public folder the same'.

Path references were rewritten mechanically (258 across 87 files), but three
classes needed hand fixes because a prefix regex cannot see them:
  - join(ROOT,'docs',...) in 10 internal scripts - a quoted SEGMENT, not a prefix
  - tauri frontendDist '../public' -> '../../public', since src-tauri is now one
    level deeper; check:tauri caught this exactly as the card predicted
  - the WordPress contract test's dirname(__DIR__,2) -> 3, same reason

Also reverted one over-reach: the rewrite renamed a FINDING-CATEGORY label
'docs' in public-hygiene-check.mjs, which is a rule name in its own header, not
a path.

Verified: full build ALL GUARDS PASS, SRI consistent; check:tauri, check:mobile
(cap sync + webDir OK), check:wp-embed, quoted-scripts, capability-citations,
capability-exemplars, library-index, public-hygiene, parse and drop all pass.
2026-09-21 23:23:23 +00:00

98 lines
4.5 KiB
Markdown

# Architecture — the shared component tree
> The simple overviews are in `library/components/README.md` and its subfolder READMEs. This is
> the full catalog and the wiring conventions every shared component follows.
`library/components/` holds the **shared, parameterized Vue components** every app-profile reuses.
Fix one here and every map benefits — never edit a shared component to customize one map.
## Catalog
| File / folder | Role |
|---|---|
| `legends/` | Legend UI — `LegendDesktop.vue`, `LegendMobile.vue`, `LegendRows.vue`, `LegendTools.vue`, `SemanticTreeLegend.vue` |
| `map-controls/` | Per-button toolbar controls (see below) |
| `poster/` | Poster export UI — dialog, preview, wall, slots (see below) |
| `charts/` | Chart components |
| `tables/` | Table components |
| `PeopleGroupDetail.vue` | Feature record card (right-side panel content); renders the live prayer-count bar |
| `ResearchMapFilterPanel.vue` | Research-map filter panel |
| `ResearchMapSideMenu.vue` | Research-map side menu |
| `SideMenuDrawer.vue` | Generic slide-in side-panel primitive |
Per-file roles and gotchas for the whole catalog are in `documentation/reference.md`.
## Conventions (every shared component)
- Every component uses `<script setup>` (Vue 3.5+).
- Every component that renders CSS calls `useShadowStyles()` so styles attach to the shadow root.
- Stores are reached via `inject('mapStore' | 'uiStore' | 'dataStore')` — never a direct
`useXxxStore()` import (LL-010, LL-023).
- The Mapbox token is reached via `inject('mapboxToken')`.
To add a component: follow the conventions above and add a matching entry to the catalog.
## map-controls/
Per-button toolbar controls. Each file is a single, focused button or input that mounts inside the
map's control area. Replaces the older monolithic `MapControlsComponent.vue`.
| File | Role |
|---|---|
| `MapToolbar.vue` | Container that lays out the buttons |
| `MapControlButton.vue` | Base button primitive (icon + tooltip + accessibility) |
| `ZoomInButton.vue` | `+` — `map.zoomIn()` |
| `ZoomOutButton.vue` | `-` — `map.zoomOut()` |
| `LocationButton.vue` | Geolocation — fly to user's position |
| `FullscreenButton.vue` | Toggle fullscreen on the map container |
| `HamburgerButton.vue` | Open/close side menu |
| `ThemeToggleButton.vue` | Light/dark theme toggle (delegates to `uiStore.theme`) |
| `HelpButton.vue` | Opens help |
| `ShareButton.vue` | Share the current view |
| `GeocoderComponent.vue` | Mapbox Geocoder wrapper — Carmen-format aware, shadow-DOM-safe (LL-004, LL-015) |
Conventions:
- Every button takes a `size` prop (`'sm' | 'md' | 'lg'`) and follows the `MapControlButton` shape.
- Click handlers go through `inject('mapStore')` or `getMap()` — never a captured Mapbox instance.
- The geocoder z-index sits above the legend and below popups.
- To add a button: copy `ZoomInButton.vue` and register it in `MapToolbar.vue`.
## poster/
Poster export UI — the dialog and preview surfaces that drive the poster composables. Used by
`research-map.vue` for printable posters.
| File / folder | Role |
|---|---|
| `PosterDialog.vue` | Modal to pick page size, orientation, and title; triggers the export |
| `PosterPreview.vue` | Live preview of the assembled poster; updates as settings change |
| `PosterWall.vue` | Grid of poster previews |
| `slots/` | Composable poster slots (see below) |
Wiring:
- `PosterDialog` reads open/close state from `inject('uiStore')`.
- The preview is fed by `useMapPoster()` (`library/composables/poster/useMapPoster.js`).
- Page sizes come from `library/constants/posterSizes.js`; defaults (font, padding, theme) from
`library/constants/posterDefaults.js`.
- To add a page size, edit `constants/posterSizes.js`.
## poster/slots/
Each slot is a small Vue component that fills one named region of the assembled poster.
| File | Slot region |
|---|---|
| `TitleSlot.vue` | Top title block (text + optional subtitle) |
| `FooterSlot.vue` | Bottom footer region |
| `LegendSlot.vue` | Legend region |
| `NorthArrowSlot.vue` | North arrow |
| `ScaleBarSlot.vue` | Scale bar |
Conventions:
- Each slot is self-contained: it receives data via props and reads no stores.
- Slot dimensions come from the layout returned by `usePosterLayout()`
(`library/composables/poster/usePosterLayout.js`).
- Styling stays inside the component; print CSS lives at the poster-document level (iframe), not in
the slot.
- To add a slot: create the component here, register it in the layout config
(`constants/posterDefaults.js`), and add it to the table above.