@atlaskit/editor-plugin-quick-insert
Version:
Quick insert plugin for @atlaskit/editor-core
85 lines (82 loc) • 2.88 kB
JavaScript
export const RETURN_TO_SEARCH = -1;
// Options whose tops are within a pixel share a row, absorbing sub-pixel layout differences.
const ROW_TOLERANCE = 1;
const getCenter = ({
left,
width
}) => left + width / 2;
const findNearestInRow = (options, rowTop, center) => {
var _nearest;
let nearest;
options.forEach((option, index) => {
if (option.isDisabled || Math.abs(option.top - rowTop) > ROW_TOLERANCE) {
return;
}
const distance = Math.abs(getCenter(option) - center);
if (!nearest || distance < nearest.distance) {
nearest = {
distance,
index
};
}
});
return (_nearest = nearest) === null || _nearest === void 0 ? void 0 : _nearest.index;
};
/**
* Finds the option an arrow key moves to in the rendered result grids. Rows can have different
* column counts, so vertical moves pick the nearest enabled option in the next row that has one.
* Returns `RETURN_TO_SEARCH` when moving before the first option, or `undefined` to stay put.
*/
export const getArrowKeyTargetIndex = (options, currentIndex, key) => {
if (currentIndex < 0) {
// Only ArrowDown enters the results from search; the other keys keep their native caret moves.
if (key !== 'ArrowDown') {
return undefined;
}
const firstEnabled = options.findIndex(({
isDisabled
}) => !isDisabled);
return firstEnabled < 0 ? undefined : firstEnabled;
}
if (key === 'ArrowLeft' || key === 'ArrowRight') {
const step = key === 'ArrowRight' ? 1 : -1;
for (let index = currentIndex + step; index >= 0 && index < options.length; index += step) {
if (!options[index].isDisabled) {
return index;
}
}
return key === 'ArrowLeft' ? RETURN_TO_SEARCH : undefined;
}
// Rows can hold different numbers of tiles (2 wide tiles above 3 regular ones in Recommended), so
// the row below or above, and the tile nearest the current one in it, come from screen positions.
const positions = options.map(({
getRect,
isDisabled
}) => {
const {
left,
top,
width
} = getRect();
return {
isDisabled,
left,
top,
width
};
});
const current = positions[currentIndex];
const isDown = key === 'ArrowDown';
// The rows past the current one in the direction of travel, each identified by its top edge,
// nearest first so the first row with an enabled tile wins.
const rowsAhead = [...new Set(positions.map(({
top
}) => top).filter(top => isDown ? top > current.top + ROW_TOLERANCE : top < current.top - ROW_TOLERANCE))].sort((first, second) => isDown ? first - second : second - first);
for (const rowTop of rowsAhead) {
const target = findNearestInRow(positions, rowTop, getCenter(current));
if (target !== undefined) {
return target;
}
}
return isDown ? undefined : RETURN_TO_SEARCH;
};