@gooddata/sdk-ui-kit 11.44.0-alpha.1 → 11.44.0-alpha.3

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 (34) hide show
  1. package/esm/@ui/UiControlButton/UiControlButton.d.ts +5 -1
  2. package/esm/@ui/UiControlButton/UiControlButton.d.ts.map +1 -1
  3. package/esm/@ui/UiControlButton/UiControlButton.js +7 -4
  4. package/esm/@ui/UiTooltip/UiTooltip.d.ts.map +1 -1
  5. package/esm/@ui/UiTooltip/UiTooltip.js +17 -7
  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/ParameterControl/ParameterControlButton.d.ts +2 -1
  18. package/esm/ParameterControl/ParameterControlButton.d.ts.map +1 -1
  19. package/esm/ParameterControl/ParameterControlButton.js +2 -2
  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 +17 -3
  27. package/package.json +11 -11
  28. package/src/@ui/UiControlButton/UiControlButton.scss +6 -0
  29. package/src/CatalogItemPicker/CatalogItemPicker.scss +10 -1
  30. package/src/ParameterControl/ParameterControl.scss +6 -0
  31. package/styles/css/UiControlButton.css +5 -0
  32. package/styles/css/UiControlButton.css.map +1 -1
  33. package/styles/css/main.css +14 -1
  34. package/styles/css/main.css.map +1 -1
@@ -20,6 +20,10 @@ export interface IUiControlButtonProps {
20
20
  isDraggable?: boolean;
21
21
  isDragging?: boolean;
22
22
  isError?: boolean;
23
+ /** Warning styling only; pair with `warningTooltip` to explain it. */
24
+ isWarning?: boolean;
25
+ /** Already-localized warning explanation. */
26
+ warningTooltip?: string;
23
27
  disabled?: boolean;
24
28
  /**
25
29
  * Already-localized string. When set together with `disabled`, a tooltip is wired up and
@@ -47,5 +51,5 @@ export interface IUiControlButtonProps {
47
51
  *
48
52
  * @internal
49
53
  */
50
- export declare function UiControlButton({ title, titleClassName, subtitle, subtitleClassName, icon, titleExtension, subtitleExtension, layout, hideChevron, isOpen, isDraggable, isDragging, isError, disabled, disabledTooltip, onClick, className, "data-testid": dataTestId, buttonRef, buttonId, dropdownId, ariaLabel }: IUiControlButtonProps): import("react/jsx-runtime").JSX.Element;
54
+ export declare function UiControlButton({ title, titleClassName, subtitle, subtitleClassName, icon, titleExtension, subtitleExtension, layout, hideChevron, isOpen, isDraggable, isDragging, isError, isWarning, warningTooltip, disabled, disabledTooltip, onClick, className, "data-testid": dataTestId, buttonRef, buttonId, dropdownId, ariaLabel }: IUiControlButtonProps): import("react/jsx-runtime").JSX.Element;
51
55
  //# sourceMappingURL=UiControlButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UiControlButton.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiControlButton/UiControlButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAmBrE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,EAC5B,KAAK,EACL,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,IAAI,EACJ,cAAc,EACd,iBAAiB,EACjB,MAAkB,EAClB,WAAW,EACX,MAAM,EACN,WAAW,EACX,UAAU,EACV,OAAO,EACP,QAAQ,EACR,eAAe,EACf,OAAO,EACP,SAAS,EACT,aAAa,EAAE,UAAU,EACzB,SAAS,EACT,QAAQ,EACR,UAAU,EACV,SAAS,EACZ,EAAE,qBAAqB,2CA+FvB"}
1
+ {"version":3,"file":"UiControlButton.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiControlButton/UiControlButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAmBrE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,EAC5B,KAAK,EACL,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,IAAI,EACJ,cAAc,EACd,iBAAiB,EACjB,MAAkB,EAClB,WAAW,EACX,MAAM,EACN,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,cAAc,EACd,QAAQ,EACR,eAAe,EACf,OAAO,EACP,SAAS,EACT,aAAa,EAAE,UAAU,EACzB,SAAS,EACT,QAAQ,EACR,UAAU,EACV,SAAS,EACZ,EAAE,qBAAqB,2CAkGvB"}
@@ -21,9 +21,11 @@ const TITLE_TOOLTIP_ALIGN_POINTS = [
21
21
  *
22
22
  * @internal
23
23
  */
24
- export function UiControlButton({ title, titleClassName, subtitle, subtitleClassName, icon, titleExtension, subtitleExtension, layout = "stacked", hideChevron, isOpen, isDraggable, isDragging, isError, disabled, disabledTooltip, onClick, className, "data-testid": dataTestId, buttonRef, buttonId, dropdownId, ariaLabel, }) {
24
+ export function UiControlButton({ title, titleClassName, subtitle, subtitleClassName, icon, titleExtension, subtitleExtension, layout = "stacked", hideChevron, isOpen, isDraggable, isDragging, isError, isWarning, warningTooltip, disabled, disabledTooltip, onClick, className, "data-testid": dataTestId, buttonRef, buttonId, dropdownId, ariaLabel, }) {
25
25
  const tooltipId = useIdPrefixed("gd-ui-kit-control-button-tooltip");
26
26
  const showDisabledTooltip = !!disabled && !!disabledTooltip;
27
+ const showWarningTooltip = !!isWarning && !!warningTooltip && !isOpen && !showDisabledTooltip;
28
+ const showTooltip = showDisabledTooltip || showWarningTooltip;
27
29
  const hasSubtitle = subtitle !== undefined || subtitleExtension !== undefined;
28
30
  // Trailing colon on the label, only when a subtitle follows it.
29
31
  const showLabelColon = layout === "row" && hasSubtitle;
@@ -45,13 +47,14 @@ export function UiControlButton({ title, titleClassName, subtitle, subtitleClass
45
47
  isDraggable: !!isDraggable,
46
48
  isDragging: !!isDragging,
47
49
  isError: !!isError,
50
+ isWarning: !!isWarning,
48
51
  disabled: !!disabled,
49
- }), className), role: "button", tabIndex: 0, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-disabled": disabled, "aria-controls": isOpen ? dropdownId : undefined, "aria-describedby": showDisabledTooltip ? tooltipId : undefined, "aria-label": ariaLabel, "data-testid": dataTestId, onClick: disabled ? undefined : onClick, onKeyDown: onKeyDown, children: [icon ? _jsx("div", { className: e("icon"), children: icon }) : null, _jsxs("div", { className: e("content"), children: [
52
+ }), className), role: "button", tabIndex: 0, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-disabled": disabled, "aria-controls": isOpen ? dropdownId : undefined, "aria-describedby": showTooltip ? tooltipId : undefined, "aria-label": ariaLabel, "data-testid": dataTestId, onClick: disabled ? undefined : onClick, onKeyDown: onKeyDown, children: [icon ? _jsx("div", { className: e("icon"), children: icon }) : null, _jsxs("div", { className: e("content"), children: [
50
53
  _jsxs("div", { className: e("title-row"), children: [
51
54
  _jsx("div", { className: e("title", { withColon: showLabelColon }), children: _jsx(ShortenedText, { tooltipAlignPoints: TITLE_TOOLTIP_ALIGN_POINTS, className: titleClassName, children: title }) }), titleExtension] }), hasSubtitle ? (_jsxs("div", { className: e("subtitle-row"), children: [typeof subtitle === "string" ? (_jsx("div", { className: e("subtitle"), children: _jsx(ShortenedText, { tooltipAlignPoints: TITLE_TOOLTIP_ALIGN_POINTS, className: subtitleClassName, children: subtitle }) })) : (subtitle), subtitleExtension] })) : null] })
52
55
  ] }));
53
- if (showDisabledTooltip) {
54
- return (_jsx(UiTooltip, { id: tooltipId, anchor: button, content: disabledTooltip, triggerBy: ["focus"], arrowPlacement: "top", showArrow: true }));
56
+ if (showTooltip) {
57
+ return (_jsx(UiTooltip, { id: tooltipId, anchor: button, content: showWarningTooltip ? warningTooltip : disabledTooltip, triggerBy: showWarningTooltip ? ["hover", "focus"] : ["focus"], arrowPlacement: "top", showArrow: true }));
55
58
  }
56
59
  return button;
57
60
  }
@@ -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,mBAA2B,EAC3B,OAAmB,EACnB,QAAQ,EACR,MAAM,EAAE,UAAU,EAClB,MAAM,EACN,OAAO,EACP,YAAY,EACZ,mBAAmB,EACtB,EAAE,eAAe,2CA6LjB"}
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";
@@ -104,12 +105,21 @@ 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
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"
@@ -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
+ }
@@ -7,6 +7,7 @@ export interface IParameterControlButtonProps {
7
7
  isActive: boolean;
8
8
  isDraggable?: boolean;
9
9
  isDragging?: boolean;
10
+ warningTooltip?: string;
10
11
  onClick?: () => void;
11
12
  className?: string;
12
13
  "data-testid"?: string;
@@ -14,5 +15,5 @@ export interface IParameterControlButtonProps {
14
15
  /**
15
16
  * @internal
16
17
  */
17
- export declare function ParameterControlButton({ name, value, isActive, isDraggable, isDragging, onClick, className, "data-testid": dataTestId }: IParameterControlButtonProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function ParameterControlButton({ name, value, isActive, isDraggable, isDragging, warningTooltip, onClick, className, "data-testid": dataTestId }: IParameterControlButtonProps): import("react/jsx-runtime").JSX.Element;
18
19
  //# sourceMappingURL=ParameterControlButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ParameterControlButton.d.ts","sourceRoot":"","sources":["../../src/ParameterControl/ParameterControlButton.tsx"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,4BAA4B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACnC,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,aAAa,EAAE,UAAU,EAC5B,EAAE,4BAA4B,2CAiB9B"}
1
+ {"version":3,"file":"ParameterControlButton.d.ts","sourceRoot":"","sources":["../../src/ParameterControl/ParameterControlButton.tsx"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,4BAA4B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACnC,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,WAAW,EACX,UAAU,EACV,cAAc,EACd,OAAO,EACP,SAAS,EACT,aAAa,EAAE,UAAU,EAC5B,EAAE,4BAA4B,2CAmB9B"}
@@ -9,8 +9,8 @@ const messages = defineMessages({
9
9
  /**
10
10
  * @internal
11
11
  */
12
- export function ParameterControlButton({ name, value, isActive, isDraggable, isDragging, onClick, className, "data-testid": dataTestId, }) {
12
+ export function ParameterControlButton({ name, value, isActive, isDraggable, isDragging, warningTooltip, onClick, className, "data-testid": dataTestId, }) {
13
13
  const intl = useIntl();
14
14
  const subtitle = intl.formatMessage(messages.valueSubtitle, { value });
15
- return (_jsx(UiControlButton, { title: name, subtitle: subtitle, isOpen: isActive, isDraggable: isDraggable, isDragging: isDragging, icon: _jsx(UiIcon, { type: "parameter", size: 16, color: "currentColor" }), onClick: onClick, className: className, "data-testid": dataTestId }));
15
+ return (_jsx(UiControlButton, { title: name, subtitle: subtitle, isOpen: isActive, isDraggable: isDraggable, isDragging: isDragging, icon: _jsx(UiIcon, { type: warningTooltip ? "warning" : "parameter", size: 16, color: "currentColor" }), isWarning: !!warningTooltip, warningTooltip: warningTooltip, onClick: onClick, className: className, "data-testid": dataTestId }));
16
16
  }
@@ -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"}