diff --git a/docs/memory/component-registry.md b/docs/memory/component-registry.md index 50027f7..c106e15 100644 --- a/docs/memory/component-registry.md +++ b/docs/memory/component-registry.md @@ -28,7 +28,7 @@ duplicates) and MUST update it after completing one. | Card | done | elevated, outlined × default, compact, none padding × interactive × selected | card.borderRadius/padding/shadow/border/background, color.surface.raised/subtle/warm, color.border.default/brand, shadow.md/lg | Content container. Elevated (shadow) or outlined (border). Interactive adds hover bg fill + shadow lift. Selected adds brand border + warm bg. Three padding presets. | | Switch | done | bordered style × checked, unchecked, disabled | switch.track.width/height/borderRadius, switch.thumb.size, color.interactive.*, color.neutral.400 | Toggle for add-ons/options. Wraps MUI Switch. Bordered pill, brand.500 fill when active. From Parsons 1.0 Figma Style One. | | Radio | done | checked, unchecked, disabled | radio.size/dotSize, color.interactive.*, color.neutral.400 | Single-select option. Wraps MUI Radio. Brand.500 fill when selected. From Parsons 1.0 Figma. | -| MapPin | done | name+price × verified, unverified | mapPin.paddingX/borderRadius/nub.size, color.brand-200/700, color.neutral-100-800 | Two-line label map marker: name (bold, required, truncated 180px) + "From $X" (centred, semibold). Verified = promoted brand palette (brand-700 bg, white text, brand-200 price). Unverified = neutral grey pill (neutral-100 bg, neutral-800 text). No active state — selection handled at the organism level (ProviderMap swaps pin → MapPopup on click). Pure CSS. role="button" + keyboard + focus ring. Name-only / price-only variants dropped in D042 (production providers always have both). | +| MapPin | done | name+price × verified, unverified | mapPin.paddingX/borderRadius/nub.size, color.brand-200/700, color.neutral-100-800 | Two-line label map marker: [verified tick (inline SVG) +] name (bold, required, truncated 210px) + "From $X" (centred, semibold). Verified providers get a Material Verified icon to the left of the name, same colour as the name text. Verified = promoted brand palette (brand-700 bg, white text, brand-200 price). Unverified = neutral grey pill (neutral-100 bg, neutral-800 text). No active state — selection handled at the organism level (ProviderMap swaps pin → MapPopup on click, or emits via `onActiveChange` when `externalisePopups` is set). Pure CSS + inline SVG (no MUI icon — mounted via createRoot outside ThemeProvider). role="button" + keyboard + focus ring. Name-only / price-only variants dropped in D042. | | ClusterMarker | done | count × verified (promoted), unverified (neutral) | color.brand-700, color.neutral-100-800, mapPin.nub.size | Circular 36px count badge for pin clusters. `hasVerified` flag drives the palette (if any provider in the cluster is verified, use the promoted brand-700 palette; else neutral grey). Same nub treatment + shadow tokens as MapPin for visual cohesion. Pure CSS + SVG. role="button" + keyboard + focus ring. Designed as the `render`-ed output of `@googlemaps/markerclusterer`. See D043. | | ColourToggle | planned | inactive, hover, active, locked × single, two-colour × desktop, mobile | | Circular colour swatch picker for products. Custom component. Deferred until product detail organisms. | | Slider | planned | single, range × desktop, mobile | | Price range filter. Wraps MUI Slider. Deferred until search/filtering molecules. | @@ -76,7 +76,7 @@ duplicates) and MUST update it after completing one. | ArrangementForm | planned | StepIndicator + ServiceSelector + AddOnOption + Button + Typography | Multi-step arrangement wizard. Deferred — build remaining atoms/molecules first. | | Navigation | done | AppBar + Link + IconButton + Button + Divider + Drawer | Responsive site header. Desktop: logo left, links right, optional CTA. Mobile: hamburger + drawer with nav items, CTA, help footer. Sticky, grey surface bg (surface.subtle). Real FA logo from brandassets/. Maps to Figma Main Nav (14:108) + Mobile Header (2391:41508). | | Footer | done | Link × n + Typography + Divider + Container + Grid | Light grey (surface.subtle) site footer — matches header. Logo + tagline + contact (phone/email) + link group columns + legal bar. Semantic HTML (footer, nav, ul). Critique: 38/40 (Excellent). | -| ProviderMap | done | MapPin + ClusterMarker + MapPopup + ClusterPopup + `@vis.gl/react-google-maps` + `@googlemaps/markerclusterer` | Google Maps provider map with clustering. **Individual pins**: `MapPin` per provider — click morphs it into a `MapPopup` at the same coord. **Clusters**: pins within 70px of each other collapse into a `ClusterMarker` (count badge) *only while zoomed out at level 13 or below* — past that, every pin shows individually. Click a cluster → `ClusterPopup` list at the cluster centroid, verified-first. **Click a row → map pans + zooms to that provider's coords (zoom 15) and opens their `MapPopup`**; cluster state cleared (no back-to-list). Map-background click reverts to pins. Markers are rendered imperatively (via `createRoot` into AdvancedMarker content elements) so `markerclusterer` can group them; popup layer remains declarative React. `selectedProviderId` force-opens a popup for external selection. Subtle empty state when no API key or no providers have coords (no throw). API key read from `VITE_GOOGLE_MAPS_API_KEY`. Desktop-only for now; mobile map sheet deferred. See D041, D042, D043, D044. | +| ProviderMap | done | MapPin + ClusterMarker + MapPopup + ClusterPopup + `@vis.gl/react-google-maps` + `@googlemaps/markerclusterer` | Google Maps provider map with clustering. **Individual pins**: `MapPin` per provider — click morphs it into a `MapPopup` at the same coord. **Clusters**: pins within 70px of each other collapse into a `ClusterMarker` (count badge) *only while zoomed out at level 13 or below* — past that, every pin shows individually. Click a cluster → `ClusterPopup` list at the cluster centroid, verified-first. **Click a row → map pans + zooms to that provider's coords (zoom 15) and opens their `MapPopup`**; cluster state cleared (no back-to-list). Map-background click reverts to pins. Markers are rendered imperatively (via `createRoot` into AdvancedMarker content elements) so `markerclusterer` can group them; popup layer remains declarative React. `selectedProviderId` force-opens a popup for external selection. **Externalisable popups (D047):** opt-in `externalisePopups` prop suppresses the internal MapPopup/ClusterPopup rendering; `onActiveChange` callback emits the active provider/cluster/exiting state; imperative ref exposes `ProviderMapHandle` with `clearActive()` + `drillIntoProvider(id)` so external callers (e.g. the mobile bottom drawer) can render the popup content themselves. `forwardRef` type is `ProviderMapHandle`, not `HTMLDivElement`. Subtle empty state when no API key or no providers have coords (no throw). API key read from `VITE_GOOGLE_MAPS_API_KEY`. See D041, D042, D043, D044, D047. | ## Templates @@ -89,8 +89,8 @@ duplicates) and MUST update it after completing one. | Component | Status | Composed of | Notes | |-----------|--------|-------------|-------| | IntroStep | done | WizardLayout (centered-form) + ToggleButtonGroup × 2 + Collapse + Typography + Button + Divider | Wizard step 1 — entry point. forWhom (Myself/Someone else) + hasPassedAway (Yes/No) with progressive disclosure. Auto-sets hasPassedAway="no" for "Myself". `