@gooddata/sdk-ui-dashboard 11.40.0-alpha.3 → 11.40.0-alpha.5

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.
@@ -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
@@ -38,9 +38,14 @@ export function DefaultDashboardFilterGroup(props) {
38
38
  }, []);
39
39
  const isFilterActive = useCallback((filter) => {
40
40
  if (isFilterBarMeasureValueFilter(filter)) {
41
+ // Measure value filters have no working selection, so they always reflect the
42
+ // committed filter regardless of the apply mode.
41
43
  return !isAllDashboardMeasureValueFilter(filter.filter);
42
44
  }
43
- const dashboardFilter = filter.filter;
45
+ // In apply-all-at-once mode the uncommitted (working) selection should be reflected in
46
+ // the group button subtitle immediately, without waiting for the Apply button. Fall back
47
+ // to the committed filter when there is no working selection.
48
+ const dashboardFilter = isApplyAllAtOnceEnabledAndSet && filter.workingFilter ? filter.workingFilter : filter.filter;
44
49
  if (isDashboardAttributeFilter(dashboardFilter)) {
45
50
  return getSelectedElementsCount(dashboardFilter) > 0;
46
51
  }
@@ -51,7 +56,7 @@ export function DefaultDashboardFilterGroup(props) {
51
56
  return dashboardFilter.matchAttributeFilter.literal.length > 0;
52
57
  }
53
58
  return true;
54
- }, []);
59
+ }, [isApplyAllAtOnceEnabledAndSet]);
55
60
  const renderFilter = useCallback((filter, AttributeFilterComponent, MeasureValueFilterComponent) => {
56
61
  if (isFilterBarMeasureValueFilter(filter)) {
57
62
  const localId = dashboardFilterLocalIdentifier(filter.filter);
@@ -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.3",
3
+ "version": "11.40.0-alpha.5",
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.3",
64
- "@gooddata/sdk-backend-spi": "11.40.0-alpha.3",
65
- "@gooddata/sdk-ui": "11.40.0-alpha.3",
66
- "@gooddata/sdk-model": "11.40.0-alpha.3",
67
- "@gooddata/sdk-ui-charts": "11.40.0-alpha.3",
68
- "@gooddata/sdk-ui-ext": "11.40.0-alpha.3",
69
- "@gooddata/sdk-ui-geo": "11.40.0-alpha.3",
70
- "@gooddata/sdk-ui-kit": "11.40.0-alpha.3",
71
- "@gooddata/sdk-ui-filters": "11.40.0-alpha.3",
72
- "@gooddata/sdk-ui-pivot": "11.40.0-alpha.3",
73
- "@gooddata/sdk-ui-theme-provider": "11.40.0-alpha.3",
74
- "@gooddata/sdk-ui-vis-commons": "11.40.0-alpha.3",
75
- "@gooddata/util": "11.40.0-alpha.3"
63
+ "@gooddata/sdk-backend-base": "11.40.0-alpha.5",
64
+ "@gooddata/sdk-backend-spi": "11.40.0-alpha.5",
65
+ "@gooddata/sdk-ui": "11.40.0-alpha.5",
66
+ "@gooddata/sdk-model": "11.40.0-alpha.5",
67
+ "@gooddata/sdk-ui-charts": "11.40.0-alpha.5",
68
+ "@gooddata/sdk-ui-ext": "11.40.0-alpha.5",
69
+ "@gooddata/sdk-ui-filters": "11.40.0-alpha.5",
70
+ "@gooddata/sdk-ui-geo": "11.40.0-alpha.5",
71
+ "@gooddata/sdk-ui-kit": "11.40.0-alpha.5",
72
+ "@gooddata/sdk-ui-pivot": "11.40.0-alpha.5",
73
+ "@gooddata/sdk-ui-theme-provider": "11.40.0-alpha.5",
74
+ "@gooddata/sdk-ui-vis-commons": "11.40.0-alpha.5",
75
+ "@gooddata/util": "11.40.0-alpha.5"
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.3",
122
- "@gooddata/eslint-config": "11.40.0-alpha.3",
123
- "@gooddata/oxlint-config": "11.40.0-alpha.3",
124
- "@gooddata/sdk-backend-mockingbird": "11.40.0-alpha.3",
125
- "@gooddata/reference-workspace": "11.40.0-alpha.3",
126
- "@gooddata/stylelint-config": "11.40.0-alpha.3"
121
+ "@gooddata/eslint-config": "11.40.0-alpha.5",
122
+ "@gooddata/i18n-toolkit": "11.40.0-alpha.5",
123
+ "@gooddata/oxlint-config": "11.40.0-alpha.5",
124
+ "@gooddata/reference-workspace": "11.40.0-alpha.5",
125
+ "@gooddata/sdk-backend-mockingbird": "11.40.0-alpha.5",
126
+ "@gooddata/stylelint-config": "11.40.0-alpha.5"
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;