Skip to content

Commit 7e5c143

Browse files
committed
Add column keyboard navigation
1 parent c146235 commit 7e5c143

File tree

4 files changed

+55
-15
lines changed

4 files changed

+55
-15
lines changed

packages/column-views/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import styles from "./column.module.sass";
1414
import {
1515
CompositeUnitsColumn,
1616
TrackedLabeledUnit,
17+
UnitKeyboardNavigation,
1718
useUnitSelectionDispatch,
1819
} from "./units";
1920
import { IUnit } from "./units/types";
@@ -230,6 +231,7 @@ function Column(
230231
mergeOverlappingSections?: boolean;
231232
showLabelColumn?: boolean;
232233
columnRef?: RefObject<HTMLDivElement>;
234+
keyboardNavigation?: boolean;
233235
}
234236
) {
235237
const {
@@ -242,6 +244,7 @@ function Column(
242244
className: baseClassName,
243245
showLabelColumn = true,
244246
mergeOverlappingSections = true,
247+
keyboardNavigation = false,
245248
columnRef,
246249
children,
247250
...rest
@@ -298,6 +301,7 @@ function Column(
298301
]);
299302
})
300303
),
304+
h.if(keyboardNavigation)(UnitKeyboardNavigation, { units: data }),
301305
children,
302306
])
303307
);

packages/column-views/src/units/boxes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ function useUnitSelectionManager(
141141

142142
useEffect(() => {
143143
if (!selected) return;
144+
// In case we haven't set the position of the unit (if we don't have a target), set the selected unit
145+
dispatch(unit, ref.current, null);
146+
147+
// Scroll the unit into view
144148
ref.current?.scrollIntoView({
145149
behavior: "smooth",
146150
block: "center",

packages/column-views/src/units/selection.ts

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { BaseUnit } from "@macrostrat/api-types";
22
import h from "@macrostrat/hyper";
3-
import { getQueryString, setQueryString } from "@macrostrat/ui-components";
3+
import {
4+
getQueryString,
5+
setQueryString,
6+
useKeyHandler,
7+
} from "@macrostrat/ui-components";
48
import {
59
Dispatch,
610
SetStateAction,
@@ -15,8 +19,8 @@ import {
1519

1620
type UnitSelectDispatch = (
1721
unit: BaseUnit | null,
18-
target: HTMLElement,
19-
event: Event
22+
target: HTMLElement | null,
23+
event: Event | null
2024
) => void;
2125

2226
const UnitSelectionContext = createContext<BaseUnit | null>(null);
@@ -73,11 +77,6 @@ function BaseUnitSelectionProvider<T extends BaseUnit>({
7377
const _onUnitSelected = useCallback(
7478
(u: T, target: HTMLElement, event: Event) => {
7579
let newUnit = u;
76-
if (u == unit) {
77-
// If the same unit is selected, deselect it
78-
newUnit = null;
79-
}
80-
8180
setUnit(newUnit);
8281
onUnitSelected?.(newUnit, target, event);
8382
},
@@ -129,9 +128,34 @@ export function useColumnNav(
129128
return [columnArgs, setCurrentColumn];
130129
}
131130

132-
function ColumnNavProvider({ children, ...defaultArgs }) {
131+
export function ColumnNavProvider({ children, ...defaultArgs }) {
133132
const value = useColumnNav(defaultArgs);
134133
return h(ColumnNavCtx.Provider, { value }, children);
135134
}
136135

137-
export { ColumnNavProvider };
136+
export function UnitKeyboardNavigation<T extends BaseUnit>({
137+
units,
138+
}: {
139+
units: T[];
140+
}) {
141+
const selectedUnit = useSelectedUnit();
142+
const selectUnit = useUnitSelectionDispatch();
143+
144+
const ix = units.findIndex((unit) => unit.unit_id === selectedUnit?.unit_id);
145+
146+
const keyMap = {
147+
38: ix - 1,
148+
40: ix + 1,
149+
};
150+
151+
useKeyHandler(
152+
(event) => {
153+
const nextIx = keyMap[event.keyCode];
154+
if (nextIx == null || nextIx < 0 || nextIx >= units.length) return;
155+
selectUnit(units[nextIx], null, null);
156+
event.stopPropagation();
157+
},
158+
[units, ix]
159+
);
160+
return null;
161+
}

packages/column-views/stories/column.stories.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import {
1111
Column,
1212
preprocessUnits,
13+
UnitKeyboardNavigation,
1314
UnitSelectionProvider,
1415
useSelectedUnit,
1516
} from "../src";
@@ -152,13 +153,13 @@ export function WithUnitSelectionPopover() {
152153
return h(
153154
UnitSelectionProvider,
154155
{
155-
onUnitSelected: (unit, target: SVGElement | HTMLElement) => {
156+
onUnitSelected: (unit, target: SVGElement | HTMLElement | null) => {
156157
if (unit == null) {
157158
setPosition(null);
158159
return;
159160
}
160161
const el: HTMLElement = ref.current;
161-
if (el == null) return;
162+
if (el == null || target == null) return;
162163
const rect = el.getBoundingClientRect();
163164
const targetRect = target.getBoundingClientRect();
164165
setPosition({
@@ -169,8 +170,15 @@ export function WithUnitSelectionPopover() {
169170
});
170171
},
171172
},
172-
h(BasicColumn, { id: 432, showLabelColumn: true, columnRef: ref }, [
173-
h(UnitSelectionPopover, { position }),
174-
])
173+
h(
174+
BasicColumn,
175+
{
176+
id: 432,
177+
showLabelColumn: true,
178+
columnRef: ref,
179+
keyboardNavigation: true,
180+
},
181+
[h(UnitSelectionPopover, { position })]
182+
)
175183
);
176184
}

0 commit comments

Comments
 (0)