keyboard.js (5.5 KB)


  1 // Keyboard: key handler state and handleKeyDown dispatch
  2 
  3 var keyHandler = {
  4 	backspace: {
  5 		canDelete: true,
  6 		blocked: false
  7 	},
  8 	enter: {
  9 		canAdd: true,
 10 		blocked: false
 11 	},
 12 	arrowDown: {
 13 		canAdd: true,
 14 		blocked: false
 15 	},
 16 	shiftArrowDown: {
 17 		blocked: false
 18 	},
 19 	shiftEnter: {
 20 		pressed: false
 21 	},
 22 	shiftRight: {
 23 		pressed: false
 24 	},
 25 	shiftLeft: {
 26 		pressed: false
 27 	}
 28 };
 29 
 30 function handleKeyDown(e, task) {
 31 	var cmd = e.metaKey || e.ctrlKey;
 32 	var hasMultiSelect = state.multiSelectedIds.length > 1;
 33 
 34 	// Alt+Up/Down: extend multi-selection
 35 	if (e.key === 'ArrowUp' && e.altKey && !cmd && !e.shiftKey) {
 36 		e.preventDefault();
 37 		extendMultiSelect(task, 'up');
 38 		return;
 39 	} else if (e.key === 'ArrowDown' && e.altKey && !cmd && !e.shiftKey) {
 40 		e.preventDefault();
 41 		extendMultiSelect(task, 'down');
 42 		return;
 43 	}
 44 
 45 	// Shift+Enter: toggle task state (bulk if multi-selected)
 46 	if (e.key === 'Enter' && e.shiftKey) {
 47 		e.preventDefault();
 48 		if (!keyHandler.shiftEnter.pressed) {
 49 			keyHandler.shiftEnter.pressed = true;
 50 			if (hasMultiSelect) {
 51 				bulkToggleTaskState();
 52 			} else {
 53 				toggleTaskState(task);
 54 			}
 55 		}
 56 	// Cmd+Up: push into task above
 57 	} else if (e.key === 'ArrowUp' && cmd && !e.shiftKey && !e.altKey) {
 58 		e.preventDefault();
 59 		if (!e.repeat) {
 60 			var success = hasMultiSelect ? pushMultiSelectedIntoTarget('up') : pushSubtaskIntoTarget(task, 'up');
 61 			if (!success) {
 62 				hasMultiSelect ? shakeAllSelected('vertical') : applyShakeAnimation(task.id, 'vertical');
 63 			}
 64 		}
 65 	// Cmd+Down: push into task below
 66 	} else if (e.key === 'ArrowDown' && cmd && !e.shiftKey && !e.altKey) {
 67 		e.preventDefault();
 68 		if (!e.repeat) {
 69 			var success = hasMultiSelect ? pushMultiSelectedIntoTarget('down') : pushSubtaskIntoTarget(task, 'down');
 70 			if (!success) {
 71 				hasMultiSelect ? shakeAllSelected('vertical') : applyShakeAnimation(task.id, 'vertical');
 72 			}
 73 		}
 74 	} else if (e.key === 'ArrowRight' && cmd && !e.shiftKey && !e.altKey) {
 75 		e.preventDefault();
 76 		hasMultiSelect ? shakeAllSelected() : applyShakeAnimation(task.id);
 77 	// Cmd+Left: pull out one level
 78 	} else if (e.key === 'ArrowLeft' && cmd && !e.shiftKey && !e.altKey) {
 79 		e.preventDefault();
 80 		if (!e.repeat) {
 81 			var success = hasMultiSelect ? pullMultiSelectedOutLayer() : pullSubtaskOutLayer(task);
 82 			if (!success) {
 83 				hasMultiSelect ? shakeAllSelected() : applyShakeAnimation(task.id);
 84 			}
 85 		}
 86 	// Cmd+Shift+Up: push and navigate
 87 	} else if (e.key === 'ArrowUp' && cmd && e.shiftKey && !e.altKey) {
 88 		e.preventDefault();
 89 		if (!e.repeat) {
 90 			var success = hasMultiSelect ? pushMultiSelectedIntoTarget('up', true) : pushSubtaskIntoTarget(task, 'up', true);
 91 			if (!success) {
 92 				hasMultiSelect ? shakeAllSelected('vertical') : applyShakeAnimation(task.id, 'vertical');
 93 			}
 94 		}
 95 	// Cmd+Shift+Down: push and navigate
 96 	} else if (e.key === 'ArrowDown' && cmd && e.shiftKey && !e.altKey) {
 97 		e.preventDefault();
 98 		if (!e.repeat) {
 99 			var success = hasMultiSelect ? pushMultiSelectedIntoTarget('down', true) : pushSubtaskIntoTarget(task, 'down', true);
100 			if (!success) {
101 				hasMultiSelect ? shakeAllSelected('vertical') : applyShakeAnimation(task.id, 'vertical');
102 			}
103 		}
104 	// Cmd+Shift+Left: pull and navigate
105 	} else if (e.key === 'ArrowLeft' && cmd && e.shiftKey && !e.altKey) {
106 		e.preventDefault();
107 		if (!e.repeat) {
108 			var success = hasMultiSelect ? pullMultiSelectedOutLayer(true) : pullSubtaskOutLayer(task, true);
109 			if (!success) {
110 				hasMultiSelect ? shakeAllSelected() : applyShakeAnimation(task.id);
111 			}
112 		}
113 	} else if (e.key === 'ArrowRight' && cmd && e.shiftKey && !e.altKey) {
114 		e.preventDefault();
115 		hasMultiSelect ? shakeAllSelected() : applyShakeAnimation(task.id);
116 	// Plain Up/Down: navigate (clears multi-select)
117 	} else if (e.key === 'ArrowUp' && !e.shiftKey && !e.altKey) {
118 		e.preventDefault();
119 		if (hasMultiSelect) clearMultiSelect();
120 		navigateTasks('up');
121 	} else if (e.key === 'ArrowDown' && !e.shiftKey && !e.altKey) {
122 		e.preventDefault();
123 		if (hasMultiSelect) clearMultiSelect();
124 		navigateTasks('down');
125 	// Shift+Up/Down: move task
126 	} else if (e.key === 'ArrowUp' && e.shiftKey && !e.altKey) {
127 		e.preventDefault();
128 		if (hasMultiSelect) {
129 			if (!moveMultiSelected('up')) shakeAllSelected('vertical');
130 		} else {
131 			if (!moveSubtask(task, 'up')) applyShakeAnimation(task.id, 'vertical');
132 		}
133 	} else if (e.key === 'ArrowDown' && e.shiftKey && !e.altKey) {
134 		e.preventDefault();
135 		if (hasMultiSelect) {
136 			if (!moveMultiSelected('down')) shakeAllSelected('vertical');
137 		} else {
138 			if (!moveSubtask(task, 'down')) applyShakeAnimation(task.id, 'vertical');
139 		}
140 	// Shift+Right: navigate into subtask (blocked during multi-select)
141 	} else if (e.key === 'ArrowRight' && e.shiftKey && !e.altKey) {
142 		e.preventDefault();
143 		if (hasMultiSelect) {
144 			if (!keyHandler.shiftRight.pressed) {
145 				keyHandler.shiftRight.pressed = true;
146 				shakeAllSelected();
147 			}
148 		} else if (!keyHandler.shiftRight.pressed) {
149 			keyHandler.shiftRight.pressed = true;
150 			if (task !== state.currentTask) {
151 				navigateIntoSubtask(task);
152 			} else {
153 				applyShakeAnimation(task.id);
154 			}
155 		}
156 	// Shift+Left: navigate to parent
157 	} else if (e.key === 'ArrowLeft' && e.shiftKey && !e.altKey) {
158 		e.preventDefault();
159 		if (hasMultiSelect && state.currentTask.id === 'root') {
160 			if (!keyHandler.shiftLeft.pressed) {
161 				keyHandler.shiftLeft.pressed = true;
162 				shakeAllSelected();
163 			}
164 		} else if (hasMultiSelect) {
165 			clearMultiSelect();
166 			if (!keyHandler.shiftLeft.pressed) {
167 				keyHandler.shiftLeft.pressed = true;
168 				navigateToParentTask();
169 			}
170 		} else if (!keyHandler.shiftLeft.pressed) {
171 			keyHandler.shiftLeft.pressed = true;
172 			navigateToParentTask();
173 		}
174 	}
175 }