Skip to content

Commit f66df5d

Browse files
committed
Flash only the cursor's field and add a persistent current highlight
The cursor reveal flashed every node on the path to the cursor, which in a deep tree lit up a whole column at once and diluted the signal of where the cursor actually landed. Flash only the field the cursor lands on; ancestors still auto-expand but no longer flash. Add a persistent highlight (a faint tint and a left accent bar) on the field the cursor sits in, so the current field is readable at any moment rather than only at the instant of a move. It is kept visually distinct from the checked, in-document state, which only recolors the field name, and applies to inline-fragment rows too.
1 parent 4bfdc36 commit f66df5d

5 files changed

Lines changed: 36 additions & 16 deletions

File tree

packages/graphiql-plugin-query-builder/src/components/field-row.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ type FieldRowProps = {
1313
expanded: boolean;
1414
/** Briefly highlight the row when it comes into view via cursor tracking. */
1515
flash?: boolean;
16+
/** Persistent highlight while the editor cursor sits on this field. */
17+
current?: boolean;
1618
argValues?: Record<string, ArgValue>;
1719
/**
1820
* Map of arg name → variable name for args that have been promoted to
@@ -38,6 +40,7 @@ export const FieldRow: FC<FieldRowProps> = ({
3840
hasChildren,
3941
expanded,
4042
flash = false,
43+
current = false,
4144
argValues = {},
4245
argVariables = {},
4346
onToggle,
@@ -56,7 +59,9 @@ export const FieldRow: FC<FieldRowProps> = ({
5659

5760
return (
5861
<div
59-
className={`graphiql-qb-field-row${flash ? ' graphiql-qb-flash' : ''}`}
62+
className={`graphiql-qb-field-row${flash ? ' graphiql-qb-flash' : ''}${
63+
current ? ' graphiql-qb-current' : ''
64+
}`}
6065
style={{ paddingLeft: indent }}
6166
data-testid="field-row"
6267
data-selected={selected ? 'true' : 'false'}

packages/graphiql-plugin-query-builder/src/components/field-tree-node.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ export const FieldTreeNode: FC<FieldTreeNodeProps> = ({ field, path }) => {
5050
const selected = isFieldSelected(doc, fullPath, operationName);
5151
const hasChildren = isObject || isAbstract;
5252

53-
const { flash, nodeRef } = useCursorReveal(fullPath, cursorPath, setExpanded);
53+
const { flash, current, nodeRef } = useCursorReveal(
54+
fullPath,
55+
cursorPath,
56+
setExpanded,
57+
);
5458

5559
function handleExpand() {
5660
setExpanded(prev => !prev);
@@ -65,6 +69,7 @@ export const FieldTreeNode: FC<FieldTreeNodeProps> = ({ field, path }) => {
6569
hasChildren={hasChildren}
6670
expanded={expanded}
6771
flash={flash}
72+
current={current}
6873
argValues={argValues}
6974
argVariables={argVariables}
7075
onToggle={onToggle}

packages/graphiql-plugin-query-builder/src/components/type-condition-selector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ const TypeConditionEntry: FC<TypeConditionEntryProps> = ({
186186
useFieldTreeContext();
187187
const [expanded, setExpanded] = useState(false);
188188
const fragmentPath = [...fieldPath, inlineFragmentSegment(typeName)];
189-
const { flash, nodeRef } = useCursorReveal(
189+
const { flash, current, nodeRef } = useCursorReveal(
190190
fragmentPath,
191191
cursorPath,
192192
setExpanded,
@@ -204,7 +204,7 @@ const TypeConditionEntry: FC<TypeConditionEntryProps> = ({
204204
<div
205205
className={`graphiql-qb-inline-fragment${
206206
flash ? ' graphiql-qb-flash' : ''
207-
}`}
207+
}${current ? ' graphiql-qb-current' : ''}`}
208208
ref={nodeRef}
209209
>
210210
<div

packages/graphiql-plugin-query-builder/src/components/use-cursor-reveal.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import { useEffect, useRef, useState } from 'react';
22

33
/**
44
* Reveals a tree node as the editor cursor moves onto it or a descendant:
5-
* auto-expands it when it's an ancestor of the cursor's field, flashes it (and
6-
* its ancestors), and scrolls it into view when the cursor lands exactly on it.
7-
* Shared by field rows and inline-fragment rows so both react to the cursor.
5+
* auto-expands it when it's an ancestor of the cursor's field, and (only when
6+
* the cursor lands exactly on it) flashes it, scrolls it into view, and marks it
7+
* as the current node so the row can show a persistent highlight. Ancestors
8+
* expand but neither flash nor highlight, to keep the "you are here" signal on
9+
* the one field the cursor sits in. Shared by field rows and inline-fragment
10+
* rows so both react to the cursor.
811
*/
912
export function useCursorReveal(
1013
fullPath: string[],
@@ -35,19 +38,16 @@ export function useCursorReveal(
3538
// hands us a fresh array on every cursor event, so re-placing the cursor on
3639
// the same field re-flashes it (handy for getting your bearings).
3740
useEffect(() => {
38-
if (!onCursorPath) {
41+
if (!isTarget) {
3942
return;
4043
}
4144
setFlash(true);
42-
// The leaf the cursor landed on is scrolled into view; ancestors only flash.
4345
// Center it vertically so it doesn't land under the sticky operation header
4446
// when scrolling up.
45-
if (isTarget) {
46-
nodeRef.current?.scrollIntoView({ block: 'center' });
47-
}
47+
nodeRef.current?.scrollIntoView({ block: 'center' });
4848
const timer = setTimeout(() => setFlash(false), 700);
4949
return () => clearTimeout(timer);
50-
}, [cursorPath, onCursorPath, isTarget]);
50+
}, [cursorPath, isTarget]);
5151

52-
return { flash, nodeRef };
52+
return { flash, current: isTarget, nodeRef };
5353
}

packages/graphiql-plugin-query-builder/src/index.css

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@
130130
padding: 1px 0;
131131
}
132132

133-
/* Briefly highlight a row (and its ancestors) when the editor cursor brings it
134-
into view, so it's obvious which field just expanded. */
133+
/* Briefly highlight the field the editor cursor lands on, so it's obvious where
134+
the cursor moved to once the tree expands down to it. */
135135
@keyframes graphiql-qb-flash {
136136
from {
137137
background-color: oklch(var(--accent-purple) / 0.22);
@@ -150,6 +150,16 @@
150150
}
151151
}
152152

153+
/* Persistent "you are here" highlight while the cursor sits on this field: a
154+
left accent bar and a faint tint. Kept visually distinct from the checked /
155+
in-document state, which only recolors the field name. */
156+
.graphiql-qb-field-row.graphiql-qb-current > .graphiql-qb-field-header,
157+
.graphiql-qb-inline-fragment.graphiql-qb-current
158+
> .graphiql-qb-inline-fragment-header {
159+
background-color: oklch(var(--accent-purple) / 0.08);
160+
box-shadow: inset 2px 0 0 oklch(var(--accent-purple));
161+
}
162+
153163
.graphiql-qb-field-header {
154164
display: flex;
155165
align-items: center;

0 commit comments

Comments
 (0)