@harborclient/sdk 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Autocomplete/AutocompleteInput.d.ts.map +1 -1
- package/dist/components/Autocomplete/AutocompleteInput.js +3 -2
- package/dist/components/Autocomplete/useAutocomplete.d.ts +7 -0
- package/dist/components/Autocomplete/useAutocomplete.d.ts.map +1 -1
- package/dist/components/Autocomplete/useAutocomplete.js +59 -13
- package/dist/components/KeyValueEditor/index.d.ts.map +1 -1
- package/dist/components/KeyValueEditor/index.js +14 -1
- package/dist/components/PageHeader/index.js +2 -2
- package/dist/components/VariableInput/index.d.ts.map +1 -1
- package/dist/components/VariableInput/index.js +6 -5
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutocompleteInput.d.ts","sourceRoot":"","sources":["../../../src/components/Autocomplete/AutocompleteInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC9F;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"AutocompleteInput.d.ts","sourceRoot":"","sources":["../../../src/components/Autocomplete/AutocompleteInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC9F;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAkErB"}
|
|
@@ -6,12 +6,13 @@ import { useAutocomplete } from './useAutocomplete.js';
|
|
|
6
6
|
* Text input with optional async autocomplete suggestions.
|
|
7
7
|
*/
|
|
8
8
|
export function AutocompleteInput({ value, onChange, source, className, onFocus, onBlur, onKeyDown, ...props }) {
|
|
9
|
+
const safeValue = value ?? '';
|
|
9
10
|
const { open, items, activeIndex, anchorRef, listboxId, onFocus: openAutocomplete, onBlur: closeAutocomplete, onInputKeyDown, selectItem, setActiveIndex, closeSuggestions } = useAutocomplete({
|
|
10
11
|
source,
|
|
11
|
-
value,
|
|
12
|
+
value: safeValue,
|
|
12
13
|
onSelect: onChange
|
|
13
14
|
});
|
|
14
|
-
return (_jsxs(_Fragment, { children: [_jsx(Input, { ...props, ref: anchorRef, role: source ? 'combobox' : undefined, "aria-autocomplete": source ? 'list' : undefined, "aria-expanded": source ? open : undefined, "aria-controls": source && open ? listboxId : undefined, "aria-activedescendant": source && open && activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined, className: className, value:
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsx(Input, { ...props, ref: anchorRef, role: source ? 'combobox' : undefined, "aria-autocomplete": source ? 'list' : undefined, "aria-expanded": source ? open : undefined, "aria-controls": source && open ? listboxId : undefined, "aria-activedescendant": source && open && activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined, className: className, value: safeValue, onChange: (event) => onChange(event.target.value), onFocus: (event) => {
|
|
15
16
|
openAutocomplete();
|
|
16
17
|
onFocus?.(event);
|
|
17
18
|
}, onBlur: (event) => {
|
|
@@ -74,6 +74,13 @@ interface UseAutocompleteResult {
|
|
|
74
74
|
*/
|
|
75
75
|
closeSuggestions: () => void;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Filters cached suggestions by the current input value.
|
|
79
|
+
*
|
|
80
|
+
* @param items - Full suggestion list.
|
|
81
|
+
* @param value - Current input value.
|
|
82
|
+
*/
|
|
83
|
+
export declare function filterAutocompleteItems(items: string[], value: string | null | undefined): string[];
|
|
77
84
|
/**
|
|
78
85
|
* Headless autocomplete state for combobox inputs.
|
|
79
86
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAutocomplete.d.ts","sourceRoot":"","sources":["../../../src/components/Autocomplete/useAutocomplete.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,UAAU,sBAAsB;IAC9B;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAE5B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChD;AAED,UAAU,qBAAqB;IAC7B;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC;IAEd;;OAEG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAEhB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,SAAS,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAE9C;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB;;;;OAIG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC;IAEpE;;;;OAIG;IACH,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,YAAY,EAAE,MAAM,IAAI,CAAC;IAEzB;;OAEG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC;;OAEG;IACH,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"useAutocomplete.d.ts","sourceRoot":"","sources":["../../../src/components/Autocomplete/useAutocomplete.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,UAAU,sBAAsB;IAC9B;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAE5B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChD;AAED,UAAU,qBAAqB;IAC7B;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC;IAEd;;OAEG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAEhB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,SAAS,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAE9C;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB;;;;OAIG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC;IAEpE;;;;OAIG;IACH,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,YAAY,EAAE,MAAM,IAAI,CAAC;IAEzB;;OAEG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC;;OAEG;IACH,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,EAAE,CAaV;AAcD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,KAAK,EACL,QAAQ,EACR,SAAS,EAAE,iBAAiB,EAC7B,EAAE,sBAAsB,GAAG,qBAAqB,CAqOhD"}
|
|
@@ -5,8 +5,8 @@ import { useCallback, useEffect, useId, useRef, useState } from '@harborclient/s
|
|
|
5
5
|
* @param items - Full suggestion list.
|
|
6
6
|
* @param value - Current input value.
|
|
7
7
|
*/
|
|
8
|
-
function
|
|
9
|
-
const trimmed = value.trim();
|
|
8
|
+
export function filterAutocompleteItems(items, value) {
|
|
9
|
+
const trimmed = (value ?? '').trim();
|
|
10
10
|
const lower = trimmed.toLowerCase();
|
|
11
11
|
return items.filter((item) => {
|
|
12
12
|
if (item.toLowerCase() === lower) {
|
|
@@ -18,6 +18,17 @@ function filterItems(items, value) {
|
|
|
18
18
|
return item.toLowerCase().includes(lower);
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Normalizes a source list response to a string array.
|
|
23
|
+
*
|
|
24
|
+
* @param list - Raw value returned from {@link AutocompleteSource.list}.
|
|
25
|
+
*/
|
|
26
|
+
function normalizeSourceList(list) {
|
|
27
|
+
if (!Array.isArray(list)) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
return list.filter((item) => typeof item === 'string');
|
|
31
|
+
}
|
|
21
32
|
/**
|
|
22
33
|
* Headless autocomplete state for combobox inputs.
|
|
23
34
|
*
|
|
@@ -29,10 +40,22 @@ export function useAutocomplete({ source, value, onSelect, anchorRef: externalAn
|
|
|
29
40
|
const cacheRef = useRef([]);
|
|
30
41
|
const loadedRef = useRef(false);
|
|
31
42
|
const loadPromiseRef = useRef(null);
|
|
43
|
+
const mountedRef = useRef(true);
|
|
32
44
|
const listboxId = useId();
|
|
45
|
+
const safeValue = value ?? '';
|
|
33
46
|
const [open, setOpen] = useState(false);
|
|
34
47
|
const [items, setItems] = useState([]);
|
|
35
48
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
49
|
+
/**
|
|
50
|
+
* Clears the mounted flag when the hook unmounts so async work cannot set state.
|
|
51
|
+
*/
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
mountedRef.current = true;
|
|
54
|
+
return () => {
|
|
55
|
+
mountedRef.current = false;
|
|
56
|
+
loadPromiseRef.current = null;
|
|
57
|
+
};
|
|
58
|
+
}, []);
|
|
36
59
|
/**
|
|
37
60
|
* Loads suggestions from the source when not already cached.
|
|
38
61
|
*/
|
|
@@ -46,11 +69,20 @@ export function useAutocomplete({ source, value, onSelect, anchorRef: externalAn
|
|
|
46
69
|
if (loadPromiseRef.current) {
|
|
47
70
|
return loadPromiseRef.current;
|
|
48
71
|
}
|
|
49
|
-
loadPromiseRef.current = source
|
|
50
|
-
|
|
72
|
+
loadPromiseRef.current = source
|
|
73
|
+
.list()
|
|
74
|
+
.then((list) => {
|
|
75
|
+
const normalized = normalizeSourceList(list);
|
|
76
|
+
cacheRef.current = normalized;
|
|
77
|
+
loadedRef.current = true;
|
|
78
|
+
loadPromiseRef.current = null;
|
|
79
|
+
return normalized;
|
|
80
|
+
})
|
|
81
|
+
.catch(() => {
|
|
82
|
+
cacheRef.current = [];
|
|
51
83
|
loadedRef.current = true;
|
|
52
84
|
loadPromiseRef.current = null;
|
|
53
|
-
return
|
|
85
|
+
return [];
|
|
54
86
|
});
|
|
55
87
|
return loadPromiseRef.current;
|
|
56
88
|
}, [source]);
|
|
@@ -59,11 +91,16 @@ export function useAutocomplete({ source, value, onSelect, anchorRef: externalAn
|
|
|
59
91
|
*/
|
|
60
92
|
const refreshItems = useCallback(() => {
|
|
61
93
|
if (!source) {
|
|
62
|
-
|
|
94
|
+
if (mountedRef.current) {
|
|
95
|
+
setItems([]);
|
|
96
|
+
}
|
|
63
97
|
return;
|
|
64
98
|
}
|
|
65
|
-
|
|
66
|
-
|
|
99
|
+
if (!mountedRef.current) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
setItems(filterAutocompleteItems(cacheRef.current, safeValue));
|
|
103
|
+
}, [source, safeValue]);
|
|
67
104
|
/**
|
|
68
105
|
* Persists a new value when it is not already known.
|
|
69
106
|
*/
|
|
@@ -71,7 +108,7 @@ export function useAutocomplete({ source, value, onSelect, anchorRef: externalAn
|
|
|
71
108
|
if (!source) {
|
|
72
109
|
return;
|
|
73
110
|
}
|
|
74
|
-
const trimmed =
|
|
111
|
+
const trimmed = safeValue.trim();
|
|
75
112
|
if (!trimmed) {
|
|
76
113
|
return;
|
|
77
114
|
}
|
|
@@ -86,7 +123,7 @@ export function useAutocomplete({ source, value, onSelect, anchorRef: externalAn
|
|
|
86
123
|
catch {
|
|
87
124
|
cacheRef.current = cacheRef.current.filter((item) => item !== trimmed);
|
|
88
125
|
}
|
|
89
|
-
}, [source,
|
|
126
|
+
}, [source, safeValue]);
|
|
90
127
|
/**
|
|
91
128
|
* Opens the suggestion list and loads items when a source is configured.
|
|
92
129
|
*/
|
|
@@ -95,15 +132,21 @@ export function useAutocomplete({ source, value, onSelect, anchorRef: externalAn
|
|
|
95
132
|
return;
|
|
96
133
|
}
|
|
97
134
|
await ensureLoaded();
|
|
98
|
-
|
|
135
|
+
if (!mountedRef.current) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const filtered = filterAutocompleteItems(cacheRef.current, safeValue);
|
|
99
139
|
setItems(filtered);
|
|
100
140
|
setActiveIndex(filtered.length > 0 ? 0 : -1);
|
|
101
141
|
setOpen(true);
|
|
102
|
-
}, [ensureLoaded, source,
|
|
142
|
+
}, [ensureLoaded, source, safeValue]);
|
|
103
143
|
/**
|
|
104
144
|
* Closes the suggestion list and resets highlight state.
|
|
105
145
|
*/
|
|
106
146
|
const closeSuggestions = useCallback(() => {
|
|
147
|
+
if (!mountedRef.current) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
107
150
|
setOpen(false);
|
|
108
151
|
setActiveIndex(-1);
|
|
109
152
|
}, []);
|
|
@@ -169,11 +212,14 @@ export function useAutocomplete({ source, value, onSelect, anchorRef: externalAn
|
|
|
169
212
|
}
|
|
170
213
|
return false;
|
|
171
214
|
}, [activeIndex, closeSuggestions, commit, items, open, selectItem, source]);
|
|
215
|
+
/**
|
|
216
|
+
* Re-filters suggestions when the input value changes while the list is open.
|
|
217
|
+
*/
|
|
172
218
|
useEffect(() => {
|
|
173
219
|
if (open) {
|
|
174
220
|
refreshItems();
|
|
175
221
|
}
|
|
176
|
-
}, [open, refreshItems,
|
|
222
|
+
}, [open, refreshItems, safeValue]);
|
|
177
223
|
return {
|
|
178
224
|
open,
|
|
179
225
|
items,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/KeyValueEditor/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,IAAI,EAAE,QAAQ,EAAE,CAAC;IAEjB;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAErC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IAEtB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAE/B;;OAEG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,QAAQ,EACR,cAAsB,EACtB,gBAA0B,EAC1B,SAAS,EACT,cAAc,EACd,SAAS,EACT,WAAW,EACZ,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/KeyValueEditor/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,IAAI,EAAE,QAAQ,EAAE,CAAC;IAEjB;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAErC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IAEtB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAE/B;;OAEG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,QAAQ,EACR,cAAsB,EACtB,gBAA0B,EAC1B,SAAS,EACT,cAAc,EACd,SAAS,EACT,WAAW,EACZ,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA2GrB"}
|
|
@@ -36,5 +36,18 @@ export function KeyValueEditor({ rows, onChange, placeholderKey = 'Key', placeho
|
|
|
36
36
|
}
|
|
37
37
|
onChange(rows.filter((_, i) => i !== index));
|
|
38
38
|
};
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Normalizes a key-value row so editors never receive undefined fields.
|
|
41
|
+
*
|
|
42
|
+
* @param row - Raw row from host state or imports.
|
|
43
|
+
*/
|
|
44
|
+
const normalizeRow = (row) => ({
|
|
45
|
+
key: row.key ?? '',
|
|
46
|
+
value: row.value ?? '',
|
|
47
|
+
enabled: row.enabled ?? true
|
|
48
|
+
});
|
|
49
|
+
return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs("tr", { children: [_jsx(TableHead, { className: "w-6 p-0", children: _jsx("span", { className: "sr-only", children: "Enable" }) }), _jsx(TableHead, { children: "Key" }), _jsx(TableHead, { children: "Value" }), _jsx(TableHead, { className: "w-7 p-0" })] }) }), _jsx(TableBody, { children: rows.map((row, index) => {
|
|
50
|
+
const normalizedRow = normalizeRow(row);
|
|
51
|
+
return (_jsxs("tr", { children: [_jsx(TableCell, { className: "w-6 p-1 text-center", children: _jsx(Checkbox, { className: "app-no-drag", checked: normalizedRow.enabled, onChange: (e) => updateRow(index, { enabled: e.target.checked }), "aria-label": `Enable row ${index + 1}`, title: "Enable" }) }), _jsx(TableCell, { children: _jsx(AutocompleteInput, { type: "text", className: "w-full", value: normalizedRow.key, source: keySource, placeholder: placeholderKey, "aria-label": `Key, row ${index + 1}`, onChange: (key) => updateRow(index, { key }) }) }), _jsx(TableCell, { children: _jsx(VariableInput, { wrapperClassName: `${fieldFrame} w-full`, className: "app-no-drag", value: normalizedRow.value, onChange: (value) => updateRow(index, { value }), variables: variables, source: valueSource, placeholder: placeholderValue, "aria-label": `Value, row ${index + 1}`, onEditVariable: onEditVariable }) }), _jsx(TableCell, { className: "w-7 p-1 text-center", children: _jsx(Button, { type: "button", variant: "iconDanger", onClick: () => removeRow(index), title: "Remove", "aria-label": `Remove row ${index + 1}`, children: _jsx(FaIcon, { icon: faXmark, className: "h-3.5 w-3.5" }) }) })] }, index));
|
|
52
|
+
}) })] }));
|
|
40
53
|
}
|
|
@@ -6,7 +6,7 @@ import { FaIcon } from '../FaIcon/index.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export function PageHeader({ title, description, icon, children, className }) {
|
|
8
8
|
const wrapperClassName = className
|
|
9
|
-
? `-mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator
|
|
10
|
-
: '-mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator
|
|
9
|
+
? `-mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator p-4 ${className}`
|
|
10
|
+
: '-mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator p-4';
|
|
11
11
|
return (_jsxs("div", { className: wrapperClassName, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("h2", { className: "m-0 flex items-center gap-2 text-[17px] font-semibold text-text", children: [icon ? _jsx(FaIcon, { icon: icon, className: "h-4 w-4 shrink-0 text-muted", "aria-hidden": true }) : null, title] }), description ? _jsx("p", { className: "m-0 mt-1 text-[14px] text-muted", children: description }) : null] }), children ? _jsx("div", { className: "flex flex-wrap items-center gap-2", children: children }) : null] }));
|
|
12
12
|
}
|
|
@@ -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;AAS/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAYnE,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;IAE3B;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;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,EACjC,MAAM,EACP,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,
|
|
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;AAS/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAYnE,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;IAE3B;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;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,EACjC,MAAM,EACP,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA2SrB"}
|
|
@@ -11,6 +11,7 @@ import { Input } from '../forms/index.js';
|
|
|
11
11
|
* host styling in HarborClient `styles.css`.
|
|
12
12
|
*/
|
|
13
13
|
export function VariableInput({ value, onChange, variables, placeholder, onKeyDown, className = '', wrapperClassName, onEditVariable, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, source }) {
|
|
14
|
+
const safeValue = value ?? '';
|
|
14
15
|
const inputRef = useRef(null);
|
|
15
16
|
const backdropRef = useRef(null);
|
|
16
17
|
const spanRefs = useRef(new Map());
|
|
@@ -19,14 +20,14 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
19
20
|
const tooltipId = useId();
|
|
20
21
|
const { open: autocompleteOpen, items: autocompleteItems, activeIndex: autocompleteActiveIndex, listboxId, onFocus: openAutocomplete, onBlur: closeAutocomplete, onInputKeyDown, selectItem, setActiveIndex, closeSuggestions } = useAutocomplete({
|
|
21
22
|
source,
|
|
22
|
-
value,
|
|
23
|
+
value: safeValue,
|
|
23
24
|
onSelect: onChange,
|
|
24
25
|
anchorRef: inputRef
|
|
25
26
|
});
|
|
26
27
|
/**
|
|
27
28
|
* Splits the input value into plain text and {{variable}} token spans for highlighting.
|
|
28
29
|
*/
|
|
29
|
-
const tokens = useMemo(() => tokenizeVariables(
|
|
30
|
+
const tokens = useMemo(() => tokenizeVariables(safeValue), [safeValue]);
|
|
30
31
|
/**
|
|
31
32
|
* Clears any pending tooltip hide timer.
|
|
32
33
|
*/
|
|
@@ -81,7 +82,7 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
81
82
|
if (!input)
|
|
82
83
|
return;
|
|
83
84
|
const offset = input.selectionStart ?? 0;
|
|
84
|
-
const match = getVariableTokenAtOffset(
|
|
85
|
+
const match = getVariableTokenAtOffset(safeValue, offset);
|
|
85
86
|
if (!match) {
|
|
86
87
|
setTooltip(null);
|
|
87
88
|
return;
|
|
@@ -160,14 +161,14 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
160
161
|
}
|
|
161
162
|
};
|
|
162
163
|
const tooltipContent = tooltip ? getVariableTooltipContent(tooltip.key, variables) : null;
|
|
163
|
-
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:
|
|
164
|
+
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: safeValue ? (tokens.map((token, index) => token.key ? (_jsx("span", { ref: (el) => {
|
|
164
165
|
if (el)
|
|
165
166
|
spanRefs.current.set(index, el);
|
|
166
167
|
else
|
|
167
168
|
spanRefs.current.delete(index);
|
|
168
169
|
}, 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", role: source ? 'combobox' : undefined, "aria-autocomplete": source ? 'list' : undefined, "aria-expanded": source ? autocompleteOpen : undefined, "aria-controls": source && autocompleteOpen ? listboxId : undefined, "aria-activedescendant": source && autocompleteOpen && autocompleteActiveIndex >= 0
|
|
169
170
|
? `${listboxId}-option-${autocompleteActiveIndex}`
|
|
170
|
-
: undefined, "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:
|
|
171
|
+
: undefined, "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: safeValue, onChange: (e) => {
|
|
171
172
|
onChange(e.target.value);
|
|
172
173
|
queueMicrotask(updateTooltipFromCaret);
|
|
173
174
|
}, onFocus: () => {
|