@stackone/malachite 2.42.2 → 2.44.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.
- package/COMPONENTS.md +3 -3
- package/README.md +7 -0
- package/dist/components/ActionMenu.js +70 -70
- package/dist/components/ActionMenu.js.map +1 -1
- package/dist/components/CardStrip.js +179 -172
- package/dist/components/CardStrip.js.map +1 -1
- package/dist/components/Chat/ChatModelPicker.js +74 -73
- package/dist/components/Chat/ChatModelPicker.js.map +1 -1
- package/dist/components/Dropdown.js +432 -419
- package/dist/components/Dropdown.js.map +1 -1
- package/dist/components/MultiSelect.js +218 -213
- package/dist/components/MultiSelect.js.map +1 -1
- package/dist/components/Popover.js +102 -102
- package/dist/components/Popover.js.map +1 -1
- package/dist/components/QueryInput.js +65 -65
- package/dist/components/QueryInput.js.map +1 -1
- package/dist/components/SegmentedControl.js +40 -38
- package/dist/components/SegmentedControl.js.map +1 -1
- package/dist/components/SelectableCard.js +194 -168
- package/dist/components/SelectableCard.js.map +1 -1
- package/dist/components/focusRing.js +7 -6
- package/dist/components/focusRing.js.map +1 -1
- package/dist/components/overlayBehaviour.js +145 -94
- package/dist/components/overlayBehaviour.js.map +1 -1
- package/dist/components/overlayLayers.js +34 -0
- package/dist/components/overlayLayers.js.map +1 -0
- package/dist/components/portalOverlay.js.map +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/tailwind.css.js +1 -1
- package/package.json +4 -3
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { jsxs as E, jsx as d } from "react/jsx-runtime";
|
|
2
|
-
import { useId as
|
|
3
|
-
import { createPortal as
|
|
4
|
-
import
|
|
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
|
|
7
|
-
import { ensureMultiSelectPanelStyles as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { computeCaretMenuGeometry as
|
|
11
|
-
const
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
56
|
+
}, _t = (r) => {
|
|
57
57
|
r.preventDefault();
|
|
58
|
-
},
|
|
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
|
-
},
|
|
66
|
+
}, Wt = (r, f) => {
|
|
67
67
|
const h = [];
|
|
68
68
|
let l = 0;
|
|
69
|
-
for (const s of
|
|
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:
|
|
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 = `${
|
|
92
|
-
|
|
93
|
-
F(() =>
|
|
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 =
|
|
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
|
|
107
|
+
const g = F(() => {
|
|
108
108
|
var o, p, D;
|
|
109
|
-
const t =
|
|
109
|
+
const t = w.current;
|
|
110
110
|
if (!t)
|
|
111
111
|
return;
|
|
112
|
-
const e = (((o =
|
|
113
|
-
|
|
114
|
-
|
|
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 =
|
|
118
|
-
menuHeight: ((D =
|
|
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 ?
|
|
126
|
-
}, [a, r,
|
|
125
|
+
a ? g() : Z(null);
|
|
126
|
+
}, [a, r, Y, l, I, g]), wt(() => {
|
|
127
127
|
if (!a)
|
|
128
128
|
return;
|
|
129
|
-
const t = () =>
|
|
129
|
+
const t = () => g();
|
|
130
130
|
window.addEventListener("scroll", t, !0), window.addEventListener("resize", t);
|
|
131
|
-
const e =
|
|
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,
|
|
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 =
|
|
141
|
-
!e || t.value !== r || (e.setSelectionRange(t.caret, t.caret), N(t.caret), n == null || n({ value: r, caret: t.caret }), R(),
|
|
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 =
|
|
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]),
|
|
151
|
-
a &&
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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 =
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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(
|
|
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,
|
|
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 ${
|
|
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:
|
|
265
|
+
ref: C,
|
|
266
266
|
"aria-hidden": "true",
|
|
267
|
-
className: `absolute inset-0 flex items-center pointer-events-none ${rt[
|
|
267
|
+
className: `absolute inset-0 flex items-center pointer-events-none ${rt[b]}`,
|
|
268
268
|
style: { ...J, whiteSpace: "pre" },
|
|
269
269
|
children: [
|
|
270
|
-
|
|
270
|
+
mt.map((t) => /* @__PURE__ */ d(
|
|
271
271
|
"span",
|
|
272
272
|
{
|
|
273
273
|
style: {
|
|
274
|
-
color:
|
|
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:
|
|
284
|
+
ref: G,
|
|
285
285
|
style: { position: "absolute", top: 0, left: 0, visibility: "hidden" },
|
|
286
|
-
children: r.slice(0,
|
|
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:
|
|
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:
|
|
308
|
-
onSelect:
|
|
307
|
+
onChange: ht,
|
|
308
|
+
onSelect: ft,
|
|
309
309
|
onScroll: () => {
|
|
310
|
-
R(), a &&
|
|
310
|
+
R(), a && g();
|
|
311
311
|
},
|
|
312
|
-
onKeyDown:
|
|
312
|
+
onKeyDown: ut,
|
|
313
313
|
onFocus: () => {
|
|
314
|
-
|
|
314
|
+
P(!0), A(!1);
|
|
315
315
|
},
|
|
316
|
-
onBlur: () =>
|
|
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[
|
|
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 &&
|
|
328
|
+
a && yt(
|
|
329
329
|
/* @__PURE__ */ d(
|
|
330
330
|
"div",
|
|
331
331
|
{
|
|
332
|
-
ref:
|
|
332
|
+
ref: m,
|
|
333
333
|
id: W,
|
|
334
334
|
role: "listbox",
|
|
335
|
-
...
|
|
336
|
-
onMouseDown:
|
|
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: _[
|
|
359
|
+
fontSize: _[b],
|
|
360
360
|
visibility: i ? "visible" : "hidden",
|
|
361
361
|
opacity: i ? 1 : 0
|
|
362
362
|
},
|
|
363
|
-
children:
|
|
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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"}
|