@trackunit/react-map 0.1.1-alpha-84ab66282fc.0 → 0.1.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.
- package/index.cjs.js +65 -34
- package/index.esm.js +66 -35
- package/package.json +10 -10
- package/src/controls/ControlRenderer.d.ts +3 -1
- package/src/controls/controlOverlaySide.d.ts +4 -0
- package/src/controls/renderers/boolean/BooleanButton.d.ts +3 -1
- package/src/controls/renderers/button/Button.d.ts +3 -1
- package/src/controls/renderers/custom/Custom.d.ts +3 -1
- package/src/controls/renderers/menu/Menu.d.ts +3 -1
- package/src/controls/renderers/radio-group/RadioGroup.d.ts +3 -1
- package/src/controls/renderers/search/Search.d.ts +3 -1
- package/src/controls/renderers/select/Select.d.ts +3 -1
- package/src/controls/renderers/shared/StandaloneControlTooltip.d.ts +14 -0
- package/src/controls/renderers/stepper/Stepper.d.ts +3 -1
- package/src/controls/renderers/toggle-group/ToggleGroup.d.ts +3 -1
- package/src/controls/types.d.ts +2 -2
package/index.cjs.js
CHANGED
|
@@ -1670,6 +1670,20 @@ const collapseControls = (config, mode, metadata) => {
|
|
|
1670
1670
|
return { navigation, settings, tools, statistics: config.statistics };
|
|
1671
1671
|
};
|
|
1672
1672
|
|
|
1673
|
+
const RIGHT_CONTROL_POPOVER_PLACEMENT = "right-start";
|
|
1674
|
+
const RIGHT_CONTROL_TOOLTIP_PLACEMENT = "right";
|
|
1675
|
+
|
|
1676
|
+
/**
|
|
1677
|
+
* Wraps standalone map controls with a right-aligned tooltip when `overlaySide` is `"right"`.
|
|
1678
|
+
* Callers should omit `title` on {@link IconButton} in that mode so tooltips do not stack.
|
|
1679
|
+
*/
|
|
1680
|
+
const StandaloneControlTooltip = ({ children, disabled = false, label, overlaySide = undefined, }) => {
|
|
1681
|
+
if (overlaySide !== "right") {
|
|
1682
|
+
return children;
|
|
1683
|
+
}
|
|
1684
|
+
return (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, disabled: disabled, interactable: false, label: label, placement: RIGHT_CONTROL_TOOLTIP_PLACEMENT, children: children }));
|
|
1685
|
+
};
|
|
1686
|
+
|
|
1673
1687
|
/**
|
|
1674
1688
|
* Shared standalone renderer for boolean controls (toggle and checkbox).
|
|
1675
1689
|
* Both render identically as an IconButton with tooltip in standalone mode.
|
|
@@ -1678,7 +1692,7 @@ const collapseControls = (config, mode, metadata) => {
|
|
|
1678
1692
|
*
|
|
1679
1693
|
* @internal
|
|
1680
1694
|
*/
|
|
1681
|
-
const BooleanButton = ({ config, resolved }) => {
|
|
1695
|
+
const BooleanButton = ({ config, overlaySide = undefined, resolved }) => {
|
|
1682
1696
|
const { className, "data-testid": dataTestId, label, onChange: onClickChange, value } = config;
|
|
1683
1697
|
const { icon, size } = resolved;
|
|
1684
1698
|
const [t] = useTranslation();
|
|
@@ -1686,7 +1700,8 @@ const BooleanButton = ({ config, resolved }) => {
|
|
|
1686
1700
|
return null;
|
|
1687
1701
|
}
|
|
1688
1702
|
const tooltipLabel = t("controls.boolean.tooltip", { label });
|
|
1689
|
-
|
|
1703
|
+
const useRightOverlay = overlaySide === "right";
|
|
1704
|
+
return (jsxRuntime.jsx(StandaloneControlTooltip, { label: tooltipLabel, overlaySide: overlaySide, children: jsxRuntime.jsx(reactComponents.IconButton, { ...(useRightOverlay ? { ariaLabel: label } : { title: tooltipLabel }), "aria-pressed": value, className: className, "data-testid": dataTestId, icon: jsxRuntime.jsx(reactComponents.Icon, { name: icon, size: "small" }), onClick: () => onClickChange(!value), size: size, variant: value ? "primary" : "secondary" }) }));
|
|
1690
1705
|
};
|
|
1691
1706
|
|
|
1692
1707
|
/**
|
|
@@ -1695,13 +1710,14 @@ const BooleanButton = ({ config, resolved }) => {
|
|
|
1695
1710
|
*
|
|
1696
1711
|
* @internal
|
|
1697
1712
|
*/
|
|
1698
|
-
const Button = ({ config, resolved }) => {
|
|
1713
|
+
const Button = ({ config, overlaySide = undefined, resolved }) => {
|
|
1699
1714
|
const { className, "data-testid": dataTestId, label, onClick } = config;
|
|
1700
1715
|
const { icon, size } = resolved;
|
|
1701
1716
|
if (icon === undefined) {
|
|
1702
1717
|
return null;
|
|
1703
1718
|
}
|
|
1704
|
-
|
|
1719
|
+
const useRightOverlay = overlaySide === "right";
|
|
1720
|
+
return (jsxRuntime.jsx(StandaloneControlTooltip, { label: label, overlaySide: overlaySide, children: jsxRuntime.jsx(reactComponents.IconButton, { ...(useRightOverlay ? { ariaLabel: label } : { title: label }), className: className, "data-testid": dataTestId, icon: jsxRuntime.jsx(reactComponents.Icon, { name: icon, size: "small" }), onClick: onClick, size: size, variant: "secondary" }) }));
|
|
1705
1721
|
};
|
|
1706
1722
|
|
|
1707
1723
|
/**
|
|
@@ -1717,7 +1733,7 @@ const Button = ({ config, resolved }) => {
|
|
|
1717
1733
|
*
|
|
1718
1734
|
* @internal
|
|
1719
1735
|
*/
|
|
1720
|
-
const Custom = ({ config, resolved, placement }) => {
|
|
1736
|
+
const Custom = ({ config, overlaySide = undefined, resolved, placement }) => {
|
|
1721
1737
|
if (!resolved.visible) {
|
|
1722
1738
|
return null;
|
|
1723
1739
|
}
|
|
@@ -1730,7 +1746,7 @@ const Custom = ({ config, resolved, placement }) => {
|
|
|
1730
1746
|
// A Fragment cannot accept those props — use an inert wrapper with `contents` layout.
|
|
1731
1747
|
const wrapped = jsxRuntime.jsx("span", { className: "contents", children: content });
|
|
1732
1748
|
const useStandaloneTooltip = placement === "standalone" && (config.autoTooltip ?? "standalone") === "standalone";
|
|
1733
|
-
return useStandaloneTooltip ? (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: config.label, children: wrapped })) : (wrapped);
|
|
1749
|
+
return useStandaloneTooltip ? (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: config.label, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: wrapped })) : (wrapped);
|
|
1734
1750
|
};
|
|
1735
1751
|
|
|
1736
1752
|
/** Shared padding for interactive menu items (button, toggle, stepper, select, radioGroup). */
|
|
@@ -2231,14 +2247,26 @@ const MenuContent = ({ availableSpace, items, presentation }) => {
|
|
|
2231
2247
|
const groups = groupItemsBySection(items);
|
|
2232
2248
|
return (jsxRuntime.jsx("div", { className: presentation === "popover" ? "rounded-lg border border-neutral-300 bg-white py-1 shadow" : "py-1", role: "menu", children: groups.map((group, groupIndex) => (jsxRuntime.jsxs(react.Fragment, { children: [group.section !== undefined ? jsxRuntime.jsx(SectionHeader, { title: group.section }) : null, group.items.map((item, itemIndex) => renderMenuItem(item, groupIndex * 100 + itemIndex, availableSpace))] }, groupIndex))) }));
|
|
2233
2249
|
};
|
|
2250
|
+
/** Popover opens right; tooltip is suppressed while the menu is open. */
|
|
2251
|
+
const MenuRightPopover = ({ availableSpace, config, customPortalId, resolved, }) => {
|
|
2252
|
+
const { className, "data-testid": dataTestId, items, label } = config;
|
|
2253
|
+
const isFullScreen = reactComponents.useIsFullscreen();
|
|
2254
|
+
return (jsxRuntime.jsx("div", { className: className ?? "w-fit", children: jsxRuntime.jsx(reactComponents.Popover, { placement: RIGHT_CONTROL_POPOVER_PLACEMENT, children: ({ isOpen }) => (jsxRuntime.jsxs(react.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, disabled: isOpen, interactable: false, label: label, placement: RIGHT_CONTROL_TOOLTIP_PLACEMENT, children: jsxRuntime.jsx(reactComponents.IconButton, { ariaLabel: label, "data-testid": dataTestId, icon: jsxRuntime.jsx(reactComponents.Icon, { name: resolved.icon ?? "QuestionMarkCircle", size: "small" }), size: resolved.size, variant: "secondary" }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { portalId: isFullScreen ? customPortalId : undefined, children: jsxRuntime.jsx(MenuContent, { availableSpace: availableSpace, items: items, presentation: "popover" }) })] })) }) }));
|
|
2255
|
+
};
|
|
2234
2256
|
/** Popover presentation -- renders a MoreMenu with position-aware placement. */
|
|
2235
|
-
const
|
|
2257
|
+
const MenuAutoPopover = ({ availableSpace, config, containerRef, customPortalId, resolved, }) => {
|
|
2236
2258
|
const { className, "data-testid": dataTestId, items, label } = config;
|
|
2237
2259
|
const isFullScreen = reactComponents.useIsFullscreen();
|
|
2238
2260
|
const triggerRef = react.useRef(null);
|
|
2239
2261
|
const popoverPlacement = usePopoverPlacement(triggerRef, containerRef);
|
|
2240
2262
|
return (jsxRuntime.jsx("div", { className: "w-fit", ref: triggerRef, children: jsxRuntime.jsx(reactComponents.MoreMenu, { className: className, customButton: jsxRuntime.jsx(reactComponents.IconButton, { "data-testid": dataTestId, icon: jsxRuntime.jsx(reactComponents.Icon, { name: resolved.icon ?? "QuestionMarkCircle", size: "small" }), size: resolved.size, title: label, variant: "secondary" }), customPortalId: isFullScreen ? customPortalId : undefined, "data-testid": dataTestId, popoverProps: { placement: popoverPlacement }, children: jsxRuntime.jsx(MenuContent, { availableSpace: availableSpace, items: items, presentation: "popover" }) }) }));
|
|
2241
2263
|
};
|
|
2264
|
+
const MenuPopover = ({ availableSpace, config, containerRef, customPortalId, overlaySide = undefined, resolved, }) => {
|
|
2265
|
+
if (overlaySide === "right") {
|
|
2266
|
+
return (jsxRuntime.jsx(MenuRightPopover, { availableSpace: availableSpace, config: config, customPortalId: customPortalId, resolved: resolved }));
|
|
2267
|
+
}
|
|
2268
|
+
return (jsxRuntime.jsx(MenuAutoPopover, { availableSpace: availableSpace, config: config, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2269
|
+
};
|
|
2242
2270
|
/** Modal presentation -- renders a Modal with no popover-specific hooks. */
|
|
2243
2271
|
const MenuModal = ({ availableSpace, config, resolved }) => (jsxRuntime.jsx(Modal, { config: config, resolved: resolved, children: jsxRuntime.jsx(MenuContent, { availableSpace: availableSpace, items: config.items, presentation: "modal" }) }));
|
|
2244
2272
|
/**
|
|
@@ -2250,11 +2278,11 @@ const MenuModal = ({ availableSpace, config, resolved }) => (jsxRuntime.jsx(Moda
|
|
|
2250
2278
|
*
|
|
2251
2279
|
* @internal
|
|
2252
2280
|
*/
|
|
2253
|
-
const Menu = ({ availableSpace, config, containerRef, customPortalId, menuPresentation, resolved, }) => {
|
|
2281
|
+
const Menu = ({ availableSpace, config, containerRef, customPortalId, menuPresentation, overlaySide = undefined, resolved, }) => {
|
|
2254
2282
|
if (menuPresentation === "modal") {
|
|
2255
2283
|
return jsxRuntime.jsx(MenuModal, { availableSpace: availableSpace, config: config, resolved: resolved });
|
|
2256
2284
|
}
|
|
2257
|
-
return (jsxRuntime.jsx(MenuPopover, { availableSpace: availableSpace, config: config, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2285
|
+
return (jsxRuntime.jsx(MenuPopover, { availableSpace: availableSpace, config: config, containerRef: containerRef, customPortalId: customPortalId, overlaySide: overlaySide, resolved: resolved }));
|
|
2258
2286
|
};
|
|
2259
2287
|
|
|
2260
2288
|
const fieldConfigForStandalonePopover = (config) => {
|
|
@@ -2272,13 +2300,16 @@ const fieldConfigForStandalonePopover = (config) => {
|
|
|
2272
2300
|
const RadioGroupPanel = ({ fieldConfig }) => {
|
|
2273
2301
|
return (jsxRuntime.jsx("div", { className: "rounded-lg border border-neutral-300 bg-white px-3 py-2 shadow", children: jsxRuntime.jsx(RadioGroupField, { config: fieldConfig }) }));
|
|
2274
2302
|
};
|
|
2275
|
-
const RadioGroupPopover = ({ config, containerRef, customPortalId, resolved }) => {
|
|
2303
|
+
const RadioGroupPopover = ({ config, containerRef, customPortalId, overlaySide = undefined, resolved, }) => {
|
|
2276
2304
|
const { className, "data-testid": dataTestId, label } = config;
|
|
2277
2305
|
const isFullScreen = reactComponents.useIsFullscreen();
|
|
2278
2306
|
const triggerRef = react.useRef(null);
|
|
2279
|
-
|
|
2280
|
-
const
|
|
2281
|
-
|
|
2307
|
+
const popoverPlacement = usePopoverPlacement(triggerRef, containerRef, { skip: overlaySide === "right" });
|
|
2308
|
+
const fieldConfig = fieldConfigForStandalonePopover(config);
|
|
2309
|
+
if (overlaySide === "right") {
|
|
2310
|
+
return (jsxRuntime.jsx("div", { className: "w-fit", ref: triggerRef, children: jsxRuntime.jsx(reactComponents.Popover, { placement: RIGHT_CONTROL_POPOVER_PLACEMENT, children: ({ isOpen }) => (jsxRuntime.jsxs(react.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, disabled: isOpen, interactable: false, label: label, placement: RIGHT_CONTROL_TOOLTIP_PLACEMENT, children: jsxRuntime.jsx(reactComponents.IconButton, { ariaLabel: label, "data-testid": dataTestId, icon: jsxRuntime.jsx(reactComponents.Icon, { name: resolved.icon ?? "EllipsisHorizontal", size: "small" }), size: resolved.size, variant: "secondary" }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { portalId: isFullScreen ? customPortalId : undefined, children: jsxRuntime.jsx(RadioGroupPanel, { fieldConfig: fieldConfig }) })] })) }) }));
|
|
2311
|
+
}
|
|
2312
|
+
return (jsxRuntime.jsx("div", { className: "w-fit", ref: triggerRef, children: jsxRuntime.jsx(reactComponents.MoreMenu, { className: className, customButton: jsxRuntime.jsx(reactComponents.IconButton, { "data-testid": dataTestId, icon: jsxRuntime.jsx(reactComponents.Icon, { name: resolved.icon ?? "EllipsisHorizontal", size: "small" }), size: resolved.size, title: label, variant: "secondary" }), customPortalId: isFullScreen ? customPortalId : undefined, "data-testid": dataTestId, popoverProps: { placement: popoverPlacement }, children: jsxRuntime.jsx(RadioGroupPanel, { fieldConfig: fieldConfig }) }) }));
|
|
2282
2313
|
};
|
|
2283
2314
|
/**
|
|
2284
2315
|
* Standalone radio group: icon button opens a popover (`MoreMenu`).
|
|
@@ -2288,8 +2319,8 @@ const RadioGroupPopover = ({ config, containerRef, customPortalId, resolved }) =
|
|
|
2288
2319
|
*
|
|
2289
2320
|
* @internal
|
|
2290
2321
|
*/
|
|
2291
|
-
const RadioGroup = ({ config, containerRef, customPortalId, resolved }) => {
|
|
2292
|
-
return (jsxRuntime.jsx(RadioGroupPopover, { config: config, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2322
|
+
const RadioGroup = ({ config, containerRef, customPortalId, overlaySide = undefined, resolved, }) => {
|
|
2323
|
+
return (jsxRuntime.jsx(RadioGroupPopover, { config: config, containerRef: containerRef, customPortalId: customPortalId, overlaySide: overlaySide, resolved: resolved }));
|
|
2293
2324
|
};
|
|
2294
2325
|
|
|
2295
2326
|
/**
|
|
@@ -2298,10 +2329,10 @@ const RadioGroup = ({ config, containerRef, customPortalId, resolved }) => {
|
|
|
2298
2329
|
*
|
|
2299
2330
|
* @internal
|
|
2300
2331
|
*/
|
|
2301
|
-
const Search = ({ config, resolved }) => {
|
|
2332
|
+
const Search = ({ config, overlaySide = undefined, resolved }) => {
|
|
2302
2333
|
const { className, "data-testid": dataTestId, id, label, onChange, onClear, placeholder, value } = config;
|
|
2303
2334
|
const { size } = resolved;
|
|
2304
|
-
return (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: label, children: jsxRuntime.jsx(reactFormComponents.Search, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, onChange: event => {
|
|
2335
|
+
return (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: label, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: jsxRuntime.jsx(reactFormComponents.Search, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, onChange: event => {
|
|
2305
2336
|
onChange(event.target.value);
|
|
2306
2337
|
}, onClear: onClear, placeholder: placeholder ?? label, value: value }) }));
|
|
2307
2338
|
};
|
|
@@ -2312,12 +2343,12 @@ const Search = ({ config, resolved }) => {
|
|
|
2312
2343
|
*
|
|
2313
2344
|
* @internal
|
|
2314
2345
|
*/
|
|
2315
|
-
const Select = ({ config, resolved }) => {
|
|
2346
|
+
const Select = ({ config, overlaySide = undefined, resolved }) => {
|
|
2316
2347
|
const { className, "data-testid": dataTestId, id, label, onChange, options, value } = config;
|
|
2317
2348
|
const { size } = resolved;
|
|
2318
2349
|
const selectOptions = [...options];
|
|
2319
2350
|
const selectedOption = selectOptions.find(option => option.value === value) ?? null;
|
|
2320
|
-
return (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: label, children: jsxRuntime.jsx(reactFormComponents.BaseSelect, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, isSearchable: false, onChange: option => {
|
|
2351
|
+
return (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: label, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: jsxRuntime.jsx(reactFormComponents.BaseSelect, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, isSearchable: false, onChange: option => {
|
|
2321
2352
|
if (option !== null) {
|
|
2322
2353
|
onChange(option.value);
|
|
2323
2354
|
}
|
|
@@ -2334,7 +2365,7 @@ const Select = ({ config, resolved }) => {
|
|
|
2334
2365
|
*
|
|
2335
2366
|
* @internal
|
|
2336
2367
|
*/
|
|
2337
|
-
const Stepper = ({ config, resolved }) => {
|
|
2368
|
+
const Stepper = ({ config, overlaySide = undefined, resolved }) => {
|
|
2338
2369
|
const { className, "data-testid": dataTestId, decrement: onClickDecrement, increment: onClickIncrement, label, } = config;
|
|
2339
2370
|
const { size } = resolved;
|
|
2340
2371
|
const [t] = useTranslation();
|
|
@@ -2347,7 +2378,7 @@ const Stepper = ({ config, resolved }) => {
|
|
|
2347
2378
|
: decrementHover.hovering
|
|
2348
2379
|
? `${label}, ${decreaseLabel}`
|
|
2349
2380
|
: label;
|
|
2350
|
-
return (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: tooltipLabel, children: jsxRuntime.jsxs("div", { className: tailwindMerge.twMerge("grid", className), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.IconButton, { "aria-label": increaseLabel, className: "rounded-b-none", icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Plus", size: "small" }), onClick: onClickIncrement, onMouseEnter: incrementHover.onMouseEnter, onMouseLeave: incrementHover.onMouseLeave, size: size, title: increaseLabel, variant: "secondary" }), jsxRuntime.jsx(reactComponents.IconButton, { "aria-label": decreaseLabel, className: "translate-y-[-1px] rounded-t-none", icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Minus", size: "small" }), onClick: onClickDecrement, onMouseEnter: decrementHover.onMouseEnter, onMouseLeave: decrementHover.onMouseLeave, size: size, title: decreaseLabel, variant: "secondary" })] }) }));
|
|
2381
|
+
return (jsxRuntime.jsx(reactComponents.Tooltip, { delayed: true, label: tooltipLabel, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: jsxRuntime.jsxs("div", { className: tailwindMerge.twMerge("grid", className), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.IconButton, { "aria-label": increaseLabel, className: "rounded-b-none", icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Plus", size: "small" }), onClick: onClickIncrement, onMouseEnter: incrementHover.onMouseEnter, onMouseLeave: incrementHover.onMouseLeave, size: size, title: increaseLabel, variant: "secondary" }), jsxRuntime.jsx(reactComponents.IconButton, { "aria-label": decreaseLabel, className: "translate-y-[-1px] rounded-t-none", icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Minus", size: "small" }), onClick: onClickDecrement, onMouseEnter: decrementHover.onMouseEnter, onMouseLeave: decrementHover.onMouseLeave, size: size, title: decreaseLabel, variant: "secondary" })] }) }));
|
|
2351
2382
|
};
|
|
2352
2383
|
|
|
2353
2384
|
function assertStandaloneToggleGroupIcons(config) {
|
|
@@ -2364,7 +2395,7 @@ function assertStandaloneToggleGroupIcons(config) {
|
|
|
2364
2395
|
*
|
|
2365
2396
|
* @internal
|
|
2366
2397
|
*/
|
|
2367
|
-
const ToggleGroup = ({ config, resolved }) => {
|
|
2398
|
+
const ToggleGroup = ({ config, overlaySide: _overlaySide = undefined, resolved }) => {
|
|
2368
2399
|
const { className, "data-testid": dataTestId, disabled, label, onChange, options, value } = config;
|
|
2369
2400
|
assertStandaloneToggleGroupIcons(config);
|
|
2370
2401
|
return (jsxRuntime.jsx("div", { "aria-label": label, className: tailwindMerge.twMerge("w-fit", className), "data-testid": dataTestId, role: "group", children: jsxRuntime.jsx("div", { className: "relative after:pointer-events-none after:absolute after:inset-0 after:rounded-md after:ring-1 after:ring-inset after:ring-neutral-300 after:content-['']", children: jsxRuntime.jsx(ToggleGroupField, { config: {
|
|
@@ -2382,28 +2413,28 @@ const ToggleGroup = ({ config, resolved }) => {
|
|
|
2382
2413
|
*
|
|
2383
2414
|
* @internal
|
|
2384
2415
|
*/
|
|
2385
|
-
const ControlRenderer = ({ availableSpace, containerRef, control, customPortalId, menuPresentation, }) => {
|
|
2416
|
+
const ControlRenderer = ({ availableSpace, containerRef, control, customPortalId, menuPresentation, overlaySide = undefined, }) => {
|
|
2386
2417
|
const resolved = resolveControl(control, { placement: "standalone", availableSpace });
|
|
2387
2418
|
switch (control.type) {
|
|
2388
2419
|
case "button":
|
|
2389
|
-
return jsxRuntime.jsx(Button, { config: control, resolved: resolved });
|
|
2420
|
+
return jsxRuntime.jsx(Button, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2390
2421
|
case "stepper":
|
|
2391
|
-
return jsxRuntime.jsx(Stepper, { config: control, resolved: resolved });
|
|
2422
|
+
return jsxRuntime.jsx(Stepper, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2392
2423
|
case "toggle":
|
|
2393
2424
|
case "checkbox":
|
|
2394
|
-
return jsxRuntime.jsx(BooleanButton, { config: control, resolved: resolved });
|
|
2425
|
+
return jsxRuntime.jsx(BooleanButton, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2395
2426
|
case "select":
|
|
2396
|
-
return jsxRuntime.jsx(Select, { config: control, resolved: resolved });
|
|
2427
|
+
return jsxRuntime.jsx(Select, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2397
2428
|
case "search":
|
|
2398
|
-
return jsxRuntime.jsx(Search, { config: control, resolved: resolved });
|
|
2429
|
+
return jsxRuntime.jsx(Search, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2399
2430
|
case "radioGroup":
|
|
2400
|
-
return (jsxRuntime.jsx(RadioGroup, { config: control, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2431
|
+
return (jsxRuntime.jsx(RadioGroup, { config: control, containerRef: containerRef, customPortalId: customPortalId, overlaySide: overlaySide, resolved: resolved }));
|
|
2401
2432
|
case "toggleGroup":
|
|
2402
|
-
return jsxRuntime.jsx(ToggleGroup, { config: control, resolved: resolved });
|
|
2433
|
+
return jsxRuntime.jsx(ToggleGroup, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2403
2434
|
case "menu":
|
|
2404
|
-
return (jsxRuntime.jsx(Menu, { availableSpace: availableSpace, config: control, containerRef: containerRef, customPortalId: customPortalId, menuPresentation: menuPresentation, resolved: resolved }));
|
|
2435
|
+
return (jsxRuntime.jsx(Menu, { availableSpace: availableSpace, config: control, containerRef: containerRef, customPortalId: customPortalId, menuPresentation: menuPresentation, overlaySide: overlaySide, resolved: resolved }));
|
|
2405
2436
|
case "custom":
|
|
2406
|
-
return jsxRuntime.jsx(Custom, { config: control, placement: "standalone", resolved: resolved });
|
|
2437
|
+
return jsxRuntime.jsx(Custom, { config: control, overlaySide: overlaySide, placement: "standalone", resolved: resolved });
|
|
2407
2438
|
default: {
|
|
2408
2439
|
const exhaustiveCheck = control;
|
|
2409
2440
|
throw new Error(`Unknown control type: ${exhaustiveCheck}`);
|
|
@@ -2477,10 +2508,10 @@ const ControlsContent = ({ api, controls: baseControlsConfig, className }) => {
|
|
|
2477
2508
|
"pointer-events-none",
|
|
2478
2509
|
// Layout grid for semantic areas
|
|
2479
2510
|
"grid h-full w-full",
|
|
2480
|
-
// Responsive grid: navigation bottom-right, settings top-left, statistics bottom-left
|
|
2511
|
+
// Responsive grid: navigation bottom-right, settings top-left, tools top-right, statistics bottom-left
|
|
2481
2512
|
"grid-cols-[1fr_auto] grid-rows-[auto_1fr_auto]",
|
|
2482
2513
|
// Isolate creates a new stacking context to avoid z-index issues
|
|
2483
|
-
"isolate", className), "data-testid": "map-controls", ref: mergedRef, children: [
|
|
2514
|
+
"isolate", className), "data-testid": "map-controls", ref: mergedRef, children: [hasNavigationControls ? (jsxRuntime.jsx("div", { className: "pointer-events-auto col-start-2 row-start-3 flex flex-col gap-1 self-end justify-self-end", children: navigation.controls.map(control => (jsxRuntime.jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation }, control.id))) })) : null, hasSettingsControls ? (jsxRuntime.jsx("div", { className: "pointer-events-auto col-start-1 row-start-1 flex w-fit flex-col gap-1", "data-testid": "map-controls-settings", children: settings.controls.map(control => (jsxRuntime.jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation, overlaySide: "right" }, control.id))) })) : null, hasToolsControls ? (jsxRuntime.jsx("div", { className: "pointer-events-auto col-start-2 row-start-1 flex flex-col gap-1 justify-self-end", "data-testid": "map-controls-tools", children: tools.controls.map(control => (jsxRuntime.jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation }, control.id))) })) : null, hasStatisticsAreaContent ? (jsxRuntime.jsxs("div", { className: tailwindMerge.twMerge("pointer-events-none col-start-1 row-start-3 flex max-h-full max-w-[20rem] flex-col items-start justify-end gap-1 overflow-visible", !hasStatisticsControls && "self-end"), "data-testid": "map-controls-statistics", children: [shouldRenderAnnotations ? (jsxRuntime.jsx("div", { className: "pointer-events-auto", "data-testid": "map-controls-statistics-annotations", children: jsxRuntime.jsx(AnnotationStack, { annotations: displayAnnotations, isExiting: isExiting }) })) : null, hasStatisticsControls ? (jsxRuntime.jsx("div", { className: "pointer-events-auto flex max-h-full w-fit max-w-full flex-col gap-1 overflow-auto", "data-testid": "map-controls-statistics-controls", children: statistics.controls.map(control => (jsxRuntime.jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation }, control.id))) })) : null] })) : null] }));
|
|
2484
2515
|
};
|
|
2485
2516
|
/**
|
|
2486
2517
|
* Map controls renderer — lays out a `ControlsConfig` across the four semantic
|
package/index.esm.js
CHANGED
|
@@ -4,7 +4,7 @@ import { INITIAL_CAMERA_STATE, INITIAL_MAP_STATUS, KEYBOARD_PAN_AMOUNT, KEYBOARD
|
|
|
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
6
|
import { cvaMerge } from '@trackunit/css-class-variance-utilities';
|
|
7
|
-
import { useMergeRefs, IconButton, Icon, Spinner, Text, useMeasure, useContainerBreakpoints, useDebounce, useWatch, useKeyboardShortcut, Tooltip, ToggleGroup as ToggleGroup$1, useIsFullscreen, MoreMenu, useHover as useHover$1, cvaInteractableItem, useLocalStorageReducer } from '@trackunit/react-components';
|
|
7
|
+
import { useMergeRefs, IconButton, Icon, Spinner, Text, useMeasure, useContainerBreakpoints, useDebounce, useWatch, useKeyboardShortcut, Tooltip, 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';
|
|
10
10
|
import { createPortal } from 'react-dom';
|
|
@@ -1669,6 +1669,20 @@ const collapseControls = (config, mode, metadata) => {
|
|
|
1669
1669
|
return { navigation, settings, tools, statistics: config.statistics };
|
|
1670
1670
|
};
|
|
1671
1671
|
|
|
1672
|
+
const RIGHT_CONTROL_POPOVER_PLACEMENT = "right-start";
|
|
1673
|
+
const RIGHT_CONTROL_TOOLTIP_PLACEMENT = "right";
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Wraps standalone map controls with a right-aligned tooltip when `overlaySide` is `"right"`.
|
|
1677
|
+
* Callers should omit `title` on {@link IconButton} in that mode so tooltips do not stack.
|
|
1678
|
+
*/
|
|
1679
|
+
const StandaloneControlTooltip = ({ children, disabled = false, label, overlaySide = undefined, }) => {
|
|
1680
|
+
if (overlaySide !== "right") {
|
|
1681
|
+
return children;
|
|
1682
|
+
}
|
|
1683
|
+
return (jsx(Tooltip, { delayed: true, disabled: disabled, interactable: false, label: label, placement: RIGHT_CONTROL_TOOLTIP_PLACEMENT, children: children }));
|
|
1684
|
+
};
|
|
1685
|
+
|
|
1672
1686
|
/**
|
|
1673
1687
|
* Shared standalone renderer for boolean controls (toggle and checkbox).
|
|
1674
1688
|
* Both render identically as an IconButton with tooltip in standalone mode.
|
|
@@ -1677,7 +1691,7 @@ const collapseControls = (config, mode, metadata) => {
|
|
|
1677
1691
|
*
|
|
1678
1692
|
* @internal
|
|
1679
1693
|
*/
|
|
1680
|
-
const BooleanButton = ({ config, resolved }) => {
|
|
1694
|
+
const BooleanButton = ({ config, overlaySide = undefined, resolved }) => {
|
|
1681
1695
|
const { className, "data-testid": dataTestId, label, onChange: onClickChange, value } = config;
|
|
1682
1696
|
const { icon, size } = resolved;
|
|
1683
1697
|
const [t] = useTranslation();
|
|
@@ -1685,7 +1699,8 @@ const BooleanButton = ({ config, resolved }) => {
|
|
|
1685
1699
|
return null;
|
|
1686
1700
|
}
|
|
1687
1701
|
const tooltipLabel = t("controls.boolean.tooltip", { label });
|
|
1688
|
-
|
|
1702
|
+
const useRightOverlay = overlaySide === "right";
|
|
1703
|
+
return (jsx(StandaloneControlTooltip, { label: tooltipLabel, overlaySide: overlaySide, children: jsx(IconButton, { ...(useRightOverlay ? { ariaLabel: label } : { title: tooltipLabel }), "aria-pressed": value, className: className, "data-testid": dataTestId, icon: jsx(Icon, { name: icon, size: "small" }), onClick: () => onClickChange(!value), size: size, variant: value ? "primary" : "secondary" }) }));
|
|
1689
1704
|
};
|
|
1690
1705
|
|
|
1691
1706
|
/**
|
|
@@ -1694,13 +1709,14 @@ const BooleanButton = ({ config, resolved }) => {
|
|
|
1694
1709
|
*
|
|
1695
1710
|
* @internal
|
|
1696
1711
|
*/
|
|
1697
|
-
const Button = ({ config, resolved }) => {
|
|
1712
|
+
const Button = ({ config, overlaySide = undefined, resolved }) => {
|
|
1698
1713
|
const { className, "data-testid": dataTestId, label, onClick } = config;
|
|
1699
1714
|
const { icon, size } = resolved;
|
|
1700
1715
|
if (icon === undefined) {
|
|
1701
1716
|
return null;
|
|
1702
1717
|
}
|
|
1703
|
-
|
|
1718
|
+
const useRightOverlay = overlaySide === "right";
|
|
1719
|
+
return (jsx(StandaloneControlTooltip, { label: label, overlaySide: overlaySide, children: jsx(IconButton, { ...(useRightOverlay ? { ariaLabel: label } : { title: label }), className: className, "data-testid": dataTestId, icon: jsx(Icon, { name: icon, size: "small" }), onClick: onClick, size: size, variant: "secondary" }) }));
|
|
1704
1720
|
};
|
|
1705
1721
|
|
|
1706
1722
|
/**
|
|
@@ -1716,7 +1732,7 @@ const Button = ({ config, resolved }) => {
|
|
|
1716
1732
|
*
|
|
1717
1733
|
* @internal
|
|
1718
1734
|
*/
|
|
1719
|
-
const Custom = ({ config, resolved, placement }) => {
|
|
1735
|
+
const Custom = ({ config, overlaySide = undefined, resolved, placement }) => {
|
|
1720
1736
|
if (!resolved.visible) {
|
|
1721
1737
|
return null;
|
|
1722
1738
|
}
|
|
@@ -1729,7 +1745,7 @@ const Custom = ({ config, resolved, placement }) => {
|
|
|
1729
1745
|
// A Fragment cannot accept those props — use an inert wrapper with `contents` layout.
|
|
1730
1746
|
const wrapped = jsx("span", { className: "contents", children: content });
|
|
1731
1747
|
const useStandaloneTooltip = placement === "standalone" && (config.autoTooltip ?? "standalone") === "standalone";
|
|
1732
|
-
return useStandaloneTooltip ? (jsx(Tooltip, { delayed: true, label: config.label, children: wrapped })) : (wrapped);
|
|
1748
|
+
return useStandaloneTooltip ? (jsx(Tooltip, { delayed: true, label: config.label, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: wrapped })) : (wrapped);
|
|
1733
1749
|
};
|
|
1734
1750
|
|
|
1735
1751
|
/** Shared padding for interactive menu items (button, toggle, stepper, select, radioGroup). */
|
|
@@ -2230,14 +2246,26 @@ const MenuContent = ({ availableSpace, items, presentation }) => {
|
|
|
2230
2246
|
const groups = groupItemsBySection(items);
|
|
2231
2247
|
return (jsx("div", { className: presentation === "popover" ? "rounded-lg border border-neutral-300 bg-white py-1 shadow" : "py-1", role: "menu", children: groups.map((group, groupIndex) => (jsxs(Fragment$1, { children: [group.section !== undefined ? jsx(SectionHeader, { title: group.section }) : null, group.items.map((item, itemIndex) => renderMenuItem(item, groupIndex * 100 + itemIndex, availableSpace))] }, groupIndex))) }));
|
|
2232
2248
|
};
|
|
2249
|
+
/** Popover opens right; tooltip is suppressed while the menu is open. */
|
|
2250
|
+
const MenuRightPopover = ({ availableSpace, config, customPortalId, resolved, }) => {
|
|
2251
|
+
const { className, "data-testid": dataTestId, items, label } = config;
|
|
2252
|
+
const isFullScreen = useIsFullscreen();
|
|
2253
|
+
return (jsx("div", { className: className ?? "w-fit", children: jsx(Popover, { placement: RIGHT_CONTROL_POPOVER_PLACEMENT, children: ({ isOpen }) => (jsxs(Fragment$1, { children: [jsx(PopoverTrigger, { children: jsx(Tooltip, { delayed: true, disabled: isOpen, interactable: false, label: label, placement: RIGHT_CONTROL_TOOLTIP_PLACEMENT, children: jsx(IconButton, { ariaLabel: label, "data-testid": dataTestId, icon: jsx(Icon, { name: resolved.icon ?? "QuestionMarkCircle", size: "small" }), size: resolved.size, variant: "secondary" }) }) }), jsx(PopoverContent, { portalId: isFullScreen ? customPortalId : undefined, children: jsx(MenuContent, { availableSpace: availableSpace, items: items, presentation: "popover" }) })] })) }) }));
|
|
2254
|
+
};
|
|
2233
2255
|
/** Popover presentation -- renders a MoreMenu with position-aware placement. */
|
|
2234
|
-
const
|
|
2256
|
+
const MenuAutoPopover = ({ availableSpace, config, containerRef, customPortalId, resolved, }) => {
|
|
2235
2257
|
const { className, "data-testid": dataTestId, items, label } = config;
|
|
2236
2258
|
const isFullScreen = useIsFullscreen();
|
|
2237
2259
|
const triggerRef = useRef(null);
|
|
2238
2260
|
const popoverPlacement = usePopoverPlacement(triggerRef, containerRef);
|
|
2239
2261
|
return (jsx("div", { className: "w-fit", ref: triggerRef, children: jsx(MoreMenu, { className: className, customButton: jsx(IconButton, { "data-testid": dataTestId, icon: jsx(Icon, { name: resolved.icon ?? "QuestionMarkCircle", size: "small" }), size: resolved.size, title: label, variant: "secondary" }), customPortalId: isFullScreen ? customPortalId : undefined, "data-testid": dataTestId, popoverProps: { placement: popoverPlacement }, children: jsx(MenuContent, { availableSpace: availableSpace, items: items, presentation: "popover" }) }) }));
|
|
2240
2262
|
};
|
|
2263
|
+
const MenuPopover = ({ availableSpace, config, containerRef, customPortalId, overlaySide = undefined, resolved, }) => {
|
|
2264
|
+
if (overlaySide === "right") {
|
|
2265
|
+
return (jsx(MenuRightPopover, { availableSpace: availableSpace, config: config, customPortalId: customPortalId, resolved: resolved }));
|
|
2266
|
+
}
|
|
2267
|
+
return (jsx(MenuAutoPopover, { availableSpace: availableSpace, config: config, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2268
|
+
};
|
|
2241
2269
|
/** Modal presentation -- renders a Modal with no popover-specific hooks. */
|
|
2242
2270
|
const MenuModal = ({ availableSpace, config, resolved }) => (jsx(Modal, { config: config, resolved: resolved, children: jsx(MenuContent, { availableSpace: availableSpace, items: config.items, presentation: "modal" }) }));
|
|
2243
2271
|
/**
|
|
@@ -2249,11 +2277,11 @@ const MenuModal = ({ availableSpace, config, resolved }) => (jsx(Modal, { config
|
|
|
2249
2277
|
*
|
|
2250
2278
|
* @internal
|
|
2251
2279
|
*/
|
|
2252
|
-
const Menu = ({ availableSpace, config, containerRef, customPortalId, menuPresentation, resolved, }) => {
|
|
2280
|
+
const Menu = ({ availableSpace, config, containerRef, customPortalId, menuPresentation, overlaySide = undefined, resolved, }) => {
|
|
2253
2281
|
if (menuPresentation === "modal") {
|
|
2254
2282
|
return jsx(MenuModal, { availableSpace: availableSpace, config: config, resolved: resolved });
|
|
2255
2283
|
}
|
|
2256
|
-
return (jsx(MenuPopover, { availableSpace: availableSpace, config: config, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2284
|
+
return (jsx(MenuPopover, { availableSpace: availableSpace, config: config, containerRef: containerRef, customPortalId: customPortalId, overlaySide: overlaySide, resolved: resolved }));
|
|
2257
2285
|
};
|
|
2258
2286
|
|
|
2259
2287
|
const fieldConfigForStandalonePopover = (config) => {
|
|
@@ -2271,13 +2299,16 @@ const fieldConfigForStandalonePopover = (config) => {
|
|
|
2271
2299
|
const RadioGroupPanel = ({ fieldConfig }) => {
|
|
2272
2300
|
return (jsx("div", { className: "rounded-lg border border-neutral-300 bg-white px-3 py-2 shadow", children: jsx(RadioGroupField, { config: fieldConfig }) }));
|
|
2273
2301
|
};
|
|
2274
|
-
const RadioGroupPopover = ({ config, containerRef, customPortalId, resolved }) => {
|
|
2302
|
+
const RadioGroupPopover = ({ config, containerRef, customPortalId, overlaySide = undefined, resolved, }) => {
|
|
2275
2303
|
const { className, "data-testid": dataTestId, label } = config;
|
|
2276
2304
|
const isFullScreen = useIsFullscreen();
|
|
2277
2305
|
const triggerRef = useRef(null);
|
|
2278
|
-
|
|
2279
|
-
const
|
|
2280
|
-
|
|
2306
|
+
const popoverPlacement = usePopoverPlacement(triggerRef, containerRef, { skip: overlaySide === "right" });
|
|
2307
|
+
const fieldConfig = fieldConfigForStandalonePopover(config);
|
|
2308
|
+
if (overlaySide === "right") {
|
|
2309
|
+
return (jsx("div", { className: "w-fit", ref: triggerRef, children: jsx(Popover, { placement: RIGHT_CONTROL_POPOVER_PLACEMENT, children: ({ isOpen }) => (jsxs(Fragment$1, { children: [jsx(PopoverTrigger, { children: jsx(Tooltip, { delayed: true, disabled: isOpen, interactable: false, label: label, placement: RIGHT_CONTROL_TOOLTIP_PLACEMENT, children: jsx(IconButton, { ariaLabel: label, "data-testid": dataTestId, icon: jsx(Icon, { name: resolved.icon ?? "EllipsisHorizontal", size: "small" }), size: resolved.size, variant: "secondary" }) }) }), jsx(PopoverContent, { portalId: isFullScreen ? customPortalId : undefined, children: jsx(RadioGroupPanel, { fieldConfig: fieldConfig }) })] })) }) }));
|
|
2310
|
+
}
|
|
2311
|
+
return (jsx("div", { className: "w-fit", ref: triggerRef, children: jsx(MoreMenu, { className: className, customButton: jsx(IconButton, { "data-testid": dataTestId, icon: jsx(Icon, { name: resolved.icon ?? "EllipsisHorizontal", size: "small" }), size: resolved.size, title: label, variant: "secondary" }), customPortalId: isFullScreen ? customPortalId : undefined, "data-testid": dataTestId, popoverProps: { placement: popoverPlacement }, children: jsx(RadioGroupPanel, { fieldConfig: fieldConfig }) }) }));
|
|
2281
2312
|
};
|
|
2282
2313
|
/**
|
|
2283
2314
|
* Standalone radio group: icon button opens a popover (`MoreMenu`).
|
|
@@ -2287,8 +2318,8 @@ const RadioGroupPopover = ({ config, containerRef, customPortalId, resolved }) =
|
|
|
2287
2318
|
*
|
|
2288
2319
|
* @internal
|
|
2289
2320
|
*/
|
|
2290
|
-
const RadioGroup = ({ config, containerRef, customPortalId, resolved }) => {
|
|
2291
|
-
return (jsx(RadioGroupPopover, { config: config, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2321
|
+
const RadioGroup = ({ config, containerRef, customPortalId, overlaySide = undefined, resolved, }) => {
|
|
2322
|
+
return (jsx(RadioGroupPopover, { config: config, containerRef: containerRef, customPortalId: customPortalId, overlaySide: overlaySide, resolved: resolved }));
|
|
2292
2323
|
};
|
|
2293
2324
|
|
|
2294
2325
|
/**
|
|
@@ -2297,10 +2328,10 @@ const RadioGroup = ({ config, containerRef, customPortalId, resolved }) => {
|
|
|
2297
2328
|
*
|
|
2298
2329
|
* @internal
|
|
2299
2330
|
*/
|
|
2300
|
-
const Search = ({ config, resolved }) => {
|
|
2331
|
+
const Search = ({ config, overlaySide = undefined, resolved }) => {
|
|
2301
2332
|
const { className, "data-testid": dataTestId, id, label, onChange, onClear, placeholder, value } = config;
|
|
2302
2333
|
const { size } = resolved;
|
|
2303
|
-
return (jsx(Tooltip, { delayed: true, label: label, children: jsx(Search$1, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, onChange: event => {
|
|
2334
|
+
return (jsx(Tooltip, { delayed: true, label: label, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: jsx(Search$1, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, onChange: event => {
|
|
2304
2335
|
onChange(event.target.value);
|
|
2305
2336
|
}, onClear: onClear, placeholder: placeholder ?? label, value: value }) }));
|
|
2306
2337
|
};
|
|
@@ -2311,12 +2342,12 @@ const Search = ({ config, resolved }) => {
|
|
|
2311
2342
|
*
|
|
2312
2343
|
* @internal
|
|
2313
2344
|
*/
|
|
2314
|
-
const Select = ({ config, resolved }) => {
|
|
2345
|
+
const Select = ({ config, overlaySide = undefined, resolved }) => {
|
|
2315
2346
|
const { className, "data-testid": dataTestId, id, label, onChange, options, value } = config;
|
|
2316
2347
|
const { size } = resolved;
|
|
2317
2348
|
const selectOptions = [...options];
|
|
2318
2349
|
const selectedOption = selectOptions.find(option => option.value === value) ?? null;
|
|
2319
|
-
return (jsx(Tooltip, { delayed: true, label: label, children: jsx(BaseSelect, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, isSearchable: false, onChange: option => {
|
|
2350
|
+
return (jsx(Tooltip, { delayed: true, label: label, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: jsx(BaseSelect, { "aria-label": label, className: className, "data-testid": dataTestId, fieldSize: size, id: id, isSearchable: false, onChange: option => {
|
|
2320
2351
|
if (option !== null) {
|
|
2321
2352
|
onChange(option.value);
|
|
2322
2353
|
}
|
|
@@ -2333,7 +2364,7 @@ const Select = ({ config, resolved }) => {
|
|
|
2333
2364
|
*
|
|
2334
2365
|
* @internal
|
|
2335
2366
|
*/
|
|
2336
|
-
const Stepper = ({ config, resolved }) => {
|
|
2367
|
+
const Stepper = ({ config, overlaySide = undefined, resolved }) => {
|
|
2337
2368
|
const { className, "data-testid": dataTestId, decrement: onClickDecrement, increment: onClickIncrement, label, } = config;
|
|
2338
2369
|
const { size } = resolved;
|
|
2339
2370
|
const [t] = useTranslation();
|
|
@@ -2346,7 +2377,7 @@ const Stepper = ({ config, resolved }) => {
|
|
|
2346
2377
|
: decrementHover.hovering
|
|
2347
2378
|
? `${label}, ${decreaseLabel}`
|
|
2348
2379
|
: label;
|
|
2349
|
-
return (jsx(Tooltip, { delayed: true, label: tooltipLabel, children: jsxs("div", { className: twMerge("grid", className), "data-testid": dataTestId, children: [jsx(IconButton, { "aria-label": increaseLabel, className: "rounded-b-none", icon: jsx(Icon, { name: "Plus", size: "small" }), onClick: onClickIncrement, onMouseEnter: incrementHover.onMouseEnter, onMouseLeave: incrementHover.onMouseLeave, size: size, title: increaseLabel, variant: "secondary" }), jsx(IconButton, { "aria-label": decreaseLabel, className: "translate-y-[-1px] rounded-t-none", icon: jsx(Icon, { name: "Minus", size: "small" }), onClick: onClickDecrement, onMouseEnter: decrementHover.onMouseEnter, onMouseLeave: decrementHover.onMouseLeave, size: size, title: decreaseLabel, variant: "secondary" })] }) }));
|
|
2380
|
+
return (jsx(Tooltip, { delayed: true, label: tooltipLabel, placement: overlaySide === "right" ? RIGHT_CONTROL_TOOLTIP_PLACEMENT : undefined, children: jsxs("div", { className: twMerge("grid", className), "data-testid": dataTestId, children: [jsx(IconButton, { "aria-label": increaseLabel, className: "rounded-b-none", icon: jsx(Icon, { name: "Plus", size: "small" }), onClick: onClickIncrement, onMouseEnter: incrementHover.onMouseEnter, onMouseLeave: incrementHover.onMouseLeave, size: size, title: increaseLabel, variant: "secondary" }), jsx(IconButton, { "aria-label": decreaseLabel, className: "translate-y-[-1px] rounded-t-none", icon: jsx(Icon, { name: "Minus", size: "small" }), onClick: onClickDecrement, onMouseEnter: decrementHover.onMouseEnter, onMouseLeave: decrementHover.onMouseLeave, size: size, title: decreaseLabel, variant: "secondary" })] }) }));
|
|
2350
2381
|
};
|
|
2351
2382
|
|
|
2352
2383
|
function assertStandaloneToggleGroupIcons(config) {
|
|
@@ -2363,7 +2394,7 @@ function assertStandaloneToggleGroupIcons(config) {
|
|
|
2363
2394
|
*
|
|
2364
2395
|
* @internal
|
|
2365
2396
|
*/
|
|
2366
|
-
const ToggleGroup = ({ config, resolved }) => {
|
|
2397
|
+
const ToggleGroup = ({ config, overlaySide: _overlaySide = undefined, resolved }) => {
|
|
2367
2398
|
const { className, "data-testid": dataTestId, disabled, label, onChange, options, value } = config;
|
|
2368
2399
|
assertStandaloneToggleGroupIcons(config);
|
|
2369
2400
|
return (jsx("div", { "aria-label": label, className: twMerge("w-fit", className), "data-testid": dataTestId, role: "group", children: jsx("div", { className: "relative after:pointer-events-none after:absolute after:inset-0 after:rounded-md after:ring-1 after:ring-inset after:ring-neutral-300 after:content-['']", children: jsx(ToggleGroupField, { config: {
|
|
@@ -2381,28 +2412,28 @@ const ToggleGroup = ({ config, resolved }) => {
|
|
|
2381
2412
|
*
|
|
2382
2413
|
* @internal
|
|
2383
2414
|
*/
|
|
2384
|
-
const ControlRenderer = ({ availableSpace, containerRef, control, customPortalId, menuPresentation, }) => {
|
|
2415
|
+
const ControlRenderer = ({ availableSpace, containerRef, control, customPortalId, menuPresentation, overlaySide = undefined, }) => {
|
|
2385
2416
|
const resolved = resolveControl(control, { placement: "standalone", availableSpace });
|
|
2386
2417
|
switch (control.type) {
|
|
2387
2418
|
case "button":
|
|
2388
|
-
return jsx(Button, { config: control, resolved: resolved });
|
|
2419
|
+
return jsx(Button, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2389
2420
|
case "stepper":
|
|
2390
|
-
return jsx(Stepper, { config: control, resolved: resolved });
|
|
2421
|
+
return jsx(Stepper, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2391
2422
|
case "toggle":
|
|
2392
2423
|
case "checkbox":
|
|
2393
|
-
return jsx(BooleanButton, { config: control, resolved: resolved });
|
|
2424
|
+
return jsx(BooleanButton, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2394
2425
|
case "select":
|
|
2395
|
-
return jsx(Select, { config: control, resolved: resolved });
|
|
2426
|
+
return jsx(Select, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2396
2427
|
case "search":
|
|
2397
|
-
return jsx(Search, { config: control, resolved: resolved });
|
|
2428
|
+
return jsx(Search, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2398
2429
|
case "radioGroup":
|
|
2399
|
-
return (jsx(RadioGroup, { config: control, containerRef: containerRef, customPortalId: customPortalId, resolved: resolved }));
|
|
2430
|
+
return (jsx(RadioGroup, { config: control, containerRef: containerRef, customPortalId: customPortalId, overlaySide: overlaySide, resolved: resolved }));
|
|
2400
2431
|
case "toggleGroup":
|
|
2401
|
-
return jsx(ToggleGroup, { config: control, resolved: resolved });
|
|
2432
|
+
return jsx(ToggleGroup, { config: control, overlaySide: overlaySide, resolved: resolved });
|
|
2402
2433
|
case "menu":
|
|
2403
|
-
return (jsx(Menu, { availableSpace: availableSpace, config: control, containerRef: containerRef, customPortalId: customPortalId, menuPresentation: menuPresentation, resolved: resolved }));
|
|
2434
|
+
return (jsx(Menu, { availableSpace: availableSpace, config: control, containerRef: containerRef, customPortalId: customPortalId, menuPresentation: menuPresentation, overlaySide: overlaySide, resolved: resolved }));
|
|
2404
2435
|
case "custom":
|
|
2405
|
-
return jsx(Custom, { config: control, placement: "standalone", resolved: resolved });
|
|
2436
|
+
return jsx(Custom, { config: control, overlaySide: overlaySide, placement: "standalone", resolved: resolved });
|
|
2406
2437
|
default: {
|
|
2407
2438
|
const exhaustiveCheck = control;
|
|
2408
2439
|
throw new Error(`Unknown control type: ${exhaustiveCheck}`);
|
|
@@ -2476,10 +2507,10 @@ const ControlsContent = ({ api, controls: baseControlsConfig, className }) => {
|
|
|
2476
2507
|
"pointer-events-none",
|
|
2477
2508
|
// Layout grid for semantic areas
|
|
2478
2509
|
"grid h-full w-full",
|
|
2479
|
-
// Responsive grid: navigation bottom-right, settings top-left, statistics bottom-left
|
|
2510
|
+
// Responsive grid: navigation bottom-right, settings top-left, tools top-right, statistics bottom-left
|
|
2480
2511
|
"grid-cols-[1fr_auto] grid-rows-[auto_1fr_auto]",
|
|
2481
2512
|
// Isolate creates a new stacking context to avoid z-index issues
|
|
2482
|
-
"isolate", className), "data-testid": "map-controls", ref: mergedRef, children: [
|
|
2513
|
+
"isolate", className), "data-testid": "map-controls", ref: mergedRef, children: [hasNavigationControls ? (jsx("div", { className: "pointer-events-auto col-start-2 row-start-3 flex flex-col gap-1 self-end justify-self-end", children: navigation.controls.map(control => (jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation }, control.id))) })) : null, hasSettingsControls ? (jsx("div", { className: "pointer-events-auto col-start-1 row-start-1 flex w-fit flex-col gap-1", "data-testid": "map-controls-settings", children: settings.controls.map(control => (jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation, overlaySide: "right" }, control.id))) })) : null, hasToolsControls ? (jsx("div", { className: "pointer-events-auto col-start-2 row-start-1 flex flex-col gap-1 justify-self-end", "data-testid": "map-controls-tools", children: tools.controls.map(control => (jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation }, control.id))) })) : null, hasStatisticsAreaContent ? (jsxs("div", { className: twMerge("pointer-events-none col-start-1 row-start-3 flex max-h-full max-w-[20rem] flex-col items-start justify-end gap-1 overflow-visible", !hasStatisticsControls && "self-end"), "data-testid": "map-controls-statistics", children: [shouldRenderAnnotations ? (jsx("div", { className: "pointer-events-auto", "data-testid": "map-controls-statistics-annotations", children: jsx(AnnotationStack, { annotations: displayAnnotations, isExiting: isExiting }) })) : null, hasStatisticsControls ? (jsx("div", { className: "pointer-events-auto flex max-h-full w-fit max-w-full flex-col gap-1 overflow-auto", "data-testid": "map-controls-statistics-controls", children: statistics.controls.map(control => (jsx(ControlRenderer, { availableSpace: availableSpace, containerRef: containerRef, control: control, customPortalId: portalId, menuPresentation: menuPresentation }, control.id))) })) : null] })) : null] }));
|
|
2483
2514
|
};
|
|
2484
2515
|
/**
|
|
2485
2516
|
* Map controls renderer — lays out a `ControlsConfig` across the four semantic
|
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-map",
|
|
3
|
-
"version": "0.1.1
|
|
3
|
+
"version": "0.1.1",
|
|
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.1.
|
|
11
|
-
"@trackunit/css-class-variance-utilities": "1.13.
|
|
12
|
-
"@trackunit/react-form-components": "2.1.
|
|
13
|
-
"@trackunit/react-core-hooks": "1.17.
|
|
14
|
-
"@trackunit/geo-json-utils": "1.14.
|
|
15
|
-
"@trackunit/i18n-library-translation": "2.0.
|
|
16
|
-
"@trackunit/react-modal": "2.1.
|
|
10
|
+
"@trackunit/react-components": "2.1.20",
|
|
11
|
+
"@trackunit/css-class-variance-utilities": "1.13.28",
|
|
12
|
+
"@trackunit/react-form-components": "2.1.21",
|
|
13
|
+
"@trackunit/react-core-hooks": "1.17.34",
|
|
14
|
+
"@trackunit/geo-json-utils": "1.14.29",
|
|
15
|
+
"@trackunit/i18n-library-translation": "2.0.22",
|
|
16
|
+
"@trackunit/react-modal": "2.1.22",
|
|
17
17
|
"react-minimal-pie-chart": "^8.4.0",
|
|
18
|
-
"@trackunit/react-map-adapter-shared": "0.0.
|
|
19
|
-
"@trackunit/ui-design-tokens": "1.13.
|
|
18
|
+
"@trackunit/react-map-adapter-shared": "0.0.8",
|
|
19
|
+
"@trackunit/ui-design-tokens": "1.13.28",
|
|
20
20
|
"@floating-ui/react": "^0.26.25",
|
|
21
21
|
"es-toolkit": "^1.39.10",
|
|
22
22
|
"tailwind-merge": "^2.0.0",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentRef, RefObject } from "react";
|
|
2
|
+
import type { ControlOverlaySide } from "./controlOverlaySide";
|
|
2
3
|
import { type AvailableSpace } from "./renderingRules";
|
|
3
4
|
import type { ControlConfig, MenuPresentation } from "./types";
|
|
4
5
|
type ControlRendererProps = Readonly<{
|
|
@@ -8,6 +9,7 @@ type ControlRendererProps = Readonly<{
|
|
|
8
9
|
control: ControlConfig;
|
|
9
10
|
customPortalId?: string;
|
|
10
11
|
menuPresentation: MenuPresentation;
|
|
12
|
+
overlaySide?: ControlOverlaySide;
|
|
11
13
|
}>;
|
|
12
14
|
/**
|
|
13
15
|
* Renders a control based on its type using exhaustive switch.
|
|
@@ -16,5 +18,5 @@ type ControlRendererProps = Readonly<{
|
|
|
16
18
|
*
|
|
17
19
|
* @internal
|
|
18
20
|
*/
|
|
19
|
-
export declare const ControlRenderer: ({ availableSpace, containerRef, control, customPortalId, menuPresentation, }: ControlRendererProps) => import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export declare const ControlRenderer: ({ availableSpace, containerRef, control, customPortalId, menuPresentation, overlaySide, }: ControlRendererProps) => import("react/jsx-runtime").JSX.Element;
|
|
20
22
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { ControlOverlaySide } from "../../controlOverlaySide";
|
|
1
2
|
import type { ResolvedControlProps } from "../../renderingRules";
|
|
2
3
|
import type { BooleanControlConfig } from "../../types";
|
|
3
4
|
type BooleanButtonProps = Readonly<{
|
|
4
5
|
config: BooleanControlConfig;
|
|
6
|
+
overlaySide?: ControlOverlaySide;
|
|
5
7
|
resolved: ResolvedControlProps;
|
|
6
8
|
}>;
|
|
7
9
|
/**
|
|
@@ -12,5 +14,5 @@ type BooleanButtonProps = Readonly<{
|
|
|
12
14
|
*
|
|
13
15
|
* @internal
|
|
14
16
|
*/
|
|
15
|
-
export declare const BooleanButton: ({ config, resolved }: BooleanButtonProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
17
|
+
export declare const BooleanButton: ({ config, overlaySide, resolved }: BooleanButtonProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
16
18
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { ControlOverlaySide } from "../../controlOverlaySide";
|
|
1
2
|
import { type ResolvedControlProps } from "../../renderingRules";
|
|
2
3
|
import type { ButtonControlConfig } from "../../types";
|
|
3
4
|
type ButtonProps = Readonly<{
|
|
4
5
|
config: ButtonControlConfig;
|
|
6
|
+
overlaySide?: ControlOverlaySide;
|
|
5
7
|
resolved: ResolvedControlProps;
|
|
6
8
|
}>;
|
|
7
9
|
/**
|
|
@@ -10,5 +12,5 @@ type ButtonProps = Readonly<{
|
|
|
10
12
|
*
|
|
11
13
|
* @internal
|
|
12
14
|
*/
|
|
13
|
-
export declare const Button: ({ config, resolved }: ButtonProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
export declare const Button: ({ config, overlaySide, resolved }: ButtonProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
14
16
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type ControlOverlaySide } from "../../controlOverlaySide";
|
|
1
2
|
import { type ResolvedControlProps } from "../../renderingRules";
|
|
2
3
|
import type { CustomControlConfig, Placement } from "../../types";
|
|
3
4
|
type CustomProps = Readonly<{
|
|
4
5
|
config: CustomControlConfig;
|
|
6
|
+
overlaySide?: ControlOverlaySide;
|
|
5
7
|
resolved: ResolvedControlProps;
|
|
6
8
|
placement: Placement;
|
|
7
9
|
}>;
|
|
@@ -18,5 +20,5 @@ type CustomProps = Readonly<{
|
|
|
18
20
|
*
|
|
19
21
|
* @internal
|
|
20
22
|
*/
|
|
21
|
-
export declare const Custom: ({ config, resolved, placement }: CustomProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
23
|
+
export declare const Custom: ({ config, overlaySide, resolved, placement }: CustomProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
22
24
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ComponentRef, type RefObject } from "react";
|
|
2
|
+
import { type ControlOverlaySide } from "../../controlOverlaySide";
|
|
2
3
|
import { type AvailableSpace, type ResolvedControlProps } from "../../renderingRules";
|
|
3
4
|
import type { MenuControlConfig, MenuPresentation } from "../../types";
|
|
4
5
|
type MenuProps = Readonly<{
|
|
@@ -8,6 +9,7 @@ type MenuProps = Readonly<{
|
|
|
8
9
|
containerRef: RefObject<ComponentRef<"div"> | null>;
|
|
9
10
|
customPortalId?: string;
|
|
10
11
|
menuPresentation: MenuPresentation;
|
|
12
|
+
overlaySide?: ControlOverlaySide;
|
|
11
13
|
resolved: ResolvedControlProps;
|
|
12
14
|
}>;
|
|
13
15
|
/**
|
|
@@ -19,5 +21,5 @@ type MenuProps = Readonly<{
|
|
|
19
21
|
*
|
|
20
22
|
* @internal
|
|
21
23
|
*/
|
|
22
|
-
export declare const Menu: ({ availableSpace, config, containerRef, customPortalId, menuPresentation, resolved, }: MenuProps) => import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export declare const Menu: ({ availableSpace, config, containerRef, customPortalId, menuPresentation, overlaySide, resolved, }: MenuProps) => import("react/jsx-runtime").JSX.Element;
|
|
23
25
|
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type ComponentRef, type RefObject } from "react";
|
|
2
|
+
import { type ControlOverlaySide } from "../../controlOverlaySide";
|
|
2
3
|
import type { ResolvedControlProps } from "../../renderingRules";
|
|
3
4
|
import type { RadioGroupControlConfig } from "../../types";
|
|
4
5
|
type RadioGroupProps = Readonly<{
|
|
5
6
|
config: RadioGroupControlConfig;
|
|
6
7
|
containerRef: RefObject<ComponentRef<"div"> | null>;
|
|
7
8
|
customPortalId?: string;
|
|
9
|
+
overlaySide?: ControlOverlaySide;
|
|
8
10
|
resolved: ResolvedControlProps;
|
|
9
11
|
}>;
|
|
10
12
|
/**
|
|
@@ -15,5 +17,5 @@ type RadioGroupProps = Readonly<{
|
|
|
15
17
|
*
|
|
16
18
|
* @internal
|
|
17
19
|
*/
|
|
18
|
-
export declare const RadioGroup: ({ config, containerRef, customPortalId, resolved }: RadioGroupProps) => import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare const RadioGroup: ({ config, containerRef, customPortalId, overlaySide, resolved, }: RadioGroupProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
21
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type ControlOverlaySide } from "../../controlOverlaySide";
|
|
1
2
|
import type { ResolvedControlProps } from "../../renderingRules";
|
|
2
3
|
import type { SearchControlConfig } from "../../types";
|
|
3
4
|
type SearchProps = Readonly<{
|
|
4
5
|
config: SearchControlConfig;
|
|
6
|
+
overlaySide?: ControlOverlaySide;
|
|
5
7
|
resolved: ResolvedControlProps;
|
|
6
8
|
}>;
|
|
7
9
|
/**
|
|
@@ -10,5 +12,5 @@ type SearchProps = Readonly<{
|
|
|
10
12
|
*
|
|
11
13
|
* @internal
|
|
12
14
|
*/
|
|
13
|
-
export declare const Search: ({ config, resolved }: SearchProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare const Search: ({ config, overlaySide, resolved }: SearchProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
16
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type ControlOverlaySide } from "../../controlOverlaySide";
|
|
1
2
|
import type { ResolvedControlProps } from "../../renderingRules";
|
|
2
3
|
import type { SelectControlConfig } from "../../types";
|
|
3
4
|
type SelectProps = Readonly<{
|
|
4
5
|
config: SelectControlConfig;
|
|
6
|
+
overlaySide?: ControlOverlaySide;
|
|
5
7
|
resolved: ResolvedControlProps;
|
|
6
8
|
}>;
|
|
7
9
|
/**
|
|
@@ -10,5 +12,5 @@ type SelectProps = Readonly<{
|
|
|
10
12
|
*
|
|
11
13
|
* @internal
|
|
12
14
|
*/
|
|
13
|
-
export declare const Select: ({ config, resolved }: SelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare const Select: ({ config, overlaySide, resolved }: SelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
16
|
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from "react";
|
|
2
|
+
import { type ControlOverlaySide } from "../../controlOverlaySide";
|
|
3
|
+
type StandaloneControlTooltipProps = Readonly<{
|
|
4
|
+
children: ReactElement;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
label: string;
|
|
7
|
+
overlaySide?: ControlOverlaySide;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Wraps standalone map controls with a right-aligned tooltip when `overlaySide` is `"right"`.
|
|
11
|
+
* Callers should omit `title` on {@link IconButton} in that mode so tooltips do not stack.
|
|
12
|
+
*/
|
|
13
|
+
export declare const StandaloneControlTooltip: ({ children, disabled, label, overlaySide, }: StandaloneControlTooltipProps) => ReactNode;
|
|
14
|
+
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type ControlOverlaySide } from "../../controlOverlaySide";
|
|
1
2
|
import { type ResolvedControlProps } from "../../renderingRules";
|
|
2
3
|
import type { StepperControlConfig } from "../../types";
|
|
3
4
|
type StepperProps = Readonly<{
|
|
4
5
|
config: StepperControlConfig;
|
|
6
|
+
overlaySide?: ControlOverlaySide;
|
|
5
7
|
resolved: ResolvedControlProps;
|
|
6
8
|
}>;
|
|
7
9
|
/**
|
|
@@ -14,5 +16,5 @@ type StepperProps = Readonly<{
|
|
|
14
16
|
*
|
|
15
17
|
* @internal
|
|
16
18
|
*/
|
|
17
|
-
export declare const Stepper: ({ config, resolved }: StepperProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare const Stepper: ({ config, overlaySide, resolved }: StepperProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
20
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { ControlOverlaySide } from "../../controlOverlaySide";
|
|
1
2
|
import type { ResolvedControlProps } from "../../renderingRules";
|
|
2
3
|
import type { ToggleGroupControlConfig } from "../../types";
|
|
3
4
|
type ToggleGroupProps = Readonly<{
|
|
4
5
|
config: ToggleGroupControlConfig;
|
|
6
|
+
overlaySide?: ControlOverlaySide;
|
|
5
7
|
resolved: ResolvedControlProps;
|
|
6
8
|
}>;
|
|
7
9
|
/**
|
|
@@ -11,5 +13,5 @@ type ToggleGroupProps = Readonly<{
|
|
|
11
13
|
*
|
|
12
14
|
* @internal
|
|
13
15
|
*/
|
|
14
|
-
export declare const ToggleGroup: ({ config, resolved }: ToggleGroupProps) => import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare const ToggleGroup: ({ config, overlaySide: _overlaySide, resolved }: ToggleGroupProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
17
|
export {};
|
package/src/controls/types.d.ts
CHANGED
|
@@ -315,7 +315,7 @@ export type SettingsConfig = Readonly<{
|
|
|
315
315
|
export type StatisticsConfig = Readonly<{
|
|
316
316
|
controls: ReadonlyArray<ControlConfig>;
|
|
317
317
|
}>;
|
|
318
|
-
/** Tools area - array of controls */
|
|
318
|
+
/** Tools area - array of controls (top-right on the map) */
|
|
319
319
|
export type ToolsConfig = Readonly<{
|
|
320
320
|
controls: ReadonlyArray<ControlConfig>;
|
|
321
321
|
}>;
|
|
@@ -325,7 +325,7 @@ export type ControlsConfig = Readonly<{
|
|
|
325
325
|
navigation: NavigationConfig;
|
|
326
326
|
/** Settings: layer toggles, layer settings (future), map style (future) */
|
|
327
327
|
settings: SettingsConfig;
|
|
328
|
-
/** Tools: drawing, measurement
|
|
328
|
+
/** Tools: top-right controls (e.g. find address, drawing, measurement) */
|
|
329
329
|
tools: ToolsConfig;
|
|
330
330
|
/** Statistics: data summaries, counts */
|
|
331
331
|
statistics: StatisticsConfig;
|