@harborclient/sdk 1.3.4 → 1.3.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.
- package/CHANGELOG.md +14 -0
- package/dist/components/Breadcrumb/SegmentShell.d.ts +12 -1
- package/dist/components/Breadcrumb/SegmentShell.d.ts.map +1 -1
- package/dist/components/Breadcrumb/SegmentShell.js +7 -5
- package/dist/components/CodeEditor/index.d.ts +7 -4
- package/dist/components/CodeEditor/index.d.ts.map +1 -1
- package/dist/components/CodeEditor/index.js +15 -176
- package/dist/components/CodeEditor/selectionActionToolbar.d.ts +162 -0
- package/dist/components/CodeEditor/selectionActionToolbar.d.ts.map +1 -0
- package/dist/components/CodeEditor/selectionActionToolbar.js +319 -0
- package/dist/components/CopyToChatButton/constants.d.ts +18 -0
- package/dist/components/CopyToChatButton/constants.d.ts.map +1 -0
- package/dist/components/CopyToChatButton/constants.js +19 -0
- package/dist/components/CopyToChatButton/index.d.ts +49 -0
- package/dist/components/CopyToChatButton/index.d.ts.map +1 -0
- package/dist/components/CopyToChatButton/index.js +43 -0
- package/dist/components/FloatingDialog/floatingDialogPosition.d.ts +52 -0
- package/dist/components/FloatingDialog/floatingDialogPosition.d.ts.map +1 -0
- package/dist/components/FloatingDialog/floatingDialogPosition.js +33 -0
- package/dist/components/FloatingDialog/index.d.ts +95 -0
- package/dist/components/FloatingDialog/index.d.ts.map +1 -0
- package/dist/components/FloatingDialog/index.js +275 -0
- package/dist/components/PageHeader/index.d.ts +2 -1
- package/dist/components/PageHeader/index.d.ts.map +1 -1
- package/dist/components/PageHeader/index.js +3 -2
- package/dist/components/SidebarItem/SidebarEnvironmentItem.d.ts +42 -4
- package/dist/components/SidebarItem/SidebarEnvironmentItem.d.ts.map +1 -1
- package/dist/components/SidebarItem/SidebarEnvironmentItem.js +34 -5
- package/dist/components/SidebarItem/SidebarWorkspaceItem.d.ts +9 -1
- package/dist/components/SidebarItem/SidebarWorkspaceItem.d.ts.map +1 -1
- package/dist/components/SidebarItem/SidebarWorkspaceItem.js +15 -2
- package/dist/components/TabBar/TabContextMenu.d.ts +3 -0
- package/dist/components/TabBar/TabContextMenu.d.ts.map +1 -1
- package/dist/components/TabBar/TabContextMenu.js +25 -16
- package/dist/components/VariableTable/index.d.ts +1 -1
- package/dist/components/VariableTable/index.d.ts.map +1 -1
- package/dist/components/VariableTable/index.js +14 -3
- package/dist/components/index.d.ts +4 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +3 -1
- package/dist/runtime/createBridgedPluginContext.js +74 -0
- package/dist/snippets.d.ts +26 -2
- package/dist/styles.css +16 -0
- package/dist/types.d.ts +273 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/package.json +1 -1
- package/dist/components/SelectionActionToolbar/index.d.ts +0 -41
- package/dist/components/SelectionActionToolbar/index.d.ts.map +0 -1
- package/dist/components/SelectionActionToolbar/index.js +0 -13
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@harborclient/sdk/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from '@harborclient/sdk/react';
|
|
3
|
+
import { cn } from '../utils.js';
|
|
4
|
+
import { FLOATING_DIALOG_DEFAULT_LEFT, FLOATING_DIALOG_DEFAULT_TOP, FLOATING_DIALOG_VIEWPORT_MARGIN_PX, isFloatingDialogFullyOnScreen } from './floatingDialogPosition.js';
|
|
5
|
+
/**
|
|
6
|
+
* Clamps a proposed size into min/max and remaining viewport space.
|
|
7
|
+
*
|
|
8
|
+
* @param width - Proposed width.
|
|
9
|
+
* @param height - Proposed height.
|
|
10
|
+
* @param left - Current panel left.
|
|
11
|
+
* @param top - Current panel top.
|
|
12
|
+
* @param minWidth - Minimum width.
|
|
13
|
+
* @param minHeight - Minimum height.
|
|
14
|
+
* @param maxWidth - Optional max width cap.
|
|
15
|
+
* @param maxHeight - Optional max height cap.
|
|
16
|
+
* @returns Clamped size.
|
|
17
|
+
*/
|
|
18
|
+
function clampFloatingDialogSize(width, height, left, top, minWidth, minHeight, maxWidth, maxHeight) {
|
|
19
|
+
const margin = FLOATING_DIALOG_VIEWPORT_MARGIN_PX;
|
|
20
|
+
const viewportWidth = typeof window !== 'undefined' ? window.innerWidth : width;
|
|
21
|
+
const viewportHeight = typeof window !== 'undefined' ? window.innerHeight : height;
|
|
22
|
+
const roomWidth = Math.max(minWidth, viewportWidth - left - margin);
|
|
23
|
+
const roomHeight = Math.max(minHeight, viewportHeight - top - margin);
|
|
24
|
+
const widthCap = maxWidth != null ? Math.min(maxWidth, roomWidth) : roomWidth;
|
|
25
|
+
const heightCap = maxHeight != null ? Math.min(maxHeight, roomHeight) : roomHeight;
|
|
26
|
+
return {
|
|
27
|
+
width: Math.min(Math.max(width, minWidth), widthCap),
|
|
28
|
+
height: Math.min(Math.max(height, minHeight), heightCap)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Non-blocking, draggable dialog panel with no modal mask.
|
|
33
|
+
*
|
|
34
|
+
* Clicks pass through to the UI behind the panel. Drag by the handle region.
|
|
35
|
+
* When `initialWidth` and `initialHeight` are provided, a southeast resize
|
|
36
|
+
* handle appears (pointer + arrow-key accessible). Parents persist geometry via
|
|
37
|
+
* `onPositionChange` / `onSizeChange`. If the panel would open or remain
|
|
38
|
+
* off-screen after a window resize, it relocates to the default corner.
|
|
39
|
+
*
|
|
40
|
+
* @param props - Dialog labelling, geometry, and content props.
|
|
41
|
+
* @returns Floating dialog element.
|
|
42
|
+
*/
|
|
43
|
+
export function FloatingDialog({ label, labelledBy, onClose, disableEscape = false, className, bodyClassName, initialLeft = FLOATING_DIALOG_DEFAULT_LEFT, initialTop = FLOATING_DIALOG_DEFAULT_TOP, onPositionChange, initialWidth, initialHeight, minWidth = 240, minHeight = 120, maxWidth, maxHeight, onSizeChange, dragHandle, children }) {
|
|
44
|
+
const resizable = initialWidth != null && initialHeight != null;
|
|
45
|
+
const panelRef = useRef(null);
|
|
46
|
+
const [position, setPosition] = useState({
|
|
47
|
+
left: initialLeft,
|
|
48
|
+
top: initialTop
|
|
49
|
+
});
|
|
50
|
+
const [size, setSize] = useState(() => resizable
|
|
51
|
+
? clampFloatingDialogSize(initialWidth, initialHeight, initialLeft, initialTop, minWidth, minHeight, maxWidth, maxHeight)
|
|
52
|
+
: null);
|
|
53
|
+
const positionRef = useRef(position);
|
|
54
|
+
const sizeRef = useRef(size);
|
|
55
|
+
const onPositionChangeRef = useRef(onPositionChange);
|
|
56
|
+
const onSizeChangeRef = useRef(onSizeChange);
|
|
57
|
+
const dragStateRef = useRef(null);
|
|
58
|
+
const resizeStateRef = useRef(null);
|
|
59
|
+
/**
|
|
60
|
+
* Keeps the latest position available to resize handlers without rebinding.
|
|
61
|
+
*/
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
positionRef.current = position;
|
|
64
|
+
}, [position]);
|
|
65
|
+
/**
|
|
66
|
+
* Keeps the latest size available to relocate / resize handlers.
|
|
67
|
+
*/
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
sizeRef.current = size;
|
|
70
|
+
}, [size]);
|
|
71
|
+
/**
|
|
72
|
+
* Keeps the latest position-change callback available to layout/resize handlers.
|
|
73
|
+
*/
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
onPositionChangeRef.current = onPositionChange;
|
|
76
|
+
}, [onPositionChange]);
|
|
77
|
+
/**
|
|
78
|
+
* Keeps the latest size-change callback available to resize handlers.
|
|
79
|
+
*/
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
onSizeChangeRef.current = onSizeChange;
|
|
82
|
+
}, [onSizeChange]);
|
|
83
|
+
/**
|
|
84
|
+
* Moves the panel to the default corner when it would sit outside the viewport.
|
|
85
|
+
*/
|
|
86
|
+
const relocateToCornerIfOffScreen = useCallback(() => {
|
|
87
|
+
const panel = panelRef.current;
|
|
88
|
+
if (panel == null) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const measured = sizeRef.current ?? {
|
|
92
|
+
width: panel.offsetWidth,
|
|
93
|
+
height: panel.offsetHeight
|
|
94
|
+
};
|
|
95
|
+
const current = positionRef.current;
|
|
96
|
+
if (isFloatingDialogFullyOnScreen(current, measured)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const corner = {
|
|
100
|
+
left: FLOATING_DIALOG_DEFAULT_LEFT,
|
|
101
|
+
top: FLOATING_DIALOG_DEFAULT_TOP
|
|
102
|
+
};
|
|
103
|
+
positionRef.current = corner;
|
|
104
|
+
setPosition(corner);
|
|
105
|
+
onPositionChangeRef.current?.(corner);
|
|
106
|
+
}, []);
|
|
107
|
+
/**
|
|
108
|
+
* After first paint and whenever the window resizes, relocates only when off-screen.
|
|
109
|
+
*/
|
|
110
|
+
useLayoutEffect(() => {
|
|
111
|
+
relocateToCornerIfOffScreen();
|
|
112
|
+
window.addEventListener('resize', relocateToCornerIfOffScreen);
|
|
113
|
+
return () => window.removeEventListener('resize', relocateToCornerIfOffScreen);
|
|
114
|
+
}, [relocateToCornerIfOffScreen]);
|
|
115
|
+
/**
|
|
116
|
+
* Closes the dialog when Escape is pressed unless disabled.
|
|
117
|
+
*/
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (disableEscape) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Dismisses the floating dialog on Escape.
|
|
124
|
+
*
|
|
125
|
+
* @param event - Keyboard event.
|
|
126
|
+
*/
|
|
127
|
+
const handleKeyDown = (event) => {
|
|
128
|
+
if (event.key === 'Escape') {
|
|
129
|
+
onClose();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
133
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
134
|
+
}, [disableEscape, onClose]);
|
|
135
|
+
/**
|
|
136
|
+
* Begins a pointer drag from the header handle.
|
|
137
|
+
*
|
|
138
|
+
* @param event - Pointer down event on the drag handle.
|
|
139
|
+
*/
|
|
140
|
+
const handlePointerDown = useCallback((event) => {
|
|
141
|
+
if (event.button !== 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
145
|
+
dragStateRef.current = {
|
|
146
|
+
pointerId: event.pointerId,
|
|
147
|
+
originX: event.clientX,
|
|
148
|
+
originY: event.clientY,
|
|
149
|
+
startLeft: position.left,
|
|
150
|
+
startTop: position.top
|
|
151
|
+
};
|
|
152
|
+
}, [position.left, position.top]);
|
|
153
|
+
/**
|
|
154
|
+
* Updates panel position while dragging.
|
|
155
|
+
*
|
|
156
|
+
* @param event - Pointer move event.
|
|
157
|
+
*/
|
|
158
|
+
const handlePointerMove = useCallback((event) => {
|
|
159
|
+
const drag = dragStateRef.current;
|
|
160
|
+
if (drag == null || drag.pointerId !== event.pointerId) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const next = {
|
|
164
|
+
left: Math.max(FLOATING_DIALOG_VIEWPORT_MARGIN_PX, drag.startLeft + (event.clientX - drag.originX)),
|
|
165
|
+
top: Math.max(FLOATING_DIALOG_VIEWPORT_MARGIN_PX, drag.startTop + (event.clientY - drag.originY))
|
|
166
|
+
};
|
|
167
|
+
positionRef.current = next;
|
|
168
|
+
setPosition(next);
|
|
169
|
+
}, []);
|
|
170
|
+
/**
|
|
171
|
+
* Ends an active pointer drag and notifies the parent of the final position.
|
|
172
|
+
*
|
|
173
|
+
* @param event - Pointer up/cancel event.
|
|
174
|
+
*/
|
|
175
|
+
const handlePointerUp = useCallback((event) => {
|
|
176
|
+
const drag = dragStateRef.current;
|
|
177
|
+
if (drag == null || drag.pointerId !== event.pointerId) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
dragStateRef.current = null;
|
|
181
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
182
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
183
|
+
}
|
|
184
|
+
onPositionChangeRef.current?.(positionRef.current);
|
|
185
|
+
}, []);
|
|
186
|
+
/**
|
|
187
|
+
* Begins a southeast resize from the resize handle.
|
|
188
|
+
*
|
|
189
|
+
* @param event - Pointer down on the resize handle.
|
|
190
|
+
*/
|
|
191
|
+
const handleResizePointerDown = useCallback((event) => {
|
|
192
|
+
if (!resizable || size == null || event.button !== 0) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
event.stopPropagation();
|
|
196
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
197
|
+
resizeStateRef.current = {
|
|
198
|
+
pointerId: event.pointerId,
|
|
199
|
+
originX: event.clientX,
|
|
200
|
+
originY: event.clientY,
|
|
201
|
+
startWidth: size.width,
|
|
202
|
+
startHeight: size.height
|
|
203
|
+
};
|
|
204
|
+
}, [resizable, size]);
|
|
205
|
+
/**
|
|
206
|
+
* Updates panel size while resizing from the SE handle.
|
|
207
|
+
*
|
|
208
|
+
* @param event - Pointer move on the resize handle.
|
|
209
|
+
*/
|
|
210
|
+
const handleResizePointerMove = useCallback((event) => {
|
|
211
|
+
const resize = resizeStateRef.current;
|
|
212
|
+
if (resize == null || resize.pointerId !== event.pointerId) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const next = clampFloatingDialogSize(resize.startWidth + (event.clientX - resize.originX), resize.startHeight + (event.clientY - resize.originY), positionRef.current.left, positionRef.current.top, minWidth, minHeight, maxWidth, maxHeight);
|
|
216
|
+
sizeRef.current = next;
|
|
217
|
+
setSize(next);
|
|
218
|
+
}, [maxHeight, maxWidth, minHeight, minWidth]);
|
|
219
|
+
/**
|
|
220
|
+
* Ends an active resize and notifies the parent of the final size.
|
|
221
|
+
*
|
|
222
|
+
* @param event - Pointer up/cancel on the resize handle.
|
|
223
|
+
*/
|
|
224
|
+
const handleResizePointerUp = useCallback((event) => {
|
|
225
|
+
const resize = resizeStateRef.current;
|
|
226
|
+
if (resize == null || resize.pointerId !== event.pointerId) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
resizeStateRef.current = null;
|
|
230
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
231
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
232
|
+
}
|
|
233
|
+
if (sizeRef.current != null) {
|
|
234
|
+
onSizeChangeRef.current?.(sizeRef.current);
|
|
235
|
+
}
|
|
236
|
+
}, []);
|
|
237
|
+
/**
|
|
238
|
+
* Adjusts size with arrow keys when the resize handle is focused.
|
|
239
|
+
*
|
|
240
|
+
* @param event - Keyboard event on the resize handle.
|
|
241
|
+
*/
|
|
242
|
+
const handleResizeKeyDown = useCallback((event) => {
|
|
243
|
+
if (size == null) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const step = event.shiftKey ? 24 : 8;
|
|
247
|
+
let deltaW = 0;
|
|
248
|
+
let deltaH = 0;
|
|
249
|
+
if (event.key === 'ArrowRight') {
|
|
250
|
+
deltaW = step;
|
|
251
|
+
}
|
|
252
|
+
else if (event.key === 'ArrowLeft') {
|
|
253
|
+
deltaW = -step;
|
|
254
|
+
}
|
|
255
|
+
else if (event.key === 'ArrowDown') {
|
|
256
|
+
deltaH = step;
|
|
257
|
+
}
|
|
258
|
+
else if (event.key === 'ArrowUp') {
|
|
259
|
+
deltaH = -step;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
event.preventDefault();
|
|
265
|
+
const next = clampFloatingDialogSize(size.width + deltaW, size.height + deltaH, positionRef.current.left, positionRef.current.top, minWidth, minHeight, maxWidth, maxHeight);
|
|
266
|
+
sizeRef.current = next;
|
|
267
|
+
setSize(next);
|
|
268
|
+
onSizeChangeRef.current?.(next);
|
|
269
|
+
}, [maxHeight, maxWidth, minHeight, minWidth, size]);
|
|
270
|
+
return (_jsxs("div", { ref: panelRef, role: "dialog", "aria-modal": "false", "aria-label": labelledBy ? undefined : label, "aria-labelledby": labelledBy, className: cn('hc-floating-dialog fixed z-[60] flex min-w-[240px] flex-col overflow-hidden rounded-lg border border-separator bg-surface shadow-xl', className), style: {
|
|
271
|
+
left: position.left,
|
|
272
|
+
top: position.top,
|
|
273
|
+
...(size != null ? { width: size.width, height: size.height } : {})
|
|
274
|
+
}, children: [_jsx("div", { className: "hc-floating-dialog-handle shrink-0 cursor-grab touch-none active:cursor-grabbing", onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerUp, children: dragHandle }), _jsx("div", { className: cn('hc-floating-dialog-body min-h-0 flex-1 overflow-auto p-3', bodyClassName), children: children }), resizable ? (_jsx("button", { type: "button", "aria-label": "Resize dialog", className: "absolute right-0 bottom-0 h-4 w-4 cursor-se-resize border-0 bg-transparent p-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent", onPointerDown: handleResizePointerDown, onPointerMove: handleResizePointerMove, onPointerUp: handleResizePointerUp, onPointerCancel: handleResizePointerUp, onKeyDown: handleResizeKeyDown, children: _jsx("span", { className: "pointer-events-none absolute right-1 bottom-1 h-2 w-2 border-r-2 border-b-2 border-muted", "aria-hidden": true }) })) : null] }));
|
|
275
|
+
}
|
|
@@ -20,7 +20,8 @@ interface Props extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Full-bleed page header with a bordered bottom edge, title block on the left,
|
|
23
|
-
* and optional action controls on the right.
|
|
23
|
+
* and optional action controls on the right. Sticks to the top of its nearest
|
|
24
|
+
* scroll ancestor so title and actions stay visible while page content scrolls.
|
|
24
25
|
*/
|
|
25
26
|
export declare function PageHeader({ title, description, icon, children, className, ...props }: Props): JSX.Element;
|
|
26
27
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/PageHeader/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,UAAU,KAAM,SAAQ,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACvE;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/PageHeader/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,UAAU,KAAM,SAAQ,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACvE;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA6BrB"}
|
|
@@ -3,8 +3,9 @@ import { FaIcon } from '../FaIcon/index.js';
|
|
|
3
3
|
import { cn } from '../utils.js';
|
|
4
4
|
/**
|
|
5
5
|
* Full-bleed page header with a bordered bottom edge, title block on the left,
|
|
6
|
-
* and optional action controls on the right.
|
|
6
|
+
* and optional action controls on the right. Sticks to the top of its nearest
|
|
7
|
+
* scroll ancestor so title and actions stay visible while page content scrolls.
|
|
7
8
|
*/
|
|
8
9
|
export function PageHeader({ title, description, icon, children, className, ...props }) {
|
|
9
|
-
return (_jsxs("div", { ...props, className: cn('hc-page-header -mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator px-6 py-4', className), children: [_jsxs("div", { className: "hc-page-header-content min-w-0 flex-1", children: [_jsxs("h2", { className: "hc-page-header-title m-0 flex items-center gap-2 text-[22px] leading-[1.15] font-bold tracking-[-0.01em] text-text", children: [icon ? (_jsx(FaIcon, { icon: icon, className: "hc-page-header-title-icon h-4 w-4 shrink-0 text-muted", "aria-hidden": true })) : null, title] }), description ? (_jsx("p", { className: "hc-page-header-description m-0 mt-1 leading-6 text-muted", children: description })) : null] }), children ? (_jsx("div", { className: "hc-page-header-actions flex flex-wrap items-center gap-2", children: children })) : null] }));
|
|
10
|
+
return (_jsxs("div", { ...props, className: cn('hc-page-header sticky top-0 z-10 -mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator bg-surface px-6 py-4', className), children: [_jsxs("div", { className: "hc-page-header-content min-w-0 flex-1", children: [_jsxs("h2", { className: "hc-page-header-title m-0 flex items-center gap-2 text-[22px] leading-[1.15] font-bold tracking-[-0.01em] text-text", children: [icon ? (_jsx(FaIcon, { icon: icon, className: "hc-page-header-title-icon h-4 w-4 shrink-0 text-muted", "aria-hidden": true })) : null, title] }), description ? (_jsx("p", { className: "hc-page-header-description m-0 mt-1 leading-6 text-muted", children: description })) : null] }), children ? (_jsx("div", { className: "hc-page-header-actions flex flex-wrap items-center gap-2", children: children })) : null] }));
|
|
10
11
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
1
2
|
import type { JSX, MouseEvent, ReactNode } from 'react';
|
|
2
3
|
import { type SidebarItemSortableConfig } from './SidebarItem.js';
|
|
3
4
|
interface Props {
|
|
@@ -21,13 +22,46 @@ interface Props {
|
|
|
21
22
|
* Whether this row should use selected/highlighted row styling.
|
|
22
23
|
*/
|
|
23
24
|
selected?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Whether this environment has nested child environments.
|
|
27
|
+
*/
|
|
28
|
+
hasChildren?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the child environment list is expanded.
|
|
31
|
+
*/
|
|
32
|
+
expanded?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Id of the child region controlled by this row (`SidebarTreeGroup` id).
|
|
35
|
+
*/
|
|
36
|
+
childrenId?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Nesting depth for indentation (`0` = root, no extra indent).
|
|
39
|
+
*/
|
|
40
|
+
level?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Total siblings at this level (tree a11y).
|
|
43
|
+
*/
|
|
44
|
+
setSize?: number;
|
|
45
|
+
/**
|
|
46
|
+
* 1-based position among siblings at this level (tree a11y).
|
|
47
|
+
*/
|
|
48
|
+
posInSet?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Toggles child environment expand/collapse when {@link hasChildren} is true.
|
|
51
|
+
*/
|
|
52
|
+
onToggleExpand?: () => void;
|
|
53
|
+
/**
|
|
54
|
+
* Icons for expand/collapse chevrons when {@link hasChildren} is true.
|
|
55
|
+
*/
|
|
56
|
+
expandIcon?: IconDefinition;
|
|
57
|
+
collapseIcon?: IconDefinition;
|
|
24
58
|
/**
|
|
25
59
|
* dnd-kit sortable configuration for environment reordering.
|
|
26
60
|
*/
|
|
27
61
|
sortable?: SidebarItemSortableConfig;
|
|
28
62
|
/**
|
|
29
|
-
* Accessible label for the listbox option. When omitted, the name
|
|
30
|
-
* from visible row content (name, variable summary).
|
|
63
|
+
* Accessible label for the listbox option or tree item. When omitted, the name
|
|
64
|
+
* is derived from visible row content (name, variable summary).
|
|
31
65
|
*/
|
|
32
66
|
ariaLabel?: string;
|
|
33
67
|
/**
|
|
@@ -64,15 +98,19 @@ interface Props {
|
|
|
64
98
|
*/
|
|
65
99
|
dataSidebarEnvironmentId?: string | number;
|
|
66
100
|
/**
|
|
67
|
-
* HTML element for the row container. Use `li` inside {@link SidebarListbox}
|
|
101
|
+
* HTML element for the row container. Use `li` inside {@link SidebarListbox}
|
|
102
|
+
* or {@link SidebarTree}.
|
|
68
103
|
*/
|
|
69
104
|
as?: 'div' | 'li';
|
|
70
105
|
}
|
|
71
106
|
/**
|
|
72
107
|
* Renders an environment row in the Collections sidebar Environments section.
|
|
73
108
|
*
|
|
109
|
+
* Flat list rows use listbox option semantics. Nested tree rows (when `level` is
|
|
110
|
+
* set) use treeitem semantics with optional expand/collapse chevrons.
|
|
111
|
+
*
|
|
74
112
|
* The accessible name is derived from visible row content (name, variable summary).
|
|
75
113
|
*/
|
|
76
|
-
export declare function SidebarEnvironmentItem({ name, variableSummary, markerDot, selected, sortable, ariaLabel, ariaSelected, ariaCurrent, onContextMenu, onClick, onDoubleClick, onEnter, actions, dataSidebarEnvironmentId, as }: Props): JSX.Element;
|
|
114
|
+
export declare function SidebarEnvironmentItem({ name, variableSummary, markerDot, selected, hasChildren, expanded, childrenId, level, setSize, posInSet, onToggleExpand, expandIcon, collapseIcon, sortable, ariaLabel, ariaSelected, ariaCurrent, onContextMenu, onClick, onDoubleClick, onEnter, actions, dataSidebarEnvironmentId, as }: Props): JSX.Element;
|
|
77
115
|
export {};
|
|
78
116
|
//# sourceMappingURL=SidebarEnvironmentItem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SidebarEnvironmentItem.d.ts","sourceRoot":"","sources":["../../../src/components/SidebarItem/SidebarEnvironmentItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAiB,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SidebarEnvironmentItem.d.ts","sourceRoot":"","sources":["../../../src/components/SidebarItem/SidebarEnvironmentItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,GAAG,EAAiB,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvE,OAAO,EAAe,KAAK,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAU/E,UAAU,KAAK;IACb;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,SAAS,CAAC,EAAE;QACV,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QAClC,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,YAAY,CAAC,EAAE,cAAc,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IAErC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAEnD;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE3C;;;OAGG;IACH,EAAE,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACnB;AAOD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,eAAe,EACf,SAAS,EACT,QAAgB,EAChB,WAAmB,EACnB,QAAgB,EAChB,UAAU,EACV,KAAK,EACL,OAAO,EACP,QAAQ,EACR,cAAc,EACd,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,aAAa,EACb,OAAO,EACP,aAAa,EACb,OAAO,EACP,OAAO,EACP,wBAAwB,EACxB,EAAS,EACV,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAmGrB"}
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@harborclient/sdk/jsx-runtime";
|
|
2
|
+
import { FaIcon } from '../FaIcon/index.js';
|
|
2
3
|
import { SidebarItem } from './SidebarItem.js';
|
|
3
4
|
import { SidebarMarkerDot } from './SidebarMarkerDot.js';
|
|
4
|
-
import { SIDEBAR_ITEM_BUTTON_CLASS } from './sidebarItemClasses.js';
|
|
5
|
+
import { SIDEBAR_CHEVRON_BUTTON_CLASS, SIDEBAR_CHEVRON_ICON_CLASS, SIDEBAR_CHEVRON_LABEL_OFFSET_CLASS, SIDEBAR_ITEM_BUTTON_CLASS } from './sidebarItemClasses.js';
|
|
6
|
+
import { stopSortableDragPointerDown } from './stopSortableDragPointerDown.js';
|
|
7
|
+
/**
|
|
8
|
+
* Nesting indent step matching Tailwind `ml-4` (1rem) per depth level.
|
|
9
|
+
*/
|
|
10
|
+
const ENVIRONMENT_LEVEL_INDENT_PX = 16;
|
|
5
11
|
/**
|
|
6
12
|
* Renders an environment row in the Collections sidebar Environments section.
|
|
7
13
|
*
|
|
14
|
+
* Flat list rows use listbox option semantics. Nested tree rows (when `level` is
|
|
15
|
+
* set) use treeitem semantics with optional expand/collapse chevrons.
|
|
16
|
+
*
|
|
8
17
|
* The accessible name is derived from visible row content (name, variable summary).
|
|
9
18
|
*/
|
|
10
|
-
export function SidebarEnvironmentItem({ name, variableSummary, markerDot, selected = false, sortable, ariaLabel, ariaSelected, ariaCurrent, onContextMenu, onClick, onDoubleClick, onEnter, actions, dataSidebarEnvironmentId, as = 'li' }) {
|
|
11
|
-
const
|
|
19
|
+
export function SidebarEnvironmentItem({ name, variableSummary, markerDot, selected = false, hasChildren = false, expanded = false, childrenId, level, setSize, posInSet, onToggleExpand, expandIcon, collapseIcon, sortable, ariaLabel, ariaSelected, ariaCurrent, onContextMenu, onClick, onDoubleClick, onEnter, actions, dataSidebarEnvironmentId, as = 'li' }) {
|
|
20
|
+
const useTreeItem = as === 'li' && level != null;
|
|
21
|
+
const useListboxOption = as === 'li' && !useTreeItem;
|
|
22
|
+
const showChevron = hasChildren && expandIcon != null && collapseIcon != null && onToggleExpand != null;
|
|
23
|
+
const indentPx = (level ?? 0) * ENVIRONMENT_LEVEL_INDENT_PX;
|
|
12
24
|
/**
|
|
13
25
|
* Opens environment settings when Enter is pressed on the name area.
|
|
14
26
|
*/
|
|
@@ -20,6 +32,8 @@ export function SidebarEnvironmentItem({ name, variableSummary, markerDot, selec
|
|
|
20
32
|
event.stopPropagation();
|
|
21
33
|
onEnter();
|
|
22
34
|
};
|
|
35
|
+
const chevronLabel = expanded ? `Collapse environment "${name}"` : `Expand environment "${name}"`;
|
|
36
|
+
const labelOffsetClass = useTreeItem ? SIDEBAR_CHEVRON_LABEL_OFFSET_CLASS : '';
|
|
23
37
|
return (_jsx(SidebarItem, { selected: selected, sortable: sortable, onContextMenu: onContextMenu, actions: actions, as: as, listboxOption: useListboxOption
|
|
24
38
|
? {
|
|
25
39
|
ariaLabel,
|
|
@@ -29,7 +43,22 @@ export function SidebarEnvironmentItem({ name, variableSummary, markerDot, selec
|
|
|
29
43
|
onDoubleClick,
|
|
30
44
|
onKeyDown: onEnter != null ? handleKeyDown : undefined
|
|
31
45
|
}
|
|
32
|
-
: undefined,
|
|
46
|
+
: undefined, treeItem: useTreeItem
|
|
47
|
+
? {
|
|
48
|
+
ariaLabel,
|
|
49
|
+
expanded: hasChildren ? expanded : undefined,
|
|
50
|
+
controlsId: hasChildren ? childrenId : undefined,
|
|
51
|
+
level: (level ?? 0) + 1,
|
|
52
|
+
setSize,
|
|
53
|
+
posInSet,
|
|
54
|
+
onClick,
|
|
55
|
+
onDoubleClick,
|
|
56
|
+
onKeyDown: onEnter != null ? handleKeyDown : undefined
|
|
57
|
+
}
|
|
58
|
+
: undefined, children: _jsxs("span", { className: SIDEBAR_ITEM_BUTTON_CLASS, style: indentPx > 0 ? { paddingLeft: indentPx } : undefined, ...(dataSidebarEnvironmentId != null
|
|
33
59
|
? { 'data-sidebar-environment-id': String(dataSidebarEnvironmentId) }
|
|
34
|
-
: {}), children: [
|
|
60
|
+
: {}), children: [showChevron ? (_jsx("button", { type: "button", className: SIDEBAR_CHEVRON_BUTTON_CLASS, onClick: (event) => {
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
onToggleExpand();
|
|
63
|
+
}, onPointerDown: stopSortableDragPointerDown, tabIndex: -1, "aria-label": chevronLabel, children: _jsx(FaIcon, { icon: expanded ? collapseIcon : expandIcon, className: SIDEBAR_CHEVRON_ICON_CLASS }) })) : useTreeItem ? (_jsx("span", { className: "inline-flex h-4 w-4 shrink-0", "aria-hidden": true })) : null, _jsxs("span", { className: `inline-flex min-w-0 flex-1 items-center gap-1.5 ${labelOffsetClass}`, children: [_jsx("span", { className: "min-w-0 truncate", children: name }), markerDot != null ? (_jsx(SidebarMarkerDot, { marker: markerDot.marker, visible: markerDot.visible, label: markerDot.label })) : null] }), _jsx("span", { className: "shrink-0 text-muted", children: variableSummary })] }) }));
|
|
35
64
|
}
|
|
@@ -38,6 +38,14 @@ interface Props {
|
|
|
38
38
|
* Called when the primary row area is activated.
|
|
39
39
|
*/
|
|
40
40
|
onClick?: (event: MouseEvent<HTMLElement>) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Called when the primary row area is double-clicked.
|
|
43
|
+
*/
|
|
44
|
+
onDoubleClick?: (event: MouseEvent<HTMLElement>) => void;
|
|
45
|
+
/**
|
|
46
|
+
* Called when Enter is pressed on the primary row area.
|
|
47
|
+
*/
|
|
48
|
+
onEnter?: () => void;
|
|
41
49
|
/**
|
|
42
50
|
* Trailing actions slot, typically a row actions menu.
|
|
43
51
|
*/
|
|
@@ -50,6 +58,6 @@ interface Props {
|
|
|
50
58
|
/**
|
|
51
59
|
* Renders a workspace row in the Collections sidebar Workspaces section.
|
|
52
60
|
*/
|
|
53
|
-
export declare function SidebarWorkspaceItem({ name, summary, icon, markerDot, selected, sortable, onContextMenu, onClick, actions, as }: Props): JSX.Element;
|
|
61
|
+
export declare function SidebarWorkspaceItem({ name, summary, icon, markerDot, selected, sortable, onContextMenu, onClick, onDoubleClick, onEnter, actions, as }: Props): JSX.Element;
|
|
54
62
|
export {};
|
|
55
63
|
//# sourceMappingURL=SidebarWorkspaceItem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SidebarWorkspaceItem.d.ts","sourceRoot":"","sources":["../../../src/components/SidebarItem/SidebarWorkspaceItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"SidebarWorkspaceItem.d.ts","sourceRoot":"","sources":["../../../src/components/SidebarItem/SidebarWorkspaceItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,GAAG,EAAiB,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvE,OAAO,EAAe,KAAK,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAI/E,UAAU,KAAK;IACb;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,IAAI,EAAE,cAAc,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE;QACV,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QAClC,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IAErC;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAEnD;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,EAAE,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,SAAS,EACT,QAAgB,EAChB,QAAQ,EACR,aAAa,EACb,OAAO,EACP,aAAa,EACb,OAAO,EACP,OAAO,EACP,EAAS,EACV,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAiDrB"}
|
|
@@ -6,11 +6,24 @@ import { SIDEBAR_ITEM_BUTTON_CLASS } from './sidebarItemClasses.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* Renders a workspace row in the Collections sidebar Workspaces section.
|
|
8
8
|
*/
|
|
9
|
-
export function SidebarWorkspaceItem({ name, summary, icon, markerDot, selected = false, sortable, onContextMenu, onClick, actions, as = 'li' }) {
|
|
9
|
+
export function SidebarWorkspaceItem({ name, summary, icon, markerDot, selected = false, sortable, onContextMenu, onClick, onDoubleClick, onEnter, actions, as = 'li' }) {
|
|
10
10
|
const useListboxOption = as === 'li';
|
|
11
|
+
/**
|
|
12
|
+
* Opens workspace settings when Enter is pressed on the row.
|
|
13
|
+
*/
|
|
14
|
+
const handleKeyDown = (event) => {
|
|
15
|
+
if (event.key !== 'Enter' || onEnter == null) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
event.stopPropagation();
|
|
20
|
+
onEnter();
|
|
21
|
+
};
|
|
11
22
|
return (_jsx(SidebarItem, { selected: selected, sortable: sortable, onContextMenu: onContextMenu, actions: actions, as: as, listboxOption: useListboxOption
|
|
12
23
|
? {
|
|
13
|
-
onClick
|
|
24
|
+
onClick,
|
|
25
|
+
onDoubleClick,
|
|
26
|
+
onKeyDown: onEnter != null ? handleKeyDown : undefined
|
|
14
27
|
}
|
|
15
28
|
: undefined, children: _jsxs("span", { className: `${SIDEBAR_ITEM_BUTTON_CLASS} gap-2 rounded-md px-2 py-1`, children: [_jsx(FaIcon, { icon: icon, className: "h-3.5 w-3.5 shrink-0 text-muted", "aria-hidden": true }), _jsxs("span", { className: "inline-flex min-w-0 flex-1 items-center gap-1.5", children: [_jsx("span", { className: "min-w-0 truncate", children: name }), markerDot != null ? (_jsx(SidebarMarkerDot, { marker: markerDot.marker, visible: markerDot.visible, label: markerDot.label })) : null] }), _jsx("span", { className: "shrink-0 text-muted", children: summary })] }) }));
|
|
16
29
|
}
|
|
@@ -17,6 +17,9 @@ interface Props {
|
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Cursor-positioned context menu for tab bars, rendered in a portal at the click point.
|
|
20
|
+
*
|
|
21
|
+
* @param props - Menu groups, cursor anchor, and close handler.
|
|
22
|
+
* @returns Portal menu panel, or null when there are no items.
|
|
20
23
|
*/
|
|
21
24
|
export declare function TabContextMenu({ groups, position, onClose }: Props): JSX.Element | null;
|
|
22
25
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabContextMenu.d.ts","sourceRoot":"","sources":["../../../src/components/TabBar/TabContextMenu.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,GAAG,EAAiB,MAAM,OAAO,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAqB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"TabContextMenu.d.ts","sourceRoot":"","sources":["../../../src/components/TabBar/TabContextMenu.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,GAAG,EAAiB,MAAM,OAAO,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAqB,MAAM,oBAAoB,CAAC;AAU1E,UAAU,KAAK;IACb;;OAEG;IACH,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC;IAErB;;OAEG;IACH,QAAQ,EAAE,YAAY,CAAC;IAEvB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAoKvF"}
|
|
@@ -2,20 +2,13 @@ import { jsx as _jsx } from "@harborclient/sdk/jsx-runtime";
|
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from '@harborclient/sdk/react';
|
|
3
3
|
import { clampMenuPosition } from '../menuPosition.js';
|
|
4
4
|
import { portalToBody } from '../portalToBody.js';
|
|
5
|
+
import { findAdjacentEnabledIndex, findEdgeEnabledIndex, isMenuItemEnabled, menuItemClass } from '../rowActionsMenuHelpers.js';
|
|
5
6
|
import { cn, resolveTabListKeyAction } from '../utils.js';
|
|
6
|
-
/**
|
|
7
|
-
* Tailwind classes for a single menu item button.
|
|
8
|
-
*
|
|
9
|
-
* @param variant - Visual variant for default or destructive actions.
|
|
10
|
-
*/
|
|
11
|
-
function menuItemClass(variant) {
|
|
12
|
-
const base = 'block w-full cursor-pointer border-none bg-transparent px-3.5 py-1.5 text-left app-no-drag';
|
|
13
|
-
return variant === 'danger'
|
|
14
|
-
? `${base} text-text hover:bg-danger/15 hover:text-danger`
|
|
15
|
-
: `${base} text-text hover:bg-selection`;
|
|
16
|
-
}
|
|
17
7
|
/**
|
|
18
8
|
* Cursor-positioned context menu for tab bars, rendered in a portal at the click point.
|
|
9
|
+
*
|
|
10
|
+
* @param props - Menu groups, cursor anchor, and close handler.
|
|
11
|
+
* @returns Portal menu panel, or null when there are no items.
|
|
19
12
|
*/
|
|
20
13
|
export function TabContextMenu({ groups, position, onClose }) {
|
|
21
14
|
const menuRef = useRef(null);
|
|
@@ -41,7 +34,7 @@ export function TabContextMenu({ groups, position, onClose }) {
|
|
|
41
34
|
});
|
|
42
35
|
}, []);
|
|
43
36
|
/**
|
|
44
|
-
* Re-clamps the menu after mount and focuses the first item once dimensions are known.
|
|
37
|
+
* Re-clamps the menu after mount and focuses the first enabled item once dimensions are known.
|
|
45
38
|
*/
|
|
46
39
|
useLayoutEffect(() => {
|
|
47
40
|
const menu = menuRef.current;
|
|
@@ -53,8 +46,11 @@ export function TabContextMenu({ groups, position, onClose }) {
|
|
|
53
46
|
width: rect.width,
|
|
54
47
|
height: rect.height
|
|
55
48
|
}));
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
const firstEnabled = findEdgeEnabledIndex(flatItems, false);
|
|
50
|
+
if (firstEnabled != null) {
|
|
51
|
+
focusItem(firstEnabled);
|
|
52
|
+
}
|
|
53
|
+
}, [position, groups, flatItems, focusItem]);
|
|
58
54
|
/**
|
|
59
55
|
* Closes the menu on outside click or Escape while it is open.
|
|
60
56
|
*/
|
|
@@ -90,6 +86,15 @@ export function TabContextMenu({ groups, position, onClose }) {
|
|
|
90
86
|
closeMenu();
|
|
91
87
|
return;
|
|
92
88
|
}
|
|
89
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
90
|
+
const direction = event.key === 'ArrowDown' ? 1 : -1;
|
|
91
|
+
const next = findAdjacentEnabledIndex(flatItems, focusedIndex, direction);
|
|
92
|
+
if (next != null) {
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
focusItem(next);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
93
98
|
const arrowIndex = resolveTabListKeyAction(event.key, focusedIndex, flatItems.length);
|
|
94
99
|
if (arrowIndex !== null) {
|
|
95
100
|
event.preventDefault();
|
|
@@ -99,7 +104,7 @@ export function TabContextMenu({ groups, position, onClose }) {
|
|
|
99
104
|
if (flatItems.length === 0) {
|
|
100
105
|
return null;
|
|
101
106
|
}
|
|
102
|
-
return portalToBody(_jsx("div", { ref: menuRef, role: "menu", "aria-label": "Tab actions", className: "hc-tab-context-menu app-no-drag fixed z-
|
|
107
|
+
return portalToBody(_jsx("div", { ref: menuRef, role: "menu", "aria-label": "Tab actions", className: "hc-tab-context-menu app-no-drag fixed z-[70] min-w-[200px] rounded-md border border-separator bg-surface py-1 shadow-md", style: { left: clampedPosition.x, top: clampedPosition.y }, onKeyDown: handleMenuKeyDown, children: groups.map((group, groupIndex) => {
|
|
103
108
|
let flatIndex = groups
|
|
104
109
|
.slice(0, groupIndex)
|
|
105
110
|
.reduce((count, groupItems) => count + groupItems.length, 0);
|
|
@@ -107,10 +112,14 @@ export function TabContextMenu({ groups, position, onClose }) {
|
|
|
107
112
|
? 'hc-tab-context-menu-group border-t border-separator'
|
|
108
113
|
: 'hc-tab-context-menu-group', children: group.map((item) => {
|
|
109
114
|
const itemIndex = flatIndex++;
|
|
115
|
+
const disabled = !isMenuItemEnabled(item);
|
|
110
116
|
return (_jsx("button", { ref: (element) => {
|
|
111
117
|
itemRefs.current[itemIndex] = element;
|
|
112
|
-
}, type: "button", role: "menuitem", tabIndex: itemIndex === focusedIndex ? 0 : -1, className: cn('hc-tab-context-menu-item', menuItemClass(item.variant)), onClick: (event) => {
|
|
118
|
+
}, type: "button", role: "menuitem", disabled: disabled, tabIndex: itemIndex === focusedIndex && !disabled ? 0 : -1, className: cn('hc-tab-context-menu-item', menuItemClass(item.variant, disabled)), onClick: (event) => {
|
|
113
119
|
event.stopPropagation();
|
|
120
|
+
if (disabled) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
114
123
|
closeMenu();
|
|
115
124
|
item.onSelect?.();
|
|
116
125
|
}, children: item.label }, item.label));
|
|
@@ -19,7 +19,7 @@ interface Props extends Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'onCh
|
|
|
19
19
|
focusKey?: string;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Editable table for key/value/default/share variable rows.
|
|
22
|
+
* Editable table for key/value/default/share variable rows with an enable toggle.
|
|
23
23
|
*/
|
|
24
24
|
export declare function VariableTable({ variables, onChange, description, focusKey, className, ...props }: Props): JSX.Element;
|
|
25
25
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableTable/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,wBAAwB,EAAE,KAAK,GAAG,EAAqB,MAAM,OAAO,CAAC;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableTable/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,wBAAwB,EAAE,KAAK,GAAG,EAAqB,MAAM,OAAO,CAAC;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkB/C,UAAU,KAAM,SAAQ,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACpF;;OAEG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IAEtB;;OAEG;IACH,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAE1C;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA4KrB"}
|