@gooddata/sdk-ui-kit 11.44.0-alpha.0 → 11.44.0-alpha.2

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.
Files changed (31) hide show
  1. package/esm/@ui/UiTooltip/UiTooltip.d.ts +1 -1
  2. package/esm/@ui/UiTooltip/UiTooltip.d.ts.map +1 -1
  3. package/esm/@ui/UiTooltip/UiTooltip.js +21 -11
  4. package/esm/@ui/UiTooltip/types.d.ts +5 -0
  5. package/esm/@ui/UiTooltip/types.d.ts.map +1 -1
  6. package/esm/CatalogItemPicker/CatalogItemPicker.d.ts.map +1 -1
  7. package/esm/CatalogItemPicker/CatalogItemPicker.js +10 -117
  8. package/esm/CatalogItemPicker/CatalogItemPickerBody.d.ts +26 -0
  9. package/esm/CatalogItemPicker/CatalogItemPickerBody.d.ts.map +1 -0
  10. package/esm/CatalogItemPicker/CatalogItemPickerBody.js +21 -0
  11. package/esm/CatalogItemPicker/CatalogItemPickerContent.d.ts +10 -0
  12. package/esm/CatalogItemPicker/CatalogItemPickerContent.d.ts.map +1 -0
  13. package/esm/CatalogItemPicker/CatalogItemPickerContent.js +16 -0
  14. package/esm/CatalogItemPicker/useCatalogItemPicker.d.ts +38 -0
  15. package/esm/CatalogItemPicker/useCatalogItemPicker.d.ts.map +1 -0
  16. package/esm/CatalogItemPicker/useCatalogItemPicker.js +139 -0
  17. package/esm/Dropdown/DropdownButton.js +1 -1
  18. package/esm/Header/HeaderSearchButton.d.ts.map +1 -1
  19. package/esm/Header/HeaderSearchButton.js +1 -1
  20. package/esm/ParameterControl/ParameterControlDropdown.d.ts +10 -1
  21. package/esm/ParameterControl/ParameterControlDropdown.d.ts.map +1 -1
  22. package/esm/ParameterControl/ParameterControlDropdown.js +38 -13
  23. package/esm/index.d.ts +1 -0
  24. package/esm/index.d.ts.map +1 -1
  25. package/esm/index.js +1 -0
  26. package/esm/sdk-ui-kit.d.ts +16 -2
  27. package/package.json +11 -11
  28. package/src/CatalogItemPicker/CatalogItemPicker.scss +10 -1
  29. package/src/ParameterControl/ParameterControl.scss +6 -0
  30. package/styles/css/main.css +9 -1
  31. package/styles/css/main.css.map +1 -1
@@ -2,5 +2,5 @@ import { type IUiTooltipProps } from "./types.js";
2
2
  /**
3
3
  * @internal
4
4
  */
5
- export declare function UiTooltip({ id, anchor, content, behaviour, arrowPlacement, triggerBy, hoverOpenDelay, hoverCloseDelay, showArrow, width, offset: offsetProp, optimalPlacement, accessibilityConfig, variant, disabled, isOpen: isOpenProp, onOpen, onClose, onOpenChange, anchorWrapperStyles }: IUiTooltipProps): import("react/jsx-runtime").JSX.Element;
5
+ export declare function UiTooltip({ id, anchor, content, behaviour, arrowPlacement, triggerBy, hoverOpenDelay, hoverCloseDelay, showArrow, width, offset: offsetProp, optimalPlacement, accessibilityConfig, accessibilityHidden, variant, disabled, isOpen: isOpenProp, onOpen, onClose, onOpenChange, anchorWrapperStyles }: IUiTooltipProps): import("react/jsx-runtime").JSX.Element;
6
6
  //# sourceMappingURL=UiTooltip.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UiTooltip.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiTooltip/UiTooltip.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAWnE;;GAEG;AACH,wBAAgB,SAAS,CAAC,EACtB,EAAE,EACF,MAAM,EACN,OAAO,EACP,SAAqB,EACrB,cAAsB,EACtB,SAAc,EACd,cAA2B,EAC3B,eAA4B,EAC5B,SAAgB,EAChB,KAAK,EACL,MAAM,EAAE,UAAU,EAClB,gBAAwB,EACxB,mBAAmB,EACnB,OAAmB,EACnB,QAAQ,EACR,MAAM,EAAE,UAAU,EAClB,MAAM,EACN,OAAO,EACP,YAAY,EACZ,mBAAmB,EACtB,EAAE,eAAe,2CA0LjB"}
1
+ {"version":3,"file":"UiTooltip.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiTooltip/UiTooltip.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAWnE;;GAEG;AACH,wBAAgB,SAAS,CAAC,EACtB,EAAE,EACF,MAAM,EACN,OAAO,EACP,SAAqB,EACrB,cAAsB,EACtB,SAAc,EACd,cAA2B,EAC3B,eAA4B,EAC5B,SAAgB,EAChB,KAAK,EACL,MAAM,EAAE,UAAU,EAClB,gBAAwB,EACxB,mBAAmB,EACnB,mBAA2B,EAC3B,OAAmB,EACnB,QAAQ,EACR,MAAM,EAAE,UAAU,EAClB,MAAM,EACN,OAAO,EACP,YAAY,EACZ,mBAAmB,EACtB,EAAE,eAAe,2CAsMjB"}
@@ -1,10 +1,11 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2025-2026 GoodData Corporation
3
3
  import { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
4
- import { FloatingArrow, FloatingPortal, safePolygon, useClick, useDismiss, useFocus, useHover, useInteractions, } from "@floating-ui/react";
4
+ import { FloatingArrow, FloatingPortal, safePolygon, useClick, useFocus, useHover, useInteractions, } from "@floating-ui/react";
5
5
  import { ConditionalScopedThemeProvider, useIsScopeThemed, useTheme } from "@gooddata/sdk-ui-theme-provider";
6
6
  import { bem } from "../@utils/bem.js";
7
- import { FLOATING_ELEMENT_DATA_ATTR, isClickInsideOwnSubtree, useRegisterFloatingAnchor, } from "../hooks/useCloseOnOutsideClick.js";
7
+ import { useCloseOnEscape } from "../hooks/useCloseOnEscape.js";
8
+ import { FLOATING_ELEMENT_DATA_ATTR, useCloseOnOutsideClick, useRegisterFloatingAnchor, } from "../hooks/useCloseOnOutsideClick.js";
8
9
  import { useFloatingPosition } from "../UiFloatingElement/useFloatingPosition.js";
9
10
  import { ARROW_HEIGHT, ARROW_WIDTH, HIDE_DELAY, SHOW_DELAY } from "./constants.js";
10
11
  import { computeArrowOffset, computeTooltipShift, getDimensionsFromRect, getFlipFallbackOrder, getOppositeBasicPlacement, } from "./utils.js";
@@ -12,7 +13,7 @@ const { b, e } = bem("gd-ui-kit-tooltip");
12
13
  /**
13
14
  * @internal
14
15
  */
15
- export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPlacement = "top", triggerBy = [], hoverOpenDelay = SHOW_DELAY, hoverCloseDelay = HIDE_DELAY, showArrow = true, width, offset: offsetProp, optimalPlacement = false, accessibilityConfig, variant = "default", disabled, isOpen: isOpenProp, onOpen, onClose, onOpenChange, anchorWrapperStyles, }) {
16
+ export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPlacement = "top", triggerBy = [], hoverOpenDelay = SHOW_DELAY, hoverCloseDelay = HIDE_DELAY, showArrow = true, width, offset: offsetProp, optimalPlacement = false, accessibilityConfig, accessibilityHidden = false, variant = "default", disabled, isOpen: isOpenProp, onOpen, onClose, onOpenChange, anchorWrapperStyles, }) {
16
17
  const [isOpenInternal, setIsOpen] = useState(false);
17
18
  const isControlled = isOpenProp !== undefined;
18
19
  const isOpen = !disabled && (isControlled ? isOpenProp : isOpenInternal || triggerBy.length === 0);
@@ -104,25 +105,34 @@ export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPla
104
105
  // ref that only has a current value once the reference element mounts,
105
106
  // which is independent of our render cycle.
106
107
  const setFloatingWithAnchorRegistry = useRegisterFloatingAnchor(refs.setFloating, () => refs.reference.current instanceof Element ? refs.reference.current : null);
107
- // Dismiss stays enabled even when controlled so outside-click / Escape can
108
- // request a close; the request is surfaced through handleOpenChange onOpenChange.
109
- const dismiss = useDismiss(context, {
110
- outsidePress: (event) => !isClickInsideOwnSubtree(event.target, refs.floating.current),
108
+ // Outside-click / Escape close via DOM-based hooks rather than floating-ui's
109
+ // useDismiss. useDismiss bails out whenever the press lands inside the parent
110
+ // floating element's React tree (its `insideReactTree` guard) — so a popover
111
+ // portalled inside a modal (e.g. the OLP share dialog) never closed on any
112
+ // click within that modal (F1-2608). useCloseOnOutsideClick keys off real DOM
113
+ // containment plus the floating-anchor registry, so it correctly ignores our
114
+ // own subtree and nested child popups while still closing on sibling/outside
115
+ // clicks. It stays enabled when controlled so the close request still flows
116
+ // through handleClose → onOpenChange.
117
+ useCloseOnOutsideClick(isOpen, handleClose, {
118
+ floatingRef: refs.floating,
119
+ anchorRef: refs.reference,
111
120
  });
112
- const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, hover, focus, click]);
121
+ useCloseOnEscape(isOpen, handleClose);
122
+ const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, click]);
113
123
  return (_jsxs(_Fragment, { children: [
114
- _jsx("div", { className: e("anchor"), ref: refs.setReference, style: anchorWrapperStyles, ...getReferenceProps(), children: anchor }), _jsx("span", { className: "sr-only", id: id, children: isOpen
124
+ _jsx("div", { className: e("anchor"), ref: refs.setReference, style: anchorWrapperStyles, ...getReferenceProps(), children: anchor }), accessibilityHidden ? null : (_jsx("span", { className: "sr-only", id: id, children: isOpen
115
125
  ? typeof content === "function"
116
126
  ? content({ onClose: handleClose, type: "screen-reader" })
117
127
  : content
118
- : null }), isOpen ? (_jsx(FloatingPortal, { children: _jsx(ConditionalScopedThemeProvider, { children: _jsxs("div", { className: b({
128
+ : null })), isOpen ? (_jsx(FloatingPortal, { children: _jsx(ConditionalScopedThemeProvider, { children: _jsxs("div", { className: b({
119
129
  width: width === "same-as-anchor" ? "same-as-anchor" : false,
120
130
  variant,
121
131
  }), ref: setFloatingWithAnchorRegistry, style: {
122
132
  zIndex,
123
133
  ...floatingStyles,
124
134
  width: width === "same-as-anchor" ? triggerDimensions?.width : width,
125
- }, [FLOATING_ELEMENT_DATA_ATTR]: true, "aria-label": accessibilityConfig?.ariaLabel, "aria-labelledby": accessibilityConfig?.ariaLabelledBy, "aria-describedby": accessibilityConfig?.ariaDescribedBy, "aria-expanded": accessibilityConfig?.ariaExpanded, role: accessibilityConfig?.role ?? "tooltip", ...getFloatingProps(), children: [typeof content === "function"
135
+ }, [FLOATING_ELEMENT_DATA_ATTR]: true, "aria-hidden": accessibilityHidden || undefined, "aria-label": accessibilityConfig?.ariaLabel, "aria-labelledby": accessibilityConfig?.ariaLabelledBy, "aria-describedby": accessibilityConfig?.ariaDescribedBy, "aria-expanded": accessibilityConfig?.ariaExpanded, role: accessibilityConfig?.role ?? "tooltip", ...getFloatingProps(), children: [typeof content === "function"
126
136
  ? content({ onClose: handleClose, type: "live" })
127
137
  : content, showArrow ? (_jsx(FloatingArrow, { staticOffset: computeArrowOffset(arrowPlacement, floatingDimensions, !middlewareData?.["flip"]?.index, theme), className: e("arrow"), ref: arrowRef, tipRadius: 1, context: context, height: ARROW_HEIGHT, width: ARROW_WIDTH })) : null] }) }) })) : null] }));
128
138
  }
@@ -35,6 +35,11 @@ export interface IUiTooltipProps {
35
35
  optimalPlacement?: boolean;
36
36
  /** ARIA attributes configuration for accessibility */
37
37
  accessibilityConfig?: IAccessibilityConfigBase;
38
+ /**
39
+ * When true, the tooltip is purely visual — hidden from assistive technologies (the anchor conveys it).
40
+ * @defaultValue false
41
+ */
42
+ accessibilityHidden?: boolean;
38
43
  /**
39
44
  * Tooltip styling variant
40
45
  * @defaultValue "default"
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiTooltip/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC3B,KAAK,GACL,WAAW,GACX,SAAS,GACT,QAAQ,GACR,cAAc,GACd,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,UAAU,GACV,OAAO,GACP,aAAa,GACb,WAAW,CAAC;AAElB;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,yBAAyB;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uGAAuG;IACvG,cAAc,CAAC,EAAE,qBAAqB,CAAC;IAEvC,6DAA6D;IAC7D,MAAM,EAAE,SAAS,CAAC;IAElB,gDAAgD;IAChD,OAAO,EAAE,SAAS,GAAG,CAAC,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,IAAI,CAAC;QAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAAA;KAAE,KAAK,SAAS,CAAC,CAAC;IAEpG,2DAA2D;IAC3D,SAAS,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,CAAC;IAE/C,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,kEAAkE;IAClE,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,kIAAkI;IAClI,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAElC,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,+FAA+F;IAC/F,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,sDAAsD;IACtD,mBAAmB,CAAC,EAAE,wBAAwB,CAAC;IAC/C;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACvC;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAElC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC;;OAEG;IACH,mBAAmB,CAAC,EAAE,aAAa,CAAC;CACvC;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiTooltip/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC3B,KAAK,GACL,WAAW,GACX,SAAS,GACT,QAAQ,GACR,cAAc,GACd,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,UAAU,GACV,OAAO,GACP,aAAa,GACb,WAAW,CAAC;AAElB;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,yBAAyB;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uGAAuG;IACvG,cAAc,CAAC,EAAE,qBAAqB,CAAC;IAEvC,6DAA6D;IAC7D,MAAM,EAAE,SAAS,CAAC;IAElB,gDAAgD;IAChD,OAAO,EAAE,SAAS,GAAG,CAAC,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,IAAI,CAAC;QAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAAA;KAAE,KAAK,SAAS,CAAC,CAAC;IAEpG,2DAA2D;IAC3D,SAAS,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,CAAC;IAE/C,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,kEAAkE;IAClE,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,kIAAkI;IAClI,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAElC,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,+FAA+F;IAC/F,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,sDAAsD;IACtD,mBAAmB,CAAC,EAAE,wBAAwB,CAAC;IAC/C;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACvC;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAElC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC;;OAEG;IACH,mBAAmB,CAAC,EAAE,aAAa,CAAC;CACvC;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogItemPicker.d.ts","sourceRoot":"","sources":["../../src/CatalogItemPicker/CatalogItemPicker.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAY1D;;GAEG;AACH,eAAO,MAAM,iBAAiB,iFAsO5B,CAAC"}
1
+ {"version":3,"file":"CatalogItemPicker.d.ts","sourceRoot":"","sources":["../../src/CatalogItemPicker/CatalogItemPicker.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D;;GAEG;AACH,eAAO,MAAM,iBAAiB,iFAqB5B,CAAC"}
@@ -1,122 +1,15 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // (C) 2026 GoodData Corporation
3
- import { memo, useEffect, useId, useMemo, useRef } from "react";
4
- import { bem } from "../@ui/@utils/bem.js";
5
- import { LoadingSpinner } from "../LoadingSpinner/LoadingSpinner.js";
6
- import { CatalogItemPickerFooter, CatalogItemPickerHeader, CatalogItemPickerList, CatalogItemPickerSearch, CatalogItemPickerTabs, } from "./CatalogItemPickerParts.js";
7
- import { testIds } from "./messages.js";
8
- import { useAttributeItemsData } from "./useAttributeItemsData.js";
9
- import { useCatalogItemPickerHandlers } from "./useCatalogItemPickerHandlers.js";
10
- import { useEnsureActiveType, useFilteredMetricItems, usePickerTabs } from "./useCatalogItemPickerHooks.js";
11
- import { useCatalogItemPickerLabels } from "./useCatalogItemPickerLabels.js";
12
- import { useCatalogItemPickerListboxItems } from "./useCatalogItemPickerListboxItems.js";
13
- import { useCatalogItemPickerState } from "./useCatalogItemPickerState.js";
14
- import { useDateDatasetSelection } from "./useDateDatasetSelection.js";
15
- import { groupMetricCatalogItems } from "./utils.js";
16
- const { b, e } = bem("gd-ui-kit-catalog-item-picker");
3
+ import { memo } from "react";
4
+ import { CatalogItemPickerBody } from "./CatalogItemPickerBody.js";
5
+ import { CatalogItemPickerHeader } from "./CatalogItemPickerParts.js";
6
+ import { useCatalogItemPicker } from "./useCatalogItemPicker.js";
17
7
  /**
18
8
  * @internal
19
9
  */
20
- export const CatalogItemPicker = memo(function CatalogItemPickerInner({ itemTypes, selectionMode, attributeItems, metricItems, groups, maxListHeight, isOpen = true, isLoading = false, onBack, onClose, onAdd, onSelect, variant = "addFilter", }) {
21
- const listboxId = useId();
22
- // Compute available items
23
- const allAttributeItems = useMemo(() => [...(attributeItems?.insightItems ?? []), ...(attributeItems?.catalogItems ?? [])], [attributeItems]);
24
- // Check if there are any date items
25
- const hasDateItems = useMemo(() => allAttributeItems.some((item) => item.type === "date"), [allAttributeItems]);
26
- const resolvedItemTypes = useMemo(() => {
27
- const filtered = itemTypes.filter((type) => type !== "date" || hasDateItems);
28
- return filtered.length > 0 ? filtered : itemTypes;
29
- }, [hasDateItems, itemTypes]);
30
- // Selection and search state
31
- const { searchString, selectedIds, activeType, setActiveType, setSelectedIds, handleItemSelect, handleSearchChange, handleSearchEscKeyPress, resetSelection, } = useCatalogItemPickerState({
32
- isOpen,
33
- selectionMode,
34
- itemTypes: resolvedItemTypes,
35
- });
36
- // Tab management
37
- const { showTabs, effectiveType } = usePickerTabs(resolvedItemTypes, activeType);
38
- const isMultiSelect = selectionMode === "multiple";
39
- // Labels
40
- const labels = useCatalogItemPickerLabels({ variant, effectiveType });
41
- // Reset to first tab when loading finishes
42
- const wasLoading = useRef(isLoading);
43
- useEffect(() => {
44
- if (wasLoading.current && !isLoading) {
45
- setActiveType(resolvedItemTypes[0]);
46
- }
47
- wasLoading.current = isLoading;
48
- }, [isLoading, resolvedItemTypes, setActiveType]);
49
- useEnsureActiveType(resolvedItemTypes, showTabs, setActiveType);
50
- // Date dataset selection
51
- const { dateDatasetOptions, effectiveSelectedDateDatasetKey, isDateDatasetDropdownOpen, shouldShowDateDatasetSelector, setSelectedDateDatasetKey, setIsDateDatasetDropdownOpen, } = useDateDatasetSelection({ allAttributeItems, effectiveType });
52
- // Attribute items data
53
- const { filteredInsightItems, filteredCatalogItems, catalogItemsByDataset, ungroupedCatalogItems } = useAttributeItemsData({
54
- availableInsightItems: attributeItems?.insightItems ?? [],
55
- availableCatalogItems: attributeItems?.catalogItems ?? [],
56
- searchString,
57
- activeType: effectiveType === "date" ? "date" : "attribute",
58
- selectedDateDatasetKey: effectiveSelectedDateDatasetKey,
59
- });
60
- // Metric items data
61
- const metricInsightItems = useFilteredMetricItems(metricItems, searchString, effectiveType, "insightItems");
62
- const metricCatalogItems = useFilteredMetricItems(metricItems, searchString, effectiveType, "catalogItems");
63
- const groupedMetricCatalogItems = useMemo(() => groupMetricCatalogItems(metricCatalogItems, groups, labels.ungroupedTitle), [labels.ungroupedTitle, metricCatalogItems, groups]);
64
- // Build listbox items
65
- const listboxItems = useCatalogItemPickerListboxItems({
66
- effectiveType,
67
- metricInsightItems,
68
- groupedMetricCatalogItems,
69
- filteredInsightItems,
70
- filteredCatalogItems,
71
- catalogItemsByDataset,
72
- ungroupedCatalogItems,
73
- selectedIds,
74
- isLoading,
75
- shouldShowDateDatasetSelector,
76
- dateDatasetOptions,
77
- effectiveSelectedDateDatasetKey,
78
- isDateDatasetDropdownOpen,
79
- listboxId,
80
- labels,
81
- variant,
82
- setSelectedDateDatasetKey,
83
- setIsDateDatasetDropdownOpen,
84
- });
85
- // Event handlers
86
- const { handleListboxSelect, handleAdd, handleTabChange } = useCatalogItemPickerHandlers({
87
- selectionMode,
88
- selectedIds,
89
- setSelectedIds,
90
- listboxItems,
91
- onClose,
92
- onAdd,
93
- onSelect,
94
- handleItemSelect,
95
- setActiveType,
96
- resetSelection,
97
- });
98
- // Computed display state
99
- const hasSelectableItems = useMemo(() => {
100
- if (effectiveType === "metric") {
101
- return (metricInsightItems.length > 0 ||
102
- groupedMetricCatalogItems.some((group) => group.items.length > 0));
103
- }
104
- return (filteredInsightItems.length > 0 ||
105
- filteredCatalogItems.length > 0 ||
106
- shouldShowDateDatasetSelector);
107
- }, [
108
- effectiveType,
109
- filteredCatalogItems.length,
110
- filteredInsightItems.length,
111
- groupedMetricCatalogItems,
112
- metricInsightItems.length,
113
- shouldShowDateDatasetSelector,
114
- ]);
115
- const shouldShowEmptyStateMessage = !isLoading && !hasSelectableItems;
116
- const isAddDisabled = !isMultiSelect || selectedIds.size === 0;
117
- const emptyMessage = searchString.trim() ? labels.emptyNoResults : labels.emptyNoItems;
118
- return (_jsxs("div", { className: b({ variant, selectionMode }), "data-testid": testIds.root, children: [
119
- _jsx(CatalogItemPickerHeader, { title: labels.title, onBack: onBack, onClose: onClose, backAriaLabel: labels.backAriaLabel, closeAriaLabel: labels.closeAriaLabel }), _jsx("div", { className: e("content", { variant }), children: isLoading ? (_jsx("div", { className: e("loading", { variant }), children: _jsx(LoadingSpinner, { className: "small" }) })) : (_jsxs(_Fragment, { children: [
120
- _jsx(CatalogItemPickerSearch, { variant: variant, value: searchString, onChange: handleSearchChange, onEscKeyPress: handleSearchEscKeyPress, placeholder: labels.searchPlaceholder, ariaLabel: labels.searchAriaLabel }), showTabs ? (_jsx(CatalogItemPickerTabs, { variant: variant, itemTypes: resolvedItemTypes, effectiveType: effectiveType, onChange: handleTabChange, getAriaLabel: (type) => labels.tabLabels?.[type] ?? labels.title })) : null, _jsx(CatalogItemPickerList, { variant: variant, listboxItems: listboxItems, isMultiSelect: isMultiSelect, maxListHeight: maxListHeight, listboxId: listboxId, ariaLabel: labels.title, shouldShowEmptyStateMessage: shouldShowEmptyStateMessage, emptyMessage: emptyMessage, onSelect: handleListboxSelect })
121
- ] })) }), isMultiSelect ? (_jsx(CatalogItemPickerFooter, { variant: variant, isAddDisabled: isAddDisabled, onClose: onClose, onAdd: handleAdd, cancelLabel: labels.cancelLabel, addLabel: labels.addLabel ?? "", addTooltip: labels.addTooltip })) : null] }));
10
+ export const CatalogItemPicker = memo(function CatalogItemPickerInner(props) {
11
+ const { maxListHeight, onBack, onClose } = props;
12
+ const controller = useCatalogItemPicker(props);
13
+ const { labels } = controller;
14
+ return (_jsx(CatalogItemPickerBody, { controller: controller, maxListHeight: maxListHeight, onClose: onClose, header: _jsx(CatalogItemPickerHeader, { title: labels.title, onBack: onBack, onClose: onClose, backAriaLabel: labels.backAriaLabel, closeAriaLabel: labels.closeAriaLabel }) }));
122
15
  });
@@ -0,0 +1,26 @@
1
+ import { type ReactNode } from "react";
2
+ import { type CatalogItemPickerController } from "./useCatalogItemPicker.js";
3
+ /**
4
+ * @internal
5
+ */
6
+ export interface ICatalogItemPickerBodyProps {
7
+ controller: CatalogItemPickerController;
8
+ maxListHeight?: number;
9
+ onClose: () => void;
10
+ /**
11
+ * Optional header slot rendered above the search + list. The full {@link CatalogItemPicker} passes
12
+ * its header here; header-less usages (e.g. the ranking filter measure dropdown) omit it.
13
+ */
14
+ header?: ReactNode;
15
+ }
16
+ /**
17
+ * The reusable inner part of the catalog item picker: the picker root, an optional header slot, search,
18
+ * optional type tabs, the grouped list and (in multi-select mode) the footer. Driven by a
19
+ * {@link useCatalogItemPicker} controller so it can be reused outside the full {@link CatalogItemPicker}
20
+ * — e.g. embedded in the ranking filter measure dropdown, which provides its own anchor button instead
21
+ * of the picker header.
22
+ *
23
+ * @internal
24
+ */
25
+ export declare function CatalogItemPickerBody({ controller, maxListHeight, onClose, header }: ICatalogItemPickerBodyProps): import("react/jsx-runtime").JSX.Element;
26
+ //# sourceMappingURL=CatalogItemPickerBody.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CatalogItemPickerBody.d.ts","sourceRoot":"","sources":["../../src/CatalogItemPicker/CatalogItemPickerBody.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAY3D,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAI7E;;GAEG;AACH,MAAM,WAAW,2BAA2B;IACxC,UAAU,EAAE,2BAA2B,CAAC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,EAClC,UAAU,EACV,aAAa,EACb,OAAO,EACP,MAAM,EACT,EAAE,2BAA2B,2CA6E7B"}
@@ -0,0 +1,21 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { bem } from "../@ui/@utils/bem.js";
3
+ import { LoadingSpinner } from "../LoadingSpinner/LoadingSpinner.js";
4
+ import { CatalogItemPickerFooter, CatalogItemPickerList, CatalogItemPickerSearch, CatalogItemPickerTabs, } from "./CatalogItemPickerParts.js";
5
+ import { testIds } from "./messages.js";
6
+ const { b, e } = bem("gd-ui-kit-catalog-item-picker");
7
+ /**
8
+ * The reusable inner part of the catalog item picker: the picker root, an optional header slot, search,
9
+ * optional type tabs, the grouped list and (in multi-select mode) the footer. Driven by a
10
+ * {@link useCatalogItemPicker} controller so it can be reused outside the full {@link CatalogItemPicker}
11
+ * — e.g. embedded in the ranking filter measure dropdown, which provides its own anchor button instead
12
+ * of the picker header.
13
+ *
14
+ * @internal
15
+ */
16
+ export function CatalogItemPickerBody({ controller, maxListHeight, onClose, header, }) {
17
+ const { variant, selectionMode, isLoading, searchString, handleSearchChange, handleSearchEscKeyPress, labels, showTabs, resolvedItemTypes, effectiveType, handleTabChange, listboxItems, isMultiSelect, listboxId, shouldShowEmptyStateMessage, emptyMessage, handleListboxSelect, isAddDisabled, handleAdd, } = controller;
18
+ return (_jsxs("div", { className: b({ variant, selectionMode }), "data-testid": testIds.root, children: [header, _jsx("div", { className: e("content", { variant }), children: isLoading ? (_jsx("div", { className: e("loading", { variant }), children: _jsx(LoadingSpinner, { className: "small" }) })) : (_jsxs(_Fragment, { children: [
19
+ _jsx(CatalogItemPickerSearch, { variant: variant, value: searchString, onChange: handleSearchChange, onEscKeyPress: handleSearchEscKeyPress, placeholder: labels.searchPlaceholder, ariaLabel: labels.searchAriaLabel }), showTabs ? (_jsx(CatalogItemPickerTabs, { variant: variant, itemTypes: resolvedItemTypes, effectiveType: effectiveType, onChange: handleTabChange, getAriaLabel: (type) => labels.tabLabels?.[type] ?? labels.title })) : null, _jsx(CatalogItemPickerList, { variant: variant, listboxItems: listboxItems, isMultiSelect: isMultiSelect, maxListHeight: maxListHeight, listboxId: listboxId, ariaLabel: labels.title, shouldShowEmptyStateMessage: shouldShowEmptyStateMessage, emptyMessage: emptyMessage, onSelect: handleListboxSelect })
20
+ ] })) }), isMultiSelect ? (_jsx(CatalogItemPickerFooter, { variant: variant, isAddDisabled: isAddDisabled, onClose: onClose, onAdd: handleAdd, cancelLabel: labels.cancelLabel, addLabel: labels.addLabel ?? "", addTooltip: labels.addTooltip })) : null] }));
21
+ }
@@ -0,0 +1,10 @@
1
+ import { type ICatalogItemPickerProps } from "./types.js";
2
+ /**
3
+ * The catalog item picker content without its header — just the search + grouped list (+ footer in
4
+ * multi-select mode). Reuses the exact same list as {@link CatalogItemPicker}; intended for embedding
5
+ * under an external anchor button (e.g. the ranking filter measure dropdown).
6
+ *
7
+ * @internal
8
+ */
9
+ export declare const CatalogItemPickerContent: import("react").NamedExoticComponent<ICatalogItemPickerProps<unknown, unknown>>;
10
+ //# sourceMappingURL=CatalogItemPickerContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CatalogItemPickerContent.d.ts","sourceRoot":"","sources":["../../src/CatalogItemPicker/CatalogItemPickerContent.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,iFAWpC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { memo } from "react";
4
+ import { CatalogItemPickerBody } from "./CatalogItemPickerBody.js";
5
+ import { useCatalogItemPicker } from "./useCatalogItemPicker.js";
6
+ /**
7
+ * The catalog item picker content without its header — just the search + grouped list (+ footer in
8
+ * multi-select mode). Reuses the exact same list as {@link CatalogItemPicker}; intended for embedding
9
+ * under an external anchor button (e.g. the ranking filter measure dropdown).
10
+ *
11
+ * @internal
12
+ */
13
+ export const CatalogItemPickerContent = memo(function CatalogItemPickerContentInner(props) {
14
+ const controller = useCatalogItemPicker(props);
15
+ return (_jsx(CatalogItemPickerBody, { controller: controller, maxListHeight: props.maxListHeight, onClose: props.onClose }));
16
+ });
@@ -0,0 +1,38 @@
1
+ import { type ICatalogItemPickerProps } from "./types.js";
2
+ /**
3
+ * Encapsulates all state, derived data and event handlers of the catalog item picker.
4
+ *
5
+ * It is shared between {@link CatalogItemPicker} (which wraps the body with a header) and
6
+ * {@link CatalogItemPickerBody} (the header-less search + grouped list, reused e.g. in the ranking
7
+ * filter measure dropdown).
8
+ *
9
+ * @internal
10
+ */
11
+ export declare function useCatalogItemPicker<TAttributePayload = unknown, TMetricPayload = unknown>({ itemTypes, selectionMode, attributeItems, metricItems, groups, isOpen, isLoading, onClose, onAdd, onSelect, variant }: ICatalogItemPickerProps<TAttributePayload, TMetricPayload>): {
12
+ listboxId: string;
13
+ labels: import("./useCatalogItemPickerListboxItems.js").ICatalogItemPickerLabels;
14
+ searchString: string;
15
+ handleSearchChange: (value: string | number) => void;
16
+ handleSearchEscKeyPress: (e: import("react").KeyboardEvent<Element>) => void;
17
+ showTabs: boolean;
18
+ effectiveType: import("./types.js").CatalogItemPickerType;
19
+ resolvedItemTypes: import("./types.js").CatalogItemPickerType[];
20
+ handleTabChange: (type: import("./types.js").CatalogItemPickerType) => void;
21
+ listboxItems: import("./listboxItemBuilders.js").CatalogItemListboxItem[];
22
+ handleListboxSelect: (item: import("../index.js").IUiListboxInteractiveItem<import("./listboxItemBuilders.js").ICatalogItemPickerListItemData<unknown>>) => void;
23
+ isMultiSelect: boolean;
24
+ isAddDisabled: boolean;
25
+ handleAdd: () => void;
26
+ shouldShowEmptyStateMessage: boolean;
27
+ emptyMessage: string;
28
+ isLoading: boolean;
29
+ selectionMode: import("./types.js").CatalogItemPickerSelectionMode;
30
+ variant: "addFilter" | "mvf";
31
+ };
32
+ /**
33
+ * Return type of {@link useCatalogItemPicker}.
34
+ *
35
+ * @internal
36
+ */
37
+ export type CatalogItemPickerController = ReturnType<typeof useCatalogItemPicker>;
38
+ //# sourceMappingURL=useCatalogItemPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCatalogItemPicker.d.ts","sourceRoot":"","sources":["../../src/CatalogItemPicker/useCatalogItemPicker.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAU1D;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,iBAAiB,GAAG,OAAO,EAAE,cAAc,GAAG,OAAO,EAAE,EACxF,SAAS,EACT,aAAa,EACb,cAAc,EACd,WAAW,EACX,MAAM,EACN,MAAa,EACb,SAAiB,EACjB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,OAAqB,EACxB,EAAE,uBAAuB,CAAC,iBAAiB,EAAE,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;EAiL5D;AAED;;;;GAIG;AACH,MAAM,MAAM,2BAA2B,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -0,0 +1,139 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useEffect, useId, useMemo, useRef } from "react";
3
+ import { useAttributeItemsData } from "./useAttributeItemsData.js";
4
+ import { useCatalogItemPickerHandlers } from "./useCatalogItemPickerHandlers.js";
5
+ import { useEnsureActiveType, useFilteredMetricItems, usePickerTabs } from "./useCatalogItemPickerHooks.js";
6
+ import { useCatalogItemPickerLabels } from "./useCatalogItemPickerLabels.js";
7
+ import { useCatalogItemPickerListboxItems } from "./useCatalogItemPickerListboxItems.js";
8
+ import { useCatalogItemPickerState } from "./useCatalogItemPickerState.js";
9
+ import { useDateDatasetSelection } from "./useDateDatasetSelection.js";
10
+ import { groupMetricCatalogItems } from "./utils.js";
11
+ /**
12
+ * Encapsulates all state, derived data and event handlers of the catalog item picker.
13
+ *
14
+ * It is shared between {@link CatalogItemPicker} (which wraps the body with a header) and
15
+ * {@link CatalogItemPickerBody} (the header-less search + grouped list, reused e.g. in the ranking
16
+ * filter measure dropdown).
17
+ *
18
+ * @internal
19
+ */
20
+ export function useCatalogItemPicker({ itemTypes, selectionMode, attributeItems, metricItems, groups, isOpen = true, isLoading = false, onClose, onAdd, onSelect, variant = "addFilter", }) {
21
+ const listboxId = useId();
22
+ // Compute available items
23
+ const allAttributeItems = useMemo(() => [...(attributeItems?.insightItems ?? []), ...(attributeItems?.catalogItems ?? [])], [attributeItems]);
24
+ // Check if there are any date items
25
+ const hasDateItems = useMemo(() => allAttributeItems.some((item) => item.type === "date"), [allAttributeItems]);
26
+ const resolvedItemTypes = useMemo(() => {
27
+ const filtered = itemTypes.filter((type) => type !== "date" || hasDateItems);
28
+ return filtered.length > 0 ? filtered : itemTypes;
29
+ }, [hasDateItems, itemTypes]);
30
+ // Selection and search state
31
+ const { searchString, selectedIds, activeType, setActiveType, setSelectedIds, handleItemSelect, handleSearchChange, handleSearchEscKeyPress, resetSelection, } = useCatalogItemPickerState({
32
+ isOpen,
33
+ selectionMode,
34
+ itemTypes: resolvedItemTypes,
35
+ });
36
+ // Tab management
37
+ const { showTabs, effectiveType } = usePickerTabs(resolvedItemTypes, activeType);
38
+ const isMultiSelect = selectionMode === "multiple";
39
+ // Labels
40
+ const labels = useCatalogItemPickerLabels({ variant, effectiveType });
41
+ // Reset to first tab when loading finishes
42
+ const wasLoading = useRef(isLoading);
43
+ useEffect(() => {
44
+ if (wasLoading.current && !isLoading) {
45
+ setActiveType(resolvedItemTypes[0]);
46
+ }
47
+ wasLoading.current = isLoading;
48
+ }, [isLoading, resolvedItemTypes, setActiveType]);
49
+ useEnsureActiveType(resolvedItemTypes, showTabs, setActiveType);
50
+ // Date dataset selection
51
+ const { dateDatasetOptions, effectiveSelectedDateDatasetKey, isDateDatasetDropdownOpen, shouldShowDateDatasetSelector, setSelectedDateDatasetKey, setIsDateDatasetDropdownOpen, } = useDateDatasetSelection({ allAttributeItems, effectiveType });
52
+ // Attribute items data
53
+ const { filteredInsightItems, filteredCatalogItems, catalogItemsByDataset, ungroupedCatalogItems } = useAttributeItemsData({
54
+ availableInsightItems: attributeItems?.insightItems ?? [],
55
+ availableCatalogItems: attributeItems?.catalogItems ?? [],
56
+ searchString,
57
+ activeType: effectiveType === "date" ? "date" : "attribute",
58
+ selectedDateDatasetKey: effectiveSelectedDateDatasetKey,
59
+ });
60
+ // Metric items data
61
+ const metricInsightItems = useFilteredMetricItems(metricItems, searchString, effectiveType, "insightItems");
62
+ const metricCatalogItems = useFilteredMetricItems(metricItems, searchString, effectiveType, "catalogItems");
63
+ const groupedMetricCatalogItems = useMemo(() => groupMetricCatalogItems(metricCatalogItems, groups, labels.ungroupedTitle), [labels.ungroupedTitle, metricCatalogItems, groups]);
64
+ // Build listbox items
65
+ const listboxItems = useCatalogItemPickerListboxItems({
66
+ effectiveType,
67
+ metricInsightItems,
68
+ groupedMetricCatalogItems,
69
+ filteredInsightItems,
70
+ filteredCatalogItems,
71
+ catalogItemsByDataset,
72
+ ungroupedCatalogItems,
73
+ selectedIds,
74
+ isLoading,
75
+ shouldShowDateDatasetSelector,
76
+ dateDatasetOptions,
77
+ effectiveSelectedDateDatasetKey,
78
+ isDateDatasetDropdownOpen,
79
+ listboxId,
80
+ labels,
81
+ variant,
82
+ setSelectedDateDatasetKey,
83
+ setIsDateDatasetDropdownOpen,
84
+ });
85
+ // Event handlers
86
+ const { handleListboxSelect, handleAdd, handleTabChange } = useCatalogItemPickerHandlers({
87
+ selectionMode,
88
+ selectedIds,
89
+ setSelectedIds,
90
+ listboxItems,
91
+ onClose,
92
+ onAdd,
93
+ onSelect,
94
+ handleItemSelect,
95
+ setActiveType,
96
+ resetSelection,
97
+ });
98
+ // Computed display state
99
+ const hasSelectableItems = useMemo(() => {
100
+ if (effectiveType === "metric") {
101
+ return (metricInsightItems.length > 0 ||
102
+ groupedMetricCatalogItems.some((group) => group.items.length > 0));
103
+ }
104
+ return (filteredInsightItems.length > 0 ||
105
+ filteredCatalogItems.length > 0 ||
106
+ shouldShowDateDatasetSelector);
107
+ }, [
108
+ effectiveType,
109
+ filteredCatalogItems.length,
110
+ filteredInsightItems.length,
111
+ groupedMetricCatalogItems,
112
+ metricInsightItems.length,
113
+ shouldShowDateDatasetSelector,
114
+ ]);
115
+ const shouldShowEmptyStateMessage = !isLoading && !hasSelectableItems;
116
+ const isAddDisabled = !isMultiSelect || selectedIds.size === 0;
117
+ const emptyMessage = searchString.trim() ? labels.emptyNoResults : labels.emptyNoItems;
118
+ return {
119
+ listboxId,
120
+ labels,
121
+ searchString,
122
+ handleSearchChange,
123
+ handleSearchEscKeyPress,
124
+ showTabs,
125
+ effectiveType,
126
+ resolvedItemTypes,
127
+ handleTabChange,
128
+ listboxItems,
129
+ handleListboxSelect,
130
+ isMultiSelect,
131
+ isAddDisabled,
132
+ handleAdd,
133
+ shouldShowEmptyStateMessage,
134
+ emptyMessage,
135
+ isLoading,
136
+ selectionMode,
137
+ variant,
138
+ };
139
+ }
@@ -21,7 +21,7 @@ export function DropdownButton({ id, className, accessibilityConfig, value, titl
21
21
  ariaLabelledBy,
22
22
  ariaExpanded: effectiveAriaExpanded,
23
23
  ariaDescribedBy,
24
- ariaControls: ariaControls ?? (isOpen ? dropdownId : undefined),
24
+ ariaControls: ariaControls ?? dropdownId,
25
25
  role: role ?? "combobox",
26
26
  popupType,
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"HeaderSearchButton.d.ts","sourceRoot":"","sources":["../../src/Header/HeaderSearchButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAqB,MAAM,OAAO,CAAC;AAWlE,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;IAC9C;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAIH,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,2CAqExE"}
1
+ {"version":3,"file":"HeaderSearchButton.d.ts","sourceRoot":"","sources":["../../src/Header/HeaderSearchButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAqB,MAAM,OAAO,CAAC;AAWlE,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;IAC9C;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAIH,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,2CAsExE"}
@@ -33,5 +33,5 @@ export function HeaderSearchButton({ children, title }) {
33
33
  isExpanded: isOpen,
34
34
  popupId: dropdownId,
35
35
  }, ref: buttonRef, children: [
36
- _jsx("span", { className: "gd-icon-header-search-button" }), _jsx("span", { className: "gd-header-search-label", children: title }), isOpen ? (_jsx(Overlay, { isModal: false, alignTo: ".gd-header-search", alignPoints: ALIGN_POINTS, closeOnEscape: true, closeOnOutsideClick: true, closeOnParentScroll: false, closeOnMouseDrag: false, onClose: toggleOpen, ignoreClicksOnByClass: [".gd-bubble"], children: _jsx(UiFocusManager, { enableFocusTrap: true, enableAutofocus: true, enableReturnFocusOnUnmount: { returnFocusTo: buttonRef }, children: _jsx("div", { id: dropdownId, className: "gd-dialog gd-dropdown overlay gd-header-search-dropdown", children: children }) }) })) : null] }));
36
+ _jsx("span", { className: "gd-icon-header-search-button" }), _jsx("span", { className: "gd-header-search-label", children: title }), isOpen ? (_jsx(Overlay, { isModal: false, alignTo: ".gd-header-search", alignPoints: ALIGN_POINTS, resizeObserverThreshold: 0.2, closeOnEscape: true, closeOnOutsideClick: true, closeOnParentScroll: false, closeOnMouseDrag: false, onClose: toggleOpen, ignoreClicksOnByClass: [".gd-bubble"], children: _jsx(UiFocusManager, { enableFocusTrap: true, enableAutofocus: true, enableReturnFocusOnUnmount: { returnFocusTo: buttonRef }, children: _jsx("div", { id: dropdownId, className: "gd-dialog gd-dropdown overlay gd-header-search-dropdown", children: children }) }) })) : null] }));
37
37
  }
@@ -1,3 +1,4 @@
1
+ import { type MessageDescriptor } from "react-intl";
1
2
  import { type INumberParameterConstraints } from "@gooddata/sdk-model";
2
3
  /**
3
4
  * @internal
@@ -16,7 +17,7 @@ export interface IParameterControlDropdownProps {
16
17
  onCancel: () => void;
17
18
  }
18
19
  /**
19
- * Dropdown panel for editing a numeric parameter value. Owns clamp, draft, validation,
20
+ * Dropdown panel for editing a numeric parameter value. Owns the draft, inline validation,
20
21
  * preview, and (mode-aware) Reset via `resetValue`.
21
22
  *
22
23
  * Reset is visible only when `resetValue` is set and differs from `value`; clicking it
@@ -25,4 +26,12 @@ export interface IParameterControlDropdownProps {
25
26
  * @internal
26
27
  */
27
28
  export declare function ParameterControlDropdown({ name, value, resetValue, constraints, onApply, onCancel }: IParameterControlDropdownProps): import("react/jsx-runtime").JSX.Element;
29
+ /**
30
+ * Returns the message to show for an invalid draft, or `undefined` when the draft is a valid
31
+ * in-range number. The single source of truth the dropdown derives its error row, input style,
32
+ * and Apply-disabled state from.
33
+ *
34
+ * @internal
35
+ */
36
+ export declare function getDraftValidationError(draft: string, constraints?: INumberParameterConstraints): MessageDescriptor | undefined;
28
37
  //# sourceMappingURL=ParameterControlDropdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ParameterControlDropdown.d.ts","sourceRoot":"","sources":["../../src/ParameterControl/ParameterControlDropdown.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAevE;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,2BAA2B,CAAC;IAC1C,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EACrC,IAAI,EACJ,KAAK,EACL,UAAU,EACV,WAAW,EACX,OAAO,EACP,QAAQ,EACX,EAAE,8BAA8B,2CAiFhC"}
1
+ {"version":3,"file":"ParameterControlDropdown.d.ts","sourceRoot":"","sources":["../../src/ParameterControl/ParameterControlDropdown.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,iBAAiB,EAA2B,MAAM,YAAY,CAAC;AAE/F,OAAO,EAAE,KAAK,2BAA2B,EAA+B,MAAM,qBAAqB,CAAC;AAmBpG;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,2BAA2B,CAAC;IAC1C,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EACrC,IAAI,EACJ,KAAK,EACL,UAAU,EACV,WAAW,EACX,OAAO,EACP,QAAQ,EACX,EAAE,8BAA8B,2CAwFhC;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,2BAA2B,GAC1C,iBAAiB,GAAG,SAAS,CAgB/B"}