react-listing-engine 0.6.5 → 0.6.7
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.
- package/README.md +33 -41
- package/dist/chunk-4CZJODMK.cjs +2 -0
- package/dist/chunk-GSWGCJCO.js +2 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +93 -11
- package/dist/index.d.ts +93 -11
- package/dist/index.js +1 -1
- package/dist/listing-app-BtNNEkX1.d.cts +347 -0
- package/dist/listing-app-IG1awue3.d.ts +347 -0
- package/dist/styled/index.cjs +1 -1
- package/dist/styled/index.d.cts +6 -7
- package/dist/styled/index.d.ts +6 -7
- package/dist/styled/index.js +1 -1
- package/dist/styles.css +31 -99
- package/package.json +12 -27
- package/dist/chunk-2VUHLHHX.cjs +0 -2
- package/dist/chunk-35KYEBAC.js +0 -2
- package/dist/chunk-CGT3RHJ7.js +0 -2
- package/dist/chunk-EYXV5OMY.cjs +0 -2
- package/dist/chunk-R3FX2V3N.js +0 -2
- package/dist/chunk-RXHR66FS.cjs +0 -2
- package/dist/components-provider-FinfaqUT.d.cts +0 -131
- package/dist/components-provider-cWVWEDXC.d.ts +0 -131
- package/dist/listing-app-BMoMKD4c.d.cts +0 -213
- package/dist/listing-app-BSJKGh7k.d.ts +0 -213
- package/dist/shadcn/index.cjs +0 -2
- package/dist/shadcn/index.d.cts +0 -259
- package/dist/shadcn/index.d.ts +0 -259
- package/dist/shadcn/index.js +0 -2
- package/dist/url-sync.controller-DK5W_0OZ.d.ts +0 -89
- package/dist/url-sync.controller-DXSIWgTa.d.cts +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
|
|
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
|
|
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 `/
|
|
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,20 +27,15 @@ pnpm add react-listing-engine
|
|
|
27
27
|
|
|
28
28
|
## Quickstart
|
|
29
29
|
|
|
30
|
-
Minimal setup — one dataset, one filter, Google Maps,
|
|
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
|
-
|
|
35
|
-
ListingProvider,
|
|
36
|
-
withDataset,
|
|
37
|
-
withFilters,
|
|
38
|
-
withMap,
|
|
34
|
+
ListingApp,
|
|
39
35
|
type EntityAdapter,
|
|
40
36
|
type FilterControlProps,
|
|
41
37
|
} from 'react-listing-engine';
|
|
42
|
-
import
|
|
43
|
-
import { googleProvider } from 'react-listing-engine/maps/google';
|
|
38
|
+
import 'react-listing-engine/styles.css';
|
|
44
39
|
|
|
45
40
|
interface Property { id: string; title: string; price: number; lat: number; lng: number }
|
|
46
41
|
interface Filters { q?: string }
|
|
@@ -55,25 +50,19 @@ const SearchControl = ({ onChange, value }: FilterControlProps<string>) => (
|
|
|
55
50
|
|
|
56
51
|
export function PropertySearch() {
|
|
57
52
|
return (
|
|
58
|
-
<
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)}
|
|
72
|
-
>
|
|
73
|
-
<ListingComponentsProviderWithDefaults>
|
|
74
|
-
<ListingLayout />
|
|
75
|
-
</ListingComponentsProviderWithDefaults>
|
|
76
|
-
</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
|
+
/>
|
|
77
66
|
);
|
|
78
67
|
}
|
|
79
68
|
```
|
|
@@ -84,8 +73,8 @@ The engine is layered so you can go as deep as you need and stop:
|
|
|
84
73
|
|
|
85
74
|
1. **Data** — implement `EntityAdapter<TEntity, TFilters>` against your own API. Nothing in the engine assumes a specific backend or entity shape.
|
|
86
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`).
|
|
87
|
-
3. **Presentation** — swap any slot via `ListingComponentsProvider` (or start from `
|
|
88
|
-
4. **Layout** — skip `
|
|
76
|
+
3. **Presentation** — swap any slot via `ListingComponentsProvider` (or start from `react-listing-engine/styled`'s `StyledComponentsProviderWithDefaults` 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.
|
|
77
|
+
4. **Layout** — skip `StyledListingLayout` entirely and arrange the structure-only compound components yourself: `ListingList`, `ListingMap`, `ListingFilters`, `ListingResultHeader`, `ListingToolbar`, `ListingPagination`.
|
|
89
78
|
|
|
90
79
|
## Google Maps setup
|
|
91
80
|
|
|
@@ -121,20 +110,23 @@ Additional datasets are **map-only** layers: only the primary dataset (the first
|
|
|
121
110
|
|
|
122
111
|
## Styled adapter
|
|
123
112
|
|
|
124
|
-
|
|
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:
|
|
125
114
|
|
|
126
115
|
```tsx
|
|
127
|
-
import {
|
|
116
|
+
import { ListingApp } from 'react-listing-engine';
|
|
117
|
+
import 'react-listing-engine/styles.css';
|
|
128
118
|
```
|
|
129
119
|
|
|
130
|
-
|
|
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:
|
|
121
|
+
|
|
122
|
+
```css
|
|
123
|
+
@import 'react-listing-engine/styles.css';
|
|
131
124
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
- 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.
|
|
125
|
+
:root {
|
|
126
|
+
--rle-primary: #0ea5e9;
|
|
127
|
+
--rle-radius: 4px;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
138
130
|
|
|
139
131
|
## Hooks
|
|
140
132
|
|
|
@@ -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 H= (_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 N= (_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)}clearedParams(){return this.list().reduce((e,i)=>Object.assign(e,i.toParams(i.fromParams({}))),{})}}, _class2);var z= (_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 V=class{constructor(e){this.options=Object.freeze({...ge,...e})}};var K= (_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 N)),this.map=e.map,this.config=new V(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 H({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 N,datasets:new z};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 Ze(t){return String(_nullishCoalesce(_optionalChain([t, 'optionalAccess', _16 => _16.title]), () => ("")))}var Xe=({item:t})=>_jsxruntime.jsx.call(void 0, "div",{children:Ze(t)}),Je=()=>_jsxruntime.jsx.call(void 0, "div",{}),Ye=()=>_jsxruntime.jsx.call(void 0, "div",{}),et=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),tt=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),it=({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"))}),rt=()=>_jsxruntime.jsx.call(void 0, "div",{role:"status",children:"No results"}),nt=()=>_jsxruntime.jsx.call(void 0, "div",{role:"status","aria-busy":"true",children:"Loading\u2026"}),st=({count:t})=>_jsxruntime.jsxs.call(void 0, "div",{children:[t," results"]}),ot=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),P={Card:Xe,Marker:Je,Popup:Ye,Sidebar:et,FilterPanel:tt,Search:it,Empty:rt,Loading:nt,ResultHeader:st,Toolbar:ot},Te=_react.createContext.call(void 0, P);function Pe(t){let{Card:e,Marker:i,Popup:r,Sidebar:n,FilterPanel:s,Search:o,Empty:a,Loading:l,ResultHeader:c,Toolbar:d,children:v}=t,L={Card:_nullishCoalesce(e, () => (P.Card)),Marker:_nullishCoalesce(i, () => (P.Marker)),Popup:_nullishCoalesce(r, () => (P.Popup)),Sidebar:_nullishCoalesce(n, () => (P.Sidebar)),FilterPanel:_nullishCoalesce(s, () => (P.FilterPanel)),Search:_nullishCoalesce(o, () => (P.Search)),Empty:_nullishCoalesce(a, () => (P.Empty)),Loading:_nullishCoalesce(l, () => (P.Loading)),ResultHeader:_nullishCoalesce(c, () => (P.ResultHeader)),Toolbar:_nullishCoalesce(d, () => (P.Toolbar))};return _jsxruntime.jsx.call(void 0, Te.Provider,{value:L,children:v})}function F(){return _react.useContext.call(void 0, Te)}var $=_react.createContext.call(void 0, null);function y(){let t=_react.useContext.call(void 0, $);if(t===null)throw new Error("useListing must be used within a <ListingProvider>");return t}function S(){let t=y(),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 W(){let t=y(),e=S(),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 Y({className:t,groupClassName:e,hideLabels:i}={}){let r=y(),{FilterPanel:n}=F(),{filters:s}=W();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 R(){return S().results}function ut(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=y(),{items:i}=R(),{pagination:r,selection:n}=S(),{Card:s,Empty:o,Loading:a}=F();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,c)=>{let d=ut(l,c);return _jsxruntime.jsx.call(void 0, s,{item:l,selected:n===d,onSelect:()=>e.selectPoint(e.primaryDatasetId,d)},d)})})}var gt={west:-179.9,south:-85,east:179.9,north:85},Ie=.1,we=.02;function ft(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=y(),s=S(),o=_react.useRef.call(void 0, null),a=_react.useRef.call(void 0, null),[l,c]=_react.useState.call(void 0, !1),d=_react.useRef.call(void 0, !1),v=_react.useRef.call(void 0, !1),L=_react.useRef.call(void 0, !1),p=n.map;return _react.useEffect.call(void 0, ()=>{let b=a.current;if(!p||!b)return;let u=[];for(let m of Object.keys(s.points)){if(s.layers[m]===!1)continue;let f=n.datasets.get(m),I=_nullishCoalesce(s.points[m], () => ([])),J={id:m,markers:I.map(E=>({id:E.id,position:E.position,iconUrl:_optionalChain([f, 'optionalAccess', _17 => _17.marker, 'access', _18 => _18.iconUrl, 'optionalCall', _19 => _19(E.entity)]),element:_optionalChain([f, 'optionalAccess', _20 => _20.marker, 'access', _21 => _21.element, 'optionalCall', _22 => _22(E.entity)])})),clustering:_optionalChain([f, 'optionalAccess', _23 => _23.clustering]),onMarkerClick:E=>n.selectPoint(m,E)};u.push(p.renderLayer(b,J))}return()=>{u.forEach(m=>m())}},[n,p,l,s.points,s.layers]),_react.useEffect.call(void 0, ()=>{if(!o.current||!p)return;let b=o.current,u=!1,m=null;return(async()=>{let f=await p.mount(b,{center:e,zoom:i});if(u){p.destroy(f);return}a.current=f,m=p.onBoundsChange(f,I=>{L.current?L.current=!1:v.current=!0,n.loadPoints(I)}),c(!0),n.loadPoints(gt)})(),()=>{u=!0,_optionalChain([m, 'optionalCall', _24 => _24()]),a.current&&(p.destroy(a.current),a.current=null),c(!1)}},[n,p]),_react.useEffect.call(void 0, ()=>{let b=a.current;if(!p||!b||e||d.current||v.current)return;let u=[];for(let f of Object.keys(s.points))if(s.layers[f]!==!1)for(let I of _nullishCoalesce(s.points[f], () => ([])))u.push(I.position);let m=ft(u);m&&(d.current=!0,L.current=!0,p.fitBounds(b,m))},[p,e,l,s.points,s.layers]),_jsxruntime.jsx.call(void 0, "div",{ref:o,className:!p&&r?"flex h-full min-h-0 w-full items-center justify-center":"h-full min-h-0 w-full",children:!p&&r})}function Ee(){let t=y(),{results:e,pagination:i}=S();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}=R(),{ResultHeader:i}=F();return _jsxruntime.jsx.call(void 0, i,{count:t.length,total:e})}function Ne(t,e){let i=y(),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 K({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, $.Provider,{value:n,children:e}):null}function M(t){return typeof t!="number"?t:new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0}).format(t)}function Ft(t){return t?"rle-card rle-card--selected":"rle-card"}function te({item:t,selected:e,onSelect:i}){let r=_nullishCoalesce(t, () => ({})),n=Ft(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:M(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 ie(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 re({children:t}){return _jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-panel",children:t})}function ne(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 se({point:t}){let e=_nullishCoalesce(t.entity, () => ({}));return _jsxruntime.jsx.call(void 0, "span",{className:"rle-pin",children:e.price!=null?M(e.price):""})}function ae({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:M(i.price)})]})]})}function le({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 de({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 pe({children:t}){return _jsxruntime.jsx.call(void 0, "aside",{className:"rle-sidebar",children:t})}function ce({children:t}){return _jsxruntime.jsx.call(void 0, "div",{className:"rle-toolbar",children:t})}var Oe={Card:te,Marker:se,Popup:ae,Sidebar:pe,FilterPanel:re,Search:de,Empty:ie,Loading:ne,ResultHeader:le,Toolbar:ce};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, Rt,{}),"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, Mt,{}),"Map"]})]})})}function Rt(){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 Mt(){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"})]})}var _reactdom = require('react-dom');function He({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,c]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>{if(!t){c(!1),a(!1);return}a(!0);let d=requestAnimationFrame(()=>c(!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, Ot,{})})]}),_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 Ot(){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 ze({search:t,onFiltersClick:e,filterCount:i=0,action:r}){let{Search:n}=F();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-btn rle-mobile-header__btn",onClick:e,children:[_jsxruntime.jsx.call(void 0, At,{}),_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-btn 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, Ht,{})))})]})}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","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 Ht(){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 Ut=_jsxruntime.jsx.call(void 0, "div",{className:"rle-empty",children:"Map unavailable"});function Vt(){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:v,scrollLeft:L,scrollWidth:p}=s;i(L<=0),n(L+v>=p-1)},a=0,l=()=>{a||(a=requestAnimationFrame(()=>{a=0,o()}))};o(),s.addEventListener("scroll",o,{passive:!0});let c,d;return typeof ResizeObserver<"u"&&(c=new ResizeObserver(o),c.observe(s)),typeof MutationObserver<"u"&&(d=new MutationObserver(l),d.observe(s,{characterData:!0,childList:!0,subtree:!0})),()=>{s.removeEventListener("scroll",o),_optionalChain([c, 'optionalAccess', _28 => _28.disconnect, 'call', _29 => _29()]),_optionalChain([d, 'optionalAccess', _30 => _30.disconnect, 'call', _31 => _31()]),a&&cancelAnimationFrame(a)}},[]),{atEnd:r,atStart:e,ref:t}}function Ve({search:t,toolbarEnd:e,mobileAction:i,autoFetch:r=!0,hasMap:n,mapCenter:s,mapZoom:o,className:a}){let l=y(),{Search:c}=F(),d=R(),{filters:v,set:L}=W(),p=_nullishCoalesce(n, () => (l.map!=null)),[b,u]=_react.useState.call(void 0, "list"),[m,f]=_react.useState.call(void 0, !1),{atEnd:I,atStart:J,ref:E}=Vt(),_=t?{value:String(_nullishCoalesce(v[t.filterKey], () => (""))),onChange:je=>{L({[t.filterKey]:je||void 0})},placeholder:t.placeholder}:void 0;_react.useEffect.call(void 0, ()=>{r!==!1&&l.applyFilters({})},[l,r]);let $e=()=>{L(l.filters.clearedParams())},We=l.filters.activeKeys(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:E,children:[_&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__search",children:_jsxruntime.jsx.call(void 0, c,{value:_.value,onChange:_.onChange,placeholder:_.placeholder})}),_jsxruntime.jsx.call(void 0, Y,{className:"rle-filters-row",groupClassName:"rle-filter-group",hideLabels:!0})]}),!J&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__fade rle-filter-bar__fade--start","aria-hidden":"true"}),!I&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__fade rle-filter-bar__fade--end","aria-hidden":"true"})]}),_jsxruntime.jsx.call(void 0, ze,{search:_,onFiltersClick:()=>f(!0),filterCount:We,action:i}),_jsxruntime.jsxs.call(void 0, "div",{className:`rle-body ${p?"rle-split":"rle-body--list-only"}`,"data-mobile-view":b,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,{})]}),p&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-map",children:_jsxruntime.jsx.call(void 0, ke,{center:s,zoom:o,fallback:Ut})})]}),p&&_jsxruntime.jsx.call(void 0, Be,{view:b,onViewChange:u}),_jsxruntime.jsx.call(void 0, He,{title:"Filters",open:m,onOpenChange:f,footer:_jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[_jsxruntime.jsx.call(void 0, "button",{type:"button",className:"rle-btn rle-btn--ghost",onClick:$e,children:"Clear all"}),_jsxruntime.jsxs.call(void 0, "button",{type:"button",className:"rle-btn rle-btn--primary",onClick:()=>f(!1),children:["Show ",qe," results"]})]}),children:_jsxruntime.jsx.call(void 0, Y,{className:"rle-filter-stack",groupClassName:"rle-filter-group"})})]})}function Ke(t){return"provider"in t}function jt(t){let e=t!=null&&!Ke(t),i=e?t.apiKey:void 0,r=e?t.mapId:void 0,[n,s]=_react.useState.call(void 0, ()=>!t||Ke(t)?{ready:!0,provider:_optionalChain([t, 'optionalAccess', _32 => _32.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 mn(t){let{datasets:e,filters:i,map:r,components:n,initialFilters:s,onFiltersChange:o,mobileAction:a,search:l,toolbarEnd:c,config:d,autoFetch:v,className:L}=t,{ready:p,provider:b}=jt(r);if(!p)return null;let u=[];for(let I of e)u.push(ve(I));i&&u.push(Le(i)),b&&u.push(he(b)),s&&u.push(be(s)),d&&u.push(ye(d));let m=fe(...u),f={...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,{...f,children:_jsxruntime.jsx.call(void 0, Ve,{className:L,search:l,toolbarEnd:c,mobileAction:a,autoFetch:v,mapCenter:_optionalChain([r, 'optionalAccess', _33 => _33.center]),mapZoom:_optionalChain([r, 'optionalAccess', _34 => _34.zoom])})})]})}exports.a = H; exports.b = N; exports.c = z; exports.d = U; exports.e = ge; exports.f = V; exports.g = K; 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 = F; exports.r = $; exports.s = y; exports.t = S; exports.u = W; exports.v = Y; exports.w = R; exports.x = Se; exports.y = ke; exports.z = Ee; exports.A = xe; exports.B = Ne; exports.C = Me; exports.D = te; exports.E = ie; exports.F = re; exports.G = ne; exports.H = se; exports.I = ae; exports.J = le; exports.K = de; exports.L = pe; exports.M = ce; exports.N = Oe; exports.O = Be; exports.P = He; exports.Q = Ve; exports.R = mn;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var H=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 N=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)}clearedParams(){return this.list().reduce((e,i)=>Object.assign(e,i.toParams(i.fromParams({}))),{})}};var z=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 V=class{options;constructor(e){this.options=Object.freeze({...ge,...e})}};var K=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 N,this.map=e.map,this.config=new V(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 H({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 N,datasets:new z};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 Qe,useContext as Ge}from"react";import{jsx as C,jsxs as at}from"react/jsx-runtime";function Ze(t){return String(t?.title??"")}var Xe=({item:t})=>C("div",{children:Ze(t)}),Je=()=>C("div",{}),Ye=()=>C("div",{}),et=({children:t})=>C("div",{children:t}),tt=({children:t})=>C("div",{children:t}),it=({value:t,onChange:e,placeholder:i})=>C("input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":i??"Search"}),rt=()=>C("div",{role:"status",children:"No results"}),nt=()=>C("div",{role:"status","aria-busy":"true",children:"Loading\u2026"}),st=({count:t})=>at("div",{children:[t," results"]}),ot=({children:t})=>C("div",{children:t}),P={Card:Xe,Marker:Je,Popup:Ye,Sidebar:et,FilterPanel:tt,Search:it,Empty:rt,Loading:nt,ResultHeader:st,Toolbar:ot},Te=Qe(P);function Pe(t){let{Card:e,Marker:i,Popup:r,Sidebar:n,FilterPanel:s,Search:o,Empty:a,Loading:l,ResultHeader:c,Toolbar:d,children:v}=t,L={Card:e??P.Card,Marker:i??P.Marker,Popup:r??P.Popup,Sidebar:n??P.Sidebar,FilterPanel:s??P.FilterPanel,Search:o??P.Search,Empty:a??P.Empty,Loading:l??P.Loading,ResultHeader:c??P.ResultHeader,Toolbar:d??P.Toolbar};return C(Te.Provider,{value:L,children:v})}function F(){return Ge(Te)}import{createContext as lt}from"react";var $=lt(null);import{useContext as dt}from"react";function y(){let t=dt($);if(t===null)throw new Error("useListing must be used within a <ListingProvider>");return t}import{useCallback as Fe,useSyncExternalStore as pt}from"react";function S(){let t=y(),e=Fe(r=>t.subscribe(r),[t]),i=Fe(()=>t.state,[t]);return pt(e,i,i)}import{useCallback as Ce}from"react";function W(){let t=y(),e=S(),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 O,jsxs as ct}from"react/jsx-runtime";function Y({className:t,groupClassName:e,hideLabels:i}={}){let r=y(),{FilterPanel:n}=F(),{filters:s}=W();return O(n,{children:O("div",{className:t??"space-y-5",children:r.filters.list().map(o=>{if(typeof o.render=="string")return O("div",{"data-filter":o.key,className:e},o.key);let a=o.render;return ct("div",{className:e,children:[o.label&&!i&&O("div",{className:"mb-1.5 text-[13px] font-medium text-foreground",children:o.label}),O(a,{value:o.fromParams(s),onChange:l=>{r.applyFilters(o.toParams(l))}})]},o.key)})})})}function R(){return S().results}import{jsx as q}from"react/jsx-runtime";function ut(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=y(),{items:i}=R(),{pagination:r,selection:n}=S(),{Card:s,Empty:o,Loading:a}=F();return r.loading&&i.length===0?q(a,{}):i.length===0?q(o,{}):q("div",{role:"list",className:t,children:i.map((l,c)=>{let d=ut(l,c);return q(s,{item:l,selected:n===d,onSelect:()=>e.selectPoint(e.primaryDatasetId,d)},d)})})}import{useEffect as ee,useRef as B,useState as mt}from"react";import{jsx as yt}from"react/jsx-runtime";var gt={west:-179.9,south:-85,east:179.9,north:85},Ie=.1,we=.02;function ft(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=y(),s=S(),o=B(null),a=B(null),[l,c]=mt(!1),d=B(!1),v=B(!1),L=B(!1),p=n.map;return ee(()=>{let b=a.current;if(!p||!b)return;let u=[];for(let m of Object.keys(s.points)){if(s.layers[m]===!1)continue;let f=n.datasets.get(m),I=s.points[m]??[],J={id:m,markers:I.map(E=>({id:E.id,position:E.position,iconUrl:f?.marker.iconUrl?.(E.entity),element:f?.marker.element?.(E.entity)})),clustering:f?.clustering,onMarkerClick:E=>n.selectPoint(m,E)};u.push(p.renderLayer(b,J))}return()=>{u.forEach(m=>m())}},[n,p,l,s.points,s.layers]),ee(()=>{if(!o.current||!p)return;let b=o.current,u=!1,m=null;return(async()=>{let f=await p.mount(b,{center:e,zoom:i});if(u){p.destroy(f);return}a.current=f,m=p.onBoundsChange(f,I=>{L.current?L.current=!1:v.current=!0,n.loadPoints(I)}),c(!0),n.loadPoints(gt)})(),()=>{u=!0,m?.(),a.current&&(p.destroy(a.current),a.current=null),c(!1)}},[n,p]),ee(()=>{let b=a.current;if(!p||!b||e||d.current||v.current)return;let u=[];for(let f of Object.keys(s.points))if(s.layers[f]!==!1)for(let I of s.points[f]??[])u.push(I.position);let m=ft(u);m&&(d.current=!0,L.current=!0,p.fitBounds(b,m))},[p,e,l,s.points,s.layers]),yt("div",{ref:o,className:!p&&r?"flex h-full min-h-0 w-full items-center justify-center":"h-full min-h-0 w-full",children:!p&&r})}import{jsx as ht}from"react/jsx-runtime";function Ee(){let t=y(),{results:e,pagination:i}=S();return e.nextCursor==null?null:ht("button",{type:"button",disabled:i.loading,onClick:()=>{t.loadPage()},children:"Load more"})}import{jsx as vt}from"react/jsx-runtime";function xe(){let{items:t,total:e}=R(),{ResultHeader:i}=F();return vt(i,{count:t.length,total:e})}import{useEffect as Lt,useRef as bt}from"react";function Ne(t,e){let i=y(),r=bt(e);r.current=e,Lt(()=>i.on(t,n=>r.current(n)),[i,t])}import{useEffect as Tt,useState as Re}from"react";import{jsx as Pt}from"react/jsx-runtime";function Me(t){let{children:e,...i}=t,[r]=Re(()=>i),[n,s]=Re(null);return Tt(()=>{let o=new K({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?Pt($.Provider,{value:n,children:e}):null}function M(t){return typeof t!="number"?t:new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0}).format(t)}import{Fragment as Ct,jsx as w,jsxs as De}from"react/jsx-runtime";function Ft(t){return t?"rle-card rle-card--selected":"rle-card"}function te({item:t,selected:e,onSelect:i}){let r=t??{},n=Ft(e),s=De(Ct,{children:[r.imageUrl?w("img",{src:r.imageUrl,alt:r.title??"",className:"rle-card-media"}):w("div",{className:"rle-card-media rle-card-media--placeholder","aria-hidden":"true"}),De("div",{className:"rle-card-body",children:[r.title&&w("span",{className:"rle-card-title",children:r.title}),r.subtitle&&w("span",{className:"rle-card-address",children:r.subtitle}),r.badge&&w("div",{className:"rle-card-info",children:w("span",{className:"rle-card-info-item",children:r.badge})}),r.price!=null&&w("span",{className:"rle-card-price",children:M(r.price)})]})]});return i?w("button",{type:"button",onClick:i,"aria-pressed":e??!1,className:n,children:s}):w("article",{className:n,children:s})}import{jsx as j,jsxs as _e}from"react/jsx-runtime";function ie(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:[j("circle",{cx:"11",cy:"11",r:"7"}),j("path",{d:"m21 21-4.3-4.3"})]}),j("p",{className:"rle-empty-title",children:"No results"}),j("p",{className:"rle-empty-hint",children:"Try adjusting your filters or search terms."})]})}import{jsx as St}from"react/jsx-runtime";function re({children:t}){return St("div",{className:"rle-filter-panel",children:t})}import{jsx as Q,jsxs as It}from"react/jsx-runtime";function ne(t){return Q("div",{className:"rle-loading",role:"status","aria-busy":"true","aria-label":"Loading results",children:Array.from({length:3}).map((e,i)=>It("div",{className:"rle-loading-item",children:[Q("div",{className:"rle-skeleton",style:{aspectRatio:"4 / 3",width:"100%"}}),Q("div",{className:"rle-skeleton",style:{height:16,width:"65%"}}),Q("div",{className:"rle-skeleton",style:{height:12,width:"35%"}})]},i))})}import{jsx as wt}from"react/jsx-runtime";function se({point:t}){let e=t.entity??{};return wt("span",{className:"rle-pin",children:e.price!=null?M(e.price):""})}import{jsx as D,jsxs as oe}from"react/jsx-runtime";function ae({entity:t,onClose:e}){let i=t??{};return oe("div",{className:"rle-popup",role:"group","aria-label":"Location details",children:[D("button",{type:"button",onClick:e,"aria-label":"Close",className:"rle-popup-close",children:oe("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:[D("path",{d:"M18 6 6 18"}),D("path",{d:"m6 6 12 12"})]})}),oe("div",{children:[i.title&&D("div",{className:"rle-card-title",children:i.title}),i.subtitle&&D("div",{className:"rle-card-address",children:i.subtitle}),i.price!=null&&D("div",{className:"rle-card-price",children:M(i.price)})]})]})}import{jsx as kt}from"react/jsx-runtime";function le({count:t,total:e}){let i=e!=null&&e!==t?`${t} of ${e} results`:`${t} results`;return kt("div",{className:"rle-result-header",children:i})}import{jsx as Et}from"react/jsx-runtime";function de({value:t,onChange:e,placeholder:i}){return Et("input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":i??"Search",className:"rle-input"})}import{jsx as xt}from"react/jsx-runtime";function pe({children:t}){return xt("aside",{className:"rle-sidebar",children:t})}import{jsx as Nt}from"react/jsx-runtime";function ce({children:t}){return Nt("div",{className:"rle-toolbar",children:t})}var Oe={Card:te,Marker:se,Popup:ae,Sidebar:pe,FilterPanel:re,Search:de,Empty:ie,Loading:ne,ResultHeader:le,Toolbar:ce};import{jsx as T,jsxs as A}from"react/jsx-runtime";function Be({view:t,onViewChange:e}){return T("nav",{className:"rle-bottom-nav","aria-label":"Listing navigation",children:A("div",{className:"rle-viewtoggle",role:"group","aria-label":"View",children:[A("button",{type:"button",className:`rle-viewtoggle__btn${t==="list"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="list",onClick:()=>e("list"),children:[T(Rt,{}),"List"]}),A("button",{type:"button",className:`rle-viewtoggle__btn${t==="map"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="map",onClick:()=>e("map"),children:[T(Mt,{}),"Map"]})]})})}function Rt(){return A("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:[T("line",{x1:"8",y1:"6",x2:"20",y2:"6"}),T("line",{x1:"8",y1:"12",x2:"20",y2:"12"}),T("line",{x1:"8",y1:"18",x2:"20",y2:"18"}),T("line",{x1:"4",y1:"6",x2:"4.01",y2:"6"}),T("line",{x1:"4",y1:"12",x2:"4.01",y2:"12"}),T("line",{x1:"4",y1:"18",x2:"4.01",y2:"18"})]})}function Mt(){return A("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:[T("polygon",{points:"1 6 8 3 16 6 23 3 23 18 16 21 8 18 1 21 1 6"}),T("line",{x1:"8",y1:"3",x2:"8",y2:"18"}),T("line",{x1:"16",y1:"6",x2:"16",y2:"21"})]})}import{useEffect as ue,useRef as Dt,useState as Ae}from"react";import{createPortal as _t}from"react-dom";import{Fragment as Bt,jsx as k,jsxs as G}from"react/jsx-runtime";function He({open:t,onOpenChange:e,title:i,children:r,footer:n}){let s=Dt(null),[o,a]=Ae(!1),[l,c]=Ae(!1);return ue(()=>{if(!t){c(!1),a(!1);return}a(!0);let d=requestAnimationFrame(()=>c(!0));return()=>cancelAnimationFrame(d)},[t]),ue(()=>{if(!o)return;let d=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=d}},[o]),ue(()=>{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:_t(G(Bt,{children:[k("div",{className:`rle-sheet-backdrop${l?" rle-sheet-backdrop--open":""}`,onClick:()=>e(!1),"aria-hidden":"true"}),G("div",{ref:s,className:`rle-sheet${l?" rle-sheet--open":""}`,role:"dialog","aria-modal":"true","aria-label":i,tabIndex:-1,children:[k("div",{className:"rle-sheet__handle","aria-hidden":"true"}),G("div",{className:"rle-sheet__header",children:[i&&k("div",{className:"rle-sheet__title",children:i}),k("button",{type:"button",className:"rle-sheet__close",onClick:()=>e(!1),"aria-label":"Close",children:k(Ot,{})})]}),k("div",{className:"rle-sheet__body",children:r}),n&&k("div",{className:"rle-sheet__footer",children:n})]})]}),document.body)}function Ot(){return G("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:[k("path",{d:"M18 6 6 18"}),k("path",{d:"m6 6 12 12"})]})}import{useEffect as Ue,useRef as zt,useState as X}from"react";import{jsx as h,jsxs as Z}from"react/jsx-runtime";function ze({search:t,onFiltersClick:e,filterCount:i=0,action:r}){let{Search:n}=F();return Z("header",{className:"rle-mobile-header",children:[t&&h("div",{className:"rle-mobile-header__search",children:h(n,{value:t.value,onChange:t.onChange,placeholder:t.placeholder})}),Z("button",{type:"button",className:"rle-btn rle-mobile-header__btn",onClick:e,children:[h(At,{}),h("span",{children:"Filters"}),i>0&&h("span",{className:"rle-mobile-header__count",children:i})]}),r&&h("button",{type:"button",className:"rle-btn rle-mobile-header__btn rle-mobile-header__btn--icon",onClick:r.onClick,"aria-label":r.label,children:r.icon??h(Ht,{})})]})}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","aria-hidden":"true",children:[h("line",{x1:"4",y1:"6",x2:"20",y2:"6"}),h("circle",{cx:"9",cy:"6",r:"2",fill:"currentColor",stroke:"none"}),h("line",{x1:"4",y1:"12",x2:"20",y2:"12"}),h("circle",{cx:"15",cy:"12",r:"2",fill:"currentColor",stroke:"none"}),h("line",{x1:"4",y1:"18",x2:"20",y2:"18"}),h("circle",{cx:"11",cy:"18",r:"2",fill:"currentColor",stroke:"none"})]})}function Ht(){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","aria-hidden":"true",children:[h("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),h("line",{x1:"5",y1:"12",x2:"19",y2:"12"})]})}import{Fragment as Kt,jsx as g,jsxs as x}from"react/jsx-runtime";var Ut=g("div",{className:"rle-empty",children:"Map unavailable"});function Vt(){let t=zt(null),[e,i]=X(!0),[r,n]=X(!0);return Ue(()=>{let s=t.current;if(!s)return;let o=()=>{let{clientWidth:v,scrollLeft:L,scrollWidth:p}=s;i(L<=0),n(L+v>=p-1)},a=0,l=()=>{a||(a=requestAnimationFrame(()=>{a=0,o()}))};o(),s.addEventListener("scroll",o,{passive:!0});let c,d;return typeof ResizeObserver<"u"&&(c=new ResizeObserver(o),c.observe(s)),typeof MutationObserver<"u"&&(d=new MutationObserver(l),d.observe(s,{characterData:!0,childList:!0,subtree:!0})),()=>{s.removeEventListener("scroll",o),c?.disconnect(),d?.disconnect(),a&&cancelAnimationFrame(a)}},[]),{atEnd:r,atStart:e,ref:t}}function Ve({search:t,toolbarEnd:e,mobileAction:i,autoFetch:r=!0,hasMap:n,mapCenter:s,mapZoom:o,className:a}){let l=y(),{Search:c}=F(),d=R(),{filters:v,set:L}=W(),p=n??l.map!=null,[b,u]=X("list"),[m,f]=X(!1),{atEnd:I,atStart:J,ref:E}=Vt(),_=t?{value:String(v[t.filterKey]??""),onChange:je=>{L({[t.filterKey]:je||void 0})},placeholder:t.placeholder}:void 0;Ue(()=>{r!==!1&&l.applyFilters({})},[l,r]);let $e=()=>{L(l.filters.clearedParams())},We=l.filters.activeKeys(v).length,qe=d.total??d.items.length;return x("div",{className:a?`rle-app ${a}`:"rle-app",children:[x("div",{className:"rle-filter-bar",children:[x("div",{className:"rle-filter-bar__scroll",ref:E,children:[_&&g("div",{className:"rle-filter-bar__search",children:g(c,{value:_.value,onChange:_.onChange,placeholder:_.placeholder})}),g(Y,{className:"rle-filters-row",groupClassName:"rle-filter-group",hideLabels:!0})]}),!J&&g("div",{className:"rle-filter-bar__fade rle-filter-bar__fade--start","aria-hidden":"true"}),!I&&g("div",{className:"rle-filter-bar__fade rle-filter-bar__fade--end","aria-hidden":"true"})]}),g(ze,{search:_,onFiltersClick:()=>f(!0),filterCount:We,action:i}),x("div",{className:`rle-body ${p?"rle-split":"rle-body--list-only"}`,"data-mobile-view":b,children:[x("div",{className:"rle-list",children:[x("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,{})]}),p&&g("div",{className:"rle-map",children:g(ke,{center:s,zoom:o,fallback:Ut})})]}),p&&g(Be,{view:b,onViewChange:u}),g(He,{title:"Filters",open:m,onOpenChange:f,footer:x(Kt,{children:[g("button",{type:"button",className:"rle-btn rle-btn--ghost",onClick:$e,children:"Clear all"}),x("button",{type:"button",className:"rle-btn rle-btn--primary",onClick:()=>f(!1),children:["Show ",qe," results"]})]}),children:g(Y,{className:"rle-filter-stack",groupClassName:"rle-filter-group"})})]})}import{useEffect as $t,useRef as Wt,useState as qt}from"react";import{jsx as me,jsxs as Gt}from"react/jsx-runtime";function Ke(t){return"provider"in t}function jt(t){let e=t!=null&&!Ke(t),i=e?t.apiKey:void 0,r=e?t.mapId:void 0,[n,s]=qt(()=>!t||Ke(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 mn(t){let{datasets:e,filters:i,map:r,components:n,initialFilters:s,onFiltersChange:o,mobileAction:a,search:l,toolbarEnd:c,config:d,autoFetch:v,className:L}=t,{ready:p,provider:b}=jt(r);if(!p)return null;let u=[];for(let I of e)u.push(ve(I));i&&u.push(Le(i)),b&&u.push(he(b)),s&&u.push(be(s)),d&&u.push(ye(d));let m=fe(...u),f={...Oe,...n};return Gt(Me,{...m,children:[o&&me(Qt,{onFiltersChange:o}),me(Pe,{...f,children:me(Ve,{className:L,search:l,toolbarEnd:c,mobileAction:a,autoFetch:v,mapCenter:r?.center,mapZoom:r?.zoom})})]})}export{H as a,N as b,z as c,U as d,ge as e,V as f,K 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,F as q,$ as r,y as s,S as t,W as u,Y as v,R as w,Se as x,ke as y,Ee as z,xe as A,Ne as B,Me as C,te as D,ie as E,re as F,ne as G,se as H,ae as I,le as J,de as K,pe as L,ce as M,Oe as N,Be as O,He as P,Ve as Q,mn as R};
|
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
|
|
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 _chunk4CZJODMKcjs = require('./chunk-4CZJODMK.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}=_chunk4CZJODMKcjs.q.call(void 0, );return _jsxruntime.jsx.call(void 0, t,{children:r})}var _react = require('react');function ot(){let r=_chunk4CZJODMKcjs.s.call(void 0, ),t=_chunk4CZJODMKcjs.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=_chunk4CZJODMKcjs.s.call(void 0, ),e=_chunk4CZJODMKcjs.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 = _chunk4CZJODMKcjs.c; exports.FilterRegistry = _chunk4CZJODMKcjs.b; exports.ListingApp = _chunk4CZJODMKcjs.R; exports.ListingComponentsProvider = _chunk4CZJODMKcjs.p; exports.ListingConfig = _chunk4CZJODMKcjs.f; exports.ListingEngine = _chunk4CZJODMKcjs.g; exports.ListingEngineContext = _chunk4CZJODMKcjs.r; exports.ListingEventType = g; exports.ListingFilters = _chunk4CZJODMKcjs.v; exports.ListingList = _chunk4CZJODMKcjs.x; exports.ListingMap = _chunk4CZJODMKcjs.y; exports.ListingPagination = _chunk4CZJODMKcjs.z; exports.ListingProvider = _chunk4CZJODMKcjs.C; exports.ListingResultHeader = _chunk4CZJODMKcjs.A; exports.ListingStore = _chunk4CZJODMKcjs.a; exports.ListingToolbar = M; exports.MemoryHistoryPort = y; exports.PaginationMode = m; exports.TypedEmitter = _chunk4CZJODMKcjs.d; exports.UrlSyncController = c; exports.composeListingProviders = _chunk4CZJODMKcjs.h; exports.listingDefaultConfig = _chunk4CZJODMKcjs.e; exports.useListing = _chunk4CZJODMKcjs.s; exports.useListingComponents = _chunk4CZJODMKcjs.q; exports.useListingEvent = _chunk4CZJODMKcjs.B; exports.useListingFilters = _chunk4CZJODMKcjs.u; exports.useListingLayer = yt; exports.useListingMap = ot; exports.useListingResults = _chunk4CZJODMKcjs.w; exports.useListingState = _chunk4CZJODMKcjs.t; exports.withConfig = _chunk4CZJODMKcjs.i; exports.withDataset = _chunk4CZJODMKcjs.k; exports.withFilters = _chunk4CZJODMKcjs.l; exports.withInitialFilters = _chunk4CZJODMKcjs.n; exports.withMap = _chunk4CZJODMKcjs.j; exports.withPrimaryDataset = _chunk4CZJODMKcjs.o; exports.withUrlSync = _chunk4CZJODMKcjs.m;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
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
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 './
|
|
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 ListingComponentsProvider, M as MarkerRenderer, u as useListingComponents } from './
|
|
5
|
-
import { H as HistoryPort, U as UrlSyncController } from './url-sync.controller-DXSIWgTa.cjs';
|
|
6
|
-
export { a as UrlSyncEngine, b as UrlSyncOptions } from './url-sync.controller-DXSIWgTa.cjs';
|
|
3
|
+
import { P as PaginationMode, D as DatasetDefinition, I as IListingConfigOptions, F as FilterRegistry, a as IListingToolbarProps } from './listing-app-BtNNEkX1.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-BtNNEkX1.cjs';
|
|
7
5
|
import * as react from 'react';
|
|
8
6
|
import { ReactNode } from 'react';
|
|
9
|
-
export { L as ListingApp, a as ListingAppProps } from './listing-app-BMoMKD4c.cjs';
|
|
10
7
|
|
|
11
8
|
declare enum ListingEventType {
|
|
12
9
|
FiltersChanged = "FiltersChanged",
|
|
@@ -213,6 +210,18 @@ declare class ListingEngine<TEntity, TFilters> {
|
|
|
213
210
|
private clearDebounce;
|
|
214
211
|
}
|
|
215
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
|
+
|
|
216
225
|
/**
|
|
217
226
|
* In-memory `HistoryPort` — holds `QueryParams` in a plain field instead of
|
|
218
227
|
* the browser URL. Used by tests (fast, no DOM/jsdom `history` needed) and by
|
|
@@ -228,6 +237,79 @@ declare class MemoryHistoryPort implements HistoryPort {
|
|
|
228
237
|
private notify;
|
|
229
238
|
}
|
|
230
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
|
+
|
|
231
313
|
/**
|
|
232
314
|
* Aggregated provider props — the composition entry point of
|
|
233
315
|
* react-listing-engine, mirroring `react-wizard-engine`'s
|
|
@@ -296,8 +378,8 @@ interface IListingFiltersProps {
|
|
|
296
378
|
* styling hook -- this component stays structure-only, so it never bakes
|
|
297
379
|
* in its own layout beyond the `space-y-5` fallback below. Pass a
|
|
298
380
|
* horizontal row (e.g. `"flex flex-wrap items-end gap-3"`) to flow filter
|
|
299
|
-
* groups inline instead of stacking them, as `/
|
|
300
|
-
* 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.
|
|
301
383
|
*/
|
|
302
384
|
className?: string;
|
|
303
385
|
/** ClassName applied to each individual filter group's wrapper `<div>` (including the string-placeholder case). */
|
|
@@ -322,7 +404,7 @@ interface IListingFiltersProps {
|
|
|
322
404
|
* `"text"`/`"range"`/`"toggle"`): there is no shared named-control registry
|
|
323
405
|
* yet, so this renders an inert `<div data-filter={def.key} />` placeholder.
|
|
324
406
|
* Wiring a real named-control registry (so `"range"` etc. resolve to an
|
|
325
|
-
* actual control
|
|
407
|
+
* actual control) is a documented future
|
|
326
408
|
* enhancement, not attempted in this task.
|
|
327
409
|
*
|
|
328
410
|
* Each filter group is wrapped in its own `<div>`: when `def.label` is set,
|
|
@@ -488,8 +570,8 @@ declare function ListingMap(props: IListingMapProps): react.JSX.Element;
|
|
|
488
570
|
*
|
|
489
571
|
* Renders a plain `<button>` rather than an injected component: `useListingComponents()`
|
|
490
572
|
* (`IListingComponents`) has no `Button` slot yet, so there is nothing to
|
|
491
|
-
* delegate to here. Wiring this up to a real Button slot (and
|
|
492
|
-
*
|
|
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
|
|
493
575
|
* exists.
|
|
494
576
|
*/
|
|
495
577
|
declare function ListingPagination(): react.JSX.Element | null;
|
|
@@ -676,4 +758,4 @@ interface IListingProviderComponentProps<TFilters> extends IListingProviderProps
|
|
|
676
758
|
*/
|
|
677
759
|
declare function ListingProvider<TEntity, TFilters>(props: IListingProviderComponentProps<TFilters>): react.JSX.Element | null;
|
|
678
760
|
|
|
679
|
-
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 };
|