Skip to content

Commit a05cc11

Browse files
Move widgets with the keyboard
1 parent 0839062 commit a05cc11

File tree

2 files changed

+100
-1
lines changed

2 files changed

+100
-1
lines changed

src/dd-draggable.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
4444
protected dragEls: HTMLElement[];
4545
/** @internal true while we are dragging an item around */
4646
protected dragging: boolean;
47+
48+
/** @internal true while we are dragging an item around */
49+
protected keyboardSelected: HTMLElement;
4750
/** @internal last drag event */
4851
protected lastDrag: DragEvent;
4952
/** @internal */
@@ -74,6 +77,9 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
7477
}
7578
// create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions)
7679
this._mouseDown = this._mouseDown.bind(this);
80+
this._mouseKeyDown = this._mouseKeyDown.bind(this);
81+
this._keyMove = this._keyMove.bind(this);
82+
this._keyUp = this._keyUp.bind(this);
7783
this._mouseMove = this._mouseMove.bind(this);
7884
this._mouseUp = this._mouseUp.bind(this);
7985
this._keyEvent = this._keyEvent.bind(this);
@@ -92,6 +98,7 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
9298
if (this.disabled === false) return;
9399
super.enable();
94100
this.dragEls.forEach(dragEl => {
101+
dragEl.addEventListener('keydown', this._mouseKeyDown)
95102
dragEl.addEventListener('mousedown', this._mouseDown);
96103
if (isTouch) {
97104
dragEl.addEventListener('touchstart', touchstart);
@@ -131,6 +138,98 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
131138
return this;
132139
}
133140

141+
protected _elCoordinates(element: HTMLElement) {
142+
const rect = element.getBoundingClientRect();
143+
const clientX = rect.left;
144+
const clientY = rect.top;
145+
const offsetX = element.offsetLeft;
146+
const offsetY = element.offsetTop;
147+
const pageX = window.scrollX + rect.left;
148+
const pageY = window.scrollY + rect.top;
149+
const screenX = window.screenX + rect.left;
150+
const screenY = window.screenY + rect.top;
151+
152+
return { clientX: clientX,
153+
clientY: clientY,
154+
offsetX: offsetX,
155+
offsetY: offsetY,
156+
pageX: pageX,
157+
pageY: pageY,
158+
screenX: screenX,
159+
screenY: screenY }
160+
}
161+
162+
protected _elNewCoordinates(event: KeyboardEvent, element: HTMLElement) {
163+
let coordinates = this._elCoordinates(element)
164+
165+
switch (event.code) {
166+
case 'ArrowRight':
167+
coordinates.clientX = coordinates.clientX + 400
168+
break
169+
case 'ArrowLeft':
170+
coordinates.clientX = coordinates.clientX - 400
171+
break
172+
case 'ArrowUp':
173+
coordinates.clientY = coordinates.clientY - 400
174+
break
175+
case 'ArrowDown':
176+
coordinates.clientY = coordinates.clientY + 400
177+
break
178+
}
179+
return coordinates
180+
}
181+
182+
protected _mouseKeyDown(e: KeyboardEvent): void {
183+
if(e.code === 'Space') {
184+
e.preventDefault()
185+
186+
const handle = e.target as HTMLElement
187+
const item: HTMLElement = handle?.closest('.grid-stack-item')
188+
this.keyboardSelected = item
189+
item.classList.add('grid-stack-item-selected')
190+
191+
e.target.dispatchEvent(new MouseEvent('mousedown'))
192+
document.addEventListener('keyup', this._keyUp)
193+
}
194+
}
195+
196+
protected _keyUp() {
197+
document.removeEventListener('keyup', this._keyUp)
198+
document.addEventListener('keydown', this._keyMove)
199+
}
200+
201+
protected _selectedItem (element: HTMLElement): HTMLElement {
202+
const items = document.querySelectorAll('.grid-stack-item')
203+
204+
return Array.from(items).filter(item => item === element)[0] as HTMLElement
205+
}
206+
207+
protected _keyMove(e: KeyboardEvent) {
208+
if (e.code === 'Space') {
209+
e.preventDefault()
210+
211+
this.keyboardSelected.classList.remove('grid-stack-item-selected')
212+
this.keyboardSelected.dispatchEvent(new MouseEvent('mouseup'))
213+
document.removeEventListener('keydown', this._keyMove)
214+
215+
return
216+
}
217+
218+
if (e.code === 'ArrowRight' ||
219+
e.code === 'ArrowLeft' ||
220+
e.code === 'ArrowUp' ||
221+
e.code === 'ArrowDown') {
222+
e.target.dispatchEvent(new MouseEvent('mousemove', { ...this._elCoordinates(this.keyboardSelected)}))
223+
e.target.dispatchEvent(new MouseEvent('mousemove', { ...this._elNewCoordinates(e, this.keyboardSelected)}))
224+
e.target.dispatchEvent(new MouseEvent('mouseup'))
225+
226+
this.keyboardSelected = this._selectedItem(this.keyboardSelected)
227+
const handle: HTMLElement = this.keyboardSelected.querySelector('.grid-item-handle')
228+
229+
handle?.dispatchEvent(new MouseEvent('mousedown'))
230+
}
231+
}
232+
134233
/** @internal call when mouse goes down before a dragstart happens */
135234
protected _mouseDown(e: MouseEvent): boolean {
136235
// don't let more than one widget handle mouseStart

src/gridstack-engine.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ export class GridStackEngine {
424424
copy.w = Math.min(this.defaultColumn, copy.w || 1);
425425
this.cacheOneLayout(copy, this.defaultColumn);
426426
}
427-
427+
428428
if (node.w > this.column) {
429429
node.w = this.column;
430430
} else if (node.w < 1) {

0 commit comments

Comments
 (0)