@stackone/malachite 2.42.2 → 2.43.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,18 +1,18 @@
1
1
  import { jsxs as E, jsx as d } from "react/jsx-runtime";
2
- import { useId as xt, useState as T, useRef as g, useCallback as F, useLayoutEffect as k, useEffect as bt, useInsertionEffect as wt } from "react";
3
- import { createPortal as vt } from "react-dom";
4
- import yt from "./Spinner.js";
2
+ import { useId as bt, useState as T, useRef as x, useCallback as F, useLayoutEffect as k, useEffect as wt, useInsertionEffect as vt } from "react";
3
+ import { createPortal as yt } from "react-dom";
4
+ import St from "./Spinner.js";
5
5
  import { CONTROL_TEXT_SIZE as _, CONTROL_TEXT_SIZE_CLASS as rt } from "./controlTextSize.js";
6
- import { pushEscapeLayer as St } from "./escapeLayers.js";
7
- import { ensureMultiSelectPanelStyles as Et } from "./optionRowStyles.js";
8
- import { useOwnedByPopover as Tt } from "./popoverTree.js";
9
- import { overlayIdentity as kt } from "./portalOverlay.js";
10
- import { computeCaretMenuGeometry as Mt } from "./queryInputGeometry.js";
11
- const Lt = {
6
+ import { pushEscapeLayer as Et } from "./escapeLayers.js";
7
+ import { ensureMultiSelectPanelStyles as Tt } from "./optionRowStyles.js";
8
+ import { usePopupLayer as kt } from "./overlayLayers.js";
9
+ import { useOwnedByPopover as Mt } from "./popoverTree.js";
10
+ import { computeCaretMenuGeometry as Lt } from "./queryInputGeometry.js";
11
+ const It = {
12
12
  small: "h-[var(--malachite-control-height-small,28px)] px-[10px] rounded-[var(--malachite-radius-10,10px)]",
13
13
  medium: "h-[var(--malachite-control-height-medium,32px)] px-[10px] rounded-xl",
14
14
  large: "h-[var(--malachite-control-height-large,42px)] px-[12px] rounded-[var(--malachite-radius-xxl,14px)]"
15
- }, It = {
15
+ }, Rt = {
16
16
  key: "var(--malachite-codeblock-keyword, rgb(12, 88, 174))",
17
17
  value: "var(--malachite-codeblock-string, rgb(2, 122, 66))",
18
18
  operator: "var(--malachite-codeblock-type, rgb(12, 88, 174))",
@@ -20,7 +20,7 @@ const Lt = {
20
20
  punctuation: "var(--malachite-codeblock-foreground, rgb(10, 10, 10))",
21
21
  error: "var(--malachite-text-error, rgb(237, 53, 54))",
22
22
  plain: "var(--malachite-text-primary, rgb(10, 10, 10))"
23
- }, Rt = {
23
+ }, Dt = {
24
24
  small: {
25
25
  fontFamily: "inherit",
26
26
  fontSize: _.small,
@@ -53,9 +53,9 @@ const Lt = {
53
53
  padding: "10px 12px",
54
54
  color: "var(--malachite-text-dimmed, rgb(161, 161, 161))",
55
55
  whiteSpace: "nowrap"
56
- }, Dt = (r) => {
56
+ }, _t = (r) => {
57
57
  r.preventDefault();
58
- }, _t = (r, f) => {
58
+ }, Ht = (r, f) => {
59
59
  const l = [...f.filter(
60
60
  (n) => n.start >= 0 && n.end <= r.length && n.start < n.end
61
61
  )].sort((n, M) => n.start - M.start), s = [];
@@ -63,10 +63,10 @@ const Lt = {
63
63
  for (const n of l)
64
64
  n.start < u || (s.push(n), u = n.end);
65
65
  return s;
66
- }, Ht = (r, f) => {
66
+ }, Wt = (r, f) => {
67
67
  const h = [];
68
68
  let l = 0;
69
- for (const s of _t(r, f))
69
+ for (const s of Ht(r, f))
70
70
  s.start > l && h.push({ start: l, end: s.start, kind: "plain" }), h.push(s), l = s.end;
71
71
  return l < r.length && h.push({ start: l, end: r.length, kind: "plain" }), h;
72
72
  }, Zt = ({
@@ -79,7 +79,7 @@ const Lt = {
79
79
  onQueryContextChange: n,
80
80
  onSubmit: M,
81
81
  placeholder: K,
82
- size: x = "large",
82
+ size: b = "large",
83
83
  disabled: L = !1,
84
84
  loading: I = !1,
85
85
  emptyMessage: H,
@@ -88,9 +88,9 @@ const Lt = {
88
88
  "aria-label": lt,
89
89
  "aria-labelledby": U
90
90
  }) => {
91
- const W = `${xt()}-listbox`, j = (t) => `${W}-option-${t}`, [O, Y] = T(!1), [Z, N] = T(() => r.length), [st, $] = T(0), [ct, A] = T(!1), [i, C] = T(null), b = g(null), G = g(null), P = g(null), w = g(null), V = g([]), z = g(null), dt = Array.isArray(l), v = l ?? [], y = v.length > 0, a = O && (I || y || dt && !y && !!H) && !ct && !L;
92
- Tt(
93
- F(() => w.current, []),
91
+ const W = `${bt()}-listbox`, j = (t) => `${W}-option-${t}`, [O, P] = T(!1), [Y, N] = T(() => r.length), [st, $] = T(0), [ct, A] = T(!1), [i, Z] = T(null), w = x(null), C = x(null), G = x(null), m = x(null), V = x([]), z = x(null), dt = Array.isArray(l), v = l ?? [], y = v.length > 0, a = O && (I || y || dt && !y && !!H) && !ct && !L, pt = kt("dropdown", a, m);
92
+ Mt(
93
+ F(() => m.current, []),
94
94
  a
95
95
  );
96
96
  const S = a && y && !I, c = y ? Math.min(st, v.length - 1) : -1;
@@ -98,71 +98,71 @@ const Lt = {
98
98
  N((t) => Math.min(t, r.length));
99
99
  }, [r]);
100
100
  const R = F(() => {
101
- const t = b.current, e = G.current;
101
+ const t = w.current, e = C.current;
102
102
  t && e && (e.style.transform = `translateX(${-t.scrollLeft}px)`);
103
103
  }, []);
104
104
  k(() => {
105
105
  R();
106
106
  }, [r, R]);
107
- const m = F(() => {
107
+ const g = F(() => {
108
108
  var o, p, D;
109
- const t = b.current;
109
+ const t = w.current;
110
110
  if (!t)
111
111
  return;
112
- const e = (((o = P.current) == null ? void 0 : o.offsetWidth) ?? 0) - t.scrollLeft;
113
- C(
114
- Mt({
112
+ const e = (((o = G.current) == null ? void 0 : o.offsetWidth) ?? 0) - t.scrollLeft;
113
+ Z(
114
+ Lt({
115
115
  inputRect: t.getBoundingClientRect(),
116
116
  caretOffsetPx: e,
117
- menuWidth: ((p = w.current) == null ? void 0 : p.offsetWidth) ?? B,
118
- menuHeight: ((D = w.current) == null ? void 0 : D.offsetHeight) ?? 0,
117
+ menuWidth: ((p = m.current) == null ? void 0 : p.offsetWidth) ?? B,
118
+ menuHeight: ((D = m.current) == null ? void 0 : D.offsetHeight) ?? 0,
119
119
  viewportWidth: window.innerWidth,
120
120
  viewportHeight: window.innerHeight
121
121
  })
122
122
  );
123
123
  }, []);
124
124
  k(() => {
125
- a ? m() : C(null);
126
- }, [a, r, Z, l, I, m]), bt(() => {
125
+ a ? g() : Z(null);
126
+ }, [a, r, Y, l, I, g]), wt(() => {
127
127
  if (!a)
128
128
  return;
129
- const t = () => m();
129
+ const t = () => g();
130
130
  window.addEventListener("scroll", t, !0), window.addEventListener("resize", t);
131
- const e = St(() => A(!0));
131
+ const e = Et(() => A(!0));
132
132
  return () => {
133
133
  window.removeEventListener("scroll", t, !0), window.removeEventListener("resize", t), e();
134
134
  };
135
- }, [a, m]), k(() => {
135
+ }, [a, g]), k(() => {
136
136
  const t = z.current;
137
137
  if (t === null)
138
138
  return;
139
139
  z.current = null;
140
- const e = b.current;
141
- !e || t.value !== r || (e.setSelectionRange(t.caret, t.caret), N(t.caret), n == null || n({ value: r, caret: t.caret }), R(), m());
140
+ const e = w.current;
141
+ !e || t.value !== r || (e.setSelectionRange(t.caret, t.caret), N(t.caret), n == null || n({ value: r, caret: t.caret }), R(), g());
142
142
  }, [r]), k(() => {
143
143
  if (!S)
144
144
  return;
145
- const t = V.current[c], e = w.current;
145
+ const t = V.current[c], e = m.current;
146
146
  if (!t || !e)
147
147
  return;
148
148
  const o = t.offsetTop + t.offsetHeight;
149
149
  o > e.scrollTop + e.clientHeight ? e.scrollTop = o - e.clientHeight : t.offsetTop < e.scrollTop && (e.scrollTop = t.offsetTop);
150
- }, [c, S]), wt(() => {
151
- a && Et();
150
+ }, [c, S]), vt(() => {
151
+ a && Tt();
152
152
  }, [a]);
153
153
  const q = (t, e) => {
154
154
  N(e), n == null || n({ value: t, caret: e });
155
- }, pt = (t) => {
155
+ }, ht = (t) => {
156
156
  const e = t.target.value;
157
157
  A(!1), $(0), f(e), q(e, t.target.selectionStart ?? e.length);
158
- }, ht = (t) => {
158
+ }, ft = (t) => {
159
159
  const e = t.currentTarget;
160
160
  q(e.value, e.selectionStart ?? e.value.length);
161
161
  }, X = (t) => {
162
162
  var et;
163
- const e = ((et = b.current) == null ? void 0 : et.selectionStart) ?? r.length, o = s ?? { start: e, end: e }, p = Math.max(0, Math.min(o.start, r.length)), D = Math.max(p, Math.min(o.end, r.length)), Q = t.insertText ?? t.label, tt = r.slice(0, p) + Q + r.slice(D);
163
+ const e = ((et = w.current) == null ? void 0 : et.selectionStart) ?? r.length, o = s ?? { start: e, end: e }, p = Math.max(0, Math.min(o.start, r.length)), D = Math.max(p, Math.min(o.end, r.length)), Q = t.insertText ?? t.label, tt = r.slice(0, p) + Q + r.slice(D);
164
164
  z.current = { value: tt, caret: p + Q.length }, f(tt), u == null || u(t);
165
- }, ft = (t) => {
165
+ }, ut = (t) => {
166
166
  if (S && (t.key === "ArrowDown" || t.key === "ArrowUp")) {
167
167
  t.preventDefault();
168
168
  const o = v.length;
@@ -181,10 +181,10 @@ const Lt = {
181
181
  return;
182
182
  }
183
183
  t.key === "Tab" && e && (t.preventDefault(), X(e));
184
- }, ut = Ht(r, h), J = Rt[x], mt = I ? (
184
+ }, mt = Wt(r, h), J = Dt[b], gt = I ? (
185
185
  // `role="listbox"` admits only option/group children.
186
186
  /* @__PURE__ */ E("div", { role: "presentation", style: ot, children: [
187
- /* @__PURE__ */ d(yt, { size: "xxxsmall" }),
187
+ /* @__PURE__ */ d(St, { size: "xxxsmall" }),
188
188
  /* @__PURE__ */ d("span", { children: "Loading…" })
189
189
  ] })
190
190
  ) : y ? v.map((t, e) => {
@@ -247,11 +247,11 @@ const Lt = {
247
247
  },
248
248
  t.id
249
249
  );
250
- }) : H ? /* @__PURE__ */ d("div", { role: "presentation", style: ot, children: H }) : null, gt = L ? "" : "hover:border-[var(--malachite-text-placeholder)]";
250
+ }) : H ? /* @__PURE__ */ d("div", { role: "presentation", style: ot, children: H }) : null, xt = L ? "" : "hover:border-[var(--malachite-text-placeholder)]";
251
251
  return /* @__PURE__ */ E(
252
252
  "div",
253
253
  {
254
- className: `relative w-full ${Lt[x]} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${gt} transition-colors flex items-center ${L ? "opacity-60 cursor-not-allowed" : ""} ${it}`,
254
+ className: `relative w-full ${It[b]} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${xt} transition-colors flex items-center ${L ? "opacity-60 cursor-not-allowed" : ""} ${it}`,
255
255
  style: {
256
256
  boxShadow: O ? "var(--malachite-shadow-accent, 0px 1px 0px 0px rgba(0, 175, 102, 0.15))" : "0px 1px 1px 0px rgba(0, 0, 0, 0.15)",
257
257
  borderColor: O ? "var(--malachite-control-border-hover, rgba(0, 175, 102, 0.4))" : void 0,
@@ -262,16 +262,16 @@ const Lt = {
262
262
  /* @__PURE__ */ E(
263
263
  "div",
264
264
  {
265
- ref: G,
265
+ ref: C,
266
266
  "aria-hidden": "true",
267
- className: `absolute inset-0 flex items-center pointer-events-none ${rt[x]}`,
267
+ className: `absolute inset-0 flex items-center pointer-events-none ${rt[b]}`,
268
268
  style: { ...J, whiteSpace: "pre" },
269
269
  children: [
270
- ut.map((t) => /* @__PURE__ */ d(
270
+ mt.map((t) => /* @__PURE__ */ d(
271
271
  "span",
272
272
  {
273
273
  style: {
274
- color: It[t.kind],
274
+ color: Rt[t.kind],
275
275
  ...t.kind === "error" ? { textDecoration: "underline wavy" } : {}
276
276
  },
277
277
  children: r.slice(t.start, t.end)
@@ -281,9 +281,9 @@ const Lt = {
281
281
  /* @__PURE__ */ d(
282
282
  "span",
283
283
  {
284
- ref: P,
284
+ ref: G,
285
285
  style: { position: "absolute", top: 0, left: 0, visibility: "hidden" },
286
- children: r.slice(0, Z)
286
+ children: r.slice(0, Y)
287
287
  }
288
288
  )
289
289
  ]
@@ -292,7 +292,7 @@ const Lt = {
292
292
  /* @__PURE__ */ d(
293
293
  "input",
294
294
  {
295
- ref: b,
295
+ ref: w,
296
296
  type: "text",
297
297
  role: "combobox",
298
298
  "aria-expanded": a,
@@ -304,19 +304,19 @@ const Lt = {
304
304
  "aria-labelledby": U,
305
305
  "aria-label": U ? void 0 : lt ?? K,
306
306
  value: r,
307
- onChange: pt,
308
- onSelect: ht,
307
+ onChange: ht,
308
+ onSelect: ft,
309
309
  onScroll: () => {
310
- R(), a && m();
310
+ R(), a && g();
311
311
  },
312
- onKeyDown: ft,
312
+ onKeyDown: ut,
313
313
  onFocus: () => {
314
- Y(!0), A(!1);
314
+ P(!0), A(!1);
315
315
  },
316
- onBlur: () => Y(!1),
316
+ onBlur: () => P(!1),
317
317
  placeholder: K,
318
318
  disabled: L,
319
- className: `absolute inset-0 w-full h-full bg-transparent border-none outline-none ${rt[x]} placeholder:text-[var(--malachite-text-placeholder)] placeholder:font-medium disabled:cursor-not-allowed`,
319
+ className: `absolute inset-0 w-full h-full bg-transparent border-none outline-none ${rt[b]} placeholder:text-[var(--malachite-text-placeholder)] placeholder:font-medium disabled:cursor-not-allowed`,
320
320
  style: {
321
321
  ...J,
322
322
  color: "transparent",
@@ -325,15 +325,15 @@ const Lt = {
325
325
  }
326
326
  )
327
327
  ] }),
328
- a && vt(
328
+ a && yt(
329
329
  /* @__PURE__ */ d(
330
330
  "div",
331
331
  {
332
- ref: w,
332
+ ref: m,
333
333
  id: W,
334
334
  role: "listbox",
335
- ...kt("dropdown"),
336
- onMouseDown: Dt,
335
+ ...pt,
336
+ onMouseDown: _t,
337
337
  style: {
338
338
  position: "fixed",
339
339
  top: (i == null ? void 0 : i.top) ?? 0,
@@ -356,11 +356,11 @@ const Lt = {
356
356
  gap: 2,
357
357
  maxHeight: 320,
358
358
  overflowY: "auto",
359
- fontSize: _[x],
359
+ fontSize: _[b],
360
360
  visibility: i ? "visible" : "hidden",
361
361
  opacity: i ? 1 : 0
362
362
  },
363
- children: mt
363
+ children: gt
364
364
  }
365
365
  ),
366
366
  document.body
@@ -1 +1 @@
1
- {"version":3,"file":"QueryInput.js","sources":["../../src/components/QueryInput.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useId,\n useInsertionEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport Spinner from './Spinner';\nimport { CONTROL_TEXT_SIZE, CONTROL_TEXT_SIZE_CLASS } from './controlTextSize';\nimport { pushEscapeLayer } from './escapeLayers';\nimport { ensureMultiSelectPanelStyles } from './optionRowStyles';\nimport { useOwnedByPopover } from './popoverTree';\nimport { overlayIdentity } from './portalOverlay';\nimport { type CaretMenuGeometry, computeCaretMenuGeometry } from './queryInputGeometry';\n\nexport type QueryTokenKind =\n | 'key'\n | 'value'\n | 'operator'\n | 'negation'\n | 'punctuation'\n | 'error'\n | 'plain';\n\nexport interface QueryToken {\n /** Index into `value`. */\n start: number;\n /** Exclusive. */\n end: number;\n kind: QueryTokenKind;\n}\n\nexport interface QuerySuggestion {\n id: string;\n label: string;\n /** Secondary text, right-aligned in the row. */\n detail?: string;\n /** What replaces `activeRange` when accepted. Defaults to `label`. */\n insertText?: string;\n}\n\nexport type QueryInputSize = 'small' | 'medium' | 'large';\n\nexport interface QueryInputProps {\n value: string;\n onChange: (value: string) => void;\n /** Ranges to colour. Tokens that are out of range, reversed or overlapping are ignored. */\n tokens?: QueryToken[];\n /** Rows for the caret menu. Empty or undefined means the menu stays closed. */\n suggestions?: QuerySuggestion[];\n /** The span an accepted suggestion replaces. Defaults to a zero-width span at the caret. */\n activeRange?: { start: number; end: number };\n onAccept?: (suggestion: QuerySuggestion) => void;\n /** Every value change AND every caret move, so the consumer can recompute tokens/suggestions. */\n onQueryContextChange?: (context: { value: string; caret: number }) => void;\n /** Enter with nothing highlighted in the menu. */\n onSubmit?: (value: string) => void;\n placeholder?: string;\n size?: QueryInputSize;\n disabled?: boolean;\n /** Suggestions are being fetched: the menu shows a loading row instead of the empty message. */\n loading?: boolean;\n emptyMessage?: string;\n className?: string;\n style?: React.CSSProperties;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n}\n\n// Same 28/32/42 ladder as Search, minus the icon gap this component has no icon to space.\nconst SIZE_CLASSES: Record<QueryInputSize, string> = {\n small: 'h-[var(--malachite-control-height-small,28px)] px-[10px] rounded-[var(--malachite-radius-10,10px)]',\n medium: 'h-[var(--malachite-control-height-medium,32px)] px-[10px] rounded-xl',\n large: 'h-[var(--malachite-control-height-large,42px)] px-[12px] rounded-[var(--malachite-radius-xxl,14px)]',\n};\n\nconst TOKEN_COLORS: Record<QueryTokenKind, string> = {\n key: 'var(--malachite-codeblock-keyword, rgb(12, 88, 174))',\n value: 'var(--malachite-codeblock-string, rgb(2, 122, 66))',\n operator: 'var(--malachite-codeblock-type, rgb(12, 88, 174))',\n negation: 'var(--malachite-text-destructive, rgb(237, 53, 54))',\n punctuation: 'var(--malachite-codeblock-foreground, rgb(10, 10, 10))',\n error: 'var(--malachite-text-error, rgb(237, 53, 54))',\n plain: 'var(--malachite-text-primary, rgb(10, 10, 10))',\n};\n\n// The one place the mirror and the input agree on typography: diverge here and the coloured\n// text drifts out from under the caret it's supposed to sit beneath.\nconst TEXT_STYLE: Record<QueryInputSize, React.CSSProperties> = {\n small: {\n fontFamily: 'inherit',\n fontSize: CONTROL_TEXT_SIZE.small,\n fontWeight: 500,\n letterSpacing: 'normal',\n lineHeight: 'normal',\n padding: 0,\n },\n medium: {\n fontFamily: 'inherit',\n fontSize: CONTROL_TEXT_SIZE.medium,\n fontWeight: 500,\n letterSpacing: 'normal',\n lineHeight: 'normal',\n padding: 0,\n },\n large: {\n fontFamily: 'inherit',\n fontSize: CONTROL_TEXT_SIZE.large,\n fontWeight: 500,\n letterSpacing: 'normal',\n lineHeight: 'normal',\n padding: 0,\n },\n};\n\n/** Before layout the portal hasn't painted yet, so the first geometry pass needs a width. */\nconst MENU_FALLBACK_WIDTH_PX = 220;\n\nconst MENU_MAX_WIDTH_PX = 360;\n\nconst MENU_MESSAGE_STYLE: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n padding: '10px 12px',\n color: 'var(--malachite-text-dimmed, rgb(161, 161, 161))',\n whiteSpace: 'nowrap',\n};\n\n/** Keeps focus (and the caret) on the input so a row click never blurs it first. */\nconst handleMenuMouseDown = (event: React.MouseEvent) => {\n event.preventDefault();\n};\n\ninterface QuerySegment {\n start: number;\n end: number;\n kind: QueryTokenKind;\n}\n\n/** Drops tokens the consumer got wrong rather than letting them corrupt the render. */\nconst sanitizeTokens = (value: string, tokens: QueryToken[]): QueryToken[] => {\n const inRange = tokens.filter(\n (token) => token.start >= 0 && token.end <= value.length && token.start < token.end,\n );\n const sorted = [...inRange].sort((a, b) => a.start - b.start);\n const accepted: QueryToken[] = [];\n let cursor = 0;\n for (const token of sorted) {\n if (token.start < cursor) {\n continue;\n }\n accepted.push(token);\n cursor = token.end;\n }\n return accepted;\n};\n\n/** Fills the gaps between tokens with 'plain' segments so the mirror's text always equals `value`. */\nconst buildSegments = (value: string, tokens: QueryToken[]): QuerySegment[] => {\n const segments: QuerySegment[] = [];\n let cursor = 0;\n for (const token of sanitizeTokens(value, tokens)) {\n if (token.start > cursor) {\n segments.push({ start: cursor, end: token.start, kind: 'plain' });\n }\n segments.push(token);\n cursor = token.end;\n }\n if (cursor < value.length) {\n segments.push({ start: cursor, end: value.length, kind: 'plain' });\n }\n return segments;\n};\n\nconst QueryInput: React.FC<QueryInputProps> = ({\n value,\n onChange,\n tokens = [],\n suggestions,\n activeRange,\n onAccept,\n onQueryContextChange,\n onSubmit,\n placeholder,\n size = 'large',\n disabled = false,\n loading = false,\n emptyMessage,\n className = '',\n style,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n}) => {\n const instanceId = useId();\n const listboxId = `${instanceId}-listbox`;\n const getRowId = (index: number) => `${listboxId}-option-${index}`;\n\n const [focused, setFocused] = useState(false);\n const [caret, setCaret] = useState(() => value.length);\n const [rawHighlightedIndex, setHighlightedIndex] = useState(0);\n // Escape closes the menu whatever `suggestions` and `loading` say. Lifted by an edit or a\n // refocus, so navigating a dismissed query does not pop it back open.\n const [dismissed, setDismissed] = useState(false);\n const [menuPosition, setMenuPosition] = useState<CaretMenuGeometry | null>(null);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const mirrorRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLSpanElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const rowRefs = useRef<(HTMLButtonElement | null)[]>([]);\n // Caret restore after an accepted suggestion. Carries the string it was computed against:\n // nothing obliges a controlled consumer to apply the value handed to `onChange`, and a\n // caret from a rejected or rewritten suggestion must not land on a later unrelated edit.\n const pendingSelectionRef = useRef<{ value: string; caret: number } | null>(null);\n\n const suggestionsProvided = Array.isArray(suggestions);\n const suggestionList = suggestions ?? [];\n const hasSuggestions = suggestionList.length > 0;\n const isEmptyList = suggestionsProvided && !hasSuggestions;\n const wantsMenu = loading || hasSuggestions || (isEmptyList && !!emptyMessage);\n const isMenuOpen = focused && wantsMenu && !dismissed && !disabled;\n useOwnedByPopover(\n useCallback(() => menuRef.current, []),\n isMenuOpen,\n );\n // `loading` paints the spinner over any rows the consumer is still holding, so nothing is\n // on screen to arrow through, accept, or point `aria-activedescendant` at.\n const rowsVisible = isMenuOpen && hasSuggestions && !loading;\n\n // A consumer's `suggestions` array is commonly rebuilt inline, so its identity changes on\n // any parent re-render and cannot stand in for \"the candidates changed\". Clamping during\n // render keeps the row the user arrowed to, and keeps the highlight inside a shrunken list.\n const highlightedIndex = hasSuggestions\n ? Math.min(rawHighlightedIndex, suggestionList.length - 1)\n : -1;\n\n useLayoutEffect(() => {\n setCaret((current) => Math.min(current, value.length));\n }, [value]);\n\n const syncMirrorScroll = useCallback(() => {\n const input = inputRef.current;\n const mirror = mirrorRef.current;\n if (input && mirror) {\n mirror.style.transform = `translateX(${-input.scrollLeft}px)`;\n }\n }, []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-syncs on every value change, not on value itself\n useLayoutEffect(() => {\n syncMirrorScroll();\n }, [value, syncMirrorScroll]);\n\n const updateMenuPosition = useCallback(() => {\n const input = inputRef.current;\n if (!input) {\n return;\n }\n const caretOffsetPx = (measureRef.current?.offsetWidth ?? 0) - input.scrollLeft;\n setMenuPosition(\n computeCaretMenuGeometry({\n inputRect: input.getBoundingClientRect(),\n caretOffsetPx,\n menuWidth: menuRef.current?.offsetWidth ?? MENU_FALLBACK_WIDTH_PX,\n menuHeight: menuRef.current?.offsetHeight ?? 0,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }),\n );\n }, []);\n\n // Depends on `caret` as well as `value`: the measuring span is sliced at the caret, so an\n // arrow-key move with no edit still changes where the menu belongs.\n // biome-ignore lint/correctness/useExhaustiveDependencies: repositions on every keystroke, not on value itself\n useLayoutEffect(() => {\n if (isMenuOpen) {\n updateMenuPosition();\n } else {\n setMenuPosition(null);\n }\n // `suggestions` and `loading` are here because they change the menu's own size: rows\n // arriving after a fetch resize it while the query and caret sit still, and a stale\n // measurement would leave `openUp` and the caret-aligned `left` computed for the old box.\n }, [isMenuOpen, value, caret, suggestions, loading, updateMenuPosition]);\n\n useEffect(() => {\n if (!isMenuOpen) {\n return undefined;\n }\n const handleScrollOrResize = () => updateMenuPosition();\n window.addEventListener('scroll', handleScrollOrResize, true);\n window.addEventListener('resize', handleScrollOrResize);\n const popEscapeLayer = pushEscapeLayer(() => setDismissed(true));\n return () => {\n window.removeEventListener('scroll', handleScrollOrResize, true);\n window.removeEventListener('resize', handleScrollOrResize);\n popEscapeLayer();\n };\n }, [isMenuOpen, updateMenuPosition]);\n\n // Restore the caret placed by the last accepted suggestion once the new `value` has\n // actually rendered. Guarded on the ref so an externally-driven value change never gets\n // its selection clobbered.\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally re-reads latest closures via pendingSelectionRef's gate\n useLayoutEffect(() => {\n const pending = pendingSelectionRef.current;\n if (pending === null) {\n return;\n }\n pendingSelectionRef.current = null;\n const input = inputRef.current;\n // Dropped unless the consumer applied exactly the string this caret was measured\n // against. A rejected or rewritten suggestion leaves the caret where the user put it.\n if (!input || pending.value !== value) {\n return;\n }\n input.setSelectionRange(pending.caret, pending.caret);\n setCaret(pending.caret);\n onQueryContextChange?.({ value, caret: pending.caret });\n syncMirrorScroll();\n updateMenuPosition();\n }, [value]);\n\n // Scrolled by hand rather than with `scrollIntoView`, which would also scroll the page and\n // carry the field itself out of view. Same reason MultiSelect does it this way.\n useLayoutEffect(() => {\n if (!rowsVisible) {\n return;\n }\n const row = rowRefs.current[highlightedIndex];\n const container = menuRef.current;\n if (!row || !container) {\n return;\n }\n const rowBottom = row.offsetTop + row.offsetHeight;\n if (rowBottom > container.scrollTop + container.clientHeight) {\n container.scrollTop = rowBottom - container.clientHeight;\n } else if (row.offsetTop < container.scrollTop) {\n container.scrollTop = row.offsetTop;\n }\n }, [highlightedIndex, rowsVisible]);\n\n // useInsertionEffect, not layout: the rules have to land before the rows paint.\n useInsertionEffect(() => {\n if (isMenuOpen) {\n ensureMultiSelectPanelStyles();\n }\n }, [isMenuOpen]);\n\n const reportCaret = (nextValue: string, nextCaret: number) => {\n setCaret(nextCaret);\n onQueryContextChange?.({ value: nextValue, caret: nextCaret });\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const next = event.target.value;\n // Editing is the user asking again, so it lifts an earlier Escape. Moving the caret is\n // not, and neither is a new `suggestions` array: that identity changes on any parent\n // re-render, which would undo the dismissal with nothing having been asked.\n setDismissed(false);\n setHighlightedIndex(0);\n onChange(next);\n reportCaret(next, event.target.selectionStart ?? next.length);\n };\n\n const handleSelect = (event: React.SyntheticEvent<HTMLInputElement>) => {\n const target = event.currentTarget;\n reportCaret(target.value, target.selectionStart ?? target.value.length);\n };\n\n const acceptSuggestion = (suggestion: QuerySuggestion) => {\n const fallbackCaret = inputRef.current?.selectionStart ?? value.length;\n const range = activeRange ?? { start: fallbackCaret, end: fallbackCaret };\n // Clamped for the same reason `sanitizeTokens` drops a bad range, except this one\n // writes: `slice` reads a negative index from the end of the string, so an unchecked\n // `start` of -1 silently rewrites the tail instead of doing nothing.\n const start = Math.max(0, Math.min(range.start, value.length));\n const end = Math.max(start, Math.min(range.end, value.length));\n const insertText = suggestion.insertText ?? suggestion.label;\n const next = value.slice(0, start) + insertText + value.slice(end);\n pendingSelectionRef.current = { value: next, caret: start + insertText.length };\n onChange(next);\n onAccept?.(suggestion);\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (rowsVisible && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {\n event.preventDefault();\n const count = suggestionList.length;\n // Steps from the clamped index, so a list that shrank moves from the row on screen\n // rather than from a raw index the user can no longer see.\n setHighlightedIndex(\n event.key === 'ArrowDown'\n ? (highlightedIndex + 1) % count\n : (highlightedIndex - 1 + count) % count,\n );\n return;\n }\n\n const highlighted =\n rowsVisible && highlightedIndex >= 0 ? suggestionList[highlightedIndex] : undefined;\n\n if (event.key === 'Enter') {\n if (highlighted) {\n event.preventDefault();\n acceptSuggestion(highlighted);\n return;\n }\n if (onSubmit) {\n // Otherwise a surrounding <form> submits too and the query is sent twice.\n // Without the prop the native submit is left alone, which is the useful default.\n event.preventDefault();\n onSubmit(value);\n }\n return;\n }\n\n if (event.key === 'Tab' && highlighted) {\n // Tab completes the term and stays put, the way an editor or a search bar does.\n // Letting focus move on would hand the next keystroke to another field.\n event.preventDefault();\n acceptSuggestion(highlighted);\n }\n };\n\n const segments = buildSegments(value, tokens);\n const textStyle = TEXT_STYLE[size];\n\n const menuContent = (() => {\n if (loading) {\n return (\n // `role=\"listbox\"` admits only option/group children.\n <div role=\"presentation\" style={MENU_MESSAGE_STYLE}>\n <Spinner size=\"xxxsmall\" />\n <span>Loading…</span>\n </div>\n );\n }\n if (hasSuggestions) {\n return suggestionList.map((suggestion, index) => {\n const highlighted = index === highlightedIndex;\n return (\n <button\n key={suggestion.id}\n ref={(element) => {\n rowRefs.current[index] = element;\n }}\n type=\"button\"\n id={getRowId(index)}\n role=\"option\"\n aria-selected={highlighted}\n // Focus stays on the input under the activedescendant model, so a row\n // must not become its own tab stop.\n tabIndex={-1}\n onClick={() => acceptSuggestion(suggestion)}\n onMouseEnter={() => setHighlightedIndex(index)}\n className=\"malachite-multiselect-option\"\n data-highlighted={highlighted}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n width: '100%',\n flexShrink: 0,\n border: 'none',\n textAlign: 'left',\n borderRadius: 8,\n padding: '8px 10px',\n font: 'inherit',\n cursor: 'pointer',\n color: 'var(--malachite-text-primary, rgb(10, 10, 10))',\n }}\n >\n <span\n style={{\n flex: 1,\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {suggestion.label}\n </span>\n {suggestion.detail && (\n <span\n style={{\n flexShrink: 0,\n fontSize: 'var(--malachite-font-size-xsmall, 11px)',\n color: 'var(--malachite-text-secondary, rgb(123, 123, 123))',\n }}\n >\n {suggestion.detail}\n </span>\n )}\n </button>\n );\n });\n }\n if (emptyMessage) {\n // `role=\"listbox\"` admits only option/group children.\n return (\n <div role=\"presentation\" style={MENU_MESSAGE_STYLE}>\n {emptyMessage}\n </div>\n );\n }\n return null;\n })();\n\n const hoverBorderClass = disabled ? '' : 'hover:border-[var(--malachite-text-placeholder)]';\n\n return (\n <div\n className={`relative w-full ${SIZE_CLASSES[size]} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${hoverBorderClass} transition-colors flex items-center ${disabled ? 'opacity-60 cursor-not-allowed' : ''} ${className}`}\n style={{\n boxShadow: focused\n ? 'var(--malachite-shadow-accent, 0px 1px 0px 0px rgba(0, 175, 102, 0.15))'\n : '0px 1px 1px 0px rgba(0, 0, 0, 0.15)',\n borderColor: focused\n ? 'var(--malachite-control-border-hover, rgba(0, 175, 102, 0.4))'\n : undefined,\n ...style,\n }}\n >\n {/* Clips here, not on the mirror: the mirror is what translates, and a transform\n carries the element's own clip rect with it, so the window onto the text would\n slide out of the field by exactly `scrollLeft`. */}\n <div className=\"relative flex-1 h-full min-w-0 overflow-hidden\">\n {/* aria-hidden: purely decorative, the input carries the accessible value. */}\n <div\n ref={mirrorRef}\n aria-hidden=\"true\"\n className={`absolute inset-0 flex items-center pointer-events-none ${CONTROL_TEXT_SIZE_CLASS[size]}`}\n style={{ ...textStyle, whiteSpace: 'pre' }}\n >\n {segments.map((segment) => (\n <span\n key={segment.start}\n style={{\n color: TOKEN_COLORS[segment.kind],\n ...(segment.kind === 'error'\n ? { textDecoration: 'underline wavy' }\n : {}),\n }}\n >\n {value.slice(segment.start, segment.end)}\n </span>\n ))}\n <span\n ref={measureRef}\n style={{ position: 'absolute', top: 0, left: 0, visibility: 'hidden' }}\n >\n {value.slice(0, caret)}\n </span>\n </div>\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={isMenuOpen}\n aria-controls={isMenuOpen ? listboxId : undefined}\n aria-activedescendant={\n rowsVisible && highlightedIndex >= 0\n ? getRowId(highlightedIndex)\n : undefined\n }\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n spellCheck={false}\n aria-labelledby={ariaLabelledBy}\n // A visible label outranks it, so the fallback only applies without one.\n aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}\n value={value}\n onChange={handleChange}\n onSelect={handleSelect}\n onScroll={() => {\n syncMirrorScroll();\n if (isMenuOpen) {\n updateMenuPosition();\n }\n }}\n onKeyDown={handleKeyDown}\n onFocus={() => {\n setFocused(true);\n setDismissed(false);\n }}\n onBlur={() => setFocused(false)}\n placeholder={placeholder}\n disabled={disabled}\n className={`absolute inset-0 w-full h-full bg-transparent border-none outline-none ${CONTROL_TEXT_SIZE_CLASS[size]} placeholder:text-[var(--malachite-text-placeholder)] placeholder:font-medium disabled:cursor-not-allowed`}\n style={{\n ...textStyle,\n color: 'transparent',\n caretColor: 'var(--malachite-text-primary, rgb(10, 10, 10))',\n }}\n />\n </div>\n {isMenuOpen &&\n createPortal(\n <div\n ref={menuRef}\n id={listboxId}\n role=\"listbox\"\n // Rides the dropdown overlay band, but borrows the multiselect option\n // rows below: their highlight follows `data-highlighted`, which is the\n // keyboard cursor this menu has, rather than a bare `:hover`.\n {...overlayIdentity('dropdown')}\n onMouseDown={handleMenuMouseDown}\n style={{\n position: 'fixed',\n top: menuPosition?.top ?? 0,\n left: menuPosition?.left ?? 0,\n minWidth: Math.min(\n MENU_FALLBACK_WIDTH_PX,\n menuPosition?.width ?? MENU_FALLBACK_WIDTH_PX,\n ),\n maxWidth: Math.min(\n MENU_MAX_WIDTH_PX,\n menuPosition?.width ?? MENU_MAX_WIDTH_PX,\n ),\n background: 'var(--malachite-card-background, rgb(255, 255, 255))',\n border: '1px solid var(--malachite-card-border, rgb(232, 232, 232))',\n borderRadius: 12,\n boxShadow: '0px 2px 5px 0px rgba(0, 0, 0, 0.1)',\n padding: 4,\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n maxHeight: 320,\n overflowY: 'auto',\n fontSize: CONTROL_TEXT_SIZE[size],\n visibility: menuPosition ? 'visible' : 'hidden',\n opacity: menuPosition ? 1 : 0,\n }}\n >\n {menuContent}\n </div>,\n document.body,\n )}\n </div>\n );\n};\n\nexport default QueryInput;\nexport { QueryInput };\n"],"names":["SIZE_CLASSES","TOKEN_COLORS","TEXT_STYLE","CONTROL_TEXT_SIZE","MENU_FALLBACK_WIDTH_PX","MENU_MAX_WIDTH_PX","MENU_MESSAGE_STYLE","handleMenuMouseDown","event","sanitizeTokens","value","tokens","sorted","token","a","b","accepted","cursor","buildSegments","segments","QueryInput","onChange","suggestions","activeRange","onAccept","onQueryContextChange","onSubmit","placeholder","size","disabled","loading","emptyMessage","className","style","ariaLabel","ariaLabelledBy","listboxId","useId","getRowId","index","focused","setFocused","useState","caret","setCaret","rawHighlightedIndex","setHighlightedIndex","dismissed","setDismissed","menuPosition","setMenuPosition","inputRef","useRef","mirrorRef","measureRef","menuRef","rowRefs","pendingSelectionRef","suggestionsProvided","suggestionList","hasSuggestions","isMenuOpen","useOwnedByPopover","useCallback","rowsVisible","highlightedIndex","useLayoutEffect","current","syncMirrorScroll","input","mirror","updateMenuPosition","caretOffsetPx","_a","computeCaretMenuGeometry","_b","_c","useEffect","handleScrollOrResize","popEscapeLayer","pushEscapeLayer","pending","row","container","rowBottom","useInsertionEffect","ensureMultiSelectPanelStyles","reportCaret","nextValue","nextCaret","handleChange","next","handleSelect","target","acceptSuggestion","suggestion","fallbackCaret","range","start","end","insertText","handleKeyDown","count","highlighted","textStyle","menuContent","jsxs","jsx","Spinner","element","hoverBorderClass","CONTROL_TEXT_SIZE_CLASS","segment","createPortal","overlayIdentity"],"mappings":"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"}
1
+ {"version":3,"file":"QueryInput.js","sources":["../../src/components/QueryInput.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useId,\n useInsertionEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport Spinner from './Spinner';\nimport { CONTROL_TEXT_SIZE, CONTROL_TEXT_SIZE_CLASS } from './controlTextSize';\nimport { pushEscapeLayer } from './escapeLayers';\nimport { ensureMultiSelectPanelStyles } from './optionRowStyles';\nimport { usePopupLayer } from './overlayLayers';\nimport { useOwnedByPopover } from './popoverTree';\nimport { type CaretMenuGeometry, computeCaretMenuGeometry } from './queryInputGeometry';\n\nexport type QueryTokenKind =\n | 'key'\n | 'value'\n | 'operator'\n | 'negation'\n | 'punctuation'\n | 'error'\n | 'plain';\n\nexport interface QueryToken {\n /** Index into `value`. */\n start: number;\n /** Exclusive. */\n end: number;\n kind: QueryTokenKind;\n}\n\nexport interface QuerySuggestion {\n id: string;\n label: string;\n /** Secondary text, right-aligned in the row. */\n detail?: string;\n /** What replaces `activeRange` when accepted. Defaults to `label`. */\n insertText?: string;\n}\n\nexport type QueryInputSize = 'small' | 'medium' | 'large';\n\nexport interface QueryInputProps {\n value: string;\n onChange: (value: string) => void;\n /** Ranges to colour. Tokens that are out of range, reversed or overlapping are ignored. */\n tokens?: QueryToken[];\n /** Rows for the caret menu. Empty or undefined means the menu stays closed. */\n suggestions?: QuerySuggestion[];\n /** The span an accepted suggestion replaces. Defaults to a zero-width span at the caret. */\n activeRange?: { start: number; end: number };\n onAccept?: (suggestion: QuerySuggestion) => void;\n /** Every value change AND every caret move, so the consumer can recompute tokens/suggestions. */\n onQueryContextChange?: (context: { value: string; caret: number }) => void;\n /** Enter with nothing highlighted in the menu. */\n onSubmit?: (value: string) => void;\n placeholder?: string;\n size?: QueryInputSize;\n disabled?: boolean;\n /** Suggestions are being fetched: the menu shows a loading row instead of the empty message. */\n loading?: boolean;\n emptyMessage?: string;\n className?: string;\n style?: React.CSSProperties;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n}\n\n// Same 28/32/42 ladder as Search, minus the icon gap this component has no icon to space.\nconst SIZE_CLASSES: Record<QueryInputSize, string> = {\n small: 'h-[var(--malachite-control-height-small,28px)] px-[10px] rounded-[var(--malachite-radius-10,10px)]',\n medium: 'h-[var(--malachite-control-height-medium,32px)] px-[10px] rounded-xl',\n large: 'h-[var(--malachite-control-height-large,42px)] px-[12px] rounded-[var(--malachite-radius-xxl,14px)]',\n};\n\nconst TOKEN_COLORS: Record<QueryTokenKind, string> = {\n key: 'var(--malachite-codeblock-keyword, rgb(12, 88, 174))',\n value: 'var(--malachite-codeblock-string, rgb(2, 122, 66))',\n operator: 'var(--malachite-codeblock-type, rgb(12, 88, 174))',\n negation: 'var(--malachite-text-destructive, rgb(237, 53, 54))',\n punctuation: 'var(--malachite-codeblock-foreground, rgb(10, 10, 10))',\n error: 'var(--malachite-text-error, rgb(237, 53, 54))',\n plain: 'var(--malachite-text-primary, rgb(10, 10, 10))',\n};\n\n// The one place the mirror and the input agree on typography: diverge here and the coloured\n// text drifts out from under the caret it's supposed to sit beneath.\nconst TEXT_STYLE: Record<QueryInputSize, React.CSSProperties> = {\n small: {\n fontFamily: 'inherit',\n fontSize: CONTROL_TEXT_SIZE.small,\n fontWeight: 500,\n letterSpacing: 'normal',\n lineHeight: 'normal',\n padding: 0,\n },\n medium: {\n fontFamily: 'inherit',\n fontSize: CONTROL_TEXT_SIZE.medium,\n fontWeight: 500,\n letterSpacing: 'normal',\n lineHeight: 'normal',\n padding: 0,\n },\n large: {\n fontFamily: 'inherit',\n fontSize: CONTROL_TEXT_SIZE.large,\n fontWeight: 500,\n letterSpacing: 'normal',\n lineHeight: 'normal',\n padding: 0,\n },\n};\n\n/** Before layout the portal hasn't painted yet, so the first geometry pass needs a width. */\nconst MENU_FALLBACK_WIDTH_PX = 220;\n\nconst MENU_MAX_WIDTH_PX = 360;\n\nconst MENU_MESSAGE_STYLE: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n padding: '10px 12px',\n color: 'var(--malachite-text-dimmed, rgb(161, 161, 161))',\n whiteSpace: 'nowrap',\n};\n\n/** Keeps focus (and the caret) on the input so a row click never blurs it first. */\nconst handleMenuMouseDown = (event: React.MouseEvent) => {\n event.preventDefault();\n};\n\ninterface QuerySegment {\n start: number;\n end: number;\n kind: QueryTokenKind;\n}\n\n/** Drops tokens the consumer got wrong rather than letting them corrupt the render. */\nconst sanitizeTokens = (value: string, tokens: QueryToken[]): QueryToken[] => {\n const inRange = tokens.filter(\n (token) => token.start >= 0 && token.end <= value.length && token.start < token.end,\n );\n const sorted = [...inRange].sort((a, b) => a.start - b.start);\n const accepted: QueryToken[] = [];\n let cursor = 0;\n for (const token of sorted) {\n if (token.start < cursor) {\n continue;\n }\n accepted.push(token);\n cursor = token.end;\n }\n return accepted;\n};\n\n/** Fills the gaps between tokens with 'plain' segments so the mirror's text always equals `value`. */\nconst buildSegments = (value: string, tokens: QueryToken[]): QuerySegment[] => {\n const segments: QuerySegment[] = [];\n let cursor = 0;\n for (const token of sanitizeTokens(value, tokens)) {\n if (token.start > cursor) {\n segments.push({ start: cursor, end: token.start, kind: 'plain' });\n }\n segments.push(token);\n cursor = token.end;\n }\n if (cursor < value.length) {\n segments.push({ start: cursor, end: value.length, kind: 'plain' });\n }\n return segments;\n};\n\nconst QueryInput: React.FC<QueryInputProps> = ({\n value,\n onChange,\n tokens = [],\n suggestions,\n activeRange,\n onAccept,\n onQueryContextChange,\n onSubmit,\n placeholder,\n size = 'large',\n disabled = false,\n loading = false,\n emptyMessage,\n className = '',\n style,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n}) => {\n const instanceId = useId();\n const listboxId = `${instanceId}-listbox`;\n const getRowId = (index: number) => `${listboxId}-option-${index}`;\n\n const [focused, setFocused] = useState(false);\n const [caret, setCaret] = useState(() => value.length);\n const [rawHighlightedIndex, setHighlightedIndex] = useState(0);\n // Escape closes the menu whatever `suggestions` and `loading` say. Lifted by an edit or a\n // refocus, so navigating a dismissed query does not pop it back open.\n const [dismissed, setDismissed] = useState(false);\n const [menuPosition, setMenuPosition] = useState<CaretMenuGeometry | null>(null);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const mirrorRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLSpanElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const rowRefs = useRef<(HTMLButtonElement | null)[]>([]);\n // Caret restore after an accepted suggestion. Carries the string it was computed against:\n // nothing obliges a controlled consumer to apply the value handed to `onChange`, and a\n // caret from a rejected or rewritten suggestion must not land on a later unrelated edit.\n const pendingSelectionRef = useRef<{ value: string; caret: number } | null>(null);\n\n const suggestionsProvided = Array.isArray(suggestions);\n const suggestionList = suggestions ?? [];\n const hasSuggestions = suggestionList.length > 0;\n const isEmptyList = suggestionsProvided && !hasSuggestions;\n const wantsMenu = loading || hasSuggestions || (isEmptyList && !!emptyMessage);\n const isMenuOpen = focused && wantsMenu && !dismissed && !disabled;\n const popupIdentity = usePopupLayer('dropdown', isMenuOpen, menuRef);\n useOwnedByPopover(\n useCallback(() => menuRef.current, []),\n isMenuOpen,\n );\n // `loading` paints the spinner over any rows the consumer is still holding, so nothing is\n // on screen to arrow through, accept, or point `aria-activedescendant` at.\n const rowsVisible = isMenuOpen && hasSuggestions && !loading;\n\n // A consumer's `suggestions` array is commonly rebuilt inline, so its identity changes on\n // any parent re-render and cannot stand in for \"the candidates changed\". Clamping during\n // render keeps the row the user arrowed to, and keeps the highlight inside a shrunken list.\n const highlightedIndex = hasSuggestions\n ? Math.min(rawHighlightedIndex, suggestionList.length - 1)\n : -1;\n\n useLayoutEffect(() => {\n setCaret((current) => Math.min(current, value.length));\n }, [value]);\n\n const syncMirrorScroll = useCallback(() => {\n const input = inputRef.current;\n const mirror = mirrorRef.current;\n if (input && mirror) {\n mirror.style.transform = `translateX(${-input.scrollLeft}px)`;\n }\n }, []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-syncs on every value change, not on value itself\n useLayoutEffect(() => {\n syncMirrorScroll();\n }, [value, syncMirrorScroll]);\n\n const updateMenuPosition = useCallback(() => {\n const input = inputRef.current;\n if (!input) {\n return;\n }\n const caretOffsetPx = (measureRef.current?.offsetWidth ?? 0) - input.scrollLeft;\n setMenuPosition(\n computeCaretMenuGeometry({\n inputRect: input.getBoundingClientRect(),\n caretOffsetPx,\n menuWidth: menuRef.current?.offsetWidth ?? MENU_FALLBACK_WIDTH_PX,\n menuHeight: menuRef.current?.offsetHeight ?? 0,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }),\n );\n }, []);\n\n // Depends on `caret` as well as `value`: the measuring span is sliced at the caret, so an\n // arrow-key move with no edit still changes where the menu belongs.\n // biome-ignore lint/correctness/useExhaustiveDependencies: repositions on every keystroke, not on value itself\n useLayoutEffect(() => {\n if (isMenuOpen) {\n updateMenuPosition();\n } else {\n setMenuPosition(null);\n }\n // `suggestions` and `loading` are here because they change the menu's own size: rows\n // arriving after a fetch resize it while the query and caret sit still, and a stale\n // measurement would leave `openUp` and the caret-aligned `left` computed for the old box.\n }, [isMenuOpen, value, caret, suggestions, loading, updateMenuPosition]);\n\n useEffect(() => {\n if (!isMenuOpen) {\n return undefined;\n }\n const handleScrollOrResize = () => updateMenuPosition();\n window.addEventListener('scroll', handleScrollOrResize, true);\n window.addEventListener('resize', handleScrollOrResize);\n const popEscapeLayer = pushEscapeLayer(() => setDismissed(true));\n return () => {\n window.removeEventListener('scroll', handleScrollOrResize, true);\n window.removeEventListener('resize', handleScrollOrResize);\n popEscapeLayer();\n };\n }, [isMenuOpen, updateMenuPosition]);\n\n // Restore the caret placed by the last accepted suggestion once the new `value` has\n // actually rendered. Guarded on the ref so an externally-driven value change never gets\n // its selection clobbered.\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally re-reads latest closures via pendingSelectionRef's gate\n useLayoutEffect(() => {\n const pending = pendingSelectionRef.current;\n if (pending === null) {\n return;\n }\n pendingSelectionRef.current = null;\n const input = inputRef.current;\n // Dropped unless the consumer applied exactly the string this caret was measured\n // against. A rejected or rewritten suggestion leaves the caret where the user put it.\n if (!input || pending.value !== value) {\n return;\n }\n input.setSelectionRange(pending.caret, pending.caret);\n setCaret(pending.caret);\n onQueryContextChange?.({ value, caret: pending.caret });\n syncMirrorScroll();\n updateMenuPosition();\n }, [value]);\n\n // Scrolled by hand rather than with `scrollIntoView`, which would also scroll the page and\n // carry the field itself out of view. Same reason MultiSelect does it this way.\n useLayoutEffect(() => {\n if (!rowsVisible) {\n return;\n }\n const row = rowRefs.current[highlightedIndex];\n const container = menuRef.current;\n if (!row || !container) {\n return;\n }\n const rowBottom = row.offsetTop + row.offsetHeight;\n if (rowBottom > container.scrollTop + container.clientHeight) {\n container.scrollTop = rowBottom - container.clientHeight;\n } else if (row.offsetTop < container.scrollTop) {\n container.scrollTop = row.offsetTop;\n }\n }, [highlightedIndex, rowsVisible]);\n\n // useInsertionEffect, not layout: the rules have to land before the rows paint.\n useInsertionEffect(() => {\n if (isMenuOpen) {\n ensureMultiSelectPanelStyles();\n }\n }, [isMenuOpen]);\n\n const reportCaret = (nextValue: string, nextCaret: number) => {\n setCaret(nextCaret);\n onQueryContextChange?.({ value: nextValue, caret: nextCaret });\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const next = event.target.value;\n // Editing is the user asking again, so it lifts an earlier Escape. Moving the caret is\n // not, and neither is a new `suggestions` array: that identity changes on any parent\n // re-render, which would undo the dismissal with nothing having been asked.\n setDismissed(false);\n setHighlightedIndex(0);\n onChange(next);\n reportCaret(next, event.target.selectionStart ?? next.length);\n };\n\n const handleSelect = (event: React.SyntheticEvent<HTMLInputElement>) => {\n const target = event.currentTarget;\n reportCaret(target.value, target.selectionStart ?? target.value.length);\n };\n\n const acceptSuggestion = (suggestion: QuerySuggestion) => {\n const fallbackCaret = inputRef.current?.selectionStart ?? value.length;\n const range = activeRange ?? { start: fallbackCaret, end: fallbackCaret };\n // Clamped for the same reason `sanitizeTokens` drops a bad range, except this one\n // writes: `slice` reads a negative index from the end of the string, so an unchecked\n // `start` of -1 silently rewrites the tail instead of doing nothing.\n const start = Math.max(0, Math.min(range.start, value.length));\n const end = Math.max(start, Math.min(range.end, value.length));\n const insertText = suggestion.insertText ?? suggestion.label;\n const next = value.slice(0, start) + insertText + value.slice(end);\n pendingSelectionRef.current = { value: next, caret: start + insertText.length };\n onChange(next);\n onAccept?.(suggestion);\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (rowsVisible && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {\n event.preventDefault();\n const count = suggestionList.length;\n // Steps from the clamped index, so a list that shrank moves from the row on screen\n // rather than from a raw index the user can no longer see.\n setHighlightedIndex(\n event.key === 'ArrowDown'\n ? (highlightedIndex + 1) % count\n : (highlightedIndex - 1 + count) % count,\n );\n return;\n }\n\n const highlighted =\n rowsVisible && highlightedIndex >= 0 ? suggestionList[highlightedIndex] : undefined;\n\n if (event.key === 'Enter') {\n if (highlighted) {\n event.preventDefault();\n acceptSuggestion(highlighted);\n return;\n }\n if (onSubmit) {\n // Otherwise a surrounding <form> submits too and the query is sent twice.\n // Without the prop the native submit is left alone, which is the useful default.\n event.preventDefault();\n onSubmit(value);\n }\n return;\n }\n\n if (event.key === 'Tab' && highlighted) {\n // Tab completes the term and stays put, the way an editor or a search bar does.\n // Letting focus move on would hand the next keystroke to another field.\n event.preventDefault();\n acceptSuggestion(highlighted);\n }\n };\n\n const segments = buildSegments(value, tokens);\n const textStyle = TEXT_STYLE[size];\n\n const menuContent = (() => {\n if (loading) {\n return (\n // `role=\"listbox\"` admits only option/group children.\n <div role=\"presentation\" style={MENU_MESSAGE_STYLE}>\n <Spinner size=\"xxxsmall\" />\n <span>Loading…</span>\n </div>\n );\n }\n if (hasSuggestions) {\n return suggestionList.map((suggestion, index) => {\n const highlighted = index === highlightedIndex;\n return (\n <button\n key={suggestion.id}\n ref={(element) => {\n rowRefs.current[index] = element;\n }}\n type=\"button\"\n id={getRowId(index)}\n role=\"option\"\n aria-selected={highlighted}\n // Focus stays on the input under the activedescendant model, so a row\n // must not become its own tab stop.\n tabIndex={-1}\n onClick={() => acceptSuggestion(suggestion)}\n onMouseEnter={() => setHighlightedIndex(index)}\n className=\"malachite-multiselect-option\"\n data-highlighted={highlighted}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n width: '100%',\n flexShrink: 0,\n border: 'none',\n textAlign: 'left',\n borderRadius: 8,\n padding: '8px 10px',\n font: 'inherit',\n cursor: 'pointer',\n color: 'var(--malachite-text-primary, rgb(10, 10, 10))',\n }}\n >\n <span\n style={{\n flex: 1,\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {suggestion.label}\n </span>\n {suggestion.detail && (\n <span\n style={{\n flexShrink: 0,\n fontSize: 'var(--malachite-font-size-xsmall, 11px)',\n color: 'var(--malachite-text-secondary, rgb(123, 123, 123))',\n }}\n >\n {suggestion.detail}\n </span>\n )}\n </button>\n );\n });\n }\n if (emptyMessage) {\n // `role=\"listbox\"` admits only option/group children.\n return (\n <div role=\"presentation\" style={MENU_MESSAGE_STYLE}>\n {emptyMessage}\n </div>\n );\n }\n return null;\n })();\n\n const hoverBorderClass = disabled ? '' : 'hover:border-[var(--malachite-text-placeholder)]';\n\n return (\n <div\n className={`relative w-full ${SIZE_CLASSES[size]} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${hoverBorderClass} transition-colors flex items-center ${disabled ? 'opacity-60 cursor-not-allowed' : ''} ${className}`}\n style={{\n boxShadow: focused\n ? 'var(--malachite-shadow-accent, 0px 1px 0px 0px rgba(0, 175, 102, 0.15))'\n : '0px 1px 1px 0px rgba(0, 0, 0, 0.15)',\n borderColor: focused\n ? 'var(--malachite-control-border-hover, rgba(0, 175, 102, 0.4))'\n : undefined,\n ...style,\n }}\n >\n {/* Clips here, not on the mirror: the mirror is what translates, and a transform\n carries the element's own clip rect with it, so the window onto the text would\n slide out of the field by exactly `scrollLeft`. */}\n <div className=\"relative flex-1 h-full min-w-0 overflow-hidden\">\n {/* aria-hidden: purely decorative, the input carries the accessible value. */}\n <div\n ref={mirrorRef}\n aria-hidden=\"true\"\n className={`absolute inset-0 flex items-center pointer-events-none ${CONTROL_TEXT_SIZE_CLASS[size]}`}\n style={{ ...textStyle, whiteSpace: 'pre' }}\n >\n {segments.map((segment) => (\n <span\n key={segment.start}\n style={{\n color: TOKEN_COLORS[segment.kind],\n ...(segment.kind === 'error'\n ? { textDecoration: 'underline wavy' }\n : {}),\n }}\n >\n {value.slice(segment.start, segment.end)}\n </span>\n ))}\n <span\n ref={measureRef}\n style={{ position: 'absolute', top: 0, left: 0, visibility: 'hidden' }}\n >\n {value.slice(0, caret)}\n </span>\n </div>\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={isMenuOpen}\n aria-controls={isMenuOpen ? listboxId : undefined}\n aria-activedescendant={\n rowsVisible && highlightedIndex >= 0\n ? getRowId(highlightedIndex)\n : undefined\n }\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n spellCheck={false}\n aria-labelledby={ariaLabelledBy}\n // A visible label outranks it, so the fallback only applies without one.\n aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}\n value={value}\n onChange={handleChange}\n onSelect={handleSelect}\n onScroll={() => {\n syncMirrorScroll();\n if (isMenuOpen) {\n updateMenuPosition();\n }\n }}\n onKeyDown={handleKeyDown}\n onFocus={() => {\n setFocused(true);\n setDismissed(false);\n }}\n onBlur={() => setFocused(false)}\n placeholder={placeholder}\n disabled={disabled}\n className={`absolute inset-0 w-full h-full bg-transparent border-none outline-none ${CONTROL_TEXT_SIZE_CLASS[size]} placeholder:text-[var(--malachite-text-placeholder)] placeholder:font-medium disabled:cursor-not-allowed`}\n style={{\n ...textStyle,\n color: 'transparent',\n caretColor: 'var(--malachite-text-primary, rgb(10, 10, 10))',\n }}\n />\n </div>\n {isMenuOpen &&\n createPortal(\n <div\n ref={menuRef}\n id={listboxId}\n role=\"listbox\"\n // Rides the dropdown overlay band, but borrows the multiselect option\n // rows below: their highlight follows `data-highlighted`, which is the\n // keyboard cursor this menu has, rather than a bare `:hover`.\n {...popupIdentity}\n onMouseDown={handleMenuMouseDown}\n style={{\n position: 'fixed',\n top: menuPosition?.top ?? 0,\n left: menuPosition?.left ?? 0,\n minWidth: Math.min(\n MENU_FALLBACK_WIDTH_PX,\n menuPosition?.width ?? MENU_FALLBACK_WIDTH_PX,\n ),\n maxWidth: Math.min(\n MENU_MAX_WIDTH_PX,\n menuPosition?.width ?? MENU_MAX_WIDTH_PX,\n ),\n background: 'var(--malachite-card-background, rgb(255, 255, 255))',\n border: '1px solid var(--malachite-card-border, rgb(232, 232, 232))',\n borderRadius: 12,\n boxShadow: '0px 2px 5px 0px rgba(0, 0, 0, 0.1)',\n padding: 4,\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n maxHeight: 320,\n overflowY: 'auto',\n fontSize: CONTROL_TEXT_SIZE[size],\n visibility: menuPosition ? 'visible' : 'hidden',\n opacity: menuPosition ? 1 : 0,\n }}\n >\n {menuContent}\n </div>,\n document.body,\n )}\n </div>\n );\n};\n\nexport default QueryInput;\nexport { QueryInput };\n"],"names":["SIZE_CLASSES","TOKEN_COLORS","TEXT_STYLE","CONTROL_TEXT_SIZE","MENU_FALLBACK_WIDTH_PX","MENU_MAX_WIDTH_PX","MENU_MESSAGE_STYLE","handleMenuMouseDown","event","sanitizeTokens","value","tokens","sorted","token","a","b","accepted","cursor","buildSegments","segments","QueryInput","onChange","suggestions","activeRange","onAccept","onQueryContextChange","onSubmit","placeholder","size","disabled","loading","emptyMessage","className","style","ariaLabel","ariaLabelledBy","listboxId","useId","getRowId","index","focused","setFocused","useState","caret","setCaret","rawHighlightedIndex","setHighlightedIndex","dismissed","setDismissed","menuPosition","setMenuPosition","inputRef","useRef","mirrorRef","measureRef","menuRef","rowRefs","pendingSelectionRef","suggestionsProvided","suggestionList","hasSuggestions","isMenuOpen","popupIdentity","usePopupLayer","useOwnedByPopover","useCallback","rowsVisible","highlightedIndex","useLayoutEffect","current","syncMirrorScroll","input","mirror","updateMenuPosition","caretOffsetPx","_a","computeCaretMenuGeometry","_b","_c","useEffect","handleScrollOrResize","popEscapeLayer","pushEscapeLayer","pending","row","container","rowBottom","useInsertionEffect","ensureMultiSelectPanelStyles","reportCaret","nextValue","nextCaret","handleChange","next","handleSelect","target","acceptSuggestion","suggestion","fallbackCaret","range","start","end","insertText","handleKeyDown","count","highlighted","textStyle","menuContent","jsxs","jsx","Spinner","element","hoverBorderClass","CONTROL_TEXT_SIZE_CLASS","segment","createPortal"],"mappings":"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"}