react-listing-engine 0.6.4 → 0.6.6

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.
Files changed (48) hide show
  1. package/README.md +54 -65
  2. package/dist/chunk-OXSYNZAJ.js +2 -0
  3. package/dist/chunk-PE27NATS.cjs +2 -0
  4. package/dist/index.cjs +2 -2
  5. package/dist/index.d.cts +95 -16
  6. package/dist/index.d.ts +95 -16
  7. package/dist/index.js +1 -1
  8. package/dist/{listing-app-wmMuDlVU.d.ts → listing-app-4Vs_SMNl.d.cts} +146 -26
  9. package/dist/{listing-app-DJbvx3oe.d.cts → listing-app-CkEGdUV9.d.ts} +146 -26
  10. package/dist/map-provider.interface-DT-v1plm.d.cts +67 -0
  11. package/dist/map-provider.interface-DT-v1plm.d.ts +67 -0
  12. package/dist/maps/google/index.d.cts +1 -2
  13. package/dist/maps/google/index.d.ts +1 -2
  14. package/dist/styled/index.cjs +1 -1
  15. package/dist/styled/index.d.cts +7 -10
  16. package/dist/styled/index.d.ts +7 -10
  17. package/dist/styled/index.js +1 -1
  18. package/dist/testing/index.d.cts +1 -2
  19. package/dist/testing/index.d.ts +1 -2
  20. package/package.json +3 -14
  21. package/dist/chunk-2VUHLHHX.cjs +0 -2
  22. package/dist/chunk-35KYEBAC.js +0 -2
  23. package/dist/chunk-5FBI2WIM.js +0 -2
  24. package/dist/chunk-6PZHSHU3.js +0 -2
  25. package/dist/chunk-CGT3RHJ7.js +0 -2
  26. package/dist/chunk-CHNUE46K.cjs +0 -2
  27. package/dist/chunk-EYXV5OMY.cjs +0 -2
  28. package/dist/chunk-LCQZIWBO.cjs +0 -2
  29. package/dist/chunk-R3FX2V3N.js +0 -2
  30. package/dist/chunk-RXHR66FS.cjs +0 -2
  31. package/dist/components-provider-CF0Mo0B8.d.cts +0 -120
  32. package/dist/components-provider-CjMxkxrP.d.ts +0 -120
  33. package/dist/entity-adapter.interface-BDgbSxhq.d.cts +0 -33
  34. package/dist/entity-adapter.interface-BDgbSxhq.d.ts +0 -33
  35. package/dist/listing-config-options.interface-ZJYY_ZvR.d.cts +0 -12
  36. package/dist/listing-config-options.interface-ZJYY_ZvR.d.ts +0 -12
  37. package/dist/map-provider.interface-BMnwW3ob.d.cts +0 -37
  38. package/dist/map-provider.interface-BxexMT3X.d.ts +0 -37
  39. package/dist/presets/rental/index.cjs +0 -2
  40. package/dist/presets/rental/index.d.cts +0 -266
  41. package/dist/presets/rental/index.d.ts +0 -266
  42. package/dist/presets/rental/index.js +0 -2
  43. package/dist/shadcn/index.cjs +0 -2
  44. package/dist/shadcn/index.d.cts +0 -261
  45. package/dist/shadcn/index.d.ts +0 -261
  46. package/dist/shadcn/index.js +0 -2
  47. package/dist/url-sync.controller-CsU_QxoS.d.cts +0 -89
  48. package/dist/url-sync.controller-DX67JivW.d.ts +0 -89
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # react-listing-engine
2
2
 
3
- Headless, composable listing engine for React: filterable list + Google-Maps multi-layer map, with pluggable data adapters, a filter/dataset registry, injectable components, and a shadcn-compatible styled adapter. Drop into property search, business directories, store locators, or any map+list browse experience.
3
+ Headless, composable listing engine for React: filterable list + Google-Maps multi-layer map, with pluggable data adapters, a filter/dataset registry, injectable components, and a Tailwind-free styled adapter. Drop into property search, business directories, store locators, or any map+list browse experience.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Headless core + React bindings.** `~/core` is framework-agnostic TypeScript (no React import); `react-listing-engine` adds hooks and compound components on top. The styled `/shadcn` adapter is opt-in.
7
+ - **Headless core + React bindings.** `~/core` is framework-agnostic TypeScript (no React import); `react-listing-engine` adds hooks and compound components on top. The Tailwind-free styled adapter (the turnkey `ListingApp` + `react-listing-engine/styles.css`) is opt-in.
8
8
  - **Generic over any entity.** Implement `EntityAdapter<TEntity, TFilters>` (`list`, `getPoints`, optional `getById`) against your own API — the engine never assumes a shape. URL serialization is a separate concern, handled by `UrlSyncController` (see `withUrlSync`), not the adapter.
9
9
  - **Fully customizable filters.** `FilterRegistry` supports `add` / `remove` / `reorder` / `replace` at runtime, not just at setup.
10
10
  - **Multiple marker layers.** `DatasetRegistry` composes any number of layers (properties, businesses, …) onto one map; each is its own `DatasetDefinition` with its own adapter and marker renderer.
11
11
  - **Google Maps behind a provider seam.** `MapProvider` is an interface — `googleProvider({ apiKey })` is the shipped implementation. The API key always comes from your own config; there is no hardcoded fallback.
12
- - **Component injection.** `ListingComponentsProvider` overrides any UI slot (`Card`, `Marker`, `Popup`, `Sidebar`, `FilterPanel`, `Search`, `Empty`, `Loading`, `ResultHeader`, `Toolbar`) — anything not provided falls back to an unstyled (or, with `/shadcn`, styled) default. `Marker` and `Popup` are defined on `IListingComponents` but **not yet wired into the map's render output** — `ListingMap` currently renders markers via each dataset's `marker.iconUrl`/`marker.element` only; injecting `Marker`/`Popup` today has no visible effect.
12
+ - **Component injection.** `ListingComponentsProvider` overrides any UI slot (`Card`, `Marker`, `Popup`, `Sidebar`, `FilterPanel`, `Search`, `Empty`, `Loading`, `ResultHeader`, `Toolbar`) — anything not provided falls back to an unstyled (or, with the `/styled` adapter, styled) default. `Marker` and `Popup` are defined on `IListingComponents` but **not yet wired into the map's render output** — `ListingMap` currently renders markers via each dataset's `marker.iconUrl`/`marker.element` only; injecting `Marker`/`Popup` today has no visible effect.
13
13
  - **URL sync.** `UrlSyncController` (via `withUrlSync`) keeps filters in sync with your router/history without the engine depending on a specific router.
14
14
  - **Dual ESM + CJS, full TypeScript types.** `sideEffects: false`, tree-shakeable subpaths, `'use client'` banners for Next.js App Router.
15
15
 
@@ -27,54 +27,53 @@ pnpm add react-listing-engine
27
27
 
28
28
  ## Quickstart
29
29
 
30
- Minimal setup using the shipped rental preset one dataset, the shipped filters, Google Maps, and the styled `/shadcn` layout:
30
+ Minimal setup one dataset, one filter, and Google Maps, rendered by the turnkey `ListingApp` (it wires the provider, the styled defaults, and the layout together). Define your own entity + filter shapes and back them with an `EntityAdapter`:
31
31
 
32
32
  ```tsx
33
33
  import {
34
- composeListingProviders,
35
- ListingProvider,
36
- withDataset,
37
- withFilters,
38
- withMap,
34
+ ListingApp,
35
+ type EntityAdapter,
36
+ type FilterControlProps,
39
37
  } from 'react-listing-engine';
40
- import { ListingComponentsProviderWithDefaults, ListingLayout } from 'react-listing-engine/shadcn';
41
- import { googleProvider } from 'react-listing-engine/maps/google';
42
- import {
43
- propertiesDataset,
44
- withRentalFilters,
45
- type PropertiesApiPort,
46
- type PropertyEntity,
47
- type RentalFilters,
48
- } from 'react-listing-engine/presets/rental';
38
+ import 'react-listing-engine/styles.css';
39
+
40
+ interface Property { id: string; title: string; price: number; lat: number; lng: number }
41
+ interface Filters { q?: string }
42
+
43
+ // Your API-backed adapter: `list(filters, page)` for the results, `getPoints(filters, bounds)`
44
+ // for the map pins. The engine never makes an HTTP call itself.
45
+ declare const adapter: EntityAdapter<Property, Filters>;
49
46
 
50
- declare const propertiesApi: PropertiesApiPort; // your implementation, calling your real API
47
+ const SearchControl = ({ onChange, value }: FilterControlProps<string>) => (
48
+ <input onChange={e => onChange(e.target.value)} placeholder="Search" value={value} />
49
+ );
51
50
 
52
51
  export function PropertySearch() {
53
52
  return (
54
- <ListingProvider<PropertyEntity, RentalFilters>
55
- {...composeListingProviders<RentalFilters>(
56
- withMap<RentalFilters>(googleProvider({ apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY! })),
57
- withDataset(propertiesDataset(propertiesApi)),
58
- withFilters(withRentalFilters()),
59
- )}
60
- >
61
- <ListingComponentsProviderWithDefaults>
62
- <ListingLayout />
63
- </ListingComponentsProviderWithDefaults>
64
- </ListingProvider>
53
+ <ListingApp<Property, Filters>
54
+ datasets={[{ id: 'properties', adapter, marker: {} }]}
55
+ filters={reg =>
56
+ reg.add<string>({
57
+ key: 'q',
58
+ order: 0,
59
+ render: SearchControl,
60
+ toParams: v => ({ q: v || undefined }),
61
+ fromParams: f => f.q ?? '',
62
+ })
63
+ }
64
+ map={{ apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY!, mapId: 'YOUR_MAP_ID' }}
65
+ />
65
66
  );
66
67
  }
67
68
  ```
68
69
 
69
- `propertiesApi` only needs to implement `PropertiesApiPort` (`list`, `search`, optional `getById`) against your own backend — the preset never makes an HTTP call itself.
70
-
71
70
  ## Customization tiers
72
71
 
73
72
  The engine is layered so you can go as deep as you need and stop:
74
73
 
75
- 1. **Data** — implement `EntityAdapter<TEntity, TFilters>` (or a preset's narrower port, e.g. `PropertiesApiPort`) against your own API. Nothing in the engine assumes a specific backend or entity shape.
74
+ 1. **Data** — implement `EntityAdapter<TEntity, TFilters>` against your own API. Nothing in the engine assumes a specific backend or entity shape.
76
75
  2. **Structure** — compose the provider with `composeListingProviders(withMap(...), withDataset(...), withFilters(...), withUrlSync(...), withInitialFilters(...), withPrimaryDataset(...), withConfig(...))`. Mutate filters via `FilterRegistry` (`add`/`remove`/`reorder`/`replace`) and layers via `DatasetRegistry` (`add`/`get`/`has`/`list`/`visibleIds`).
77
- 3. **Presentation** — swap any slot via `ListingComponentsProvider` (or start from `ListingComponentsProviderWithDefaults` for the `/shadcn` look and override only what you need). Injectable slots: `Card`, `Marker`, `Popup`, `Sidebar`, `FilterPanel`, `Search`, `Empty`, `Loading`, `ResultHeader`, `Toolbar`. `Marker`/`Popup` are defined but not yet wired into the map's render output (see the Features note above) — every other slot renders as described.
76
+ 3. **Presentation** — swap any slot via `ListingComponentsProvider` (or start from `ListingComponentsProviderWithDefaults` for the styled look and override only what you need). Injectable slots: `Card`, `Marker`, `Popup`, `Sidebar`, `FilterPanel`, `Search`, `Empty`, `Loading`, `ResultHeader`, `Toolbar`. `Marker`/`Popup` are defined but not yet wired into the map's render output (see the Features note above) — every other slot renders as described.
78
77
  4. **Layout** — skip `ListingLayout` entirely and arrange the structure-only compound components yourself: `ListingList`, `ListingMap`, `ListingFilters`, `ListingResultHeader`, `ListingToolbar`, `ListingPagination`.
79
78
 
80
79
  ## Google Maps setup
@@ -93,40 +92,41 @@ const map = googleProvider({
93
92
  - `loaderOptions` (optional) forwards extra `@googlemaps/js-api-loader` config (language, region, preloaded libraries); `key` is always taken from `apiKey` and can't be overridden there.
94
93
  - `DatasetDefinition.clustering` (`{ maxZoom }` or `false`) is implemented by the shipped `googleProvider`: when set, that layer's markers are wrapped in a `MarkerClusterer` (from the OPTIONAL peer dependency `@googlemaps/markerclusterer` — install it to enable clustering; without it, `googleProvider` warns once and falls back to plain, unclustered markers, no crash) with a custom renderer that draws a solid red circle showing the cluster's count. No Mapbox provider ships either; `MapProvider` is the seam if you want to add one.
95
94
 
96
- ## Nearby businesses / multiple layers
95
+ ## Multiple marker layers
97
96
 
98
- A second marker layer composes onto the same map with one more `withDataset` call:
97
+ A second marker layer (nearby businesses, schools, transit, …) composes onto the same map with one more `withDataset` call — its own `EntityAdapter` and marker:
99
98
 
100
99
  ```ts
101
- import { nearbyBusinessesDataset } from 'react-listing-engine/presets/rental';
102
-
103
- withDataset(
104
- nearbyBusinessesDataset(businessesApi, {
105
- icons: { grocery: '/icons/grocery.png', schools: '/icons/schools.png' },
106
- }),
107
- )
100
+ withDataset({
101
+ id: 'businesses',
102
+ adapter: businessesAdapter, // your EntityAdapter for the second layer
103
+ marker: { iconUrl: b => categoryIcons[b.category] },
104
+ })
108
105
  ```
109
106
 
110
- `BusinessCategory` is an open `string` type, not a closed union — new categories are just new entries in your `icons`/`categories` data, no library change required. `KNOWN_BUSINESS_CATEGORIES` ships as a seeded starter list for building category pickers. Additional datasets are map-only layers; only the primary dataset (the first one added, or whichever id you pass as `primaryDatasetId` via `withPrimaryDataset`) drives the results list and pagination — implementing `list` on a secondary dataset's adapter has no effect on the list/pagination unless that dataset is made primary.
107
+ Additional datasets are **map-only** layers: only the primary dataset (the first one added, or whichever id you pass as `primaryDatasetId` via `withPrimaryDataset`) drives the results list and pagination — implementing `list` on a secondary dataset's adapter has no effect on the list/pagination unless that dataset is made primary.
111
108
 
112
- **Filter shape caveat.** `ListingEngine.loadPoints` calls every visible layer's `getPoints` with the *primary* dataset's `TFilters` — there's a single filter state per engine, not one per layer. A secondary dataset whose filters have a genuinely different shape (e.g. `nearbyBusinessesDataset`'s `BusinessFilters` vs. the primary `RentalFilters`) is **not** driven by the engine's filter state at all; the engine's filter object simply isn't in `BusinessFilters`' shape by the time it reaches that layer's adapter. Filter such a layer through the dataset factory's own `opts` instead — e.g. `nearbyBusinessesDataset(api, { categories })` which closes over the restriction at construction time rather than reading it from `useListingFilters()`.
109
+ **Filter-shape caveat.** `ListingEngine.loadPoints` calls every visible layer's `getPoints` with the *primary* dataset's `TFilters` — there's a single filter state per engine, not one per layer. A secondary dataset whose filters have a genuinely different shape is **not** driven by the engine's filter state at all; the engine's filter object simply isn't in that shape by the time it reaches the layer's adapter. Filter such a layer at construction instead — close over the restriction in the adapter you hand to `withDataset` rather than reading it from `useListingFilters()`.
113
110
 
114
111
  ## Styled adapter
115
112
 
116
- `react-listing-engine/shadcn` ships a full default UI (`ListingLayout` plus every `Default*` slot component) built on Tailwind utility classes matching shadcn's token conventions:
113
+ The default UI is the **Tailwind-free** `/styled` adapter: the turnkey `ListingApp` (above), or the lower-level `StyledListingLayout` + `StyledComponentsProviderWithDefaults` from `react-listing-engine/styled`. It ships **self-contained CSS** — no Tailwind, no build step, no token setup. Import the stylesheet once:
117
114
 
118
115
  ```tsx
119
- import { ListingComponentsProviderWithDefaults, ListingLayout } from 'react-listing-engine/shadcn';
116
+ import { ListingApp } from 'react-listing-engine';
117
+ import 'react-listing-engine/styles.css';
120
118
  ```
121
119
 
122
- It needs Tailwind to see its class names and the shadcn-style CSS variables to be defined. Either:
120
+ Every visual value is a `--rle-*` CSS variable declared on `:root`, so you retheme the whole UI by overriding a subset in your own CSS loaded *after* the stylesheet:
123
121
 
124
- - point Tailwind's `@source` at the package's compiled output so its classes aren't purged:
125
- ```css
126
- @import "tailwindcss";
127
- @source "../node_modules/react-listing-engine/dist/**/*.{js,cjs}";
128
- ```
129
- - or define the semantic tokens it reads yourself (`--color-background`, `--color-foreground`, `--color-card`, `--color-popover`, `--color-border`, `--color-primary`, `--color-secondary`, `--color-muted`, `--color-accent`, `--color-destructive`, plus `-foreground` variants), the same set any shadcn-based project already ships. [`examples/basic/src/index.css`](./examples/basic/src/index.css) is the source of truth for the full list.
122
+ ```css
123
+ @import 'react-listing-engine/styles.css';
124
+
125
+ :root {
126
+ --rle-primary: #0ea5e9;
127
+ --rle-radius: 4px;
128
+ }
129
+ ```
130
130
 
131
131
  ## Hooks
132
132
 
@@ -138,17 +138,6 @@ It needs Tailwind to see its class names and the shadcn-style CSS variables to b
138
138
  - `useListingLayer(id)` — one dataset's visibility, points, and a `toggle()`.
139
139
  - `useListingEvent(type | '*', handler)` — subscribe to engine events for the component's lifetime.
140
140
 
141
- ## Examples
142
-
143
- [`examples/basic`](./examples/basic) is a Vite app with four scenarios, run locally (`pnpm install && pnpm dev` inside that folder):
144
-
145
- - **Properties only** — one dataset, the shipped rental filters, and the styled `/shadcn` defaults.
146
- - **Properties + businesses** — a second nearby-businesses marker layer composed alongside the properties dataset.
147
- - **Custom components** — `ListingComponentsProvider` with an app-authored `Card` and `Empty` slot.
148
- - **Custom filters** — `withFilters(reg => reg.add/remove/reorder)` mutating the shipped rental filter set.
149
-
150
- Each scenario works without a Google Maps key (the map is simply not rendered; a notice explains why) — set `VITE_GOOGLE_MAPS_KEY` in `examples/basic/.env` to see the map layer too.
151
-
152
141
  ## Support
153
142
 
154
143
  If `react-listing-engine` saves you time, consider sponsoring continued maintenance:
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ var z=class{state;listeners=new Set;constructor(e){this.state=this.freezeState({filters:{...e.filters},results:{items:[],nextCursor:null},bounds:null,selection:null,pagination:{mode:e.mode??"paged",loading:!1},layers:{},points:{}})}getState(){return this.state}setFilters(e){this.setState({filters:{...this.state.filters,...e}})}setResults(e){this.setState({results:{items:[...e.items],nextCursor:e.nextCursor,total:e.total}})}appendResults(e){this.setState({results:{items:[...this.state.results.items,...e.items],nextCursor:e.nextCursor,total:e.total}})}setBounds(e){this.setState({bounds:e?{...e}:null})}setSelection(e){this.setState({selection:e})}setLayerVisible(e,i){this.setState({layers:{...this.state.layers,[e]:i}})}setLoading(e){this.setState({pagination:{...this.state.pagination,loading:e}})}setPoints(e,i){this.setState({points:{...this.state.points,[e]:[...i]}})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}setState(e){this.state=this.freezeState({...this.state,...e}),this.notify()}notify(){for(let e of this.listeners)e()}freezeState(e){Object.freeze(e.filters),Object.freeze(e.results.items),Object.freeze(e.results),e.bounds&&Object.freeze(e.bounds),Object.freeze(e.pagination),Object.freeze(e.layers);for(let i of Object.values(e.points))Object.freeze(i);return Object.freeze(e.points),Object.freeze(e)}};var M=class{defs=new Map;add(e){if(this.defs.has(e.key))throw new Error(`FilterRegistry: filter "${e.key}" is already registered`);return this.defs.set(e.key,{...e}),this}remove(e){return this.defs.delete(e),this}replace(e,i){if(!this.defs.has(e))throw new Error(`FilterRegistry: cannot replace unregistered filter "${e}"`);return this.defs.set(e,{...i,key:e}),this}reorder(e){let i=this.list(),r=e.filter(s=>this.defs.has(s));r.forEach((s,o)=>{this.defs.get(s).order=o});let n=new Set(r);return i.filter(s=>!n.has(s.key)).forEach((s,o)=>{s.order=r.length+o}),this}list(){return[...this.defs.values()].sort((e,i)=>e.order-i.order)}has(e){return this.defs.has(e)}toFilters(e){return this.list().filter(r=>Object.hasOwn(e,r.key)).map(r=>r.toParams(e[r.key])).reduce((r,n)=>({...r,...n}),{})}activeKeys(e){return this.list().filter(i=>i.isActive?.(e)).map(i=>i.key)}};var V=class{defs=new Map;add(e){if(this.defs.has(e.id))throw new Error(`DatasetRegistry: dataset "${e.id}" is already registered`);return this.defs.set(e.id,{...e}),this}get(e){return this.defs.get(e)}has(e){return this.defs.has(e)}list(){return[...this.defs.values()]}visibleIds(){return this.list().filter(e=>e.visible?.()!==!1).map(e=>e.id)}};var U=class{map=new Map;dispose(){this.map.clear()}emit(e){let i=this.map.get(e.type);if(i)for(let n of[...i])n(e);let r=this.map.get("*");if(r)for(let n of[...r])n(e)}on(e,i){let r=this.map.get(e);return r||(r=new Set,this.map.set(e,r)),r.add(i),()=>{r.delete(i)}}};var ge={pagination:"paged",pageSize:20,debounceMs:250};var K=class{options;constructor(e){this.options=Object.freeze({...ge,...e})}};var $=class{filters;map;datasets;primaryDatasetId;store;emitter=new U;config;debounceTimer=null;debounceResolve=null;queryToken=0;pointsToken=0;constructor(e){this.datasets=e.datasets,this.filters=e.filters??new M,this.map=e.map,this.config=new K(e.config);let i=e.primaryDatasetId??this.datasets.list()[0]?.id;if(i==null||!this.datasets.has(i))throw new Error(`ListingEngine: primary dataset "${i??""}" is not registered \u2014 pass a valid primaryDatasetId or register at least one dataset`);this.primaryDatasetId=i,this.store=new z({filters:e.initialFilters??{},mode:this.config.options.pagination})}get state(){return this.store.getState()}get options(){return this.config.options}applyFilters(e){this.store.setFilters(e),this.emitter.emit({type:"FiltersChanged",filters:this.currentFilters()}),this.clearDebounce();let i=++this.queryToken,r=this.config.options.debounceMs;return r<=0?this.runQuery(i):new Promise(n=>{this.debounceResolve=n,this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.debounceResolve=null,n(this.runQuery(i))},r)})}async loadPage(){let e=this.state.results.nextCursor;if(e===null)return;let i=++this.queryToken,r=this.primaryDataset();this.store.setLoading(!0);try{let n=await r.adapter.list(this.currentFilters(),{cursor:e,limit:this.config.options.pageSize});if(i!==this.queryToken)return;this.config.options.pagination==="infinite"?this.store.appendResults(n):this.store.setResults(n),this.emitter.emit({type:"ResultsLoaded",datasetId:this.primaryDatasetId,count:n.items.length})}finally{i===this.queryToken&&this.store.setLoading(!1)}}async loadPoints(e){this.store.setBounds(e),this.emitter.emit({type:"BoundsChanged",bounds:e});let i=this.currentFilters(),r=++this.pointsToken;await Promise.allSettled(this.datasets.visibleIds().map(async n=>{let s=this.datasets.get(n);if(!s)return;let o=await s.adapter.getPoints(i,e);r===this.pointsToken&&this.store.setPoints(n,o)}))}selectPoint(e,i){this.store.setSelection(i);let r=this.state.points[e]?.find(n=>n.id===i);r&&this.emitter.emit({type:"PointClicked",datasetId:e,id:i,entity:r.entity})}toggleLayer(e){let r=!(this.state.layers[e]??!0);this.store.setLayerVisible(e,r),this.emitter.emit({type:"LayerToggled",datasetId:e,visible:r})}subscribe(e){return this.store.subscribe(e)}on(e,i){return this.emitter.on(e,i)}dispose(){this.clearDebounce(),this.emitter.dispose()}async runQuery(e){if(e!==this.queryToken)return;let i=this.primaryDataset();this.store.setLoading(!0);try{let r=await i.adapter.list(this.currentFilters(),{cursor:null,limit:this.config.options.pageSize});if(e!==this.queryToken)return;this.store.setResults(r),this.emitter.emit({type:"ResultsLoaded",datasetId:this.primaryDatasetId,count:r.items.length})}finally{e===this.queryToken&&this.store.setLoading(!1)}}primaryDataset(){return this.datasets.get(this.primaryDatasetId)}currentFilters(){return this.store.getState().filters}clearDebounce(){this.debounceTimer!==null&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.debounceResolve!==null&&(this.debounceResolve(),this.debounceResolve=null)}};function fe(...t){let e={config:{},filters:new M,datasets:new V};for(let i of t)i(e);return e}var ye=t=>e=>{e.config={...e.config,...t}},he=t=>e=>{e.map=t},ve=t=>e=>{e.datasets.add(t)},Le=t=>e=>{t(e.filters)},pi=t=>e=>{e.urlSync=t},be=t=>e=>{e.initialFilters=t},ci=t=>e=>{e.primaryDatasetId=t};import{createContext as Ze,useContext as Xe}from"react";import{jsx as F,jsxs as dt}from"react/jsx-runtime";function Je(t){return String(t?.title??"")}var Ye=({item:t})=>F("div",{children:Je(t)}),et=()=>F("div",{}),tt=()=>F("div",{}),it=({children:t})=>F("div",{children:t}),rt=({children:t})=>F("div",{children:t}),nt=({value:t,onChange:e,placeholder:i})=>F("input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":i??"Search"}),st=()=>F("div",{role:"status",children:"No results"}),ot=()=>F("div",{role:"status","aria-busy":"true",children:"Loading\u2026"}),at=({count:t})=>dt("div",{children:[t," results"]}),lt=({children:t})=>F("div",{children:t}),b={Card:Ye,Marker:et,Popup:tt,Sidebar:it,FilterPanel:rt,Search:nt,Empty:st,Loading:ot,ResultHeader:at,Toolbar:lt},Te=Ze(b);function Pe(t){let{Card:e,Marker:i,Popup:r,Sidebar:n,FilterPanel:s,Search:o,Empty:a,Loading:l,ResultHeader:y,Toolbar:d,children:v}=t,T={Card:e??b.Card,Marker:i??b.Marker,Popup:r??b.Popup,Sidebar:n??b.Sidebar,FilterPanel:s??b.FilterPanel,Search:o??b.Search,Empty:a??b.Empty,Loading:l??b.Loading,ResultHeader:y??b.ResultHeader,Toolbar:d??b.Toolbar};return F(Te.Provider,{value:T,children:v})}function P(){return Xe(Te)}import{createContext as pt}from"react";var W=pt(null);import{useContext as ct}from"react";function f(){let t=ct(W);if(t===null)throw new Error("useListing must be used within a <ListingProvider>");return t}import{useCallback as Fe,useSyncExternalStore as ut}from"react";function C(){let t=f(),e=Fe(r=>t.subscribe(r),[t]),i=Fe(()=>t.state,[t]);return ut(e,i,i)}import{useCallback as Ce}from"react";function q(){let t=f(),e=C(),i=Ce(n=>t.applyFilters(n),[t]),r=Ce((n,s)=>t.applyFilters({[n]:s}),[t]);return{filters:e.filters,set:i,setField:r}}import{jsx as A,jsxs as mt}from"react/jsx-runtime";function J({className:t,groupClassName:e,hideLabels:i}={}){let r=f(),{FilterPanel:n}=P(),{filters:s}=q();return A(n,{children:A("div",{className:t??"space-y-5",children:r.filters.list().map(o=>{if(typeof o.render=="string")return A("div",{"data-filter":o.key,className:e},o.key);let a=o.render;return mt("div",{className:e,children:[o.label&&!i&&A("div",{className:"mb-1.5 text-[13px] font-medium text-foreground",children:o.label}),A(a,{value:o.fromParams(s),onChange:l=>{r.applyFilters(o.toParams(l))}})]},o.key)})})})}function D(){return C().results}import{jsx as j}from"react/jsx-runtime";function gt(t,e){if(t&&typeof t=="object"&&"id"in t){let i=t.id;if(typeof i=="string"||typeof i=="number")return i}return e}function Se({className:t}={}){let e=f(),{items:i}=D(),{pagination:r,selection:n}=C(),{Card:s,Empty:o,Loading:a}=P();return r.loading&&i.length===0?j(a,{}):i.length===0?j(o,{}):j("div",{role:"list",className:t,children:i.map((l,y)=>{let d=gt(l,y);return j(s,{item:l,selected:n===d,onSelect:()=>e.selectPoint(e.primaryDatasetId,d)},d)})})}import{useEffect as Y,useRef as H,useState as ft}from"react";import{jsx as vt}from"react/jsx-runtime";var yt={west:-179.9,south:-85,east:179.9,north:85},Ie=.1,we=.02;function ht(t){if(t.length===0)return null;let e=t[0].lng,i=t[0].lng,r=t[0].lat,n=t[0].lat;for(let{lat:a,lng:l}of t)l<e&&(e=l),l>i&&(i=l),a<r&&(r=a),a>n&&(n=a);let s=n>r?(n-r)*Ie:we,o=i>e?(i-e)*Ie:we;return{west:e-o,east:i+o,south:r-s,north:n+s}}function ke(t){let{center:e,zoom:i,fallback:r}=t,n=f(),s=C(),o=H(null),a=H(null),[l,y]=ft(!1),d=H(!1),v=H(!1),T=H(!1),u=n.map;return Y(()=>{let L=a.current;if(!u||!L)return;let c=[];for(let m of Object.keys(s.points)){if(s.layers[m]===!1)continue;let h=n.datasets.get(m),S=s.points[m]??[],x={id:m,markers:S.map(k=>({id:k.id,position:k.position,iconUrl:h?.marker.iconUrl?.(k.entity),element:h?.marker.element?.(k.entity)})),clustering:h?.clustering,onMarkerClick:k=>n.selectPoint(m,k)};c.push(u.renderLayer(L,x))}return()=>{c.forEach(m=>m())}},[n,u,l,s.points,s.layers]),Y(()=>{if(!o.current||!u)return;let L=o.current,c=!1,m=null;return(async()=>{let h=await u.mount(L,{center:e,zoom:i});if(c){u.destroy(h);return}a.current=h,m=u.onBoundsChange(h,S=>{T.current?T.current=!1:v.current=!0,n.loadPoints(S)}),y(!0),n.loadPoints(yt)})(),()=>{c=!0,m?.(),a.current&&(u.destroy(a.current),a.current=null),y(!1)}},[n,u]),Y(()=>{let L=a.current;if(!u||!L||e||d.current||v.current)return;let c=[];for(let h of Object.keys(s.points))if(s.layers[h]!==!1)for(let S of s.points[h]??[])c.push(S.position);let m=ht(c);m&&(d.current=!0,T.current=!0,u.fitBounds(L,m))},[u,e,l,s.points,s.layers]),vt("div",{ref:o,className:!u&&r?"flex h-full min-h-0 w-full items-center justify-center":"h-full min-h-0 w-full",children:!u&&r})}import{jsx as Lt}from"react/jsx-runtime";function Ee(){let t=f(),{results:e,pagination:i}=C();return e.nextCursor==null?null:Lt("button",{type:"button",disabled:i.loading,onClick:()=>{t.loadPage()},children:"Load more"})}import{jsx as bt}from"react/jsx-runtime";function xe(){let{items:t,total:e}=D(),{ResultHeader:i}=P();return bt(i,{count:t.length,total:e})}import{useEffect as Tt,useRef as Pt}from"react";function Ne(t,e){let i=f(),r=Pt(e);r.current=e,Tt(()=>i.on(t,n=>r.current(n)),[i,t])}import{useEffect as Ft,useState as Re}from"react";import{jsx as Ct}from"react/jsx-runtime";function Me(t){let{children:e,...i}=t,[r]=Re(()=>i),[n,s]=Re(null);return Ft(()=>{let o=new $({datasets:r.datasets,filters:r.filters,config:r.config,map:r.map,initialFilters:r.initialFilters,primaryDatasetId:r.primaryDatasetId});return r.urlSync&&r.urlSync.start(o),s(o),()=>{r.urlSync&&r.urlSync.stop(),o.dispose(),s(a=>a===o?null:a)}},[r]),n?Ct(W.Provider,{value:n,children:e}):null}function _(t){return typeof t!="number"?t:new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0}).format(t)}import{Fragment as It,jsx as I,jsxs as De}from"react/jsx-runtime";function St(t){return t?"rle-card rle-card--selected":"rle-card"}function ee({item:t,selected:e,onSelect:i}){let r=t??{},n=St(e),s=De(It,{children:[r.imageUrl?I("img",{src:r.imageUrl,alt:r.title??"",className:"rle-card-media"}):I("div",{className:"rle-card-media rle-card-media--placeholder","aria-hidden":"true"}),De("div",{className:"rle-card-body",children:[r.title&&I("span",{className:"rle-card-title",children:r.title}),r.subtitle&&I("span",{className:"rle-card-address",children:r.subtitle}),r.badge&&I("div",{className:"rle-card-info",children:I("span",{className:"rle-card-info-item",children:r.badge})}),r.price!=null&&I("span",{className:"rle-card-price",children:_(r.price)})]})]});return i?I("button",{type:"button",onClick:i,"aria-pressed":e??!1,className:n,children:s}):I("article",{className:n,children:s})}import{jsx as Q,jsxs as _e}from"react/jsx-runtime";function te(t){return _e("div",{role:"status",className:"rle-empty",children:[_e("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round",className:"rle-empty-icon","aria-hidden":"true",children:[Q("circle",{cx:"11",cy:"11",r:"7"}),Q("path",{d:"m21 21-4.3-4.3"})]}),Q("p",{className:"rle-empty-title",children:"No results"}),Q("p",{className:"rle-empty-hint",children:"Try adjusting your filters or search terms."})]})}import{jsx as wt}from"react/jsx-runtime";function ie({children:t}){return wt("div",{className:"rle-filter-panel",children:t})}import{jsx as G,jsxs as kt}from"react/jsx-runtime";function re(t){return G("div",{className:"rle-loading",role:"status","aria-busy":"true","aria-label":"Loading results",children:Array.from({length:3}).map((e,i)=>kt("div",{className:"rle-loading-item",children:[G("div",{className:"rle-skeleton",style:{aspectRatio:"4 / 3",width:"100%"}}),G("div",{className:"rle-skeleton",style:{height:16,width:"65%"}}),G("div",{className:"rle-skeleton",style:{height:12,width:"35%"}})]},i))})}import{jsx as Et}from"react/jsx-runtime";function ne({point:t}){let e=t.entity??{};return Et("span",{className:"rle-pin",children:e.price!=null?_(e.price):""})}import{jsx as O,jsxs as se}from"react/jsx-runtime";function oe({entity:t,onClose:e}){let i=t??{};return se("div",{className:"rle-popup",role:"group","aria-label":"Location details",children:[O("button",{type:"button",onClick:e,"aria-label":"Close",className:"rle-popup-close",children:se("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[O("path",{d:"M18 6 6 18"}),O("path",{d:"m6 6 12 12"})]})}),se("div",{children:[i.title&&O("div",{className:"rle-card-title",children:i.title}),i.subtitle&&O("div",{className:"rle-card-address",children:i.subtitle}),i.price!=null&&O("div",{className:"rle-card-price",children:_(i.price)})]})]})}import{jsx as xt}from"react/jsx-runtime";function ae({count:t,total:e}){let i=e!=null&&e!==t?`${t} of ${e} results`:`${t} results`;return xt("div",{className:"rle-result-header",children:i})}import{jsx as Nt}from"react/jsx-runtime";function le({value:t,onChange:e,placeholder:i}){return Nt("input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":i??"Search",className:"rle-input"})}import{jsx as Rt}from"react/jsx-runtime";function de({children:t}){return Rt("aside",{className:"rle-sidebar",children:t})}import{jsx as Mt}from"react/jsx-runtime";function pe({children:t}){return Mt("div",{className:"rle-toolbar",children:t})}var Oe={Card:ee,Marker:ne,Popup:oe,Sidebar:de,FilterPanel:ie,Search:le,Empty:te,Loading:re,ResultHeader:ae,Toolbar:pe};import{jsx as p,jsxs as N}from"react/jsx-runtime";function Be({view:t,onViewChange:e}){return p("nav",{className:"rle-bottom-nav","aria-label":"Listing navigation",children:N("div",{className:"rle-viewtoggle",role:"group","aria-label":"View",children:[N("button",{type:"button",className:`rle-viewtoggle__btn${t==="list"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="list",onClick:()=>e("list"),children:[p(Dt,{}),"List"]}),N("button",{type:"button",className:`rle-viewtoggle__btn${t==="map"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="map",onClick:()=>e("map"),children:[p(_t,{}),"Map"]})]})})}function Ae(){return N("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[p("line",{x1:"4",y1:"6",x2:"20",y2:"6"}),p("circle",{cx:"9",cy:"6",r:"2",fill:"currentColor",stroke:"none"}),p("line",{x1:"4",y1:"12",x2:"20",y2:"12"}),p("circle",{cx:"15",cy:"12",r:"2",fill:"currentColor",stroke:"none"}),p("line",{x1:"4",y1:"18",x2:"20",y2:"18"}),p("circle",{cx:"11",cy:"18",r:"2",fill:"currentColor",stroke:"none"})]})}function Dt(){return N("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[p("line",{x1:"8",y1:"6",x2:"20",y2:"6"}),p("line",{x1:"8",y1:"12",x2:"20",y2:"12"}),p("line",{x1:"8",y1:"18",x2:"20",y2:"18"}),p("line",{x1:"4",y1:"6",x2:"4.01",y2:"6"}),p("line",{x1:"4",y1:"12",x2:"4.01",y2:"12"}),p("line",{x1:"4",y1:"18",x2:"4.01",y2:"18"})]})}function _t(){return N("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[p("polygon",{points:"1 6 8 3 16 6 23 3 23 18 16 21 8 18 1 21 1 6"}),p("line",{x1:"8",y1:"3",x2:"8",y2:"18"}),p("line",{x1:"16",y1:"6",x2:"16",y2:"21"})]})}function He(){return N("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[p("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),p("line",{x1:"5",y1:"12",x2:"19",y2:"12"})]})}import{useEffect as ce,useRef as Ot,useState as ze}from"react";import{createPortal as Bt}from"react-dom";import{Fragment as Ht,jsx as w,jsxs as Z}from"react/jsx-runtime";function Ve({open:t,onOpenChange:e,title:i,children:r,footer:n}){let s=Ot(null),[o,a]=ze(!1),[l,y]=ze(!1);return ce(()=>{if(!t){y(!1),a(!1);return}a(!0);let d=requestAnimationFrame(()=>y(!0));return()=>cancelAnimationFrame(d)},[t]),ce(()=>{if(!o)return;let d=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=d}},[o]),ce(()=>{if(!o)return;s.current?.focus();function d(v){v.key==="Escape"&&e(!1)}return document.addEventListener("keydown",d),()=>document.removeEventListener("keydown",d)},[o,e]),!o||typeof document>"u"?null:Bt(Z(Ht,{children:[w("div",{className:`rle-sheet-backdrop${l?" rle-sheet-backdrop--open":""}`,onClick:()=>e(!1),"aria-hidden":"true"}),Z("div",{ref:s,className:`rle-sheet${l?" rle-sheet--open":""}`,role:"dialog","aria-modal":"true","aria-label":i,tabIndex:-1,children:[w("div",{className:"rle-sheet__handle","aria-hidden":"true"}),Z("div",{className:"rle-sheet__header",children:[i&&w("div",{className:"rle-sheet__title",children:i}),w("button",{type:"button",className:"rle-sheet__close",onClick:()=>e(!1),"aria-label":"Close",children:w(At,{})})]}),w("div",{className:"rle-sheet__body",children:r}),n&&w("div",{className:"rle-sheet__footer",children:n})]})]}),document.body)}function At(){return Z("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[w("path",{d:"M18 6 6 18"}),w("path",{d:"m6 6 12 12"})]})}import{useEffect as $e,useRef as zt,useState as X}from"react";import{jsx as R,jsxs as Ue}from"react/jsx-runtime";function Ke({search:t,onFiltersClick:e,filterCount:i=0,action:r}){let{Search:n}=P();return Ue("header",{className:"rle-mobile-header",children:[t&&R("div",{className:"rle-mobile-header__search",children:R(n,{value:t.value,onChange:t.onChange,placeholder:t.placeholder})}),Ue("button",{type:"button",className:"rle-mobile-header__btn",onClick:e,children:[R(Ae,{}),R("span",{children:"Filters"}),i>0&&R("span",{className:"rle-mobile-header__count",children:i})]}),r&&R("button",{type:"button",className:"rle-mobile-header__btn rle-mobile-header__btn--icon",onClick:r.onClick,"aria-label":r.label,children:r.icon??R(He,{})})]})}import{Fragment as Kt,jsx as g,jsxs as E}from"react/jsx-runtime";var Vt=g("div",{className:"rle-empty",children:"Map unavailable"});function Ut(){let t=zt(null),[e,i]=X(!0),[r,n]=X(!0);return $e(()=>{let s=t.current;if(!s)return;let o=()=>{let{clientWidth:y,scrollLeft:d,scrollWidth:v}=s;i(d<=0),n(d+y>=v-1)};o(),s.addEventListener("scroll",o,{passive:!0});let a,l;return typeof ResizeObserver<"u"&&(a=new ResizeObserver(o),a.observe(s)),typeof MutationObserver<"u"&&(l=new MutationObserver(o),l.observe(s,{characterData:!0,childList:!0,subtree:!0})),()=>{s.removeEventListener("scroll",o),a?.disconnect(),l?.disconnect()}},[]),{atEnd:r,atStart:e,ref:t}}function We({search:t,toolbarEnd:e,mobileAction:i,autoFetch:r=!0,hasMap:n=!0,mapCenter:s,mapZoom:o,className:a}){let l=f(),{Search:y}=P(),d=D(),{filters:v}=q(),[T,u]=X("list"),[L,c]=X(!1),{atEnd:m,atStart:h,ref:S}=Ut(),x=t?{value:String(v[t.filterKey]??""),onChange:B=>{l.applyFilters({[t.filterKey]:B||void 0})},placeholder:t.placeholder}:void 0;$e(()=>{r!==!1&&l.applyFilters({})},[l,r]);let k=()=>{let B=l.filters.list().reduce((Ge,me)=>Object.assign(Ge,me.toParams(me.fromParams({}))),{});l.applyFilters(B)},je=l.filters.list().filter(B=>B.isActive?.(v)).length,Qe=d.total??d.items.length;return E("div",{className:a?`rle-app ${a}`:"rle-app",children:[E("div",{className:"rle-filter-bar",children:[E("div",{className:"rle-filter-bar__scroll",ref:S,children:[x&&g("div",{className:"rle-filter-bar__search",children:g(y,{value:x.value,onChange:x.onChange,placeholder:x.placeholder})}),g(J,{className:"rle-filters-row",groupClassName:"rle-filter-group",hideLabels:!0})]}),!h&&g("div",{className:"rle-filter-bar__fade rle-filter-bar__fade--start","aria-hidden":"true"}),!m&&g("div",{className:"rle-filter-bar__fade rle-filter-bar__fade--end","aria-hidden":"true"})]}),g(Ke,{search:x,onFiltersClick:()=>c(!0),filterCount:je,action:i}),E("div",{className:`rle-body ${n?"rle-split":"rle-body--list-only"}`,"data-mobile-view":T,children:[E("div",{className:"rle-list",children:[E("div",{className:"rle-list-header",children:[g(xe,{}),e&&g("div",{className:"rle-list-header__toolbar",children:e})]}),g(Se,{className:"rle-list-grid"}),g(Ee,{})]}),n&&g("div",{className:"rle-map",children:g(ke,{center:s,zoom:o,fallback:Vt})})]}),n&&g(Be,{view:T,onViewChange:u}),g(Ve,{title:"Filters",open:L,onOpenChange:c,footer:E(Kt,{children:[g("button",{type:"button",className:"rle-btn rle-btn--ghost",onClick:k,children:"Clear all"}),E("button",{type:"button",className:"rle-btn rle-btn--primary",onClick:()=>c(!1),children:["Show ",Qe," results"]})]}),children:g(J,{className:"rle-filter-stack",groupClassName:"rle-filter-group"})})]})}import{useEffect as $t,useRef as Wt,useState as qt}from"react";import{jsx as ue,jsxs as Gt}from"react/jsx-runtime";function qe(t){return"provider"in t}function jt(t){let e=t!=null&&!qe(t),i=e?t.apiKey:void 0,r=e?t.mapId:void 0,[n,s]=qt(()=>!t||qe(t)?{ready:!0,provider:t?.provider}:{ready:!1});return $t(()=>{if(!i)return;let o=!1;return import("./maps/google/index.js").then(({googleProvider:a})=>{o||s({ready:!0,provider:a({apiKey:i,mapId:r})})}),()=>{o=!0}},[i,r]),n}function Qt({onFiltersChange:t}){let e=Wt(t);return e.current=t,Ne("FiltersChanged",i=>{i.type==="FiltersChanged"&&e.current(i.filters)}),null}function gn(t){let{datasets:e,filters:i,map:r,components:n,initialFilters:s,onFiltersChange:o,mobileAction:a,search:l,toolbarEnd:y,config:d,autoFetch:v,className:T}=t,{ready:u,provider:L}=jt(r);if(!u)return null;let c=[];for(let S of e)c.push(ve(S));i&&c.push(Le(i)),L&&c.push(he(L)),s&&c.push(be(s)),d&&c.push(ye(d));let m=fe(...c),h={...Oe,...n};return Gt(Me,{...m,children:[o&&ue(Qt,{onFiltersChange:o}),ue(Pe,{...h,children:ue(We,{className:T,search:l,toolbarEnd:y,mobileAction:a,autoFetch:v,hasMap:r!=null,mapCenter:r?.center,mapZoom:r?.zoom})})]})}export{z as a,M as b,V as c,U as d,ge as e,K as f,$ as g,fe as h,ye as i,he as j,ve as k,Le as l,pi as m,be as n,ci as o,Pe as p,P as q,W as r,f as s,C as t,q as u,J as v,D as w,Se as x,ke as y,Ee as z,xe as A,Ne as B,Me as C,ee as D,te as E,ie as F,re as G,ne as H,oe as I,ae as J,le as K,de as L,pe as M,Oe as N,Be as O,Ve as P,We as Q,gn as R};
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;"use client";
2
+ var z= (_class =class{__init() {this.listeners=new Set}constructor(e){;_class.prototype.__init.call(this);this.state=this.freezeState({filters:{...e.filters},results:{items:[],nextCursor:null},bounds:null,selection:null,pagination:{mode:_nullishCoalesce(e.mode, () => ("paged")),loading:!1},layers:{},points:{}})}getState(){return this.state}setFilters(e){this.setState({filters:{...this.state.filters,...e}})}setResults(e){this.setState({results:{items:[...e.items],nextCursor:e.nextCursor,total:e.total}})}appendResults(e){this.setState({results:{items:[...this.state.results.items,...e.items],nextCursor:e.nextCursor,total:e.total}})}setBounds(e){this.setState({bounds:e?{...e}:null})}setSelection(e){this.setState({selection:e})}setLayerVisible(e,i){this.setState({layers:{...this.state.layers,[e]:i}})}setLoading(e){this.setState({pagination:{...this.state.pagination,loading:e}})}setPoints(e,i){this.setState({points:{...this.state.points,[e]:[...i]}})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}setState(e){this.state=this.freezeState({...this.state,...e}),this.notify()}notify(){for(let e of this.listeners)e()}freezeState(e){Object.freeze(e.filters),Object.freeze(e.results.items),Object.freeze(e.results),e.bounds&&Object.freeze(e.bounds),Object.freeze(e.pagination),Object.freeze(e.layers);for(let i of Object.values(e.points))Object.freeze(i);return Object.freeze(e.points),Object.freeze(e)}}, _class);var M= (_class2 =class{constructor() { _class2.prototype.__init2.call(this); }__init2() {this.defs=new Map}add(e){if(this.defs.has(e.key))throw new Error(`FilterRegistry: filter "${e.key}" is already registered`);return this.defs.set(e.key,{...e}),this}remove(e){return this.defs.delete(e),this}replace(e,i){if(!this.defs.has(e))throw new Error(`FilterRegistry: cannot replace unregistered filter "${e}"`);return this.defs.set(e,{...i,key:e}),this}reorder(e){let i=this.list(),r=e.filter(s=>this.defs.has(s));r.forEach((s,o)=>{this.defs.get(s).order=o});let n=new Set(r);return i.filter(s=>!n.has(s.key)).forEach((s,o)=>{s.order=r.length+o}),this}list(){return[...this.defs.values()].sort((e,i)=>e.order-i.order)}has(e){return this.defs.has(e)}toFilters(e){return this.list().filter(r=>Object.hasOwn(e,r.key)).map(r=>r.toParams(e[r.key])).reduce((r,n)=>({...r,...n}),{})}activeKeys(e){return this.list().filter(i=>_optionalChain([i, 'access', _2 => _2.isActive, 'optionalCall', _3 => _3(e)])).map(i=>i.key)}}, _class2);var V= (_class3 =class{constructor() { _class3.prototype.__init3.call(this); }__init3() {this.defs=new Map}add(e){if(this.defs.has(e.id))throw new Error(`DatasetRegistry: dataset "${e.id}" is already registered`);return this.defs.set(e.id,{...e}),this}get(e){return this.defs.get(e)}has(e){return this.defs.has(e)}list(){return[...this.defs.values()]}visibleIds(){return this.list().filter(e=>_optionalChain([e, 'access', _4 => _4.visible, 'optionalCall', _5 => _5()])!==!1).map(e=>e.id)}}, _class3);var U= (_class4 =class{constructor() { _class4.prototype.__init4.call(this); }__init4() {this.map=new Map}dispose(){this.map.clear()}emit(e){let i=this.map.get(e.type);if(i)for(let n of[...i])n(e);let r=this.map.get("*");if(r)for(let n of[...r])n(e)}on(e,i){let r=this.map.get(e);return r||(r=new Set,this.map.set(e,r)),r.add(i),()=>{r.delete(i)}}}, _class4);var ge={pagination:"paged",pageSize:20,debounceMs:250};var K=class{constructor(e){this.options=Object.freeze({...ge,...e})}};var $= (_class5 =class{__init5() {this.emitter=new U}__init6() {this.debounceTimer=null}__init7() {this.debounceResolve=null}__init8() {this.queryToken=0}__init9() {this.pointsToken=0}constructor(e){;_class5.prototype.__init5.call(this);_class5.prototype.__init6.call(this);_class5.prototype.__init7.call(this);_class5.prototype.__init8.call(this);_class5.prototype.__init9.call(this);this.datasets=e.datasets,this.filters=_nullishCoalesce(e.filters, () => (new M)),this.map=e.map,this.config=new K(e.config);let i=_nullishCoalesce(e.primaryDatasetId, () => (_optionalChain([this, 'access', _6 => _6.datasets, 'access', _7 => _7.list, 'call', _8 => _8(), 'access', _9 => _9[0], 'optionalAccess', _10 => _10.id])));if(i==null||!this.datasets.has(i))throw new Error(`ListingEngine: primary dataset "${_nullishCoalesce(i, () => (""))}" is not registered \u2014 pass a valid primaryDatasetId or register at least one dataset`);this.primaryDatasetId=i,this.store=new z({filters:_nullishCoalesce(e.initialFilters, () => ({})),mode:this.config.options.pagination})}get state(){return this.store.getState()}get options(){return this.config.options}applyFilters(e){this.store.setFilters(e),this.emitter.emit({type:"FiltersChanged",filters:this.currentFilters()}),this.clearDebounce();let i=++this.queryToken,r=this.config.options.debounceMs;return r<=0?this.runQuery(i):new Promise(n=>{this.debounceResolve=n,this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.debounceResolve=null,n(this.runQuery(i))},r)})}async loadPage(){let e=this.state.results.nextCursor;if(e===null)return;let i=++this.queryToken,r=this.primaryDataset();this.store.setLoading(!0);try{let n=await r.adapter.list(this.currentFilters(),{cursor:e,limit:this.config.options.pageSize});if(i!==this.queryToken)return;this.config.options.pagination==="infinite"?this.store.appendResults(n):this.store.setResults(n),this.emitter.emit({type:"ResultsLoaded",datasetId:this.primaryDatasetId,count:n.items.length})}finally{i===this.queryToken&&this.store.setLoading(!1)}}async loadPoints(e){this.store.setBounds(e),this.emitter.emit({type:"BoundsChanged",bounds:e});let i=this.currentFilters(),r=++this.pointsToken;await Promise.allSettled(this.datasets.visibleIds().map(async n=>{let s=this.datasets.get(n);if(!s)return;let o=await s.adapter.getPoints(i,e);r===this.pointsToken&&this.store.setPoints(n,o)}))}selectPoint(e,i){this.store.setSelection(i);let r=_optionalChain([this, 'access', _11 => _11.state, 'access', _12 => _12.points, 'access', _13 => _13[e], 'optionalAccess', _14 => _14.find, 'call', _15 => _15(n=>n.id===i)]);r&&this.emitter.emit({type:"PointClicked",datasetId:e,id:i,entity:r.entity})}toggleLayer(e){let r=!(_nullishCoalesce(this.state.layers[e], () => (!0)));this.store.setLayerVisible(e,r),this.emitter.emit({type:"LayerToggled",datasetId:e,visible:r})}subscribe(e){return this.store.subscribe(e)}on(e,i){return this.emitter.on(e,i)}dispose(){this.clearDebounce(),this.emitter.dispose()}async runQuery(e){if(e!==this.queryToken)return;let i=this.primaryDataset();this.store.setLoading(!0);try{let r=await i.adapter.list(this.currentFilters(),{cursor:null,limit:this.config.options.pageSize});if(e!==this.queryToken)return;this.store.setResults(r),this.emitter.emit({type:"ResultsLoaded",datasetId:this.primaryDatasetId,count:r.items.length})}finally{e===this.queryToken&&this.store.setLoading(!1)}}primaryDataset(){return this.datasets.get(this.primaryDatasetId)}currentFilters(){return this.store.getState().filters}clearDebounce(){this.debounceTimer!==null&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.debounceResolve!==null&&(this.debounceResolve(),this.debounceResolve=null)}}, _class5);function fe(...t){let e={config:{},filters:new M,datasets:new V};for(let i of t)i(e);return e}var ye=t=>e=>{e.config={...e.config,...t}},he= exports.j =t=>e=>{e.map=t},ve= exports.k =t=>e=>{e.datasets.add(t)},Le= exports.l =t=>e=>{t(e.filters)},pi= exports.m =t=>e=>{e.urlSync=t},be= exports.n =t=>e=>{e.initialFilters=t},ci= exports.o =t=>e=>{e.primaryDatasetId=t};var _react = require('react');var _jsxruntime = require('react/jsx-runtime');function Je(t){return String(_nullishCoalesce(_optionalChain([t, 'optionalAccess', _16 => _16.title]), () => ("")))}var Ye=({item:t})=>_jsxruntime.jsx.call(void 0, "div",{children:Je(t)}),et=()=>_jsxruntime.jsx.call(void 0, "div",{}),tt=()=>_jsxruntime.jsx.call(void 0, "div",{}),it=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),rt=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),nt=({value:t,onChange:e,placeholder:i})=>_jsxruntime.jsx.call(void 0, "input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":_nullishCoalesce(i, () => ("Search"))}),st=()=>_jsxruntime.jsx.call(void 0, "div",{role:"status",children:"No results"}),ot=()=>_jsxruntime.jsx.call(void 0, "div",{role:"status","aria-busy":"true",children:"Loading\u2026"}),at=({count:t})=>_jsxruntime.jsxs.call(void 0, "div",{children:[t," results"]}),lt=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),b={Card:Ye,Marker:et,Popup:tt,Sidebar:it,FilterPanel:rt,Search:nt,Empty:st,Loading:ot,ResultHeader:at,Toolbar:lt},Te=_react.createContext.call(void 0, b);function Pe(t){let{Card:e,Marker:i,Popup:r,Sidebar:n,FilterPanel:s,Search:o,Empty:a,Loading:l,ResultHeader:y,Toolbar:d,children:v}=t,T={Card:_nullishCoalesce(e, () => (b.Card)),Marker:_nullishCoalesce(i, () => (b.Marker)),Popup:_nullishCoalesce(r, () => (b.Popup)),Sidebar:_nullishCoalesce(n, () => (b.Sidebar)),FilterPanel:_nullishCoalesce(s, () => (b.FilterPanel)),Search:_nullishCoalesce(o, () => (b.Search)),Empty:_nullishCoalesce(a, () => (b.Empty)),Loading:_nullishCoalesce(l, () => (b.Loading)),ResultHeader:_nullishCoalesce(y, () => (b.ResultHeader)),Toolbar:_nullishCoalesce(d, () => (b.Toolbar))};return _jsxruntime.jsx.call(void 0, Te.Provider,{value:T,children:v})}function P(){return _react.useContext.call(void 0, Te)}var W=_react.createContext.call(void 0, null);function f(){let t=_react.useContext.call(void 0, W);if(t===null)throw new Error("useListing must be used within a <ListingProvider>");return t}function C(){let t=f(),e=_react.useCallback.call(void 0, r=>t.subscribe(r),[t]),i=_react.useCallback.call(void 0, ()=>t.state,[t]);return _react.useSyncExternalStore.call(void 0, e,i,i)}function q(){let t=f(),e=C(),i=_react.useCallback.call(void 0, n=>t.applyFilters(n),[t]),r=_react.useCallback.call(void 0, (n,s)=>t.applyFilters({[n]:s}),[t]);return{filters:e.filters,set:i,setField:r}}function J({className:t,groupClassName:e,hideLabels:i}={}){let r=f(),{FilterPanel:n}=P(),{filters:s}=q();return _jsxruntime.jsx.call(void 0, n,{children:_jsxruntime.jsx.call(void 0, "div",{className:_nullishCoalesce(t, () => ("space-y-5")),children:r.filters.list().map(o=>{if(typeof o.render=="string")return _jsxruntime.jsx.call(void 0, "div",{"data-filter":o.key,className:e},o.key);let a=o.render;return _jsxruntime.jsxs.call(void 0, "div",{className:e,children:[o.label&&!i&&_jsxruntime.jsx.call(void 0, "div",{className:"mb-1.5 text-[13px] font-medium text-foreground",children:o.label}),_jsxruntime.jsx.call(void 0, a,{value:o.fromParams(s),onChange:l=>{r.applyFilters(o.toParams(l))}})]},o.key)})})})}function D(){return C().results}function gt(t,e){if(t&&typeof t=="object"&&"id"in t){let i=t.id;if(typeof i=="string"||typeof i=="number")return i}return e}function Se({className:t}={}){let e=f(),{items:i}=D(),{pagination:r,selection:n}=C(),{Card:s,Empty:o,Loading:a}=P();return r.loading&&i.length===0?_jsxruntime.jsx.call(void 0, a,{}):i.length===0?_jsxruntime.jsx.call(void 0, o,{}):_jsxruntime.jsx.call(void 0, "div",{role:"list",className:t,children:i.map((l,y)=>{let d=gt(l,y);return _jsxruntime.jsx.call(void 0, s,{item:l,selected:n===d,onSelect:()=>e.selectPoint(e.primaryDatasetId,d)},d)})})}var yt={west:-179.9,south:-85,east:179.9,north:85},Ie=.1,we=.02;function ht(t){if(t.length===0)return null;let e=t[0].lng,i=t[0].lng,r=t[0].lat,n=t[0].lat;for(let{lat:a,lng:l}of t)l<e&&(e=l),l>i&&(i=l),a<r&&(r=a),a>n&&(n=a);let s=n>r?(n-r)*Ie:we,o=i>e?(i-e)*Ie:we;return{west:e-o,east:i+o,south:r-s,north:n+s}}function ke(t){let{center:e,zoom:i,fallback:r}=t,n=f(),s=C(),o=_react.useRef.call(void 0, null),a=_react.useRef.call(void 0, null),[l,y]=_react.useState.call(void 0, !1),d=_react.useRef.call(void 0, !1),v=_react.useRef.call(void 0, !1),T=_react.useRef.call(void 0, !1),u=n.map;return _react.useEffect.call(void 0, ()=>{let L=a.current;if(!u||!L)return;let c=[];for(let m of Object.keys(s.points)){if(s.layers[m]===!1)continue;let h=n.datasets.get(m),S=_nullishCoalesce(s.points[m], () => ([])),x={id:m,markers:S.map(k=>({id:k.id,position:k.position,iconUrl:_optionalChain([h, 'optionalAccess', _17 => _17.marker, 'access', _18 => _18.iconUrl, 'optionalCall', _19 => _19(k.entity)]),element:_optionalChain([h, 'optionalAccess', _20 => _20.marker, 'access', _21 => _21.element, 'optionalCall', _22 => _22(k.entity)])})),clustering:_optionalChain([h, 'optionalAccess', _23 => _23.clustering]),onMarkerClick:k=>n.selectPoint(m,k)};c.push(u.renderLayer(L,x))}return()=>{c.forEach(m=>m())}},[n,u,l,s.points,s.layers]),_react.useEffect.call(void 0, ()=>{if(!o.current||!u)return;let L=o.current,c=!1,m=null;return(async()=>{let h=await u.mount(L,{center:e,zoom:i});if(c){u.destroy(h);return}a.current=h,m=u.onBoundsChange(h,S=>{T.current?T.current=!1:v.current=!0,n.loadPoints(S)}),y(!0),n.loadPoints(yt)})(),()=>{c=!0,_optionalChain([m, 'optionalCall', _24 => _24()]),a.current&&(u.destroy(a.current),a.current=null),y(!1)}},[n,u]),_react.useEffect.call(void 0, ()=>{let L=a.current;if(!u||!L||e||d.current||v.current)return;let c=[];for(let h of Object.keys(s.points))if(s.layers[h]!==!1)for(let S of _nullishCoalesce(s.points[h], () => ([])))c.push(S.position);let m=ht(c);m&&(d.current=!0,T.current=!0,u.fitBounds(L,m))},[u,e,l,s.points,s.layers]),_jsxruntime.jsx.call(void 0, "div",{ref:o,className:!u&&r?"flex h-full min-h-0 w-full items-center justify-center":"h-full min-h-0 w-full",children:!u&&r})}function Ee(){let t=f(),{results:e,pagination:i}=C();return e.nextCursor==null?null:_jsxruntime.jsx.call(void 0, "button",{type:"button",disabled:i.loading,onClick:()=>{t.loadPage()},children:"Load more"})}function xe(){let{items:t,total:e}=D(),{ResultHeader:i}=P();return _jsxruntime.jsx.call(void 0, i,{count:t.length,total:e})}function Ne(t,e){let i=f(),r=_react.useRef.call(void 0, e);r.current=e,_react.useEffect.call(void 0, ()=>i.on(t,n=>r.current(n)),[i,t])}function Me(t){let{children:e,...i}=t,[r]=_react.useState.call(void 0, ()=>i),[n,s]=_react.useState.call(void 0, null);return _react.useEffect.call(void 0, ()=>{let o=new $({datasets:r.datasets,filters:r.filters,config:r.config,map:r.map,initialFilters:r.initialFilters,primaryDatasetId:r.primaryDatasetId});return r.urlSync&&r.urlSync.start(o),s(o),()=>{r.urlSync&&r.urlSync.stop(),o.dispose(),s(a=>a===o?null:a)}},[r]),n?_jsxruntime.jsx.call(void 0, W.Provider,{value:n,children:e}):null}function _(t){return typeof t!="number"?t:new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0}).format(t)}function St(t){return t?"rle-card rle-card--selected":"rle-card"}function ee({item:t,selected:e,onSelect:i}){let r=_nullishCoalesce(t, () => ({})),n=St(e),s=_jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[r.imageUrl?_jsxruntime.jsx.call(void 0, "img",{src:r.imageUrl,alt:_nullishCoalesce(r.title, () => ("")),className:"rle-card-media"}):_jsxruntime.jsx.call(void 0, "div",{className:"rle-card-media rle-card-media--placeholder","aria-hidden":"true"}),_jsxruntime.jsxs.call(void 0, "div",{className:"rle-card-body",children:[r.title&&_jsxruntime.jsx.call(void 0, "span",{className:"rle-card-title",children:r.title}),r.subtitle&&_jsxruntime.jsx.call(void 0, "span",{className:"rle-card-address",children:r.subtitle}),r.badge&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-card-info",children:_jsxruntime.jsx.call(void 0, "span",{className:"rle-card-info-item",children:r.badge})}),r.price!=null&&_jsxruntime.jsx.call(void 0, "span",{className:"rle-card-price",children:_(r.price)})]})]});return i?_jsxruntime.jsx.call(void 0, "button",{type:"button",onClick:i,"aria-pressed":_nullishCoalesce(e, () => (!1)),className:n,children:s}):_jsxruntime.jsx.call(void 0, "article",{className:n,children:s})}function te(t){return _jsxruntime.jsxs.call(void 0, "div",{role:"status",className:"rle-empty",children:[_jsxruntime.jsxs.call(void 0, "svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round",className:"rle-empty-icon","aria-hidden":"true",children:[_jsxruntime.jsx.call(void 0, "circle",{cx:"11",cy:"11",r:"7"}),_jsxruntime.jsx.call(void 0, "path",{d:"m21 21-4.3-4.3"})]}),_jsxruntime.jsx.call(void 0, "p",{className:"rle-empty-title",children:"No results"}),_jsxruntime.jsx.call(void 0, "p",{className:"rle-empty-hint",children:"Try adjusting your filters or search terms."})]})}function ie({children:t}){return _jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-panel",children:t})}function re(t){return _jsxruntime.jsx.call(void 0, "div",{className:"rle-loading",role:"status","aria-busy":"true","aria-label":"Loading results",children:Array.from({length:3}).map((e,i)=>_jsxruntime.jsxs.call(void 0, "div",{className:"rle-loading-item",children:[_jsxruntime.jsx.call(void 0, "div",{className:"rle-skeleton",style:{aspectRatio:"4 / 3",width:"100%"}}),_jsxruntime.jsx.call(void 0, "div",{className:"rle-skeleton",style:{height:16,width:"65%"}}),_jsxruntime.jsx.call(void 0, "div",{className:"rle-skeleton",style:{height:12,width:"35%"}})]},i))})}function ne({point:t}){let e=_nullishCoalesce(t.entity, () => ({}));return _jsxruntime.jsx.call(void 0, "span",{className:"rle-pin",children:e.price!=null?_(e.price):""})}function oe({entity:t,onClose:e}){let i=_nullishCoalesce(t, () => ({}));return _jsxruntime.jsxs.call(void 0, "div",{className:"rle-popup",role:"group","aria-label":"Location details",children:[_jsxruntime.jsx.call(void 0, "button",{type:"button",onClick:e,"aria-label":"Close",className:"rle-popup-close",children:_jsxruntime.jsxs.call(void 0, "svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[_jsxruntime.jsx.call(void 0, "path",{d:"M18 6 6 18"}),_jsxruntime.jsx.call(void 0, "path",{d:"m6 6 12 12"})]})}),_jsxruntime.jsxs.call(void 0, "div",{children:[i.title&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-card-title",children:i.title}),i.subtitle&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-card-address",children:i.subtitle}),i.price!=null&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-card-price",children:_(i.price)})]})]})}function ae({count:t,total:e}){let i=e!=null&&e!==t?`${t} of ${e} results`:`${t} results`;return _jsxruntime.jsx.call(void 0, "div",{className:"rle-result-header",children:i})}function le({value:t,onChange:e,placeholder:i}){return _jsxruntime.jsx.call(void 0, "input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":_nullishCoalesce(i, () => ("Search")),className:"rle-input"})}function de({children:t}){return _jsxruntime.jsx.call(void 0, "aside",{className:"rle-sidebar",children:t})}function pe({children:t}){return _jsxruntime.jsx.call(void 0, "div",{className:"rle-toolbar",children:t})}var Oe={Card:ee,Marker:ne,Popup:oe,Sidebar:de,FilterPanel:ie,Search:le,Empty:te,Loading:re,ResultHeader:ae,Toolbar:pe};function Be({view:t,onViewChange:e}){return _jsxruntime.jsx.call(void 0, "nav",{className:"rle-bottom-nav","aria-label":"Listing navigation",children:_jsxruntime.jsxs.call(void 0, "div",{className:"rle-viewtoggle",role:"group","aria-label":"View",children:[_jsxruntime.jsxs.call(void 0, "button",{type:"button",className:`rle-viewtoggle__btn${t==="list"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="list",onClick:()=>e("list"),children:[_jsxruntime.jsx.call(void 0, Dt,{}),"List"]}),_jsxruntime.jsxs.call(void 0, "button",{type:"button",className:`rle-viewtoggle__btn${t==="map"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="map",onClick:()=>e("map"),children:[_jsxruntime.jsx.call(void 0, _t,{}),"Map"]})]})})}function Ae(){return _jsxruntime.jsxs.call(void 0, "svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[_jsxruntime.jsx.call(void 0, "line",{x1:"4",y1:"6",x2:"20",y2:"6"}),_jsxruntime.jsx.call(void 0, "circle",{cx:"9",cy:"6",r:"2",fill:"currentColor",stroke:"none"}),_jsxruntime.jsx.call(void 0, "line",{x1:"4",y1:"12",x2:"20",y2:"12"}),_jsxruntime.jsx.call(void 0, "circle",{cx:"15",cy:"12",r:"2",fill:"currentColor",stroke:"none"}),_jsxruntime.jsx.call(void 0, "line",{x1:"4",y1:"18",x2:"20",y2:"18"}),_jsxruntime.jsx.call(void 0, "circle",{cx:"11",cy:"18",r:"2",fill:"currentColor",stroke:"none"})]})}function Dt(){return _jsxruntime.jsxs.call(void 0, "svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[_jsxruntime.jsx.call(void 0, "line",{x1:"8",y1:"6",x2:"20",y2:"6"}),_jsxruntime.jsx.call(void 0, "line",{x1:"8",y1:"12",x2:"20",y2:"12"}),_jsxruntime.jsx.call(void 0, "line",{x1:"8",y1:"18",x2:"20",y2:"18"}),_jsxruntime.jsx.call(void 0, "line",{x1:"4",y1:"6",x2:"4.01",y2:"6"}),_jsxruntime.jsx.call(void 0, "line",{x1:"4",y1:"12",x2:"4.01",y2:"12"}),_jsxruntime.jsx.call(void 0, "line",{x1:"4",y1:"18",x2:"4.01",y2:"18"})]})}function _t(){return _jsxruntime.jsxs.call(void 0, "svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[_jsxruntime.jsx.call(void 0, "polygon",{points:"1 6 8 3 16 6 23 3 23 18 16 21 8 18 1 21 1 6"}),_jsxruntime.jsx.call(void 0, "line",{x1:"8",y1:"3",x2:"8",y2:"18"}),_jsxruntime.jsx.call(void 0, "line",{x1:"16",y1:"6",x2:"16",y2:"21"})]})}function He(){return _jsxruntime.jsxs.call(void 0, "svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[_jsxruntime.jsx.call(void 0, "line",{x1:"12",y1:"5",x2:"12",y2:"19"}),_jsxruntime.jsx.call(void 0, "line",{x1:"5",y1:"12",x2:"19",y2:"12"})]})}var _reactdom = require('react-dom');function Ve({open:t,onOpenChange:e,title:i,children:r,footer:n}){let s=_react.useRef.call(void 0, null),[o,a]=_react.useState.call(void 0, !1),[l,y]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>{if(!t){y(!1),a(!1);return}a(!0);let d=requestAnimationFrame(()=>y(!0));return()=>cancelAnimationFrame(d)},[t]),_react.useEffect.call(void 0, ()=>{if(!o)return;let d=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=d}},[o]),_react.useEffect.call(void 0, ()=>{if(!o)return;_optionalChain([s, 'access', _25 => _25.current, 'optionalAccess', _26 => _26.focus, 'call', _27 => _27()]);function d(v){v.key==="Escape"&&e(!1)}return document.addEventListener("keydown",d),()=>document.removeEventListener("keydown",d)},[o,e]),!o||typeof document>"u"?null:_reactdom.createPortal.call(void 0, _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[_jsxruntime.jsx.call(void 0, "div",{className:`rle-sheet-backdrop${l?" rle-sheet-backdrop--open":""}`,onClick:()=>e(!1),"aria-hidden":"true"}),_jsxruntime.jsxs.call(void 0, "div",{ref:s,className:`rle-sheet${l?" rle-sheet--open":""}`,role:"dialog","aria-modal":"true","aria-label":i,tabIndex:-1,children:[_jsxruntime.jsx.call(void 0, "div",{className:"rle-sheet__handle","aria-hidden":"true"}),_jsxruntime.jsxs.call(void 0, "div",{className:"rle-sheet__header",children:[i&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-sheet__title",children:i}),_jsxruntime.jsx.call(void 0, "button",{type:"button",className:"rle-sheet__close",onClick:()=>e(!1),"aria-label":"Close",children:_jsxruntime.jsx.call(void 0, At,{})})]}),_jsxruntime.jsx.call(void 0, "div",{className:"rle-sheet__body",children:r}),n&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-sheet__footer",children:n})]})]}),document.body)}function At(){return _jsxruntime.jsxs.call(void 0, "svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",width:"16",height:"16","aria-hidden":"true",children:[_jsxruntime.jsx.call(void 0, "path",{d:"M18 6 6 18"}),_jsxruntime.jsx.call(void 0, "path",{d:"m6 6 12 12"})]})}function Ke({search:t,onFiltersClick:e,filterCount:i=0,action:r}){let{Search:n}=P();return _jsxruntime.jsxs.call(void 0, "header",{className:"rle-mobile-header",children:[t&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-mobile-header__search",children:_jsxruntime.jsx.call(void 0, n,{value:t.value,onChange:t.onChange,placeholder:t.placeholder})}),_jsxruntime.jsxs.call(void 0, "button",{type:"button",className:"rle-mobile-header__btn",onClick:e,children:[_jsxruntime.jsx.call(void 0, Ae,{}),_jsxruntime.jsx.call(void 0, "span",{children:"Filters"}),i>0&&_jsxruntime.jsx.call(void 0, "span",{className:"rle-mobile-header__count",children:i})]}),r&&_jsxruntime.jsx.call(void 0, "button",{type:"button",className:"rle-mobile-header__btn rle-mobile-header__btn--icon",onClick:r.onClick,"aria-label":r.label,children:_nullishCoalesce(r.icon, () => (_jsxruntime.jsx.call(void 0, He,{})))})]})}var Vt=_jsxruntime.jsx.call(void 0, "div",{className:"rle-empty",children:"Map unavailable"});function Ut(){let t=_react.useRef.call(void 0, null),[e,i]=_react.useState.call(void 0, !0),[r,n]=_react.useState.call(void 0, !0);return _react.useEffect.call(void 0, ()=>{let s=t.current;if(!s)return;let o=()=>{let{clientWidth:y,scrollLeft:d,scrollWidth:v}=s;i(d<=0),n(d+y>=v-1)};o(),s.addEventListener("scroll",o,{passive:!0});let a,l;return typeof ResizeObserver<"u"&&(a=new ResizeObserver(o),a.observe(s)),typeof MutationObserver<"u"&&(l=new MutationObserver(o),l.observe(s,{characterData:!0,childList:!0,subtree:!0})),()=>{s.removeEventListener("scroll",o),_optionalChain([a, 'optionalAccess', _28 => _28.disconnect, 'call', _29 => _29()]),_optionalChain([l, 'optionalAccess', _30 => _30.disconnect, 'call', _31 => _31()])}},[]),{atEnd:r,atStart:e,ref:t}}function We({search:t,toolbarEnd:e,mobileAction:i,autoFetch:r=!0,hasMap:n=!0,mapCenter:s,mapZoom:o,className:a}){let l=f(),{Search:y}=P(),d=D(),{filters:v}=q(),[T,u]=_react.useState.call(void 0, "list"),[L,c]=_react.useState.call(void 0, !1),{atEnd:m,atStart:h,ref:S}=Ut(),x=t?{value:String(_nullishCoalesce(v[t.filterKey], () => (""))),onChange:B=>{l.applyFilters({[t.filterKey]:B||void 0})},placeholder:t.placeholder}:void 0;_react.useEffect.call(void 0, ()=>{r!==!1&&l.applyFilters({})},[l,r]);let k=()=>{let B=l.filters.list().reduce((Ge,me)=>Object.assign(Ge,me.toParams(me.fromParams({}))),{});l.applyFilters(B)},je=l.filters.list().filter(B=>_optionalChain([B, 'access', _32 => _32.isActive, 'optionalCall', _33 => _33(v)])).length,Qe=_nullishCoalesce(d.total, () => (d.items.length));return _jsxruntime.jsxs.call(void 0, "div",{className:a?`rle-app ${a}`:"rle-app",children:[_jsxruntime.jsxs.call(void 0, "div",{className:"rle-filter-bar",children:[_jsxruntime.jsxs.call(void 0, "div",{className:"rle-filter-bar__scroll",ref:S,children:[x&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__search",children:_jsxruntime.jsx.call(void 0, y,{value:x.value,onChange:x.onChange,placeholder:x.placeholder})}),_jsxruntime.jsx.call(void 0, J,{className:"rle-filters-row",groupClassName:"rle-filter-group",hideLabels:!0})]}),!h&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__fade rle-filter-bar__fade--start","aria-hidden":"true"}),!m&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__fade rle-filter-bar__fade--end","aria-hidden":"true"})]}),_jsxruntime.jsx.call(void 0, Ke,{search:x,onFiltersClick:()=>c(!0),filterCount:je,action:i}),_jsxruntime.jsxs.call(void 0, "div",{className:`rle-body ${n?"rle-split":"rle-body--list-only"}`,"data-mobile-view":T,children:[_jsxruntime.jsxs.call(void 0, "div",{className:"rle-list",children:[_jsxruntime.jsxs.call(void 0, "div",{className:"rle-list-header",children:[_jsxruntime.jsx.call(void 0, xe,{}),e&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-list-header__toolbar",children:e})]}),_jsxruntime.jsx.call(void 0, Se,{className:"rle-list-grid"}),_jsxruntime.jsx.call(void 0, Ee,{})]}),n&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-map",children:_jsxruntime.jsx.call(void 0, ke,{center:s,zoom:o,fallback:Vt})})]}),n&&_jsxruntime.jsx.call(void 0, Be,{view:T,onViewChange:u}),_jsxruntime.jsx.call(void 0, Ve,{title:"Filters",open:L,onOpenChange:c,footer:_jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[_jsxruntime.jsx.call(void 0, "button",{type:"button",className:"rle-btn rle-btn--ghost",onClick:k,children:"Clear all"}),_jsxruntime.jsxs.call(void 0, "button",{type:"button",className:"rle-btn rle-btn--primary",onClick:()=>c(!1),children:["Show ",Qe," results"]})]}),children:_jsxruntime.jsx.call(void 0, J,{className:"rle-filter-stack",groupClassName:"rle-filter-group"})})]})}function qe(t){return"provider"in t}function jt(t){let e=t!=null&&!qe(t),i=e?t.apiKey:void 0,r=e?t.mapId:void 0,[n,s]=_react.useState.call(void 0, ()=>!t||qe(t)?{ready:!0,provider:_optionalChain([t, 'optionalAccess', _34 => _34.provider])}:{ready:!1});return _react.useEffect.call(void 0, ()=>{if(!i)return;let o=!1;return Promise.resolve().then(() => _interopRequireWildcard(require("./maps/google/index.cjs"))).then(({googleProvider:a})=>{o||s({ready:!0,provider:a({apiKey:i,mapId:r})})}),()=>{o=!0}},[i,r]),n}function Qt({onFiltersChange:t}){let e=_react.useRef.call(void 0, t);return e.current=t,Ne("FiltersChanged",i=>{i.type==="FiltersChanged"&&e.current(i.filters)}),null}function gn(t){let{datasets:e,filters:i,map:r,components:n,initialFilters:s,onFiltersChange:o,mobileAction:a,search:l,toolbarEnd:y,config:d,autoFetch:v,className:T}=t,{ready:u,provider:L}=jt(r);if(!u)return null;let c=[];for(let S of e)c.push(ve(S));i&&c.push(Le(i)),L&&c.push(he(L)),s&&c.push(be(s)),d&&c.push(ye(d));let m=fe(...c),h={...Oe,...n};return _jsxruntime.jsxs.call(void 0, Me,{...m,children:[o&&_jsxruntime.jsx.call(void 0, Qt,{onFiltersChange:o}),_jsxruntime.jsx.call(void 0, Pe,{...h,children:_jsxruntime.jsx.call(void 0, We,{className:T,search:l,toolbarEnd:y,mobileAction:a,autoFetch:v,hasMap:r!=null,mapCenter:_optionalChain([r, 'optionalAccess', _35 => _35.center]),mapZoom:_optionalChain([r, 'optionalAccess', _36 => _36.zoom])})})]})}exports.a = z; exports.b = M; exports.c = V; exports.d = U; exports.e = ge; exports.f = K; exports.g = $; exports.h = fe; exports.i = ye; exports.j = he; exports.k = ve; exports.l = Le; exports.m = pi; exports.n = be; exports.o = ci; exports.p = Pe; exports.q = P; exports.r = W; exports.s = f; exports.t = C; exports.u = q; exports.v = J; exports.w = D; exports.x = Se; exports.y = ke; exports.z = Ee; exports.A = xe; exports.B = Ne; exports.C = Me; exports.D = ee; exports.E = te; exports.F = ie; exports.G = re; exports.H = ne; exports.I = oe; exports.J = ae; exports.K = le; exports.L = de; exports.M = pe; exports.N = Oe; exports.O = Be; exports.P = Ve; exports.Q = We; exports.R = gn;
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;"use client";
2
- var _chunkRXHR66FScjs = require('./chunk-RXHR66FS.cjs');var _chunkEYXV5OMYcjs = require('./chunk-EYXV5OMY.cjs');var _chunkCHNUE46Kcjs = require('./chunk-CHNUE46K.cjs');var _chunk2VUHLHHXcjs = require('./chunk-2VUHLHHX.cjs');var d=(o=>(o.Paged="paged",o.Infinite="infinite",o))(d||{});var f=(r=>(r.FiltersChanged="FiltersChanged",r.ResultsLoaded="ResultsLoaded",r.PointClicked="PointClicked",r.BoundsChanged="BoundsChanged",r.LayerToggled="LayerToggled",r))(f||{});var u= (_class =class{__init() {this.listeners=new Set}constructor(t={}){;_class.prototype.__init.call(this);this.query={...t}}getQuery(){return{...this.query}}setQuery(t){this.query={...t},this.notify()}subscribe(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}notify(){for(let t of[...this.listeners])t()}}, _class);var _jsxruntime = require('react/jsx-runtime');function Z({children:e}){let{Toolbar:t}=_chunkEYXV5OMYcjs.q.call(void 0, );return _jsxruntime.jsx.call(void 0, t,{children:e})}var _react = require('react');function tt(){let e=_chunkEYXV5OMYcjs.s.call(void 0, ),t=_chunkEYXV5OMYcjs.t.call(void 0, ),o=_react.useCallback.call(void 0, a=>e.loadPoints(a),[e]),n=_react.useCallback.call(void 0, (a,r)=>e.selectPoint(a,r),[e]);return{bounds:t.bounds,points:t.points,loadPoints:o,selectPoint:n}}function it(e){let t=_chunkEYXV5OMYcjs.s.call(void 0, ),o=_chunkEYXV5OMYcjs.t.call(void 0, ),n=_react.useCallback.call(void 0, ()=>t.toggleLayer(e),[t,e]);return{visible:_nullishCoalesce(o.layers[e], () => (!0)),points:_nullishCoalesce(o.points[e], () => ([])),toggle:n}}exports.BrowserHistoryPort = _chunkCHNUE46Kcjs.a; exports.DatasetRegistry = _chunkEYXV5OMYcjs.c; exports.FilterRegistry = _chunkEYXV5OMYcjs.b; exports.ListingApp = _chunkRXHR66FScjs.p; exports.ListingComponentsProvider = _chunkEYXV5OMYcjs.p; exports.ListingConfig = _chunkEYXV5OMYcjs.f; exports.ListingEngine = _chunkEYXV5OMYcjs.g; exports.ListingEngineContext = _chunkEYXV5OMYcjs.r; exports.ListingEventType = f; exports.ListingFilters = _chunkEYXV5OMYcjs.v; exports.ListingList = _chunkEYXV5OMYcjs.x; exports.ListingMap = _chunkEYXV5OMYcjs.y; exports.ListingPagination = _chunkEYXV5OMYcjs.z; exports.ListingProvider = _chunkEYXV5OMYcjs.B; exports.ListingResultHeader = _chunkEYXV5OMYcjs.A; exports.ListingStore = _chunkEYXV5OMYcjs.a; exports.ListingToolbar = Z; exports.MemoryHistoryPort = u; exports.PaginationMode = d; exports.TypedEmitter = _chunkEYXV5OMYcjs.d; exports.UrlSyncController = _chunk2VUHLHHXcjs.a; exports.composeListingProviders = _chunkEYXV5OMYcjs.h; exports.listingDefaultConfig = _chunkEYXV5OMYcjs.e; exports.useListing = _chunkEYXV5OMYcjs.s; exports.useListingComponents = _chunkEYXV5OMYcjs.q; exports.useListingEvent = _chunkRXHR66FScjs.a; exports.useListingFilters = _chunkEYXV5OMYcjs.u; exports.useListingLayer = it; exports.useListingMap = tt; exports.useListingResults = _chunkEYXV5OMYcjs.w; exports.useListingState = _chunkEYXV5OMYcjs.t; exports.withConfig = _chunkEYXV5OMYcjs.i; exports.withDataset = _chunkEYXV5OMYcjs.k; exports.withFilters = _chunkEYXV5OMYcjs.l; exports.withInitialFilters = _chunkEYXV5OMYcjs.n; exports.withMap = _chunkEYXV5OMYcjs.j; exports.withPrimaryDataset = _chunkEYXV5OMYcjs.o; exports.withUrlSync = _chunkEYXV5OMYcjs.m;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;"use client";
2
+ var _chunkPE27NATScjs = require('./chunk-PE27NATS.cjs');var m=(e=>(e.Paged="paged",e.Infinite="infinite",e))(m||{});var g=(s=>(s.FiltersChanged="FiltersChanged",s.ResultsLoaded="ResultsLoaded",s.PointClicked="PointClicked",s.BoundsChanged="BoundsChanged",s.LayerToggled="LayerToggled",s))(g||{});var y= (_class =class{__init() {this.listeners=new Set}constructor(t={}){;_class.prototype.__init.call(this);this.query={...t}}getQuery(){return{...this.query}}setQuery(t){this.query={...t},this.notify()}subscribe(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}notify(){for(let t of[...this.listeners])t()}}, _class);function P(r,t){let e=new Set([...Object.keys(r),...Object.keys(t)]);for(let i of e)if(r[i]!==t[i])return!1;return!0}var c= (_class2 =class{__init2() {this.isSyncing=!1}__init3() {this.unsubscribeEngine=null}__init4() {this.unsubscribeHistory=null}constructor(t){;_class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);this.history=t.history,this.toQueryFn=t.toQuery,this.toFiltersFn=t.toFilters,this.hydrateOnStart=_nullishCoalesce(t.hydrateOnStart, () => (!0))}start(t){this.stop(),this.unsubscribeEngine=t.subscribe(()=>{this.isSyncing||this.syncEngineToHistory(t)}),this.unsubscribeHistory=this.history.subscribe(()=>{this.isSyncing||this.withSyncGuard(()=>{this.applyFiltersSafely(t,this.toFiltersFn(this.history.getQuery()))})}),this.hydrateOnStart&&this.withSyncGuard(()=>{this.applyFiltersSafely(t,this.toFiltersFn(this.history.getQuery()))})}stop(){_optionalChain([this, 'access', _ => _.unsubscribeEngine, 'optionalCall', _2 => _2()]),this.unsubscribeEngine=null,_optionalChain([this, 'access', _3 => _3.unsubscribeHistory, 'optionalCall', _4 => _4()]),this.unsubscribeHistory=null}syncEngineToHistory(t){let e=this.toQueryFn(t.state.filters);P(e,this.history.getQuery())||this.withSyncGuard(()=>this.history.setQuery(e))}applyFiltersSafely(t,e){Promise.resolve(t.applyFilters(e)).catch(()=>{})}withSyncGuard(t){this.isSyncing=!0;try{t()}finally{this.isSyncing=!1}}}, _class2);var p=class{constructor(t={}){this.mode=_nullishCoalesce(t.mode, () => ("replace"))}getQuery(){if(typeof window>"u")return{};let t=new URLSearchParams(window.location.search),e={};for(let[i,o]of t)o!==""&&(e[i]=o);return e}setQuery(t){if(typeof window>"u")return;let e=new URLSearchParams;for(let[h,u]of Object.entries(t))u===void 0||u===""||e.set(h,u);let i=e.toString(),o=i?`?${i}`:"";if(o===window.location.search)return;let s=`${window.location.pathname}${o}${window.location.hash}`;this.mode==="push"?window.history.pushState(window.history.state,"",s):window.history.replaceState(window.history.state,"",s)}subscribe(t){return typeof window>"u"?()=>{}:(window.addEventListener("popstate",t),()=>{window.removeEventListener("popstate",t)})}};var _jsxruntime = require('react/jsx-runtime');function M({children:r}){let{Toolbar:t}=_chunkPE27NATScjs.q.call(void 0, );return _jsxruntime.jsx.call(void 0, t,{children:r})}var _react = require('react');function ot(){let r=_chunkPE27NATScjs.s.call(void 0, ),t=_chunkPE27NATScjs.t.call(void 0, ),e=_react.useCallback.call(void 0, o=>r.loadPoints(o),[r]),i=_react.useCallback.call(void 0, (o,s)=>r.selectPoint(o,s),[r]);return{bounds:t.bounds,points:t.points,loadPoints:e,selectPoint:i}}function yt(r){let t=_chunkPE27NATScjs.s.call(void 0, ),e=_chunkPE27NATScjs.t.call(void 0, ),i=_react.useCallback.call(void 0, ()=>t.toggleLayer(r),[t,r]);return{visible:_nullishCoalesce(e.layers[r], () => (!0)),points:_nullishCoalesce(e.points[r], () => ([])),toggle:i}}exports.BrowserHistoryPort = p; exports.DatasetRegistry = _chunkPE27NATScjs.c; exports.FilterRegistry = _chunkPE27NATScjs.b; exports.ListingApp = _chunkPE27NATScjs.R; exports.ListingComponentsProvider = _chunkPE27NATScjs.p; exports.ListingConfig = _chunkPE27NATScjs.f; exports.ListingEngine = _chunkPE27NATScjs.g; exports.ListingEngineContext = _chunkPE27NATScjs.r; exports.ListingEventType = g; exports.ListingFilters = _chunkPE27NATScjs.v; exports.ListingList = _chunkPE27NATScjs.x; exports.ListingMap = _chunkPE27NATScjs.y; exports.ListingPagination = _chunkPE27NATScjs.z; exports.ListingProvider = _chunkPE27NATScjs.C; exports.ListingResultHeader = _chunkPE27NATScjs.A; exports.ListingStore = _chunkPE27NATScjs.a; exports.ListingToolbar = M; exports.MemoryHistoryPort = y; exports.PaginationMode = m; exports.TypedEmitter = _chunkPE27NATScjs.d; exports.UrlSyncController = c; exports.composeListingProviders = _chunkPE27NATScjs.h; exports.listingDefaultConfig = _chunkPE27NATScjs.e; exports.useListing = _chunkPE27NATScjs.s; exports.useListingComponents = _chunkPE27NATScjs.q; exports.useListingEvent = _chunkPE27NATScjs.B; exports.useListingFilters = _chunkPE27NATScjs.u; exports.useListingLayer = yt; exports.useListingMap = ot; exports.useListingResults = _chunkPE27NATScjs.w; exports.useListingState = _chunkPE27NATScjs.t; exports.withConfig = _chunkPE27NATScjs.i; exports.withDataset = _chunkPE27NATScjs.k; exports.withFilters = _chunkPE27NATScjs.l; exports.withInitialFilters = _chunkPE27NATScjs.n; exports.withMap = _chunkPE27NATScjs.j; exports.withPrimaryDataset = _chunkPE27NATScjs.o; exports.withUrlSync = _chunkPE27NATScjs.m;
package/dist/index.d.cts CHANGED
@@ -1,15 +1,9 @@
1
- import { P as Page, B as Bounds, E as EntityId, M as MapPoint, Q as QueryParams, L as LatLng } from './entity-adapter.interface-BDgbSxhq.cjs';
2
- export { a as EntityAdapter, b as PageRequest } from './entity-adapter.interface-BDgbSxhq.cjs';
3
- import { U as Unsubscribe, M as MapProvider } from './map-provider.interface-BMnwW3ob.cjs';
4
- export { a as MapHandle, b as MapInitOptions, R as RenderedLayer } from './map-provider.interface-BMnwW3ob.cjs';
5
- import { D as DatasetDefinition, F as FilterRegistry, I as IListingToolbarProps } from './components-provider-CF0Mo0B8.cjs';
6
- export { C as ClusterOptions, a as FilterControlProps, b as FilterDefinition, c as IListingCardProps, d as IListingComponents, e as IListingEmptyProps, f as IListingFilterPanelProps, g as IListingLoadingProps, h as IListingMarkerProps, i as IListingPopupProps, j as IListingResultHeaderProps, k as IListingSearchProps, l as IListingSidebarProps, L as ListingComponentsProvider, M as MarkerRenderer, u as useListingComponents } from './components-provider-CF0Mo0B8.cjs';
7
- import { P as PaginationMode, I as IListingConfigOptions } from './listing-config-options.interface-ZJYY_ZvR.cjs';
8
- import { H as HistoryPort, U as UrlSyncController } from './url-sync.controller-CsU_QxoS.cjs';
9
- export { a as UrlSyncEngine, b as UrlSyncOptions } from './url-sync.controller-CsU_QxoS.cjs';
1
+ import { P as Page, B as Bounds, E as EntityId, M as MapPoint, U as Unsubscribe, a as MapProvider, Q as QueryParams, L as LatLng } from './map-provider.interface-DT-v1plm.cjs';
2
+ export { b as EntityAdapter, c as MapHandle, d as MapInitOptions, e as PageRequest, R as RenderedLayer } from './map-provider.interface-DT-v1plm.cjs';
3
+ import { P as PaginationMode, D as DatasetDefinition, I as IListingConfigOptions, F as FilterRegistry, a as IListingToolbarProps } from './listing-app-4Vs_SMNl.cjs';
4
+ export { C as ClusterOptions, b as FilterControlProps, c as FilterDefinition, d as IListingCardProps, e as IListingComponents, f as IListingEmptyProps, g as IListingFilterPanelProps, h as IListingLoadingProps, i as IListingMarkerProps, j as IListingPopupProps, k as IListingResultHeaderProps, l as IListingSearchProps, m as IListingSidebarProps, L as ListingApp, n as ListingAppProps, o as ListingComponentsProvider, M as MarkerRenderer, u as useListingComponents } from './listing-app-4Vs_SMNl.cjs';
10
5
  import * as react from 'react';
11
6
  import { ReactNode } from 'react';
12
- export { L as ListingApp, a as ListingAppProps } from './listing-app-DJbvx3oe.cjs';
13
7
 
14
8
  declare enum ListingEventType {
15
9
  FiltersChanged = "FiltersChanged",
@@ -216,6 +210,18 @@ declare class ListingEngine<TEntity, TFilters> {
216
210
  private clearDebounce;
217
211
  }
218
212
 
213
+ /**
214
+ * DOM-agnostic abstraction over "the URL's query string" — or, in tests/SSR,
215
+ * an in-memory stand-in (`MemoryHistoryPort`). `UrlSyncController` talks to
216
+ * this interface only, never to `window.location`/`history` directly, so it
217
+ * stays framework- and environment-free like the rest of `src/core`.
218
+ */
219
+ interface HistoryPort {
220
+ getQuery(): QueryParams;
221
+ setQuery(params: QueryParams): void;
222
+ subscribe(cb: () => void): () => void;
223
+ }
224
+
219
225
  /**
220
226
  * In-memory `HistoryPort` — holds `QueryParams` in a plain field instead of
221
227
  * the browser URL. Used by tests (fast, no DOM/jsdom `history` needed) and by
@@ -231,6 +237,79 @@ declare class MemoryHistoryPort implements HistoryPort {
231
237
  private notify;
232
238
  }
233
239
 
240
+ interface UrlSyncEngine<TFilters> {
241
+ subscribe(cb: () => void): () => void;
242
+ readonly state: {
243
+ readonly filters: TFilters;
244
+ };
245
+ applyFilters(patch: Partial<TFilters>): Promise<void> | void;
246
+ }
247
+ interface UrlSyncOptions<TFilters> {
248
+ history: HistoryPort;
249
+ toQuery(filters: TFilters): QueryParams;
250
+ toFilters(query: QueryParams): Partial<TFilters>;
251
+ hydrateOnStart?: boolean;
252
+ }
253
+ /**
254
+ * Bidirectional, DOM-agnostic sync between an engine's `filters` and a
255
+ * `HistoryPort`'s query params. Framework-free: talks to `UrlSyncEngine`
256
+ * (a structural subset `ListingEngine` satisfies) and `HistoryPort`, nothing
257
+ * else — no `window`, no React.
258
+ *
259
+ * OPTIONAL, not the primary path: `styled/listing-app.tsx`'s turnkey
260
+ * `ListingApp` (the package's main-entry default) does NOT wire this up —
261
+ * it uses an event-based URL API instead (`initialFilters` in,
262
+ * `onFiltersChange` out; see that file's doc comment), so the library never
263
+ * touches `window.history` by default. This class remains exported for
264
+ * consumers who explicitly want the library to own history writes/reads
265
+ * itself, by wiring it directly against a hand-built engine.
266
+ *
267
+ * Echo-loop guard: both subscriptions below are driven by the SAME
268
+ * `isSyncing` flag. `ListingEngine.applyFilters()` and `MemoryHistoryPort`
269
+ * (and any real browser HistoryPort) both notify their subscribers
270
+ * SYNCHRONOUSLY as part of the write (`store.setFilters()` -> `notify()`
271
+ * happens before any debounce timer, and `MemoryHistoryPort.setQuery()`
272
+ * notifies before returning) — so when this controller initiates a write on
273
+ * one side, the reciprocal subscription on the other side fires within the
274
+ * very same call stack, before `isSyncing` is reset. Wrapping each
275
+ * controller-initiated write in `isSyncing = true; ...; isSyncing = false`
276
+ * (via try/finally, so a throwing `toQuery`/`toFilters`/notify can't leave it
277
+ * stuck) means that reciprocal callback observes `isSyncing === true` and
278
+ * short-circuits instead of writing back — one hop each direction, never a
279
+ * cascade. `hydrateOnStart`'s initial engine write is wrapped the same way,
280
+ * since it must not immediately echo the just-hydrated filters back out to
281
+ * history as a redundant `setQuery`.
282
+ *
283
+ * `isSyncing` only covers that SYNCHRONOUS window, though — the real
284
+ * `ListingEngine.applyFilters()` notifies AGAIN, asynchronously: after
285
+ * `await adapter.list(...)` resolves, `store.setResults()` and
286
+ * `store.setLoading(false)` each call `notify()`, well after `isSyncing` has
287
+ * already been reset to `false` by the `withSyncGuard` that kicked the query
288
+ * off. Left unguarded, that async tail would fire the engine subscription
289
+ * below and echo an identical (but spurious) `history.setQuery(...)` on
290
+ * every completed query. Rather than widening `isSyncing` to cover the whole
291
+ * async query (which would also swallow legitimate concurrent history
292
+ * changes that land mid-query), `syncEngineToHistory` is idempotent BY
293
+ * VALUE instead: it computes the target query and skips the write entirely
294
+ * when it already matches `history.getQuery()` — timing-independent, so it
295
+ * suppresses the async-tail echo without touching `isSyncing` at all.
296
+ */
297
+ declare class UrlSyncController<TFilters> {
298
+ private readonly history;
299
+ private readonly toQueryFn;
300
+ private readonly toFiltersFn;
301
+ private readonly hydrateOnStart;
302
+ private isSyncing;
303
+ private unsubscribeEngine;
304
+ private unsubscribeHistory;
305
+ constructor(opts: UrlSyncOptions<TFilters>);
306
+ start(engine: UrlSyncEngine<TFilters>): void;
307
+ stop(): void;
308
+ private syncEngineToHistory;
309
+ private applyFiltersSafely;
310
+ private withSyncGuard;
311
+ }
312
+
234
313
  /**
235
314
  * Aggregated provider props — the composition entry point of
236
315
  * react-listing-engine, mirroring `react-wizard-engine`'s
@@ -299,8 +378,8 @@ interface IListingFiltersProps {
299
378
  * styling hook -- this component stays structure-only, so it never bakes
300
379
  * in its own layout beyond the `space-y-5` fallback below. Pass a
301
380
  * horizontal row (e.g. `"flex flex-wrap items-end gap-3"`) to flow filter
302
- * groups inline instead of stacking them, as `/shadcn`'s `ListingLayout`
303
- * does for its top filter bar.
381
+ * groups inline instead of stacking them, as the `/styled` adapter's
382
+ * `StyledListingLayout` does for its top filter bar.
304
383
  */
305
384
  className?: string;
306
385
  /** ClassName applied to each individual filter group's wrapper `<div>` (including the string-placeholder case). */
@@ -325,7 +404,7 @@ interface IListingFiltersProps {
325
404
  * `"text"`/`"range"`/`"toggle"`): there is no shared named-control registry
326
405
  * yet, so this renders an inert `<div data-filter={def.key} />` placeholder.
327
406
  * Wiring a real named-control registry (so `"range"` etc. resolve to an
328
- * actual control, likely in the `/shadcn` adapter) is a documented future
407
+ * actual control) is a documented future
329
408
  * enhancement, not attempted in this task.
330
409
  *
331
410
  * Each filter group is wrapped in its own `<div>`: when `def.label` is set,
@@ -491,8 +570,8 @@ declare function ListingMap(props: IListingMapProps): react.JSX.Element;
491
570
  *
492
571
  * Renders a plain `<button>` rather than an injected component: `useListingComponents()`
493
572
  * (`IListingComponents`) has no `Button` slot yet, so there is nothing to
494
- * delegate to here. Wiring this up to a real Button slot (and to the shadcn
495
- * adapter's styled Button) is a documented future enhancement once that slot
573
+ * delegate to here. Wiring this up to a real Button slot (and a styled Button
574
+ * in the `/styled` adapter) is a documented future enhancement once that slot
496
575
  * exists.
497
576
  */
498
577
  declare function ListingPagination(): react.JSX.Element | null;
@@ -679,4 +758,4 @@ interface IListingProviderComponentProps<TFilters> extends IListingProviderProps
679
758
  */
680
759
  declare function ListingProvider<TEntity, TFilters>(props: IListingProviderComponentProps<TFilters>): react.JSX.Element | null;
681
760
 
682
- export { Bounds, BrowserHistoryPort, type BrowserHistoryPortOptions, DatasetDefinition, DatasetRegistry, EntityId, FilterRegistry, HistoryPort, IListingConfigOptions, type IListingFiltersProps, type IListingMapProps, type IListingProviderProps, IListingToolbarProps, LatLng, ListingConfig, ListingEngine, ListingEngineContext, type ListingEngineOptions, type ListingEvent, ListingEventType, ListingFilters, ListingList, ListingMap, ListingPagination, ListingProvider, type ListingProviderMod, ListingResultHeader, type ListingState, ListingStore, type ListingStoreInit, ListingToolbar, MapPoint, MapProvider, MemoryHistoryPort, Page, PaginationMode, QueryParams, TypedEmitter, Unsubscribe, UrlSyncController, composeListingProviders, listingDefaultConfig, useListing, useListingEvent, useListingFilters, useListingLayer, useListingMap, useListingResults, useListingState, withConfig, withDataset, withFilters, withInitialFilters, withMap, withPrimaryDataset, withUrlSync };
761
+ export { Bounds, BrowserHistoryPort, type BrowserHistoryPortOptions, DatasetDefinition, DatasetRegistry, EntityId, FilterRegistry, type HistoryPort, IListingConfigOptions, type IListingFiltersProps, type IListingMapProps, type IListingProviderProps, IListingToolbarProps, LatLng, ListingConfig, ListingEngine, ListingEngineContext, type ListingEngineOptions, type ListingEvent, ListingEventType, ListingFilters, ListingList, ListingMap, ListingPagination, ListingProvider, type ListingProviderMod, ListingResultHeader, type ListingState, ListingStore, type ListingStoreInit, ListingToolbar, MapPoint, MapProvider, MemoryHistoryPort, Page, PaginationMode, QueryParams, TypedEmitter, Unsubscribe, UrlSyncController, type UrlSyncEngine, type UrlSyncOptions, composeListingProviders, listingDefaultConfig, useListing, useListingEvent, useListingFilters, useListingLayer, useListingMap, useListingResults, useListingState, withConfig, withDataset, withFilters, withInitialFilters, withMap, withPrimaryDataset, withUrlSync };