@gooddata/sdk-ui-dashboard 11.40.0-alpha.2 → 11.40.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.
@@ -1,3 +1,3 @@
1
- export declare const LIB_VERSION = "11.40.0-alpha.2";
1
+ export declare const LIB_VERSION = "11.40.0-alpha.4";
2
2
  export declare const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
3
3
  export declare const LIB_NAME = "@gooddata/sdk-ui-dashboard";
package/esm/__version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // (C) 2021 GoodData Corporation
2
2
  // DO NOT CHANGE THIS FILE, IT IS RE-GENERATED ON EVERY BUILD
3
- export const LIB_VERSION = "11.40.0-alpha.2";
3
+ export const LIB_VERSION = "11.40.0-alpha.4";
4
4
  export const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
5
5
  export const LIB_NAME = "@gooddata/sdk-ui-dashboard";
@@ -8,5 +8,5 @@ export function DraggableInsightList({ recalculateSizeReference, searchAutofocus
8
8
  useEffect(() => {
9
9
  flexRef.current?.updateSize();
10
10
  }, [recalculateSizeReference]);
11
- return (_jsx("div", { className: "gd-visualizations-list gd-flex-item-stretch gd-flex-row-container", children: _jsx(FlexDimensions, { ref: flexRef, measureHeight: true, measureWidth: false, className: "visualizations-flex-dimensions", children: _jsx(DraggableInsightListCore, { WrapInsightListItemWithDragComponent: WrapInsightListItemWithDragComponent, searchAutofocus: searchAutofocus, enableDescriptions: enableDescriptions }) }) }));
11
+ return (_jsx("div", { className: "gd-visualizations-list gd-flex-item-stretch gd-flex-row-container", children: _jsx(FlexDimensions, { ref: flexRef, measureHeight: true, measureWidth: true, className: "visualizations-flex-dimensions", children: _jsx(DraggableInsightListCore, { WrapInsightListItemWithDragComponent: WrapInsightListItemWithDragComponent, searchAutofocus: searchAutofocus, enableDescriptions: enableDescriptions }) }) }));
12
12
  }
@@ -7,5 +7,5 @@ interface IDraggableInsightListItemWrapperProps extends IInsightListItemProps {
7
7
  insight: IInsight;
8
8
  }
9
9
  export declare function DraggableInsightListItemBody(props: CustomDashboardInsightListItemComponentProps): import("react/jsx-runtime").JSX.Element;
10
- export declare function DraggableInsightListItemWrapper({ WrapInsightListItemWithDragComponent, className, isLocked, title, description, showDescriptionPanel, type, updated, insight, onDescriptionPanelOpen, metadataTimeZone, useRichText, useReferences, LoadingComponent, filters }: IDraggableInsightListItemWrapperProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function DraggableInsightListItemWrapper({ WrapInsightListItemWithDragComponent, className, isLocked, title, description, showDescriptionPanel, type, updated, insight, onDescriptionPanelOpen, metadataTimeZone, useRichText, useReferences, LoadingComponent, filters, width }: IDraggableInsightListItemWrapperProps): import("react/jsx-runtime").JSX.Element;
11
11
  export {};
@@ -5,7 +5,7 @@ export function DraggableInsightListItemBody(props) {
5
5
  const { className } = props;
6
6
  return (_jsx("div", { className: className, children: _jsx(InsightListItem, { ...props }) }));
7
7
  }
8
- export function DraggableInsightListItemWrapper({ WrapInsightListItemWithDragComponent, className, isLocked, title, description, showDescriptionPanel, type, updated, insight, onDescriptionPanelOpen, metadataTimeZone, useRichText, useReferences, LoadingComponent, filters, }) {
8
+ export function DraggableInsightListItemWrapper({ WrapInsightListItemWithDragComponent, className, isLocked, title, description, showDescriptionPanel, type, updated, insight, onDescriptionPanelOpen, metadataTimeZone, useRichText, useReferences, LoadingComponent, filters, width, }) {
9
9
  return (_jsx(DraggableInsightListItem, { WrapInsightListItemWithDragComponent: WrapInsightListItemWithDragComponent, ListItemComponent: DraggableInsightListItemBody, listItemComponentProps: {
10
10
  className,
11
11
  isLocked,
@@ -20,5 +20,6 @@ export function DraggableInsightListItemWrapper({ WrapInsightListItemWithDragCom
20
20
  useReferences,
21
21
  LoadingComponent,
22
22
  filters,
23
+ width,
23
24
  }, insight: insight }));
24
25
  }
@@ -1,9 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
3
3
  import { useWidgetSelection } from "../../../model/react/useWidgetSelection.js";
4
4
  import { selectSettings } from "../../../model/store/config/configSelectors.js";
5
5
  import { CreationPanel } from "./CreationPanel.js";
6
6
  import { FloatingToolbar } from "./FloatingToolbar.js";
7
+ import { SidebarResizeChrome } from "./SidebarResizeChrome.js";
7
8
  /**
8
9
  * @internal
9
10
  */
@@ -13,10 +14,16 @@ export function SidebarConfigurationPanel(props) {
13
14
  const DeleteDropZoneComponent = props.DeleteDropZoneComponent;
14
15
  const settings = useDashboardSelector(selectSettings);
15
16
  const enableEnhancedInsightPicker = settings?.enableEnhancedInsightPicker ?? false;
17
+ // const enableDashboardSidebarResize = settings?.enableDashboardSidebarResize ?? false;
18
+ const enableDashboardSidebarResize = true;
16
19
  if (enableEnhancedInsightPicker) {
17
20
  return _jsx(FloatingToolbar, {});
18
21
  }
19
- return (_jsxs("div", { className: "col gd-flex-item gd-sidebar-container", onClick: deselectWidgets, children: [
22
+ const content = (_jsxs(_Fragment, { children: [
20
23
  _jsx("div", { className: "flex-panel-full-height", children: _jsx(CreationPanel, { className: configurationPanelClassName, WrapCreatePanelItemWithDragComponent: WrapCreatePanelItemWithDragComponent, WrapInsightListItemWithDragComponent: WrapInsightListItemWithDragComponent, AttributeFilterComponentSet: AttributeFilterComponentSet, InsightWidgetComponentSet: InsightWidgetComponentSet, RichTextWidgetComponentSet: RichTextWidgetComponentSet, VisualizationSwitcherWidgetComponentSet: VisualizationSwitcherWidgetComponentSet, DashboardLayoutWidgetComponentSet: DashboardLayoutWidgetComponentSet }) }), _jsx(DeleteDropZoneComponent, {})
21
24
  ] }));
25
+ if (enableDashboardSidebarResize) {
26
+ return _jsx(SidebarResizeChrome, { onContainerClick: deselectWidgets, children: content });
27
+ }
28
+ return (_jsx("div", { className: "col gd-flex-item gd-sidebar-container", onClick: deselectWidgets, children: content }));
22
29
  }
@@ -0,0 +1,21 @@
1
+ import { type ReactElement, type ReactNode } from "react";
2
+ /**
3
+ * Internal presentational wrapper that holds the dashboard sidebar drag-to-resize state.
4
+ *
5
+ * Owns: committed width (persisted to localStorage), live drag width (only used for the dashed
6
+ * indicator transform), drag phase, and a window-width tracker that enforces a minimum editor
7
+ * canvas width without overwriting the user's persisted preference.
8
+ *
9
+ * Renders `.gd-sidebar-container` itself so the resize handle can be positioned absolutely inside
10
+ * it — that element is sticky+100vh, so the handle (and the grip centered inside it) tracks the
11
+ * visible viewport rather than the full dashboard-root height.
12
+ *
13
+ * Drag uses `setPointerCapture` on the handle button so pointer events keep flowing to it even
14
+ * when the cursor leaves the 6 px hit area. No DOM overlay or window-level listener is needed.
15
+ *
16
+ * @internal
17
+ */
18
+ export declare function SidebarResizeChrome({ onContainerClick, children }: {
19
+ onContainerClick?: () => void;
20
+ children: ReactNode;
21
+ }): ReactElement;
@@ -0,0 +1,112 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useEffect, useRef, useState, } from "react";
4
+ import cx from "classnames";
5
+ import { clamp } from "lodash-es";
6
+ import { useLocalStorage } from "@gooddata/sdk-ui";
7
+ import { makeKeyboardNavigation, useCloseOnEscape } from "@gooddata/sdk-ui-kit";
8
+ const SIDEBAR_MIN_WIDTH = 230;
9
+ const SIDEBAR_MAX_WIDTH = 500;
10
+ const EDITOR_MIN_WIDTH = 960;
11
+ const KEYBOARD_STEP = 10;
12
+ const LOCAL_STORAGE_KEY = "gd-dashboard-sidebar-width";
13
+ const handleSeparatorKeyboardNavigation = makeKeyboardNavigation({
14
+ shrink: [{ code: "ArrowLeft" }],
15
+ grow: [{ code: "ArrowRight" }],
16
+ minimize: [{ code: "Home" }],
17
+ maximize: [{ code: "End" }],
18
+ });
19
+ /**
20
+ * Returns the current `window.innerWidth`, kept in sync with the `resize` event.
21
+ */
22
+ function useWindowWidth() {
23
+ const [width, setWidth] = useState(() => window.innerWidth);
24
+ useEffect(() => {
25
+ const handleResize = () => setWidth(window.innerWidth);
26
+ window.addEventListener("resize", handleResize);
27
+ return () => window.removeEventListener("resize", handleResize);
28
+ }, []);
29
+ return width;
30
+ }
31
+ /**
32
+ * Internal presentational wrapper that holds the dashboard sidebar drag-to-resize state.
33
+ *
34
+ * Owns: committed width (persisted to localStorage), live drag width (only used for the dashed
35
+ * indicator transform), drag phase, and a window-width tracker that enforces a minimum editor
36
+ * canvas width without overwriting the user's persisted preference.
37
+ *
38
+ * Renders `.gd-sidebar-container` itself so the resize handle can be positioned absolutely inside
39
+ * it — that element is sticky+100vh, so the handle (and the grip centered inside it) tracks the
40
+ * visible viewport rather than the full dashboard-root height.
41
+ *
42
+ * Drag uses `setPointerCapture` on the handle button so pointer events keep flowing to it even
43
+ * when the cursor leaves the 6 px hit area. No DOM overlay or window-level listener is needed.
44
+ *
45
+ * @internal
46
+ */
47
+ export function SidebarResizeChrome({ onContainerClick, children, }) {
48
+ const [dragWidth, setDragWidth] = useState(null);
49
+ const dragOriginRef = useRef(null);
50
+ const containerWidth = useWindowWidth();
51
+ const [persistedWidth, setPersistedWidth] = useLocalStorage(LOCAL_STORAGE_KEY, SIDEBAR_MIN_WIDTH);
52
+ const effectiveMax = clamp(containerWidth - EDITOR_MIN_WIDTH, SIDEBAR_MIN_WIDTH, SIDEBAR_MAX_WIDTH);
53
+ const effectiveWidth = clamp(persistedWidth, SIDEBAR_MIN_WIDTH, effectiveMax);
54
+ const isDragging = dragWidth !== null;
55
+ const isResizable = effectiveMax > SIDEBAR_MIN_WIDTH;
56
+ const endDrag = () => {
57
+ dragOriginRef.current = null;
58
+ setDragWidth(null);
59
+ };
60
+ useCloseOnEscape(isDragging, endDrag);
61
+ const handleKeyDown = handleSeparatorKeyboardNavigation({
62
+ shrink: () => setPersistedWidth(clamp(effectiveWidth - KEYBOARD_STEP, SIDEBAR_MIN_WIDTH, effectiveMax)),
63
+ grow: () => setPersistedWidth(clamp(effectiveWidth + KEYBOARD_STEP, SIDEBAR_MIN_WIDTH, effectiveMax)),
64
+ minimize: () => setPersistedWidth(SIDEBAR_MIN_WIDTH),
65
+ maximize: () => setPersistedWidth(effectiveMax),
66
+ });
67
+ const handlePointerDown = (event) => {
68
+ event.preventDefault();
69
+ event.currentTarget.setPointerCapture(event.pointerId);
70
+ dragOriginRef.current = {
71
+ clientX: event.clientX,
72
+ width: effectiveWidth,
73
+ pointerId: event.pointerId,
74
+ };
75
+ setDragWidth(effectiveWidth);
76
+ };
77
+ const handlePointerMove = (event) => {
78
+ if (event.pointerId !== dragOriginRef.current?.pointerId) {
79
+ return;
80
+ }
81
+ const width = widthFromClientX(event.clientX);
82
+ if (width !== null) {
83
+ setDragWidth(width);
84
+ }
85
+ };
86
+ const handlePointerUp = (event) => {
87
+ if (event.pointerId !== dragOriginRef.current?.pointerId) {
88
+ return;
89
+ }
90
+ const width = widthFromClientX(event.clientX);
91
+ if (width !== null) {
92
+ setPersistedWidth(width);
93
+ }
94
+ endDrag();
95
+ };
96
+ const widthFromClientX = (clientX) => {
97
+ const origin = dragOriginRef.current;
98
+ if (!origin) {
99
+ return null;
100
+ }
101
+ return clamp(origin.width + (clientX - origin.clientX), SIDEBAR_MIN_WIDTH, effectiveMax);
102
+ };
103
+ return (_jsx("div", { className: cx("gd-resizable-sidebar", {
104
+ "gd-resizable-sidebar--resizable": isResizable,
105
+ "gd-resizable-sidebar--dragging": isDragging,
106
+ }), style: { width: effectiveWidth }, children: _jsxs("div", { className: "col gd-flex-item gd-sidebar-container gd-sidebar-container--resizable", onClick: onContainerClick, children: [children, _jsx("button", { type: "button", style: {
107
+ "--drag-x": `${isDragging ? dragWidth : effectiveWidth}px`,
108
+ }, className: cx("gd-resizable-sidebar__handle", {
109
+ "gd-resizable-sidebar__handle--dragging": isDragging,
110
+ }), disabled: !isResizable, role: "separator", "aria-orientation": "vertical", "aria-valuemin": SIDEBAR_MIN_WIDTH, "aria-valuemax": effectiveMax, "aria-valuenow": effectiveWidth, onPointerDown: isResizable ? handlePointerDown : undefined, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: endDrag, onKeyDown: isDragging ? undefined : handleKeyDown, children: _jsx("span", { className: "gd-resizable-sidebar__handle-grip", "aria-hidden": "true" }) })
111
+ ] }) }));
112
+ }
@@ -328,6 +328,7 @@ export type CustomDashboardInsightListItemComponentProps = {
328
328
  useRichText?: boolean;
329
329
  useReferences?: boolean;
330
330
  LoadingComponent?: ComponentType;
331
+ width?: number;
331
332
  };
332
333
  /**
333
334
  * @internal
@@ -1764,6 +1764,7 @@ export declare type CustomDashboardInsightListItemComponentProps = {
1764
1764
  useRichText?: boolean;
1765
1765
  useReferences?: boolean;
1766
1766
  LoadingComponent?: ComponentType;
1767
+ width?: number;
1767
1768
  };
1768
1769
 
1769
1770
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-dashboard",
3
- "version": "11.40.0-alpha.2",
3
+ "version": "11.40.0-alpha.4",
4
4
  "description": "GoodData SDK - Dashboard Component",
5
5
  "license": "LicenseRef-LICENSE",
6
6
  "author": "GoodData Corporation",
@@ -60,19 +60,19 @@
60
60
  "ts-invariant": "0.10.3",
61
61
  "tslib": "2.8.1",
62
62
  "uuid": "11.1.0",
63
- "@gooddata/sdk-backend-base": "11.40.0-alpha.2",
64
- "@gooddata/sdk-backend-spi": "11.40.0-alpha.2",
65
- "@gooddata/sdk-model": "11.40.0-alpha.2",
66
- "@gooddata/sdk-ui-charts": "11.40.0-alpha.2",
67
- "@gooddata/sdk-ui": "11.40.0-alpha.2",
68
- "@gooddata/sdk-ui-ext": "11.40.0-alpha.2",
69
- "@gooddata/sdk-ui-filters": "11.40.0-alpha.2",
70
- "@gooddata/sdk-ui-geo": "11.40.0-alpha.2",
71
- "@gooddata/sdk-ui-kit": "11.40.0-alpha.2",
72
- "@gooddata/sdk-ui-pivot": "11.40.0-alpha.2",
73
- "@gooddata/sdk-ui-theme-provider": "11.40.0-alpha.2",
74
- "@gooddata/sdk-ui-vis-commons": "11.40.0-alpha.2",
75
- "@gooddata/util": "11.40.0-alpha.2"
63
+ "@gooddata/sdk-backend-base": "11.40.0-alpha.4",
64
+ "@gooddata/sdk-backend-spi": "11.40.0-alpha.4",
65
+ "@gooddata/sdk-model": "11.40.0-alpha.4",
66
+ "@gooddata/sdk-ui": "11.40.0-alpha.4",
67
+ "@gooddata/sdk-ui-charts": "11.40.0-alpha.4",
68
+ "@gooddata/sdk-ui-ext": "11.40.0-alpha.4",
69
+ "@gooddata/sdk-ui-filters": "11.40.0-alpha.4",
70
+ "@gooddata/sdk-ui-geo": "11.40.0-alpha.4",
71
+ "@gooddata/sdk-ui-kit": "11.40.0-alpha.4",
72
+ "@gooddata/sdk-ui-pivot": "11.40.0-alpha.4",
73
+ "@gooddata/sdk-ui-vis-commons": "11.40.0-alpha.4",
74
+ "@gooddata/sdk-ui-theme-provider": "11.40.0-alpha.4",
75
+ "@gooddata/util": "11.40.0-alpha.4"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@microsoft/api-documenter": "^7.17.0",
@@ -106,9 +106,9 @@
106
106
  "eslint-plugin-sonarjs": "3.0.6",
107
107
  "happy-dom": "18.0.1",
108
108
  "npm-run-all": "^4.1.5",
109
- "oxfmt": "0.45.0",
110
- "oxlint": "^1.43.0",
111
- "oxlint-tsgolint": "0.11.4",
109
+ "oxfmt": "0.52.0",
110
+ "oxlint": "1.51.0",
111
+ "oxlint-tsgolint": "0.15.0",
112
112
  "raf": "3.4.1",
113
113
  "react": "19.1.1",
114
114
  "react-dom": "19.1.1",
@@ -116,14 +116,14 @@
116
116
  "stylelint": "^16.26.1",
117
117
  "svgo": "^2.8.0",
118
118
  "typescript": "5.9.3",
119
- "vitest": "4.1.0",
119
+ "vitest": "4.1.8",
120
120
  "vitest-dom": "0.1.1",
121
- "@gooddata/i18n-toolkit": "11.40.0-alpha.2",
122
- "@gooddata/eslint-config": "11.40.0-alpha.2",
123
- "@gooddata/oxlint-config": "11.40.0-alpha.2",
124
- "@gooddata/reference-workspace": "11.40.0-alpha.2",
125
- "@gooddata/sdk-backend-mockingbird": "11.40.0-alpha.2",
126
- "@gooddata/stylelint-config": "11.40.0-alpha.2"
121
+ "@gooddata/eslint-config": "11.40.0-alpha.4",
122
+ "@gooddata/i18n-toolkit": "11.40.0-alpha.4",
123
+ "@gooddata/oxlint-config": "11.40.0-alpha.4",
124
+ "@gooddata/reference-workspace": "11.40.0-alpha.4",
125
+ "@gooddata/sdk-backend-mockingbird": "11.40.0-alpha.4",
126
+ "@gooddata/stylelint-config": "11.40.0-alpha.4"
127
127
  },
128
128
  "peerDependencies": {
129
129
  "react": "^18.0.0 || ^19.0.0",
@@ -30783,6 +30783,79 @@ figure {
30783
30783
  height: 100vh;
30784
30784
  border-right: 1px solid var(--gd-palette-complementary-3, #dde4eb);
30785
30785
  }
30786
+ .gd-sidebar-container--resizable {
30787
+ min-width: 230px;
30788
+ width: 100%;
30789
+ }
30790
+
30791
+ .gd-resizable-sidebar {
30792
+ display: flex;
30793
+ flex-direction: column;
30794
+ flex: 0 0 auto;
30795
+ }
30796
+ .gd-resizable-sidebar--dragging, .gd-resizable-sidebar:has(.gd-resizable-sidebar__handle:focus) {
30797
+ z-index: 9999;
30798
+ }
30799
+ .gd-resizable-sidebar__handle {
30800
+ opacity: 0;
30801
+ position: absolute;
30802
+ top: 0;
30803
+ bottom: 0;
30804
+ transform: translateX(calc(var(--drag-x, 0) - 50%));
30805
+ z-index: 2;
30806
+ display: flex;
30807
+ align-items: center;
30808
+ justify-content: center;
30809
+ width: 6px;
30810
+ background: transparent;
30811
+ border: 0;
30812
+ cursor: col-resize;
30813
+ touch-action: none;
30814
+ }
30815
+ .gd-resizable-sidebar__handle:focus {
30816
+ outline: none;
30817
+ }
30818
+ .gd-resizable-sidebar__handle:disabled {
30819
+ cursor: initial;
30820
+ }
30821
+ .gd-resizable-sidebar__handle--dragging {
30822
+ opacity: 1;
30823
+ }
30824
+ .gd-resizable-sidebar--resizable:hover .gd-resizable-sidebar__handle, .gd-resizable-sidebar--resizable:focus-within .gd-resizable-sidebar__handle {
30825
+ opacity: 1;
30826
+ }
30827
+ .gd-resizable-sidebar__handle-grip {
30828
+ display: flex;
30829
+ align-items: center;
30830
+ justify-content: center;
30831
+ gap: 10px;
30832
+ height: 25px;
30833
+ padding: 5px 3px;
30834
+ background: var(--gd-palette-complementary-0);
30835
+ border: 1px solid var(--gd-palette-complementary-5);
30836
+ border-radius: 2px;
30837
+ }
30838
+ .gd-resizable-sidebar--dragging .gd-resizable-sidebar__handle-grip, .gd-resizable-sidebar__handle:focus .gd-resizable-sidebar__handle-grip {
30839
+ border: 2px solid #14b2e2;
30840
+ border-radius: 3px;
30841
+ }
30842
+ .gd-resizable-sidebar--dragging .gd-resizable-sidebar__handle-grip::after, .gd-resizable-sidebar__handle:focus .gd-resizable-sidebar__handle-grip::after {
30843
+ content: "";
30844
+ position: absolute;
30845
+ top: 0;
30846
+ bottom: 0;
30847
+ left: 50%;
30848
+ transform: translateX(-50%);
30849
+ z-index: -1;
30850
+ width: 2px;
30851
+ background: #14b2e2;
30852
+ box-shadow: 0 0 0 4px var(--gd-palette-primary-base-t85, rgba(20, 178, 226, 0.15));
30853
+ }
30854
+
30855
+ body:has(.gd-resizable-sidebar--dragging),
30856
+ body:has(.gd-resizable-sidebar--dragging) * {
30857
+ cursor: col-resize;
30858
+ }
30786
30859
 
30787
30860
  .add-item-panel {
30788
30861
  padding: 0 7px;