@gooddata/sdk-ui-kit 11.57.0-alpha.3 → 11.57.0-alpha.4

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 (139) hide show
  1. package/esm/@ui/@utils/keyboardNavigation.d.ts +10 -0
  2. package/esm/@ui/@utils/keyboardNavigation.d.ts.map +1 -1
  3. package/esm/@ui/@utils/keyboardNavigation.js +9 -0
  4. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.d.ts +4 -6
  5. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.d.ts.map +1 -1
  6. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.js +10 -8
  7. package/esm/@ui/UiAutocomplete/useAsyncListSource.js +1 -1
  8. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts +8 -1
  9. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts.map +1 -1
  10. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.js +10 -6
  11. package/esm/@ui/UiListbox/defaults/DefaultUiListboxInteractiveItemComponent.d.ts.map +1 -1
  12. package/esm/@ui/UiListbox/defaults/DefaultUiListboxInteractiveItemComponent.js +6 -2
  13. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuContentItem.d.ts.map +1 -1
  14. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuContentItem.js +7 -2
  15. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuInteractiveItem.d.ts.map +1 -1
  16. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuInteractiveItem.js +7 -2
  17. package/esm/@ui/UiModalDialog/UiDialogHeader.d.ts.map +1 -1
  18. package/esm/@ui/UiModalDialog/UiDialogHeader.js +6 -3
  19. package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.d.ts +16 -3
  20. package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.d.ts.map +1 -1
  21. package/esm/@ui/UiObjectShareDialog/UiObjectShareDialogCard.js +2 -3
  22. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts +9 -4
  23. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts.map +1 -1
  24. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.js +20 -29
  25. package/esm/@ui/UiTags/hooks/useResponsiveTags.js +1 -1
  26. package/esm/@ui/UiToolbar/UiToolbar.d.ts +52 -0
  27. package/esm/@ui/UiToolbar/UiToolbar.d.ts.map +1 -0
  28. package/esm/@ui/UiToolbar/UiToolbar.js +44 -0
  29. package/esm/@ui/UiToolbar/UiToolbarChevron.d.ts +8 -0
  30. package/esm/@ui/UiToolbar/UiToolbarChevron.d.ts.map +1 -0
  31. package/esm/@ui/UiToolbar/UiToolbarChevron.js +12 -0
  32. package/esm/@ui/UiToolbar/UiToolbarItemTooltip.d.ts +14 -0
  33. package/esm/@ui/UiToolbar/UiToolbarItemTooltip.d.ts.map +1 -0
  34. package/esm/@ui/UiToolbar/UiToolbarItemTooltip.js +11 -0
  35. package/esm/@ui/UiToolbar/constants.d.ts +5 -0
  36. package/esm/@ui/UiToolbar/constants.d.ts.map +1 -0
  37. package/esm/@ui/UiToolbar/constants.js +6 -0
  38. package/esm/@ui/UiToolbar/context.d.ts +23 -0
  39. package/esm/@ui/UiToolbar/context.d.ts.map +1 -0
  40. package/esm/@ui/UiToolbar/context.js +6 -0
  41. package/esm/@ui/UiToolbar/rovingFocusUtils.d.ts +55 -0
  42. package/esm/@ui/UiToolbar/rovingFocusUtils.d.ts.map +1 -0
  43. package/esm/@ui/UiToolbar/rovingFocusUtils.js +220 -0
  44. package/esm/@ui/UiToolbar/toolbarItemProps.d.ts +35 -0
  45. package/esm/@ui/UiToolbar/toolbarItemProps.d.ts.map +1 -0
  46. package/esm/@ui/UiToolbar/toolbarItemProps.js +2 -0
  47. package/esm/@ui/UiToolbar/useToolbarRovingFocus.d.ts +19 -0
  48. package/esm/@ui/UiToolbar/useToolbarRovingFocus.d.ts.map +1 -0
  49. package/esm/@ui/UiToolbar/useToolbarRovingFocus.js +172 -0
  50. package/esm/@ui/UiToolbarButton/UiToolbarButton.d.ts +19 -0
  51. package/esm/@ui/UiToolbarButton/UiToolbarButton.d.ts.map +1 -0
  52. package/esm/@ui/UiToolbarButton/UiToolbarButton.js +29 -0
  53. package/esm/@ui/UiToolbarButton/useToolbarItemRole.d.ts +38 -0
  54. package/esm/@ui/UiToolbarButton/useToolbarItemRole.d.ts.map +1 -0
  55. package/esm/@ui/UiToolbarButton/useToolbarItemRole.js +67 -0
  56. package/esm/@ui/UiToolbarColorSwatch/UiToolbarColorSwatch.d.ts +38 -0
  57. package/esm/@ui/UiToolbarColorSwatch/UiToolbarColorSwatch.d.ts.map +1 -0
  58. package/esm/@ui/UiToolbarColorSwatch/UiToolbarColorSwatch.js +13 -0
  59. package/esm/@ui/UiToolbarDivider/UiToolbarDivider.d.ts +7 -0
  60. package/esm/@ui/UiToolbarDivider/UiToolbarDivider.d.ts.map +1 -0
  61. package/esm/@ui/UiToolbarDivider/UiToolbarDivider.js +12 -0
  62. package/esm/@ui/UiToolbarIconButton/UiToolbarIconButton.d.ts +34 -0
  63. package/esm/@ui/UiToolbarIconButton/UiToolbarIconButton.d.ts.map +1 -0
  64. package/esm/@ui/UiToolbarIconButton/UiToolbarIconButton.js +34 -0
  65. package/esm/@ui/UiToolbarIconSelect/UiToolbarIconSelect.d.ts +54 -0
  66. package/esm/@ui/UiToolbarIconSelect/UiToolbarIconSelect.d.ts.map +1 -0
  67. package/esm/@ui/UiToolbarIconSelect/UiToolbarIconSelect.js +26 -0
  68. package/esm/@ui/UiToolbarMoreButton/UiToolbarMoreButton.d.ts +41 -0
  69. package/esm/@ui/UiToolbarMoreButton/UiToolbarMoreButton.d.ts.map +1 -0
  70. package/esm/@ui/UiToolbarMoreButton/UiToolbarMoreButton.js +12 -0
  71. package/esm/@ui/UiToolbarSegmentedControl/UiToolbarSegmentedControl.d.ts +37 -0
  72. package/esm/@ui/UiToolbarSegmentedControl/UiToolbarSegmentedControl.d.ts.map +1 -0
  73. package/esm/@ui/UiToolbarSegmentedControl/UiToolbarSegmentedControl.js +145 -0
  74. package/esm/@ui/UiToolbarSegmentedControl/context.d.ts +12 -0
  75. package/esm/@ui/UiToolbarSegmentedControl/context.d.ts.map +1 -0
  76. package/esm/@ui/UiToolbarSegmentedControl/context.js +3 -0
  77. package/esm/@ui/UiToolbarSelect/UiToolbarSelect.d.ts +51 -0
  78. package/esm/@ui/UiToolbarSelect/UiToolbarSelect.d.ts.map +1 -0
  79. package/esm/@ui/UiToolbarSelect/UiToolbarSelect.js +24 -0
  80. package/esm/@ui/UiToolbarStepper/UiToolbarStepper.d.ts +75 -0
  81. package/esm/@ui/UiToolbarStepper/UiToolbarStepper.d.ts.map +1 -0
  82. package/esm/@ui/UiToolbarStepper/UiToolbarStepper.js +85 -0
  83. package/esm/@ui/UiTooltip/UiTooltip.d.ts +1 -1
  84. package/esm/@ui/UiTooltip/UiTooltip.d.ts.map +1 -1
  85. package/esm/@ui/UiTooltip/UiTooltip.js +26 -8
  86. package/esm/@ui/UiTooltip/types.d.ts +12 -0
  87. package/esm/@ui/UiTooltip/types.d.ts.map +1 -1
  88. package/esm/@ui/hooks/useElementSize.d.ts.map +1 -1
  89. package/esm/@ui/hooks/useElementSize.js +2 -1
  90. package/esm/@ui/hooks/useIsTextTruncated.d.ts +12 -0
  91. package/esm/@ui/hooks/useIsTextTruncated.d.ts.map +1 -0
  92. package/esm/@ui/hooks/useIsTextTruncated.js +29 -0
  93. package/esm/Dropdown/Dropdown.js +1 -1
  94. package/esm/EditableLabel/EditableLabel.js +1 -1
  95. package/esm/List/LegacyList.js +1 -1
  96. package/esm/List/List.js +1 -1
  97. package/esm/Messages/toasts/NonContextToastsInterop.js +1 -1
  98. package/esm/Messages/toasts/ToastsCenterMessage.d.ts.map +1 -1
  99. package/esm/Messages/toasts/ToastsCenterMessage.js +1 -1
  100. package/esm/ResponsiveText/ResponsiveText.js +3 -3
  101. package/esm/ShortenedText/ShortenedText.js +1 -1
  102. package/esm/index.d.ts +15 -1
  103. package/esm/index.d.ts.map +1 -1
  104. package/esm/index.js +12 -1
  105. package/esm/locales.d.ts +4 -16
  106. package/esm/locales.d.ts.map +1 -1
  107. package/esm/locales.js +2 -6
  108. package/esm/sdk-ui-kit.d.ts +513 -15
  109. package/esm/syntaxHighlightingInput/hooks/useCodemirror.js +1 -1
  110. package/esm/utils/domUtilities.d.ts +4 -0
  111. package/esm/utils/domUtilities.d.ts.map +1 -1
  112. package/esm/utils/domUtilities.js +4 -1
  113. package/esm/utils/events.d.ts +3 -0
  114. package/esm/utils/events.d.ts.map +1 -1
  115. package/esm/utils/events.js +4 -1
  116. package/package.json +12 -13
  117. package/src/@ui/UiAddGranteeDialog/UiAddGranteeDialog.scss +8 -0
  118. package/src/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.scss +0 -6
  119. package/src/@ui/UiGranteeRow/UiGranteeRow.scss +5 -0
  120. package/src/@ui/UiListbox/UiListbox.scss +4 -0
  121. package/src/@ui/UiMenu/UiMenu.scss +3 -0
  122. package/src/@ui/UiModalDialog/UiModalDialog.scss +16 -3
  123. package/src/@ui/UiObjectShareDialog/UiObjectShareDialog.scss +40 -1
  124. package/src/@ui/UiPermissionMenu/UiPermissionMenu.scss +7 -3
  125. package/src/@ui/UiToolbar/UiToolbar.scss +15 -0
  126. package/src/@ui/UiToolbar/UiToolbarChevron.scss +11 -0
  127. package/src/@ui/UiToolbar/toolbarItem.scss +133 -0
  128. package/src/@ui/UiToolbarButton/UiToolbarButton.scss +15 -0
  129. package/src/@ui/UiToolbarColorSwatch/UiToolbarColorSwatch.scss +66 -0
  130. package/src/@ui/UiToolbarDivider/UiToolbarDivider.scss +11 -0
  131. package/src/@ui/UiToolbarIconButton/UiToolbarIconButton.scss +30 -0
  132. package/src/@ui/UiToolbarIconSelect/UiToolbarIconSelect.scss +12 -0
  133. package/src/@ui/UiToolbarSegmentedControl/UiToolbarSegmentedControl.scss +10 -0
  134. package/src/@ui/UiToolbarSelect/UiToolbarSelect.scss +29 -0
  135. package/src/@ui/UiToolbarStepper/UiToolbarStepper.scss +53 -0
  136. package/src/@ui/defaultTheme.scss +1 -0
  137. package/src/@ui/index.scss +10 -0
  138. package/styles/css/main.css +563 -9
  139. package/styles/css/main.css.map +1 -1
@@ -0,0 +1,37 @@
1
+ import { type ReactNode } from "react";
2
+ import { type UiToolbarNamingConfig } from "../UiToolbar/context.js";
3
+ import { type UiToolbarSegmentedControlChangeEvent } from "./context.js";
4
+ /**
5
+ * @internal
6
+ */
7
+ export interface IUiToolbarSegmentedControlProps {
8
+ value: string | undefined;
9
+ onChange: (value: string, event: UiToolbarSegmentedControlChangeEvent) => void;
10
+ /**
11
+ * The group needs an accessible name.
12
+ */
13
+ accessibilityConfig: UiToolbarNamingConfig;
14
+ isDisabled?: boolean;
15
+ /**
16
+ * Two to five {@link UiToolbarButton} or {@link UiToolbarIconButton} items, each with a `value`.
17
+ */
18
+ children: ReactNode;
19
+ dataTestId?: string;
20
+ }
21
+ /**
22
+ * Mutually exclusive choices rendered as one borderless strip. Arrow keys move the selection and
23
+ * the focus; the whole group is one tab stop. Standalone the arrows wrap; inside a {@link UiToolbar}
24
+ * they continue to the neighbouring toolbar item at the ends.
25
+ *
26
+ * @example
27
+ * ```
28
+ * <UiToolbarSegmentedControl value={mode} onChange={setMode} accessibilityConfig={{ ariaLabel: "Scope" }}>
29
+ * <UiToolbarButton value="cell" label="Cell" />
30
+ * <UiToolbarButton value="row" label="Row" />
31
+ * </UiToolbarSegmentedControl>
32
+ * ```
33
+ *
34
+ * @internal
35
+ */
36
+ export declare const UiToolbarSegmentedControl: import("react").ForwardRefExoticComponent<IUiToolbarSegmentedControlProps & import("react").RefAttributes<HTMLDivElement>>;
37
+ //# sourceMappingURL=UiToolbarSegmentedControl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiToolbarSegmentedControl.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiToolbarSegmentedControl/UiToolbarSegmentedControl.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEH,KAAK,SAAS,EAQjB,MAAM,OAAO,CAAC;AAOf,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAQrE,OAAO,EAEH,KAAK,oCAAoC,EAE5C,MAAM,cAAc,CAAC;AAEtB;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC5C,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,oCAAoC,KAAK,IAAI,CAAC;IAC/E;;OAEG;IACH,mBAAmB,EAAE,qBAAqB,CAAC;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAiDD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB,4HA6HrC,CAAC"}
@@ -0,0 +1,145 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, } from "react";
4
+ import { useAutoupdateRef } from "@gooddata/sdk-ui";
5
+ import { accessibilityConfigToAttributes } from "../../typings/utilities.js";
6
+ import { bem } from "../@utils/bem.js";
7
+ import { makeRadioGroupKeyboardNavigation } from "../@utils/keyboardNavigation.js";
8
+ import { TOOLBAR_COMPOSITE_ATTR, TOOLBAR_ROOT_ATTR, hasSystemModifier, isEligibleFocusable, } from "../UiToolbar/rovingFocusUtils.js";
9
+ import { UiToolbarSegmentedControlContext, } from "./context.js";
10
+ const { b } = bem("gd-ui-kit-toolbar-segmented-control");
11
+ // Decided from the DOM: a group rendered through a portal shares the toolbar's React tree but not
12
+ // its element tree, and only the element tree is what the toolbar's roving tabindex reaches. Only a
13
+ // UiToolbar manages the group; any other role="toolbar" ancestor does not.
14
+ function isInsideToolbar(group) {
15
+ return group.parentElement?.closest(`[${TOOLBAR_ROOT_ATTR}]`) != null;
16
+ }
17
+ function getRadios(group) {
18
+ return Array.from(group.querySelectorAll('[role="radio"]'));
19
+ }
20
+ const NAVIGABLE_RADIO = { isDisabledFocusable: false };
21
+ /**
22
+ * A radio the arrows may land on: rendered, visible and not disabled.
23
+ */
24
+ function isNavigableRadio(group, radio) {
25
+ return isEligibleFocusable(group, radio, NAVIGABLE_RADIO);
26
+ }
27
+ /**
28
+ * Next enabled radio in the given direction. Wraps when `loop` is set, otherwise returns null at the ends.
29
+ */
30
+ function findNextRadio(group, radios, from, delta, loop) {
31
+ // The current radio is never a candidate, so a wrap-around cannot re-select the same value.
32
+ const maxStep = from >= 0 ? radios.length - 1 : radios.length;
33
+ for (let step = 1; step <= maxStep; step++) {
34
+ const index = from + step * delta;
35
+ if (!loop && (index < 0 || index >= radios.length)) {
36
+ return null;
37
+ }
38
+ const candidate = radios[(index + radios.length) % radios.length];
39
+ if (candidate && isNavigableRadio(group, candidate)) {
40
+ return candidate;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ /**
46
+ * Mutually exclusive choices rendered as one borderless strip. Arrow keys move the selection and
47
+ * the focus; the whole group is one tab stop. Standalone the arrows wrap; inside a {@link UiToolbar}
48
+ * they continue to the neighbouring toolbar item at the ends.
49
+ *
50
+ * @example
51
+ * ```
52
+ * <UiToolbarSegmentedControl value={mode} onChange={setMode} accessibilityConfig={{ ariaLabel: "Scope" }}>
53
+ * <UiToolbarButton value="cell" label="Cell" />
54
+ * <UiToolbarButton value="row" label="Row" />
55
+ * </UiToolbarSegmentedControl>
56
+ * ```
57
+ *
58
+ * @internal
59
+ */
60
+ export const UiToolbarSegmentedControl = forwardRef(function UiToolbarSegmentedControl({ value, onChange, accessibilityConfig, isDisabled = false, children, dataTestId }, ref) {
61
+ const groupRef = useRef(null);
62
+ useImperativeHandle(ref, () => groupRef.current, []);
63
+ const onChangeRef = useAutoupdateRef(onChange);
64
+ const contextValue = useMemo(() => ({ value, onChange, isDisabled }), [value, onChange, isDisabled]);
65
+ // Standalone the group owns the roving tabindex; inside a toolbar the toolbar does. Decided
66
+ // from the DOM, because a group rendered through a portal shares the toolbar's React tree
67
+ // but not its element tree.
68
+ const syncStandaloneTabIndex = useCallback(() => {
69
+ const group = groupRef.current;
70
+ if (!group || isInsideToolbar(group)) {
71
+ return;
72
+ }
73
+ const radios = getRadios(group).filter((radio) => isNavigableRadio(group, radio));
74
+ const target = radios.find((radio) => radio.getAttribute("aria-checked") === "true") ?? radios[0];
75
+ for (const radio of getRadios(group)) {
76
+ radio.tabIndex = radio === target ? 0 : -1;
77
+ }
78
+ }, []);
79
+ useLayoutEffect(() => {
80
+ syncStandaloneTabIndex();
81
+ });
82
+ // Media queries can hide a radio without a render; a viewport change is the cue.
83
+ useEffect(() => {
84
+ window.addEventListener("resize", syncStandaloneTabIndex);
85
+ return () => window.removeEventListener("resize", syncStandaloneTabIndex);
86
+ }, [syncStandaloneTabIndex]);
87
+ // A radio can change its own eligibility without re-rendering the group.
88
+ useEffect(() => {
89
+ const group = groupRef.current;
90
+ if (!group || typeof MutationObserver === "undefined") {
91
+ return undefined;
92
+ }
93
+ const observer = new MutationObserver(syncStandaloneTabIndex);
94
+ observer.observe(group, {
95
+ childList: true,
96
+ subtree: true,
97
+ attributes: true,
98
+ attributeFilter: [
99
+ "disabled",
100
+ "aria-disabled",
101
+ "aria-checked",
102
+ "aria-hidden",
103
+ "hidden",
104
+ "class",
105
+ "style",
106
+ ],
107
+ });
108
+ return () => observer.disconnect();
109
+ }, [syncStandaloneTabIndex]);
110
+ const onKeyDown = useMemo(() => {
111
+ const step = (event, delta) => {
112
+ const group = groupRef.current;
113
+ if (!group || event.defaultPrevented || hasSystemModifier(event)) {
114
+ return;
115
+ }
116
+ const radios = getRadios(group);
117
+ const current = event.target.closest('[role="radio"]');
118
+ const index = current ? radios.indexOf(current) : -1;
119
+ // Only the horizontal keys mean something to the enclosing toolbar, so only they hand
120
+ // off at the ends; vertical keys keep cycling inside the group.
121
+ const isHorizontal = event.code === "ArrowLeft" || event.code === "ArrowRight";
122
+ const loop = !isInsideToolbar(group) || !isHorizontal;
123
+ const next = findNextRadio(group, radios, index, delta, loop);
124
+ const nextValue = next?.dataset["value"];
125
+ if (!next || nextValue === undefined) {
126
+ // Only a horizontal key at the end of a toolbar group is handed on; every other
127
+ // arrow stays contained in the group so it cannot scroll the page.
128
+ if (loop) {
129
+ event.preventDefault();
130
+ event.stopPropagation();
131
+ }
132
+ return;
133
+ }
134
+ event.preventDefault();
135
+ event.stopPropagation();
136
+ onChangeRef.current(nextValue, event);
137
+ next.focus();
138
+ };
139
+ return makeRadioGroupKeyboardNavigation({
140
+ onFocusPrevious: (event) => step(event, -1),
141
+ onFocusNext: (event) => step(event, 1),
142
+ }, { shouldPreventDefault: false, shouldStopPropagation: false });
143
+ }, [onChangeRef]);
144
+ return (_jsx(UiToolbarSegmentedControlContext.Provider, { value: contextValue, children: _jsx("div", { ref: groupRef, className: b(), ...accessibilityConfigToAttributes(accessibilityConfig), role: "radiogroup", "aria-disabled": isDisabled || undefined, "data-testid": dataTestId, onKeyDown: onKeyDown, [TOOLBAR_COMPOSITE_ATTR]: "", children: children }) }));
145
+ });
@@ -0,0 +1,12 @@
1
+ import { type KeyboardEvent, type MouseEvent } from "react";
2
+ /**
3
+ * @internal
4
+ */
5
+ export type UiToolbarSegmentedControlChangeEvent = MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>;
6
+ export interface IUiToolbarSegmentedControlContextValue {
7
+ value: string | undefined;
8
+ onChange: (value: string, event: UiToolbarSegmentedControlChangeEvent) => void;
9
+ isDisabled: boolean;
10
+ }
11
+ export declare const UiToolbarSegmentedControlContext: import("react").Context<IUiToolbarSegmentedControlContextValue | null>;
12
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiToolbarSegmentedControl/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAiB,MAAM,OAAO,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;AAExG,MAAM,WAAW,sCAAsC;IACnD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,oCAAoC,KAAK,IAAI,CAAC;IAC/E,UAAU,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,gCAAgC,wEAE5C,CAAC"}
@@ -0,0 +1,3 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { createContext } from "react";
3
+ export const UiToolbarSegmentedControlContext = createContext(null);
@@ -0,0 +1,51 @@
1
+ import { type KeyboardEvent, type MouseEvent, type ReactNode } from "react";
2
+ import { type IAccessibilityConfigBase } from "../../typings/accessibility.js";
3
+ import { type IUiDropdownButtonRenderProps } from "../UiDropdown/types.js";
4
+ /**
5
+ * @internal
6
+ */
7
+ export interface IUiToolbarSelectProps {
8
+ /**
9
+ * The current selection, or the prompt when `isPlaceholder` is set.
10
+ */
11
+ label: string;
12
+ /**
13
+ * "fixed" is 240px wide and truncates the label so the toolbar does not move as the selection
14
+ * changes.
15
+ * @defaultValue "hug"
16
+ */
17
+ width?: "hug" | "fixed";
18
+ /**
19
+ * Nothing is chosen yet; the label reads as prompt text.
20
+ * @defaultValue false
21
+ */
22
+ isPlaceholder?: boolean;
23
+ /**
24
+ * Held while the popup is on screen. Flips the chevron and keeps the pressed look.
25
+ * @defaultValue false
26
+ */
27
+ isOpen?: boolean;
28
+ isDisabled?: boolean;
29
+ /**
30
+ * Shown on hover and focus, for example the full name of a truncated label.
31
+ */
32
+ tooltip?: ReactNode;
33
+ onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
34
+ onKeyDown?: (event: KeyboardEvent<HTMLButtonElement>) => void;
35
+ id?: string;
36
+ dataId?: string;
37
+ dataTestId?: string;
38
+ accessibilityConfig?: IAccessibilityConfigBase;
39
+ /**
40
+ * Attributes from the `renderButton` callback of {@link UiDropdown}. Without them the trigger
41
+ * announces a listbox popup.
42
+ */
43
+ ariaAttributes?: IUiDropdownButtonRenderProps["ariaAttributes"];
44
+ }
45
+ /**
46
+ * Label-driven dropdown trigger of a toolbar.
47
+ *
48
+ * @internal
49
+ */
50
+ export declare const UiToolbarSelect: import("react").ForwardRefExoticComponent<IUiToolbarSelectProps & import("react").RefAttributes<HTMLButtonElement>>;
51
+ //# sourceMappingURL=UiToolbarSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiToolbarSelect.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiToolbarSelect/UiToolbarSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAc,MAAM,OAAO,CAAC;AAIxF,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAG/E,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAK3E;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACxB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;IACzD,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,wBAAwB,CAAC;IAC/C;;;OAGG;IACH,cAAc,CAAC,EAAE,4BAA4B,CAAC,gBAAgB,CAAC,CAAC;CACnE;AAID;;;;GAIG;AACH,eAAO,MAAM,eAAe,qHAkD1B,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { forwardRef } from "react";
4
+ import { simplifyText } from "@gooddata/util";
5
+ import { accessibilityConfigToAttributes } from "../../typings/utilities.js";
6
+ import { bem } from "../@utils/bem.js";
7
+ import { UiToolbarChevron } from "../UiToolbar/UiToolbarChevron.js";
8
+ import { UiToolbarItemTooltip } from "../UiToolbar/UiToolbarItemTooltip.js";
9
+ import { useToolbarItemRole } from "../UiToolbarButton/useToolbarItemRole.js";
10
+ const { b, e } = bem("gd-ui-kit-toolbar-select");
11
+ /**
12
+ * Label-driven dropdown trigger of a toolbar.
13
+ *
14
+ * @internal
15
+ */
16
+ export const UiToolbarSelect = forwardRef(function UiToolbarSelect({ label, width = "hug", isPlaceholder = false, isOpen = false, isDisabled, tooltip, onClick, onKeyDown, id, dataId, dataTestId, accessibilityConfig, ariaAttributes, }, ref) {
17
+ const { buttonProps } = useToolbarItemRole({ isDisabled, onClick, onKeyDown });
18
+ const testId = dataTestId ?? simplifyText(label);
19
+ const button = (_jsxs("button", { ref: ref, id: id, type: "button", className: b({ width, isPlaceholder, isActive: isOpen }), ...accessibilityConfigToAttributes(accessibilityConfig), title: width === "fixed" && tooltip === undefined ? label : undefined, "data-id": dataId, "data-testid": testId, "aria-haspopup": accessibilityConfig?.ariaHaspopup ?? "listbox", "aria-expanded": accessibilityConfig?.ariaExpanded ?? isOpen, ...buttonProps, ...ariaAttributes, children: [_jsx("span", { className: e("label"), children: label }), _jsx(UiToolbarChevron, { isOpen: isOpen })] }));
20
+ if (tooltip === undefined) {
21
+ return button;
22
+ }
23
+ return (_jsx(UiToolbarItemTooltip, { content: tooltip, isDisabled: isOpen, children: button }));
24
+ });
@@ -0,0 +1,75 @@
1
+ import { type KeyboardEvent, type MouseEvent, type Ref } from "react";
2
+ import { type IconType } from "../@types/icon.js";
3
+ import { type IUiDropdownButtonRenderProps } from "../UiDropdown/types.js";
4
+ /**
5
+ * @internal
6
+ */
7
+ export interface IUiToolbarStepperAccessibilityConfig {
8
+ /**
9
+ * Name of the value, for example "Zoom" or "Page".
10
+ */
11
+ ariaLabel: string;
12
+ /**
13
+ * Name of the increase control, for example "Zoom in" or "Next page".
14
+ */
15
+ incrementLabel: string;
16
+ /**
17
+ * Name of the decrease control, for example "Zoom out" or "Previous page".
18
+ */
19
+ decrementLabel: string;
20
+ }
21
+ /**
22
+ * @internal
23
+ */
24
+ export interface IUiToolbarStepperProps {
25
+ /**
26
+ * "value" adjusts a quantity with minus and plus. "pagination" moves through a sequence with
27
+ * left and right arrows and shows the value as a readout.
28
+ */
29
+ variant: "value" | "pagination";
30
+ /**
31
+ * The committed value as text, for example "100%" or "1 / 2".
32
+ */
33
+ value: string;
34
+ onStep: (direction: 1 | -1) => void;
35
+ /**
36
+ * @defaultValue true
37
+ */
38
+ canStepUp?: boolean;
39
+ /**
40
+ * @defaultValue true
41
+ */
42
+ canStepDown?: boolean;
43
+ isDisabled?: boolean;
44
+ accessibilityConfig: IUiToolbarStepperAccessibilityConfig;
45
+ decreaseIcon?: IconType;
46
+ increaseIcon?: IconType;
47
+ /**
48
+ * Makes the value of the "value" variant editable. Enter commits the typed text. Escape and blur
49
+ * revert the typed text to the committed value.
50
+ */
51
+ onCommit?: (value: string) => void;
52
+ /**
53
+ * Held while the preset list is on screen.
54
+ * @defaultValue false
55
+ */
56
+ isOpen?: boolean;
57
+ /**
58
+ * Attributes from the `renderButton` callback of a {@link UiDropdown} with `triggerRole: "combobox"`.
59
+ */
60
+ ariaAttributes?: IUiDropdownButtonRenderProps["ariaAttributes"];
61
+ inputRef?: Ref<HTMLInputElement>;
62
+ onClick?: (event: MouseEvent<HTMLInputElement>) => void;
63
+ /**
64
+ * Runs before the built-in key handling, which is skipped when the event is default-prevented.
65
+ */
66
+ onInputKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
67
+ dataTestId?: string;
68
+ }
69
+ /**
70
+ * Two side controls around a value. Each part is its own toolbar stop.
71
+ *
72
+ * @internal
73
+ */
74
+ export declare const UiToolbarStepper: import("react").ForwardRefExoticComponent<IUiToolbarStepperProps & import("react").RefAttributes<HTMLDivElement>>;
75
+ //# sourceMappingURL=UiToolbarStepper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiToolbarStepper.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiToolbarStepper/UiToolbarStepper.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,GAAG,EAAwB,MAAM,OAAO,CAAC;AAE9G,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAK3E;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACjD;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC;;;OAGG;IACH,OAAO,EAAE,OAAO,GAAG,YAAY,CAAC;IAChC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mBAAmB,EAAE,oCAAoC,CAAC;IAC1D,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,cAAc,CAAC,EAAE,4BAA4B,CAAC,gBAAgB,CAAC,CAAC;IAChE,QAAQ,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACxD;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAsBD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,mHA8H3B,CAAC"}
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { forwardRef, useState } from "react";
4
+ import { bem } from "../@utils/bem.js";
5
+ import { hasSystemModifier } from "../UiToolbar/rovingFocusUtils.js";
6
+ import { isPopupKey } from "../UiToolbarButton/useToolbarItemRole.js";
7
+ import { UiToolbarIconButton } from "../UiToolbarIconButton/UiToolbarIconButton.js";
8
+ const { b, e } = bem("gd-ui-kit-toolbar-stepper");
9
+ // The side buttons are not the popup trigger. An enclosing dropdown wrapper would otherwise toggle
10
+ // its popup on these keys and cancel the buttons' native click.
11
+ function keepPopupKeys(event) {
12
+ if (!isPopupKey(event)) {
13
+ return;
14
+ }
15
+ event.stopPropagation();
16
+ // Enter and Space keep their default so the button still clicks; the arrows would only scroll.
17
+ if (event.code === "ArrowUp" || event.code === "ArrowDown") {
18
+ event.preventDefault();
19
+ }
20
+ }
21
+ const DEFAULT_ICONS = {
22
+ value: { decrease: "minus", increase: "plus" },
23
+ pagination: { decrease: "navigateLeft", increase: "navigateRight" },
24
+ };
25
+ /**
26
+ * Two side controls around a value. Each part is its own toolbar stop.
27
+ *
28
+ * @internal
29
+ */
30
+ export const UiToolbarStepper = forwardRef(function UiToolbarStepper({ variant, value, onStep, canStepUp = true, canStepDown = true, isDisabled = false, accessibilityConfig, decreaseIcon, increaseIcon, onCommit, isOpen = false, ariaAttributes, inputRef, onClick, onInputKeyDown, dataTestId, }, ref) {
31
+ const isEditable = variant === "value" && onCommit !== undefined;
32
+ const [draft, setDraft] = useState(value);
33
+ const [committed, setCommitted] = useState(value);
34
+ if (committed !== value) {
35
+ setCommitted(value);
36
+ setDraft(value);
37
+ }
38
+ const handleKeyDown = (event) => {
39
+ // A disabled value is read-only but focusable; the keys that would open an enclosing
40
+ // dropdown are swallowed so the popup stays closed.
41
+ if (isDisabled) {
42
+ if (isPopupKey(event)) {
43
+ event.preventDefault();
44
+ event.stopPropagation();
45
+ }
46
+ return;
47
+ }
48
+ onInputKeyDown?.(event);
49
+ if (event.defaultPrevented) {
50
+ return;
51
+ }
52
+ // While an IME is composing, the arrows walk the candidate list and Enter accepts a
53
+ // candidate. Those presses belong to the input method, not to the stepper.
54
+ if (event.nativeEvent.isComposing) {
55
+ return;
56
+ }
57
+ // Modified arrows are left alone: Alt+ArrowDown opens the preset list of a combobox and
58
+ // the other combinations belong to the browser or the OS.
59
+ if (event.code === "ArrowUp" && !hasSystemModifier(event)) {
60
+ event.preventDefault();
61
+ event.stopPropagation();
62
+ if (canStepUp) {
63
+ onStep(1);
64
+ }
65
+ }
66
+ else if (event.code === "ArrowDown" && !hasSystemModifier(event)) {
67
+ event.preventDefault();
68
+ event.stopPropagation();
69
+ if (canStepDown) {
70
+ onStep(-1);
71
+ }
72
+ }
73
+ else if (event.code === "Enter") {
74
+ event.preventDefault();
75
+ event.stopPropagation();
76
+ onCommit?.(draft);
77
+ }
78
+ else if (event.code === "Escape") {
79
+ // Not stopped: an enclosing dropdown closes its preset list on the same key.
80
+ setDraft(value);
81
+ }
82
+ };
83
+ const icons = DEFAULT_ICONS[variant];
84
+ return (_jsxs("div", { ref: ref, className: b({ variant }), "data-testid": dataTestId, children: [_jsx(UiToolbarIconButton, { size: "small", icon: decreaseIcon ?? icons.decrease, label: accessibilityConfig.decrementLabel, isDisabled: isDisabled || !canStepDown, onClick: () => onStep(-1), onKeyDown: keepPopupKeys }), isEditable ? (_jsx("input", { ref: inputRef, className: e("value", { isEditable, isActive: isOpen }), type: "text", inputMode: "decimal", value: draft, "aria-label": accessibilityConfig.ariaLabel, "aria-disabled": isDisabled || undefined, readOnly: isDisabled, onChange: (event) => setDraft(event.target.value), onBlur: () => setDraft(value), onClick: isDisabled ? undefined : onClick, onKeyDown: handleKeyDown, ...ariaAttributes })) : (_jsx("span", { className: e("value", { isEditable }), role: "status", "aria-label": accessibilityConfig.ariaLabel, children: value })), isEditable ? (_jsx("span", { className: "sr-only", role: "status", "aria-label": accessibilityConfig.ariaLabel, children: value })) : null, _jsx(UiToolbarIconButton, { size: "small", icon: increaseIcon ?? icons.increase, label: accessibilityConfig.incrementLabel, isDisabled: isDisabled || !canStepUp, onClick: () => onStep(1), onKeyDown: keepPopupKeys })] }));
85
+ });
@@ -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, accessibilityHidden, variant, disabled, isOpen: isOpenProp, onOpen, onClose, onOpenChange, component, inlineAnchor, 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, component, inlineAnchor, anchorWrapperStyles, closeOnAnchorClick, delayGroup, }: 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,mBAA2B,EAC3B,OAAmB,EACnB,QAAQ,EACR,MAAM,EAAE,UAAU,EAClB,MAAM,EACN,OAAO,EACP,YAAY,EACZ,SAAiB,EACjB,YAAoB,EACpB,mBAAmB,GACtB,EAAE,eAAe,2CA4MjB"}
1
+ {"version":3,"file":"UiTooltip.d.ts","sourceRoot":"","sources":["../../../src/@ui/UiTooltip/UiTooltip.tsx"],"names":[],"mappings":"AA6BA,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,SAAiB,EACjB,YAAoB,EACpB,mBAAmB,EACnB,kBAA0B,EAC1B,UAAkB,GACrB,EAAE,eAAe,2CA+NjB"}
@@ -1,7 +1,7 @@
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, useFocus, useHover, useInteractions, } from "@floating-ui/react";
4
+ import { FloatingArrow, FloatingPortal, safePolygon, useClick, useDelayGroup, useDismiss, 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
7
  import { useCloseOnEscape } from "../hooks/useCloseOnEscape.js";
@@ -13,7 +13,7 @@ const { b, e } = bem("gd-ui-kit-tooltip");
13
13
  /**
14
14
  * @internal
15
15
  */
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, component = "div", inlineAnchor = false, 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, component = "div", inlineAnchor = false, anchorWrapperStyles, closeOnAnchorClick = false, delayGroup = false, }) {
17
17
  const [isOpenInternal, setIsOpen] = useState(false);
18
18
  const isControlled = isOpenProp !== undefined;
19
19
  const isOpen = !disabled && (isControlled ? isOpenProp : isOpenInternal || triggerBy.length === 0);
@@ -68,7 +68,7 @@ export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPla
68
68
  const triggerDimensions = useMemo(() => {
69
69
  const rect = refs.reference.current?.getBoundingClientRect?.();
70
70
  return getDimensionsFromRect(rect ?? null);
71
- }, [refs.reference, isOpen]); // eslint-disable-line react-hooks/exhaustive-deps
71
+ }, [refs.reference, isOpen]); // oxlint-disable-line react-hooks/exhaustive-deps
72
72
  // Use state + layoutEffect to get dimensions AFTER DOM paints
73
73
  // This fixes arrow positioning for *-end placements on initial render
74
74
  const [floatingDimensions, setFloatingDimensions] = useState({ width: 0, height: 0 });
@@ -86,14 +86,18 @@ export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPla
86
86
  // triggers are suppressed — otherwise an anchor that is shared with another
87
87
  // popover (e.g. the labels picker reusing the permission-menu button) would
88
88
  // toggle both.
89
+ // useHover does not read the group delay on its own; it has to be passed in.
90
+ const group = useDelayGroup(context, { enabled: delayGroup });
89
91
  const hover = useHover(context, {
90
92
  enabled: !isControlled && triggerBy.includes("hover"),
91
93
  move: false,
92
94
  handleClose: safePolygon({ requireIntent: true }),
93
- delay: {
94
- open: hoverOpenDelay,
95
- close: hoverCloseDelay,
96
- },
95
+ delay: delayGroup
96
+ ? group.delay
97
+ : {
98
+ open: hoverOpenDelay,
99
+ close: hoverCloseDelay,
100
+ },
97
101
  });
98
102
  const focus = useFocus(context, {
99
103
  enabled: !isControlled && triggerBy.includes("focus"),
@@ -101,6 +105,15 @@ export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPla
101
105
  const click = useClick(context, {
102
106
  enabled: !isControlled && triggerBy.includes("click"),
103
107
  });
108
+ // Outside press and Escape stay with the DOM-based hooks below. Stays enabled while closed:
109
+ // the close it emits cancels a hover open that is still waiting on its delay.
110
+ const anchorPressDismiss = useDismiss(context, {
111
+ enabled: closeOnAnchorClick && !isControlled,
112
+ referencePress: true,
113
+ referencePressEvent: "click",
114
+ outsidePress: false,
115
+ escapeKey: false,
116
+ });
104
117
  // Read the anchor lazily — floating-ui's `refs.reference` is a mutable
105
118
  // ref that only has a current value once the reference element mounts,
106
119
  // which is independent of our render cycle.
@@ -119,7 +132,12 @@ export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPla
119
132
  anchorRef: refs.reference,
120
133
  });
121
134
  useCloseOnEscape(isOpen, handleClose);
122
- const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, click]);
135
+ const { getReferenceProps, getFloatingProps } = useInteractions([
136
+ hover,
137
+ focus,
138
+ click,
139
+ anchorPressDismiss,
140
+ ]);
123
141
  const Component = component === "div" ? "div" : "span";
124
142
  return (_jsxs(_Fragment, { children: [_jsx(Component, { className: e("anchor", { inline: inlineAnchor }), ref: refs.setReference, style: anchorWrapperStyles, ...getReferenceProps(), children: anchor }), accessibilityHidden ? null : (_jsx("span", { className: "sr-only", id: id, children: isOpen
125
143
  ? typeof content === "function"
@@ -92,6 +92,18 @@ export interface IUiTooltipProps {
92
92
  * Custom styles for the anchor wrapper element
93
93
  */
94
94
  anchorWrapperStyles?: CSSProperties;
95
+ /**
96
+ * Close the tooltip when the anchor is clicked. Uncontrolled mode only.
97
+ * @defaultValue false
98
+ */
99
+ closeOnAnchorClick?: boolean;
100
+ /**
101
+ * Take the hover delays from an enclosing floating-ui `FloatingDelayGroup` instead of
102
+ * `hoverOpenDelay` / `hoverCloseDelay`. Sibling tooltips in the group open instantly while one
103
+ * of them was recently open.
104
+ * @defaultValue false
105
+ */
106
+ delayGroup?: boolean;
95
107
  }
96
108
  export type Dimensions = {
97
109
  width: number;
@@ -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;;;;OAIG;IACH,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;IACrB;;OAEG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;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;;;;OAIG;IACH,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;IACrB;;OAEG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,mBAAmB,CAAC,EAAE,aAAa,CAAC;IAEpC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;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":"useElementSize.d.ts","sourceRoot":"","sources":["../../../src/@ui/hooks/useElementSize.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAqC,MAAM,OAAO,CAAC;AAE/E;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,CAAC,EAAE,cAAc;;;;EAmC1E"}
1
+ {"version":3,"file":"useElementSize.d.ts","sourceRoot":"","sources":["../../../src/@ui/hooks/useElementSize.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAqC,MAAM,OAAO,CAAC;AAE/E;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,CAAC,EAAE,cAAc;;;;EAoC1E"}
@@ -7,6 +7,7 @@ export function useElementSize(deps) {
7
7
  const ref = useRef(null);
8
8
  const [height, setHeight] = useState(0);
9
9
  const [width, setWidth] = useState(0);
10
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
10
11
  useLayoutEffect(() => {
11
12
  const resizeObserver = new ResizeObserver((entries) => {
12
13
  const [entry] = entries;
@@ -27,7 +28,7 @@ export function useElementSize(deps) {
27
28
  setWidth(0);
28
29
  }
29
30
  };
30
- // eslint-disable-next-line react-hooks/exhaustive-deps
31
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
31
32
  }, deps ?? []);
32
33
  return {
33
34
  ref,
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tells whether the text of an element is cut off by its width. Attach the returned ref to the
3
+ * element with `white-space: nowrap` and `overflow: hidden`, and pass the rendered text so a
4
+ * new text in the same element is measured again; the answer also follows size changes.
5
+ *
6
+ * @internal
7
+ */
8
+ export declare function useIsTextTruncated(text: string): {
9
+ ref: (element: HTMLElement | null) => void;
10
+ isTruncated: boolean;
11
+ };
12
+ //# sourceMappingURL=useIsTextTruncated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsTextTruncated.d.ts","sourceRoot":"","sources":["../../../src/@ui/hooks/useIsTextTruncated.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC9C,GAAG,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,OAAO,CAAC;CACxB,CAwBA"}