@burdenoff/microfe-housingvista 2026.923.1 → 2026.924.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"CoordinatePicker.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/components/maps/CoordinatePicker.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAgDD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,KAAK,IAAI,CAAC;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,GAAI,gCAAgC,qBAAqB,gCA6HrF,CAAC"}
1
+ {"version":3,"file":"CoordinatePicker.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/components/maps/CoordinatePicker.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAmFD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,KAAK,IAAI,CAAC;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,GAAI,gCAAgC,qBAAqB,gCA8HrF,CAAC"}
@@ -4,22 +4,22 @@ import { MapContainer as r } from "../../../node_modules/react-leaflet/lib/MapCo
4
4
  import { Marker as i } from "../../../node_modules/react-leaflet/lib/Marker.js";
5
5
  import { TileLayer as a } from "../../../node_modules/react-leaflet/lib/TileLayer.js";
6
6
  import { OSM_ATTRIBUTION as o, OSM_TILE_URL as s, createPropertyPinIcon as c, isRealCoord as l } from "./leaflet-setup.js";
7
- import { useEffect as u, useState as d } from "react";
8
- import { Crosshair as f, Trash2 as p } from "lucide-react";
9
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
7
+ import { useEffect as u, useRef as d, useState as f } from "react";
8
+ import { Crosshair as p, Trash2 as m } from "lucide-react";
9
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
10
  //#region src/housingvista/components/maps/CoordinatePicker.tsx
11
- var g = [20.5937, 78.9629];
12
- function _() {
13
- let [e, t] = d(!1);
11
+ var _ = [20.5937, 78.9629];
12
+ function v() {
13
+ let [e, t] = f(!1);
14
14
  return u(() => t(!0), []), e;
15
15
  }
16
- function v({ onSelect: e }) {
16
+ function y({ onSelect: e }) {
17
17
  return n({ click: (t) => e({
18
18
  latitude: Number(t.latlng.lat.toFixed(6)),
19
19
  longitude: Number(t.latlng.lng.toFixed(6))
20
20
  }) }), null;
21
21
  }
22
- function y({ latitude: e, longitude: n, zoom: r }) {
22
+ function b({ latitude: e, longitude: n, zoom: r }) {
23
23
  let i = t();
24
24
  return u(() => {
25
25
  i.setView([e, n], r);
@@ -30,38 +30,49 @@ function y({ latitude: e, longitude: n, zoom: r }) {
30
30
  r
31
31
  ]), null;
32
32
  }
33
- var b = ({ value: t, onChange: n, className: u }) => {
34
- let b = _(), [x, S] = d({
33
+ function x({ center: e }) {
34
+ let n = t(), r = d(e);
35
+ return r.current = e, u(() => {
36
+ let e = n.getContainer(), t = () => {
37
+ n.invalidateSize(), n.setView(r.current, n.getZoom(), { animate: !1 });
38
+ }, i = () => n.invalidateSize(), a = requestAnimationFrame(t), o = window.setTimeout(t, 250), s = typeof ResizeObserver < "u" ? new ResizeObserver(i) : null;
39
+ return s?.observe(e), window.addEventListener("resize", i), () => {
40
+ cancelAnimationFrame(a), window.clearTimeout(o), s?.disconnect(), window.removeEventListener("resize", i);
41
+ };
42
+ }, [n]), null;
43
+ }
44
+ var S = ({ value: t, onChange: n, className: u }) => {
45
+ let d = v(), [S, C] = f({
35
46
  latitude: t ? String(t.latitude) : "",
36
47
  longitude: t ? String(t.longitude) : ""
37
- }), C = (e) => {
38
- n(e), S(e ? {
48
+ }), w = (e) => {
49
+ n(e), C(e ? {
39
50
  latitude: String(e.latitude),
40
51
  longitude: String(e.longitude)
41
52
  } : {
42
53
  latitude: "",
43
54
  longitude: ""
44
55
  });
45
- }, w = (e) => C(e), T = (e) => {
46
- S(e);
56
+ }, T = (e) => w(e), E = (e) => {
57
+ C(e);
47
58
  let t = Number(e.latitude), r = Number(e.longitude);
48
59
  n(e.latitude.trim() !== "" && e.longitude.trim() !== "" && l(t, r) ? {
49
60
  latitude: t,
50
61
  longitude: r
51
62
  } : null);
52
- }, E = () => {
53
- navigator.geolocation && navigator.geolocation.getCurrentPosition((e) => C({
63
+ }, D = () => {
64
+ navigator.geolocation && navigator.geolocation.getCurrentPosition((e) => w({
54
65
  latitude: Number(e.coords.latitude.toFixed(6)),
55
66
  longitude: Number(e.coords.longitude.toFixed(6))
56
67
  }), () => {});
57
- }, D = t ? [t.latitude, t.longitude] : g, O = "h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]";
58
- return /* @__PURE__ */ h("div", {
68
+ }, O = t ? [t.latitude, t.longitude] : _, k = "h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]";
69
+ return /* @__PURE__ */ g("div", {
59
70
  className: e("space-y-2", u),
60
71
  children: [
61
- /* @__PURE__ */ m("div", {
72
+ /* @__PURE__ */ h("div", {
62
73
  className: "h-64 w-full overflow-hidden rounded-md border border-border-subtle",
63
- children: b ? /* @__PURE__ */ h(r, {
64
- center: D,
74
+ children: d ? /* @__PURE__ */ g(r, {
75
+ center: O,
65
76
  zoom: t ? 13 : 4,
66
77
  scrollWheelZoom: !0,
67
78
  style: {
@@ -70,72 +81,73 @@ var b = ({ value: t, onChange: n, className: u }) => {
70
81
  },
71
82
  className: "z-0",
72
83
  children: [
73
- /* @__PURE__ */ m(a, {
84
+ /* @__PURE__ */ h(a, {
74
85
  url: s,
75
86
  attribution: o
76
87
  }),
77
- /* @__PURE__ */ m(v, { onSelect: w }),
78
- t && /* @__PURE__ */ m(y, {
88
+ /* @__PURE__ */ h(x, { center: O }),
89
+ /* @__PURE__ */ h(y, { onSelect: T }),
90
+ t && /* @__PURE__ */ h(b, {
79
91
  latitude: t.latitude,
80
92
  longitude: t.longitude,
81
93
  zoom: 13
82
94
  }),
83
- t && /* @__PURE__ */ m(i, {
95
+ t && /* @__PURE__ */ h(i, {
84
96
  position: [t.latitude, t.longitude],
85
97
  icon: c("Pin")
86
98
  })
87
99
  ]
88
- }) : /* @__PURE__ */ m("div", { className: "h-full w-full bg-bg-sunken" })
100
+ }) : /* @__PURE__ */ h("div", { className: "h-full w-full bg-bg-sunken" })
89
101
  }),
90
- /* @__PURE__ */ h("div", {
102
+ /* @__PURE__ */ g("div", {
91
103
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
92
- children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
104
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
93
105
  htmlFor: "latitude",
94
106
  className: "mb-1.5 block text-sm font-medium text-text-primary",
95
107
  children: "Latitude"
96
- }), /* @__PURE__ */ m("input", {
108
+ }), /* @__PURE__ */ h("input", {
97
109
  id: "latitude",
98
110
  name: "latitude",
99
111
  inputMode: "decimal",
100
- value: x.latitude,
101
- onChange: (e) => T({
112
+ value: S.latitude,
113
+ onChange: (e) => E({
102
114
  latitude: e.target.value,
103
- longitude: x.longitude
115
+ longitude: S.longitude
104
116
  }),
105
117
  placeholder: "e.g., 12.9716",
106
- className: O
107
- })] }), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
118
+ className: k
119
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
108
120
  htmlFor: "longitude",
109
121
  className: "mb-1.5 block text-sm font-medium text-text-primary",
110
122
  children: "Longitude"
111
- }), /* @__PURE__ */ m("input", {
123
+ }), /* @__PURE__ */ h("input", {
112
124
  id: "longitude",
113
125
  name: "longitude",
114
126
  inputMode: "decimal",
115
- value: x.longitude,
116
- onChange: (e) => T({
117
- latitude: x.latitude,
127
+ value: S.longitude,
128
+ onChange: (e) => E({
129
+ latitude: S.latitude,
118
130
  longitude: e.target.value
119
131
  }),
120
132
  placeholder: "e.g., 77.5946",
121
- className: O
133
+ className: k
122
134
  })] })]
123
135
  }),
124
- /* @__PURE__ */ h("div", {
136
+ /* @__PURE__ */ g("div", {
125
137
  className: "flex items-center gap-2",
126
- children: [/* @__PURE__ */ h("button", {
138
+ children: [/* @__PURE__ */ g("button", {
127
139
  type: "button",
128
- onClick: E,
140
+ onClick: D,
129
141
  className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-primary transition-colors hover:bg-bg-sunken",
130
- children: [/* @__PURE__ */ m(f, { className: "h-4 w-4" }), "Use my location"]
131
- }), t && /* @__PURE__ */ h("button", {
142
+ children: [/* @__PURE__ */ h(p, { className: "h-4 w-4" }), "Use my location"]
143
+ }), t && /* @__PURE__ */ g("button", {
132
144
  type: "button",
133
- onClick: () => C(null),
145
+ onClick: () => w(null),
134
146
  className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-secondary transition-colors hover:bg-bg-sunken",
135
- children: [/* @__PURE__ */ m(p, { className: "h-4 w-4" }), "Clear"]
147
+ children: [/* @__PURE__ */ h(m, { className: "h-4 w-4" }), "Clear"]
136
148
  })]
137
149
  }),
138
- /* @__PURE__ */ m("p", {
150
+ /* @__PURE__ */ h("p", {
139
151
  className: "text-xs text-text-secondary",
140
152
  children: "Click the map to drop a pin, type coordinates, or use your device location. Optional — it powers map search and area insights."
141
153
  })
@@ -143,6 +155,6 @@ var b = ({ value: t, onChange: n, className: u }) => {
143
155
  });
144
156
  };
145
157
  //#endregion
146
- export { b as CoordinatePicker };
158
+ export { S as CoordinatePicker };
147
159
 
148
160
  //# sourceMappingURL=CoordinatePicker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CoordinatePicker.js","names":[],"sources":["../../../../src/housingvista/components/maps/CoordinatePicker.tsx"],"sourcesContent":["/**\n * CoordinatePicker — click-to-pin latitude/longitude capture for the listing\n * wizard (BOFF-7795 / PROP-03). A single-marker Leaflet map: clicking drops or\n * moves a pin and reports `{ latitude, longitude }`. Manual number inputs stay\n * in sync (validated with `isRealCoord`), plus a \"Use my location\"\n * (geolocation) and \"Clear\". Optional — improves map search and area insights.\n * @module housingvista\n */\n\nimport { useEffect, useState } from 'react';\nimport { MapContainer, Marker, TileLayer, useMap, useMapEvents } from 'react-leaflet';\nimport { Crosshair, Trash2 } from 'lucide-react';\nimport { createPropertyPinIcon, isRealCoord, OSM_ATTRIBUTION, OSM_TILE_URL } from './leaflet-setup';\nimport { cn } from '../../utils/helpers';\n\nexport interface PickerCoordinates {\n latitude: number;\n longitude: number;\n}\n\n// India centroid — HousingVista is India-first, so an unset pin starts here.\nconst DEFAULT_CENTER: [number, number] = [20.5937, 78.9629];\n\n/** SSR/lazy safety: only mount Leaflet (which touches the DOM) after mount. */\nfunction useIsMounted(): boolean {\n const [mounted, setMounted] = useState(false);\n useEffect(() => setMounted(true), []);\n return mounted;\n}\n\ninterface ClickCaptureProps {\n onSelect: (coords: PickerCoordinates) => void;\n}\n\n/** Reports a map click back up as a chosen coordinate. */\nfunction ClickCapture({ onSelect }: ClickCaptureProps) {\n useMapEvents({\n click: (event) =>\n onSelect({\n latitude: Number(event.latlng.lat.toFixed(6)),\n longitude: Number(event.latlng.lng.toFixed(6)),\n }),\n });\n return null;\n}\n\ninterface RecenterMapProps {\n latitude: number;\n longitude: number;\n zoom: number;\n}\n\n/**\n * react-leaflet treats `MapContainer` center/zoom as initialization-only, so\n * this re-centres the map whenever the chosen coordinates change (e.g. after\n * manual entry or \"use my location\") — otherwise a pin set off-screen is never\n * revealed (BOFF-7797 Codex P2).\n */\nfunction RecenterMap({ latitude, longitude, zoom }: RecenterMapProps) {\n const map = useMap();\n useEffect(() => {\n map.setView([latitude, longitude], zoom);\n }, [map, latitude, longitude, zoom]);\n return null;\n}\n\nexport interface CoordinatePickerProps {\n value: PickerCoordinates | null;\n onChange: (value: PickerCoordinates | null) => void;\n className?: string;\n}\n\nexport const CoordinatePicker = ({ value, onChange, className }: CoordinatePickerProps) => {\n const mounted = useIsMounted();\n const [manual, setManual] = useState<{ latitude: string; longitude: string }>({\n latitude: value ? String(value.latitude) : '',\n longitude: value ? String(value.longitude) : '',\n });\n\n const commit = (coords: PickerCoordinates | null) => {\n onChange(coords);\n setManual(\n coords\n ? { latitude: String(coords.latitude), longitude: String(coords.longitude) }\n : { latitude: '', longitude: '' }\n );\n };\n\n const handlePin = (coords: PickerCoordinates) => commit(coords);\n\n const handleManual = (next: { latitude: string; longitude: string }) => {\n setManual(next);\n const lat = Number(next.latitude);\n const lng = Number(next.longitude);\n const valid = next.latitude.trim() !== '' && next.longitude.trim() !== '' && isRealCoord(lat, lng);\n // Only ever hand a valid pair up; clear stale coordinates the moment the\n // visible inputs stop representing one, so submit can't send old values.\n onChange(valid ? { latitude: lat, longitude: lng } : null);\n };\n\n const useMyLocation = () => {\n if (!navigator.geolocation) return;\n navigator.geolocation.getCurrentPosition(\n (pos) =>\n commit({\n latitude: Number(pos.coords.latitude.toFixed(6)),\n longitude: Number(pos.coords.longitude.toFixed(6)),\n }),\n () => {\n // Permission denied / unavailable — leave the field for manual entry.\n }\n );\n };\n\n const center: [number, number] = value ? [value.latitude, value.longitude] : DEFAULT_CENTER;\n const inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n return (\n <div className={cn('space-y-2', className)}>\n <div className=\"h-64 w-full overflow-hidden rounded-md border border-border-subtle\">\n {mounted ? (\n <MapContainer\n center={center}\n zoom={value ? 13 : 4}\n scrollWheelZoom\n style={{ height: '100%', width: '100%' }}\n className=\"z-0\"\n >\n <TileLayer url={OSM_TILE_URL} attribution={OSM_ATTRIBUTION} />\n <ClickCapture onSelect={handlePin} />\n {value && <RecenterMap latitude={value.latitude} longitude={value.longitude} zoom={13} />}\n {value && <Marker position={[value.latitude, value.longitude]} icon={createPropertyPinIcon('Pin')} />}\n </MapContainer>\n ) : (\n <div className=\"h-full w-full bg-bg-sunken\" />\n )}\n </div>\n\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"latitude\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Latitude\n </label>\n <input\n id=\"latitude\"\n name=\"latitude\"\n inputMode=\"decimal\"\n value={manual.latitude}\n onChange={(event) => handleManual({ latitude: event.target.value, longitude: manual.longitude })}\n placeholder=\"e.g., 12.9716\"\n className={inputClass}\n />\n </div>\n <div>\n <label htmlFor=\"longitude\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Longitude\n </label>\n <input\n id=\"longitude\"\n name=\"longitude\"\n inputMode=\"decimal\"\n value={manual.longitude}\n onChange={(event) => handleManual({ latitude: manual.latitude, longitude: event.target.value })}\n placeholder=\"e.g., 77.5946\"\n className={inputClass}\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={useMyLocation}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Crosshair className=\"h-4 w-4\" />\n Use my location\n </button>\n {value && (\n <button\n type=\"button\"\n onClick={() => commit(null)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-secondary transition-colors hover:bg-bg-sunken\"\n >\n <Trash2 className=\"h-4 w-4\" />\n Clear\n </button>\n )}\n </div>\n\n <p className=\"text-xs text-text-secondary\">\n Click the map to drop a pin, type coordinates, or use your device location. Optional — it powers map search and\n area insights.\n </p>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CoordinatePicker.js","names":[],"sources":["../../../../src/housingvista/components/maps/CoordinatePicker.tsx"],"sourcesContent":["/**\n * CoordinatePicker — click-to-pin latitude/longitude capture for the listing\n * wizard (BOFF-7795 / PROP-03). A single-marker Leaflet map: clicking drops or\n * moves a pin and reports `{ latitude, longitude }`. Manual number inputs stay\n * in sync (validated with `isRealCoord`), plus a \"Use my location\"\n * (geolocation) and \"Clear\". Optional — improves map search and area insights.\n * @module housingvista\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport { MapContainer, Marker, TileLayer, useMap, useMapEvents } from 'react-leaflet';\nimport { Crosshair, Trash2 } from 'lucide-react';\nimport { createPropertyPinIcon, isRealCoord, OSM_ATTRIBUTION, OSM_TILE_URL } from './leaflet-setup';\nimport { cn } from '../../utils/helpers';\n\nexport interface PickerCoordinates {\n latitude: number;\n longitude: number;\n}\n\n// India centroid — HousingVista is India-first, so an unset pin starts here.\nconst DEFAULT_CENTER: [number, number] = [20.5937, 78.9629];\n\n/** SSR/lazy safety: only mount Leaflet (which touches the DOM) after mount. */\nfunction useIsMounted(): boolean {\n const [mounted, setMounted] = useState(false);\n useEffect(() => setMounted(true), []);\n return mounted;\n}\n\ninterface ClickCaptureProps {\n onSelect: (coords: PickerCoordinates) => void;\n}\n\n/** Reports a map click back up as a chosen coordinate. */\nfunction ClickCapture({ onSelect }: ClickCaptureProps) {\n useMapEvents({\n click: (event) =>\n onSelect({\n latitude: Number(event.latlng.lat.toFixed(6)),\n longitude: Number(event.latlng.lng.toFixed(6)),\n }),\n });\n return null;\n}\n\ninterface RecenterMapProps {\n latitude: number;\n longitude: number;\n zoom: number;\n}\n\n/**\n * react-leaflet treats `MapContainer` center/zoom as initialization-only, so\n * this re-centres the map whenever the chosen coordinates change (e.g. after\n * manual entry or \"use my location\") — otherwise a pin set off-screen is never\n * revealed (BOFF-7797 Codex P2).\n */\nfunction RecenterMap({ latitude, longitude, zoom }: RecenterMapProps) {\n const map = useMap();\n useEffect(() => {\n map.setView([latitude, longitude], zoom);\n }, [map, latitude, longitude, zoom]);\n return null;\n}\n\n/**\n * Leaflet measures and anchors its container once at init. Inside the listing\n * wizard the Location step is still animating/laying out at that moment, so the\n * map caches a stale size AND pixel origin — tiles end up scattered in a narrow\n * strip instead of filling the card (BOFF-7807). `invalidateSize()` alone is not\n * enough: when the container width is unchanged Leaflet early-returns and never\n * re-anchors the pane. So on mount we re-measure AND force a re-anchor with\n * `setView`, then keep the size correct on later container resizes.\n */\nfunction AutoSize({ center }: { center: [number, number] }) {\n const map = useMap();\n const centerRef = useRef(center);\n centerRef.current = center;\n useEffect(() => {\n const container = map.getContainer();\n const relayout = () => {\n map.invalidateSize();\n map.setView(centerRef.current, map.getZoom(), { animate: false });\n };\n const onResize = () => map.invalidateSize();\n const raf = requestAnimationFrame(relayout);\n const timer = window.setTimeout(relayout, 250);\n const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(onResize) : null;\n observer?.observe(container);\n window.addEventListener('resize', onResize);\n return () => {\n cancelAnimationFrame(raf);\n window.clearTimeout(timer);\n observer?.disconnect();\n window.removeEventListener('resize', onResize);\n };\n }, [map]);\n return null;\n}\n\nexport interface CoordinatePickerProps {\n value: PickerCoordinates | null;\n onChange: (value: PickerCoordinates | null) => void;\n className?: string;\n}\n\nexport const CoordinatePicker = ({ value, onChange, className }: CoordinatePickerProps) => {\n const mounted = useIsMounted();\n const [manual, setManual] = useState<{ latitude: string; longitude: string }>({\n latitude: value ? String(value.latitude) : '',\n longitude: value ? String(value.longitude) : '',\n });\n\n const commit = (coords: PickerCoordinates | null) => {\n onChange(coords);\n setManual(\n coords\n ? { latitude: String(coords.latitude), longitude: String(coords.longitude) }\n : { latitude: '', longitude: '' }\n );\n };\n\n const handlePin = (coords: PickerCoordinates) => commit(coords);\n\n const handleManual = (next: { latitude: string; longitude: string }) => {\n setManual(next);\n const lat = Number(next.latitude);\n const lng = Number(next.longitude);\n const valid = next.latitude.trim() !== '' && next.longitude.trim() !== '' && isRealCoord(lat, lng);\n // Only ever hand a valid pair up; clear stale coordinates the moment the\n // visible inputs stop representing one, so submit can't send old values.\n onChange(valid ? { latitude: lat, longitude: lng } : null);\n };\n\n const useMyLocation = () => {\n if (!navigator.geolocation) return;\n navigator.geolocation.getCurrentPosition(\n (pos) =>\n commit({\n latitude: Number(pos.coords.latitude.toFixed(6)),\n longitude: Number(pos.coords.longitude.toFixed(6)),\n }),\n () => {\n // Permission denied / unavailable — leave the field for manual entry.\n }\n );\n };\n\n const center: [number, number] = value ? [value.latitude, value.longitude] : DEFAULT_CENTER;\n const inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n return (\n <div className={cn('space-y-2', className)}>\n <div className=\"h-64 w-full overflow-hidden rounded-md border border-border-subtle\">\n {mounted ? (\n <MapContainer\n center={center}\n zoom={value ? 13 : 4}\n scrollWheelZoom\n style={{ height: '100%', width: '100%' }}\n className=\"z-0\"\n >\n <TileLayer url={OSM_TILE_URL} attribution={OSM_ATTRIBUTION} />\n <AutoSize center={center} />\n <ClickCapture onSelect={handlePin} />\n {value && <RecenterMap latitude={value.latitude} longitude={value.longitude} zoom={13} />}\n {value && <Marker position={[value.latitude, value.longitude]} icon={createPropertyPinIcon('Pin')} />}\n </MapContainer>\n ) : (\n <div className=\"h-full w-full bg-bg-sunken\" />\n )}\n </div>\n\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"latitude\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Latitude\n </label>\n <input\n id=\"latitude\"\n name=\"latitude\"\n inputMode=\"decimal\"\n value={manual.latitude}\n onChange={(event) => handleManual({ latitude: event.target.value, longitude: manual.longitude })}\n placeholder=\"e.g., 12.9716\"\n className={inputClass}\n />\n </div>\n <div>\n <label htmlFor=\"longitude\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Longitude\n </label>\n <input\n id=\"longitude\"\n name=\"longitude\"\n inputMode=\"decimal\"\n value={manual.longitude}\n onChange={(event) => handleManual({ latitude: manual.latitude, longitude: event.target.value })}\n placeholder=\"e.g., 77.5946\"\n className={inputClass}\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={useMyLocation}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Crosshair className=\"h-4 w-4\" />\n Use my location\n </button>\n {value && (\n <button\n type=\"button\"\n onClick={() => commit(null)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-secondary transition-colors hover:bg-bg-sunken\"\n >\n <Trash2 className=\"h-4 w-4\" />\n Clear\n </button>\n )}\n </div>\n\n <p className=\"text-xs text-text-secondary\">\n Click the map to drop a pin, type coordinates, or use your device location. Optional — it powers map search and\n area insights.\n </p>\n </div>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-housingvista",
3
- "version": "2026.923.1",
3
+ "version": "2026.924.1",
4
4
  "description": "Burdenoff HousingVista Microfrontend - Comprehensive real estate platform for property management, investment, and transactions",
5
5
  "type": "module",
6
6
  "files": [