Skip to content

Commit

Permalink
Implement all the ARIA keyboard interactions, and selection follows f…
Browse files Browse the repository at this point in the history
…ocus
  • Loading branch information
ericallam committed Mar 18, 2022
1 parent 084a93e commit f9671c6
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 56 deletions.
13 changes: 13 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": ["$tsc-watch"],
"group": "build",
"label": "tsc: watch - tsconfig.json"
}
]
}
2 changes: 1 addition & 1 deletion app/components/ColumnItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function ColumnItem({ item }: { item: ColumnViewNode }) {
return (
<div
className={`flex h-9 items-center justify-items-stretch mx-1 px-1 py-1 my-1 rounded-sm ${stateStyle}`}
onClick={() => goToNodeId(id)}
onClick={() => goToNodeId(id, "columnView")}
ref={htmlElement}
>
<div className="w-4 flex-none flex-col justify-items-center">
Expand Down
2 changes: 1 addition & 1 deletion app/components/Home/HomeInfoBoxSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function HomeInfoBoxSectionContent() {

useEffect(() => {
const selectedPath = infoBoxData[index].highlight;
api.goToNodeId(selectedPath);
api.goToNodeId(selectedPath, "home");
}, [index]);

const resetInterval = () => {
Expand Down
2 changes: 1 addition & 1 deletion app/components/JsonEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function JsonEditor() {

const path = JSONHeroPath.fromPointer(pointer);

goToNodeId(path.toString());
goToNodeId(path.toString(), "editor");
},
[goToNodeId]
);
Expand Down
63 changes: 52 additions & 11 deletions app/components/JsonTreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import { Body } from "./Primitives/Body";
import { Mono } from "./Primitives/Mono";

export function JsonTreeView() {
const { selectedNodeId } = useJsonColumnViewState();
const { selectedNodeId, selectedNodeSource } = useJsonColumnViewState();
const { goToNodeId } = useJsonColumnViewAPI();

const { tree, parentRef } = useJsonTreeViewContext();

// Scroll to the selected node when this component is first rendered.
const scrolledToNodeRef = useRef(false);

useEffect(() => {
Expand All @@ -24,6 +25,43 @@ export function JsonTreeView() {
}
}, [selectedNodeId, scrolledToNodeRef]);

// This focuses and scrolls to the selected node when the selectedNodeId
// is set from a source other than this tree (e.g. the search bar, path bar, related values).
useEffect(() => {
if (
tree.focusedNodeId &&
selectedNodeId &&
tree.focusedNodeId !== selectedNodeId
) {
if (selectedNodeSource !== "tree") {
if (selectedNodeId === "$") {
tree.focusFirst();
} else {
tree.focusNode(selectedNodeId);
tree.scrollToNode(selectedNodeId);
}
}
}
}, [tree.focusedNodeId, goToNodeId, selectedNodeId, selectedNodeSource]);

// This is what syncs the tree view's focused node to the column view selected node
const previousFocusedNodeId = useRef<string | null>(null);

useEffect(() => {
if (!previousFocusedNodeId.current) {
previousFocusedNodeId.current = tree.focusedNodeId;
return;
}

if (
tree.focusedNodeId &&
previousFocusedNodeId.current !== tree.focusedNodeId
) {
previousFocusedNodeId.current = tree.focusedNodeId;
goToNodeId(tree.focusedNodeId, "tree");
}
}, [previousFocusedNodeId, tree.focusedNodeId, tree.focusNode, goToNodeId]);

return (
<div
className="text-white w-full"
Expand All @@ -44,7 +82,6 @@ export function JsonTreeView() {
virtualNode={virtualNode}
key={virtualNode.node.id}
onToggle={() => tree.toggleNode(virtualNode.node.id)}
onClick={() => goToNodeId(virtualNode.node.id)}
selectedNodeId={selectedNodeId}
/>
))}
Expand All @@ -55,20 +92,29 @@ export function JsonTreeView() {

function TreeViewNode({
virtualNode,
onClick,
onToggle,
selectedNodeId,
}: {
virtualNode: VirtualNode<JsonTreeViewNode>;
selectedNodeId?: string;
onClick?: (node: JsonTreeViewNode) => void;
onToggle?: (node: JsonTreeViewNode) => void;
}) {
const { tree } = useJsonTreeViewContext();

const { node, virtualItem, depth } = virtualNode;

const indentClassName = computeTreeNodePaddingClass(depth);

const isSelected = selectedNodeId === node.id;
const isFocused = tree.focusedNodeId === node.id;

const elementRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (elementRef.current && isFocused) {
elementRef.current.focus();
}
}, [elementRef, isFocused]);

return (
<div
Expand All @@ -82,12 +128,7 @@ function TreeViewNode({
}}
key={virtualNode.node.id}
{...virtualNode.getItemProps()}
onClick={(e) => {
if (onClick) {
e.stopPropagation();
onClick(virtualNode.node);
}
}}
ref={elementRef}
>
<div
className={`h-full flex select-none ${
Expand All @@ -103,7 +144,7 @@ function TreeViewNode({
<span
onClick={(e) => {
if (onToggle) {
e.stopPropagation();
e.preventDefault();
onToggle(node);
}
}}
Expand Down
2 changes: 1 addition & 1 deletion app/components/PathBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function PathBarLink({
: "hover:bg-slate-300 px-2 py-[3px] rounded-sm transition dark:hover:bg-white dark:hover:bg-opacity-[5%]"
}`}
style={{ flexShrink: 1 }}
onClick={() => goToNodeId(node.id)}
onClick={() => goToNodeId(node.id, "pathBar")}
>
<div className="w-4 flex-shrink-[0.5] flex-grow-0 flex-col justify-items-center whitespace-nowrap overflow-x-hidden transition dark:text-slate-400">
{node.icon && <node.icon className="h-3 w-3" />}
Expand Down
3 changes: 2 additions & 1 deletion app/components/PathPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ export function PathPreview({
: "disabled"
}`}
onClick={() =>
isEnabled && goToNodeId(components[components.length - 1].id)
isEnabled &&
goToNodeId(components[components.length - 1].id, "relatedValues")
}
>
<div
Expand Down
2 changes: 1 addition & 1 deletion app/hooks/useJsonColumnView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function JsonColumnViewProvider({ children }: { children: ReactNode }) {
const restoredState = JSON.parse(storage) as ColumnViewInstanceState;
if (!restoredState.selectedNodeId) return;

api.goToNodeId(restoredState.selectedNodeId);
api.goToNodeId(restoredState.selectedNodeId, "localStorage");
}, [doc.id, isStateRestored.current, state, api]);

useEffect(() => {
Expand Down
Loading

0 comments on commit f9671c6

Please sign in to comment.