[カスタム絵文字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 { createColumn } from '@/components/grid/column.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 = {
|
||||
row: GridRow,
|
||||
|
|
@ -289,161 +289,143 @@ function onKeyDown(ev: KeyboardEvent) {
|
|||
const max = availableBounds.value;
|
||||
const bounds = rangedBounds.value;
|
||||
|
||||
handleKeyEvent(ev, [
|
||||
{
|
||||
code: 'Delete', handler: () => {
|
||||
if (rangedRows.value.length > 0) {
|
||||
if (rowSetting.events.delete) {
|
||||
rowSetting.events.delete(rangedRows.value);
|
||||
}
|
||||
} else {
|
||||
const context = createContext();
|
||||
removeDataFromGrid(context, (cell) => {
|
||||
emitCellValue(cell, undefined);
|
||||
});
|
||||
makeHotkey({
|
||||
'delete': () => {
|
||||
if (rangedRows.value.length > 0) {
|
||||
if (rowSetting.events.delete) {
|
||||
rowSetting.events.delete(rangedRows.value);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
code: 'KeyC', modifiers: ['Control'], handler: () => {
|
||||
} else {
|
||||
const context = createContext();
|
||||
copyGridDataToClipboard(data.value, context);
|
||||
},
|
||||
},
|
||||
{
|
||||
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);
|
||||
removeDataFromGrid(context, (cell) => {
|
||||
emitCellValue(cell, undefined);
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'ArrowRight', modifiers: ['Control', 'Shift'], handler: () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row },
|
||||
rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row },
|
||||
});
|
||||
},
|
||||
'ctrl+c|meta+c': () => {
|
||||
const context = createContext();
|
||||
copyGridDataToClipboard(data.value, context);
|
||||
},
|
||||
{
|
||||
code: 'ArrowLeft', modifiers: ['Control', 'Shift'], handler: () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: max.leftTop.col, row: bounds.leftTop.row },
|
||||
rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row },
|
||||
});
|
||||
},
|
||||
'ctrl+v|meta+v': async () => {
|
||||
const _cells = cells.value;
|
||||
const context = createContext();
|
||||
await pasteToGridFromClipboard(context, (row, col, parsedValue) => {
|
||||
emitCellValue(_cells[row.index].cells[col.index], parsedValue);
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ArrowUp', modifiers: ['Control', 'Shift'], handler: () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: bounds.leftTop.col, row: max.leftTop.row },
|
||||
rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row },
|
||||
});
|
||||
},
|
||||
'ctrl+shift+right|meta+shift+right': () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row },
|
||||
rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row },
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ArrowDown', modifiers: ['Control', 'Shift'], handler: () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row },
|
||||
rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row },
|
||||
});
|
||||
},
|
||||
'ctrl+shift+left|meta+shift+left': () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: max.leftTop.col, row: bounds.leftTop.row },
|
||||
rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row },
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ArrowRight', modifiers: ['Shift'], handler: () => {
|
||||
updateSelectionRange({
|
||||
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,
|
||||
},
|
||||
});
|
||||
},
|
||||
'ctrl+shift+up|meta+shift+up': () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: bounds.leftTop.col, row: max.leftTop.row },
|
||||
rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row },
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ArrowLeft', modifiers: ['Shift'], handler: () => {
|
||||
updateSelectionRange({
|
||||
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,
|
||||
},
|
||||
});
|
||||
},
|
||||
'ctrl+shift+down|meta+shift+down': () => {
|
||||
updateSelectionRange({
|
||||
leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row },
|
||||
rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row },
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ArrowUp', modifiers: ['Shift'], handler: () => {
|
||||
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+right|meta+right': () => {
|
||||
selectionCell({ col: max.rightBottom.col, row: selectedCellAddress.row });
|
||||
},
|
||||
{
|
||||
code: 'ArrowDown', modifiers: ['Shift'], handler: () => {
|
||||
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+left|meta+left': () => {
|
||||
selectionCell({ col: max.leftTop.col, row: selectedCellAddress.row });
|
||||
},
|
||||
{
|
||||
code: 'ArrowDown', handler: () => {
|
||||
selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row + 1 });
|
||||
},
|
||||
'ctrl+up|meta+up': () => {
|
||||
selectionCell({ col: selectedCellAddress.col, row: max.leftTop.row });
|
||||
},
|
||||
{
|
||||
code: 'ArrowUp', handler: () => {
|
||||
selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row - 1 });
|
||||
},
|
||||
'ctrl+down|meta+down': () => {
|
||||
selectionCell({ col: selectedCellAddress.col, row: max.rightBottom.row });
|
||||
},
|
||||
{
|
||||
code: 'ArrowRight', handler: () => {
|
||||
selectionCell({ col: selectedCellAddress.col + 1, row: selectedCellAddress.row });
|
||||
},
|
||||
'shift+right': () => {
|
||||
updateSelectionRange({
|
||||
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,
|
||||
},
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ArrowLeft', handler: () => {
|
||||
selectionCell({ col: selectedCellAddress.col - 1, row: selectedCellAddress.row });
|
||||
},
|
||||
'shift+left': () => {
|
||||
updateSelectionRange({
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,12 +50,12 @@ let latestHotkey: Pattern & { callback: CallbackFunction } | null = null;
|
|||
//#endregion
|
||||
|
||||
//#region impl
|
||||
export const makeHotkey = (keymap: Keymap) => {
|
||||
export const makeHotkey = (keymap: Keymap, ignoreElements = IGNORE_ELEMENTS) => {
|
||||
const actions = parseKeymap(keymap);
|
||||
return (ev: KeyboardEvent) => {
|
||||
if ('pswp' in window && window.pswp != null) return;
|
||||
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;
|
||||
}
|
||||
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