react-listing-engine 0.6.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +5 -3
- package/dist/chunk-3O6X563H.cjs +2 -0
- package/dist/chunk-SIMKBYC6.js +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +74 -14
- package/dist/index.d.ts +74 -14
- package/dist/index.js +1 -1
- package/dist/{listing-app-CtF5E1ja.d.cts → listing-app-C7EeHlmP.d.cts} +14 -3
- package/dist/{listing-app-BeYPcPVw.d.ts → listing-app-DHFJtnKI.d.ts} +14 -3
- package/dist/map-provider.interface-B5Law7gE.d.cts +143 -0
- package/dist/map-provider.interface-B5Law7gE.d.ts +143 -0
- package/dist/maps/google/index.cjs +1 -1
- package/dist/maps/google/index.d.cts +1 -1
- package/dist/maps/google/index.d.ts +1 -1
- package/dist/maps/google/index.js +1 -1
- package/dist/styled/index.cjs +1 -1
- package/dist/styled/index.d.cts +3 -3
- package/dist/styled/index.d.ts +3 -3
- package/dist/styled/index.js +1 -1
- package/dist/testing/index.cjs +2 -2
- package/dist/testing/index.d.cts +37 -1
- package/dist/testing/index.d.ts +37 -1
- package/dist/testing/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-CZCVR4OW.js +0 -2
- package/dist/chunk-KJW54NRT.cjs +0 -2
- package/dist/map-provider.interface-DT-v1plm.d.cts +0 -67
- package/dist/map-provider.interface-DT-v1plm.d.ts +0 -67
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# react-listing-engine
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add Airbnb-style map interactivity.
|
|
8
|
+
|
|
9
|
+
- **Marker hover/selected repaint:** new `hovered` store state + `setHovered`, exposed on `useListingMap()`. `MapProvider.updateMarkerStates` toggles `rle-marker--selected` / `rle-marker--hovered` classes on marker containers (consumers style them). The highlight persists across marker recreation on pan/zoom.
|
|
10
|
+
- **On-map Popup:** the previously-inert `Popup` component slot now renders as an anchored `OverlayView` when a marker is selected. It stays anchored while panning and dismisses on the popup's close, `Esc`, or a map-background click. Backed by new `MapProvider.mountOverlay` and `MapProvider.onMapClick`.
|
|
11
|
+
- **Map controls slot:** `ListingApp` gains an optional `mapControls?: ReactNode` overlay slot, and `useListingMap()` gains `zoomIn()`, `zoomOut()`, `toggleFullscreen()`. Fullscreen targets the map wrapper so custom controls stay visible.
|
|
12
|
+
|
|
13
|
+
Consumers who don't use the new props/slots see no behavior change.
|
|
14
|
+
|
|
15
|
+
**Interface change (minor, pre-1.0):** the `MapProvider` interface gained required methods — `updateMarkerStates`, `mountOverlay`, `onMapClick`, `zoomIn`, `zoomOut`, `toggleFullscreen`. The built-in `googleProvider` and `FakeMapProvider` implement them; external custom `MapProvider` implementations must add them to compile against 0.7.0.
|
|
16
|
+
|
|
3
17
|
## 0.6.9
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ export function PropertySearch() {
|
|
|
61
61
|
fromParams: f => f.q ?? '',
|
|
62
62
|
})
|
|
63
63
|
}
|
|
64
|
-
map={{ apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY
|
|
64
|
+
map={{ apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY! }}
|
|
65
65
|
/>
|
|
66
66
|
);
|
|
67
67
|
}
|
|
@@ -83,12 +83,14 @@ import { googleProvider } from 'react-listing-engine/maps/google';
|
|
|
83
83
|
|
|
84
84
|
const map = googleProvider({
|
|
85
85
|
apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY!, // required — no hardcoded fallback
|
|
86
|
-
mapId: 'YOUR_MAP_ID', //
|
|
86
|
+
mapId: 'YOUR_MAP_ID', // optional — defaults to Google's zero-config dev 'DEMO_MAP_ID'
|
|
87
87
|
});
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
- `apiKey` always comes from **your** env/config; `googleProvider` throws immediately if it's falsy.
|
|
91
|
-
- `mapId` is
|
|
91
|
+
- `mapId` is what Google requires to render `AdvancedMarkerElement` markers. It defaults to Google's documented zero-config dev Map ID (`'DEMO_MAP_ID'`), so markers work out of the box — supply your own Cloud Console Map ID for production traffic or Cloud-based map styling.
|
|
92
|
+
- `mapOptions` (optional) forwards extra `google.maps.MapOptions` to every map the provider creates — zoom envelope (`minZoom`/`maxZoom`), UI chrome (`disableDefaultUI`, `zoomControl`), gesture handling, etc. The provider's own `mapId`/`center`/`zoom` always win over it.
|
|
93
|
+
- `styles` (optional) applies legacy JSON map styling (`google.maps.MapTypeStyle[]`). Mutually exclusive with `mapId` — Google ignores JSON styles whenever a Map ID is present — so setting it switches the provider into a no-Map-ID mode that renders markers as `OverlayView` HTML overlays instead of `AdvancedMarkerElement`s. Marker clustering isn't supported in this mode (it falls back to plain markers with a one-time console warning).
|
|
92
94
|
- `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.
|
|
93
95
|
- `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.
|
|
94
96
|
|
|
@@ -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 Q= (_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,hovered: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})}setHovered(e){this.setState({hovered: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 H= (_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(o=>this.defs.has(o));r.forEach((o,n)=>{this.defs.get(o).order=n});let s=new Set(r);return i.filter(o=>!s.has(o.key)).forEach((o,n)=>{o.order=r.length+n}),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,s)=>({...r,...s}),{})}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 G= (_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 Z= (_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 s of[...i])s(e);let r=this.map.get("*");if(r)for(let s of[...r])s(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 Ce={pagination:"paged",pageSize:20,debounceMs:250};var X=class{constructor(e){this.options=Object.freeze({...Ce,...e})}};var J= (_class5 =class{__init5() {this.emitter=new Z}__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 H)),this.map=e.map,this.config=new X(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 Q({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(s=>{this.debounceResolve=s,this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.debounceResolve=null,s(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 s=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(s):this.store.setResults(s),this.emitter.emit({type:"ResultsLoaded",datasetId:this.primaryDatasetId,count:s.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 s=>{let o=this.datasets.get(s);if(!o)return;let n=await o.adapter.getPoints(i,e);r===this.pointsToken&&this.store.setPoints(s,n)}))}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(s=>s.id===i)]);r&&this.emitter.emit({type:"PointClicked",datasetId:e,id:r.id,entity:r.entity})}setHovered(e,i){this.store.setHovered(i)}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 Se(...t){let e={config:{},filters:new H,datasets:new G};for(let i of t)i(e);return e}var Ie=t=>e=>{e.config={...e.config,...t}},we= exports.j =t=>e=>{e.map=t},ke= exports.k =t=>e=>{e.datasets.add(t)},Ee= exports.l =t=>e=>{t(e.filters)},yi= exports.m =t=>e=>{e.urlSync=t},xe= exports.n =t=>e=>{e.initialFilters=t},vi= exports.o =t=>e=>{e.primaryDatasetId=t};var _react = require('react');var _jsxruntime = require('react/jsx-runtime');function nt(t){return String(_nullishCoalesce(_optionalChain([t, 'optionalAccess', _16 => _16.title]), () => ("")))}var st=({item:t})=>_jsxruntime.jsx.call(void 0, "div",{children:nt(t)}),ot=()=>_jsxruntime.jsx.call(void 0, "div",{}),le= exports.p =()=>_jsxruntime.jsx.call(void 0, "div",{}),at=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),lt=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),dt=({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"))}),pt=()=>_jsxruntime.jsx.call(void 0, "div",{role:"status",children:"No results"}),ct=()=>_jsxruntime.jsx.call(void 0, "div",{role:"status","aria-busy":"true",children:"Loading\u2026"}),ut=({count:t})=>_jsxruntime.jsxs.call(void 0, "div",{children:[t," results"]}),mt=({children:t})=>_jsxruntime.jsx.call(void 0, "div",{children:t}),C={Card:st,Marker:ot,Popup:le,Sidebar:at,FilterPanel:lt,Search:dt,Empty:pt,Loading:ct,ResultHeader:ut,Toolbar:mt},Ne=_react.createContext.call(void 0, C);function Re(t){let{Card:e,Marker:i,Popup:r,Sidebar:s,FilterPanel:o,Search:n,Empty:a,Loading:d,ResultHeader:l,Toolbar:c,children:h}=t,b={Card:_nullishCoalesce(e, () => (C.Card)),Marker:_nullishCoalesce(i, () => (C.Marker)),Popup:_nullishCoalesce(r, () => (C.Popup)),Sidebar:_nullishCoalesce(s, () => (C.Sidebar)),FilterPanel:_nullishCoalesce(o, () => (C.FilterPanel)),Search:_nullishCoalesce(n, () => (C.Search)),Empty:_nullishCoalesce(a, () => (C.Empty)),Loading:_nullishCoalesce(d, () => (C.Loading)),ResultHeader:_nullishCoalesce(l, () => (C.ResultHeader)),Toolbar:_nullishCoalesce(c, () => (C.Toolbar))};return _jsxruntime.jsx.call(void 0, Ne.Provider,{value:b,children:h})}function P(){return _react.useContext.call(void 0, Ne)}var Y=_react.createContext.call(void 0, null);function g(){let t=_react.useContext.call(void 0, Y);if(t===null)throw new Error("useListing must be used within a <ListingProvider>");return t}function I(){let t=g(),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 ee(){let t=g(),e=I(),i=_react.useCallback.call(void 0, s=>t.applyFilters(s),[t]),r=_react.useCallback.call(void 0, (s,o)=>t.applyFilters({[s]:o}),[t]);return{filters:e.filters,set:i,setField:r}}function de({className:t,groupClassName:e,hideLabels:i}={}){let r=g(),{FilterPanel:s}=P(),{filters:o}=ee();return _jsxruntime.jsx.call(void 0, s,{children:_jsxruntime.jsx.call(void 0, "div",{className:_nullishCoalesce(t, () => ("space-y-5")),children:r.filters.list().map(n=>{if(typeof n.render=="string")return _jsxruntime.jsx.call(void 0, "div",{"data-filter":n.key,className:e},n.key);let a=n.render;return _jsxruntime.jsxs.call(void 0, "div",{className:e,children:[n.label&&!i&&_jsxruntime.jsx.call(void 0, "div",{className:"mb-1.5 text-[13px] font-medium text-foreground",children:n.label}),_jsxruntime.jsx.call(void 0, a,{value:n.fromParams(o),onChange:d=>{r.applyFilters(n.toParams(d))}})]},n.key)})})})}function z(){return I().results}function Lt(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 Oe({className:t}={}){let e=g(),{items:i}=z(),{pagination:r,selection:s}=I(),{Card:o,Empty:n,Loading:a}=P();return r.loading&&i.length===0?_jsxruntime.jsx.call(void 0, a,{}):i.length===0?_jsxruntime.jsx.call(void 0, n,{}):_jsxruntime.jsx.call(void 0, "div",{role:"list",className:t,children:i.map((d,l)=>{let c=Lt(d,l);return _jsxruntime.jsx.call(void 0, o,{item:d,selected:s===c,onSelect:()=>e.selectPoint(e.primaryDatasetId,c)},c)})})}var _reactdom = require('react-dom');var Pt={west:-179.9,south:-85,east:179.9,north:85},Be=.1,Ae=.02;function Tt(t){if(t.length===0)return null;let e=t[0].lng,i=t[0].lng,r=t[0].lat,s=t[0].lat;for(let{lat:a,lng:d}of t)d<e&&(e=d),d>i&&(i=d),a<r&&(r=a),a>s&&(s=a);let o=s>r?(s-r)*Be:Ae,n=i>e?(i-e)*Be:Ae;return{west:e-n,east:i+n,south:r-o,north:s+o}}function ze(t){let{center:e,zoom:i,fallback:r,mapControls:s}=t,o=g(),n=I(),a=_react.useRef.call(void 0, null),d=_react.useRef.call(void 0, null),l=_react.useRef.call(void 0, null),[c,h]=_react.useState.call(void 0, !1),b=_react.useRef.call(void 0, !1),w=_react.useRef.call(void 0, !1),k=_react.useRef.call(void 0, !1),p=o.map;_react.useEffect.call(void 0, ()=>{let L=l.current;if(!p||!L)return;let f=[];for(let u of Object.keys(n.points)){if(n.layers[u]===!1)continue;let y=o.datasets.get(u),R=_nullishCoalesce(n.points[u], () => ([])),ae={id:u,markers:R.map(M=>({id:M.id,position:M.position,iconUrl:_optionalChain([y, 'optionalAccess', _17 => _17.marker, 'access', _18 => _18.iconUrl, 'optionalCall', _19 => _19(M.entity)]),element:_optionalChain([y, 'optionalAccess', _20 => _20.marker, 'access', _21 => _21.element, 'optionalCall', _22 => _22(M.entity)])})),clustering:_optionalChain([y, 'optionalAccess', _23 => _23.clustering]),onMarkerClick:M=>o.selectPoint(u,M)};f.push(p.renderLayer(L,ae))}return()=>{f.forEach(u=>u())}},[o,p,c,n.points,n.layers]),_react.useEffect.call(void 0, ()=>{if(!a.current||!p)return;let L=a.current,f=!1,u=null;return(async()=>{let y=await p.mount(L,{center:e,zoom:i,fullscreenTarget:_nullishCoalesce(d.current, () => (void 0))});if(f){p.destroy(y);return}l.current=y,u=p.onBoundsChange(y,R=>{k.current?k.current=!1:w.current=!0,o.loadPoints(R)}),h(!0),o.loadPoints(Pt)})(),()=>{f=!0,_optionalChain([u, 'optionalCall', _24 => _24()]),l.current&&(p.destroy(l.current),l.current=null),h(!1)}},[o,p]),_react.useEffect.call(void 0, ()=>{let L=l.current;if(!p||!L||e||b.current||w.current)return;let f=[];for(let y of Object.keys(n.points))if(n.layers[y]!==!1)for(let R of _nullishCoalesce(n.points[y], () => ([])))f.push(R.position);let u=Tt(f);u&&(b.current=!0,k.current=!0,p.fitBounds(L,u))},[p,e,c,n.points,n.layers]),_react.useEffect.call(void 0, ()=>{_optionalChain([p, 'optionalAccess', _25 => _25.updateMarkerStates, 'call', _26 => _26(n.selection,n.hovered)])},[p,c,n.selection,n.hovered]);let{Popup:F}=P(),O=F!==le,_=_nullishCoalesce(n.points[o.primaryDatasetId], () => ([])),A=n.selection!=null?_.find(L=>L.id===n.selection):void 0,[V,j]=_react.useState.call(void 0, null),N=_react.useRef.call(void 0, A);return N.current=A,_react.useEffect.call(void 0, ()=>{let L=l.current;if(!p||!L||!O)return;let f=N.current;if(!f)return;let u=p.mountOverlay(f.position);j({entity:f.entity,position:f.position,container:u.container});let y=()=>o.selectPoint(o.primaryDatasetId,null),R=M=>{M.key==="Escape"&&y()};document.addEventListener("keydown",R);let ae=p.onMapClick(y);return()=>{document.removeEventListener("keydown",R),ae(),u.unmount(),j(null)}},[o,p,c,n.selection,O]),_jsxruntime.jsxs.call(void 0, "div",{ref:d,className:"relative h-full min-h-0 w-full",children:[_jsxruntime.jsxs.call(void 0, "div",{ref:a,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,O&&V?_reactdom.createPortal.call(void 0, _jsxruntime.jsx.call(void 0, F,{entity:V.entity,onClose:()=>o.selectPoint(o.primaryDatasetId,null)}),V.container):null]}),s!=null&&_jsxruntime.jsx.call(void 0, "div",{className:"pointer-events-none absolute inset-0",children:_jsxruntime.jsx.call(void 0, "div",{className:"pointer-events-auto",children:s})})]})}function Ke(){let t=g(),{results:e,pagination:i}=I();return e.nextCursor==null?null:_jsxruntime.jsx.call(void 0, "button",{type:"button",disabled:i.loading,onClick:()=>{t.loadPage()},children:"Load more"})}function Ue(){let{items:t,total:e}=z(),{ResultHeader:i}=P();return _jsxruntime.jsx.call(void 0, i,{count:t.length,total:e})}function Ve(t,e){let i=g(),r=_react.useRef.call(void 0, e);r.current=e,_react.useEffect.call(void 0, ()=>i.on(t,s=>r.current(s)),[i,t])}function We(t){let{children:e,...i}=t,[r]=_react.useState.call(void 0, ()=>i),[s,o]=_react.useState.call(void 0, null);return _react.useEffect.call(void 0, ()=>{let n=new J({datasets:r.datasets,filters:r.filters,config:r.config,map:r.map,initialFilters:r.initialFilters,primaryDatasetId:r.primaryDatasetId});return r.urlSync&&r.urlSync.start(n),o(n),()=>{r.urlSync&&r.urlSync.stop(),n.dispose(),o(a=>a===n?null:a)}},[r]),s?_jsxruntime.jsx.call(void 0, Y.Provider,{value:s,children:e}):null}function K(t){return typeof t!="number"?t:new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0}).format(t)}function Et(t){return t?"rle-card rle-card--selected":"rle-card"}function ce({item:t,selected:e,onSelect:i}){let r=_nullishCoalesce(t, () => ({})),s=Et(e),o=_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:K(r.price)})]})]});return i?_jsxruntime.jsx.call(void 0, "button",{type:"button",onClick:i,"aria-pressed":_nullishCoalesce(e, () => (!1)),className:s,children:o}):_jsxruntime.jsx.call(void 0, "article",{className:s,children:o})}function ue(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 me({children:t}){return _jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-panel",children:t})}function ge(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 fe({point:t}){let e=_nullishCoalesce(t.entity, () => ({}));return _jsxruntime.jsx.call(void 0, "span",{className:"rle-pin",children:e.price!=null?K(e.price):""})}function ve({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:K(i.price)})]})]})}function he({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 be({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 Qe={Card:ce,Marker:fe,Popup:ve,Sidebar:be,FilterPanel:me,Search:Le,Empty:ue,Loading:ge,ResultHeader:he,Toolbar:Pe};function Ge({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, At,{}),"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, Ht,{}),"Map"]})]})})}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, "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 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",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 Xe({open:t,onOpenChange:e,title:i,children:r,footer:s}){let o=_react.useRef.call(void 0, null),[n,a]=_react.useState.call(void 0, !1),[d,l]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>{if(!t){l(!1),a(!1);return}a(!0);let c=requestAnimationFrame(()=>l(!0));return()=>cancelAnimationFrame(c)},[t]),_react.useEffect.call(void 0, ()=>{if(!n)return;let c=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=c}},[n]),_react.useEffect.call(void 0, ()=>{if(!n)return;_optionalChain([o, 'access', _27 => _27.current, 'optionalAccess', _28 => _28.focus, 'call', _29 => _29()]);function c(h){h.key==="Escape"&&e(!1)}return document.addEventListener("keydown",c),()=>document.removeEventListener("keydown",c)},[n,e]),!n||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${d?" rle-sheet-backdrop--open":""}`,onClick:()=>e(!1),"aria-hidden":"true"}),_jsxruntime.jsxs.call(void 0, "div",{ref:o,className:`rle-sheet${d?" 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, Ut,{})})]}),_jsxruntime.jsx.call(void 0, "div",{className:"rle-sheet__body",children:r}),s&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-sheet__footer",children:s})]})]}),document.body)}function Ut(){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 Je({search:t,onFiltersClick:e,filterCount:i=0,action:r}){let{Search:s}=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, s,{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, $t,{}),_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, Wt,{})))})]})}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","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 Wt(){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 jt=_jsxruntime.jsx.call(void 0, "div",{className:"rle-empty",children:"Map unavailable"});function Qt(){let t=_react.useRef.call(void 0, null),[e,i]=_react.useState.call(void 0, !0),[r,s]=_react.useState.call(void 0, !0);return _react.useEffect.call(void 0, ()=>{let o=t.current;if(!o)return;let n=()=>{let{clientWidth:h,scrollLeft:b,scrollWidth:w}=o;i(b<=0),s(b+h>=w-1)},a=0,d=()=>{a||(a=requestAnimationFrame(()=>{a=0,n()}))};n(),o.addEventListener("scroll",n,{passive:!0});let l,c;return typeof ResizeObserver<"u"&&(l=new ResizeObserver(n),l.observe(o)),typeof MutationObserver<"u"&&(c=new MutationObserver(d),c.observe(o,{characterData:!0,childList:!0,subtree:!0})),()=>{o.removeEventListener("scroll",n),_optionalChain([l, 'optionalAccess', _30 => _30.disconnect, 'call', _31 => _31()]),_optionalChain([c, 'optionalAccess', _32 => _32.disconnect, 'call', _33 => _33()]),a&&cancelAnimationFrame(a)}},[]),{atEnd:r,atStart:e,ref:t}}function et({search:t,toolbarEnd:e,mobileAction:i,autoFetch:r=!0,hasMap:s,mapCenter:o,mapZoom:n,mapControls:a,className:d}){let l=g(),{Search:c}=P(),h=z(),{filters:b,set:w}=ee(),k=_nullishCoalesce(s, () => (l.map!=null)),[p,F]=_react.useState.call(void 0, "list"),[O,_]=_react.useState.call(void 0, !1),{atEnd:A,atStart:V,ref:j}=Qt(),N=t?{value:String(_nullishCoalesce(b[t.filterKey], () => (""))),onChange:y=>{w({[t.filterKey]:y||void 0})},placeholder:t.placeholder}:void 0;_react.useEffect.call(void 0, ()=>{r!==!1&&l.applyFilters({})},[l,r]);let L=()=>{w(l.filters.clearedParams())},f=l.filters.activeKeys(b).length,u=_nullishCoalesce(h.total, () => (h.items.length));return _jsxruntime.jsxs.call(void 0, "div",{className:d?`rle-app ${d}`:"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:j,children:[N&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__search",children:_jsxruntime.jsx.call(void 0, c,{value:N.value,onChange:N.onChange,placeholder:N.placeholder})}),_jsxruntime.jsx.call(void 0, de,{className:"rle-filters-row",groupClassName:"rle-filter-group",hideLabels:!0})]}),!V&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__fade rle-filter-bar__fade--start","aria-hidden":"true"}),!A&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-filter-bar__fade rle-filter-bar__fade--end","aria-hidden":"true"})]}),_jsxruntime.jsx.call(void 0, Je,{search:N,onFiltersClick:()=>_(!0),filterCount:f,action:i}),_jsxruntime.jsxs.call(void 0, "div",{className:`rle-body ${k?"rle-split":"rle-body--list-only"}`,"data-mobile-view":p,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, Ue,{}),e&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-list-header__toolbar",children:e})]}),_jsxruntime.jsx.call(void 0, Oe,{className:"rle-list-grid"}),_jsxruntime.jsx.call(void 0, Ke,{})]}),k&&_jsxruntime.jsx.call(void 0, "div",{className:"rle-map",children:_jsxruntime.jsx.call(void 0, ze,{center:o,zoom:n,fallback:jt,mapControls:a})})]}),k&&_jsxruntime.jsx.call(void 0, Ge,{view:p,onViewChange:F}),_jsxruntime.jsx.call(void 0, Xe,{title:"Filters",open:O,onOpenChange:_,footer:_jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[_jsxruntime.jsx.call(void 0, "button",{type:"button",className:"rle-btn rle-btn--ghost",onClick:L,children:"Clear all"}),_jsxruntime.jsxs.call(void 0, "button",{type:"button",className:"rle-btn rle-btn--primary",onClick:()=>_(!1),children:["Show ",u," results"]})]}),children:_jsxruntime.jsx.call(void 0, de,{className:"rle-filter-stack",groupClassName:"rle-filter-group"})})]})}function tt(t){return"provider"in t}function Yt(t){let e=t!=null&&!tt(t),i=e?t.apiKey:void 0,r=e?t.mapId:void 0,s=e?t.mapOptions:void 0,o=e?t.styles:void 0,[n,a]=_react.useState.call(void 0, ()=>!t||tt(t)?{ready:!0,provider:_optionalChain([t, 'optionalAccess', _34 => _34.provider])}:{ready:!1});return _react.useEffect.call(void 0, ()=>{if(!i)return;let d=!1;return Promise.resolve().then(() => _interopRequireWildcard(require("./maps/google/index.cjs"))).then(({googleProvider:l})=>{d||a({ready:!0,provider:l({apiKey:i,mapId:r,mapOptions:s,styles:o})})}),()=>{d=!0}},[i,r]),n}function ei({onFiltersChange:t}){let e=_react.useRef.call(void 0, t);return e.current=t,Ve("FiltersChanged",i=>{i.type==="FiltersChanged"&&e.current(i.filters)}),null}function Pn(t){let{datasets:e,filters:i,map:r,components:s,initialFilters:o,onFiltersChange:n,mobileAction:a,search:d,toolbarEnd:l,mapControls:c,config:h,autoFetch:b,className:w}=t,{ready:k,provider:p}=Yt(r);if(!k)return null;let F=[];for(let A of e)F.push(ke(A));i&&F.push(Ee(i)),p&&F.push(we(p)),o&&F.push(xe(o)),h&&F.push(Ie(h));let O=Se(...F),_={...Qe,...s};return _jsxruntime.jsxs.call(void 0, We,{...O,children:[n&&_jsxruntime.jsx.call(void 0, ei,{onFiltersChange:n}),_jsxruntime.jsx.call(void 0, Re,{..._,children:_jsxruntime.jsx.call(void 0, et,{className:w,search:d,toolbarEnd:l,mobileAction:a,autoFetch:b,mapCenter:_optionalChain([r, 'optionalAccess', _35 => _35.center]),mapZoom:_optionalChain([r, 'optionalAccess', _36 => _36.zoom]),mapControls:c})})]})}exports.a = Q; exports.b = H; exports.c = G; exports.d = Z; exports.e = Ce; exports.f = X; exports.g = J; exports.h = Se; exports.i = Ie; exports.j = we; exports.k = ke; exports.l = Ee; exports.m = yi; exports.n = xe; exports.o = vi; exports.p = le; exports.q = Re; exports.r = P; exports.s = Y; exports.t = g; exports.u = I; exports.v = ee; exports.w = de; exports.x = z; exports.y = Oe; exports.z = ze; exports.A = Ke; exports.B = Ue; exports.C = Ve; exports.D = We; exports.E = ce; exports.F = ue; exports.G = me; exports.H = ge; exports.I = fe; exports.J = ve; exports.K = he; exports.L = Le; exports.M = be; exports.N = Pe; exports.O = Qe; exports.P = Ge; exports.Q = Xe; exports.R = et; exports.S = Pn;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var Q=class{state;listeners=new Set;constructor(e){this.state=this.freezeState({filters:{...e.filters},results:{items:[],nextCursor:null},bounds:null,selection:null,hovered: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})}setHovered(e){this.setState({hovered: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 H=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(o=>this.defs.has(o));r.forEach((o,n)=>{this.defs.get(o).order=n});let s=new Set(r);return i.filter(o=>!s.has(o.key)).forEach((o,n)=>{o.order=r.length+n}),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,s)=>({...r,...s}),{})}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 G=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 Z=class{map=new Map;dispose(){this.map.clear()}emit(e){let i=this.map.get(e.type);if(i)for(let s of[...i])s(e);let r=this.map.get("*");if(r)for(let s of[...r])s(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 Ce={pagination:"paged",pageSize:20,debounceMs:250};var X=class{options;constructor(e){this.options=Object.freeze({...Ce,...e})}};var J=class{filters;map;datasets;primaryDatasetId;store;emitter=new Z;config;debounceTimer=null;debounceResolve=null;queryToken=0;pointsToken=0;constructor(e){this.datasets=e.datasets,this.filters=e.filters??new H,this.map=e.map,this.config=new X(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 Q({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(s=>{this.debounceResolve=s,this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.debounceResolve=null,s(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 s=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(s):this.store.setResults(s),this.emitter.emit({type:"ResultsLoaded",datasetId:this.primaryDatasetId,count:s.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 s=>{let o=this.datasets.get(s);if(!o)return;let n=await o.adapter.getPoints(i,e);r===this.pointsToken&&this.store.setPoints(s,n)}))}selectPoint(e,i){this.store.setSelection(i);let r=this.state.points[e]?.find(s=>s.id===i);r&&this.emitter.emit({type:"PointClicked",datasetId:e,id:r.id,entity:r.entity})}setHovered(e,i){this.store.setHovered(i)}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 Se(...t){let e={config:{},filters:new H,datasets:new G};for(let i of t)i(e);return e}var Ie=t=>e=>{e.config={...e.config,...t}},we=t=>e=>{e.map=t},ke=t=>e=>{e.datasets.add(t)},Ee=t=>e=>{t(e.filters)},yi=t=>e=>{e.urlSync=t},xe=t=>e=>{e.initialFilters=t},vi=t=>e=>{e.primaryDatasetId=t};import{createContext as it,useContext as rt}from"react";import{jsx as S,jsxs as gt}from"react/jsx-runtime";function nt(t){return String(t?.title??"")}var st=({item:t})=>S("div",{children:nt(t)}),ot=()=>S("div",{}),le=()=>S("div",{}),at=({children:t})=>S("div",{children:t}),lt=({children:t})=>S("div",{children:t}),dt=({value:t,onChange:e,placeholder:i})=>S("input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":i??"Search"}),pt=()=>S("div",{role:"status",children:"No results"}),ct=()=>S("div",{role:"status","aria-busy":"true",children:"Loading\u2026"}),ut=({count:t})=>gt("div",{children:[t," results"]}),mt=({children:t})=>S("div",{children:t}),C={Card:st,Marker:ot,Popup:le,Sidebar:at,FilterPanel:lt,Search:dt,Empty:pt,Loading:ct,ResultHeader:ut,Toolbar:mt},Ne=it(C);function Re(t){let{Card:e,Marker:i,Popup:r,Sidebar:s,FilterPanel:o,Search:n,Empty:a,Loading:d,ResultHeader:l,Toolbar:c,children:h}=t,b={Card:e??C.Card,Marker:i??C.Marker,Popup:r??C.Popup,Sidebar:s??C.Sidebar,FilterPanel:o??C.FilterPanel,Search:n??C.Search,Empty:a??C.Empty,Loading:d??C.Loading,ResultHeader:l??C.ResultHeader,Toolbar:c??C.Toolbar};return S(Ne.Provider,{value:b,children:h})}function P(){return rt(Ne)}import{createContext as ft}from"react";var Y=ft(null);import{useContext as yt}from"react";function g(){let t=yt(Y);if(t===null)throw new Error("useListing must be used within a <ListingProvider>");return t}import{useCallback as Me,useSyncExternalStore as vt}from"react";function I(){let t=g(),e=Me(r=>t.subscribe(r),[t]),i=Me(()=>t.state,[t]);return vt(e,i,i)}import{useCallback as De}from"react";function ee(){let t=g(),e=I(),i=De(s=>t.applyFilters(s),[t]),r=De((s,o)=>t.applyFilters({[s]:o}),[t]);return{filters:e.filters,set:i,setField:r}}import{jsx as $,jsxs as ht}from"react/jsx-runtime";function de({className:t,groupClassName:e,hideLabels:i}={}){let r=g(),{FilterPanel:s}=P(),{filters:o}=ee();return $(s,{children:$("div",{className:t??"space-y-5",children:r.filters.list().map(n=>{if(typeof n.render=="string")return $("div",{"data-filter":n.key,className:e},n.key);let a=n.render;return ht("div",{className:e,children:[n.label&&!i&&$("div",{className:"mb-1.5 text-[13px] font-medium text-foreground",children:n.label}),$(a,{value:n.fromParams(o),onChange:d=>{r.applyFilters(n.toParams(d))}})]},n.key)})})})}function z(){return I().results}import{jsx as te}from"react/jsx-runtime";function Lt(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 Oe({className:t}={}){let e=g(),{items:i}=z(),{pagination:r,selection:s}=I(),{Card:o,Empty:n,Loading:a}=P();return r.loading&&i.length===0?te(a,{}):i.length===0?te(n,{}):te("div",{role:"list",className:t,children:i.map((d,l)=>{let c=Lt(d,l);return te(o,{item:d,selected:s===c,onSelect:()=>e.selectPoint(e.primaryDatasetId,c)},c)})})}import{useEffect as W,useRef as B,useState as _e}from"react";import{createPortal as bt}from"react-dom";import{jsx as pe,jsxs as He}from"react/jsx-runtime";var Pt={west:-179.9,south:-85,east:179.9,north:85},Be=.1,Ae=.02;function Tt(t){if(t.length===0)return null;let e=t[0].lng,i=t[0].lng,r=t[0].lat,s=t[0].lat;for(let{lat:a,lng:d}of t)d<e&&(e=d),d>i&&(i=d),a<r&&(r=a),a>s&&(s=a);let o=s>r?(s-r)*Be:Ae,n=i>e?(i-e)*Be:Ae;return{west:e-n,east:i+n,south:r-o,north:s+o}}function ze(t){let{center:e,zoom:i,fallback:r,mapControls:s}=t,o=g(),n=I(),a=B(null),d=B(null),l=B(null),[c,h]=_e(!1),b=B(!1),w=B(!1),k=B(!1),p=o.map;W(()=>{let L=l.current;if(!p||!L)return;let f=[];for(let u of Object.keys(n.points)){if(n.layers[u]===!1)continue;let y=o.datasets.get(u),R=n.points[u]??[],ae={id:u,markers:R.map(M=>({id:M.id,position:M.position,iconUrl:y?.marker.iconUrl?.(M.entity),element:y?.marker.element?.(M.entity)})),clustering:y?.clustering,onMarkerClick:M=>o.selectPoint(u,M)};f.push(p.renderLayer(L,ae))}return()=>{f.forEach(u=>u())}},[o,p,c,n.points,n.layers]),W(()=>{if(!a.current||!p)return;let L=a.current,f=!1,u=null;return(async()=>{let y=await p.mount(L,{center:e,zoom:i,fullscreenTarget:d.current??void 0});if(f){p.destroy(y);return}l.current=y,u=p.onBoundsChange(y,R=>{k.current?k.current=!1:w.current=!0,o.loadPoints(R)}),h(!0),o.loadPoints(Pt)})(),()=>{f=!0,u?.(),l.current&&(p.destroy(l.current),l.current=null),h(!1)}},[o,p]),W(()=>{let L=l.current;if(!p||!L||e||b.current||w.current)return;let f=[];for(let y of Object.keys(n.points))if(n.layers[y]!==!1)for(let R of n.points[y]??[])f.push(R.position);let u=Tt(f);u&&(b.current=!0,k.current=!0,p.fitBounds(L,u))},[p,e,c,n.points,n.layers]),W(()=>{p?.updateMarkerStates(n.selection,n.hovered)},[p,c,n.selection,n.hovered]);let{Popup:F}=P(),O=F!==le,_=n.points[o.primaryDatasetId]??[],A=n.selection!=null?_.find(L=>L.id===n.selection):void 0,[V,j]=_e(null),N=B(A);return N.current=A,W(()=>{let L=l.current;if(!p||!L||!O)return;let f=N.current;if(!f)return;let u=p.mountOverlay(f.position);j({entity:f.entity,position:f.position,container:u.container});let y=()=>o.selectPoint(o.primaryDatasetId,null),R=M=>{M.key==="Escape"&&y()};document.addEventListener("keydown",R);let ae=p.onMapClick(y);return()=>{document.removeEventListener("keydown",R),ae(),u.unmount(),j(null)}},[o,p,c,n.selection,O]),He("div",{ref:d,className:"relative h-full min-h-0 w-full",children:[He("div",{ref:a,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,O&&V?bt(pe(F,{entity:V.entity,onClose:()=>o.selectPoint(o.primaryDatasetId,null)}),V.container):null]}),s!=null&&pe("div",{className:"pointer-events-none absolute inset-0",children:pe("div",{className:"pointer-events-auto",children:s})})]})}import{jsx as Ft}from"react/jsx-runtime";function Ke(){let t=g(),{results:e,pagination:i}=I();return e.nextCursor==null?null:Ft("button",{type:"button",disabled:i.loading,onClick:()=>{t.loadPage()},children:"Load more"})}import{jsx as Ct}from"react/jsx-runtime";function Ue(){let{items:t,total:e}=z(),{ResultHeader:i}=P();return Ct(i,{count:t.length,total:e})}import{useEffect as St,useRef as It}from"react";function Ve(t,e){let i=g(),r=It(e);r.current=e,St(()=>i.on(t,s=>r.current(s)),[i,t])}import{useEffect as wt,useState as $e}from"react";import{jsx as kt}from"react/jsx-runtime";function We(t){let{children:e,...i}=t,[r]=$e(()=>i),[s,o]=$e(null);return wt(()=>{let n=new J({datasets:r.datasets,filters:r.filters,config:r.config,map:r.map,initialFilters:r.initialFilters,primaryDatasetId:r.primaryDatasetId});return r.urlSync&&r.urlSync.start(n),o(n),()=>{r.urlSync&&r.urlSync.stop(),n.dispose(),o(a=>a===n?null:a)}},[r]),s?kt(Y.Provider,{value:s,children:e}):null}function K(t){return typeof t!="number"?t:new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0}).format(t)}import{Fragment as xt,jsx as E,jsxs as qe}from"react/jsx-runtime";function Et(t){return t?"rle-card rle-card--selected":"rle-card"}function ce({item:t,selected:e,onSelect:i}){let r=t??{},s=Et(e),o=qe(xt,{children:[r.imageUrl?E("img",{src:r.imageUrl,alt:r.title??"",className:"rle-card-media"}):E("div",{className:"rle-card-media rle-card-media--placeholder","aria-hidden":"true"}),qe("div",{className:"rle-card-body",children:[r.title&&E("span",{className:"rle-card-title",children:r.title}),r.subtitle&&E("span",{className:"rle-card-address",children:r.subtitle}),r.badge&&E("div",{className:"rle-card-info",children:E("span",{className:"rle-card-info-item",children:r.badge})}),r.price!=null&&E("span",{className:"rle-card-price",children:K(r.price)})]})]});return i?E("button",{type:"button",onClick:i,"aria-pressed":e??!1,className:s,children:o}):E("article",{className:s,children:o})}import{jsx as ie,jsxs as je}from"react/jsx-runtime";function ue(t){return je("div",{role:"status",className:"rle-empty",children:[je("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:[ie("circle",{cx:"11",cy:"11",r:"7"}),ie("path",{d:"m21 21-4.3-4.3"})]}),ie("p",{className:"rle-empty-title",children:"No results"}),ie("p",{className:"rle-empty-hint",children:"Try adjusting your filters or search terms."})]})}import{jsx as Nt}from"react/jsx-runtime";function me({children:t}){return Nt("div",{className:"rle-filter-panel",children:t})}import{jsx as re,jsxs as Rt}from"react/jsx-runtime";function ge(t){return re("div",{className:"rle-loading",role:"status","aria-busy":"true","aria-label":"Loading results",children:Array.from({length:3}).map((e,i)=>Rt("div",{className:"rle-loading-item",children:[re("div",{className:"rle-skeleton",style:{aspectRatio:"4 / 3",width:"100%"}}),re("div",{className:"rle-skeleton",style:{height:16,width:"65%"}}),re("div",{className:"rle-skeleton",style:{height:12,width:"35%"}})]},i))})}import{jsx as Mt}from"react/jsx-runtime";function fe({point:t}){let e=t.entity??{};return Mt("span",{className:"rle-pin",children:e.price!=null?K(e.price):""})}import{jsx as U,jsxs as ye}from"react/jsx-runtime";function ve({entity:t,onClose:e}){let i=t??{};return ye("div",{className:"rle-popup",role:"group","aria-label":"Location details",children:[U("button",{type:"button",onClick:e,"aria-label":"Close",className:"rle-popup-close",children:ye("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:[U("path",{d:"M18 6 6 18"}),U("path",{d:"m6 6 12 12"})]})}),ye("div",{children:[i.title&&U("div",{className:"rle-card-title",children:i.title}),i.subtitle&&U("div",{className:"rle-card-address",children:i.subtitle}),i.price!=null&&U("div",{className:"rle-card-price",children:K(i.price)})]})]})}import{jsx as Dt}from"react/jsx-runtime";function he({count:t,total:e}){let i=e!=null&&e!==t?`${t} of ${e} results`:`${t} results`;return Dt("div",{className:"rle-result-header",children:i})}import{jsx as Ot}from"react/jsx-runtime";function Le({value:t,onChange:e,placeholder:i}){return Ot("input",{type:"search",value:t,placeholder:i,onChange:r=>e(r.target.value),"aria-label":i??"Search",className:"rle-input"})}import{jsx as _t}from"react/jsx-runtime";function be({children:t}){return _t("aside",{className:"rle-sidebar",children:t})}import{jsx as Bt}from"react/jsx-runtime";function Pe({children:t}){return Bt("div",{className:"rle-toolbar",children:t})}var Qe={Card:ce,Marker:fe,Popup:ve,Sidebar:be,FilterPanel:me,Search:Le,Empty:ue,Loading:ge,ResultHeader:he,Toolbar:Pe};import{jsx as T,jsxs as q}from"react/jsx-runtime";function Ge({view:t,onViewChange:e}){return T("nav",{className:"rle-bottom-nav","aria-label":"Listing navigation",children:q("div",{className:"rle-viewtoggle",role:"group","aria-label":"View",children:[q("button",{type:"button",className:`rle-viewtoggle__btn${t==="list"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="list",onClick:()=>e("list"),children:[T(At,{}),"List"]}),q("button",{type:"button",className:`rle-viewtoggle__btn${t==="map"?" rle-viewtoggle__btn--active":""}`,"aria-pressed":t==="map",onClick:()=>e("map"),children:[T(Ht,{}),"Map"]})]})})}function At(){return q("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 Ht(){return q("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 Te,useRef as zt,useState as Ze}from"react";import{createPortal as Kt}from"react-dom";import{Fragment as Vt,jsx as x,jsxs as ne}from"react/jsx-runtime";function Xe({open:t,onOpenChange:e,title:i,children:r,footer:s}){let o=zt(null),[n,a]=Ze(!1),[d,l]=Ze(!1);return Te(()=>{if(!t){l(!1),a(!1);return}a(!0);let c=requestAnimationFrame(()=>l(!0));return()=>cancelAnimationFrame(c)},[t]),Te(()=>{if(!n)return;let c=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=c}},[n]),Te(()=>{if(!n)return;o.current?.focus();function c(h){h.key==="Escape"&&e(!1)}return document.addEventListener("keydown",c),()=>document.removeEventListener("keydown",c)},[n,e]),!n||typeof document>"u"?null:Kt(ne(Vt,{children:[x("div",{className:`rle-sheet-backdrop${d?" rle-sheet-backdrop--open":""}`,onClick:()=>e(!1),"aria-hidden":"true"}),ne("div",{ref:o,className:`rle-sheet${d?" rle-sheet--open":""}`,role:"dialog","aria-modal":"true","aria-label":i,tabIndex:-1,children:[x("div",{className:"rle-sheet__handle","aria-hidden":"true"}),ne("div",{className:"rle-sheet__header",children:[i&&x("div",{className:"rle-sheet__title",children:i}),x("button",{type:"button",className:"rle-sheet__close",onClick:()=>e(!1),"aria-label":"Close",children:x(Ut,{})})]}),x("div",{className:"rle-sheet__body",children:r}),s&&x("div",{className:"rle-sheet__footer",children:s})]})]}),document.body)}function Ut(){return ne("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:[x("path",{d:"M18 6 6 18"}),x("path",{d:"m6 6 12 12"})]})}import{useEffect as Ye,useRef as qt,useState as oe}from"react";import{jsx as v,jsxs as se}from"react/jsx-runtime";function Je({search:t,onFiltersClick:e,filterCount:i=0,action:r}){let{Search:s}=P();return se("header",{className:"rle-mobile-header",children:[t&&v("div",{className:"rle-mobile-header__search",children:v(s,{value:t.value,onChange:t.onChange,placeholder:t.placeholder})}),se("button",{type:"button",className:"rle-btn rle-mobile-header__btn",onClick:e,children:[v($t,{}),v("span",{children:"Filters"}),i>0&&v("span",{className:"rle-mobile-header__count",children:i})]}),r&&v("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??v(Wt,{})})]})}function $t(){return se("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:[v("line",{x1:"4",y1:"6",x2:"20",y2:"6"}),v("circle",{cx:"9",cy:"6",r:"2",fill:"currentColor",stroke:"none"}),v("line",{x1:"4",y1:"12",x2:"20",y2:"12"}),v("circle",{cx:"15",cy:"12",r:"2",fill:"currentColor",stroke:"none"}),v("line",{x1:"4",y1:"18",x2:"20",y2:"18"}),v("circle",{cx:"11",cy:"18",r:"2",fill:"currentColor",stroke:"none"})]})}function Wt(){return se("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:[v("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),v("line",{x1:"5",y1:"12",x2:"19",y2:"12"})]})}import{Fragment as Gt,jsx as m,jsxs as D}from"react/jsx-runtime";var jt=m("div",{className:"rle-empty",children:"Map unavailable"});function Qt(){let t=qt(null),[e,i]=oe(!0),[r,s]=oe(!0);return Ye(()=>{let o=t.current;if(!o)return;let n=()=>{let{clientWidth:h,scrollLeft:b,scrollWidth:w}=o;i(b<=0),s(b+h>=w-1)},a=0,d=()=>{a||(a=requestAnimationFrame(()=>{a=0,n()}))};n(),o.addEventListener("scroll",n,{passive:!0});let l,c;return typeof ResizeObserver<"u"&&(l=new ResizeObserver(n),l.observe(o)),typeof MutationObserver<"u"&&(c=new MutationObserver(d),c.observe(o,{characterData:!0,childList:!0,subtree:!0})),()=>{o.removeEventListener("scroll",n),l?.disconnect(),c?.disconnect(),a&&cancelAnimationFrame(a)}},[]),{atEnd:r,atStart:e,ref:t}}function et({search:t,toolbarEnd:e,mobileAction:i,autoFetch:r=!0,hasMap:s,mapCenter:o,mapZoom:n,mapControls:a,className:d}){let l=g(),{Search:c}=P(),h=z(),{filters:b,set:w}=ee(),k=s??l.map!=null,[p,F]=oe("list"),[O,_]=oe(!1),{atEnd:A,atStart:V,ref:j}=Qt(),N=t?{value:String(b[t.filterKey]??""),onChange:y=>{w({[t.filterKey]:y||void 0})},placeholder:t.placeholder}:void 0;Ye(()=>{r!==!1&&l.applyFilters({})},[l,r]);let L=()=>{w(l.filters.clearedParams())},f=l.filters.activeKeys(b).length,u=h.total??h.items.length;return D("div",{className:d?`rle-app ${d}`:"rle-app",children:[D("div",{className:"rle-filter-bar",children:[D("div",{className:"rle-filter-bar__scroll",ref:j,children:[N&&m("div",{className:"rle-filter-bar__search",children:m(c,{value:N.value,onChange:N.onChange,placeholder:N.placeholder})}),m(de,{className:"rle-filters-row",groupClassName:"rle-filter-group",hideLabels:!0})]}),!V&&m("div",{className:"rle-filter-bar__fade rle-filter-bar__fade--start","aria-hidden":"true"}),!A&&m("div",{className:"rle-filter-bar__fade rle-filter-bar__fade--end","aria-hidden":"true"})]}),m(Je,{search:N,onFiltersClick:()=>_(!0),filterCount:f,action:i}),D("div",{className:`rle-body ${k?"rle-split":"rle-body--list-only"}`,"data-mobile-view":p,children:[D("div",{className:"rle-list",children:[D("div",{className:"rle-list-header",children:[m(Ue,{}),e&&m("div",{className:"rle-list-header__toolbar",children:e})]}),m(Oe,{className:"rle-list-grid"}),m(Ke,{})]}),k&&m("div",{className:"rle-map",children:m(ze,{center:o,zoom:n,fallback:jt,mapControls:a})})]}),k&&m(Ge,{view:p,onViewChange:F}),m(Xe,{title:"Filters",open:O,onOpenChange:_,footer:D(Gt,{children:[m("button",{type:"button",className:"rle-btn rle-btn--ghost",onClick:L,children:"Clear all"}),D("button",{type:"button",className:"rle-btn rle-btn--primary",onClick:()=>_(!1),children:["Show ",u," results"]})]}),children:m(de,{className:"rle-filter-stack",groupClassName:"rle-filter-group"})})]})}import{useEffect as Zt,useRef as Xt,useState as Jt}from"react";import{jsx as Fe,jsxs as ti}from"react/jsx-runtime";function tt(t){return"provider"in t}function Yt(t){let e=t!=null&&!tt(t),i=e?t.apiKey:void 0,r=e?t.mapId:void 0,s=e?t.mapOptions:void 0,o=e?t.styles:void 0,[n,a]=Jt(()=>!t||tt(t)?{ready:!0,provider:t?.provider}:{ready:!1});return Zt(()=>{if(!i)return;let d=!1;return import("./maps/google/index.js").then(({googleProvider:l})=>{d||a({ready:!0,provider:l({apiKey:i,mapId:r,mapOptions:s,styles:o})})}),()=>{d=!0}},[i,r]),n}function ei({onFiltersChange:t}){let e=Xt(t);return e.current=t,Ve("FiltersChanged",i=>{i.type==="FiltersChanged"&&e.current(i.filters)}),null}function Pn(t){let{datasets:e,filters:i,map:r,components:s,initialFilters:o,onFiltersChange:n,mobileAction:a,search:d,toolbarEnd:l,mapControls:c,config:h,autoFetch:b,className:w}=t,{ready:k,provider:p}=Yt(r);if(!k)return null;let F=[];for(let A of e)F.push(ke(A));i&&F.push(Ee(i)),p&&F.push(we(p)),o&&F.push(xe(o)),h&&F.push(Ie(h));let O=Se(...F),_={...Qe,...s};return ti(We,{...O,children:[n&&Fe(ei,{onFiltersChange:n}),Fe(Re,{..._,children:Fe(et,{className:w,search:d,toolbarEnd:l,mobileAction:a,autoFetch:b,mapCenter:r?.center,mapZoom:r?.zoom,mapControls:c})})]})}export{Q as a,H as b,G as c,Z as d,Ce as e,X as f,J as g,Se as h,Ie as i,we as j,ke as k,Ee as l,yi as m,xe as n,vi as o,le as p,Re as q,P as r,Y as s,g as t,I as u,ee as v,de as w,z as x,Oe as y,ze as z,Ke as A,Ue as B,Ve as C,We as D,ce as E,ue as F,me as G,ge as H,fe as I,ve as J,he as K,Le as L,be as M,Pe as N,Qe as O,Ge as P,Xe as Q,et as R,Pn as S};
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
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
|
|
2
|
+
var _chunk3O6X563Hcjs = require('./chunk-3O6X563H.cjs');var P=(r=>(r.Paged="paged",r.Infinite="infinite",r))(P||{});var v=(i=>(i.FiltersChanged="FiltersChanged",i.ResultsLoaded="ResultsLoaded",i.PointClicked="PointClicked",i.BoundsChanged="BoundsChanged",i.LayerToggled="LayerToggled",i))(v||{});var d= (_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 b(e,t){let r=new Set([...Object.keys(e),...Object.keys(t)]);for(let s of r)if(e[s]!==t[s])return!1;return!0}var h= (_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 r=this.toQueryFn(t.state.filters);b(r,this.history.getQuery())||this.withSyncGuard(()=>this.history.setQuery(r))}applyFiltersSafely(t,r){Promise.resolve(t.applyFilters(r)).catch(()=>{})}withSyncGuard(t){this.isSyncing=!0;try{t()}finally{this.isSyncing=!1}}}, _class2);var m=class{constructor(t={}){this.mode=_nullishCoalesce(t.mode, () => ("replace"))}getQuery(){if(typeof window>"u")return{};let t=new URLSearchParams(window.location.search),r={};for(let[s,o]of t)o!==""&&(r[s]=o);return r}setQuery(t){if(typeof window>"u")return;let r=new URLSearchParams;for(let[c,a]of Object.entries(t))a===void 0||a===""||r.set(c,a);let s=r.toString(),o=s?`?${s}`:"";if(o===window.location.search)return;let i=`${window.location.pathname}${o}${window.location.hash}`;this.mode==="push"?window.history.pushState(window.history.state,"",i):window.history.replaceState(window.history.state,"",i)}subscribe(t){return typeof window>"u"?()=>{}:(window.addEventListener("popstate",t),()=>{window.removeEventListener("popstate",t)})}};var _jsxruntime = require('react/jsx-runtime');function rt({children:e}){let{Toolbar:t}=_chunk3O6X563Hcjs.r.call(void 0, );return _jsxruntime.jsx.call(void 0, t,{children:e})}var _react = require('react');function lt(){let e=_chunk3O6X563Hcjs.t.call(void 0, ),t=_chunk3O6X563Hcjs.u.call(void 0, ),r=_react.useCallback.call(void 0, l=>e.loadPoints(l),[e]),s=_react.useCallback.call(void 0, (l,f)=>e.selectPoint(l,f),[e]),o=_react.useCallback.call(void 0, l=>e.setHovered(e.primaryDatasetId,l),[e]),i=_react.useCallback.call(void 0, ()=>_optionalChain([e, 'access', _5 => _5.map, 'optionalAccess', _6 => _6.zoomIn, 'call', _7 => _7()]),[e]),c=_react.useCallback.call(void 0, ()=>_optionalChain([e, 'access', _8 => _8.map, 'optionalAccess', _9 => _9.zoomOut, 'call', _10 => _10()]),[e]),a=_react.useCallback.call(void 0, ()=>_optionalChain([e, 'access', _11 => _11.map, 'optionalAccess', _12 => _12.toggleFullscreen, 'call', _13 => _13()]),[e]);return{bounds:t.bounds,points:t.points,hovered:t.hovered,loadPoints:r,selectPoint:s,setHovered:o,zoomIn:i,zoomOut:c,toggleFullscreen:a}}function dt(e){let t=_chunk3O6X563Hcjs.t.call(void 0, ),r=_chunk3O6X563Hcjs.u.call(void 0, ),s=_react.useCallback.call(void 0, ()=>t.toggleLayer(e),[t,e]);return{visible:_nullishCoalesce(r.layers[e], () => (!0)),points:_nullishCoalesce(r.points[e], () => ([])),toggle:s}}exports.BrowserHistoryPort = m; exports.DatasetRegistry = _chunk3O6X563Hcjs.c; exports.FallbackPopup = _chunk3O6X563Hcjs.p; exports.FilterRegistry = _chunk3O6X563Hcjs.b; exports.ListingApp = _chunk3O6X563Hcjs.S; exports.ListingComponentsProvider = _chunk3O6X563Hcjs.q; exports.ListingConfig = _chunk3O6X563Hcjs.f; exports.ListingEngine = _chunk3O6X563Hcjs.g; exports.ListingEngineContext = _chunk3O6X563Hcjs.s; exports.ListingEventType = v; exports.ListingFilters = _chunk3O6X563Hcjs.w; exports.ListingList = _chunk3O6X563Hcjs.y; exports.ListingMap = _chunk3O6X563Hcjs.z; exports.ListingPagination = _chunk3O6X563Hcjs.A; exports.ListingProvider = _chunk3O6X563Hcjs.D; exports.ListingResultHeader = _chunk3O6X563Hcjs.B; exports.ListingStore = _chunk3O6X563Hcjs.a; exports.ListingToolbar = rt; exports.MemoryHistoryPort = d; exports.PaginationMode = P; exports.TypedEmitter = _chunk3O6X563Hcjs.d; exports.UrlSyncController = h; exports.composeListingProviders = _chunk3O6X563Hcjs.h; exports.listingDefaultConfig = _chunk3O6X563Hcjs.e; exports.useListing = _chunk3O6X563Hcjs.t; exports.useListingComponents = _chunk3O6X563Hcjs.r; exports.useListingEvent = _chunk3O6X563Hcjs.C; exports.useListingFilters = _chunk3O6X563Hcjs.v; exports.useListingLayer = dt; exports.useListingMap = lt; exports.useListingResults = _chunk3O6X563Hcjs.x; exports.useListingState = _chunk3O6X563Hcjs.u; exports.withConfig = _chunk3O6X563Hcjs.i; exports.withDataset = _chunk3O6X563Hcjs.k; exports.withFilters = _chunk3O6X563Hcjs.l; exports.withInitialFilters = _chunk3O6X563Hcjs.n; exports.withMap = _chunk3O6X563Hcjs.j; exports.withPrimaryDataset = _chunk3O6X563Hcjs.o; exports.withUrlSync = _chunk3O6X563Hcjs.m;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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-
|
|
2
|
-
export { b as EntityAdapter, c as MapHandle, d as MapInitOptions, e as PageRequest, R as RenderedLayer } from './map-provider.interface-
|
|
3
|
-
import { P as PaginationMode, D as DatasetDefinition, I as IListingConfigOptions, F as FilterRegistry, a as IListingToolbarProps } from './listing-app-
|
|
4
|
-
export { C as ClusterOptions, b as
|
|
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-B5Law7gE.cjs';
|
|
2
|
+
export { b as EntityAdapter, c as MapHandle, d as MapInitOptions, e as MapOverlayHandle, f as PageRequest, R as RenderedLayer } from './map-provider.interface-B5Law7gE.cjs';
|
|
3
|
+
import { P as PaginationMode, D as DatasetDefinition, I as IListingConfigOptions, F as FilterRegistry, a as IListingToolbarProps } from './listing-app-C7EeHlmP.cjs';
|
|
4
|
+
export { C as ClusterOptions, b as FallbackPopup, c as FilterControlProps, d as FilterDefinition, e as IListingCardProps, f as IListingComponents, g as IListingEmptyProps, h as IListingFilterPanelProps, i as IListingLoadingProps, j as IListingMarkerProps, k as IListingPopupProps, l as IListingResultHeaderProps, m as IListingSearchProps, n as IListingSidebarProps, L as ListingApp, o as ListingAppProps, p as ListingComponentsProvider, M as MarkerRenderer, u as useListingComponents } from './listing-app-C7EeHlmP.cjs';
|
|
5
5
|
import * as react from 'react';
|
|
6
6
|
import { ReactNode } from 'react';
|
|
7
7
|
|
|
@@ -18,6 +18,7 @@ interface ListingState<TEntity, TFilters> {
|
|
|
18
18
|
results: Page<TEntity>;
|
|
19
19
|
bounds: Bounds | null;
|
|
20
20
|
selection: EntityId | null;
|
|
21
|
+
hovered: EntityId | null;
|
|
21
22
|
pagination: {
|
|
22
23
|
mode: PaginationMode;
|
|
23
24
|
loading: boolean;
|
|
@@ -43,6 +44,7 @@ declare class ListingStore<TEntity, TFilters> {
|
|
|
43
44
|
appendResults(page: Page<TEntity>): void;
|
|
44
45
|
setBounds(bounds: Bounds | null): void;
|
|
45
46
|
setSelection(id: EntityId | null): void;
|
|
47
|
+
setHovered(id: EntityId | null): void;
|
|
46
48
|
setLayerVisible(id: string, visible: boolean): void;
|
|
47
49
|
setLoading(loading: boolean): void;
|
|
48
50
|
setPoints(datasetId: string, points: MapPoint<unknown>[]): void;
|
|
@@ -177,6 +179,7 @@ declare class ListingEngine<TEntity, TFilters> {
|
|
|
177
179
|
readonly north: number;
|
|
178
180
|
} | null;
|
|
179
181
|
readonly selection: EntityId | null;
|
|
182
|
+
readonly hovered: EntityId | null;
|
|
180
183
|
readonly pagination: {
|
|
181
184
|
readonly mode: PaginationMode;
|
|
182
185
|
readonly loading: boolean;
|
|
@@ -199,7 +202,8 @@ declare class ListingEngine<TEntity, TFilters> {
|
|
|
199
202
|
applyFilters(patch: Partial<TFilters>): Promise<void>;
|
|
200
203
|
loadPage(): Promise<void>;
|
|
201
204
|
loadPoints(bounds: Bounds): Promise<void>;
|
|
202
|
-
selectPoint(datasetId: string, id: EntityId): void;
|
|
205
|
+
selectPoint(datasetId: string, id: EntityId | null): void;
|
|
206
|
+
setHovered(_datasetId: string, id: EntityId | null): void;
|
|
203
207
|
toggleLayer(id: string): void;
|
|
204
208
|
subscribe(cb: () => void): () => void;
|
|
205
209
|
on(type: ListingEvent<TEntity, TFilters>['type'] | '*', cb: (e: ListingEvent<TEntity, TFilters>) => void): () => void;
|
|
@@ -460,6 +464,17 @@ interface IListingMapProps {
|
|
|
460
464
|
* for the provider to mount into.
|
|
461
465
|
*/
|
|
462
466
|
fallback?: ReactNode;
|
|
467
|
+
/**
|
|
468
|
+
* Rendered as an absolutely-positioned overlay floating over the map (e.g. zoom/fullscreen
|
|
469
|
+
* buttons), independent of `MapProvider.mount`/the map SDK -- unlike the `Popup` slot (which is
|
|
470
|
+
* portaled through `provider.mountOverlay` so it can be lat/lng-anchored and pan with the map),
|
|
471
|
+
* this is a plain React child laid over the WHOLE map area, positioned by the consumer's own
|
|
472
|
+
* CSS on `mapControls`' content (e.g. `top-right`). The overlay wrapper itself is
|
|
473
|
+
* `pointer-events: none` (so it never blocks map drag/click-through) while `mapControls` is
|
|
474
|
+
* wrapped in a `pointer-events: auto` node so its own interactive content stays clickable.
|
|
475
|
+
* Omit (the default, `undefined`) to render nothing extra -- no behavior change.
|
|
476
|
+
*/
|
|
477
|
+
mapControls?: ReactNode;
|
|
463
478
|
}
|
|
464
479
|
/**
|
|
465
480
|
* Structure-only map mount point.
|
|
@@ -475,8 +490,9 @@ interface IListingMapProps {
|
|
|
475
490
|
* `ListingEngine.datasets` in the task report). Each marker's click routes
|
|
476
491
|
* to `engine.selectPoint(datasetId, markerId)`.
|
|
477
492
|
* - Mount effect (declared SECOND): awaits `provider.mount(container, {
|
|
478
|
-
* center, zoom })`, stashes the resulting `MapHandle` in a ref, and wires
|
|
479
|
-
* `provider.onBoundsChange(handle, b => engine.loadPoints(b))`.
|
|
493
|
+
* center, zoom, fullscreenTarget })`, stashes the resulting `MapHandle` in a ref, and wires
|
|
494
|
+
* `provider.onBoundsChange(handle, b => engine.loadPoints(b))`. `fullscreenTarget` is the outer
|
|
495
|
+
* wrapper (`wrapperRef`), not `container` itself -- see "`mapControls`" below for why. Cleanup
|
|
480
496
|
* unsubscribes bounds and calls `provider.destroy(handle)`. Also kicks a
|
|
481
497
|
* one-time, unbounded `engine.loadPoints(WORLD_BOUNDS)` right after the
|
|
482
498
|
* handle is ready -- see "Auto-fit" below for why.
|
|
@@ -547,17 +563,50 @@ interface IListingMapProps {
|
|
|
547
563
|
* subscription registered in the first place) instead of leaking a live map
|
|
548
564
|
* instance that nothing in the component tree references anymore.
|
|
549
565
|
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
566
|
+
* Popup overlay: when a `Popup` slot is injected (via `ListingComponentsProvider`)
|
|
567
|
+
* AND `state.selection` resolves to a loaded point of the primary dataset, the
|
|
568
|
+
* injected `Popup` is rendered -- via `createPortal` -- into an on-map overlay
|
|
569
|
+
* anchored at that point (`provider.mountOverlay(point.position)`; see that
|
|
570
|
+
* method's doc comment for the sync-container / async-attach split). The
|
|
571
|
+
* selected entity + anchor position are CAPTURED into component state
|
|
572
|
+
* (`capturedPopup`) when the overlay mounts, and the rendered `Popup` reads from
|
|
573
|
+
* that snapshot rather than from the live, pan-reactive `selected` -- so a pan
|
|
574
|
+
* that drops the selected point out of `state.points` leaves the open popup
|
|
575
|
+
* anchored and intact (it pans with the map like a Google InfoWindow) instead
|
|
576
|
+
* of tearing its content out and leaving an empty overlay behind. The popup is
|
|
577
|
+
* dismissed -- clearing the capture, unmounting the overlay, and clearing the
|
|
578
|
+
* selection via `engine.selectPoint(primary, null)` -- by the `Popup`'s own
|
|
579
|
+
* `onClose`, the `Esc` key, or a click on the map BACKGROUND
|
|
580
|
+
* (`provider.onMapClick`; marker clicks live in a separate pane and never fire
|
|
581
|
+
* it). Fully backward compatible: with NO `Popup` slot provided, nothing is
|
|
582
|
+
* mounted and there is no behavior change (detected by `Popup !== FallbackPopup`
|
|
583
|
+
* reference identity).
|
|
584
|
+
*
|
|
585
|
+
* Deliberately out of scope for this task (documented future enhancement):
|
|
586
|
+
* rendering the injected `Marker` React component INTO map markers via portals
|
|
587
|
+
* (only `iconUrl` + `onMarkerClick` -> `selectPoint` is wired).
|
|
555
588
|
*
|
|
556
589
|
* `fallback`: when `engine.map` is `undefined` (no `MapProvider` configured),
|
|
557
590
|
* `fallback` renders centered inside the same ref'd container instead of an
|
|
558
591
|
* empty div. The mount/layer effects both already no-op without a `provider`
|
|
559
592
|
* (see their guards below), so swapping in `fallback` content here is purely
|
|
560
593
|
* a render-output change -- it does not touch the mount lifecycle.
|
|
594
|
+
*
|
|
595
|
+
* `mapControls`: rendered as a plain (non-portaled) React overlay laid over the WHOLE map area --
|
|
596
|
+
* see `IListingMapProps.mapControls`'s own doc comment. Deliberately NOT a child of the ref'd
|
|
597
|
+
* container passed to `provider.mount()`: a real map SDK (e.g. Google Maps) takes ownership of
|
|
598
|
+
* that element's contents, so `mapControls` is instead a sibling inside an outer wrapper `<div>`
|
|
599
|
+
* (`wrapperRef`), absolutely positioned over it via CSS -- never competing with the map SDK for
|
|
600
|
+
* that node's children. `null`/`undefined` renders nothing extra (no wrapper divs at all), so
|
|
601
|
+
* this is a fully backward-compatible addition.
|
|
602
|
+
*
|
|
603
|
+
* Because `mapControls` is a SIBLING of the map mount div rather than a descendant, a
|
|
604
|
+
* fullscreen/zoom button rendered through it needs `toggleFullscreen()` to target an element that
|
|
605
|
+
* CONTAINS both of them -- the Fullscreen API only shows the target element and its descendants,
|
|
606
|
+
* so fullscreening the mount div alone would make any such button disappear the instant
|
|
607
|
+
* fullscreen is entered. `wrapperRef` (the outer `<div>` itself) is passed as `fullscreenTarget`
|
|
608
|
+
* in the mount effect above for exactly this reason -- see `MapInitOptions.fullscreenTarget`'s
|
|
609
|
+
* doc comment.
|
|
561
610
|
*/
|
|
562
611
|
declare function ListingMap(props: IListingMapProps): react.JSX.Element;
|
|
563
612
|
|
|
@@ -646,6 +695,7 @@ declare function useListingState<TEntity = unknown, TFilters = unknown>(): {
|
|
|
646
695
|
readonly north: number;
|
|
647
696
|
} | null;
|
|
648
697
|
readonly selection: EntityId | null;
|
|
698
|
+
readonly hovered: EntityId | null;
|
|
649
699
|
readonly pagination: {
|
|
650
700
|
readonly mode: PaginationMode;
|
|
651
701
|
readonly loading: boolean;
|
|
@@ -687,7 +737,12 @@ declare function useListingFilters<TFilters = unknown>(): {
|
|
|
687
737
|
setField: <K_2 extends keyof TFilters>(key: K_2, value: TFilters[K_2]) => Promise<void>;
|
|
688
738
|
};
|
|
689
739
|
|
|
690
|
-
/**
|
|
740
|
+
/**
|
|
741
|
+
* Map-facing slice of listing state (bounds, per-dataset points, hovered
|
|
742
|
+
* marker id) plus the engine actions that drive them. `hovered` is bound to
|
|
743
|
+
* the primary dataset (`engine.primaryDatasetId`) -- a transient
|
|
744
|
+
* highlight-on-hover affordance, independent of `selectPoint`'s `selection`.
|
|
745
|
+
*/
|
|
691
746
|
declare function useListingMap(): {
|
|
692
747
|
bounds: {
|
|
693
748
|
readonly west: number;
|
|
@@ -705,8 +760,13 @@ declare function useListingMap(): {
|
|
|
705
760
|
readonly entity: unknown;
|
|
706
761
|
}[];
|
|
707
762
|
};
|
|
763
|
+
hovered: EntityId | null;
|
|
708
764
|
loadPoints: (bounds: Bounds) => Promise<void>;
|
|
709
|
-
selectPoint: (datasetId: string, id: EntityId) => void;
|
|
765
|
+
selectPoint: (datasetId: string, id: EntityId | null) => void;
|
|
766
|
+
setHovered: (id: EntityId | null) => void;
|
|
767
|
+
zoomIn: () => void | undefined;
|
|
768
|
+
zoomOut: () => void | undefined;
|
|
769
|
+
toggleFullscreen: () => void | undefined;
|
|
710
770
|
};
|
|
711
771
|
|
|
712
772
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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-
|
|
2
|
-
export { b as EntityAdapter, c as MapHandle, d as MapInitOptions, e as PageRequest, R as RenderedLayer } from './map-provider.interface-
|
|
3
|
-
import { P as PaginationMode, D as DatasetDefinition, I as IListingConfigOptions, F as FilterRegistry, a as IListingToolbarProps } from './listing-app-
|
|
4
|
-
export { C as ClusterOptions, b as
|
|
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-B5Law7gE.js';
|
|
2
|
+
export { b as EntityAdapter, c as MapHandle, d as MapInitOptions, e as MapOverlayHandle, f as PageRequest, R as RenderedLayer } from './map-provider.interface-B5Law7gE.js';
|
|
3
|
+
import { P as PaginationMode, D as DatasetDefinition, I as IListingConfigOptions, F as FilterRegistry, a as IListingToolbarProps } from './listing-app-DHFJtnKI.js';
|
|
4
|
+
export { C as ClusterOptions, b as FallbackPopup, c as FilterControlProps, d as FilterDefinition, e as IListingCardProps, f as IListingComponents, g as IListingEmptyProps, h as IListingFilterPanelProps, i as IListingLoadingProps, j as IListingMarkerProps, k as IListingPopupProps, l as IListingResultHeaderProps, m as IListingSearchProps, n as IListingSidebarProps, L as ListingApp, o as ListingAppProps, p as ListingComponentsProvider, M as MarkerRenderer, u as useListingComponents } from './listing-app-DHFJtnKI.js';
|
|
5
5
|
import * as react from 'react';
|
|
6
6
|
import { ReactNode } from 'react';
|
|
7
7
|
|
|
@@ -18,6 +18,7 @@ interface ListingState<TEntity, TFilters> {
|
|
|
18
18
|
results: Page<TEntity>;
|
|
19
19
|
bounds: Bounds | null;
|
|
20
20
|
selection: EntityId | null;
|
|
21
|
+
hovered: EntityId | null;
|
|
21
22
|
pagination: {
|
|
22
23
|
mode: PaginationMode;
|
|
23
24
|
loading: boolean;
|
|
@@ -43,6 +44,7 @@ declare class ListingStore<TEntity, TFilters> {
|
|
|
43
44
|
appendResults(page: Page<TEntity>): void;
|
|
44
45
|
setBounds(bounds: Bounds | null): void;
|
|
45
46
|
setSelection(id: EntityId | null): void;
|
|
47
|
+
setHovered(id: EntityId | null): void;
|
|
46
48
|
setLayerVisible(id: string, visible: boolean): void;
|
|
47
49
|
setLoading(loading: boolean): void;
|
|
48
50
|
setPoints(datasetId: string, points: MapPoint<unknown>[]): void;
|
|
@@ -177,6 +179,7 @@ declare class ListingEngine<TEntity, TFilters> {
|
|
|
177
179
|
readonly north: number;
|
|
178
180
|
} | null;
|
|
179
181
|
readonly selection: EntityId | null;
|
|
182
|
+
readonly hovered: EntityId | null;
|
|
180
183
|
readonly pagination: {
|
|
181
184
|
readonly mode: PaginationMode;
|
|
182
185
|
readonly loading: boolean;
|
|
@@ -199,7 +202,8 @@ declare class ListingEngine<TEntity, TFilters> {
|
|
|
199
202
|
applyFilters(patch: Partial<TFilters>): Promise<void>;
|
|
200
203
|
loadPage(): Promise<void>;
|
|
201
204
|
loadPoints(bounds: Bounds): Promise<void>;
|
|
202
|
-
selectPoint(datasetId: string, id: EntityId): void;
|
|
205
|
+
selectPoint(datasetId: string, id: EntityId | null): void;
|
|
206
|
+
setHovered(_datasetId: string, id: EntityId | null): void;
|
|
203
207
|
toggleLayer(id: string): void;
|
|
204
208
|
subscribe(cb: () => void): () => void;
|
|
205
209
|
on(type: ListingEvent<TEntity, TFilters>['type'] | '*', cb: (e: ListingEvent<TEntity, TFilters>) => void): () => void;
|
|
@@ -460,6 +464,17 @@ interface IListingMapProps {
|
|
|
460
464
|
* for the provider to mount into.
|
|
461
465
|
*/
|
|
462
466
|
fallback?: ReactNode;
|
|
467
|
+
/**
|
|
468
|
+
* Rendered as an absolutely-positioned overlay floating over the map (e.g. zoom/fullscreen
|
|
469
|
+
* buttons), independent of `MapProvider.mount`/the map SDK -- unlike the `Popup` slot (which is
|
|
470
|
+
* portaled through `provider.mountOverlay` so it can be lat/lng-anchored and pan with the map),
|
|
471
|
+
* this is a plain React child laid over the WHOLE map area, positioned by the consumer's own
|
|
472
|
+
* CSS on `mapControls`' content (e.g. `top-right`). The overlay wrapper itself is
|
|
473
|
+
* `pointer-events: none` (so it never blocks map drag/click-through) while `mapControls` is
|
|
474
|
+
* wrapped in a `pointer-events: auto` node so its own interactive content stays clickable.
|
|
475
|
+
* Omit (the default, `undefined`) to render nothing extra -- no behavior change.
|
|
476
|
+
*/
|
|
477
|
+
mapControls?: ReactNode;
|
|
463
478
|
}
|
|
464
479
|
/**
|
|
465
480
|
* Structure-only map mount point.
|
|
@@ -475,8 +490,9 @@ interface IListingMapProps {
|
|
|
475
490
|
* `ListingEngine.datasets` in the task report). Each marker's click routes
|
|
476
491
|
* to `engine.selectPoint(datasetId, markerId)`.
|
|
477
492
|
* - Mount effect (declared SECOND): awaits `provider.mount(container, {
|
|
478
|
-
* center, zoom })`, stashes the resulting `MapHandle` in a ref, and wires
|
|
479
|
-
* `provider.onBoundsChange(handle, b => engine.loadPoints(b))`.
|
|
493
|
+
* center, zoom, fullscreenTarget })`, stashes the resulting `MapHandle` in a ref, and wires
|
|
494
|
+
* `provider.onBoundsChange(handle, b => engine.loadPoints(b))`. `fullscreenTarget` is the outer
|
|
495
|
+
* wrapper (`wrapperRef`), not `container` itself -- see "`mapControls`" below for why. Cleanup
|
|
480
496
|
* unsubscribes bounds and calls `provider.destroy(handle)`. Also kicks a
|
|
481
497
|
* one-time, unbounded `engine.loadPoints(WORLD_BOUNDS)` right after the
|
|
482
498
|
* handle is ready -- see "Auto-fit" below for why.
|
|
@@ -547,17 +563,50 @@ interface IListingMapProps {
|
|
|
547
563
|
* subscription registered in the first place) instead of leaking a live map
|
|
548
564
|
* instance that nothing in the component tree references anymore.
|
|
549
565
|
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
566
|
+
* Popup overlay: when a `Popup` slot is injected (via `ListingComponentsProvider`)
|
|
567
|
+
* AND `state.selection` resolves to a loaded point of the primary dataset, the
|
|
568
|
+
* injected `Popup` is rendered -- via `createPortal` -- into an on-map overlay
|
|
569
|
+
* anchored at that point (`provider.mountOverlay(point.position)`; see that
|
|
570
|
+
* method's doc comment for the sync-container / async-attach split). The
|
|
571
|
+
* selected entity + anchor position are CAPTURED into component state
|
|
572
|
+
* (`capturedPopup`) when the overlay mounts, and the rendered `Popup` reads from
|
|
573
|
+
* that snapshot rather than from the live, pan-reactive `selected` -- so a pan
|
|
574
|
+
* that drops the selected point out of `state.points` leaves the open popup
|
|
575
|
+
* anchored and intact (it pans with the map like a Google InfoWindow) instead
|
|
576
|
+
* of tearing its content out and leaving an empty overlay behind. The popup is
|
|
577
|
+
* dismissed -- clearing the capture, unmounting the overlay, and clearing the
|
|
578
|
+
* selection via `engine.selectPoint(primary, null)` -- by the `Popup`'s own
|
|
579
|
+
* `onClose`, the `Esc` key, or a click on the map BACKGROUND
|
|
580
|
+
* (`provider.onMapClick`; marker clicks live in a separate pane and never fire
|
|
581
|
+
* it). Fully backward compatible: with NO `Popup` slot provided, nothing is
|
|
582
|
+
* mounted and there is no behavior change (detected by `Popup !== FallbackPopup`
|
|
583
|
+
* reference identity).
|
|
584
|
+
*
|
|
585
|
+
* Deliberately out of scope for this task (documented future enhancement):
|
|
586
|
+
* rendering the injected `Marker` React component INTO map markers via portals
|
|
587
|
+
* (only `iconUrl` + `onMarkerClick` -> `selectPoint` is wired).
|
|
555
588
|
*
|
|
556
589
|
* `fallback`: when `engine.map` is `undefined` (no `MapProvider` configured),
|
|
557
590
|
* `fallback` renders centered inside the same ref'd container instead of an
|
|
558
591
|
* empty div. The mount/layer effects both already no-op without a `provider`
|
|
559
592
|
* (see their guards below), so swapping in `fallback` content here is purely
|
|
560
593
|
* a render-output change -- it does not touch the mount lifecycle.
|
|
594
|
+
*
|
|
595
|
+
* `mapControls`: rendered as a plain (non-portaled) React overlay laid over the WHOLE map area --
|
|
596
|
+
* see `IListingMapProps.mapControls`'s own doc comment. Deliberately NOT a child of the ref'd
|
|
597
|
+
* container passed to `provider.mount()`: a real map SDK (e.g. Google Maps) takes ownership of
|
|
598
|
+
* that element's contents, so `mapControls` is instead a sibling inside an outer wrapper `<div>`
|
|
599
|
+
* (`wrapperRef`), absolutely positioned over it via CSS -- never competing with the map SDK for
|
|
600
|
+
* that node's children. `null`/`undefined` renders nothing extra (no wrapper divs at all), so
|
|
601
|
+
* this is a fully backward-compatible addition.
|
|
602
|
+
*
|
|
603
|
+
* Because `mapControls` is a SIBLING of the map mount div rather than a descendant, a
|
|
604
|
+
* fullscreen/zoom button rendered through it needs `toggleFullscreen()` to target an element that
|
|
605
|
+
* CONTAINS both of them -- the Fullscreen API only shows the target element and its descendants,
|
|
606
|
+
* so fullscreening the mount div alone would make any such button disappear the instant
|
|
607
|
+
* fullscreen is entered. `wrapperRef` (the outer `<div>` itself) is passed as `fullscreenTarget`
|
|
608
|
+
* in the mount effect above for exactly this reason -- see `MapInitOptions.fullscreenTarget`'s
|
|
609
|
+
* doc comment.
|
|
561
610
|
*/
|
|
562
611
|
declare function ListingMap(props: IListingMapProps): react.JSX.Element;
|
|
563
612
|
|
|
@@ -646,6 +695,7 @@ declare function useListingState<TEntity = unknown, TFilters = unknown>(): {
|
|
|
646
695
|
readonly north: number;
|
|
647
696
|
} | null;
|
|
648
697
|
readonly selection: EntityId | null;
|
|
698
|
+
readonly hovered: EntityId | null;
|
|
649
699
|
readonly pagination: {
|
|
650
700
|
readonly mode: PaginationMode;
|
|
651
701
|
readonly loading: boolean;
|
|
@@ -687,7 +737,12 @@ declare function useListingFilters<TFilters = unknown>(): {
|
|
|
687
737
|
setField: <K_2 extends keyof TFilters>(key: K_2, value: TFilters[K_2]) => Promise<void>;
|
|
688
738
|
};
|
|
689
739
|
|
|
690
|
-
/**
|
|
740
|
+
/**
|
|
741
|
+
* Map-facing slice of listing state (bounds, per-dataset points, hovered
|
|
742
|
+
* marker id) plus the engine actions that drive them. `hovered` is bound to
|
|
743
|
+
* the primary dataset (`engine.primaryDatasetId`) -- a transient
|
|
744
|
+
* highlight-on-hover affordance, independent of `selectPoint`'s `selection`.
|
|
745
|
+
*/
|
|
691
746
|
declare function useListingMap(): {
|
|
692
747
|
bounds: {
|
|
693
748
|
readonly west: number;
|
|
@@ -705,8 +760,13 @@ declare function useListingMap(): {
|
|
|
705
760
|
readonly entity: unknown;
|
|
706
761
|
}[];
|
|
707
762
|
};
|
|
763
|
+
hovered: EntityId | null;
|
|
708
764
|
loadPoints: (bounds: Bounds) => Promise<void>;
|
|
709
|
-
selectPoint: (datasetId: string, id: EntityId) => void;
|
|
765
|
+
selectPoint: (datasetId: string, id: EntityId | null) => void;
|
|
766
|
+
setHovered: (id: EntityId | null) => void;
|
|
767
|
+
zoomIn: () => void | undefined;
|
|
768
|
+
zoomOut: () => void | undefined;
|
|
769
|
+
toggleFullscreen: () => void | undefined;
|
|
710
770
|
};
|
|
711
771
|
|
|
712
772
|
/**
|