Skip to content

Commit

Permalink
ENH Improve keyboard support
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Feb 1, 2024
1 parent 03ffff8 commit 5651f93
Show file tree
Hide file tree
Showing 5 changed files with 7,030 additions and 9 deletions.
6,724 changes: 6,723 additions & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

217 changes: 216 additions & 1 deletion client/dist/styles/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 66 additions & 2 deletions client/src/components/LinkField/LinkField.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
import React, { useState, useEffect, createContext } from 'react';
import { bindActionCreators, compose } from 'redux';
import { connect } from 'react-redux';
import { DndContext, closestCenter, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
import {
closestCenter,
DndContext,
KeyboardCode,
KeyboardSensor,
PointerSensor,
rectIntersection,
useSensor,
useSensors
} from '@dnd-kit/core';
import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { restrictToVerticalAxis, restrictToParentElement } from '@dnd-kit/modifiers';
import { injectGraphql } from 'lib/Injector';
Expand Down Expand Up @@ -39,6 +48,9 @@ const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController';
* ownerClass - class name of the owner DataObject
* ownerRelation - name of the relation on the owner DataObject
*/

// https://github.com/clauderic/dnd-kit/issues/857#issuecomment-1815355983

const LinkField = ({
value = null,
onChange,
Expand All @@ -65,6 +77,58 @@ const LinkField = ({
activationConstraint: {
distance: 10
}
}),
useSensor(KeyboardSensor, {
coordinateGetter: (event, args) => {
event.preventDefault();
const { active, over, droppableContainers } = args.context;
if (!active || !active.data || !active.data.current) {
return;
}
const items = active.data.current.sortable.items;
const overId = over ? over.id : active.id;
const overIndex = items.indexOf(overId);
const activeIndex = items.indexOf(active.id);
const directionUp = -1;
const directionDown = 1;
let nextIndex = overIndex;
let direction = directionDown;
switch (event.code) {
case KeyboardCode.Down:
case KeyboardCode.Right:
nextIndex = Math.min(overIndex + 1, items.length - 1);
break;
case KeyboardCode.Up:
case KeyboardCode.Left:
nextIndex = Math.max(0, overIndex - 1);
direction = directionUp;
break;
default:
return;
}
if (overIndex === nextIndex) {
return;
}
const sortedItems = arrayMove(items, activeIndex, overIndex);
const currentNodeIdAtNextIndex = sortedItems[nextIndex];
if (!droppableContainers.has(currentNodeIdAtNextIndex)) {
return;
}
const activeNode = droppableContainers.get(active.id).node.current;
if (!droppableContainers.has(active.id)) {
return;
}
const newNode = droppableContainers.get(currentNodeIdAtNextIndex).node.current;
const activeRect = activeNode.getBoundingClientRect();
const newRect = newNode.getBoundingClientRect();
const offset = direction === directionDown
? newRect.top - activeRect.bottom
: activeRect.top - newRect.bottom;
return {
x: 0,
y: activeRect.top + direction * (newRect.height + offset),
};
}
})
);

Expand Down Expand Up @@ -253,7 +317,7 @@ const LinkField = ({

const sortableLinks = () => {
if (isMulti && !readonly && !disabled) {
return <div className={linksClassName}>
return <div className={linksClassName} onBlur={() => setIsSorting(false)}>
<DndContext modifiers={[restrictToVerticalAxis, restrictToParentElement]}
sensors={sensors}
collisionDetection={closestCenter}
Expand Down
13 changes: 10 additions & 3 deletions client/src/components/LinkPicker/LinkPicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,26 @@
}

.link-picker__drag-handle {
display: none;
left: 5px;
position: absolute;
z-index: 100;

&:hover {
cursor: grab;
}

.font-icon-drag-handle {
opacity: 0%;

&:focus {
opacity: 100%;
}
}
}

.link-picker__link:hover {
.link-picker__drag-handle {
display: block;
.link-picker__drag-handle .font-icon-drag-handle {
opacity: 100%;
}
}

Expand Down
17 changes: 15 additions & 2 deletions client/src/components/LinkPicker/LinkPickerTitle.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,28 @@ const LinkPickerTitle = ({
if (['draft', 'modified'].includes(versionState)) {
onUnpublishedVersionedState();
}
// Remove the default tabindex="0" attribute from the sortable element because we're going to manually
// add this to the drag handle instead (probably on the <i> tabindex="0")
delete attributes.tabIndex;
return <div
className={className}
ref={setNodeRef}
style={style}
{...attributes}
{...listeners}
>
{ (isMulti && !readonly && !disabled) && <div className="link-picker__drag-handle"><i className="font-icon-drag-handle"></i></div> }
<Button disabled={loading} className={`link-picker__button ${typeIcon}`} color="secondary" onClick={stopPropagation(onClick)}>
{ (isMulti && !readonly && !disabled) && <div className="link-picker__drag-handle"><i className="font-icon-drag-handle" tabIndex="0"></i></div> }
<Button
disabled={loading}
className={`link-picker__button ${typeIcon}`}
color="secondary"
onClick={stopPropagation(onClick)}
onKeyDown={(evt) => {
// Prevent the triggering the parent's keyboard sorting handler
evt.nativeEvent.stopImmediatePropagation();
evt.stopPropagation();
}}
>
<div className="link-picker__link-detail">
<div className="link-picker__title">
<span className="link-picker__title-text">{title}</span>
Expand Down

0 comments on commit 5651f93

Please sign in to comment.