[カスタム絵文字beta]MacのCmdキー対応とCtrl/Cmd+Arrowキー対応 (#16621)
* [カスタム絵文字beta]MacのCmdキー対応とCtrl/Cmd+Arrowキー対応 * Update packages/frontend/src/components/grid/MkGrid.vue Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
This commit is contained in:
parent
37526de323
commit
41aa0c8efe
|
|
@ -71,7 +71,7 @@ import {
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { createColumn } from '@/components/grid/column.js';
|
import { createColumn } from '@/components/grid/column.js';
|
||||||
import { createRow, defaultGridRowSetting, resetRow } from '@/components/grid/row.js';
|
import { createRow, defaultGridRowSetting, resetRow } from '@/components/grid/row.js';
|
||||||
import { handleKeyEvent } from '@/utility/key-event.js';
|
import { makeHotkey } from '@/utility/hotkey.js';
|
||||||
|
|
||||||
type RowHolder = {
|
type RowHolder = {
|
||||||
row: GridRow,
|
row: GridRow,
|
||||||
|
|
@ -289,161 +289,143 @@ function onKeyDown(ev: KeyboardEvent) {
|
||||||
const max = availableBounds.value;
|
const max = availableBounds.value;
|
||||||
const bounds = rangedBounds.value;
|
const bounds = rangedBounds.value;
|
||||||
|
|
||||||
handleKeyEvent(ev, [
|
makeHotkey({
|
||||||
{
|
'delete': () => {
|
||||||
code: 'Delete', handler: () => {
|
if (rangedRows.value.length > 0) {
|
||||||
if (rangedRows.value.length > 0) {
|
if (rowSetting.events.delete) {
|
||||||
if (rowSetting.events.delete) {
|
rowSetting.events.delete(rangedRows.value);
|
||||||
rowSetting.events.delete(rangedRows.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const context = createContext();
|
|
||||||
removeDataFromGrid(context, (cell) => {
|
|
||||||
emitCellValue(cell, undefined);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'KeyC', modifiers: ['Control'], handler: () => {
|
|
||||||
const context = createContext();
|
const context = createContext();
|
||||||
copyGridDataToClipboard(data.value, context);
|
removeDataFromGrid(context, (cell) => {
|
||||||
},
|
emitCellValue(cell, undefined);
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'KeyV', modifiers: ['Control'], handler: async () => {
|
|
||||||
const _cells = cells.value;
|
|
||||||
const context = createContext();
|
|
||||||
await pasteToGridFromClipboard(context, (row, col, parsedValue) => {
|
|
||||||
emitCellValue(_cells[row.index].cells[col.index], parsedValue);
|
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
'ctrl+c|meta+c': () => {
|
||||||
code: 'ArrowRight', modifiers: ['Control', 'Shift'], handler: () => {
|
const context = createContext();
|
||||||
updateSelectionRange({
|
copyGridDataToClipboard(data.value, context);
|
||||||
leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row },
|
|
||||||
rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+v|meta+v': async () => {
|
||||||
code: 'ArrowLeft', modifiers: ['Control', 'Shift'], handler: () => {
|
const _cells = cells.value;
|
||||||
updateSelectionRange({
|
const context = createContext();
|
||||||
leftTop: { col: max.leftTop.col, row: bounds.leftTop.row },
|
await pasteToGridFromClipboard(context, (row, col, parsedValue) => {
|
||||||
rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row },
|
emitCellValue(_cells[row.index].cells[col.index], parsedValue);
|
||||||
});
|
});
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+shift+right|meta+shift+right': () => {
|
||||||
code: 'ArrowUp', modifiers: ['Control', 'Shift'], handler: () => {
|
updateSelectionRange({
|
||||||
updateSelectionRange({
|
leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row },
|
||||||
leftTop: { col: bounds.leftTop.col, row: max.leftTop.row },
|
rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row },
|
||||||
rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row },
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+shift+left|meta+shift+left': () => {
|
||||||
code: 'ArrowDown', modifiers: ['Control', 'Shift'], handler: () => {
|
updateSelectionRange({
|
||||||
updateSelectionRange({
|
leftTop: { col: max.leftTop.col, row: bounds.leftTop.row },
|
||||||
leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row },
|
rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row },
|
||||||
rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row },
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+shift+up|meta+shift+up': () => {
|
||||||
code: 'ArrowRight', modifiers: ['Shift'], handler: () => {
|
updateSelectionRange({
|
||||||
updateSelectionRange({
|
leftTop: { col: bounds.leftTop.col, row: max.leftTop.row },
|
||||||
leftTop: {
|
rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row },
|
||||||
col: bounds.leftTop.col < selectedCellAddress.col
|
});
|
||||||
? bounds.leftTop.col + 1
|
|
||||||
: selectedCellAddress.col,
|
|
||||||
row: bounds.leftTop.row,
|
|
||||||
},
|
|
||||||
rightBottom: {
|
|
||||||
col: (bounds.rightBottom.col > selectedCellAddress.col || bounds.leftTop.col === selectedCellAddress.col)
|
|
||||||
? bounds.rightBottom.col + 1
|
|
||||||
: selectedCellAddress.col,
|
|
||||||
row: bounds.rightBottom.row,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+shift+down|meta+shift+down': () => {
|
||||||
code: 'ArrowLeft', modifiers: ['Shift'], handler: () => {
|
updateSelectionRange({
|
||||||
updateSelectionRange({
|
leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row },
|
||||||
leftTop: {
|
rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row },
|
||||||
col: (bounds.leftTop.col < selectedCellAddress.col || bounds.rightBottom.col === selectedCellAddress.col)
|
});
|
||||||
? bounds.leftTop.col - 1
|
|
||||||
: selectedCellAddress.col,
|
|
||||||
row: bounds.leftTop.row,
|
|
||||||
},
|
|
||||||
rightBottom: {
|
|
||||||
col: bounds.rightBottom.col > selectedCellAddress.col
|
|
||||||
? bounds.rightBottom.col - 1
|
|
||||||
: selectedCellAddress.col,
|
|
||||||
row: bounds.rightBottom.row,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+right|meta+right': () => {
|
||||||
code: 'ArrowUp', modifiers: ['Shift'], handler: () => {
|
selectionCell({ col: max.rightBottom.col, row: selectedCellAddress.row });
|
||||||
updateSelectionRange({
|
|
||||||
leftTop: {
|
|
||||||
col: bounds.leftTop.col,
|
|
||||||
row: (bounds.leftTop.row < selectedCellAddress.row || bounds.rightBottom.row === selectedCellAddress.row)
|
|
||||||
? bounds.leftTop.row - 1
|
|
||||||
: selectedCellAddress.row,
|
|
||||||
},
|
|
||||||
rightBottom: {
|
|
||||||
col: bounds.rightBottom.col,
|
|
||||||
row: bounds.rightBottom.row > selectedCellAddress.row
|
|
||||||
? bounds.rightBottom.row - 1
|
|
||||||
: selectedCellAddress.row,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+left|meta+left': () => {
|
||||||
code: 'ArrowDown', modifiers: ['Shift'], handler: () => {
|
selectionCell({ col: max.leftTop.col, row: selectedCellAddress.row });
|
||||||
updateSelectionRange({
|
|
||||||
leftTop: {
|
|
||||||
col: bounds.leftTop.col,
|
|
||||||
row: bounds.leftTop.row < selectedCellAddress.row
|
|
||||||
? bounds.leftTop.row + 1
|
|
||||||
: selectedCellAddress.row,
|
|
||||||
},
|
|
||||||
rightBottom: {
|
|
||||||
col: bounds.rightBottom.col,
|
|
||||||
row: (bounds.rightBottom.row > selectedCellAddress.row || bounds.leftTop.row === selectedCellAddress.row)
|
|
||||||
? bounds.rightBottom.row + 1
|
|
||||||
: selectedCellAddress.row,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+up|meta+up': () => {
|
||||||
code: 'ArrowDown', handler: () => {
|
selectionCell({ col: selectedCellAddress.col, row: max.leftTop.row });
|
||||||
selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row + 1 });
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'ctrl+down|meta+down': () => {
|
||||||
code: 'ArrowUp', handler: () => {
|
selectionCell({ col: selectedCellAddress.col, row: max.rightBottom.row });
|
||||||
selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row - 1 });
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'shift+right': () => {
|
||||||
code: 'ArrowRight', handler: () => {
|
updateSelectionRange({
|
||||||
selectionCell({ col: selectedCellAddress.col + 1, row: selectedCellAddress.row });
|
leftTop: {
|
||||||
},
|
col: bounds.leftTop.col < selectedCellAddress.col
|
||||||
|
? bounds.leftTop.col + 1
|
||||||
|
: selectedCellAddress.col,
|
||||||
|
row: bounds.leftTop.row,
|
||||||
|
},
|
||||||
|
rightBottom: {
|
||||||
|
col: (bounds.rightBottom.col > selectedCellAddress.col || bounds.leftTop.col === selectedCellAddress.col)
|
||||||
|
? bounds.rightBottom.col + 1
|
||||||
|
: selectedCellAddress.col,
|
||||||
|
row: bounds.rightBottom.row,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
{
|
'shift+left': () => {
|
||||||
code: 'ArrowLeft', handler: () => {
|
updateSelectionRange({
|
||||||
selectionCell({ col: selectedCellAddress.col - 1, row: selectedCellAddress.row });
|
leftTop: {
|
||||||
},
|
col: (bounds.leftTop.col < selectedCellAddress.col || bounds.rightBottom.col === selectedCellAddress.col)
|
||||||
|
? bounds.leftTop.col - 1
|
||||||
|
: selectedCellAddress.col,
|
||||||
|
row: bounds.leftTop.row,
|
||||||
|
},
|
||||||
|
rightBottom: {
|
||||||
|
col: bounds.rightBottom.col > selectedCellAddress.col
|
||||||
|
? bounds.rightBottom.col - 1
|
||||||
|
: selectedCellAddress.col,
|
||||||
|
row: bounds.rightBottom.row,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
]);
|
'shift+up': () => {
|
||||||
|
updateSelectionRange({
|
||||||
|
leftTop: {
|
||||||
|
col: bounds.leftTop.col,
|
||||||
|
row: (bounds.leftTop.row < selectedCellAddress.row || bounds.rightBottom.row === selectedCellAddress.row)
|
||||||
|
? bounds.leftTop.row - 1
|
||||||
|
: selectedCellAddress.row,
|
||||||
|
},
|
||||||
|
rightBottom: {
|
||||||
|
col: bounds.rightBottom.col,
|
||||||
|
row: bounds.rightBottom.row > selectedCellAddress.row
|
||||||
|
? bounds.rightBottom.row - 1
|
||||||
|
: selectedCellAddress.row,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'shift+down': () => {
|
||||||
|
updateSelectionRange({
|
||||||
|
leftTop: {
|
||||||
|
col: bounds.leftTop.col,
|
||||||
|
row: bounds.leftTop.row < selectedCellAddress.row
|
||||||
|
? bounds.leftTop.row + 1
|
||||||
|
: selectedCellAddress.row,
|
||||||
|
},
|
||||||
|
rightBottom: {
|
||||||
|
col: bounds.rightBottom.col,
|
||||||
|
row: (bounds.rightBottom.row > selectedCellAddress.row || bounds.leftTop.row === selectedCellAddress.row)
|
||||||
|
? bounds.rightBottom.row + 1
|
||||||
|
: selectedCellAddress.row,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'down': () => {
|
||||||
|
selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row + 1 });
|
||||||
|
},
|
||||||
|
'up': () => {
|
||||||
|
selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row - 1 });
|
||||||
|
},
|
||||||
|
'right': () => {
|
||||||
|
selectionCell({ col: selectedCellAddress.col + 1, row: selectedCellAddress.row });
|
||||||
|
},
|
||||||
|
'left': () => {
|
||||||
|
selectionCell({ col: selectedCellAddress.col - 1, row: selectedCellAddress.row });
|
||||||
|
},
|
||||||
|
}, [])(ev);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,12 @@ let latestHotkey: Pattern & { callback: CallbackFunction } | null = null;
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region impl
|
//#region impl
|
||||||
export const makeHotkey = (keymap: Keymap) => {
|
export const makeHotkey = (keymap: Keymap, ignoreElements = IGNORE_ELEMENTS) => {
|
||||||
const actions = parseKeymap(keymap);
|
const actions = parseKeymap(keymap);
|
||||||
return (ev: KeyboardEvent) => {
|
return (ev: KeyboardEvent) => {
|
||||||
if ('pswp' in window && window.pswp != null) return;
|
if ('pswp' in window && window.pswp != null) return;
|
||||||
if (window.document.activeElement != null) {
|
if (window.document.activeElement != null) {
|
||||||
if (IGNORE_ELEMENTS.includes(window.document.activeElement.tagName.toLowerCase())) return;
|
if (ignoreElements.includes(window.document.activeElement.tagName.toLowerCase())) return;
|
||||||
if (getHTMLElementOrNull(window.document.activeElement)?.isContentEditable) return;
|
if (getHTMLElementOrNull(window.document.activeElement)?.isContentEditable) return;
|
||||||
}
|
}
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
|
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link KeyboardEvent.code} の値を表す文字列。不足分は適宜追加する
|
|
||||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values
|
|
||||||
*/
|
|
||||||
export type KeyCode = (
|
|
||||||
| 'Backspace'
|
|
||||||
| 'Tab'
|
|
||||||
| 'Enter'
|
|
||||||
| 'Shift'
|
|
||||||
| 'Control'
|
|
||||||
| 'Alt'
|
|
||||||
| 'Pause'
|
|
||||||
| 'CapsLock'
|
|
||||||
| 'Escape'
|
|
||||||
| 'Space'
|
|
||||||
| 'PageUp'
|
|
||||||
| 'PageDown'
|
|
||||||
| 'End'
|
|
||||||
| 'Home'
|
|
||||||
| 'ArrowLeft'
|
|
||||||
| 'ArrowUp'
|
|
||||||
| 'ArrowRight'
|
|
||||||
| 'ArrowDown'
|
|
||||||
| 'Insert'
|
|
||||||
| 'Delete'
|
|
||||||
| 'Digit0'
|
|
||||||
| 'Digit1'
|
|
||||||
| 'Digit2'
|
|
||||||
| 'Digit3'
|
|
||||||
| 'Digit4'
|
|
||||||
| 'Digit5'
|
|
||||||
| 'Digit6'
|
|
||||||
| 'Digit7'
|
|
||||||
| 'Digit8'
|
|
||||||
| 'Digit9'
|
|
||||||
| 'KeyA'
|
|
||||||
| 'KeyB'
|
|
||||||
| 'KeyC'
|
|
||||||
| 'KeyD'
|
|
||||||
| 'KeyE'
|
|
||||||
| 'KeyF'
|
|
||||||
| 'KeyG'
|
|
||||||
| 'KeyH'
|
|
||||||
| 'KeyI'
|
|
||||||
| 'KeyJ'
|
|
||||||
| 'KeyK'
|
|
||||||
| 'KeyL'
|
|
||||||
| 'KeyM'
|
|
||||||
| 'KeyN'
|
|
||||||
| 'KeyO'
|
|
||||||
| 'KeyP'
|
|
||||||
| 'KeyQ'
|
|
||||||
| 'KeyR'
|
|
||||||
| 'KeyS'
|
|
||||||
| 'KeyT'
|
|
||||||
| 'KeyU'
|
|
||||||
| 'KeyV'
|
|
||||||
| 'KeyW'
|
|
||||||
| 'KeyX'
|
|
||||||
| 'KeyY'
|
|
||||||
| 'KeyZ'
|
|
||||||
| 'MetaLeft'
|
|
||||||
| 'MetaRight'
|
|
||||||
| 'ContextMenu'
|
|
||||||
| 'F1'
|
|
||||||
| 'F2'
|
|
||||||
| 'F3'
|
|
||||||
| 'F4'
|
|
||||||
| 'F5'
|
|
||||||
| 'F6'
|
|
||||||
| 'F7'
|
|
||||||
| 'F8'
|
|
||||||
| 'F9'
|
|
||||||
| 'F10'
|
|
||||||
| 'F11'
|
|
||||||
| 'F12'
|
|
||||||
| 'NumLock'
|
|
||||||
| 'ScrollLock'
|
|
||||||
| 'Semicolon'
|
|
||||||
| 'Equal'
|
|
||||||
| 'Comma'
|
|
||||||
| 'Minus'
|
|
||||||
| 'Period'
|
|
||||||
| 'Slash'
|
|
||||||
| 'Backquote'
|
|
||||||
| 'BracketLeft'
|
|
||||||
| 'Backslash'
|
|
||||||
| 'BracketRight'
|
|
||||||
| 'Quote'
|
|
||||||
| 'Meta'
|
|
||||||
| 'AltGraph'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修飾キーを表す文字列。不足分は適宜追加する。
|
|
||||||
*/
|
|
||||||
export type KeyModifier = (
|
|
||||||
| 'Shift'
|
|
||||||
| 'Control'
|
|
||||||
| 'Alt'
|
|
||||||
| 'Meta'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 押下されたキー以外の状態を表す文字列。不足分は適宜追加する。
|
|
||||||
*/
|
|
||||||
export type KeyState = (
|
|
||||||
| 'composing'
|
|
||||||
| 'repeat'
|
|
||||||
);
|
|
||||||
|
|
||||||
export type KeyEventHandler = {
|
|
||||||
modifiers?: KeyModifier[];
|
|
||||||
states?: KeyState[];
|
|
||||||
code: KeyCode | 'any';
|
|
||||||
handler: (event: KeyboardEvent) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function handleKeyEvent(event: KeyboardEvent, handlers: KeyEventHandler[]) {
|
|
||||||
function checkModifier(ev: KeyboardEvent, modifiers? : KeyModifier[]) {
|
|
||||||
if (modifiers) {
|
|
||||||
return modifiers.every(modifier => ev.getModifierState(modifier));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkState(ev: KeyboardEvent, states?: KeyState[]) {
|
|
||||||
if (states) {
|
|
||||||
return states.every(state => ev.getModifierState(state));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let hit = false;
|
|
||||||
for (const handler of handlers.filter(it => it.code === event.code)) {
|
|
||||||
if (checkModifier(event, handler.modifiers) && checkState(event, handler.states)) {
|
|
||||||
handler.handler(event);
|
|
||||||
hit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hit) {
|
|
||||||
for (const handler of handlers.filter(it => it.code === 'any')) {
|
|
||||||
handler.handler(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue