@harborclient/sdk 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +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,CAgErB"}
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: value, onChange: (event) => onChange(event.target.value), onFocus: (event) => {
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;AAuBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,KAAK,EACL,QAAQ,EACR,SAAS,EAAE,iBAAiB,EAC7B,EAAE,sBAAsB,GAAG,qBAAqB,CAgMhD"}
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 filterItems(items, value) {
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.list().then((list) => {
50
- cacheRef.current = list;
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 list;
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
- setItems([]);
94
+ if (mountedRef.current) {
95
+ setItems([]);
96
+ }
63
97
  return;
64
98
  }
65
- setItems(filterItems(cacheRef.current, value));
66
- }, [source, value]);
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 = value.trim();
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, value]);
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
- const filtered = filterItems(cacheRef.current, value);
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, value]);
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, value]);
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,CA6FrB"}
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
- 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) => (_jsxs("tr", { children: [_jsx(TableCell, { className: "w-6 p-1 text-center", children: _jsx(Checkbox, { className: "app-no-drag", checked: row.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: row.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: row.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))) })] }));
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
  }
@@ -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,CA0SrB"}
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(value), [value]);
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(value, offset);
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: value ? (tokens.map((token, index) => token.key ? (_jsx("span", { ref: (el) => {
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: value, onChange: (e) => {
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: () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harborclient/sdk",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "TypeScript SDK for HarborClient plugin development.",
5
5
  "keywords": [
6
6
  "harborclient",