tempest-react-sdk 0.15.0 → 0.16.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/README.md +3 -1
- package/dist/br-geo-Q_VQpVMX.js +8 -0
- package/dist/br-geo-Q_VQpVMX.js.map +1 -0
- package/dist/br-geo-rADqOFQL.cjs +2 -0
- package/dist/br-geo-rADqOFQL.cjs.map +1 -0
- package/dist/br.cjs +2 -0
- package/dist/br.cjs.map +1 -0
- package/dist/br.d.ts +182 -0
- package/dist/br.js +311 -0
- package/dist/br.js.map +1 -0
- package/dist/projection-C2VXUKZJ.js +124 -0
- package/dist/projection-C2VXUKZJ.js.map +1 -0
- package/dist/projection-CkMNK7HA.cjs +2 -0
- package/dist/projection-CkMNK7HA.cjs.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +3 -3
- package/dist/tempest-react-sdk.cjs.map +1 -1
- package/dist/tempest-react-sdk.js +2643 -2749
- package/dist/tempest-react-sdk.js.map +1 -1
- package/package.json +6 -1
package/dist/br.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"br.js","sources":["../src/br/locations.ts","../src/br/BrazilMap.tsx","../src/br/BrazilStateCitySelect.tsx","../src/br/index.ts"],"sourcesContent":["import rawLocations from \"./data/br-locations.json\";\n\n/** The 27 Brazilian federative units (26 states + Federal District). */\nexport type UF =\n | \"AC\"\n | \"AL\"\n | \"AP\"\n | \"AM\"\n | \"BA\"\n | \"CE\"\n | \"DF\"\n | \"ES\"\n | \"GO\"\n | \"MA\"\n | \"MT\"\n | \"MS\"\n | \"MG\"\n | \"PA\"\n | \"PB\"\n | \"PR\"\n | \"PE\"\n | \"PI\"\n | \"RJ\"\n | \"RN\"\n | \"RS\"\n | \"RO\"\n | \"RR\"\n | \"SC\"\n | \"SP\"\n | \"SE\"\n | \"TO\";\n\n/** The five Brazilian macro-regions (IBGE). */\nexport type BrRegion = \"Norte\" | \"Nordeste\" | \"Centro-Oeste\" | \"Sudeste\" | \"Sul\";\n\n/** A federative unit with its display name and city list. */\nexport interface BrazilState {\n /** Two-letter acronym, e.g. `\"SP\"`. */\n uf: UF;\n /** Full name, e.g. `\"São Paulo\"`. */\n name: string;\n /** Macro-region the state belongs to. */\n region: BrRegion;\n /** City names within the state, alphabetically. */\n cities: string[];\n}\n\n/** A `{ value, label }` option, handy for `<Select>` / `<Combobox>`. */\nexport interface Choice {\n value: string;\n label: string;\n}\n\n/** IBGE macro-region of each federative unit. */\nconst REGION_BY_UF: Record<UF, BrRegion> = {\n AC: \"Norte\",\n AP: \"Norte\",\n AM: \"Norte\",\n PA: \"Norte\",\n RO: \"Norte\",\n RR: \"Norte\",\n TO: \"Norte\",\n AL: \"Nordeste\",\n BA: \"Nordeste\",\n CE: \"Nordeste\",\n MA: \"Nordeste\",\n PB: \"Nordeste\",\n PE: \"Nordeste\",\n PI: \"Nordeste\",\n RN: \"Nordeste\",\n SE: \"Nordeste\",\n DF: \"Centro-Oeste\",\n GO: \"Centro-Oeste\",\n MT: \"Centro-Oeste\",\n MS: \"Centro-Oeste\",\n ES: \"Sudeste\",\n MG: \"Sudeste\",\n RJ: \"Sudeste\",\n SP: \"Sudeste\",\n PR: \"Sul\",\n RS: \"Sul\",\n SC: \"Sul\",\n};\n\ninterface RawState {\n sigla: string;\n estado: string;\n cidades: string[];\n}\n\nconst RAW = rawLocations as unknown as { dataLocals: RawState[] };\n\n/** Normalized, frozen list of all 27 states (built once at module load). */\nconst STATES: readonly BrazilState[] = RAW.dataLocals\n .map((entry) => {\n const uf = entry.sigla as UF;\n return {\n uf,\n name: entry.estado,\n region: REGION_BY_UF[uf],\n cities: entry.cidades,\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name, \"pt-BR\"));\n\nconst STATE_BY_UF = new Map<UF, BrazilState>(STATES.map((s) => [s.uf, s]));\n\n/** All 27 federative units, sorted by name. */\nexport function listStates(): readonly BrazilState[] {\n return STATES;\n}\n\n/** Look up a single state by acronym (case-insensitive). Returns `null` if unknown. */\nexport function getState(uf: string): BrazilState | null {\n const normalized = normalizeUf(uf);\n return normalized ? (STATE_BY_UF.get(normalized) ?? null) : null;\n}\n\n/**\n * City names for a federative unit (case-insensitive acronym). Returns an empty\n * array for an unknown UF — \"no matches\" is a valid result, not an error.\n */\nexport function citiesByUf(uf: string): string[] {\n return getState(uf)?.cities ?? [];\n}\n\n/** States belonging to a macro-region. */\nexport function statesByRegion(region: BrRegion): readonly BrazilState[] {\n return STATES.filter((s) => s.region === region);\n}\n\n/** True when `value` is one of the 27 valid acronyms (case-insensitive). */\nexport function isValidUf(value: string): boolean {\n return normalizeUf(value) !== null;\n}\n\n/**\n * Normalize an acronym to canonical uppercase form, or `null` if it is not a\n * valid UF. `\"sp\"` → `\"SP\"`, `\"xx\"` → `null`.\n */\nexport function normalizeUf(value: string): UF | null {\n const upper = value.trim().toUpperCase();\n return (REGION_BY_UF as Record<string, BrRegion>)[upper] ? (upper as UF) : null;\n}\n\n/** True when `city` exists within `uf` (both case-insensitive). */\nexport function isValidCity(uf: string, city: string): boolean {\n const target = city.trim().toLowerCase();\n return citiesByUf(uf).some((c) => c.toLowerCase() === target);\n}\n\n/** `{ value: uf, label: name }` options for every state, for a `<Select>`. */\nexport function ufChoices(): Choice[] {\n return STATES.map((s) => ({ value: s.uf, label: s.name }));\n}\n\n/** `{ value, label }` options for every city in a UF (value === label). */\nexport function cityChoices(uf: string): Choice[] {\n return citiesByUf(uf).map((c) => ({ value: c, label: c }));\n}\n","import { useEffect, useMemo, useRef, useState, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { fitProjection, type FittedProjection } from \"@/geo/projection\";\nimport type { GeoBounds } from \"@/geo/types\";\nimport type { BrUfFeature, BrUfFeatureCollection, Ring } from \"./br-geo\";\nimport type { UF } from \"./locations\";\nimport styles from \"./BrazilMap.module.css\";\n\nexport interface BrazilMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n /** Currently selected UF(s) — highlighted. Accepts one or many. */\n selected?: UF | readonly UF[] | null;\n /** Fired when a state is clicked. */\n onSelect?: (uf: UF) => void;\n /**\n * Optional choropleth values per UF. When set, each state is tinted between\n * `minColor` and `maxColor` by its value (linear). States without a value\n * use the base surface color.\n */\n values?: Partial<Record<UF, number>>;\n /** Choropleth low-end color. Default: a light primary tint. */\n minColor?: string;\n /** Choropleth high-end color. Default: the primary token. */\n maxColor?: string;\n /** Viewport height in pixels. Default: `440`. */\n height?: number;\n /** Inner padding in pixels. Default: `12`. */\n padding?: number;\n /** Render the UF acronym at each state centroid. Default: `true`. */\n showLabels?: boolean;\n /** Accessible label for the map region. Default: `\"Mapa do Brasil por estado\"`. */\n label?: string;\n /** Custom content when the geometry is still loading. */\n loadingContent?: ReactNode;\n}\n\n/** Iterate every ring of a feature (Polygon or MultiPolygon). */\nfunction ringsOf(feature: BrUfFeature): Ring[] {\n const { type, coordinates } = feature.geometry;\n return type === \"MultiPolygon\" ? (coordinates as Ring[][]).flat() : (coordinates as Ring[]);\n}\n\n/** Bounding box across every coordinate in the collection. */\nfunction collectionBounds(collection: BrUfFeatureCollection): GeoBounds {\n let minLat = 90;\n let maxLat = -90;\n let minLon = 180;\n let maxLon = -180;\n for (const feature of collection.features) {\n for (const ring of ringsOf(feature)) {\n for (const [lon, lat] of ring) {\n if (lat < minLat) minLat = lat;\n if (lat > maxLat) maxLat = lat;\n if (lon < minLon) minLon = lon;\n if (lon > maxLon) maxLon = lon;\n }\n }\n }\n return { minLatitude: minLat, maxLatitude: maxLat, minLongitude: minLon, maxLongitude: maxLon };\n}\n\n/** Build the SVG `d` path for a feature (all rings, evenodd for holes). */\nfunction featurePath(feature: BrUfFeature, projection: FittedProjection): string {\n return ringsOf(feature)\n .map((ring) => {\n const pts = ring.map(([lon, lat]) => {\n const p = projection.project({ latitude: lat, longitude: lon });\n return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;\n });\n return `M${pts.join(\"L\")}Z`;\n })\n .join(\"\");\n}\n\n/** Rough visual centroid of a feature (average of its outer-ring points). */\nfunction featureCentroid(\n feature: BrUfFeature,\n projection: FittedProjection,\n): { x: number; y: number } {\n const rings = ringsOf(feature);\n const outer = rings.reduce((a, b) => (b.length > a.length ? b : a), rings[0] ?? []);\n let sx = 0;\n let sy = 0;\n for (const [lon, lat] of outer) {\n const p = projection.project({ latitude: lat, longitude: lon });\n sx += p.x;\n sy += p.y;\n }\n const n = outer.length || 1;\n return { x: sx / n, y: sy / n };\n}\n\nfunction lerpColor(from: string, to: string, t: number): string {\n const parse = (c: string): [number, number, number] => {\n const hex = c.replace(\"#\", \"\");\n const full =\n hex.length === 3\n ? hex\n .split(\"\")\n .map((ch) => ch + ch)\n .join(\"\")\n : hex;\n return [\n parseInt(full.slice(0, 2), 16),\n parseInt(full.slice(2, 4), 16),\n parseInt(full.slice(4, 6), 16),\n ];\n };\n const [r1, g1, b1] = parse(from);\n const [r2, g2, b2] = parse(to);\n const mix = (a: number, b: number): number => Math.round(a + (b - a) * t);\n return `rgb(${mix(r1, r2)}, ${mix(g1, g2)}, ${mix(b1, b2)})`;\n}\n\n/**\n * Clickable choropleth map of Brazil's 27 federative units. Renders the bundled\n * simplified UF GeoJSON as SVG paths — **no external tiles or paid API**. Click\n * a state to fire `onSelect(uf)`; pass `selected` to highlight and `values` to\n * tint states by a metric.\n *\n * The GeoJSON (~36 KB gzip) is loaded lazily, so importing this component does\n * not pull the geometry until it actually mounts.\n *\n * @example\n * const [uf, setUf] = useState<UF | null>(null);\n * <BrazilMap selected={uf} onSelect={setUf} />\n *\n * @example\n * // Choropleth by a metric per state\n * <BrazilMap values={{ SP: 120, RJ: 90, MG: 60 }} />\n */\nexport function BrazilMap({\n selected,\n onSelect,\n values,\n minColor = \"#dbeafe\",\n maxColor = \"#2563eb\",\n height = 440,\n padding = 12,\n showLabels = true,\n label = \"Mapa do Brasil por estado\",\n loadingContent,\n className,\n style,\n ...rest\n}: BrazilMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n const [collection, setCollection] = useState<BrUfFeatureCollection | null>(null);\n\n useEffect(() => {\n let active = true;\n void import(\"./br-geo\").then((m) => {\n if (active) setCollection(m.BR_UF_GEOJSON);\n });\n return () => {\n active = false;\n };\n }, []);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const selectedSet = useMemo<Set<UF>>(() => {\n if (!selected) return new Set();\n return new Set(Array.isArray(selected) ? selected : [selected as UF]);\n }, [selected]);\n\n const valueRange = useMemo<[number, number] | null>(() => {\n if (!values) return null;\n const nums = Object.values(values).filter((v): v is number => typeof v === \"number\");\n if (nums.length === 0) return null;\n return [Math.min(...nums), Math.max(...nums)];\n }, [values]);\n\n const shapes = useMemo(() => {\n if (!collection) return null;\n const projection = fitProjection(collectionBounds(collection), width, height, { padding });\n return collection.features.map((feature) => ({\n uf: feature.properties.uf,\n name: feature.properties.name,\n d: featurePath(feature, projection),\n centroid: featureCentroid(feature, projection),\n }));\n }, [collection, width, height, padding]);\n\n function fillFor(uf: UF): string | undefined {\n if (!values || !valueRange) return undefined;\n const v = values[uf];\n if (typeof v !== \"number\") return undefined;\n const [min, max] = valueRange;\n const t = max === min ? 1 : (v - min) / (max - min);\n return lerpColor(minColor, maxColor, t);\n }\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"group\"\n aria-label={label}\n {...rest}\n >\n {!shapes ? (\n <div className={styles.loading}>{loadingContent ?? \"Carregando mapa…\"}</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {shapes.map((shape) => {\n const isSelected = selectedSet.has(shape.uf);\n return (\n <path\n key={shape.uf}\n className={cn(styles.state, isSelected && styles.selected)}\n d={shape.d}\n fillRule=\"evenodd\"\n fill={isSelected ? undefined : fillFor(shape.uf)}\n data-uf={shape.uf}\n tabIndex={onSelect ? 0 : undefined}\n role={onSelect ? \"button\" : undefined}\n aria-label={shape.name}\n aria-pressed={onSelect ? isSelected : undefined}\n onClick={onSelect ? () => onSelect(shape.uf) : undefined}\n onKeyDown={\n onSelect\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onSelect(shape.uf);\n }\n }\n : undefined\n }\n >\n <title>{shape.name}</title>\n </path>\n );\n })}\n\n {showLabels &&\n shapes.map((shape) => (\n <text\n key={`label-${shape.uf}`}\n className={styles.label}\n x={shape.centroid.x}\n y={shape.centroid.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n >\n {shape.uf}\n </text>\n ))}\n </svg>\n )}\n </div>\n );\n}\n","import { useMemo, useState, type ReactElement } from \"react\";\nimport { Select } from \"@/components/Select\";\nimport { cityChoices, listStates, normalizeUf, type UF } from \"./locations\";\n\n/** Current selection emitted by {@link BrazilStateCitySelect}. */\nexport interface BrazilStateCitySelection {\n /** Selected federative unit, or `null` when none. */\n uf: UF | null;\n /** Selected city, or `null` when none. */\n city: string | null;\n}\n\nexport interface BrazilStateCitySelectProps {\n /** Pre-selected UF (uncontrolled initial value). */\n defaultUf?: UF;\n /** Pre-selected city (uncontrolled initial value). */\n defaultCity?: string;\n /** Fired whenever the state or city changes. */\n onChange?: (selection: BrazilStateCitySelection) => void;\n /** Label for the state select. Default: `\"Estado\"`. */\n stateLabel?: string;\n /** Label for the city select. Default: `\"Cidade\"`. */\n cityLabel?: string;\n /** Placeholder for the state select. Default: `\"Selecione o estado\"`. */\n statePlaceholder?: string;\n /** Placeholder for the city select. Default: `\"Selecione a cidade\"`. */\n cityPlaceholder?: string;\n /** Disable both selects. */\n disabled?: boolean;\n /** Layout of the two selects. Default: `\"row\"`. */\n layout?: \"row\" | \"column\";\n}\n\n/**\n * Cascading Estado → Cidade selector backed by the bundled BR locations data —\n * pick a state and the city list narrows to that UF's municipalities. No\n * network, no external API. Complements {@link BrazilMap} (wire the map's\n * `onSelect` to drive the same UF).\n *\n * @example\n * <BrazilStateCitySelect onChange={({ uf, city }) => console.log(uf, city)} />\n */\nexport function BrazilStateCitySelect({\n defaultUf,\n defaultCity,\n onChange,\n stateLabel = \"Estado\",\n cityLabel = \"Cidade\",\n statePlaceholder = \"Selecione o estado\",\n cityPlaceholder = \"Selecione a cidade\",\n disabled = false,\n layout = \"row\",\n}: BrazilStateCitySelectProps): ReactElement {\n const [uf, setUf] = useState<UF | null>(defaultUf ?? null);\n const [city, setCity] = useState<string | null>(defaultCity ?? null);\n\n const stateOptions = useMemo(\n () => listStates().map((s) => ({ value: s.uf, label: s.name })),\n [],\n );\n const cityOptions = useMemo(() => (uf ? cityChoices(uf) : []), [uf]);\n\n function handleUf(next: string): void {\n const nextUf = normalizeUf(next);\n setUf(nextUf);\n setCity(null);\n onChange?.({ uf: nextUf, city: null });\n }\n\n function handleCity(next: string): void {\n const nextCity = next || null;\n setCity(nextCity);\n onChange?.({ uf, city: nextCity });\n }\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: layout === \"row\" ? \"row\" : \"column\",\n gap: \"var(--tempest-space-3, 12px)\",\n flexWrap: \"wrap\",\n }}\n >\n <Select\n label={stateLabel}\n placeholder={statePlaceholder}\n options={stateOptions}\n value={uf ?? \"\"}\n disabled={disabled}\n onChange={(e) => handleUf(e.target.value)}\n style={{ minWidth: 0 }}\n />\n <Select\n label={cityLabel}\n placeholder={cityPlaceholder}\n options={cityOptions}\n value={city ?? \"\"}\n disabled={disabled || !uf}\n onChange={(e) => handleCity(e.target.value)}\n style={{ minWidth: 0 }}\n />\n </div>\n );\n}\n","// Brazilian locations data (states + cities) — mirrors the `utils/locations`\n// module of tempest-fastapi-sdk.\nexport {\n listStates,\n getState,\n citiesByUf,\n statesByRegion,\n isValidUf,\n normalizeUf,\n isValidCity,\n ufChoices,\n cityChoices,\n} from \"./locations\";\nexport type { UF, BrRegion, BrazilState, Choice } from \"./locations\";\n\n// UF GeoJSON types (the geometry itself is lazy-loaded by BrazilMap).\nimport type { BrUfFeatureCollection } from \"./br-geo\";\nexport type { BrUfFeature, BrUfFeatureCollection, BrUfGeometry, Ring } from \"./br-geo\";\n\n/**\n * Lazily load the bundled simplified UF GeoJSON (~36 KB gzip). Kept out of the\n * synchronous barrel so a data-only import never pulls the geometry.\n */\nexport async function loadBrUfGeoJson(): Promise<BrUfFeatureCollection> {\n const mod = await import(\"./br-geo\");\n return mod.BR_UF_GEOJSON;\n}\n\n// Components\nexport { BrazilMap } from \"./BrazilMap\";\nexport type { BrazilMapProps } from \"./BrazilMap\";\nexport { BrazilStateCitySelect } from \"./BrazilStateCitySelect\";\nexport type { BrazilStateCitySelectProps, BrazilStateCitySelection } from \"./BrazilStateCitySelect\";\n"],"names":["REGION_BY_UF","RAW","rawLocations","STATES","entry","uf","b","STATE_BY_UF","s","listStates","getState","normalized","normalizeUf","citiesByUf","statesByRegion","region","isValidUf","value","upper","isValidCity","city","target","c","ufChoices","cityChoices","ringsOf","feature","type","coordinates","collectionBounds","collection","minLat","maxLat","minLon","maxLon","ring","lon","lat","featurePath","projection","p","featureCentroid","rings","outer","a","sx","sy","n","lerpColor","from","to","t","parse","hex","full","ch","r1","g1","b1","r2","g2","b2","mix","BrazilMap","selected","onSelect","values","minColor","maxColor","height","padding","showLabels","label","loadingContent","className","style","rest","containerRef","useRef","width","setWidth","useState","setCollection","useEffect","active","m","node","observer","entries","measured","selectedSet","useMemo","valueRange","nums","v","shapes","fitProjection","fillFor","min","max","jsx","cn","styles","jsxs","shape","isSelected","e","BrazilStateCitySelect","defaultUf","defaultCity","onChange","stateLabel","cityLabel","statePlaceholder","cityPlaceholder","disabled","layout","setUf","setCity","stateOptions","cityOptions","handleUf","next","nextUf","handleCity","nextCity","Select","loadBrUfGeoJson"],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsxs as f, jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as y, useId as j } from "react";
|
|
3
|
+
import { c as b } from "./cn-C0Sxc9eb.js";
|
|
4
|
+
const I = "tempest_wrapper_KS2K3", w = "tempest_label_Lmgos", P = "tempest_required_PvDVJ", S = "tempest_field_h-wBy", T = "tempest_select_cjdcr", C = "tempest_caret_MdCao", R = "tempest_error_sw9MU", k = "tempest_helper_frosK", v = "tempest_errorText_-zd6i", a = {
|
|
5
|
+
wrapper: I,
|
|
6
|
+
label: w,
|
|
7
|
+
required: P,
|
|
8
|
+
field: S,
|
|
9
|
+
select: T,
|
|
10
|
+
caret: C,
|
|
11
|
+
error: R,
|
|
12
|
+
helper: k,
|
|
13
|
+
errorText: v
|
|
14
|
+
}, X = y(function({
|
|
15
|
+
label: e,
|
|
16
|
+
helperText: n,
|
|
17
|
+
error: r,
|
|
18
|
+
options: i,
|
|
19
|
+
placeholder: o,
|
|
20
|
+
wrapperClassName: u,
|
|
21
|
+
className: m,
|
|
22
|
+
children: p,
|
|
23
|
+
id: h,
|
|
24
|
+
required: d,
|
|
25
|
+
...c
|
|
26
|
+
}, M) {
|
|
27
|
+
const x = j(), _ = h ?? x;
|
|
28
|
+
return /* @__PURE__ */ f("div", { className: b(a.wrapper, r && a.error, u), children: [
|
|
29
|
+
e && /* @__PURE__ */ f("label", { htmlFor: _, className: a.label, children: [
|
|
30
|
+
e,
|
|
31
|
+
d && /* @__PURE__ */ s("span", { className: a.required, children: "*" })
|
|
32
|
+
] }),
|
|
33
|
+
/* @__PURE__ */ f("div", { className: a.field, children: [
|
|
34
|
+
/* @__PURE__ */ f(
|
|
35
|
+
"select",
|
|
36
|
+
{
|
|
37
|
+
ref: M,
|
|
38
|
+
id: _,
|
|
39
|
+
"aria-invalid": !!r,
|
|
40
|
+
required: d,
|
|
41
|
+
className: b(a.select, m),
|
|
42
|
+
...c,
|
|
43
|
+
children: [
|
|
44
|
+
o && /* @__PURE__ */ s("option", { value: "", disabled: !0, hidden: !0, children: o }),
|
|
45
|
+
i?.map((l) => /* @__PURE__ */ s("option", { value: l.value, disabled: l.disabled, children: l.label }, l.value)),
|
|
46
|
+
p
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
),
|
|
50
|
+
/* @__PURE__ */ s("span", { className: a.caret, "aria-hidden": !0, children: /* @__PURE__ */ s(E, {}) })
|
|
51
|
+
] }),
|
|
52
|
+
r ? /* @__PURE__ */ s("span", { className: a.errorText, children: r }) : n ? /* @__PURE__ */ s("span", { className: a.helper, children: n }) : null
|
|
53
|
+
] });
|
|
54
|
+
});
|
|
55
|
+
function E() {
|
|
56
|
+
return /* @__PURE__ */ s("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ s(
|
|
57
|
+
"path",
|
|
58
|
+
{
|
|
59
|
+
d: "M6 9l6 6 6-6",
|
|
60
|
+
stroke: "currentColor",
|
|
61
|
+
strokeWidth: "2",
|
|
62
|
+
strokeLinecap: "round",
|
|
63
|
+
strokeLinejoin: "round"
|
|
64
|
+
}
|
|
65
|
+
) });
|
|
66
|
+
}
|
|
67
|
+
function q(t) {
|
|
68
|
+
return Number.isFinite(t) && t >= -90 && t <= 90;
|
|
69
|
+
}
|
|
70
|
+
function A(t) {
|
|
71
|
+
return Number.isFinite(t) && t >= -180 && t <= 180;
|
|
72
|
+
}
|
|
73
|
+
function z(t) {
|
|
74
|
+
if (typeof t != "object" || t === null) return !1;
|
|
75
|
+
const e = t;
|
|
76
|
+
return typeof e.latitude == "number" && typeof e.longitude == "number" && q(e.latitude) && A(e.longitude);
|
|
77
|
+
}
|
|
78
|
+
function F(t) {
|
|
79
|
+
return Math.min(90, Math.max(-90, t));
|
|
80
|
+
}
|
|
81
|
+
function B(t) {
|
|
82
|
+
return ((t + 180) % 360 + 360) % 360 - 180;
|
|
83
|
+
}
|
|
84
|
+
const N = 85.05112878;
|
|
85
|
+
function g(t) {
|
|
86
|
+
const n = Math.max(
|
|
87
|
+
-N,
|
|
88
|
+
Math.min(N, F(t.latitude))
|
|
89
|
+
) * Math.PI / 180, r = (t.longitude + 180) / 360, i = (1 - Math.log(Math.tan(n) + 1 / Math.cos(n)) / Math.PI) / 2;
|
|
90
|
+
return { x: r, y: i };
|
|
91
|
+
}
|
|
92
|
+
function D(t) {
|
|
93
|
+
const e = t.x * 360 - 180, n = Math.PI * (1 - 2 * t.y);
|
|
94
|
+
return { latitude: Math.atan(Math.sinh(n)) * 180 / Math.PI, longitude: e };
|
|
95
|
+
}
|
|
96
|
+
function U(t, e, n, r = {}) {
|
|
97
|
+
const { padding: i = 16 } = r, o = g({
|
|
98
|
+
latitude: t.maxLatitude,
|
|
99
|
+
longitude: t.minLongitude
|
|
100
|
+
}), u = g({
|
|
101
|
+
latitude: t.minLatitude,
|
|
102
|
+
longitude: t.maxLongitude
|
|
103
|
+
}), m = u.x - o.x || Number.EPSILON, p = u.y - o.y || Number.EPSILON, h = Math.max(1, e - i * 2), d = Math.max(1, n - i * 2), c = Math.min(h / m, d / p), M = i + (h - m * c) / 2, x = i + (d - p * c) / 2;
|
|
104
|
+
return { project: (l) => {
|
|
105
|
+
const L = g(l);
|
|
106
|
+
return {
|
|
107
|
+
x: M + (L.x - o.x) * c,
|
|
108
|
+
y: x + (L.y - o.y) * c
|
|
109
|
+
};
|
|
110
|
+
}, scale: c, width: e, height: n };
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
N as M,
|
|
114
|
+
X as S,
|
|
115
|
+
q as a,
|
|
116
|
+
A as b,
|
|
117
|
+
F as c,
|
|
118
|
+
U as f,
|
|
119
|
+
z as i,
|
|
120
|
+
B as n,
|
|
121
|
+
g as p,
|
|
122
|
+
D as u
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=projection-C2VXUKZJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection-C2VXUKZJ.js","sources":["../src/components/Select/Select.tsx","../src/geo/types.ts","../src/geo/projection.ts"],"sourcesContent":["import { forwardRef, useId } from \"react\";\nimport type { SelectHTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Select.module.css\";\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {\n label?: string;\n helperText?: string;\n error?: string;\n options?: SelectOption[];\n placeholder?: string;\n wrapperClassName?: string;\n}\n\n/**\n * Native `<select>` wrapper with label/helper/error slots. Either provide\n * `options` for a quick render, or pass `<option>` children directly.\n */\nexport const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(\n {\n label,\n helperText,\n error,\n options,\n placeholder,\n wrapperClassName,\n className,\n children,\n id,\n required,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const selectId = id ?? generatedId;\n\n return (\n <div className={cn(styles.wrapper, error && styles.error, wrapperClassName)}>\n {label && (\n <label htmlFor={selectId} className={styles.label}>\n {label}\n {required && <span className={styles.required}>*</span>}\n </label>\n )}\n <div className={styles.field}>\n <select\n ref={ref}\n id={selectId}\n aria-invalid={!!error}\n required={required}\n className={cn(styles.select, className)}\n {...props}\n >\n {placeholder && (\n <option value=\"\" disabled hidden>\n {placeholder}\n </option>\n )}\n {options?.map((opt) => (\n <option key={opt.value} value={opt.value} disabled={opt.disabled}>\n {opt.label}\n </option>\n ))}\n {children}\n </select>\n <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\n </div>\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n});\n\nfunction CaretIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 9l6 6 6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n","/**\n * WGS84 geographic coordinate. Mirrors the `Coordinate` schema from\n * `tempest-fastapi-sdk` (`geo/schemas.py`) — `latitude` in `[-90, 90]`,\n * `longitude` in `[-180, 180]`, serialized snake_case on the wire.\n */\nexport interface Coordinate {\n /** WGS84 latitude in degrees, `[-90, 90]`. E.g. `-23.5505`. */\n latitude: number;\n /** WGS84 longitude in degrees, `[-180, 180]`. E.g. `-46.6333`. */\n longitude: number;\n}\n\n/**\n * A single sample in a recorded trajectory: a {@link Coordinate} stamped with\n * the epoch millisecond it was captured, plus the optional accuracy radius\n * reported by the Geolocation API.\n */\nexport interface TrackPoint extends Coordinate {\n /** Capture time in epoch milliseconds (`GeolocationPosition.timestamp`). */\n timestamp: number;\n /** Horizontal accuracy radius in meters, if the device reported one. */\n accuracy?: number;\n}\n\n/**\n * Travel mode. Mirrors the `TravelMode` string enum from `tempest-fastapi-sdk`\n * (`geo/enums.py`) — the on-the-wire value is the raw string.\n */\nexport type TravelMode = \"car\" | \"motorcycle\" | \"bus\";\n\n/**\n * Estimated travel between two coordinates. Mirrors the `TravelEstimate`\n * schema from `tempest-fastapi-sdk` (`geo/schemas.py`), snake_case preserved so\n * a response deserializes straight into this type.\n */\nexport interface TravelEstimate {\n /** Travel mode the estimate was computed for. */\n mode: TravelMode;\n /** Great-circle distance scaled by circuity, in kilometers (`>= 0`). */\n distance_km: number;\n /** Estimated duration in minutes (`>= 0`). */\n duration_minutes: number;\n /** How the estimate was produced. `\"heuristic\"` (offline) or `\"osrm\"`. */\n source: \"heuristic\" | \"osrm\";\n}\n\n/**\n * Axis-aligned geographic bounding box. `min`/`max` follow the same degree\n * ranges as {@link Coordinate}.\n */\nexport interface GeoBounds {\n minLatitude: number;\n maxLatitude: number;\n minLongitude: number;\n maxLongitude: number;\n}\n\n/** True when `value` is a finite latitude in `[-90, 90]`. */\nexport function isValidLatitude(value: number): boolean {\n return Number.isFinite(value) && value >= -90 && value <= 90;\n}\n\n/** True when `value` is a finite longitude in `[-180, 180]`. */\nexport function isValidLongitude(value: number): boolean {\n return Number.isFinite(value) && value >= -180 && value <= 180;\n}\n\n/**\n * Type guard for {@link Coordinate}: an object with finite, in-range\n * `latitude` and `longitude`.\n */\nexport function isCoordinate(value: unknown): value is Coordinate {\n if (typeof value !== \"object\" || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.latitude === \"number\" &&\n typeof candidate.longitude === \"number\" &&\n isValidLatitude(candidate.latitude) &&\n isValidLongitude(candidate.longitude)\n );\n}\n\n/** Clamp a latitude into the valid `[-90, 90]` range. */\nexport function clampLatitude(latitude: number): number {\n return Math.min(90, Math.max(-90, latitude));\n}\n\n/**\n * Normalize a longitude into the `[-180, 180]` range, wrapping values that\n * cross the antimeridian (e.g. `190` → `-170`).\n */\nexport function normalizeLongitude(longitude: number): number {\n const wrapped = ((((longitude + 180) % 360) + 360) % 360) - 180;\n return wrapped;\n}\n","import { clampLatitude } from \"./types\";\nimport type { Coordinate, GeoBounds } from \"./types\";\n\n/**\n * A point projected onto the unit Web Mercator plane. Both axes are in `[0, 1]`\n * — `x` grows east, `y` grows south (screen convention).\n */\nexport interface MercatorPoint {\n x: number;\n y: number;\n}\n\n/** A pixel coordinate inside the plotting viewport. */\nexport interface PixelPoint {\n x: number;\n y: number;\n}\n\n/**\n * Web Mercator (EPSG:3857) latitude clamp. Latitudes beyond this diverge to\n * infinity in the projection, so tile maps cap here.\n */\nexport const MERCATOR_MAX_LATITUDE = 85.05112878;\n\n/**\n * Project a geographic coordinate onto the unit Web Mercator plane. This is the\n * same projection tile servers use, so a self-hosted tile layer and the\n * tile-free SVG plot line up pixel-for-pixel.\n *\n * @param coord - Coordinate to project.\n * @returns `{ x, y }` in `[0, 1]`.\n */\nexport function projectMercator(coord: Coordinate): MercatorPoint {\n const lat = Math.max(\n -MERCATOR_MAX_LATITUDE,\n Math.min(MERCATOR_MAX_LATITUDE, clampLatitude(coord.latitude)),\n );\n const latRad = (lat * Math.PI) / 180;\n const x = (coord.longitude + 180) / 360;\n const y = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2;\n return { x, y };\n}\n\n/**\n * Inverse of {@link projectMercator}: recover a coordinate from a unit-plane\n * point.\n *\n * @param point - `{ x, y }` in `[0, 1]`.\n * @returns The geographic coordinate.\n */\nexport function unprojectMercator(point: MercatorPoint): Coordinate {\n const longitude = point.x * 360 - 180;\n const n = Math.PI * (1 - 2 * point.y);\n const latitude = (Math.atan(Math.sinh(n)) * 180) / Math.PI;\n return { latitude, longitude };\n}\n\n/** A ready-to-use mapping from coordinates to viewport pixels. */\nexport interface FittedProjection {\n /** Project a coordinate to a pixel inside the viewport. */\n project: (coord: Coordinate) => PixelPoint;\n /** Uniform scale (unit-plane → pixels) actually used, after aspect fit. */\n scale: number;\n /** Viewport width in pixels. */\n width: number;\n /** Viewport height in pixels. */\n height: number;\n}\n\n/** Options for {@link fitProjection}. */\nexport interface FitProjectionOptions {\n /** Inner padding in pixels kept clear on every edge. Default: `16`. */\n padding?: number;\n}\n\n/**\n * Build a projection that fits `bounds` into a `width × height` viewport while\n * preserving aspect ratio (uniform scale, centered). This is what powers the\n * tile-free trajectory plot: project the bounds, scale to the SVG box, keep\n * shapes undistorted.\n *\n * @param bounds - Geographic extent to fit.\n * @param width - Viewport width in pixels.\n * @param height - Viewport height in pixels.\n * @param options - Padding tuning.\n * @returns A {@link FittedProjection} with a `project(coord)` mapper.\n */\nexport function fitProjection(\n bounds: GeoBounds,\n width: number,\n height: number,\n options: FitProjectionOptions = {},\n): FittedProjection {\n const { padding = 16 } = options;\n\n const topLeft = projectMercator({\n latitude: bounds.maxLatitude,\n longitude: bounds.minLongitude,\n });\n const bottomRight = projectMercator({\n latitude: bounds.minLatitude,\n longitude: bounds.maxLongitude,\n });\n\n const spanX = bottomRight.x - topLeft.x || Number.EPSILON;\n const spanY = bottomRight.y - topLeft.y || Number.EPSILON;\n\n const innerWidth = Math.max(1, width - padding * 2);\n const innerHeight = Math.max(1, height - padding * 2);\n\n // Uniform scale keeps the trajectory undistorted; fit the tighter axis.\n const scale = Math.min(innerWidth / spanX, innerHeight / spanY);\n\n // Center the projected content within the padded box.\n const offsetX = padding + (innerWidth - spanX * scale) / 2;\n const offsetY = padding + (innerHeight - spanY * scale) / 2;\n\n const project = (coord: Coordinate): PixelPoint => {\n const projected = projectMercator(coord);\n return {\n x: offsetX + (projected.x - topLeft.x) * scale,\n y: offsetY + (projected.y - topLeft.y) * scale,\n };\n };\n\n return { project, scale, width, height };\n}\n"],"names":["Select","forwardRef","label","helperText","error","options","placeholder","wrapperClassName","className","children","id","required","props","ref","generatedId","useId","selectId","jsxs","cn","styles","jsx","opt","CaretIcon","isValidLatitude","value","isValidLongitude","isCoordinate","candidate","clampLatitude","latitude","normalizeLongitude","longitude","MERCATOR_MAX_LATITUDE","projectMercator","coord","latRad","x","y","unprojectMercator","point","fitProjection","bounds","width","height","padding","topLeft","bottomRight","spanX","spanY","innerWidth","innerHeight","scale","offsetX","offsetY","projected"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";const n=require("react/jsx-runtime"),g=require("react"),N=require("./cn-CNScIEB4.cjs"),w="tempest_wrapper_KS2K3",P="tempest_label_Lmgos",T="tempest_required_PvDVJ",R="tempest_field_h-wBy",C="tempest_select_cjdcr",q="tempest_caret_MdCao",A="tempest_error_sw9MU",E="tempest_helper_frosK",S="tempest_errorText_-zd6i",i={wrapper:w,label:P,required:T,field:R,select:C,caret:q,error:A,helper:E,errorText:S},V=g.forwardRef(function({label:e,helperText:r,error:s,options:a,placeholder:o,wrapperClassName:u,className:p,children:m,id:h,required:d,...c},f){const M=g.useId(),j=h??M;return n.jsxs("div",{className:N.cn(i.wrapper,s&&i.error,u),children:[e&&n.jsxs("label",{htmlFor:j,className:i.label,children:[e,d&&n.jsx("span",{className:i.required,children:"*"})]}),n.jsxs("div",{className:i.field,children:[n.jsxs("select",{ref:f,id:j,"aria-invalid":!!s,required:d,className:N.cn(i.select,p),...c,children:[o&&n.jsx("option",{value:"",disabled:!0,hidden:!0,children:o}),a?.map(l=>n.jsx("option",{value:l.value,disabled:l.disabled,children:l.label},l.value)),m]}),n.jsx("span",{className:i.caret,"aria-hidden":!0,children:n.jsx(k,{})})]}),s?n.jsx("span",{className:i.errorText,children:s}):r?n.jsx("span",{className:i.helper,children:r}):null]})});function k(){return n.jsx("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",children:n.jsx("path",{d:"M6 9l6 6 6-6",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function b(t){return Number.isFinite(t)&&t>=-90&&t<=90}function y(t){return Number.isFinite(t)&&t>=-180&&t<=180}function v(t){if(typeof t!="object"||t===null)return!1;const e=t;return typeof e.latitude=="number"&&typeof e.longitude=="number"&&b(e.latitude)&&y(e.longitude)}function I(t){return Math.min(90,Math.max(-90,t))}function O(t){return((t+180)%360+360)%360-180}const _=85.05112878;function x(t){const r=Math.max(-_,Math.min(_,I(t.latitude)))*Math.PI/180,s=(t.longitude+180)/360,a=(1-Math.log(Math.tan(r)+1/Math.cos(r))/Math.PI)/2;return{x:s,y:a}}function X(t){const e=t.x*360-180,r=Math.PI*(1-2*t.y);return{latitude:Math.atan(Math.sinh(r))*180/Math.PI,longitude:e}}function z(t,e,r,s={}){const{padding:a=16}=s,o=x({latitude:t.maxLatitude,longitude:t.minLongitude}),u=x({latitude:t.minLatitude,longitude:t.maxLongitude}),p=u.x-o.x||Number.EPSILON,m=u.y-o.y||Number.EPSILON,h=Math.max(1,e-a*2),d=Math.max(1,r-a*2),c=Math.min(h/p,d/m),f=a+(h-p*c)/2,M=a+(d-m*c)/2;return{project:l=>{const L=x(l);return{x:f+(L.x-o.x)*c,y:M+(L.y-o.y)*c}},scale:c,width:e,height:r}}exports.MERCATOR_MAX_LATITUDE=_;exports.Select=V;exports.clampLatitude=I;exports.fitProjection=z;exports.isCoordinate=v;exports.isValidLatitude=b;exports.isValidLongitude=y;exports.normalizeLongitude=O;exports.projectMercator=x;exports.unprojectMercator=X;
|
|
2
|
+
//# sourceMappingURL=projection-CkMNK7HA.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection-CkMNK7HA.cjs","sources":["../src/components/Select/Select.tsx","../src/geo/types.ts","../src/geo/projection.ts"],"sourcesContent":["import { forwardRef, useId } from \"react\";\nimport type { SelectHTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Select.module.css\";\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {\n label?: string;\n helperText?: string;\n error?: string;\n options?: SelectOption[];\n placeholder?: string;\n wrapperClassName?: string;\n}\n\n/**\n * Native `<select>` wrapper with label/helper/error slots. Either provide\n * `options` for a quick render, or pass `<option>` children directly.\n */\nexport const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(\n {\n label,\n helperText,\n error,\n options,\n placeholder,\n wrapperClassName,\n className,\n children,\n id,\n required,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const selectId = id ?? generatedId;\n\n return (\n <div className={cn(styles.wrapper, error && styles.error, wrapperClassName)}>\n {label && (\n <label htmlFor={selectId} className={styles.label}>\n {label}\n {required && <span className={styles.required}>*</span>}\n </label>\n )}\n <div className={styles.field}>\n <select\n ref={ref}\n id={selectId}\n aria-invalid={!!error}\n required={required}\n className={cn(styles.select, className)}\n {...props}\n >\n {placeholder && (\n <option value=\"\" disabled hidden>\n {placeholder}\n </option>\n )}\n {options?.map((opt) => (\n <option key={opt.value} value={opt.value} disabled={opt.disabled}>\n {opt.label}\n </option>\n ))}\n {children}\n </select>\n <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\n </div>\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n});\n\nfunction CaretIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 9l6 6 6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n","/**\n * WGS84 geographic coordinate. Mirrors the `Coordinate` schema from\n * `tempest-fastapi-sdk` (`geo/schemas.py`) — `latitude` in `[-90, 90]`,\n * `longitude` in `[-180, 180]`, serialized snake_case on the wire.\n */\nexport interface Coordinate {\n /** WGS84 latitude in degrees, `[-90, 90]`. E.g. `-23.5505`. */\n latitude: number;\n /** WGS84 longitude in degrees, `[-180, 180]`. E.g. `-46.6333`. */\n longitude: number;\n}\n\n/**\n * A single sample in a recorded trajectory: a {@link Coordinate} stamped with\n * the epoch millisecond it was captured, plus the optional accuracy radius\n * reported by the Geolocation API.\n */\nexport interface TrackPoint extends Coordinate {\n /** Capture time in epoch milliseconds (`GeolocationPosition.timestamp`). */\n timestamp: number;\n /** Horizontal accuracy radius in meters, if the device reported one. */\n accuracy?: number;\n}\n\n/**\n * Travel mode. Mirrors the `TravelMode` string enum from `tempest-fastapi-sdk`\n * (`geo/enums.py`) — the on-the-wire value is the raw string.\n */\nexport type TravelMode = \"car\" | \"motorcycle\" | \"bus\";\n\n/**\n * Estimated travel between two coordinates. Mirrors the `TravelEstimate`\n * schema from `tempest-fastapi-sdk` (`geo/schemas.py`), snake_case preserved so\n * a response deserializes straight into this type.\n */\nexport interface TravelEstimate {\n /** Travel mode the estimate was computed for. */\n mode: TravelMode;\n /** Great-circle distance scaled by circuity, in kilometers (`>= 0`). */\n distance_km: number;\n /** Estimated duration in minutes (`>= 0`). */\n duration_minutes: number;\n /** How the estimate was produced. `\"heuristic\"` (offline) or `\"osrm\"`. */\n source: \"heuristic\" | \"osrm\";\n}\n\n/**\n * Axis-aligned geographic bounding box. `min`/`max` follow the same degree\n * ranges as {@link Coordinate}.\n */\nexport interface GeoBounds {\n minLatitude: number;\n maxLatitude: number;\n minLongitude: number;\n maxLongitude: number;\n}\n\n/** True when `value` is a finite latitude in `[-90, 90]`. */\nexport function isValidLatitude(value: number): boolean {\n return Number.isFinite(value) && value >= -90 && value <= 90;\n}\n\n/** True when `value` is a finite longitude in `[-180, 180]`. */\nexport function isValidLongitude(value: number): boolean {\n return Number.isFinite(value) && value >= -180 && value <= 180;\n}\n\n/**\n * Type guard for {@link Coordinate}: an object with finite, in-range\n * `latitude` and `longitude`.\n */\nexport function isCoordinate(value: unknown): value is Coordinate {\n if (typeof value !== \"object\" || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.latitude === \"number\" &&\n typeof candidate.longitude === \"number\" &&\n isValidLatitude(candidate.latitude) &&\n isValidLongitude(candidate.longitude)\n );\n}\n\n/** Clamp a latitude into the valid `[-90, 90]` range. */\nexport function clampLatitude(latitude: number): number {\n return Math.min(90, Math.max(-90, latitude));\n}\n\n/**\n * Normalize a longitude into the `[-180, 180]` range, wrapping values that\n * cross the antimeridian (e.g. `190` → `-170`).\n */\nexport function normalizeLongitude(longitude: number): number {\n const wrapped = ((((longitude + 180) % 360) + 360) % 360) - 180;\n return wrapped;\n}\n","import { clampLatitude } from \"./types\";\nimport type { Coordinate, GeoBounds } from \"./types\";\n\n/**\n * A point projected onto the unit Web Mercator plane. Both axes are in `[0, 1]`\n * — `x` grows east, `y` grows south (screen convention).\n */\nexport interface MercatorPoint {\n x: number;\n y: number;\n}\n\n/** A pixel coordinate inside the plotting viewport. */\nexport interface PixelPoint {\n x: number;\n y: number;\n}\n\n/**\n * Web Mercator (EPSG:3857) latitude clamp. Latitudes beyond this diverge to\n * infinity in the projection, so tile maps cap here.\n */\nexport const MERCATOR_MAX_LATITUDE = 85.05112878;\n\n/**\n * Project a geographic coordinate onto the unit Web Mercator plane. This is the\n * same projection tile servers use, so a self-hosted tile layer and the\n * tile-free SVG plot line up pixel-for-pixel.\n *\n * @param coord - Coordinate to project.\n * @returns `{ x, y }` in `[0, 1]`.\n */\nexport function projectMercator(coord: Coordinate): MercatorPoint {\n const lat = Math.max(\n -MERCATOR_MAX_LATITUDE,\n Math.min(MERCATOR_MAX_LATITUDE, clampLatitude(coord.latitude)),\n );\n const latRad = (lat * Math.PI) / 180;\n const x = (coord.longitude + 180) / 360;\n const y = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2;\n return { x, y };\n}\n\n/**\n * Inverse of {@link projectMercator}: recover a coordinate from a unit-plane\n * point.\n *\n * @param point - `{ x, y }` in `[0, 1]`.\n * @returns The geographic coordinate.\n */\nexport function unprojectMercator(point: MercatorPoint): Coordinate {\n const longitude = point.x * 360 - 180;\n const n = Math.PI * (1 - 2 * point.y);\n const latitude = (Math.atan(Math.sinh(n)) * 180) / Math.PI;\n return { latitude, longitude };\n}\n\n/** A ready-to-use mapping from coordinates to viewport pixels. */\nexport interface FittedProjection {\n /** Project a coordinate to a pixel inside the viewport. */\n project: (coord: Coordinate) => PixelPoint;\n /** Uniform scale (unit-plane → pixels) actually used, after aspect fit. */\n scale: number;\n /** Viewport width in pixels. */\n width: number;\n /** Viewport height in pixels. */\n height: number;\n}\n\n/** Options for {@link fitProjection}. */\nexport interface FitProjectionOptions {\n /** Inner padding in pixels kept clear on every edge. Default: `16`. */\n padding?: number;\n}\n\n/**\n * Build a projection that fits `bounds` into a `width × height` viewport while\n * preserving aspect ratio (uniform scale, centered). This is what powers the\n * tile-free trajectory plot: project the bounds, scale to the SVG box, keep\n * shapes undistorted.\n *\n * @param bounds - Geographic extent to fit.\n * @param width - Viewport width in pixels.\n * @param height - Viewport height in pixels.\n * @param options - Padding tuning.\n * @returns A {@link FittedProjection} with a `project(coord)` mapper.\n */\nexport function fitProjection(\n bounds: GeoBounds,\n width: number,\n height: number,\n options: FitProjectionOptions = {},\n): FittedProjection {\n const { padding = 16 } = options;\n\n const topLeft = projectMercator({\n latitude: bounds.maxLatitude,\n longitude: bounds.minLongitude,\n });\n const bottomRight = projectMercator({\n latitude: bounds.minLatitude,\n longitude: bounds.maxLongitude,\n });\n\n const spanX = bottomRight.x - topLeft.x || Number.EPSILON;\n const spanY = bottomRight.y - topLeft.y || Number.EPSILON;\n\n const innerWidth = Math.max(1, width - padding * 2);\n const innerHeight = Math.max(1, height - padding * 2);\n\n // Uniform scale keeps the trajectory undistorted; fit the tighter axis.\n const scale = Math.min(innerWidth / spanX, innerHeight / spanY);\n\n // Center the projected content within the padded box.\n const offsetX = padding + (innerWidth - spanX * scale) / 2;\n const offsetY = padding + (innerHeight - spanY * scale) / 2;\n\n const project = (coord: Coordinate): PixelPoint => {\n const projected = projectMercator(coord);\n return {\n x: offsetX + (projected.x - topLeft.x) * scale,\n y: offsetY + (projected.y - topLeft.y) * scale,\n };\n };\n\n return { project, scale, width, height };\n}\n"],"names":["Select","forwardRef","label","helperText","error","options","placeholder","wrapperClassName","className","children","id","required","props","ref","generatedId","useId","selectId","jsxs","cn","styles","jsx","opt","CaretIcon","isValidLatitude","value","isValidLongitude","isCoordinate","candidate","clampLatitude","latitude","normalizeLongitude","longitude","MERCATOR_MAX_LATITUDE","projectMercator","coord","latRad","x","y","unprojectMercator","point","n","fitProjection","bounds","width","height","padding","topLeft","bottomRight","spanX","spanY","innerWidth","innerHeight","scale","offsetX","offsetY","projected"],"mappings":"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"}
|