@gooddata/sdk-ui-kit 11.41.0-alpha.2 → 11.41.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.
- package/esm/@ui/UiControlButton/UiControlButton.d.ts +7 -1
- package/esm/@ui/UiControlButton/UiControlButton.js +7 -2
- package/esm/@ui/UiListbox/UiListbox.js +6 -2
- package/esm/@ui/UiTags/UiTag.d.ts +1 -0
- package/esm/@ui/UiTags/UiTag.js +2 -2
- package/esm/@ui/UiTags/UiTags.d.ts +1 -1
- package/esm/@ui/UiTags/UiTags.js +13 -8
- package/esm/@ui/UiTags/types.d.ts +13 -0
- package/esm/AutoSize/AutoSize.js +25 -4
- package/esm/WidgetNotice/WidgetNotice.d.ts +26 -0
- package/esm/WidgetNotice/WidgetNotice.js +37 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/sdk-ui-kit.d.ts +47 -2
- package/esm/tsdoc-metadata.json +1 -1
- package/esm/utils/drag.d.ts +2 -1
- package/esm/utils/drag.js +1 -1
- package/esm/utils/scroll.d.ts +2 -1
- package/esm/utils/scroll.js +1 -1
- package/package.json +12 -12
- package/src/@ui/UiControlButton/UiControlButton.scss +67 -0
- package/src/@ui/UiTags/UiTags.scss +5 -0
- package/src/WidgetNotice/WidgetNotice.scss +114 -0
- package/styles/css/UiControlButton.css +48 -0
- package/styles/css/UiControlButton.css.map +1 -1
- package/styles/css/main.css +151 -0
- package/styles/css/main.css.map +1 -1
- package/styles/scss/main.scss +1 -0
|
@@ -10,6 +10,12 @@ export interface IUiControlButtonProps {
|
|
|
10
10
|
icon?: ReactNode;
|
|
11
11
|
titleExtension?: ReactNode;
|
|
12
12
|
subtitleExtension?: ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* `"stacked"` (default): title above subtitle, chevron beside subtitle.
|
|
15
|
+
* `"row"`: single full-width line, flat background, chevron pinned right.
|
|
16
|
+
*/
|
|
17
|
+
layout?: "stacked" | "row";
|
|
18
|
+
hideChevron?: boolean;
|
|
13
19
|
isOpen?: boolean;
|
|
14
20
|
isDraggable?: boolean;
|
|
15
21
|
isDragging?: boolean;
|
|
@@ -41,4 +47,4 @@ export interface IUiControlButtonProps {
|
|
|
41
47
|
*
|
|
42
48
|
* @internal
|
|
43
49
|
*/
|
|
44
|
-
export declare function UiControlButton({ title, titleClassName, subtitle, subtitleClassName, icon, titleExtension, subtitleExtension, isOpen, isDraggable, isDragging, isError, disabled, disabledTooltip, onClick, className, "data-testid": dataTestId, buttonRef, buttonId, dropdownId, ariaLabel }: IUiControlButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
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;
|
|
@@ -21,9 +21,12 @@ const TITLE_TOOLTIP_ALIGN_POINTS = [
|
|
|
21
21
|
*
|
|
22
22
|
* @internal
|
|
23
23
|
*/
|
|
24
|
-
export function UiControlButton({ title, titleClassName, subtitle, subtitleClassName, icon, titleExtension, subtitleExtension, 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, 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 hasSubtitle = subtitle !== undefined || subtitleExtension !== undefined;
|
|
28
|
+
// Trailing colon on the label, only when a subtitle follows it.
|
|
29
|
+
const showLabelColon = layout === "row" && hasSubtitle;
|
|
27
30
|
const onKeyDown = (event) => {
|
|
28
31
|
if (!isActionKey(event)) {
|
|
29
32
|
return;
|
|
@@ -36,6 +39,8 @@ export function UiControlButton({ title, titleClassName, subtitle, subtitleClass
|
|
|
36
39
|
onClick?.();
|
|
37
40
|
};
|
|
38
41
|
const button = (_jsxs("div", { id: buttonId, ref: buttonRef, className: cx(b({
|
|
42
|
+
layout,
|
|
43
|
+
hideChevron: !!hideChevron,
|
|
39
44
|
isOpen: !!isOpen,
|
|
40
45
|
isDraggable: !!isDraggable,
|
|
41
46
|
isDragging: !!isDragging,
|
|
@@ -43,7 +48,7 @@ export function UiControlButton({ title, titleClassName, subtitle, subtitleClass
|
|
|
43
48
|
disabled: !!disabled,
|
|
44
49
|
}), className), role: "button", tabIndex: 0, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-disabled": disabled, "aria-controls": isOpen ? dropdownId : undefined, "aria-describedby": showDisabledTooltip ? tooltipId : undefined, "aria-label": ariaLabel, "data-testid": dataTestId, onClick: disabled ? undefined : onClick, onKeyDown: onKeyDown, children: [icon ? _jsx("div", { className: e("icon"), children: icon }) : null, _jsxs("div", { className: e("content"), children: [
|
|
45
50
|
_jsxs("div", { className: e("title-row"), children: [
|
|
46
|
-
_jsx("div", { className: e("title"), children: _jsx(ShortenedText, { tooltipAlignPoints: TITLE_TOOLTIP_ALIGN_POINTS, className: titleClassName, children: title }) }), titleExtension] }),
|
|
51
|
+
_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] })
|
|
47
52
|
] }));
|
|
48
53
|
if (showDisabledTooltip) {
|
|
49
54
|
return (_jsx(UiTooltip, { id: tooltipId, anchor: button, content: disabledTooltip, triggerBy: ["focus"], arrowPlacement: "top", showArrow: true }));
|
|
@@ -135,9 +135,13 @@ export function UiListbox({ items, dataTestId, itemDataTestId, width, maxWidth,
|
|
|
135
135
|
type: "mouse",
|
|
136
136
|
newTab: e.ctrlKey || e.metaKey || e.button === 1,
|
|
137
137
|
});
|
|
138
|
-
}, item: item, isFocused: index === focusedIndex, isSelected: item.id === selectedItemId, isCompact: isCompact }) }, item.id)) : (
|
|
138
|
+
}, item: item, isFocused: index === focusedIndex, isSelected: item.id === selectedItemId, isCompact: isCompact }) }, item.id)) : (
|
|
139
|
+
// Static items (separators, headers) are presentational; `role="presentation"`
|
|
140
|
+
// keeps the <li> out of the a11y tree so it isn't exposed as a `listitem`,
|
|
141
|
+
// which is an invalid child of `role="listbox"` (only `option`/`group` allowed).
|
|
142
|
+
_jsx("li", { ref: (el) => {
|
|
139
143
|
itemRefs.current[index] = el;
|
|
140
|
-
}, "data-testid": testId, children: _jsx(StaticItemComponent, { item: item }) }, item.id ?? index));
|
|
144
|
+
}, role: "presentation", "data-testid": testId, children: _jsx(StaticItemComponent, { item: item }) }, item.id ?? index));
|
|
141
145
|
}) }) }));
|
|
142
146
|
}
|
|
143
147
|
const makeItemId = (listboxId, item) => item?.type === "interactive" ? `item-${listboxId}-${item.id}` : undefined;
|
package/esm/@ui/UiTags/UiTag.js
CHANGED
|
@@ -4,9 +4,9 @@ import { forwardRef } from "react";
|
|
|
4
4
|
import { bem } from "../@utils/bem.js";
|
|
5
5
|
import { UiTag as UiTagComponent } from "../UiTag/UiTag.js";
|
|
6
6
|
const { e } = bem("gd-ui-kit-tags");
|
|
7
|
-
export const UiTag = forwardRef(function UiTag({ tag, maxWidth, isDeletable, isDisabled, isFocused, deleteLabel, onDelete, onClick }, ref) {
|
|
7
|
+
export const UiTag = forwardRef(function UiTag({ tag, maxWidth, isDeletable, isDisabled, isFocused, size, deleteLabel, onDelete, onClick }, ref) {
|
|
8
8
|
const canBeDeleted = isDeletable && (tag.isDeletable ?? true);
|
|
9
|
-
return (_jsx("div", { className: e("tag", { isFocused: isFocused ?? false }), role: "listitem", style: { maxWidth }, children: _jsx(UiTagComponent, { ref: ref, tabIndex: -1, deleteTabIndex: -1, label: `${tag.label}`, isDeletable: canBeDeleted, isDisabled: isDisabled, accessibilityConfig: {
|
|
9
|
+
return (_jsx("div", { className: e("tag", { isFocused: isFocused ?? false }), role: "listitem", style: { maxWidth }, children: _jsx(UiTagComponent, { ref: ref, tabIndex: -1, deleteTabIndex: -1, label: `${tag.label}`, size: size, iconBefore: tag.iconBefore, isDeletable: canBeDeleted, isDisabled: isDisabled, accessibilityConfig: {
|
|
10
10
|
ariaLabel: tag.label,
|
|
11
11
|
deleteAriaLabel: `${deleteLabel ?? "Delete"} ${tag.label}`,
|
|
12
12
|
}, onDelete: (e) => {
|
|
@@ -2,4 +2,4 @@ import { type IUiTagsProps } from "./types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
*/
|
|
5
|
-
export declare function UiTags({ tags, tagOptions, addLabel, nameLabel, cancelLabel, closeLabel, saveLabel, noTagsLabel, moreLabel, removeLabel, creatableLabel, mode, canDeleteTags, canCreateTag, readOnly, onTagClick, onTagAdd, onTagRemove, accessibilityConfig }: IUiTagsProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function UiTags({ tags, tagOptions, addLabel, nameLabel, cancelLabel, closeLabel, saveLabel, noTagsLabel, moreLabel, removeLabel, creatableLabel, mode, size, canDeleteTags, canCreateTag, readOnly, onTagClick, onTagAdd, onTagRemove, accessibilityConfig, renderAddButton }: IUiTagsProps): import("react/jsx-runtime").JSX.Element;
|
package/esm/@ui/UiTags/UiTags.js
CHANGED
|
@@ -17,13 +17,15 @@ const defaultAccessibilityConfig = {};
|
|
|
17
17
|
/**
|
|
18
18
|
* @internal
|
|
19
19
|
*/
|
|
20
|
-
export function UiTags({ tags, tagOptions, addLabel = "Add tag", nameLabel = "Name", cancelLabel = "Cancel", closeLabel = "Close", saveLabel = "Save", noTagsLabel = "No tags", moreLabel = "More tags", removeLabel = "Remove", creatableLabel = "(Create new)", mode = "single-line", canDeleteTags = true, canCreateTag = true, readOnly = false, onTagClick = () => { }, onTagAdd = () => { }, onTagRemove = () => { }, accessibilityConfig = defaultAccessibilityConfig, }) {
|
|
20
|
+
export function UiTags({ tags, tagOptions, addLabel = "Add tag", nameLabel = "Name", cancelLabel = "Cancel", closeLabel = "Close", saveLabel = "Save", noTagsLabel = "No tags", moreLabel = "More tags", removeLabel = "Remove", creatableLabel = "(Create new)", mode = "single-line", size = "small", canDeleteTags = true, canCreateTag = true, readOnly = false, onTagClick = () => { }, onTagAdd = () => { }, onTagRemove = () => { }, accessibilityConfig = defaultAccessibilityConfig, renderAddButton, }) {
|
|
21
21
|
const inputRef = useRef(null);
|
|
22
22
|
const [popupOpen, setPopupOpen] = useState(false);
|
|
23
23
|
const popupId = useId();
|
|
24
24
|
const isDeletable = canDeleteTags && !readOnly;
|
|
25
|
-
|
|
26
|
-
const
|
|
25
|
+
// A custom add-button slot takes precedence over the built-in create-tag combobox.
|
|
26
|
+
const hasCustomAddButton = !!renderAddButton && !readOnly;
|
|
27
|
+
const isAddable = canCreateTag && !readOnly && !hasCustomAddButton;
|
|
28
|
+
const { rootRef, showedTags, hiddenTags, tagsContainerRef, hiddenTagsContainerRef, tooltipTagsContainerRef, addButtonRef, allContainerRef, tooltipWidth, availableWidth, lastAvailableWidth, setTooltipContainer, } = useResponsiveTags(tags, mode, [canCreateTag, canDeleteTags, readOnly, hasCustomAddButton]);
|
|
27
29
|
const items = [...showedTags, hiddenTags];
|
|
28
30
|
const { handleKeyDown, interactionState, onMoreOpen, onMoreClose, onAddOpen, onAddClose, showedFocusedIndex, hiddenFocusedIndex, tag, setTag, onTagClickHandler, onTagRemoveHandler, onTagAddHandler, } = useTagsInteractions(rootRef, tooltipTagsContainerRef, showedTags, hiddenTags, onTagClick, onTagAdd, onTagRemove);
|
|
29
31
|
const comboboxOptions = useMemo(() => {
|
|
@@ -37,7 +39,7 @@ export function UiTags({ tags, tagOptions, addLabel = "Add tag", nameLabel = "Na
|
|
|
37
39
|
}, [tagOptions, tags]);
|
|
38
40
|
return (_jsxs("div", { ref: rootRef, tabIndex: tags.length === 0 ? -1 : 0, onKeyDown: handleKeyDown, className: b({ readOnly, mode }), "aria-label": accessibilityConfig?.ariaLabel, "aria-labelledby": accessibilityConfig?.ariaLabelledBy, "aria-describedby": accessibilityConfig?.ariaDescribedBy, role: accessibilityConfig?.role ?? "list", children: [
|
|
39
41
|
_jsx("div", { className: e("shadow-container"), ref: allContainerRef, children: tags.map((tag) => {
|
|
40
|
-
return (_jsx(UiTag, { tag: tag, isDeletable: isDeletable }, getKey(tag, isDeletable, readOnly)));
|
|
42
|
+
return (_jsx(UiTag, { tag: tag, isDeletable: isDeletable, size: size }, getKey(tag, isDeletable, readOnly)));
|
|
41
43
|
}) }), _jsxs("div", { className: e("tags-container"), ref: tagsContainerRef, children: [items.map((tag, i) => {
|
|
42
44
|
if (Array.isArray(tag)) {
|
|
43
45
|
if (hiddenTags.length === 0) {
|
|
@@ -55,7 +57,7 @@ export function UiTags({ tags, tagOptions, addLabel = "Add tag", nameLabel = "Na
|
|
|
55
57
|
}, tabIndex: 0, children: hiddenTags.map((tag, i) => {
|
|
56
58
|
return (_jsx(UiTag, { tag: tag, deleteLabel: removeLabel, ref: (ref) => {
|
|
57
59
|
interactionState.current.tags[tag.id] = ref;
|
|
58
|
-
}, isDeletable: isDeletable, isDisabled: readOnly, isFocused: hiddenFocusedIndex === i, maxWidth: tooltipWidth, onDelete: onTagRemoveHandler, onClick: onTagClickHandler }, i));
|
|
60
|
+
}, isDeletable: isDeletable, isDisabled: readOnly, isFocused: hiddenFocusedIndex === i, size: size, maxWidth: tooltipWidth, onDelete: onTagRemoveHandler, onClick: onTagClickHandler }, i));
|
|
59
61
|
}) })), footer: ({ onClose }) => (_jsx(_Fragment, { children: _jsx(UiButton, { ref: (ref) => {
|
|
60
62
|
interactionState.current.close = ref;
|
|
61
63
|
}, label: closeLabel, variant: "secondary", onClick: () => {
|
|
@@ -64,8 +66,11 @@ export function UiTags({ tags, tagOptions, addLabel = "Add tag", nameLabel = "Na
|
|
|
64
66
|
}
|
|
65
67
|
return (_jsx(UiTag, { tag: tag, ref: (ref) => {
|
|
66
68
|
interactionState.current.tags[tag.id] = ref;
|
|
67
|
-
}, isDeletable: isDeletable, isDisabled: readOnly, deleteLabel: removeLabel, isFocused: showedFocusedIndex === i, onDelete: onTagRemoveHandler, onClick: onTagClickHandler, maxWidth: i === showedTags.length - 1 ? lastAvailableWidth : availableWidth }, getKey(tag, isDeletable, readOnly)));
|
|
68
|
-
}),
|
|
69
|
+
}, isDeletable: isDeletable, isDisabled: readOnly, deleteLabel: removeLabel, isFocused: showedFocusedIndex === i, size: size, onDelete: onTagRemoveHandler, onClick: onTagClickHandler, maxWidth: i === showedTags.length - 1 ? lastAvailableWidth : availableWidth }, getKey(tag, isDeletable, readOnly)));
|
|
70
|
+
}), hasCustomAddButton ? (_jsx("div", { className: e("add-button"), ref: addButtonRef, onClick: (e) => {
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
}, children: renderAddButton?.() })) : isAddable ? (_jsx("div", { className: e("add-button"), ref: addButtonRef, onClick: (e) => {
|
|
69
74
|
e.stopPropagation();
|
|
70
75
|
e.preventDefault();
|
|
71
76
|
}, children: _jsx(UiPopover, { id: popupId, onOpen: () => {
|
|
@@ -106,7 +111,7 @@ export function UiTags({ tags, tagOptions, addLabel = "Add tag", nameLabel = "Na
|
|
|
106
111
|
}, ref: (ref) => {
|
|
107
112
|
interactionState.current.save = ref;
|
|
108
113
|
} })
|
|
109
|
-
] })) }) })) : null, !isAddable && tags.length === 0 ? (_jsx("div", { className: e("empty-state"), children: noTagsLabel })) : null] })
|
|
114
|
+
] })) }) })) : null, !isAddable && !hasCustomAddButton && tags.length === 0 ? (_jsx("div", { className: e("empty-state"), children: noTagsLabel })) : null] })
|
|
110
115
|
] }));
|
|
111
116
|
}
|
|
112
117
|
function getKey(tag, isDeletable, readOnly) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
1
2
|
import { type IAccessibilityConfigBase } from "../../typings/accessibility.js";
|
|
3
|
+
import { type IconType } from "../@types/icon.js";
|
|
2
4
|
/**
|
|
3
5
|
* @internal
|
|
4
6
|
*/
|
|
@@ -18,10 +20,20 @@ export interface IUiTagsProps {
|
|
|
18
20
|
canCreateTag?: boolean;
|
|
19
21
|
canDeleteTags?: boolean;
|
|
20
22
|
mode?: "single-line" | "multi-line";
|
|
23
|
+
/** Visual size of the rendered tags. Defaults to "small". */
|
|
24
|
+
size?: "small" | "large";
|
|
21
25
|
onTagClick?: (tag: IUiTagDef) => void;
|
|
22
26
|
onTagAdd?: (tag: IUiTagDef) => void;
|
|
23
27
|
onTagRemove?: (tag: IUiTagDef) => void;
|
|
24
28
|
accessibilityConfig?: IAccessibilityConfigBase;
|
|
29
|
+
/**
|
|
30
|
+
* Renders custom content in the add-button slot — the last item in the tags row,
|
|
31
|
+
* in-flow with the tags so it sits right after them (and the "+N" overflow chip)
|
|
32
|
+
* and is included in the responsive overflow measurement. When provided, it
|
|
33
|
+
* replaces the built-in create-tag combobox; use it to host a bespoke picker
|
|
34
|
+
* while keeping the tag display and overflow behaviour. Ignored when readOnly.
|
|
35
|
+
*/
|
|
36
|
+
renderAddButton?: () => ReactNode;
|
|
25
37
|
}
|
|
26
38
|
/**
|
|
27
39
|
* @internal
|
|
@@ -30,4 +42,5 @@ export interface IUiTagDef {
|
|
|
30
42
|
id: string;
|
|
31
43
|
label: string;
|
|
32
44
|
isDeletable?: boolean;
|
|
45
|
+
iconBefore?: IconType;
|
|
33
46
|
}
|
package/esm/AutoSize/AutoSize.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
// (C) 2007-
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
// (C) 2007-2026 GoodData Corporation
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { throttle } from "lodash-es";
|
|
5
5
|
import { elementRegion } from "../utils/domUtilities.js";
|
|
6
6
|
/**
|
|
@@ -23,10 +23,31 @@ export function AutoSize({ children }) {
|
|
|
23
23
|
});
|
|
24
24
|
}, []);
|
|
25
25
|
const throttledUpdateSize = useMemo(() => throttle(updateSize, 250, { leading: false }), [updateSize]);
|
|
26
|
+
// Measure synchronously before paint so the first committed frame already has a real width
|
|
27
|
+
// instead of 0 (which made children shrink-wrap). This handles the initial render.
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
updateSize();
|
|
30
|
+
}, [updateSize]);
|
|
26
31
|
useEffect(() => {
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
const node = wrapperRef.current;
|
|
33
|
+
// Re-measure whenever the wrapper actually resizes. This catches the post-mount layout
|
|
34
|
+
// settle (e.g. a fullscreen overlay finishing its alignment a few frames later, where the
|
|
35
|
+
// synchronous measure above read a not-yet-final width - such as before a scrollbar
|
|
36
|
+
// disappears) as well as any later container or window resize - precisely, instead of
|
|
37
|
+
// guessing with a fixed timeout. Throttled to coalesce bursts during continuous resizing.
|
|
38
|
+
let observer;
|
|
39
|
+
if (node && typeof ResizeObserver !== "undefined") {
|
|
40
|
+
observer = new ResizeObserver(() => {
|
|
41
|
+
throttledUpdateSize();
|
|
42
|
+
});
|
|
43
|
+
observer.observe(node);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Fallback for environments without ResizeObserver.
|
|
47
|
+
window.addEventListener("resize", throttledUpdateSize);
|
|
48
|
+
}
|
|
29
49
|
return () => {
|
|
50
|
+
observer?.disconnect();
|
|
30
51
|
throttledUpdateSize.cancel();
|
|
31
52
|
window.removeEventListener("resize", throttledUpdateSize);
|
|
32
53
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
export type WidgetNoticeType = "info" | "success" | "warning" | "error";
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export interface IWidgetNoticeProps {
|
|
10
|
+
type?: WidgetNoticeType;
|
|
11
|
+
message: ReactNode;
|
|
12
|
+
action?: ReactNode;
|
|
13
|
+
detail?: ReactNode;
|
|
14
|
+
detailAction?: ReactNode;
|
|
15
|
+
expandLabel?: ReactNode;
|
|
16
|
+
collapseLabel?: ReactNode;
|
|
17
|
+
defaultExpanded?: boolean;
|
|
18
|
+
showIcon?: boolean;
|
|
19
|
+
onClose?: () => void;
|
|
20
|
+
closeButtonLabel?: string;
|
|
21
|
+
dataTestId?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export declare function WidgetNotice({ type, message, action, detail, detailAction, expandLabel, collapseLabel, defaultExpanded, showIcon, onClose, closeButtonLabel, dataTestId }: IWidgetNoticeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { useId, useState } from "react";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { bem } from "../@ui/@utils/bem.js";
|
|
6
|
+
import { UiIcon } from "../@ui/UiIcon/UiIcon.js";
|
|
7
|
+
import { UiIconButton } from "../@ui/UiIconButton/UiIconButton.js";
|
|
8
|
+
import { commonDialogMessages } from "../locales.js";
|
|
9
|
+
const { b, e } = bem("gd-ui-kit-widget-notice");
|
|
10
|
+
const ICON_BY_TYPE = {
|
|
11
|
+
info: "infoCircle",
|
|
12
|
+
success: "checkCircle",
|
|
13
|
+
warning: "warning",
|
|
14
|
+
error: "exclamationCircle",
|
|
15
|
+
};
|
|
16
|
+
const ICON_COLOR_BY_TYPE = {
|
|
17
|
+
info: "primary",
|
|
18
|
+
success: "success",
|
|
19
|
+
warning: "warning",
|
|
20
|
+
error: "error",
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export function WidgetNotice({ type = "info", message, action, detail, detailAction, expandLabel, collapseLabel, defaultExpanded = false, showIcon = true, onClose, closeButtonLabel, dataTestId, }) {
|
|
26
|
+
const intl = useIntl();
|
|
27
|
+
const detailId = useId();
|
|
28
|
+
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
29
|
+
const hasDetail = Boolean(detail);
|
|
30
|
+
const effectiveCloseButtonLabel = closeButtonLabel ?? intl.formatMessage(commonDialogMessages.close);
|
|
31
|
+
return (_jsxs("div", { className: b({ type, expandable: hasDetail }), "data-testid": dataTestId, children: [
|
|
32
|
+
_jsxs("div", { className: e("header"), children: [
|
|
33
|
+
_jsxs("div", { className: e("content"), children: [showIcon ? (_jsx("span", { className: e("icon"), "aria-hidden": "true", children: _jsx(UiIcon, { type: ICON_BY_TYPE[type], size: 16, color: ICON_COLOR_BY_TYPE[type] }) })) : null, _jsxs("div", { className: e("text"), children: [
|
|
34
|
+
_jsx("span", { className: e("message"), children: message }), action ? _jsx("span", { className: e("action"), children: action }) : null, hasDetail ? (_jsx("button", { type: "button", className: e("toggle"), "aria-expanded": isExpanded, "aria-controls": isExpanded ? detailId : undefined, onClick: () => setIsExpanded((wasExpanded) => !wasExpanded), children: isExpanded ? collapseLabel : expandLabel })) : null] })
|
|
35
|
+
] }), onClose ? (_jsx(UiIconButton, { icon: "cross", variant: "tertiary", size: "xsmall", label: effectiveCloseButtonLabel, onClick: onClose, dataTestId: "widget-notice-close" })) : null] }), hasDetail && isExpanded ? (_jsxs("div", { id: detailId, className: e("detail"), children: [
|
|
36
|
+
_jsx("div", { className: e("detail-text"), children: detail }), detailAction ? _jsx("div", { className: e("detail-action"), children: detailAction }) : null] })) : null] }));
|
|
37
|
+
}
|
package/esm/index.d.ts
CHANGED
|
@@ -95,6 +95,7 @@ export { ToastMessageList } from "./Messages/toasts/ToastsCenterMessage.js";
|
|
|
95
95
|
export { ToastsCenter, ToastsCenterContextProvider, ScreenReaderToast, } from "./Messages/toasts/ToastsCenter.js";
|
|
96
96
|
export { NonContextToastsInterop } from "./Messages/toasts/NonContextToastsInterop.js";
|
|
97
97
|
export { ToastsCenterContext, useToastsCenterValue, type IToastsCenterContext, } from "./Messages/toasts/context.js";
|
|
98
|
+
export { WidgetNotice, type IWidgetNoticeProps, type WidgetNoticeType } from "./WidgetNotice/WidgetNotice.js";
|
|
98
99
|
export type { IDialogBaseProps, IDialogProps, IConfirmDialogBaseProps, IExportDialogProps, IExportDialogBaseProps, IDialogCloseButtonProps, IExportDialogData, PageOrientation, PageSize, IExportTabularPdfDialogData, IExportTabularPdfDialogProps, } from "./Dialog/typings.js";
|
|
99
100
|
export { BackButton, type IBackButtonProps } from "./Dialog/BackButton.js";
|
|
100
101
|
export { ConfirmDialog } from "./Dialog/ConfirmDialog.js";
|
package/esm/index.js
CHANGED
|
@@ -87,6 +87,7 @@ export { ToastMessageList } from "./Messages/toasts/ToastsCenterMessage.js";
|
|
|
87
87
|
export { ToastsCenter, ToastsCenterContextProvider, ScreenReaderToast, } from "./Messages/toasts/ToastsCenter.js";
|
|
88
88
|
export { NonContextToastsInterop } from "./Messages/toasts/NonContextToastsInterop.js";
|
|
89
89
|
export { ToastsCenterContext, useToastsCenterValue, } from "./Messages/toasts/context.js";
|
|
90
|
+
export { WidgetNotice } from "./WidgetNotice/WidgetNotice.js";
|
|
90
91
|
export { BackButton } from "./Dialog/BackButton.js";
|
|
91
92
|
export { ConfirmDialog } from "./Dialog/ConfirmDialog.js";
|
|
92
93
|
export { ConfirmDialogBase } from "./Dialog/ConfirmDialogBase.js";
|
package/esm/sdk-ui-kit.d.ts
CHANGED
|
@@ -6767,6 +6767,12 @@ export declare interface IUiControlButtonProps {
|
|
|
6767
6767
|
icon?: ReactNode;
|
|
6768
6768
|
titleExtension?: ReactNode;
|
|
6769
6769
|
subtitleExtension?: ReactNode;
|
|
6770
|
+
/**
|
|
6771
|
+
* `"stacked"` (default): title above subtitle, chevron beside subtitle.
|
|
6772
|
+
* `"row"`: single full-width line, flat background, chevron pinned right.
|
|
6773
|
+
*/
|
|
6774
|
+
layout?: "stacked" | "row";
|
|
6775
|
+
hideChevron?: boolean;
|
|
6770
6776
|
isOpen?: boolean;
|
|
6771
6777
|
isDraggable?: boolean;
|
|
6772
6778
|
isDragging?: boolean;
|
|
@@ -8347,6 +8353,7 @@ export declare interface IUiTagDef {
|
|
|
8347
8353
|
id: string;
|
|
8348
8354
|
label: string;
|
|
8349
8355
|
isDeletable?: boolean;
|
|
8356
|
+
iconBefore?: IconType;
|
|
8350
8357
|
}
|
|
8351
8358
|
|
|
8352
8359
|
/**
|
|
@@ -8368,10 +8375,20 @@ export declare interface IUiTagsProps {
|
|
|
8368
8375
|
canCreateTag?: boolean;
|
|
8369
8376
|
canDeleteTags?: boolean;
|
|
8370
8377
|
mode?: "single-line" | "multi-line";
|
|
8378
|
+
/** Visual size of the rendered tags. Defaults to "small". */
|
|
8379
|
+
size?: "small" | "large";
|
|
8371
8380
|
onTagClick?: (tag: IUiTagDef) => void;
|
|
8372
8381
|
onTagAdd?: (tag: IUiTagDef) => void;
|
|
8373
8382
|
onTagRemove?: (tag: IUiTagDef) => void;
|
|
8374
8383
|
accessibilityConfig?: IAccessibilityConfigBase;
|
|
8384
|
+
/**
|
|
8385
|
+
* Renders custom content in the add-button slot — the last item in the tags row,
|
|
8386
|
+
* in-flow with the tags so it sits right after them (and the "+N" overflow chip)
|
|
8387
|
+
* and is included in the responsive overflow measurement. When provided, it
|
|
8388
|
+
* replaces the built-in create-tag combobox; use it to host a bespoke picker
|
|
8389
|
+
* while keeping the tag display and overflow behaviour. Ignored when readOnly.
|
|
8390
|
+
*/
|
|
8391
|
+
renderAddButton?: () => ReactNode;
|
|
8375
8392
|
}
|
|
8376
8393
|
|
|
8377
8394
|
/**
|
|
@@ -8864,6 +8881,24 @@ export declare interface IWebComponentsOptions {
|
|
|
8864
8881
|
unit?: UnitsType;
|
|
8865
8882
|
}
|
|
8866
8883
|
|
|
8884
|
+
/**
|
|
8885
|
+
* @internal
|
|
8886
|
+
*/
|
|
8887
|
+
export declare interface IWidgetNoticeProps {
|
|
8888
|
+
type?: WidgetNoticeType;
|
|
8889
|
+
message: ReactNode;
|
|
8890
|
+
action?: ReactNode;
|
|
8891
|
+
detail?: ReactNode;
|
|
8892
|
+
detailAction?: ReactNode;
|
|
8893
|
+
expandLabel?: ReactNode;
|
|
8894
|
+
collapseLabel?: ReactNode;
|
|
8895
|
+
defaultExpanded?: boolean;
|
|
8896
|
+
showIcon?: boolean;
|
|
8897
|
+
onClose?: () => void;
|
|
8898
|
+
closeButtonLabel?: string;
|
|
8899
|
+
dataTestId?: string;
|
|
8900
|
+
}
|
|
8901
|
+
|
|
8867
8902
|
/**
|
|
8868
8903
|
* @internal
|
|
8869
8904
|
*/
|
|
@@ -10156,7 +10191,7 @@ export declare function UiConfirmDialog({ title, description, confirmLabel, conf
|
|
|
10156
10191
|
*
|
|
10157
10192
|
* @internal
|
|
10158
10193
|
*/
|
|
10159
|
-
export declare function UiControlButton({ title, titleClassName, subtitle, subtitleClassName, icon, titleExtension, subtitleExtension, isOpen, isDraggable, isDragging, isError, disabled, disabledTooltip, onClick, className, "data-testid": dataTestId, buttonRef, buttonId, dropdownId, ariaLabel }: IUiControlButtonProps): JSX.Element;
|
|
10194
|
+
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): JSX.Element;
|
|
10160
10195
|
|
|
10161
10196
|
/**
|
|
10162
10197
|
* @internal
|
|
@@ -10592,7 +10627,7 @@ export declare type UiTagProps = {
|
|
|
10592
10627
|
/**
|
|
10593
10628
|
* @internal
|
|
10594
10629
|
*/
|
|
10595
|
-
export declare function UiTags({ tags, tagOptions, addLabel, nameLabel, cancelLabel, closeLabel, saveLabel, noTagsLabel, moreLabel, removeLabel, creatableLabel, mode, canDeleteTags, canCreateTag, readOnly, onTagClick, onTagAdd, onTagRemove, accessibilityConfig }: IUiTagsProps): JSX.Element;
|
|
10630
|
+
export declare function UiTags({ tags, tagOptions, addLabel, nameLabel, cancelLabel, closeLabel, saveLabel, noTagsLabel, moreLabel, removeLabel, creatableLabel, mode, size, canDeleteTags, canCreateTag, readOnly, onTagClick, onTagAdd, onTagRemove, accessibilityConfig, renderAddButton }: IUiTagsProps): JSX.Element;
|
|
10596
10631
|
|
|
10597
10632
|
/**
|
|
10598
10633
|
* Single-line text input with optional label and leading / trailing icons.
|
|
@@ -11239,6 +11274,16 @@ export declare type VariantTooltip = "tooltip";
|
|
|
11239
11274
|
*/
|
|
11240
11275
|
export declare type VerticalPosition = "top" | "center" | "bottom";
|
|
11241
11276
|
|
|
11277
|
+
/**
|
|
11278
|
+
* @internal
|
|
11279
|
+
*/
|
|
11280
|
+
export declare function WidgetNotice({ type, message, action, detail, detailAction, expandLabel, collapseLabel, defaultExpanded, showIcon, onClose, closeButtonLabel, dataTestId }: IWidgetNoticeProps): JSX.Element;
|
|
11281
|
+
|
|
11282
|
+
/**
|
|
11283
|
+
* @internal
|
|
11284
|
+
*/
|
|
11285
|
+
export declare type WidgetNoticeType = "info" | "success" | "warning" | "error";
|
|
11286
|
+
|
|
11242
11287
|
/**
|
|
11243
11288
|
* @internal
|
|
11244
11289
|
*/
|
package/esm/tsdoc-metadata.json
CHANGED
package/esm/utils/drag.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DebouncedFunc } from "lodash-es";
|
|
1
2
|
/**
|
|
2
3
|
* Custom event name for goodstrap drag events.
|
|
3
4
|
* Used by Overlay to handle closeOnMouseDrag and other drag-related behaviors.
|
|
@@ -9,4 +10,4 @@ export declare const GOODSTRAP_DRAG_EVENT = "goodstrap.drag";
|
|
|
9
10
|
* This event is throttled by default
|
|
10
11
|
* @internal
|
|
11
12
|
*/
|
|
12
|
-
export declare const handleOnGoodstrapDragEvent:
|
|
13
|
+
export declare const handleOnGoodstrapDragEvent: DebouncedFunc<() => void>;
|
package/esm/utils/drag.js
CHANGED
package/esm/utils/scroll.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DebouncedFunc } from "lodash-es";
|
|
1
2
|
/**
|
|
2
3
|
* Custom event name for goodstrap scroll events.
|
|
3
4
|
* Used by Overlay to handle closeOnParentScroll and other scroll-related behaviors.
|
|
@@ -9,4 +10,4 @@ export declare const GOODSTRAP_SCROLLED_EVENT = "goodstrap.scrolled";
|
|
|
9
10
|
* This event is throttled by default
|
|
10
11
|
* @internal
|
|
11
12
|
*/
|
|
12
|
-
export declare const handleOnScrollEvent:
|
|
13
|
+
export declare const handleOnScrollEvent: DebouncedFunc<(node: HTMLElement) => void>;
|
package/esm/utils/scroll.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-ui-kit",
|
|
3
|
-
"version": "11.41.0-alpha.
|
|
3
|
+
"version": "11.41.0-alpha.4",
|
|
4
4
|
"description": "GoodData SDK - UI Building Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "GoodData Corporation",
|
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
"ts-invariant": "0.10.3",
|
|
75
75
|
"tslib": "2.8.1",
|
|
76
76
|
"unified": "^11.0.5",
|
|
77
|
-
"uuid": "11.1.
|
|
78
|
-
"@gooddata/sdk-backend-spi": "11.41.0-alpha.
|
|
79
|
-
"@gooddata/sdk-
|
|
80
|
-
"@gooddata/sdk-
|
|
81
|
-
"@gooddata/
|
|
82
|
-
"@gooddata/
|
|
77
|
+
"uuid": "11.1.1",
|
|
78
|
+
"@gooddata/sdk-backend-spi": "11.41.0-alpha.4",
|
|
79
|
+
"@gooddata/sdk-ui": "11.41.0-alpha.4",
|
|
80
|
+
"@gooddata/sdk-model": "11.41.0-alpha.4",
|
|
81
|
+
"@gooddata/util": "11.41.0-alpha.4",
|
|
82
|
+
"@gooddata/sdk-ui-theme-provider": "11.41.0-alpha.4"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@microsoft/api-documenter": "^7.17.0",
|
|
@@ -128,11 +128,11 @@
|
|
|
128
128
|
"typescript": "5.9.3",
|
|
129
129
|
"vitest": "4.1.8",
|
|
130
130
|
"vitest-dom": "0.1.1",
|
|
131
|
-
"@gooddata/eslint-config": "11.41.0-alpha.
|
|
132
|
-
"@gooddata/oxlint-config": "11.41.0-alpha.
|
|
133
|
-
"@gooddata/reference-workspace": "11.41.0-alpha.
|
|
134
|
-
"@gooddata/sdk-backend-mockingbird": "11.41.0-alpha.
|
|
135
|
-
"@gooddata/stylelint-config": "11.41.0-alpha.
|
|
131
|
+
"@gooddata/eslint-config": "11.41.0-alpha.4",
|
|
132
|
+
"@gooddata/oxlint-config": "11.41.0-alpha.4",
|
|
133
|
+
"@gooddata/reference-workspace": "11.41.0-alpha.4",
|
|
134
|
+
"@gooddata/sdk-backend-mockingbird": "11.41.0-alpha.4",
|
|
135
|
+
"@gooddata/stylelint-config": "11.41.0-alpha.4"
|
|
136
136
|
},
|
|
137
137
|
"peerDependencies": {
|
|
138
138
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -115,6 +115,73 @@ $drag-handle-icon: "data:image/svg+xml,%3Csvg width='7' height='26' viewBox='0 0
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// Single full-width line, flat (no border/gradient), chevron pinned right.
|
|
119
|
+
// Declared after the state modifiers so its flat `::after` wins.
|
|
120
|
+
&--layout-row {
|
|
121
|
+
width: 100%;
|
|
122
|
+
max-width: none;
|
|
123
|
+
box-sizing: border-box;
|
|
124
|
+
font-size: 14px;
|
|
125
|
+
border-radius: 0;
|
|
126
|
+
padding-top: 15px;
|
|
127
|
+
padding-bottom: 15px;
|
|
128
|
+
// Room for the pinned chevron
|
|
129
|
+
padding-right: 28px;
|
|
130
|
+
|
|
131
|
+
// Flat: no chip border or open-state gradient.
|
|
132
|
+
&::after {
|
|
133
|
+
display: none;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#{$root}__icon {
|
|
137
|
+
display: flex;
|
|
138
|
+
height: 100%;
|
|
139
|
+
align-items: center;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// One left-aligned line; chevron is taken out of flow.
|
|
143
|
+
#{$root}__content {
|
|
144
|
+
flex-direction: row;
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: flex-start;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// min-width: 0 lets a long label ellipsise instead of overflowing.
|
|
150
|
+
#{$root}__title-row {
|
|
151
|
+
flex: 0 1 auto;
|
|
152
|
+
min-width: 0;
|
|
153
|
+
max-width: 100%;
|
|
154
|
+
margin-right: 5px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#{$root}__title--withColon::after {
|
|
158
|
+
content: ":";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#{$root}__subtitle-row {
|
|
162
|
+
flex: 1 1 auto;
|
|
163
|
+
min-width: 0;
|
|
164
|
+
max-width: none;
|
|
165
|
+
|
|
166
|
+
// Chevron
|
|
167
|
+
&::after {
|
|
168
|
+
position: absolute;
|
|
169
|
+
top: 50%;
|
|
170
|
+
// `content` is inset by padding-right lands it 12px from the button edge.
|
|
171
|
+
right: -16px;
|
|
172
|
+
margin-left: 0;
|
|
173
|
+
font-size: 18px;
|
|
174
|
+
transform: translateY(-50%);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
&--hideChevron {
|
|
180
|
+
#{$root}__subtitle-row::after {
|
|
181
|
+
display: none;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
118
185
|
&__icon {
|
|
119
186
|
position: relative;
|
|
120
187
|
z-index: 1;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
$tags_line_height: 20px;
|
|
6
6
|
$tags_tooltip_max_width: 280px;
|
|
7
|
+
$tags_tooltip_viewport_margin: 160px;
|
|
7
8
|
|
|
8
9
|
@mixin tag($margin-right: 5px) {
|
|
9
10
|
.gd-ui-kit-tags__tag {
|
|
@@ -135,6 +136,10 @@ $tags_tooltip_max_width: 280px;
|
|
|
135
136
|
display: flex;
|
|
136
137
|
max-width: $tags_tooltip_max_width;
|
|
137
138
|
width: $tags_tooltip_max_width;
|
|
139
|
+
max-height: calc(100vh - #{$tags_tooltip_viewport_margin});
|
|
140
|
+
overflow-x: hidden;
|
|
141
|
+
overflow-y: auto;
|
|
142
|
+
overscroll-behavior: contain;
|
|
138
143
|
flex-direction: row;
|
|
139
144
|
flex-wrap: wrap;
|
|
140
145
|
gap: 5px;
|