@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.
- package/esm/@ui/UiControlButton/UiControlButton.d.ts +5 -1
- package/esm/@ui/UiControlButton/UiControlButton.d.ts.map +1 -1
- package/esm/@ui/UiControlButton/UiControlButton.js +7 -4
- package/esm/@ui/UiTooltip/UiTooltip.d.ts.map +1 -1
- package/esm/@ui/UiTooltip/UiTooltip.js +17 -7
- package/esm/CatalogItemPicker/CatalogItemPicker.d.ts.map +1 -1
- package/esm/CatalogItemPicker/CatalogItemPicker.js +10 -117
- package/esm/CatalogItemPicker/CatalogItemPickerBody.d.ts +26 -0
- package/esm/CatalogItemPicker/CatalogItemPickerBody.d.ts.map +1 -0
- package/esm/CatalogItemPicker/CatalogItemPickerBody.js +21 -0
- package/esm/CatalogItemPicker/CatalogItemPickerContent.d.ts +10 -0
- package/esm/CatalogItemPicker/CatalogItemPickerContent.d.ts.map +1 -0
- package/esm/CatalogItemPicker/CatalogItemPickerContent.js +16 -0
- package/esm/CatalogItemPicker/useCatalogItemPicker.d.ts +38 -0
- package/esm/CatalogItemPicker/useCatalogItemPicker.d.ts.map +1 -0
- package/esm/CatalogItemPicker/useCatalogItemPicker.js +139 -0
- package/esm/ParameterControl/ParameterControlButton.d.ts +2 -1
- package/esm/ParameterControl/ParameterControlButton.d.ts.map +1 -1
- package/esm/ParameterControl/ParameterControlButton.js +2 -2
- package/esm/ParameterControl/ParameterControlDropdown.d.ts +10 -1
- package/esm/ParameterControl/ParameterControlDropdown.d.ts.map +1 -1
- package/esm/ParameterControl/ParameterControlDropdown.js +38 -13
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -0
- package/esm/sdk-ui-kit.d.ts +17 -3
- package/package.json +11 -11
- package/src/@ui/UiControlButton/UiControlButton.scss +6 -0
- package/src/CatalogItemPicker/CatalogItemPicker.scss +10 -1
- package/src/ParameterControl/ParameterControl.scss +6 -0
- package/styles/css/UiControlButton.css +5 -0
- package/styles/css/UiControlButton.css.map +1 -1
- package/styles/css/main.css +14 -1
- 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,
|
|
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":
|
|
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 (
|
|
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,
|
|
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,
|
|
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 {
|
|
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
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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":"
|
|
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 {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2026 GoodData Corporation
|
|
3
|
-
import { memo
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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(
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
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,
|
|
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
|
|
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":"
|
|
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"}
|