@trackunit/react-map 0.2.125 → 0.2.127

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/index.cjs.js CHANGED
@@ -1059,17 +1059,18 @@ const FallbackAnnotationContent = ({ store }) => {
1059
1059
  return (jsxRuntime.jsx("div", { className: "pointer-events-none col-start-1 row-start-1 self-end justify-self-start", children: jsxRuntime.jsx(AnnotationStack, { annotations: displayAnnotations, isExiting: isExiting }) }));
1060
1060
  };
1061
1061
  const DEBOUNCE_DELAY = 200;
1062
- const cvaLoadingOverlay = cssClassVarianceUtilities.cvaMerge([
1063
- "pointer-events-none",
1064
- "col-start-1",
1065
- "row-start-1",
1066
- "flex",
1067
- "items-start",
1068
- "justify-center",
1069
- "transition-[opacity,display]",
1070
- "duration-150",
1071
- "ease-in-out",
1072
- ], {
1062
+ const cvaLoadingOverlay = cssClassVarianceUtilities.cva({
1063
+ base: [
1064
+ "pointer-events-none",
1065
+ "col-start-1",
1066
+ "row-start-1",
1067
+ "flex",
1068
+ "items-start",
1069
+ "justify-center",
1070
+ "transition-[opacity,display]",
1071
+ "duration-150",
1072
+ "ease-in-out",
1073
+ ],
1073
1074
  variants: {
1074
1075
  visible: {
1075
1076
  true: ["opacity-100"],
@@ -1900,7 +1901,7 @@ const Custom = ({ config, overlaySide = undefined, resolved, placement }) => {
1900
1901
  };
1901
1902
 
1902
1903
  /** Shared padding for interactive menu items (button, toggle, stepper, select, radioGroup). */
1903
- const cvaControlMenuItemBase = cssClassVarianceUtilities.cvaMerge(["px-3", "py-2"]);
1904
+ const cvaControlMenuItemBase = cssClassVarianceUtilities.cva({ base: ["px-3", "py-2"] });
1904
1905
  // ============================================================================
1905
1906
  // Cross-Cutting Middleware
1906
1907
  // ============================================================================
@@ -2697,21 +2698,22 @@ const ControlsInner = ({ api, controls, className }) => {
2697
2698
  // shallow equality is sufficient; no custom comparator needed.
2698
2699
  const Controls = react.memo(ControlsInner);
2699
2700
 
2700
- const cvaMapPreview = cssClassVarianceUtilities.cvaMerge([
2701
- "relative",
2702
- "h-[72px]",
2703
- "w-[72px]",
2704
- "overflow-clip",
2705
- "[--radius:theme(borderRadius.xl)]",
2706
- "[--border:2px]",
2707
- "rounded-[var(--radius)]",
2708
- "before:pointer-events-none",
2709
- "before:absolute",
2710
- "before:inset-0",
2711
- "before:z-10",
2712
- "before:rounded-[var(--radius)]",
2713
- "before:border-[length:var(--border)]",
2714
- ], {
2701
+ const cvaMapPreview = cssClassVarianceUtilities.cva({
2702
+ base: [
2703
+ "relative",
2704
+ "h-[72px]",
2705
+ "w-[72px]",
2706
+ "overflow-clip",
2707
+ "[--radius:theme(borderRadius.xl)]",
2708
+ "[--border:2px]",
2709
+ "rounded-[var(--radius)]",
2710
+ "before:pointer-events-none",
2711
+ "before:absolute",
2712
+ "before:inset-0",
2713
+ "before:z-10",
2714
+ "before:rounded-[var(--radius)]",
2715
+ "before:border-[length:var(--border)]",
2716
+ ],
2715
2717
  variants: {
2716
2718
  selected: {
2717
2719
  true: ["before:border-primary-500", "before:ring-2", "before:ring-inset", "before:ring-white"],
@@ -3980,19 +3982,21 @@ const resolveMarkerDomSize = (size, direction, form) => {
3980
3982
  * properties that need to animate, intentionally excluding `width` to prevent
3981
3983
  * non-composited layout thrashing. See `MarkerAnimatedSurface.tsx` for details.
3982
3984
  */
3983
- const cvaMapMarker = cssClassVarianceUtilities.cvaMerge([
3984
- "inline-flex",
3985
- "items-center",
3986
- "relative",
3987
- "overflow-hidden",
3988
- "select-none",
3989
- "cursor-pointer",
3990
- "rounded-full",
3991
- "outline-none",
3992
- "focus-visible:ring-2",
3993
- "focus-visible:ring-info-500",
3994
- "focus-visible:ring-offset-2",
3995
- ]);
3985
+ const cvaMapMarker = cssClassVarianceUtilities.cva({
3986
+ base: [
3987
+ "inline-flex",
3988
+ "items-center",
3989
+ "relative",
3990
+ "overflow-hidden",
3991
+ "select-none",
3992
+ "cursor-pointer",
3993
+ "rounded-full",
3994
+ "outline-none",
3995
+ "focus-visible:ring-2",
3996
+ "focus-visible:ring-info-500",
3997
+ "focus-visible:ring-offset-2",
3998
+ ],
3999
+ });
3996
4000
  /**
3997
4001
  * Label span inside pill markers.
3998
4002
  *
@@ -4005,19 +4009,14 @@ const cvaMapMarker = cssClassVarianceUtilities.cvaMerge([
4005
4009
  * alone biases the text upward (extra space below). `overflow-x: clip` gives the
4006
4010
  * horizontal ellipsis without a vertical clip on the span itself.
4007
4011
  */
4008
- const cvaPillLabel = cssClassVarianceUtilities.cvaMerge([
4009
- "min-w-0",
4010
- "flex-1",
4011
- "overflow-x-clip",
4012
- "whitespace-nowrap",
4013
- "text-ellipsis",
4014
- "font-medium",
4015
- "leading-none",
4016
- ]);
4012
+ const cvaPillLabel = cssClassVarianceUtilities.cva({
4013
+ base: ["min-w-0", "flex-1", "overflow-x-clip", "whitespace-nowrap", "text-ellipsis", "font-medium", "leading-none"],
4014
+ });
4017
4015
  /**
4018
4016
  * Inner indicator disc: marker fill via `--marker-color`, border via CSS `color-mix` on that token (plan §5).
4019
4017
  */
4020
- const cvaMarkerIndicator = cssClassVarianceUtilities.cvaMerge(["rounded-full", "bg-[var(--marker-color)]", "flex", "items-center", "justify-center"], {
4018
+ const cvaMarkerIndicator = cssClassVarianceUtilities.cva({
4019
+ base: ["rounded-full", "bg-[var(--marker-color)]", "flex", "items-center", "justify-center"],
4021
4020
  variants: {
4022
4021
  size: {
4023
4022
  xs: ["h-2", "w-2"],
@@ -5579,6 +5578,102 @@ const useFitToContent = (api, handles, options) => {
5579
5578
  return react.useMemo(() => ({ hasFitted, isWaiting, fitNow }), [hasFitted, isWaiting, fitNow]);
5580
5579
  };
5581
5580
 
5581
+ const isWithinBounds = (position, bounds) => {
5582
+ const [lng, lat] = position;
5583
+ const [minLng, minLat, maxLng, maxLat] = bounds;
5584
+ const lngInside = minLng > maxLng ? lng >= minLng || lng <= maxLng : lng >= minLng && lng <= maxLng;
5585
+ return lngInside && lat >= minLat && lat <= maxLat;
5586
+ };
5587
+ const overlaps = (a, b, footprint) => {
5588
+ // Shortest angular longitude delta so labels near the antimeridian (e.g. 179.9° and
5589
+ // -179.9°) are correctly seen as ~0.2° apart, not ~359.8°, matching `isWithinBounds`.
5590
+ const rawLngDelta = Math.abs(a[0] - b[0]);
5591
+ const lngDelta = Math.min(rawLngDelta, 360 - rawLngDelta);
5592
+ return lngDelta < footprint.widthDeg && Math.abs(a[1] - b[1]) < footprint.heightDeg;
5593
+ };
5594
+ /**
5595
+ * Chooses which markers render a label so that no two labels overlap.
5596
+ *
5597
+ * Greedy placement: walks `items` in order, skips any outside the view box,
5598
+ * and adds a label only when its nominal footprint does not collide with an
5599
+ * already-placed one, stopping at `ceiling`. The budget is emergent — however
5600
+ * many non-overlapping labels fit, bounded by the ceiling.
5601
+ *
5602
+ * Deterministic: identical input yields an identical set. Phase 1 places in the
5603
+ * caller-provided order; priority ordering, dispersion, and pin-on-a-stick
5604
+ * displacement layer on top in later phases.
5605
+ */
5606
+ const buildLabelPlacement = ({ items, getId, getPosition, bounds, footprint, ceiling, }) => {
5607
+ const shown = new Set();
5608
+ const placed = [];
5609
+ for (const item of items) {
5610
+ if (shown.size >= ceiling)
5611
+ break;
5612
+ const position = getPosition(item);
5613
+ if (bounds !== null && !isWithinBounds(position, bounds))
5614
+ continue;
5615
+ if (placed.some(other => overlaps(other, position, footprint)))
5616
+ continue;
5617
+ shown.add(getId(item));
5618
+ placed.push(position);
5619
+ }
5620
+ return shown;
5621
+ };
5622
+
5623
+ const WEB_MERCATOR_TILE_SIZE_PX = 256;
5624
+ /**
5625
+ * Degrees of longitude/latitude spanned by a single screen pixel at `zoom`,
5626
+ * near `latitudeDeg`, under Web Mercator.
5627
+ *
5628
+ * Longitude degrees-per-pixel is constant everywhere; latitude degrees-per-pixel
5629
+ * scales by `cos(latitude)` because Mercator stretches higher latitudes. Used to
5630
+ * convert a pixel label footprint into a geo bounding box so collision runs in
5631
+ * the same lng/lat space the marker positions already live in.
5632
+ */
5633
+ const degreesPerPixel$1 = (zoom, latitudeDeg) => {
5634
+ const worldWidthPx = WEB_MERCATOR_TILE_SIZE_PX * 2 ** zoom;
5635
+ const lng = 360 / worldWidthPx;
5636
+ return { lng, lat: lng * Math.cos((latitudeDeg * Math.PI) / 180) };
5637
+ };
5638
+ /**
5639
+ * Upper-bound label footprint as a geo-space width/height, for collision testing.
5640
+ *
5641
+ * A rendered pill's size does NOT follow the zoom circle-size tier: `MapMarker`
5642
+ * lays every pill out at the fixed {@link MARKER_PILL_CONTENT_LAYOUT_SIZE}, and the
5643
+ * label text is capped at `maxLabelWidthPx` (ellipsis beyond). We size the collision
5644
+ * box to that upper bound — widest pill that can render — so the box is never smaller
5645
+ * than what paints, which is what keeps the "no two labels overlap" guarantee honest.
5646
+ * Density is tuned via `paddingPx`, not by shrinking the box.
5647
+ */
5648
+ const labelFootprintDeg = ({ zoom, latitudeDeg, paddingPx }) => {
5649
+ const { circle, pillPaddingY } = MARKER_SIZE_MAP[MARKER_PILL_CONTENT_LAYOUT_SIZE];
5650
+ const { maxLabelWidthPx, gapPx, paddingRightPx } = MARKER_TUNING.pill;
5651
+ // Left pad (== pillPaddingY) + icon disc + gap + max label text + right pad. Assume the icon
5652
+ // disc is present (the wider case) so the box stays an upper bound.
5653
+ const widthPx = pillPaddingY + circle + gapPx + maxLabelWidthPx + paddingRightPx + 2 * paddingPx;
5654
+ const heightPx = markerPillOuterHeightPx(MARKER_PILL_CONTENT_LAYOUT_SIZE) + 2 * paddingPx;
5655
+ const perPixel = degreesPerPixel$1(zoom, latitudeDeg);
5656
+ return { widthDeg: widthPx * perPixel.lng, heightDeg: heightPx * perPixel.lat };
5657
+ };
5658
+
5659
+ const EMPTY_LABEL_IDS = new Set();
5660
+ /**
5661
+ * Viewport-driven label placement: returns the ids of the markers that should
5662
+ * render a label such that no two labels overlap, bounded by `ceiling`.
5663
+ *
5664
+ * Derives an upper-bound pill footprint (`labelFootprintDeg`) and delegates
5665
+ * selection to the pure `buildLabelPlacement`. Exposed as a hook so the placement
5666
+ * participates in React's stability model and later phases can hold incumbency
5667
+ * state here without changing the public surface.
5668
+ */
5669
+ const useLabelPlacement = ({ enabled, items, getId, getPosition, bounds, zoom, paddingPx, ceiling, }) => react.useMemo(() => {
5670
+ if (!enabled)
5671
+ return EMPTY_LABEL_IDS;
5672
+ const latitudeDeg = bounds === null ? 0 : (bounds[1] + bounds[3]) / 2;
5673
+ const footprint = labelFootprintDeg({ zoom, latitudeDeg, paddingPx });
5674
+ return buildLabelPlacement({ items, getId, getPosition, bounds, footprint, ceiling });
5675
+ }, [enabled, items, getId, getPosition, bounds, zoom, paddingPx, ceiling]);
5676
+
5582
5677
  // ============================================================================
5583
5678
  // Helpers
5584
5679
  // ============================================================================
@@ -6403,20 +6498,21 @@ const extractSinglePartInfo = (geometry) => {
6403
6498
  }
6404
6499
  };
6405
6500
 
6406
- const cvaShapeLabel = cssClassVarianceUtilities.cvaMerge([
6407
- "select-none",
6408
- "inline-flex",
6409
- "items-center",
6410
- "gap-1",
6411
- "whitespace-nowrap",
6412
- "px-2",
6413
- "py-0.5",
6414
- "text-xs",
6415
- "font-medium",
6416
- "leading-tight",
6417
- "transition-opacity",
6418
- "duration-150",
6419
- ], {
6501
+ const cvaShapeLabel = cssClassVarianceUtilities.cva({
6502
+ base: [
6503
+ "select-none",
6504
+ "inline-flex",
6505
+ "items-center",
6506
+ "gap-1",
6507
+ "whitespace-nowrap",
6508
+ "px-2",
6509
+ "py-0.5",
6510
+ "text-xs",
6511
+ "font-medium",
6512
+ "leading-tight",
6513
+ "transition-opacity",
6514
+ "duration-150",
6515
+ ],
6420
6516
  variants: {
6421
6517
  theme: {
6422
6518
  light: ["shadow-sm"],
@@ -6473,7 +6569,8 @@ const EDGE_LABEL_BORDER_RADIUS = {
6473
6569
  right: "3px 0 3px 3px",
6474
6570
  },
6475
6571
  };
6476
- const cvaFitMode = cssClassVarianceUtilities.cvaMerge([], {
6572
+ const cvaFitMode = cssClassVarianceUtilities.cva({
6573
+ base: [],
6477
6574
  variants: {
6478
6575
  fitMode: {
6479
6576
  fits: ["pointer-events-auto"],
@@ -12208,6 +12305,7 @@ exports.useExpandedIds = useExpandedIds;
12208
12305
  exports.useFitFeatureBounds = useFitFeatureBounds;
12209
12306
  exports.useFitToContent = useFitToContent;
12210
12307
  exports.useImageOverlay = useImageOverlay;
12308
+ exports.useLabelPlacement = useLabelPlacement;
12211
12309
  exports.useLayers = useLayers;
12212
12310
  exports.useMap = useMap;
12213
12311
  exports.useMapAnnotation = useMapAnnotation;
package/index.esm.js CHANGED
@@ -3,7 +3,7 @@ import { useNamespaceTranslation, registerTranslations } from '@trackunit/i18n-l
3
3
  import { INITIAL_CAMERA_STATE, INITIAL_MAP_STATUS, KEYBOARD_PAN_AMOUNT, KEYBOARD_ZOOM_AMOUNT, mapAppearanceSchema, DEFAULT_MAP_APPEARANCE, WORLD_BBOX, INITIAL_INTERACTION_STATE, mapThemeSchema, mergeAntimeridianFeatures, extractSourceData, computeMarkerDomPortalZIndex, densifyGeodesicFeatures, angularDistance, intermediatePoint, GEODESIC_MAX_SEGMENT_KM, geometryTypeToShapeType, resolveStrokeColors } from '@trackunit/react-map-adapter-shared';
4
4
  export { ANCHOR_SELECTOR, DEFAULT_MAP_APPEARANCE, HIT_SURFACE_SELECTOR, INITIAL_CAMERA_STATE, INITIAL_INTERACTION_STATE, INITIAL_MAP_STATE, INITIAL_MAP_STATUS, computeMarkerDomPortalZIndex, defineAdapter, geometryTypeToShapeType, mapAppearanceSchema, mapThemeSchema, mapTypeSchema } from '@trackunit/react-map-adapter-shared';
5
5
  import { useSyncExternalStore, useMemo, useState, useRef, useCallback, useLayoutEffect, useEffect, useReducer, createContext, useContext, memo, Suspense, useId, Fragment as Fragment$1, forwardRef } from 'react';
6
- import { cvaMerge } from '@trackunit/css-class-variance-utilities';
6
+ import { cva } from '@trackunit/css-class-variance-utilities';
7
7
  import { Tooltip, useMergeRefs, Icon, Spinner, Text, useMeasure, useContainerBreakpoints, useDebounce, useWatch, useKeyboardShortcut, IconButton, ToggleGroup as ToggleGroup$1, useIsFullscreen, Popover, PopoverTrigger, PopoverContent, MoreMenu, useHover as useHover$1, cvaInteractableItem, useLocalStorageReducer } from '@trackunit/react-components';
8
8
  import { twMerge } from 'tailwind-merge';
9
9
  import { useFloating, autoUpdate, offset, flip, useHover, safePolygon, useInteractions, shift, hide, useDismiss, useRole } from '@floating-ui/react';
@@ -1058,17 +1058,18 @@ const FallbackAnnotationContent = ({ store }) => {
1058
1058
  return (jsx("div", { className: "pointer-events-none col-start-1 row-start-1 self-end justify-self-start", children: jsx(AnnotationStack, { annotations: displayAnnotations, isExiting: isExiting }) }));
1059
1059
  };
1060
1060
  const DEBOUNCE_DELAY = 200;
1061
- const cvaLoadingOverlay = cvaMerge([
1062
- "pointer-events-none",
1063
- "col-start-1",
1064
- "row-start-1",
1065
- "flex",
1066
- "items-start",
1067
- "justify-center",
1068
- "transition-[opacity,display]",
1069
- "duration-150",
1070
- "ease-in-out",
1071
- ], {
1061
+ const cvaLoadingOverlay = cva({
1062
+ base: [
1063
+ "pointer-events-none",
1064
+ "col-start-1",
1065
+ "row-start-1",
1066
+ "flex",
1067
+ "items-start",
1068
+ "justify-center",
1069
+ "transition-[opacity,display]",
1070
+ "duration-150",
1071
+ "ease-in-out",
1072
+ ],
1072
1073
  variants: {
1073
1074
  visible: {
1074
1075
  true: ["opacity-100"],
@@ -1899,7 +1900,7 @@ const Custom = ({ config, overlaySide = undefined, resolved, placement }) => {
1899
1900
  };
1900
1901
 
1901
1902
  /** Shared padding for interactive menu items (button, toggle, stepper, select, radioGroup). */
1902
- const cvaControlMenuItemBase = cvaMerge(["px-3", "py-2"]);
1903
+ const cvaControlMenuItemBase = cva({ base: ["px-3", "py-2"] });
1903
1904
  // ============================================================================
1904
1905
  // Cross-Cutting Middleware
1905
1906
  // ============================================================================
@@ -2696,21 +2697,22 @@ const ControlsInner = ({ api, controls, className }) => {
2696
2697
  // shallow equality is sufficient; no custom comparator needed.
2697
2698
  const Controls = memo(ControlsInner);
2698
2699
 
2699
- const cvaMapPreview = cvaMerge([
2700
- "relative",
2701
- "h-[72px]",
2702
- "w-[72px]",
2703
- "overflow-clip",
2704
- "[--radius:theme(borderRadius.xl)]",
2705
- "[--border:2px]",
2706
- "rounded-[var(--radius)]",
2707
- "before:pointer-events-none",
2708
- "before:absolute",
2709
- "before:inset-0",
2710
- "before:z-10",
2711
- "before:rounded-[var(--radius)]",
2712
- "before:border-[length:var(--border)]",
2713
- ], {
2700
+ const cvaMapPreview = cva({
2701
+ base: [
2702
+ "relative",
2703
+ "h-[72px]",
2704
+ "w-[72px]",
2705
+ "overflow-clip",
2706
+ "[--radius:theme(borderRadius.xl)]",
2707
+ "[--border:2px]",
2708
+ "rounded-[var(--radius)]",
2709
+ "before:pointer-events-none",
2710
+ "before:absolute",
2711
+ "before:inset-0",
2712
+ "before:z-10",
2713
+ "before:rounded-[var(--radius)]",
2714
+ "before:border-[length:var(--border)]",
2715
+ ],
2714
2716
  variants: {
2715
2717
  selected: {
2716
2718
  true: ["before:border-primary-500", "before:ring-2", "before:ring-inset", "before:ring-white"],
@@ -3979,19 +3981,21 @@ const resolveMarkerDomSize = (size, direction, form) => {
3979
3981
  * properties that need to animate, intentionally excluding `width` to prevent
3980
3982
  * non-composited layout thrashing. See `MarkerAnimatedSurface.tsx` for details.
3981
3983
  */
3982
- const cvaMapMarker = cvaMerge([
3983
- "inline-flex",
3984
- "items-center",
3985
- "relative",
3986
- "overflow-hidden",
3987
- "select-none",
3988
- "cursor-pointer",
3989
- "rounded-full",
3990
- "outline-none",
3991
- "focus-visible:ring-2",
3992
- "focus-visible:ring-info-500",
3993
- "focus-visible:ring-offset-2",
3994
- ]);
3984
+ const cvaMapMarker = cva({
3985
+ base: [
3986
+ "inline-flex",
3987
+ "items-center",
3988
+ "relative",
3989
+ "overflow-hidden",
3990
+ "select-none",
3991
+ "cursor-pointer",
3992
+ "rounded-full",
3993
+ "outline-none",
3994
+ "focus-visible:ring-2",
3995
+ "focus-visible:ring-info-500",
3996
+ "focus-visible:ring-offset-2",
3997
+ ],
3998
+ });
3995
3999
  /**
3996
4000
  * Label span inside pill markers.
3997
4001
  *
@@ -4004,19 +4008,14 @@ const cvaMapMarker = cvaMerge([
4004
4008
  * alone biases the text upward (extra space below). `overflow-x: clip` gives the
4005
4009
  * horizontal ellipsis without a vertical clip on the span itself.
4006
4010
  */
4007
- const cvaPillLabel = cvaMerge([
4008
- "min-w-0",
4009
- "flex-1",
4010
- "overflow-x-clip",
4011
- "whitespace-nowrap",
4012
- "text-ellipsis",
4013
- "font-medium",
4014
- "leading-none",
4015
- ]);
4011
+ const cvaPillLabel = cva({
4012
+ base: ["min-w-0", "flex-1", "overflow-x-clip", "whitespace-nowrap", "text-ellipsis", "font-medium", "leading-none"],
4013
+ });
4016
4014
  /**
4017
4015
  * Inner indicator disc: marker fill via `--marker-color`, border via CSS `color-mix` on that token (plan §5).
4018
4016
  */
4019
- const cvaMarkerIndicator = cvaMerge(["rounded-full", "bg-[var(--marker-color)]", "flex", "items-center", "justify-center"], {
4017
+ const cvaMarkerIndicator = cva({
4018
+ base: ["rounded-full", "bg-[var(--marker-color)]", "flex", "items-center", "justify-center"],
4020
4019
  variants: {
4021
4020
  size: {
4022
4021
  xs: ["h-2", "w-2"],
@@ -5578,6 +5577,102 @@ const useFitToContent = (api, handles, options) => {
5578
5577
  return useMemo(() => ({ hasFitted, isWaiting, fitNow }), [hasFitted, isWaiting, fitNow]);
5579
5578
  };
5580
5579
 
5580
+ const isWithinBounds = (position, bounds) => {
5581
+ const [lng, lat] = position;
5582
+ const [minLng, minLat, maxLng, maxLat] = bounds;
5583
+ const lngInside = minLng > maxLng ? lng >= minLng || lng <= maxLng : lng >= minLng && lng <= maxLng;
5584
+ return lngInside && lat >= minLat && lat <= maxLat;
5585
+ };
5586
+ const overlaps = (a, b, footprint) => {
5587
+ // Shortest angular longitude delta so labels near the antimeridian (e.g. 179.9° and
5588
+ // -179.9°) are correctly seen as ~0.2° apart, not ~359.8°, matching `isWithinBounds`.
5589
+ const rawLngDelta = Math.abs(a[0] - b[0]);
5590
+ const lngDelta = Math.min(rawLngDelta, 360 - rawLngDelta);
5591
+ return lngDelta < footprint.widthDeg && Math.abs(a[1] - b[1]) < footprint.heightDeg;
5592
+ };
5593
+ /**
5594
+ * Chooses which markers render a label so that no two labels overlap.
5595
+ *
5596
+ * Greedy placement: walks `items` in order, skips any outside the view box,
5597
+ * and adds a label only when its nominal footprint does not collide with an
5598
+ * already-placed one, stopping at `ceiling`. The budget is emergent — however
5599
+ * many non-overlapping labels fit, bounded by the ceiling.
5600
+ *
5601
+ * Deterministic: identical input yields an identical set. Phase 1 places in the
5602
+ * caller-provided order; priority ordering, dispersion, and pin-on-a-stick
5603
+ * displacement layer on top in later phases.
5604
+ */
5605
+ const buildLabelPlacement = ({ items, getId, getPosition, bounds, footprint, ceiling, }) => {
5606
+ const shown = new Set();
5607
+ const placed = [];
5608
+ for (const item of items) {
5609
+ if (shown.size >= ceiling)
5610
+ break;
5611
+ const position = getPosition(item);
5612
+ if (bounds !== null && !isWithinBounds(position, bounds))
5613
+ continue;
5614
+ if (placed.some(other => overlaps(other, position, footprint)))
5615
+ continue;
5616
+ shown.add(getId(item));
5617
+ placed.push(position);
5618
+ }
5619
+ return shown;
5620
+ };
5621
+
5622
+ const WEB_MERCATOR_TILE_SIZE_PX = 256;
5623
+ /**
5624
+ * Degrees of longitude/latitude spanned by a single screen pixel at `zoom`,
5625
+ * near `latitudeDeg`, under Web Mercator.
5626
+ *
5627
+ * Longitude degrees-per-pixel is constant everywhere; latitude degrees-per-pixel
5628
+ * scales by `cos(latitude)` because Mercator stretches higher latitudes. Used to
5629
+ * convert a pixel label footprint into a geo bounding box so collision runs in
5630
+ * the same lng/lat space the marker positions already live in.
5631
+ */
5632
+ const degreesPerPixel$1 = (zoom, latitudeDeg) => {
5633
+ const worldWidthPx = WEB_MERCATOR_TILE_SIZE_PX * 2 ** zoom;
5634
+ const lng = 360 / worldWidthPx;
5635
+ return { lng, lat: lng * Math.cos((latitudeDeg * Math.PI) / 180) };
5636
+ };
5637
+ /**
5638
+ * Upper-bound label footprint as a geo-space width/height, for collision testing.
5639
+ *
5640
+ * A rendered pill's size does NOT follow the zoom circle-size tier: `MapMarker`
5641
+ * lays every pill out at the fixed {@link MARKER_PILL_CONTENT_LAYOUT_SIZE}, and the
5642
+ * label text is capped at `maxLabelWidthPx` (ellipsis beyond). We size the collision
5643
+ * box to that upper bound — widest pill that can render — so the box is never smaller
5644
+ * than what paints, which is what keeps the "no two labels overlap" guarantee honest.
5645
+ * Density is tuned via `paddingPx`, not by shrinking the box.
5646
+ */
5647
+ const labelFootprintDeg = ({ zoom, latitudeDeg, paddingPx }) => {
5648
+ const { circle, pillPaddingY } = MARKER_SIZE_MAP[MARKER_PILL_CONTENT_LAYOUT_SIZE];
5649
+ const { maxLabelWidthPx, gapPx, paddingRightPx } = MARKER_TUNING.pill;
5650
+ // Left pad (== pillPaddingY) + icon disc + gap + max label text + right pad. Assume the icon
5651
+ // disc is present (the wider case) so the box stays an upper bound.
5652
+ const widthPx = pillPaddingY + circle + gapPx + maxLabelWidthPx + paddingRightPx + 2 * paddingPx;
5653
+ const heightPx = markerPillOuterHeightPx(MARKER_PILL_CONTENT_LAYOUT_SIZE) + 2 * paddingPx;
5654
+ const perPixel = degreesPerPixel$1(zoom, latitudeDeg);
5655
+ return { widthDeg: widthPx * perPixel.lng, heightDeg: heightPx * perPixel.lat };
5656
+ };
5657
+
5658
+ const EMPTY_LABEL_IDS = new Set();
5659
+ /**
5660
+ * Viewport-driven label placement: returns the ids of the markers that should
5661
+ * render a label such that no two labels overlap, bounded by `ceiling`.
5662
+ *
5663
+ * Derives an upper-bound pill footprint (`labelFootprintDeg`) and delegates
5664
+ * selection to the pure `buildLabelPlacement`. Exposed as a hook so the placement
5665
+ * participates in React's stability model and later phases can hold incumbency
5666
+ * state here without changing the public surface.
5667
+ */
5668
+ const useLabelPlacement = ({ enabled, items, getId, getPosition, bounds, zoom, paddingPx, ceiling, }) => useMemo(() => {
5669
+ if (!enabled)
5670
+ return EMPTY_LABEL_IDS;
5671
+ const latitudeDeg = bounds === null ? 0 : (bounds[1] + bounds[3]) / 2;
5672
+ const footprint = labelFootprintDeg({ zoom, latitudeDeg, paddingPx });
5673
+ return buildLabelPlacement({ items, getId, getPosition, bounds, footprint, ceiling });
5674
+ }, [enabled, items, getId, getPosition, bounds, zoom, paddingPx, ceiling]);
5675
+
5581
5676
  // ============================================================================
5582
5677
  // Helpers
5583
5678
  // ============================================================================
@@ -6402,20 +6497,21 @@ const extractSinglePartInfo = (geometry) => {
6402
6497
  }
6403
6498
  };
6404
6499
 
6405
- const cvaShapeLabel = cvaMerge([
6406
- "select-none",
6407
- "inline-flex",
6408
- "items-center",
6409
- "gap-1",
6410
- "whitespace-nowrap",
6411
- "px-2",
6412
- "py-0.5",
6413
- "text-xs",
6414
- "font-medium",
6415
- "leading-tight",
6416
- "transition-opacity",
6417
- "duration-150",
6418
- ], {
6500
+ const cvaShapeLabel = cva({
6501
+ base: [
6502
+ "select-none",
6503
+ "inline-flex",
6504
+ "items-center",
6505
+ "gap-1",
6506
+ "whitespace-nowrap",
6507
+ "px-2",
6508
+ "py-0.5",
6509
+ "text-xs",
6510
+ "font-medium",
6511
+ "leading-tight",
6512
+ "transition-opacity",
6513
+ "duration-150",
6514
+ ],
6419
6515
  variants: {
6420
6516
  theme: {
6421
6517
  light: ["shadow-sm"],
@@ -6472,7 +6568,8 @@ const EDGE_LABEL_BORDER_RADIUS = {
6472
6568
  right: "3px 0 3px 3px",
6473
6569
  },
6474
6570
  };
6475
- const cvaFitMode = cvaMerge([], {
6571
+ const cvaFitMode = cva({
6572
+ base: [],
6476
6573
  variants: {
6477
6574
  fitMode: {
6478
6575
  fits: ["pointer-events-auto"],
@@ -12120,4 +12217,4 @@ const mockMapApi = (overrides) => {
12120
12217
  */
12121
12218
  setupLibraryTranslations();
12122
12219
 
12123
- export { ClusterMarker, ClusterStick, Controls, DEFAULT_MARKER_SIZE_BREAKPOINTS, DefaultControls, Layers, MARKER_DARK_PILL, MARKER_DISC_BORDER_WIDTH_PX, MARKER_LIGHT_PILL, MARKER_PILL_CONTENT_LAYOUT_SIZE, MARKER_SIZE_MAP, MARKER_TUNING, MapLoadingState, MapMarker, MapMarkerIcon, PanelIconButton, ShapeAnnotationLabel, buildExpandedIds, cvaMapMarker, cvaMarkerIndicator, mockMapApi, useAdaptiveMarkerHelpers, useAutoPanResolver, useCameraIdle, useCameraState, useClusterCountFormat, useControlStack, useControls, useDefaultControls, useDirectionIndicator, useEntitiesNearCursor, useExpandedIds, useFitFeatureBounds, useFitToContent, useImageOverlay, useLayers, useMap, useMapAnnotation, useMapAnnotations, useMapAppearanceControls, useMapKeyboardNavigation, useMarkerColors, useMarkerStateResolvers, useMarkers, usePanel, usePanelPreload, usePreviewMap, useRoute, useShapeLabelHelpers, useShapes, useViewportContext };
12220
+ export { ClusterMarker, ClusterStick, Controls, DEFAULT_MARKER_SIZE_BREAKPOINTS, DefaultControls, Layers, MARKER_DARK_PILL, MARKER_DISC_BORDER_WIDTH_PX, MARKER_LIGHT_PILL, MARKER_PILL_CONTENT_LAYOUT_SIZE, MARKER_SIZE_MAP, MARKER_TUNING, MapLoadingState, MapMarker, MapMarkerIcon, PanelIconButton, ShapeAnnotationLabel, buildExpandedIds, cvaMapMarker, cvaMarkerIndicator, mockMapApi, useAdaptiveMarkerHelpers, useAutoPanResolver, useCameraIdle, useCameraState, useClusterCountFormat, useControlStack, useControls, useDefaultControls, useDirectionIndicator, useEntitiesNearCursor, useExpandedIds, useFitFeatureBounds, useFitToContent, useImageOverlay, useLabelPlacement, useLayers, useMap, useMapAnnotation, useMapAnnotations, useMapAppearanceControls, useMapKeyboardNavigation, useMarkerColors, useMarkerStateResolvers, useMarkers, usePanel, usePanelPreload, usePreviewMap, useRoute, useShapeLabelHelpers, useShapes, useViewportContext };
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@trackunit/react-map",
3
- "version": "0.2.125",
3
+ "version": "0.2.127",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
7
7
  "node": ">=24.x"
8
8
  },
9
9
  "dependencies": {
10
- "@trackunit/react-components": "2.12.0",
11
- "@trackunit/css-class-variance-utilities": "1.14.28",
12
- "@trackunit/react-form-components": "2.6.47",
13
- "@trackunit/react-core-hooks": "1.21.40",
14
- "@trackunit/geo-json-utils": "1.15.30",
15
- "@trackunit/i18n-library-translation": "2.4.40",
10
+ "@trackunit/react-components": "2.13.1",
11
+ "@trackunit/css-class-variance-utilities": "2.0.0",
12
+ "@trackunit/react-form-components": "2.6.49",
13
+ "@trackunit/react-core-hooks": "1.21.42",
14
+ "@trackunit/geo-json-utils": "1.15.32",
15
+ "@trackunit/i18n-library-translation": "2.4.42",
16
16
  "react-minimal-pie-chart": "^8.4.0",
17
- "@trackunit/react-map-adapter-shared": "0.0.111",
18
- "@trackunit/react-map-color-utils": "0.0.91",
19
- "@trackunit/ui-design-tokens": "1.15.18",
17
+ "@trackunit/react-map-adapter-shared": "0.0.113",
18
+ "@trackunit/react-map-color-utils": "0.0.93",
19
+ "@trackunit/ui-design-tokens": "1.15.20",
20
20
  "@floating-ui/react": "^0.26.25",
21
21
  "es-toolkit": "^1.39.10",
22
22
  "tailwind-merge": "^2.0.0",
@@ -1,7 +1,12 @@
1
1
  import type { AvailableSpace, Compactness, ControlConfig, MenuItemConfig, Placement, RenderContext, ResolvedControlProps, ResolvedControlPropsBase } from "./types";
2
2
  export type { AvailableSpace, Compactness, Placement, RenderContext, ResolvedControlProps, ResolvedControlPropsBase };
3
3
  /** Shared padding for interactive menu items (button, toggle, stepper, select, radioGroup). */
4
- export declare const cvaControlMenuItemBase: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
4
+ export declare const cvaControlMenuItemBase: import("cva").CVAComponent<Omit<{
5
+ base: string[];
6
+ }, "defaultVariants"> & {
7
+ variants: unknown;
8
+ defaultVariants: Omit<{}, never>;
9
+ }, unknown>;
5
10
  /** Context available to all middleware and resolvers */
6
11
  export type MiddlewareContext = Readonly<{
7
12
  renderContext: RenderContext;
package/src/index.d.ts CHANGED
@@ -53,6 +53,7 @@ export { useImageOverlay, type UseImageOverlayOptions, type UseImageOverlayRetur
53
53
  export { buildExpandedIds, useExpandedIds, type MapFocus, type MapFocusTier, type MapFocusTierDisplay, } from "./layers/mapFocus";
54
54
  export { useRoute, type UseRouteOptions, type UseRouteReturn } from "./layers/routes/useRoute";
55
55
  export { useFitToContent, type FitToContentOptions, type FitToContentResult } from "./layers/useFitToContent";
56
+ export { useLabelPlacement, type UseLabelPlacementParams } from "./layers/useLabelPlacement";
56
57
  export { useLayers, type UseLayersReturn } from "./layers/useLayers";
57
58
  export { DEFAULT_MARKER_SIZE_BREAKPOINTS, MARKER_DISC_BORDER_WIDTH_PX, type MarkerSizeBreakpoint, type PickMarkerSizeOptions, type PickRenderMediumOptions, type PickSymbolDiameterOptions, } from "./layers/useMarkers/adaptiveHelpers";
58
59
  export { useMarkers, type UseMarkersOptions, type UseMarkersReturn } from "./layers/useMarkers/useMarkers";
@@ -0,0 +1,29 @@
1
+ import type { GeoJsonBbox } from "@trackunit/geo-json-utils";
2
+ import type { LabelFootprintDeg } from "./internal/labelFootprint";
3
+ type Position = readonly [number, number, ...Array<number>];
4
+ export type BuildLabelPlacementParams<TAsset> = Readonly<{
5
+ /** Candidate markers, in the order they should be considered for a label. */
6
+ items: ReadonlyArray<TAsset>;
7
+ getId: (item: TAsset) => string;
8
+ getPosition: (item: TAsset) => Position;
9
+ /** Current camera view box; candidates outside it are skipped. `null` disables clipping. */
10
+ bounds: Readonly<GeoJsonBbox> | null;
11
+ /** Nominal geo footprint every label is assumed to occupy, for collision testing. */
12
+ footprint: LabelFootprintDeg;
13
+ /** Maximum number of labels to place (DOM-node ceiling). */
14
+ ceiling: number;
15
+ }>;
16
+ /**
17
+ * Chooses which markers render a label so that no two labels overlap.
18
+ *
19
+ * Greedy placement: walks `items` in order, skips any outside the view box,
20
+ * and adds a label only when its nominal footprint does not collide with an
21
+ * already-placed one, stopping at `ceiling`. The budget is emergent — however
22
+ * many non-overlapping labels fit, bounded by the ceiling.
23
+ *
24
+ * Deterministic: identical input yields an identical set. Phase 1 places in the
25
+ * caller-provided order; priority ordering, dispersion, and pin-on-a-stick
26
+ * displacement layer on top in later phases.
27
+ */
28
+ export declare const buildLabelPlacement: <TAsset>({ items, getId, getPosition, bounds, footprint, ceiling, }: BuildLabelPlacementParams<TAsset>) => ReadonlySet<string>;
29
+ export {};
@@ -0,0 +1,37 @@
1
+ export type DegreesPerPixel = Readonly<{
2
+ lng: number;
3
+ lat: number;
4
+ }>;
5
+ /**
6
+ * Degrees of longitude/latitude spanned by a single screen pixel at `zoom`,
7
+ * near `latitudeDeg`, under Web Mercator.
8
+ *
9
+ * Longitude degrees-per-pixel is constant everywhere; latitude degrees-per-pixel
10
+ * scales by `cos(latitude)` because Mercator stretches higher latitudes. Used to
11
+ * convert a pixel label footprint into a geo bounding box so collision runs in
12
+ * the same lng/lat space the marker positions already live in.
13
+ */
14
+ export declare const degreesPerPixel: (zoom: number, latitudeDeg: number) => DegreesPerPixel;
15
+ export type LabelFootprintDeg = Readonly<{
16
+ widthDeg: number;
17
+ heightDeg: number;
18
+ }>;
19
+ export type LabelFootprintParams = Readonly<{
20
+ /** Current camera zoom. */
21
+ zoom: number;
22
+ /** Reference latitude (typically the viewport centre) for the lat-per-pixel scale. */
23
+ latitudeDeg: number;
24
+ /** Extra spacing added around every label — the density dial. */
25
+ paddingPx: number;
26
+ }>;
27
+ /**
28
+ * Upper-bound label footprint as a geo-space width/height, for collision testing.
29
+ *
30
+ * A rendered pill's size does NOT follow the zoom circle-size tier: `MapMarker`
31
+ * lays every pill out at the fixed {@link MARKER_PILL_CONTENT_LAYOUT_SIZE}, and the
32
+ * label text is capped at `maxLabelWidthPx` (ellipsis beyond). We size the collision
33
+ * box to that upper bound — widest pill that can render — so the box is never smaller
34
+ * than what paints, which is what keeps the "no two labels overlap" guarantee honest.
35
+ * Density is tuned via `paddingPx`, not by shrinking the box.
36
+ */
37
+ export declare const labelFootprintDeg: ({ zoom, latitudeDeg, paddingPx }: LabelFootprintParams) => LabelFootprintDeg;
@@ -0,0 +1,31 @@
1
+ import type { GeoJsonBbox } from "@trackunit/geo-json-utils";
2
+ type Position = readonly [number, number, ...Array<number>];
3
+ export type UseLabelPlacementParams<TAsset> = Readonly<{
4
+ /** When false the hook returns a stable empty set without computing placement. */
5
+ enabled: boolean;
6
+ /** Candidate markers, in the order they should be considered for a label. */
7
+ items: ReadonlyArray<TAsset>;
8
+ /** Stable id accessor — pass a referentially stable function (module const or `useCallback`). */
9
+ getId: (item: TAsset) => string;
10
+ /** Stable position accessor — pass a referentially stable function. */
11
+ getPosition: (item: TAsset) => Position;
12
+ /** Current camera view box; candidates outside it are skipped. `null` disables clipping. */
13
+ bounds: Readonly<GeoJsonBbox> | null;
14
+ /** Current camera zoom; drives the px→geo footprint scale. */
15
+ zoom: number;
16
+ /** Spacing added around every label footprint — the density dial. */
17
+ paddingPx: number;
18
+ /** Maximum number of labels to place (DOM-node ceiling). */
19
+ ceiling: number;
20
+ }>;
21
+ /**
22
+ * Viewport-driven label placement: returns the ids of the markers that should
23
+ * render a label such that no two labels overlap, bounded by `ceiling`.
24
+ *
25
+ * Derives an upper-bound pill footprint (`labelFootprintDeg`) and delegates
26
+ * selection to the pure `buildLabelPlacement`. Exposed as a hook so the placement
27
+ * participates in React's stability model and later phases can hold incumbency
28
+ * state here without changing the public surface.
29
+ */
30
+ export declare const useLabelPlacement: <TAsset>({ enabled, items, getId, getPosition, bounds, zoom, paddingPx, ceiling, }: UseLabelPlacementParams<TAsset>) => ReadonlySet<string>;
31
+ export {};
@@ -9,9 +9,28 @@ type ShapeLabelPillProps = Readonly<{
9
9
  "data-testid"?: string;
10
10
  children: ReactNode;
11
11
  }>;
12
- export declare const cvaShapeLabel: (props?: ({
13
- theme?: "light" | "dark" | null | undefined;
14
- } & import("class-variance-authority/types").ClassProp) | undefined) => string;
12
+ export declare const cvaShapeLabel: import("cva").CVAComponent<Omit<{
13
+ base: string[];
14
+ variants: {
15
+ theme: {
16
+ light: string[];
17
+ dark: string[];
18
+ };
19
+ };
20
+ }, "defaultVariants"> & {
21
+ variants: {
22
+ theme: {
23
+ light: string[];
24
+ dark: string[];
25
+ };
26
+ };
27
+ defaultVariants: Omit<{}, never>;
28
+ }, {
29
+ theme: {
30
+ light: string[];
31
+ dark: string[];
32
+ };
33
+ }>;
15
34
  /**
16
35
  * Shared styled container for shape labels (edge labels and annotation labels).
17
36
  *
@@ -12,7 +12,12 @@
12
12
  * properties that need to animate, intentionally excluding `width` to prevent
13
13
  * non-composited layout thrashing. See `MarkerAnimatedSurface.tsx` for details.
14
14
  */
15
- export declare const cvaMapMarker: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
15
+ export declare const cvaMapMarker: import("cva").CVAComponent<Omit<{
16
+ base: string[];
17
+ }, "defaultVariants"> & {
18
+ variants: unknown;
19
+ defaultVariants: Omit<{}, never>;
20
+ }, unknown>;
16
21
  /**
17
22
  * Label span inside pill markers.
18
23
  *
@@ -25,11 +30,62 @@ export declare const cvaMapMarker: (props?: import("class-variance-authority/typ
25
30
  * alone biases the text upward (extra space below). `overflow-x: clip` gives the
26
31
  * horizontal ellipsis without a vertical clip on the span itself.
27
32
  */
28
- export declare const cvaPillLabel: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
33
+ export declare const cvaPillLabel: import("cva").CVAComponent<Omit<{
34
+ base: string[];
35
+ }, "defaultVariants"> & {
36
+ variants: unknown;
37
+ defaultVariants: Omit<{}, never>;
38
+ }, unknown>;
29
39
  /**
30
40
  * Inner indicator disc: marker fill via `--marker-color`, border via CSS `color-mix` on that token (plan §5).
31
41
  */
32
- export declare const cvaMarkerIndicator: (props?: ({
33
- size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
34
- theme?: "light" | "dark" | null | undefined;
35
- } & import("class-variance-authority/types").ClassProp) | undefined) => string;
42
+ export declare const cvaMarkerIndicator: import("cva").CVAComponent<Omit<{
43
+ base: string[];
44
+ variants: {
45
+ size: {
46
+ xs: string[];
47
+ sm: string[];
48
+ md: string[];
49
+ lg: string[];
50
+ xl: string[];
51
+ };
52
+ theme: {
53
+ light: string[];
54
+ dark: string[];
55
+ };
56
+ };
57
+ defaultVariants: {
58
+ size: "md";
59
+ theme: "light";
60
+ };
61
+ }, "defaultVariants"> & {
62
+ variants: {
63
+ size: {
64
+ xs: string[];
65
+ sm: string[];
66
+ md: string[];
67
+ lg: string[];
68
+ xl: string[];
69
+ };
70
+ theme: {
71
+ light: string[];
72
+ dark: string[];
73
+ };
74
+ };
75
+ defaultVariants: Omit<{}, "size" | "theme"> & {
76
+ size: "md";
77
+ theme: "light";
78
+ };
79
+ }, {
80
+ size: {
81
+ xs: string[];
82
+ sm: string[];
83
+ md: string[];
84
+ lg: string[];
85
+ xl: string[];
86
+ };
87
+ theme: {
88
+ light: string[];
89
+ dark: string[];
90
+ };
91
+ }>;
@@ -1,8 +1,57 @@
1
1
  /** Pads numeric string: 3-digit int part + reserved sign column to avoid layout shift when sign flips. */
2
2
  export declare const formatPaddedNumber: (n: number, decimals: number) => string;
3
- export declare const cvaCoordCell: (props?: ({
4
- variant?: "lng" | "lat" | "we" | "sn" | null | undefined;
5
- } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
- export declare const cvaStateDisplay: (props?: ({
7
- expanded?: boolean | null | undefined;
8
- } & import("class-variance-authority/types").ClassProp) | undefined) => string;
3
+ export declare const cvaCoordCell: import("cva").CVAComponent<Omit<{
4
+ base: string[];
5
+ variants: {
6
+ variant: {
7
+ lng: string[];
8
+ lat: string[];
9
+ we: string[];
10
+ sn: string[];
11
+ };
12
+ };
13
+ }, "defaultVariants"> & {
14
+ variants: {
15
+ variant: {
16
+ lng: string[];
17
+ lat: string[];
18
+ we: string[];
19
+ sn: string[];
20
+ };
21
+ };
22
+ defaultVariants: Omit<{}, never>;
23
+ }, {
24
+ variant: {
25
+ lng: string[];
26
+ lat: string[];
27
+ we: string[];
28
+ sn: string[];
29
+ };
30
+ }>;
31
+ export declare const cvaStateDisplay: import("cva").CVAComponent<Omit<{
32
+ base: string[];
33
+ variants: {
34
+ expanded: {
35
+ true: string[];
36
+ false: string[];
37
+ };
38
+ };
39
+ defaultVariants: {
40
+ expanded: true;
41
+ };
42
+ }, "defaultVariants"> & {
43
+ variants: {
44
+ expanded: {
45
+ true: string[];
46
+ false: string[];
47
+ };
48
+ };
49
+ defaultVariants: Omit<{}, "expanded"> & {
50
+ expanded: true;
51
+ };
52
+ }, {
53
+ expanded: {
54
+ true: string[];
55
+ false: string[];
56
+ };
57
+ }>;
@@ -1 +0,0 @@
1
- {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../../libs/react/map/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["export {};\n"]}