Skip to content

Commit

Permalink
#5429 Splitter | Keyboard support added
Browse files Browse the repository at this point in the history
  • Loading branch information
yigitfindikli committed Jan 4, 2024
1 parent ee491c9 commit 28a86a9
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 25 deletions.
117 changes: 103 additions & 14 deletions components/lib/splitter/Splitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const Splitter = React.memo(
const nextPanelSize = React.useRef(null);
const nextPanelSizeNew = React.useRef(null);
const prevPanelIndex = React.useRef(null);
const timer = React.useRef(null);
const [panelSizes, setPanelSizes] = React.useState([]);
const [nested, setNested] = React.useState(false);
const isStateful = props.stateKey != null;
Expand Down Expand Up @@ -130,19 +131,27 @@ export const Splitter = React.memo(
if (stateString) setPanelSizes(JSON.parse(stateString));
}, [getStorage, props.stateKey]);

const onResizeStart = (event, index) => {
const onResizeStart = (event, index, isKeyDown) => {
gutterRef.current = gutterRefs.current[index];
let pageX = event.type === 'touchstart' ? event.touches[0].pageX : event.pageX;
let pageY = event.type === 'touchstart' ? event.touches[0].pageY : event.pageY;
const horizontal = props.layout === 'horizontal';

size.current = props.layout === 'horizontal' ? DomHandler.getWidth(elementRef.current) : DomHandler.getHeight(elementRef.current);
size.current = horizontal ? DomHandler.getWidth(elementRef.current) : DomHandler.getHeight(elementRef.current);
dragging.current = true;
startPos.current = props.layout === 'horizontal' ? pageX : pageY;
startPos.current = horizontal ? pageX : pageY;
prevPanelElement.current = gutterRef.current.previousElementSibling;
nextPanelElement.current = gutterRef.current.nextElementSibling;
prevPanelSize.current = (100 * (props.layout === 'horizontal' ? DomHandler.getOuterWidth(prevPanelElement.current, true) : DomHandler.getOuterHeight(prevPanelElement.current, true))) / size.current;

if (isKeyDown) {
prevPanelSize.current = horizontal ? DomHandler.getOuterWidth(prevPanelElement.current, true) : DomHandler.getOuterHeight(prevPanelElement.current, true);
nextPanelSize.current = horizontal ? DomHandler.getOuterWidth(nextPanelElement.current, true) : DomHandler.getOuterHeight(nextPanelElement.current, true);
} else {
prevPanelSize.current = (100 * (horizontal ? DomHandler.getOuterWidth(prevPanelElement.current, true) : DomHandler.getOuterHeight(prevPanelElement.current, true))) / size.current;
nextPanelSize.current = (100 * (horizontal ? DomHandler.getOuterWidth(nextPanelElement.current, true) : DomHandler.getOuterHeight(nextPanelElement.current, true))) / size.current;
}

prevPanelSizeNew.current = prevPanelSize.current;
nextPanelSize.current = (100 * (props.layout === 'horizontal' ? DomHandler.getOuterWidth(nextPanelElement.current, true) : DomHandler.getOuterHeight(nextPanelElement.current, true))) / size.current;
nextPanelSizeNew.current = nextPanelSize.current;
prevPanelIndex.current = index;
!isUnstyled() && DomHandler.addClass(gutterRef.current, 'p-splitter-gutter-resizing');
Expand All @@ -151,16 +160,27 @@ export const Splitter = React.memo(
elementRef.current.setAttribute('data-p-splitter-resizing', true);
};

const onResize = (event) => {
let newPos;
const onResize = (event, step = 0, isKeyDown = false) => {
let newPos, newNextPanelSize, newPrevPanelSize;
let horizontal = props.layout === 'horizontal';
let pageX = event.type === 'touchmove' ? event.touches[0].pageX : event.pageX;
let pageY = event.type === 'touchmove' ? event.touches[0].pageY : event.pageY;

if (props.layout === 'horizontal') newPos = (pageX * 100) / size.current - (startPos.current * 100) / size.current;
else newPos = (pageY * 100) / size.current - (startPos.current * 100) / size.current;
if (isKeyDown) {
if (horizontal) {
newPrevPanelSize = (100 * (prevPanelSize.current + step)) / size.current;
newNextPanelSize = (100 * (nextPanelSize.current - step)) / size.current;
} else {
newPrevPanelSize = (100 * (prevPanelSize.current - step)) / size.current;
newNextPanelSize = (100 * (nextPanelSize.current + step)) / size.current;
}
} else {
if (horizontal) newPos = (pageX * 100) / size.current - (startPos.current * 100) / size.current;
else newPos = (pageY * 100) / size.current - (startPos.current * 100) / size.current;

let newPrevPanelSize = prevPanelSize.current + newPos;
let newNextPanelSize = nextPanelSize.current - newPos;
newPrevPanelSize = prevPanelSize.current + newPos;
newNextPanelSize = nextPanelSize.current - newPos;
}

if (validateResize(newPrevPanelSize, newNextPanelSize)) {
prevPanelSizeNew.current = newPrevPanelSize;
Expand Down Expand Up @@ -192,19 +212,86 @@ export const Splitter = React.memo(
});

!isUnstyled() && DomHandler.removeClass(gutterRef.current, 'p-splitter-gutter-resizing');
gutterRef.current.setAttribute('data-p-splitter-gutter-resizing', false);
gutterRefs.current && Object.keys(gutterRefs.current).forEach((key) => gutterRefs.current[key].setAttribute('data-p-splitter-gutter-resizing', false));
!isUnstyled() && DomHandler.removeClass(elementRef.current, 'p-splitter-resizing');
elementRef.current.setAttribute('data-p-splitter-resizing', false);
clear();
};

const onGutterKeyUp = () => {
clearTimer();
onResizeEnd();
};

const onGutterKeyDown = (event, index) => {
switch (event.code) {
case 'ArrowLeft': {
if (props.layout === 'horizontal') {
setTimer(event, index, props.step * -1);
}

event.preventDefault();
break;
}

case 'ArrowRight': {
if (props.layout === 'horizontal') {
setTimer(event, index, props.step);
}

event.preventDefault();
break;
}

case 'ArrowDown': {
if (props.layout === 'vertical') {
setTimer(event, index, props.step * -1);
}

event.preventDefault();
break;
}

case 'ArrowUp': {
if (props.layout === 'vertical') {
setTimer(event, index, props.step);
}

event.preventDefault();
break;
}

default:
//no op
break;
}
};

const repeat = (event, index, step) => {
onResizeStart(event, index, true);
onResize(event, step, true);
};

const setTimer = (event, index, step) => {
clearTimer();
timer.current = setTimeout(() => {
repeat(event, index, step);
}, 40);
};

const clearTimer = () => {
if (timer.current) {
clearTimeout(timer.current);
}
};

const onGutterMouseDown = (event, index) => {
onResizeStart(event, index);
onResizeStart(event, index, false);
bindMouseListeners();
};

const onGutterTouchStart = (event, index) => {
onResizeStart(event, index);
onResizeStart(event, index, false);

window.addEventListener('touchmove', onGutterTouchMove, { passive: false, cancelable: false });
window.addEventListener('touchend', onGutterTouchEnd);
Expand Down Expand Up @@ -253,6 +340,8 @@ export const Splitter = React.memo(
className: cx('gutter'),
style: props.layout === 'horizontal' ? { width: props.gutterSize + 'px' } : { height: props.gutterSize + 'px' },
onMouseDown: (event) => onGutterMouseDown(event, index),
onKeyDown: (event) => onGutterKeyDown(event, index),
onKeyUp: onGutterKeyUp,
onTouchStart: (event) => onGutterTouchStart(event, index),
onTouchMove: (event) => onGutterTouchMove(event),
onTouchEnd: (event) => onGutterTouchEnd(event),
Expand Down
23 changes: 12 additions & 11 deletions components/lib/splitter/SplitterBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ const styles = `
display: flex;
flex-wrap: nowrap;
}
.p-splitter-vertical {
flex-direction: column;
}
.p-splitter-panel {
flex-grow: 1;
}
.p-splitter-panel-nested {
display: flex;
}
.p-splitter-panel .p-splitter {
flex-grow: 1;
border: 0 none;
}
.p-splitter-gutter {
flex-grow: 0;
flex-shrink: 0;
Expand All @@ -42,30 +42,30 @@ const styles = `
justify-content: center;
cursor: col-resize;
}
.p-splitter-horizontal.p-splitter-resizing {
cursor: col-resize;
user-select: none;
}
.p-splitter-horizontal > .p-splitter-gutter > .p-splitter-gutter-handle {
height: 24px;
width: 100%;
}
.p-splitter-horizontal > .p-splitter-gutter {
cursor: col-resize;
}
.p-splitter-vertical.p-splitter-resizing {
cursor: row-resize;
user-select: none;
}
.p-splitter-vertical > .p-splitter-gutter {
cursor: row-resize;
}
.p-splitter-vertical > .p-splitter-gutter > .p-splitter-gutter-handle {
width: 24px;
height: 100%;
Expand All @@ -80,6 +80,7 @@ export const SplitterBase = ComponentBase.extend({
className: null,
gutterSize: 4,
id: null,
step: 5,
layout: 'horizontal',
onResizeEnd: null,
stateKey: null,
Expand Down
5 changes: 5 additions & 0 deletions components/lib/splitter/splitter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ interface SplitterPanelProps {
* @readonly
*/
children?: React.ReactNode | undefined;
/**
* Step factor to increment/decrement the size of the panels while pressing the arrow keys.
* @defaultValue 5
*/
step?: number | undefined;
/**
* Uses to pass attributes to DOM elements inside the component.
* @type {SplitterPanelPassThroughOptions}
Expand Down

0 comments on commit 28a86a9

Please sign in to comment.