UNPKG

@shutootaki/gwm

Version:
148 lines 5.74 kB
import { useState } from 'react'; import { useInput } from 'ink'; import { deleteWordLeft } from '../utils/keyboard.js'; /** * Ink の useInput を用いたカーソル付き文字列編集ロジックを共通化したカスタムフック。 * TextInput / SelectList / MultiSelectList で共通のショートカットに対応する。 */ export function useEditableText(options = {}) { const { initialValue = '', skipChars = [] } = options; const [state, setState] = useState({ value: initialValue, cursorPosition: initialValue.length, }); // 共通の文字編集ショートカットを処理 useInput((input, key) => { // 左矢印 or Ctrl+B: カーソルを左に移動 if (key.leftArrow || (key.ctrl && input === 'b')) { setState((prev) => ({ ...prev, cursorPosition: Math.max(0, prev.cursorPosition - 1), })); return; } // 右矢印 or Ctrl+F: カーソルを右に移動 if (key.rightArrow || (key.ctrl && input === 'f')) { setState((prev) => ({ ...prev, cursorPosition: Math.min(prev.value.length, prev.cursorPosition + 1), })); return; } // Cmd+Left or Ctrl+A: 行頭に移動 if ((key.meta && key.leftArrow) || (key.ctrl && input === 'a')) { setState((prev) => ({ ...prev, cursorPosition: 0, })); return; } // Cmd+Right or Ctrl+E: 行末に移動 if ((key.meta && key.rightArrow) || (key.ctrl && input === 'e')) { setState((prev) => ({ ...prev, cursorPosition: prev.value.length, })); return; } // Backspace: カーソルの左の文字を削除 if (key.backspace) { setState((prev) => { if (prev.cursorPosition === 0) return prev; return { value: prev.value.slice(0, prev.cursorPosition - 1) + prev.value.slice(prev.cursorPosition), cursorPosition: prev.cursorPosition - 1, }; }); return; } // Delete: バックスペースと同義(Mac) if (key.delete && !key.meta) { setState((prev) => { if (prev.cursorPosition === 0) return prev; return { value: prev.value.slice(0, prev.cursorPosition - 1) + prev.value.slice(prev.cursorPosition), cursorPosition: prev.cursorPosition - 1, }; }); return; } // Ctrl+D: 右側の文字を1文字削除 if (key.ctrl && input === 'd') { setState((prev) => { if (prev.cursorPosition >= prev.value.length) return prev; return { value: prev.value.slice(0, prev.cursorPosition) + prev.value.slice(prev.cursorPosition + 1), cursorPosition: prev.cursorPosition, }; }); return; } // Ctrl+U: 全削除 if (key.ctrl && input === 'u') { setState({ value: '', cursorPosition: 0, }); return; } // Ctrl+W または Option+Backspace: 直前の単語を削除 if ((key.ctrl && input === 'w') || (key.meta && key.backspace)) { setState((prev) => { if (prev.cursorPosition > 0) { const [newValue, newPos] = deleteWordLeft(prev.value, prev.cursorPosition); return { value: newValue, cursorPosition: newPos, }; } return prev; }); return; } // Command+Delete (Mac): 全削除 if ((key.meta && key.delete) || (key.meta && input === '\u007F')) { setState({ value: '', cursorPosition: 0, }); return; } // Ctrl+N / Ctrl+P: SelectList や MultiSelectList でカーソル移動として使うため、文字入力としては無視する if (key.ctrl && (input === 'n' || input === 'p')) { return; } // 通常の文字入力およびペースト操作の処理 if (input) { // skipCharsに含まれる文字をフィルタリング const filteredInput = skipChars.length > 0 ? input .split('') .filter((char) => !skipChars.includes(char)) .join('') : input; // フィルタリング後にテキストが残っている場合のみ処理 if (filteredInput.length > 0) { setState((prev) => ({ value: prev.value.slice(0, prev.cursorPosition) + filteredInput + prev.value.slice(prev.cursorPosition), cursorPosition: prev.cursorPosition + filteredInput.length, })); } } }); return { value: state.value, setValue: (newValue) => setState((prev) => ({ ...prev, value: newValue })), cursorPosition: state.cursorPosition, setCursorPosition: (newPos) => setState((prev) => ({ ...prev, cursorPosition: newPos })), }; } //# sourceMappingURL=useEditableText.js.map