@bsuite/ui 1.3.2 → 1.3.4-next.0

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":"entity-selector.d.ts","sourceRoot":"","sources":["../src/entity-selector.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity-selector.d.ts","sourceRoot":"","sources":["../src/entity-selector.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAEpE,OAAO,KAAK,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAoBhE;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;AAE5E;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;IAClC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CACpC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,4BAA4B;IAE3C,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAA;IAC5B,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK;QAE9B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAA;KAC7B,CAAA;CACF;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpE;;;;;OAKG;IACH,cAAc,EAAE,4BAA4B,CAAA;IAC5C,4EAA4E;IAC5E,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;;OAQG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,wDAAwD;IACxD,YAAY,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAA;IAChC,gDAAgD;IAChD,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAA;IACnC,kCAAkC;IAClC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,mBAAmB,CAAA;IAC9D,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qBAAqB;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iFAAiF;IACjF,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,mCAAmC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IAC3E;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE;QAAE,UAAU,EAAE,OAAO,CAAA;KAAE,KAAK,SAAS,CAAA;IAChE,0CAA0C;IAC1C,EAAE,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAA;IAC7C,kCAAkC;IAClC,IAAI,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;IACjD,oCAAoC;IACpC,kBAAkB,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC,kBAAkB,CAAC,CAAA;IAC3E,gCAAgC;IAChC,cAAc,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,CAAA;IACnE,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,CAAA;IACzE;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AA8CD,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAChE,cAAc,EACd,MAAgB,EAChB,KAAK,EACL,MAAM,EACN,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,aAAa,EACb,aAAmB,EACnB,QAAQ,EACR,WAAyB,EACzB,QAAgB,EAChB,UAAU,EACV,aAA4B,EAC5B,QAAY,EACZ,UAAgB,EAChB,KAAU,EACV,OAAsB,EACtB,cAAsB,EACtB,SAAgB,EAChB,SAAS,EACT,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,EAAE,EACF,IAAI,EACJ,kBAAkB,EAAE,eAAe,EACnC,cAAc,EAAE,WAAW,EAC3B,iBAAiB,EAAE,cAAc,EACjC,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,YAAY,EACZ,cAAiC,GAClC,EAAE,mBAAmB,CAAC,CAAC,CAAC,+BAqgBxB;AAED,eAAe,cAAc,CAAA"}
@@ -8,6 +8,45 @@ import { Popover, PopoverContent, PopoverTrigger } from './popover.js';
8
8
  import { StatusBadge } from './status-badge.js';
9
9
  import { cn } from './utils.js';
10
10
  // ============================================================================
11
+ // SEARCH WORDS
12
+ // ============================================================================
13
+ /**
14
+ * What separates one search word from the next.
15
+ *
16
+ * PostgREST ends an unquoted `or` value at `,` or `)`, and a backslash escapes
17
+ * nothing outside double quotes. So an escaped `\)` reaches Postgres as a bare
18
+ * `\` at the end of the pattern ("LIKE pattern must not end with escape
19
+ * character"), and a search for "Acme Pty Ltd (WA)" showed that raw error. These
20
+ * characters cannot be escaped in an unquoted value, so they separate words,
21
+ * the same rule crm7's `escapeSearchTerm` follows. The LIKE wildcards and escape
22
+ * a person types by accident (`%` `_` `*` `\`) separate words for the same
23
+ * reason: they are never meant as patterns here.
24
+ */
25
+ const SEARCH_WORD_SEPARATORS = ',()"%_*\\';
26
+ /** A pasted paragraph would otherwise add one `or` parameter per word, times the columns. */
27
+ const MAX_SEARCH_WORDS = 8;
28
+ /** The words of a search, each of which must match some column. Empty when there is nothing to match. */
29
+ function searchWords(term) {
30
+ const words = [];
31
+ let word = '';
32
+ for (const character of term) {
33
+ if (character.trim() === '' || SEARCH_WORD_SEPARATORS.includes(character)) {
34
+ if (word) {
35
+ words.push(word);
36
+ if (words.length === MAX_SEARCH_WORDS)
37
+ return words;
38
+ word = '';
39
+ }
40
+ }
41
+ else {
42
+ word += character;
43
+ }
44
+ }
45
+ if (word)
46
+ words.push(word);
47
+ return words;
48
+ }
49
+ // ============================================================================
11
50
  // COMPONENT
12
51
  // ============================================================================
13
52
  export function EntitySelector({ supabaseClient, logger = console, table, schema, value, onSelect, displayField, secondaryField, searchColumns, selectColumns = '*', filterFn, placeholder = 'Search...', disabled = false, onQuickAdd, quickAddLabel = 'Create new', minChars = 0, debounceMs = 250, limit = 50, orderBy = 'created_at', orderAscending = false, clearable = true, className, selectedLabel, onQueryStateChange, renderRow, id, name, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, 'aria-labelledby': ariaLabelledBy, resolveValueTable, resolveValueSchema, resolveValueSelectColumns, suggestedIds, suggestedLabel = 'Already linked', }) {
@@ -29,6 +68,10 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
29
68
  const [error, setError] = useState(null);
30
69
  const debounceRef = useRef(null);
31
70
  const hasLoadedInitial = useRef(false);
71
+ // Search requests can finish out of order. Increment on every query change,
72
+ // including the debounce interval, so an earlier result cannot repaint the
73
+ // options for the text currently shown in the input.
74
+ const searchGeneration = useRef(0);
32
75
  /**
33
76
  * Returns a schema-scoped `.from(table)` builder. Defaults to the public
34
77
  * schema (identical to the pre-existing `supabaseClient.from(table)`
@@ -97,6 +140,7 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
97
140
  }, [value, resolveValueTable, resolveTableName, resolveSelectColumns, fromResolveTable]); // eslint-disable-line react-hooks/exhaustive-deps
98
141
  // ── Search / load items ────────────────────────────────────────────────
99
142
  const fetchItems = useCallback(async (searchTerm) => {
143
+ const generation = ++searchGeneration.current;
100
144
  setIsLoading(true);
101
145
  setError(null);
102
146
  try {
@@ -121,17 +165,16 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
121
165
  if (filterFn) {
122
166
  q = filterFn(q);
123
167
  }
124
- // Apply search if term is long enough
168
+ // Apply search if term is long enough.
169
+ // EVERY WORD must match SOME column. One `.or()` per word: PostgREST
170
+ // ANDs separate `or` params. Matching the whole phrase against each
171
+ // column alone found "Nguyen" but not "Steve Nguyen", because no
172
+ // single column holds a full name (crm7#1127 F12). Commas separate
173
+ // words too, so "Nguyen, Steve" works.
125
174
  if (searchTerm.length >= Math.max(minChars, 1)) {
126
- const escaped = searchTerm
127
- .replace(/\\/g, '\\\\')
128
- .replace(/"/g, '\\"')
129
- .replace(/,/g, '\\,')
130
- .replace(/\(/g, '\\(')
131
- .replace(/\)/g, '\\)');
132
- const term = `%${escaped}%`;
133
- const orConditions = searchColumns.map((col) => `${col}.ilike.${term}`).join(',');
134
- q = q.or(orConditions);
175
+ for (const word of searchWords(searchTerm)) {
176
+ q = q.or(searchColumns.map((col) => `${col}.ilike.%${word}%`).join(','));
177
+ }
135
178
  }
136
179
  // ORDER BEFORE LIMIT. Without this the picker returns an arbitrary
137
180
  // `limit`-sized slice of the matches, so a freshly created row is
@@ -158,6 +201,8 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
158
201
  ({ data, error: queryError } = await build(false));
159
202
  }
160
203
  }
204
+ if (generation !== searchGeneration.current)
205
+ return;
161
206
  if (queryError) {
162
207
  logger.warn(`EntitySelector(${table}): search error`, queryError.message);
163
208
  setError(queryError.message || 'Failed to load results.');
@@ -167,12 +212,15 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
167
212
  setItems(data ?? []);
168
213
  }
169
214
  catch (err) {
215
+ if (generation !== searchGeneration.current)
216
+ return;
170
217
  logger.error(`EntitySelector(${table}): unexpected error`, err);
171
218
  setError(err instanceof Error ? err.message : 'Failed to load results.');
172
219
  setItems([]);
173
220
  }
174
221
  finally {
175
- setIsLoading(false);
222
+ if (generation === searchGeneration.current)
223
+ setIsLoading(false);
176
224
  }
177
225
  }, [table, selectColumns, searchColumns, filterFn, minChars, limit, fromTable, logger]);
178
226
  /**
@@ -244,7 +292,11 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
244
292
  }, [open, fetchItems]);
245
293
  // ── Debounced search on query change ───────────────────────────────────
246
294
  const handleQueryChange = useCallback((val) => {
295
+ ++searchGeneration.current;
247
296
  setQuery(val);
297
+ setItems([]);
298
+ setError(null);
299
+ setIsLoading(true);
248
300
  if (debounceRef.current)
249
301
  clearTimeout(debounceRef.current);
250
302
  debounceRef.current = setTimeout(() => fetchItems(val), debounceMs);
@@ -300,8 +352,21 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
300
352
  if (!result || typeof result.then !== 'function') {
301
353
  setSelectedItem(item);
302
354
  }
303
- setOpen(false);
304
- setQuery('');
355
+ handleOpenChange(false);
356
+ };
357
+ const handleOpenChange = (nextOpen) => {
358
+ if (!nextOpen) {
359
+ ++searchGeneration.current;
360
+ if (debounceRef.current)
361
+ clearTimeout(debounceRef.current);
362
+ debounceRef.current = null;
363
+ hasLoadedInitial.current = false;
364
+ setQuery('');
365
+ setItems([]);
366
+ setError(null);
367
+ setIsLoading(false);
368
+ }
369
+ setOpen(nextOpen);
305
370
  };
306
371
  const handleClear = (e) => {
307
372
  e.stopPropagation();
@@ -310,7 +375,7 @@ export function EntitySelector({ supabaseClient, logger = console, table, schema
310
375
  };
311
376
  // ── Render ─────────────────────────────────────────────────────────────
312
377
  const triggerLabel = selectedItem ? (selectedLabel ?? displayField(selectedItem)) : placeholder;
313
- return (_jsxs("div", { className: "flex gap-1.5", children: [_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", role: "combobox", "aria-expanded": open, id: id, name: name, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-labelledby": ariaLabelledBy, disabled: disabled, className: cn('w-full justify-between font-normal', !selectedItem && 'text-muted-foreground', className), children: [_jsx("span", { className: "truncate min-w-0", children: triggerLabel }), _jsxs("div", { className: "flex items-center gap-1 ml-2 shrink-0", children: [clearable && selectedItem && (_jsx(X, { className: "h-3.5 w-3.5 opacity-50 hover:opacity-100", onClick: handleClear })), _jsx(ChevronsUpDown, { className: "h-3.5 w-3.5 opacity-50" })] })] }) }), _jsx(PopoverContent, { className: "w-[--radix-popover-trigger-width] p-0", align: "start", children: _jsxs(Command, { shouldFilter: false, children: [_jsx(CommandInput, { placeholder: placeholder, value: query, onValueChange: handleQueryChange }), _jsxs(CommandList, { children: [isLoading ? (_jsxs("div", { className: "flex items-center justify-center py-6", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }), _jsx("span", { className: "ml-2 text-sm text-muted-foreground", children: "Searching..." })] })) : error ? (_jsxs("div", { className: "text-center py-6 px-4 text-sm text-error-text", role: "alert", children: ["Couldn't load results: ", error] })) : items.length === 0 && suggestedItems.length === 0 ? (_jsx(CommandEmpty, { children: query.length > 0 ? `No results for "${query}"` : 'No items found' })) : (_jsxs(_Fragment, { children: [suggestedItems.length > 0 && query.length === 0 && (_jsx(CommandGroup, { heading: suggestedLabel, children: suggestedItems.map((item) => renderItem(item)) })), (() => {
378
+ return (_jsxs("div", { className: "flex gap-1.5", children: [_jsxs(Popover, { open: open, onOpenChange: handleOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", role: "combobox", "aria-expanded": open, id: id, name: name, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-labelledby": ariaLabelledBy, disabled: disabled, className: cn('w-full justify-between font-normal', !selectedItem && 'text-muted-foreground', className), children: [_jsx("span", { className: "truncate min-w-0", children: triggerLabel }), _jsxs("div", { className: "flex items-center gap-1 ml-2 shrink-0", children: [clearable && selectedItem && (_jsx(X, { className: "h-3.5 w-3.5 opacity-50 hover:opacity-100", onClick: handleClear })), _jsx(ChevronsUpDown, { className: "h-3.5 w-3.5 opacity-50" })] })] }) }), _jsx(PopoverContent, { className: "w-[--radix-popover-trigger-width] p-0", align: "start", children: _jsxs(Command, { shouldFilter: false, children: [_jsx(CommandInput, { placeholder: placeholder, value: query, onValueChange: handleQueryChange }), _jsxs(CommandList, { children: [isLoading ? (_jsxs("div", { className: "flex items-center justify-center py-6", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }), _jsx("span", { className: "ml-2 text-sm text-muted-foreground", children: "Searching..." })] })) : error ? (_jsxs("div", { className: "text-center py-6 px-4 text-sm text-error-text", role: "alert", children: ["Couldn't load results: ", error] })) : items.length === 0 && (query.length > 0 || suggestedItems.length === 0) ? (_jsx(CommandEmpty, { children: query.length > 0 ? `No results for "${query}"` : 'No items found' })) : (_jsxs(_Fragment, { children: [suggestedItems.length > 0 && query.length === 0 && (_jsx(CommandGroup, { heading: suggestedLabel, children: suggestedItems.map((item) => renderItem(item)) })), (() => {
314
379
  // De-dupe so a suggested row is not offered twice; showing
315
380
  // the same entity in both groups reads as two different
316
381
  // records.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsuite/ui",
3
- "version": "1.3.2",
3
+ "version": "1.3.4-next.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {