courthive-components 3.12.2 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ import { CourtCardConfig } from '../court-card/types';
2
+ import { CourtLayoutCallbacks, CourtLayoutConfig, CourtLayoutCourt, CourtLayoutData } from './types';
3
+ export interface CourtGroup {
4
+ key: string;
5
+ label: string;
6
+ courts: CourtLayoutCourt[];
7
+ }
8
+ export declare function settingOf(court: CourtLayoutCourt): string;
9
+ export declare function surfaceOf(court: CourtLayoutCourt): string;
10
+ /**
11
+ * Split courts into display groups. Order within a group is always courtOrder-first; the ORDER OF
12
+ * THE GROUPS follows first appearance in that sorted list, so a venue whose court 1 is outdoor
13
+ * leads with outdoor rather than with whatever sorts first alphabetically.
14
+ */
15
+ export declare function groupCourts(courts: CourtLayoutCourt[], grouping: CourtLayoutConfig['grouping']): CourtGroup[];
16
+ /** "8 outdoor hard, 4 indoor hard" — the breakdown, most common first. */
17
+ export declare function summarizeCourts(courts: CourtLayoutCourt[]): string;
18
+ export declare function buildCourtLayout(data: CourtLayoutData, config?: Partial<CourtLayoutConfig>, callbacks?: CourtLayoutCallbacks): HTMLElement;
19
+ /**
20
+ * Card config for a cell, given what the group heading already says. Repeating "Outdoor" on every
21
+ * card inside a group headed OUTDOOR is noise, so the redundant corner badge is dropped — but only
22
+ * the redundant one. `floodlit` is never implied by grouping and always survives.
23
+ */
24
+ export declare function cellCardConfig(grouping: CourtLayoutConfig['grouping']): Partial<CourtCardConfig> | undefined;
@@ -0,0 +1,3 @@
1
+ import { CourtLayoutConfig } from './types';
2
+ export declare const DEFAULT_COURT_LAYOUT_CONFIG: CourtLayoutConfig;
3
+ export declare function mergeCourtLayoutConfig(override?: Partial<CourtLayoutConfig>): CourtLayoutConfig;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Court Layout — public surface.
3
+ *
4
+ * A venue's courts as an ordered, grouped grid of court cards. No map, no coordinates, no optional
5
+ * dependency — see venue-locator for the map.
6
+ */
7
+ export { buildCourtLayout, cellCardConfig, groupCourts, settingOf, summarizeCourts, surfaceOf } from './buildCourtLayout';
8
+ export { DEFAULT_COURT_LAYOUT_CONFIG, mergeCourtLayoutConfig } from './defaultConfig';
9
+ export type { CourtGroup } from './buildCourtLayout';
10
+ export type { CourtLayoutCallbacks, CourtLayoutConfig, CourtLayoutCourt, CourtLayoutData, CourtLayoutGrouping } from './types';
@@ -0,0 +1,8 @@
1
+ export declare const clRootStyle: () => string;
2
+ export declare const clSummaryStyle: () => string;
3
+ export declare const clSummaryCountStyle: () => string;
4
+ export declare const clGroupStyle: () => string;
5
+ export declare const clGroupHeadingStyle: () => string;
6
+ export declare const clGroupCountStyle: () => string;
7
+ export declare const clGridStyle: () => string;
8
+ export declare const clEmptyStyle: () => string;
@@ -0,0 +1,46 @@
1
+ import { CourtSport } from '../courts/courtSvgUtil';
2
+ /**
3
+ * A court as it arrives from the factory / facilities registry. Intentionally loose — it is passed
4
+ * to `mapCourtToCardData`, which does the normalising, so a caller can hand over a TODS `Court`
5
+ * untouched.
6
+ */
7
+ export interface CourtLayoutCourt {
8
+ courtId?: string;
9
+ courtName: string;
10
+ courtAbbreviation?: string;
11
+ indoorOutdoor?: string;
12
+ surfaceCategory?: string;
13
+ surfaceType?: string;
14
+ floodlit?: boolean;
15
+ courtOrder?: number;
16
+ [key: string]: unknown;
17
+ }
18
+ export interface CourtLayoutData {
19
+ venueId?: string;
20
+ venueName?: string;
21
+ courts: CourtLayoutCourt[];
22
+ }
23
+ /**
24
+ * How the grid is divided.
25
+ * 'none' — one grid, ordered by courtOrder
26
+ * 'setting' — indoor vs outdoor (the scheduling-relevant split: rain, lights, curfew)
27
+ * 'surface' — hard vs clay vs grass …
28
+ * 'both' — setting, then surface within it
29
+ */
30
+ export type CourtLayoutGrouping = 'none' | 'setting' | 'surface' | 'both';
31
+ export interface CourtLayoutConfig {
32
+ grouping: CourtLayoutGrouping;
33
+ /** Sport whose court outline each card draws. */
34
+ sport: CourtSport;
35
+ /** Heading above each group, with its court count. */
36
+ showGroupHeadings: boolean;
37
+ /** One-line "12 courts · 8 outdoor hard, 4 indoor hard" summary above the grid. */
38
+ showSummary: boolean;
39
+ /** Minimum card width; the grid auto-fills to the container. Any CSS length. */
40
+ minCardWidth: string;
41
+ /** Render nothing but a message when `courts` is empty. */
42
+ emptyMessage: string;
43
+ }
44
+ export interface CourtLayoutCallbacks {
45
+ onCourtClick?: (court: CourtLayoutCourt) => void;
46
+ }
@@ -0,0 +1,23 @@
1
+ import { VenueLocatorCallbacks, VenueLocatorConfig, VenueLocatorCourt, VenueLocatorData, VenueLocatorTileLayer, VenueLocatorView } from './types';
2
+ export declare function hasVenueGeo(data: VenueLocatorData): boolean;
3
+ /** Re-exported so the locator's public surface stays stable; the comparator is shared with court-layout. */
4
+ export declare function sortVenueCourts(courts: VenueLocatorCourt[]): VenueLocatorCourt[];
5
+ export declare function describeCourt(court: VenueLocatorCourt): string;
6
+ export declare function buildVenueLocator(data: VenueLocatorData, config?: Partial<VenueLocatorConfig>, callbacks?: VenueLocatorCallbacks): HTMLElement;
7
+ /** Remove the Leaflet instance and observers for a locator built by buildVenueLocator. */
8
+ export declare function destroyVenueLocator(root: HTMLElement): void;
9
+ /**
10
+ * Leaflet ships its own stylesheet, and this component deliberately does not import it (leaflet is
11
+ * an optional peerDependency, so a static CSS import would make it mandatory). Without it Leaflet's
12
+ * panes are unpositioned and the map paints as a broken band of tiles — a failure that looks like a
13
+ * component bug and is silent otherwise. `.leaflet-container` is `position: relative` in that
14
+ * stylesheet, so anything else means it never loaded.
15
+ */
16
+ export declare function leafletCssMissing(containerPosition: string | undefined): boolean;
17
+ export declare function isDarkTheme(): boolean;
18
+ /**
19
+ * Which tile source a given view should render right now. The street view swaps to a real dark
20
+ * basemap under a dark theme — filtering light tiles to fake one produces a false-colour negative.
21
+ * Satellite never swaps: there is no dark equivalent of a photograph.
22
+ */
23
+ export declare function resolveTileLayer(cfg: VenueLocatorConfig, view: VenueLocatorView, dark: boolean): VenueLocatorTileLayer;
@@ -0,0 +1,3 @@
1
+ import { VenueLocatorConfig } from './types';
2
+ export declare const DEFAULT_VENUE_LOCATOR_CONFIG: VenueLocatorConfig;
3
+ export declare function mergeVenueLocatorConfig(override?: Partial<VenueLocatorConfig>): VenueLocatorConfig;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Venue Locator — public surface.
3
+ *
4
+ * Requires the optional peerDependency `leaflet` (and its stylesheet) in the host:
5
+ * pnpm add leaflet
6
+ * import 'leaflet/dist/leaflet.css';
7
+ * Without it the component still renders — header + court list, with a "Map unavailable" zone.
8
+ */
9
+ export { buildVenueLocator, destroyVenueLocator, describeCourt, hasVenueGeo, isDarkTheme, leafletCssMissing, resolveTileLayer, sortVenueCourts } from './buildVenueLocator';
10
+ export { mapVenueToLocatorData } from './mapVenue';
11
+ export { DEFAULT_VENUE_LOCATOR_CONFIG, mergeVenueLocatorConfig } from './defaultConfig';
12
+ export type { VenueLocatorCallbacks, VenueLocatorConfig, VenueLocatorCourt, VenueLocatorData, VenueLocatorLayout, VenueLocatorTileLayer, VenueLocatorView } from './types';
@@ -0,0 +1,2 @@
1
+ import { VenueLocatorData } from './types';
2
+ export declare function mapVenueToLocatorData(venue: any): VenueLocatorData;
@@ -0,0 +1,25 @@
1
+ export declare const vlRootStyle: () => string;
2
+ export declare const vlHeaderStyle: () => string;
3
+ export declare const vlTitleStyle: () => string;
4
+ export declare const vlAddressStyle: () => string;
5
+ export declare const vlNotesStyle: () => string;
6
+ export declare const vlBodyStyle: () => string;
7
+ export declare const vlBodyBelowStyle: () => string;
8
+ export declare const vlMapWrapStyle: () => string;
9
+ export declare const vlMapStyle: () => string;
10
+ export declare const vlMapInvertibleStyle: () => string;
11
+ export declare const vlToggleStyle: () => string;
12
+ export declare const vlToggleButtonStyle: () => string;
13
+ export declare const vlFallbackStyle: () => string;
14
+ export declare const vlFallbackIconStyle: () => string;
15
+ export declare const vlCourtsStyle: () => string;
16
+ export declare const vlCourtsTitleStyle: () => string;
17
+ export declare const vlCourtListStyle: () => string;
18
+ export declare const vlCourtStyle: () => string;
19
+ export declare const vlCourtClickableStyle: () => string;
20
+ export declare const vlCourtNameStyle: () => string;
21
+ export declare const vlCourtMetaStyle: () => string;
22
+ export declare const vlPipStyle: () => string;
23
+ export declare const vlMarkerStyle: () => string;
24
+ export declare const vlPopupTitleStyle: () => string;
25
+ export declare const vlPopupAddressStyle: () => string;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Venue Locator — Type Definitions
3
+ *
4
+ * Flat data + JSON-serializable config consumed by buildVenueLocator.
5
+ * Mirrors the venue-card / court-card pattern.
6
+ *
7
+ * Scope: ONE venue on a real map — "where is this club". Courts are LISTED beside the
8
+ * map rather than plotted on it, because the facility registry carries coordinates on the
9
+ * facility, not per court (`facility_courts` has no latitude/longitude). If per-court geo
10
+ * ever lands, `VenueLocatorCourt.latitude/longitude` is where it plugs in.
11
+ */
12
+ export interface VenueLocatorCourt {
13
+ courtId: string;
14
+ courtName: string;
15
+ /** INDOOR / OUTDOOR / undefined (unknown). */
16
+ indoorOutdoor?: 'INDOOR' | 'OUTDOOR';
17
+ /** Surface category (HARD/CLAY/GRASS/CARPET/...) — raw upper-case. */
18
+ surfaceCategory?: string;
19
+ /** True when the court has floodlights. */
20
+ floodlit?: boolean;
21
+ /** Ordering within the venue (TODS courtOrder). Sorted ascending when present. */
22
+ courtOrder?: number;
23
+ /**
24
+ * Per-court coordinates. Unused today — the registry stores geo on the facility only.
25
+ * When present the court is plotted as its own marker instead of folding into the venue pin.
26
+ */
27
+ latitude?: number;
28
+ longitude?: number;
29
+ }
30
+ export interface VenueLocatorData {
31
+ venueId: string;
32
+ venueName: string;
33
+ venueAbbreviation?: string;
34
+ /** Pre-formatted "123 Main St, City, REGION" — rendered under the title and in the popup. */
35
+ addressFormatted?: string;
36
+ /** Venue coordinates. Without both, the component renders its no-geo state. */
37
+ latitude?: number;
38
+ longitude?: number;
39
+ /** Courts at this venue, listed beside the map. */
40
+ courts?: VenueLocatorCourt[];
41
+ /** Free-form note rendered under the address. */
42
+ notes?: string;
43
+ }
44
+ /**
45
+ * A tile layer definition. Deliberately the same shape TMX already uses in
46
+ * `config/locationConfig.ts` so a consumer can pass `env.leaflet.map` /
47
+ * `env.leaflet.satellite` straight through without translation.
48
+ */
49
+ export interface VenueLocatorTileLayer {
50
+ tileLayer: string;
51
+ attribution: string;
52
+ maxZoom?: number;
53
+ }
54
+ export type VenueLocatorView = 'map' | 'satellite';
55
+ /** Where the court list sits relative to the map. */
56
+ export type VenueLocatorLayout = 'side' | 'below' | 'none';
57
+ export interface VenueLocatorConfig {
58
+ /** Tile sources. Defaults mirror TMX's leafletConfig defaults (OSM + Esri imagery). */
59
+ tiles: Record<VenueLocatorView, VenueLocatorTileLayer>;
60
+ /**
61
+ * Street tiles used under a dark theme. Kept OUT of `tiles` on purpose: `tiles` is keyed by
62
+ * VenueLocatorView and drives the view toggle, and a dark basemap is the same view rendered
63
+ * differently, not a third thing to choose. Set to `null` to force the CSS-filter path.
64
+ */
65
+ darkTileLayer: VenueLocatorTileLayer | null;
66
+ /** Which tile source to show first. */
67
+ view: VenueLocatorView;
68
+ /** Render the map/satellite toggle. Hidden when only one tile source is configured. */
69
+ showViewToggle: boolean;
70
+ /** Initial zoom on the venue. */
71
+ zoom: number;
72
+ /** Map height as any CSS length. */
73
+ height: string;
74
+ /** Court list placement. */
75
+ courtsLayout: VenueLocatorLayout;
76
+ /** Show the venue title + address block above the map. */
77
+ showHeader: boolean;
78
+ /** Open a popup on the venue marker with name + address. */
79
+ showPopup: boolean;
80
+ /** Allow scroll-wheel zoom. Off by default so the map doesn't hijack page scroll. */
81
+ scrollWheelZoom: boolean;
82
+ /**
83
+ * Dark-theme handling for the street view.
84
+ * 'auto' — swap to `darkTileLayer` when the theme is dark; if no dark layer is configured,
85
+ * fall back to inverting the street tiles in CSS (legible, but not pretty).
86
+ * 'never' — always render the light street tiles.
87
+ * Satellite is never darkened either way: inverting imagery yields a false-colour photo, and
88
+ * there is no dark equivalent of a photograph.
89
+ */
90
+ darkTiles: 'auto' | 'never';
91
+ }
92
+ export interface VenueLocatorCallbacks {
93
+ /** Venue marker (or its popup) clicked. */
94
+ onVenueClick?: (data: VenueLocatorData) => void;
95
+ /** A court in the list was clicked. */
96
+ onCourtClick?: (court: VenueLocatorCourt, data: VenueLocatorData) => void;
97
+ /** Tile view switched. */
98
+ onViewChange?: (view: VenueLocatorView) => void;
99
+ /**
100
+ * Leaflet could not be loaded or the map failed to initialize. The component has already
101
+ * rendered its fallback (header + court list) by the time this fires — it is a signal for
102
+ * the host to log, not an error the host must handle to keep the UI coherent.
103
+ */
104
+ onMapUnavailable?: (reason: Error) => void;
105
+ }