@harborclient/sdk 0.6.3 → 0.6.6
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/dist/components/BusyIndicator/index.d.ts +3 -2
- package/dist/components/BusyIndicator/index.d.ts.map +1 -1
- package/dist/components/BusyIndicator/index.js +11 -3
- package/dist/components/Button/index.d.ts +23 -8
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Button/index.js +6 -4
- package/dist/components/CodeEditor/index.d.ts +9 -1
- package/dist/components/CodeEditor/index.d.ts.map +1 -1
- package/dist/components/CodeEditor/index.js +186 -62
- package/dist/components/FieldError/index.d.ts.map +1 -1
- package/dist/components/FieldError/index.js +1 -1
- package/dist/components/FormDataEditor/index.d.ts.map +1 -1
- package/dist/components/FormDataEditor/index.js +10 -38
- package/dist/components/FormDataEditor/utils.d.ts +19 -0
- package/dist/components/FormDataEditor/utils.d.ts.map +1 -0
- package/dist/components/FormDataEditor/utils.js +28 -0
- package/dist/components/FormGroup/index.d.ts +6 -1
- package/dist/components/FormGroup/index.d.ts.map +1 -1
- package/dist/components/FormGroup/index.js +25 -31
- package/dist/components/KeyValueEditor/index.d.ts.map +1 -1
- package/dist/components/KeyValueEditor/index.js +2 -3
- package/dist/components/Modal/ModalFormLayout.d.ts.map +1 -1
- package/dist/components/Modal/ModalFormLayout.js +2 -1
- package/dist/components/Modal/ModalHeader.d.ts +5 -1
- package/dist/components/Modal/ModalHeader.d.ts.map +1 -1
- package/dist/components/Modal/ModalHeader.js +2 -2
- package/dist/components/Modal/index.d.ts.map +1 -1
- package/dist/components/Modal/index.js +4 -2
- package/dist/components/OverlayCloseButton/index.d.ts.map +1 -1
- package/dist/components/OverlayCloseButton/index.js +1 -2
- package/dist/components/Page/index.d.ts +46 -0
- package/dist/components/Page/index.d.ts.map +1 -0
- package/dist/components/Page/index.js +18 -0
- package/dist/components/PageSidebar/index.d.ts +45 -0
- package/dist/components/PageSidebar/index.d.ts.map +1 -0
- package/dist/components/PageSidebar/index.js +24 -0
- package/dist/components/Resizable/Resizable.d.ts.map +1 -1
- package/dist/components/Resizable/Resizable.js +1 -1
- package/dist/components/Resizable/ResizeHandle.js +2 -2
- package/dist/components/ResourceList/index.d.ts +11 -1
- package/dist/components/ResourceList/index.d.ts.map +1 -1
- package/dist/components/ResourceList/index.js +4 -2
- package/dist/components/RowActionsMenu/index.d.ts.map +1 -1
- package/dist/components/RowActionsMenu/index.js +150 -11
- package/dist/components/SegmentedTabs/index.d.ts.map +1 -1
- package/dist/components/SegmentedTabs/index.js +2 -2
- package/dist/components/SidebarLayout/index.d.ts +22 -0
- package/dist/components/SidebarLayout/index.d.ts.map +1 -0
- package/dist/components/SidebarLayout/index.js +11 -0
- package/dist/components/TabCloseButton/index.d.ts +1 -1
- package/dist/components/TabCloseButton/index.js +2 -2
- package/dist/components/Table/index.d.ts +103 -0
- package/dist/components/Table/index.d.ts.map +1 -0
- package/dist/components/Table/index.js +99 -0
- package/dist/components/VariableInput/index.d.ts.map +1 -1
- package/dist/components/VariableInput/index.js +84 -10
- package/dist/components/VariableTable/index.d.ts.map +1 -1
- package/dist/components/VariableTable/index.js +2 -2
- package/dist/components/enhanceControl.d.ts +28 -0
- package/dist/components/enhanceControl.d.ts.map +1 -0
- package/dist/components/enhanceControl.js +84 -0
- package/dist/components/enhanceControl.test.d.ts +2 -0
- package/dist/components/enhanceControl.test.d.ts.map +1 -0
- package/dist/components/enhanceControl.test.js +83 -0
- package/dist/components/index.d.ts +6 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -1
- package/dist/components/useDialogFocus.d.ts +3 -2
- package/dist/components/useDialogFocus.d.ts.map +1 -1
- package/dist/components/useDialogFocus.js +61 -4
- package/dist/components/utils.d.ts +25 -0
- package/dist/components/utils.d.ts.map +1 -1
- package/dist/components/utils.js +24 -0
- package/dist/components/utils.test.js +18 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/variables/index.d.ts +1 -1
- package/dist/variables/index.d.ts.map +1 -1
- package/dist/variables/index.js +1 -1
- package/dist/variables/tokens.d.ts +31 -0
- package/dist/variables/tokens.d.ts.map +1 -1
- package/dist/variables/tokens.js +38 -1
- package/dist/variables/tokens.test.js +46 -1
- package/package.json +1 -1
- package/dist/components/OverlayPage/index.d.ts +0 -40
- package/dist/components/OverlayPage/index.d.ts.map +0 -1
- package/dist/components/OverlayPage/index.js +0 -18
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@harborclient/sdk/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from '@harborclient/sdk/react';
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from '@harborclient/sdk/react';
|
|
3
3
|
import { Button } from '../Button/index.js';
|
|
4
4
|
import { FaIcon } from '../FaIcon/index.js';
|
|
5
5
|
import { faBars } from '@fortawesome/free-solid-svg-icons';
|
|
6
|
+
import { resolveMenuTypeahead, resolveTabListKeyAction } from '../utils.js';
|
|
7
|
+
const TYPEAHEAD_TIMEOUT_MS = 500;
|
|
6
8
|
/**
|
|
7
9
|
* Tailwind classes for a single menu item button.
|
|
8
10
|
*
|
|
@@ -19,7 +21,79 @@ function menuItemClass(variant) {
|
|
|
19
21
|
*/
|
|
20
22
|
export function RowActionsMenu({ groups, menuId, openMenuId, onOpenChange }) {
|
|
21
23
|
const isOpen = openMenuId === menuId;
|
|
24
|
+
const menuElementId = `${menuId}-menu`;
|
|
22
25
|
const rootRef = useRef(null);
|
|
26
|
+
const triggerRef = useRef(null);
|
|
27
|
+
const itemRefs = useRef([]);
|
|
28
|
+
const typeaheadBuffer = useRef('');
|
|
29
|
+
const typeaheadTimer = useRef(null);
|
|
30
|
+
const wasOpenRef = useRef(isOpen);
|
|
31
|
+
const [focusedIndex, setFocusedIndex] = useState(0);
|
|
32
|
+
const flatItems = useMemo(() => groups.flat(), [groups]);
|
|
33
|
+
const itemLabels = useMemo(() => flatItems.map((item) => item.label), [flatItems]);
|
|
34
|
+
/**
|
|
35
|
+
* Clears the accumulated typeahead buffer.
|
|
36
|
+
*/
|
|
37
|
+
const clearTypeahead = useCallback(() => {
|
|
38
|
+
typeaheadBuffer.current = '';
|
|
39
|
+
if (typeaheadTimer.current != null) {
|
|
40
|
+
window.clearTimeout(typeaheadTimer.current);
|
|
41
|
+
typeaheadTimer.current = null;
|
|
42
|
+
}
|
|
43
|
+
}, []);
|
|
44
|
+
/**
|
|
45
|
+
* Closes the menu and returns focus to the trigger button.
|
|
46
|
+
*/
|
|
47
|
+
const closeMenu = useCallback(() => {
|
|
48
|
+
clearTypeahead();
|
|
49
|
+
onOpenChange(null);
|
|
50
|
+
requestAnimationFrame(() => {
|
|
51
|
+
triggerRef.current?.focus();
|
|
52
|
+
});
|
|
53
|
+
}, [clearTypeahead, onOpenChange]);
|
|
54
|
+
/**
|
|
55
|
+
* Focuses a menu item by index and updates roving tabindex state.
|
|
56
|
+
*
|
|
57
|
+
* @param index - Flat index of the menu item to focus.
|
|
58
|
+
*/
|
|
59
|
+
const focusItem = useCallback((index) => {
|
|
60
|
+
setFocusedIndex(index);
|
|
61
|
+
requestAnimationFrame(() => {
|
|
62
|
+
itemRefs.current[index]?.focus();
|
|
63
|
+
});
|
|
64
|
+
}, []);
|
|
65
|
+
/**
|
|
66
|
+
* Opens the menu and focuses the first or last item.
|
|
67
|
+
*
|
|
68
|
+
* @param focusLast - When true, focus the last item instead of the first.
|
|
69
|
+
*/
|
|
70
|
+
const openMenu = useCallback((focusLast = false) => {
|
|
71
|
+
if (flatItems.length === 0)
|
|
72
|
+
return;
|
|
73
|
+
setFocusedIndex(focusLast ? flatItems.length - 1 : 0);
|
|
74
|
+
onOpenChange(menuId);
|
|
75
|
+
}, [flatItems.length, menuId, onOpenChange]);
|
|
76
|
+
/**
|
|
77
|
+
* Moves focus into the menu after it opens and item refs are mounted.
|
|
78
|
+
*/
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (isOpen && !wasOpenRef.current) {
|
|
81
|
+
requestAnimationFrame(() => {
|
|
82
|
+
itemRefs.current[focusedIndex]?.focus();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
wasOpenRef.current = isOpen;
|
|
86
|
+
}, [focusedIndex, isOpen]);
|
|
87
|
+
/**
|
|
88
|
+
* Resets item refs when the menu closes.
|
|
89
|
+
*/
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!isOpen) {
|
|
92
|
+
itemRefs.current = [];
|
|
93
|
+
setFocusedIndex(0);
|
|
94
|
+
clearTypeahead();
|
|
95
|
+
}
|
|
96
|
+
}, [clearTypeahead, isOpen]);
|
|
23
97
|
/**
|
|
24
98
|
* Closes the menu on outside click or Escape while it is open.
|
|
25
99
|
*/
|
|
@@ -28,12 +102,13 @@ export function RowActionsMenu({ groups, menuId, openMenuId, onOpenChange }) {
|
|
|
28
102
|
return;
|
|
29
103
|
const handleMouseDown = (e) => {
|
|
30
104
|
if (rootRef.current && !rootRef.current.contains(e.target)) {
|
|
31
|
-
|
|
105
|
+
closeMenu();
|
|
32
106
|
}
|
|
33
107
|
};
|
|
34
108
|
const handleKeyDown = (e) => {
|
|
35
109
|
if (e.key === 'Escape') {
|
|
36
|
-
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
closeMenu();
|
|
37
112
|
}
|
|
38
113
|
};
|
|
39
114
|
document.addEventListener('mousedown', handleMouseDown);
|
|
@@ -42,13 +117,77 @@ export function RowActionsMenu({ groups, menuId, openMenuId, onOpenChange }) {
|
|
|
42
117
|
document.removeEventListener('mousedown', handleMouseDown);
|
|
43
118
|
document.removeEventListener('keydown', handleKeyDown);
|
|
44
119
|
};
|
|
45
|
-
}, [
|
|
46
|
-
|
|
120
|
+
}, [closeMenu, isOpen]);
|
|
121
|
+
/**
|
|
122
|
+
* Handles keyboard interaction on the menu trigger when closed.
|
|
123
|
+
*
|
|
124
|
+
* @param event - Keyboard event from the trigger button.
|
|
125
|
+
*/
|
|
126
|
+
const handleTriggerKeyDown = (event) => {
|
|
127
|
+
if (isOpen)
|
|
128
|
+
return;
|
|
129
|
+
if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
openMenu(false);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (event.key === 'ArrowUp') {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
openMenu(true);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Handles keyboard navigation within the open menu.
|
|
141
|
+
*
|
|
142
|
+
* @param event - Keyboard event from the menu container.
|
|
143
|
+
*/
|
|
144
|
+
const handleMenuKeyDown = (event) => {
|
|
145
|
+
if (flatItems.length === 0)
|
|
146
|
+
return;
|
|
147
|
+
if (event.key === 'Tab') {
|
|
148
|
+
closeMenu();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const arrowIndex = resolveTabListKeyAction(event.key, focusedIndex, flatItems.length);
|
|
152
|
+
if (arrowIndex !== null) {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
clearTypeahead();
|
|
155
|
+
focusItem(arrowIndex);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const typeahead = resolveMenuTypeahead(itemLabels, focusedIndex, event.key, typeaheadBuffer.current);
|
|
159
|
+
if (typeahead) {
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
typeaheadBuffer.current = typeahead.buffer;
|
|
162
|
+
if (typeaheadTimer.current != null) {
|
|
163
|
+
window.clearTimeout(typeaheadTimer.current);
|
|
164
|
+
}
|
|
165
|
+
typeaheadTimer.current = window.setTimeout(() => {
|
|
166
|
+
typeaheadBuffer.current = '';
|
|
167
|
+
typeaheadTimer.current = null;
|
|
168
|
+
}, TYPEAHEAD_TIMEOUT_MS);
|
|
169
|
+
focusItem(typeahead.index);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
return (_jsxs("div", { ref: rootRef, className: "relative shrink-0", children: [_jsx(Button, { innerRef: triggerRef, type: "button", variant: "icon", title: "Actions", "aria-label": "Row actions", "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": isOpen ? menuElementId : undefined, onClick: (e) => {
|
|
47
173
|
e.stopPropagation();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
174
|
+
if (isOpen) {
|
|
175
|
+
closeMenu();
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
openMenu(false);
|
|
179
|
+
}
|
|
180
|
+
}, onKeyDown: handleTriggerKeyDown, children: _jsx(FaIcon, { icon: faBars, className: "h-3.5 w-3.5" }) }), isOpen && (_jsx("div", { id: menuElementId, role: "menu", className: "absolute right-0 top-full z-10 mt-0.5 min-w-[120px] rounded-md border border-separator bg-surface py-1 shadow-md app-no-drag", onKeyDown: handleMenuKeyDown, children: groups.map((group, groupIndex) => {
|
|
181
|
+
let flatIndex = groups.slice(0, groupIndex).reduce((count, g) => count + g.length, 0);
|
|
182
|
+
return (_jsx("div", { className: groupIndex > 0 ? 'border-t border-separator' : undefined, children: group.map((item) => {
|
|
183
|
+
const itemIndex = flatIndex++;
|
|
184
|
+
return (_jsx("button", { ref: (el) => {
|
|
185
|
+
itemRefs.current[itemIndex] = el;
|
|
186
|
+
}, type: "button", role: "menuitem", tabIndex: itemIndex === focusedIndex ? 0 : -1, className: menuItemClass(item.variant), onClick: (e) => {
|
|
187
|
+
e.stopPropagation();
|
|
188
|
+
closeMenu();
|
|
189
|
+
item.onSelect();
|
|
190
|
+
}, children: item.label }, item.label));
|
|
191
|
+
}) }, groupIndex));
|
|
192
|
+
}) }))] }));
|
|
54
193
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SegmentedTabs/index.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3D,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM;IACvC;;OAEG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA+BD,UAAU,UAAU,CAAC,CAAC,SAAS,MAAM;IACnC;;OAEG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAE7B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,EACnD,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACT,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,CAmB7B;AAED,UAAU,UAAU,CAAC,CAAC,SAAS,MAAM;IACnC;;OAEG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EAClD,KAAK,EACL,QAAQ,EACR,SAAS,EACV,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAkBpC;AAED,UAAU,KAAK,CAAC,CAAC,SAAS,MAAM;IAC9B;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAEnB;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,CAAC;IAEV;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAE9B;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAEhC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,EAC9C,IAAI,EACJ,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,YAAY,EACtB,SAAiB,EACjB,SAAS,EACT,OAAgB,EAChB,SAAS,EAAE,aAAa,EACzB,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SegmentedTabs/index.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3D,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM;IACvC;;OAEG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA+BD,UAAU,UAAU,CAAC,CAAC,SAAS,MAAM;IACnC;;OAEG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAE7B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,EACnD,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACT,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,CAmB7B;AAED,UAAU,UAAU,CAAC,CAAC,SAAS,MAAM;IACnC;;OAEG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EAClD,KAAK,EACL,QAAQ,EACR,SAAS,EACV,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAkBpC;AAED,UAAU,KAAK,CAAC,CAAC,SAAS,MAAM;IAC9B;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAEnB;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,CAAC;IAEV;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAE9B;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAEhC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,EAC9C,IAAI,EACJ,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,YAAY,EACtB,SAAiB,EACjB,SAAS,EACT,OAAgB,EAChB,SAAS,EAAE,aAAa,EACzB,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,CA+GxB"}
|
|
@@ -91,7 +91,7 @@ export function SegmentedTabs({ tabs, value: valueProp, onChange: onChangeProp,
|
|
|
91
91
|
});
|
|
92
92
|
}, [visibleTabs, value, onChange]);
|
|
93
93
|
const isRadiogroup = pattern === 'radiogroup';
|
|
94
|
-
return (_jsx("div", { className: groupClassName, role: isRadiogroup ? 'radiogroup' : 'tablist', "aria-label": ariaLabel, onKeyDown: handleKeyDown, children: visibleTabs.map((tab) => {
|
|
94
|
+
return (_jsx("div", { className: groupClassName, role: isRadiogroup ? 'radiogroup' : 'tablist', "aria-label": ariaLabel, ...(!isRadiogroup ? { 'aria-orientation': 'horizontal' } : {}), onKeyDown: handleKeyDown, children: visibleTabs.map((tab) => {
|
|
95
95
|
const selected = value === tab.value;
|
|
96
96
|
const tabClassName = `${segment(selected)}${fullWidth ? ' flex-1' : ''}`;
|
|
97
97
|
return (_jsx("button", { ref: (element) => {
|
|
@@ -105,7 +105,7 @@ export function SegmentedTabs({ tabs, value: valueProp, onChange: onChangeProp,
|
|
|
105
105
|
role: 'tab',
|
|
106
106
|
id: getTabId(tab.value),
|
|
107
107
|
'aria-selected': selected,
|
|
108
|
-
'aria-controls': getPanelId(tab.value)
|
|
108
|
+
...(context ? { 'aria-controls': getPanelId(tab.value) } : {})
|
|
109
109
|
}), children: _jsxs("span", { className: "inline-flex items-center gap-1.5", children: [tab.label, tab.indicator && _jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-accent", "aria-hidden": true })] }) }, tab.value));
|
|
110
110
|
}) }));
|
|
111
111
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
/**
|
|
4
|
+
* Sidebar navigation rendered in the left column.
|
|
5
|
+
*/
|
|
6
|
+
sidebar: ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* Scrollable main content rendered in the right column.
|
|
9
|
+
*/
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Additional Tailwind classes merged onto the outer flex column wrapper.
|
|
13
|
+
*/
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Two-pane layout shell: a fixed sidebar column and a scrollable content area
|
|
18
|
+
* with standard page padding.
|
|
19
|
+
*/
|
|
20
|
+
export declare function SidebarLayout({ sidebar, children, className }: Props): JSX.Element;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SidebarLayout/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5C,UAAU,KAAK;IACb;;OAEG;IACH,OAAO,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAalF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@harborclient/sdk/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Two-pane layout shell: a fixed sidebar column and a scrollable content area
|
|
4
|
+
* with standard page padding.
|
|
5
|
+
*/
|
|
6
|
+
export function SidebarLayout({ sidebar, children, className }) {
|
|
7
|
+
const outer = className
|
|
8
|
+
? `flex min-h-0 flex-1 flex-col ${className}`
|
|
9
|
+
: 'flex min-h-0 flex-1 flex-col';
|
|
10
|
+
return (_jsx("div", { className: outer, children: _jsxs("div", { className: "flex min-h-0 flex-1", children: [sidebar, _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: children })] }) }));
|
|
11
|
+
}
|
|
@@ -14,7 +14,7 @@ interface Props {
|
|
|
14
14
|
title?: string;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Close control for document-style tabs in the request editor and AI chat.
|
|
18
18
|
*
|
|
19
19
|
* @param ariaLabel - Accessible name describing which tab closes.
|
|
20
20
|
* @param onClick - Click handler; callers should stop propagation when needed.
|
|
@@ -2,12 +2,12 @@ import { jsx as _jsx } from "@harborclient/sdk/jsx-runtime";
|
|
|
2
2
|
import { faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
3
3
|
import { FaIcon } from '../FaIcon/index.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Close control for document-style tabs in the request editor and AI chat.
|
|
6
6
|
*
|
|
7
7
|
* @param ariaLabel - Accessible name describing which tab closes.
|
|
8
8
|
* @param onClick - Click handler; callers should stop propagation when needed.
|
|
9
9
|
* @param title - Optional native tooltip text.
|
|
10
10
|
*/
|
|
11
11
|
export function TabCloseButton({ ariaLabel, onClick, title = 'Close tab' }) {
|
|
12
|
-
return (_jsx("button", { type: "button", className: "inline-flex aspect-square shrink-0 cursor-pointer items-center justify-center self-stretch rounded-md border-none bg-transparent text-[14px] text-muted
|
|
12
|
+
return (_jsx("button", { type: "button", className: "inline-flex aspect-square shrink-0 cursor-pointer items-center justify-center self-stretch rounded-md border-none bg-transparent text-[14px] text-muted hover:bg-selection hover:text-text focus-visible:bg-selection focus-visible:text-text app-no-drag", title: title, "aria-label": ariaLabel, onClick: onClick, children: _jsx(FaIcon, { icon: faXmark, className: "h-3.5 w-3.5" }) }));
|
|
13
13
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { HTMLAttributes, JSX, ReactNode, TdHTMLAttributes, ThHTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Layout preset for {@link Table} and its subcomponents.
|
|
4
|
+
*/
|
|
5
|
+
export type TableVariant = 'bordered' | 'loose';
|
|
6
|
+
/**
|
|
7
|
+
* Default header cell classes for bordered editor tables.
|
|
8
|
+
*/
|
|
9
|
+
export declare const tableHeadClass = "border-r border-b border-separator p-3 text-left text-[14px] font-medium uppercase tracking-wide text-muted last:border-r-0";
|
|
10
|
+
/**
|
|
11
|
+
* Default body cell classes for bordered editor tables.
|
|
12
|
+
*/
|
|
13
|
+
export declare const tableCellClass = "border-r border-b border-separator p-3 last:border-r-0";
|
|
14
|
+
/**
|
|
15
|
+
* Header cell classes for loose tables with separate cell spacing.
|
|
16
|
+
*/
|
|
17
|
+
export declare const tableHeadClassLoose = "pb-1 text-left text-[14px] font-medium uppercase tracking-wide text-muted";
|
|
18
|
+
/**
|
|
19
|
+
* Body cell classes for loose tables with separate cell spacing.
|
|
20
|
+
*/
|
|
21
|
+
export declare const tableCellClassLoose = "";
|
|
22
|
+
interface TableProps {
|
|
23
|
+
/**
|
|
24
|
+
* Table header and body sections.
|
|
25
|
+
*/
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* Layout preset for the table shell and cell styling.
|
|
29
|
+
*/
|
|
30
|
+
variant?: TableVariant;
|
|
31
|
+
/**
|
|
32
|
+
* Additional Tailwind classes merged onto the outer wrapper or table element.
|
|
33
|
+
*/
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Table shell for editable row layouts.
|
|
38
|
+
*
|
|
39
|
+
* @param children - {@link TableHeader} and {@link TableBody} sections.
|
|
40
|
+
* @param variant - Bordered collapsed layout or loose separate-cell spacing.
|
|
41
|
+
* @param className - Extra classes on the wrapper (bordered) or table element (loose).
|
|
42
|
+
*/
|
|
43
|
+
export declare function Table({ children, variant, className }: TableProps): JSX.Element;
|
|
44
|
+
interface TableSectionProps extends HTMLAttributes<HTMLTableSectionElement> {
|
|
45
|
+
/**
|
|
46
|
+
* Header or body row content.
|
|
47
|
+
*/
|
|
48
|
+
children: ReactNode;
|
|
49
|
+
/**
|
|
50
|
+
* Additional Tailwind classes merged onto the section element.
|
|
51
|
+
*/
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Table header section wrapper.
|
|
56
|
+
*
|
|
57
|
+
* @param children - Header row elements, typically native `tr` nodes.
|
|
58
|
+
* @param className - Extra classes appended after any section preset.
|
|
59
|
+
*/
|
|
60
|
+
export declare function TableHeader({ children, className, ...rest }: TableSectionProps): JSX.Element;
|
|
61
|
+
/**
|
|
62
|
+
* Table body section wrapper.
|
|
63
|
+
*
|
|
64
|
+
* @param children - Body row elements, typically native `tr` nodes.
|
|
65
|
+
* @param className - Extra classes appended after the layout preset.
|
|
66
|
+
*/
|
|
67
|
+
export declare function TableBody({ children, className, ...rest }: TableSectionProps): JSX.Element;
|
|
68
|
+
interface TableHeadProps extends ThHTMLAttributes<HTMLTableCellElement> {
|
|
69
|
+
/**
|
|
70
|
+
* Header cell content.
|
|
71
|
+
*/
|
|
72
|
+
children?: ReactNode;
|
|
73
|
+
/**
|
|
74
|
+
* Additional Tailwind classes merged after the header cell preset.
|
|
75
|
+
*/
|
|
76
|
+
className?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Header cell with table styling for the active variant.
|
|
80
|
+
*
|
|
81
|
+
* @param children - Header label content.
|
|
82
|
+
* @param className - Extra classes appended after the layout preset.
|
|
83
|
+
*/
|
|
84
|
+
export declare function TableHead({ children, className, scope, ...rest }: TableHeadProps): JSX.Element;
|
|
85
|
+
interface TableCellProps extends TdHTMLAttributes<HTMLTableCellElement> {
|
|
86
|
+
/**
|
|
87
|
+
* Body cell content.
|
|
88
|
+
*/
|
|
89
|
+
children?: ReactNode;
|
|
90
|
+
/**
|
|
91
|
+
* Additional Tailwind classes merged after the body cell preset.
|
|
92
|
+
*/
|
|
93
|
+
className?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Body cell with table styling for the active variant.
|
|
97
|
+
*
|
|
98
|
+
* @param children - Cell content.
|
|
99
|
+
* @param className - Extra classes appended after the layout preset.
|
|
100
|
+
*/
|
|
101
|
+
export declare function TableCell({ children, className, ...rest }: TableCellProps): JSX.Element;
|
|
102
|
+
export {};
|
|
103
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Table/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAEhG;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;AAEhD;;GAEG;AACH,eAAO,MAAM,cAAc,gIACoG,CAAC;AAEhI;;GAEG;AACH,eAAO,MAAM,cAAc,2DAA2D,CAAC;AAEvF;;GAEG;AACH,eAAO,MAAM,mBAAmB,8EAC6C,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAuBtC,UAAU,UAAU;IAClB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,OAAoB,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,GAAG,CAAC,OAAO,CAwB5F;AAED,UAAU,iBAAkB,SAAQ,cAAc,CAAC,uBAAuB,CAAC;IACzE;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,CAM5F;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,CAU1F;AAED,UAAU,cAAe,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IACrE;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,SAAS,EACT,KAAa,EACb,GAAG,IAAI,EACR,EAAE,cAAc,GAAG,GAAG,CAAC,OAAO,CAU9B;AAED,UAAU,cAAe,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IACrE;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,GAAG,GAAG,CAAC,OAAO,CAUvF"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@harborclient/sdk/jsx-runtime";
|
|
2
|
+
import { createContext, createElement, useContext } from '@harborclient/sdk/react';
|
|
3
|
+
/**
|
|
4
|
+
* Default header cell classes for bordered editor tables.
|
|
5
|
+
*/
|
|
6
|
+
export const tableHeadClass = 'border-r border-b border-separator p-3 text-left text-[14px] font-medium uppercase tracking-wide text-muted last:border-r-0';
|
|
7
|
+
/**
|
|
8
|
+
* Default body cell classes for bordered editor tables.
|
|
9
|
+
*/
|
|
10
|
+
export const tableCellClass = 'border-r border-b border-separator p-3 last:border-r-0';
|
|
11
|
+
/**
|
|
12
|
+
* Header cell classes for loose tables with separate cell spacing.
|
|
13
|
+
*/
|
|
14
|
+
export const tableHeadClassLoose = 'pb-1 text-left text-[14px] font-medium uppercase tracking-wide text-muted';
|
|
15
|
+
/**
|
|
16
|
+
* Body cell classes for loose tables with separate cell spacing.
|
|
17
|
+
*/
|
|
18
|
+
export const tableCellClassLoose = '';
|
|
19
|
+
const TableVariantContext = createContext('bordered');
|
|
20
|
+
/**
|
|
21
|
+
* Returns preset classes merged with optional overrides.
|
|
22
|
+
*
|
|
23
|
+
* @param base - Variant preset classes.
|
|
24
|
+
* @param className - Additional classes appended after the preset.
|
|
25
|
+
*/
|
|
26
|
+
function mergeClasses(base, className) {
|
|
27
|
+
if (base && className) {
|
|
28
|
+
return `${base} ${className}`;
|
|
29
|
+
}
|
|
30
|
+
if (base) {
|
|
31
|
+
return base;
|
|
32
|
+
}
|
|
33
|
+
if (className) {
|
|
34
|
+
return className;
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Table shell for editable row layouts.
|
|
40
|
+
*
|
|
41
|
+
* @param children - {@link TableHeader} and {@link TableBody} sections.
|
|
42
|
+
* @param variant - Bordered collapsed layout or loose separate-cell spacing.
|
|
43
|
+
* @param className - Extra classes on the wrapper (bordered) or table element (loose).
|
|
44
|
+
*/
|
|
45
|
+
export function Table({ children, variant = 'bordered', className }) {
|
|
46
|
+
if (variant === 'loose') {
|
|
47
|
+
const tableBase = 'w-full border-separate border-spacing-x-1.5 border-spacing-y-1.5';
|
|
48
|
+
const tableClasses = className ? `${tableBase} ${className}` : tableBase;
|
|
49
|
+
return createElement(TableVariantContext.Provider, { value: variant }, createElement('table', { className: tableClasses }, children));
|
|
50
|
+
}
|
|
51
|
+
const wrapperBase = 'overflow-hidden rounded-md border border-separator';
|
|
52
|
+
const wrapperClasses = className ? `${wrapperBase} ${className}` : wrapperBase;
|
|
53
|
+
return createElement(TableVariantContext.Provider, { value: variant }, createElement('div', { className: wrapperClasses }, createElement('table', { className: 'w-full border-collapse' }, children)));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Table header section wrapper.
|
|
57
|
+
*
|
|
58
|
+
* @param children - Header row elements, typically native `tr` nodes.
|
|
59
|
+
* @param className - Extra classes appended after any section preset.
|
|
60
|
+
*/
|
|
61
|
+
export function TableHeader({ children, className, ...rest }) {
|
|
62
|
+
return (_jsx("thead", { className: className, ...rest, children: children }));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Table body section wrapper.
|
|
66
|
+
*
|
|
67
|
+
* @param children - Body row elements, typically native `tr` nodes.
|
|
68
|
+
* @param className - Extra classes appended after the layout preset.
|
|
69
|
+
*/
|
|
70
|
+
export function TableBody({ children, className, ...rest }) {
|
|
71
|
+
const variant = useContext(TableVariantContext);
|
|
72
|
+
const base = variant === 'bordered' ? '[&_tr:last-child_td]:border-b-0' : '';
|
|
73
|
+
const classes = mergeClasses(base, className);
|
|
74
|
+
return (_jsx("tbody", { className: classes, ...rest, children: children }));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Header cell with table styling for the active variant.
|
|
78
|
+
*
|
|
79
|
+
* @param children - Header label content.
|
|
80
|
+
* @param className - Extra classes appended after the layout preset.
|
|
81
|
+
*/
|
|
82
|
+
export function TableHead({ children, className, scope = 'col', ...rest }) {
|
|
83
|
+
const variant = useContext(TableVariantContext);
|
|
84
|
+
const base = variant === 'loose' ? tableHeadClassLoose : tableHeadClass;
|
|
85
|
+
const classes = mergeClasses(base, className);
|
|
86
|
+
return (_jsx("th", { scope: scope, className: classes, ...rest, children: children }));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Body cell with table styling for the active variant.
|
|
90
|
+
*
|
|
91
|
+
* @param children - Cell content.
|
|
92
|
+
* @param className - Extra classes appended after the layout preset.
|
|
93
|
+
*/
|
|
94
|
+
export function TableCell({ children, className, ...rest }) {
|
|
95
|
+
const variant = useContext(TableVariantContext);
|
|
96
|
+
const base = variant === 'loose' ? tableCellClassLoose : tableCellClass;
|
|
97
|
+
const classes = mergeClasses(base, className);
|
|
98
|
+
return (_jsx("td", { className: classes, ...rest, children: children }));
|
|
99
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableInput/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableInput/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkB/C,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,SAAS,EACT,WAAW,EACX,SAAS,EACT,SAAc,EACd,gBAAgB,EAChB,cAAc,EACd,EAAE,EACF,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EAClC,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA0PrB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@harborclient/sdk/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useRef, useState } from '@harborclient/sdk/react';
|
|
3
|
-
import { getDynamicVariableDescription, resolveVariable, tokenizeVariables } from '../../variables/index.js';
|
|
2
|
+
import { useEffect, useId, useMemo, useRef, useState } from '@harborclient/sdk/react';
|
|
3
|
+
import { getDynamicVariableDescription, getVariableTokenAtOffset, getVariableTooltipContent, resolveVariable, tokenizeVariables } from '../../variables/index.js';
|
|
4
4
|
import { Input } from '../forms/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* Text input that highlights {{variable}} tokens and shows resolved values on hover.
|
|
@@ -14,6 +14,7 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
14
14
|
const spanRefs = useRef(new Map());
|
|
15
15
|
const hideTimer = useRef(null);
|
|
16
16
|
const [tooltip, setTooltip] = useState(null);
|
|
17
|
+
const tooltipId = useId();
|
|
17
18
|
/**
|
|
18
19
|
* Splits the input value into plain text and {{variable}} token spans for highlighting.
|
|
19
20
|
*/
|
|
@@ -48,6 +49,53 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
48
49
|
backdrop.scrollLeft = input.scrollLeft;
|
|
49
50
|
}
|
|
50
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* Shows a tooltip for a variable token at the given screen position.
|
|
54
|
+
*
|
|
55
|
+
* @param key - Variable name from the token.
|
|
56
|
+
* @param top - Top coordinate for tooltip placement.
|
|
57
|
+
* @param left - Horizontal center coordinate for tooltip placement.
|
|
58
|
+
*/
|
|
59
|
+
const showTooltipForKey = (key, top, left) => {
|
|
60
|
+
setTooltip({
|
|
61
|
+
key,
|
|
62
|
+
value: resolveVariable(key, variables),
|
|
63
|
+
dynamicDescription: getDynamicVariableDescription(key),
|
|
64
|
+
top,
|
|
65
|
+
left
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Updates the tooltip based on the current text caret position.
|
|
70
|
+
*/
|
|
71
|
+
const updateTooltipFromCaret = () => {
|
|
72
|
+
const input = inputRef.current;
|
|
73
|
+
if (!input)
|
|
74
|
+
return;
|
|
75
|
+
const offset = input.selectionStart ?? 0;
|
|
76
|
+
const match = getVariableTokenAtOffset(value, offset);
|
|
77
|
+
if (!match) {
|
|
78
|
+
setTooltip(null);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let tokenIndex = -1;
|
|
82
|
+
let position = 0;
|
|
83
|
+
for (const [index, token] of tokens.entries()) {
|
|
84
|
+
if (token.key && position === match.start) {
|
|
85
|
+
tokenIndex = index;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
position += token.text.length;
|
|
89
|
+
}
|
|
90
|
+
const span = tokenIndex >= 0 ? spanRefs.current.get(tokenIndex) : undefined;
|
|
91
|
+
if (span) {
|
|
92
|
+
const rect = span.getBoundingClientRect();
|
|
93
|
+
showTooltipForKey(match.key, rect.top, rect.left + rect.width / 2);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const rect = input.getBoundingClientRect();
|
|
97
|
+
showTooltipForKey(match.key, rect.top, rect.left + rect.width / 2);
|
|
98
|
+
};
|
|
51
99
|
/**
|
|
52
100
|
* Shows a tooltip when the pointer is over a variable token span.
|
|
53
101
|
*
|
|
@@ -66,24 +114,50 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
66
114
|
e.clientX <= rect.right &&
|
|
67
115
|
e.clientY >= rect.top &&
|
|
68
116
|
e.clientY <= rect.bottom) {
|
|
69
|
-
|
|
70
|
-
key: token.key,
|
|
71
|
-
value: resolveVariable(token.key, variables),
|
|
72
|
-
dynamicDescription: getDynamicVariableDescription(token.key),
|
|
73
|
-
top: rect.top,
|
|
74
|
-
left: rect.left + rect.width / 2
|
|
75
|
-
});
|
|
117
|
+
showTooltipForKey(token.key, rect.top, rect.left + rect.width / 2);
|
|
76
118
|
return;
|
|
77
119
|
}
|
|
78
120
|
}
|
|
79
121
|
setTooltip(null);
|
|
80
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* Handles keyboard events on the input, including tooltip dismissal.
|
|
125
|
+
*
|
|
126
|
+
* @param event - Keyboard event from the input.
|
|
127
|
+
*/
|
|
128
|
+
const handleKeyDown = (event) => {
|
|
129
|
+
if (event.key === 'Escape' && tooltip) {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
setTooltip(null);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
onKeyDown?.(event);
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Updates the tooltip after keyboard navigation moves the caret.
|
|
138
|
+
*
|
|
139
|
+
* @param event - Keyboard event from the input.
|
|
140
|
+
*/
|
|
141
|
+
const handleKeyUp = (event) => {
|
|
142
|
+
if (event.key === 'ArrowLeft' ||
|
|
143
|
+
event.key === 'ArrowRight' ||
|
|
144
|
+
event.key === 'ArrowUp' ||
|
|
145
|
+
event.key === 'ArrowDown' ||
|
|
146
|
+
event.key === 'Home' ||
|
|
147
|
+
event.key === 'End') {
|
|
148
|
+
updateTooltipFromCaret();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const tooltipContent = tooltip ? getVariableTooltipContent(tooltip.key, variables) : null;
|
|
81
152
|
return (_jsxs("div", { className: wrapperClassName ? `relative min-w-0 ${wrapperClassName}` : 'relative min-w-0 flex-1', children: [_jsx("div", { ref: backdropRef, "aria-hidden": true, className: "pointer-events-none absolute inset-0 overflow-hidden whitespace-nowrap px-2 py-1.5 text-[14px] text-inherit", children: value ? (tokens.map((token, index) => token.key ? (_jsx("span", { ref: (el) => {
|
|
82
153
|
if (el)
|
|
83
154
|
spanRefs.current.set(index, el);
|
|
84
155
|
else
|
|
85
156
|
spanRefs.current.delete(index);
|
|
86
|
-
}, className: "text-[#32D2E2]", children: token.text }, index)) : (_jsx("span", { children: token.text }, index)))) : (_jsx("span", { className: "text-muted", children: placeholder })) }), _jsx(Input, { ref: inputRef, id: id, variant: "plain", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className: `relative w-full min-w-0 border-none bg-transparent px-2 py-1.5 text-[14px] text-transparent caret-text focus-visible:shadow-none ${className}`, type: "text", placeholder: placeholder, value: value, onChange: (e) =>
|
|
157
|
+
}, className: "text-[#32D2E2]", children: token.text }, index)) : (_jsx("span", { children: token.text }, index)))) : (_jsx("span", { className: "text-muted", children: placeholder })) }), _jsx(Input, { ref: inputRef, id: id, variant: "plain", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": tooltip ? tooltipId : undefined, className: `relative w-full min-w-0 border-none bg-transparent px-2 py-1.5 text-[14px] text-transparent caret-text focus-visible:shadow-none ${className}`, type: "text", placeholder: placeholder, value: value, onChange: (e) => {
|
|
158
|
+
onChange(e.target.value);
|
|
159
|
+
queueMicrotask(updateTooltipFromCaret);
|
|
160
|
+
}, onFocus: updateTooltipFromCaret, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onSelect: updateTooltipFromCaret, onClick: updateTooltipFromCaret, onScroll: syncScroll, onMouseMove: handleMouseMove, onMouseLeave: scheduleHide }), tooltip && tooltipContent && (_jsxs("div", { id: tooltipId, role: "tooltip", className: "pointer-events-auto fixed z-50 flex max-w-sm -translate-x-1/2 -translate-y-full flex-col gap-1.5 rounded-md border border-separator bg-surface px-3 py-2 text-[14px] text-text shadow-md", style: { top: tooltip.top - 4, left: tooltip.left }, onMouseEnter: cancelHide, onMouseLeave: () => setTooltip(null), children: [_jsx("span", { className: tooltipContent.muted ? 'text-muted' : undefined, children: tooltipContent.text }), onEditVariable && (_jsx("button", { type: "button", className: "self-start text-[14px] text-accent hover:underline app-no-drag", "aria-label": `Edit value for ${tooltip.key}`, onClick: () => {
|
|
87
161
|
onEditVariable();
|
|
88
162
|
setTooltip(null);
|
|
89
163
|
}, children: "Edit value" }))] }))] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableTable/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableTable/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAY/C,UAAU,KAAK;IACb;;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;CACtB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAmHtF"}
|