【コーディング】キーボード

作成: Calc Page(CodePen)

<div id="keyboad-app-all">
  
  <!-- ===== エディタエリア ===== -->
  <div class="editor-area">
    <div class="editor-wrap" data-editor="01">
      <div class="editor" id="editor01"></div>
    </div>
    
    <div class="editor-wrap" data-editor="02">
      <div class="editor" id="editor02"></div>
    </div>
    
    <div class="editor-wrap" data-editor="03">
      <div class="editor" id="editor03"></div>
    </div>
    
    <div class="editor-wrap" data-editor="04">
      <div class="editor" id="editor04"></div>
    </div>
    
    <div class="editor-wrap" data-editor="05">
      <div class="editor" id="editor05"></div>
    </div>
 
    <div class="editor-wrap" data-editor="06">
      <div class="editor" id="editor06"></div>
    </div>
  
  </div>

  <!-- ===== キーボードエリア ===== -->
  <div class="keyboard-area">
    <div class="keyboard">
      <div class="keyboard-view">
   
        <!-- 記号 -->
        <div class="keyboard-panel panel-markpad">
          <div class="row">
            <button class="red" 
                data-key="clear">AC</button>
            <button>!</button>
            <button>@</button>
            <button>#</button>
            <button>$</button>
            <button>%</button>
          </div>
      
          <div class="row">
            <button>^</button>
            <button>&</button>
            <button>-</button>
            <button>_</button>
            <button>/</button>
            <button>¥</button>
          </div>
      
          <div class="row">
            <button>:</button>
            <button>;</button>
            <button>"</button>
            <button>'</button>
            <button>?</button>
            <button>*</button>
          </div>
          
          <div class="row">
            <button>~</button>
            <button>,</button>
            <button>.</button>
            <button class="orange" 
                data-key="undo" 
                style="font-size: 25px;">↩︎</button>
            <button class="gray func" 
                data-key="up">↑</button>
            <button class="orange" 
                data-key="redo" 
                style="font-size: 25px;">↪︎</button>
          </div>
     
          <div class="row">
            <button>|</button>
            <button><</button>
            <button>></button>
            <button class="gray func" 
                data-key="left">←</button>
            <button class="gray func" 
                data-key="down">↓</button>
            <button class="gray func" 
                data-key="right">→</button>
          </div>
        </div>

   
        <!-- 左側 -->
        <div class="keyboard-panel panel-text active">
          <div class="row">
            <button class="gray-light editor-btn"
                data-editor="01"
                data-key="switch">01</button>
            <button class="gray-light editor-btn"
                data-editor="02"
                data-key="switch">02</button>
            <button class="gray-light editor-btn"
                data-editor="03"
                data-key="switch">03</button>
            <button class="gray-light editor-btn"
                data-editor="04"
                data-key="switch">04</button>
            <button class="gray-light editor-btn"
                data-editor="05"
                data-key="switch">05</button>
            <button class="gray-light editor-btn"
                data-editor="06"
                data-key="switch">06</button>
          </div>
          
          <div class="row">
            <button data-value="Q" 
                data-label="q">q</button>
            <button data-value="W" 
                data-label="w">w</button>
            <button data-value="E" 
                data-label="e">e</button>
            <button data-value="R" 
                data-label="r">r</button>
            <button data-value="T" 
                data-label="t">t</button>
            <button data-value="Y" 
                data-label="y">y</button>
          </div>
     
          <div class="row">
            <button data-value="A" 
                data-label="a">a</button>
            <button data-value="S" 
                data-label="s">s</button>
            <button data-value="D" 
                data-label="d">d</button>
            <button data-value="F" 
                data-label="f">f</button>
            <button data-value="G" 
                data-label="g">g</button>
            <button data-value="H" 
                data-label="h">h</button>
          </div>
          
          <div class="row">
            <button data-value="Z" 
                data-label="z">z</button>
            <button data-value="X" 
                data-label="x">x</button>
            <button data-value="C" 
                data-label="c">c</button>
            <button data-value="V" 
                data-label="v">v</button>
            <button data-value="B" 
                data-label="b">b</button>
            <button data-value="N" 
                data-label="n">n</button>
          </div>
    
          <div class="row">
            <button class="orange func" 
                data-key="shift">Shift</button>
            <button class="orange func" 
                data-key="space">Space</button>
            <button class="orange">Tab</button>
            <button class="orange func" 
                data-key="enter">Enter</button>
            <button class="orange func" 
                data-key="back">Back</button>
            <button class="orange func" 
                data-key="shift">Shift</button>
          </div>
        </div>

        <!-- テンキー -->
        <div class="keyboard-panel panel-numpad">
          <div class="numpad">
            <div class="row">
              <button></button>
              <button></button>
              <button class="gray-light">(</button>
              <button class="gray-light">.</button>
              <button class="gray-light">)</button>
              <button class="gray">÷</button>
            </div>
      
            <div class="row">
              <button data-value="U" 
                data-label="u">u</button>
              <button data-value="O" 
                data-label="o">o</button>
              <button class="gray-light">7</button>
              <button class="gray-light">8</button>
              <button class="gray-light">9</button>
              <button class="gray">×</button>
            </div>
       
            <div class="row">
              <button data-value="J" 
                data-label="j">j</button>
              <button data-value="L" 
                data-label="l">l</button>
              <button class="gray-light">4</button>
              <button class="gray-light">5</button>
              <button class="gray-light">6</button>
              <button class="gray">-</button>
            </div>
       
            <div class="row">
              <button data-value="M" 
                data-label="m">m</button>
              <button data-value="P" 
                data-label="p">p</button>
              <button class="gray-light">1</button>
              <button class="gray-light">2</button>
              <button class="gray-light">3</button>
              <button class="gray">+</button>
            </div>
       
            <div class="row">
              <button data-value="K" 
                data-label="k">k</button>
              <button data-value="I" 
                data-label="i">i</button>
              <button class="gray-light">0</button>
              <button class="gray-light">00</button>
              <button class="gray-light">000</button>
              <button class="gray">=</button>
            </div>
          </div>
        </div>
        
      </div>
    </div>

    <!-- 切替 -->
    <div class="keyboard-switch">
      <button data-target="markpad">Mark</button>
      <button data-target="textpad">Text</button>
      <button data-target="numpad">Number</button>
    </div>
  </div>
  
</div>

$bg-boad: #38bdf8;
$bg-red: #ef4444;
$bg-blue: #2196f3;
$bg-orange: #ff9800;
$bg-yellow: #eaf5ff;
$bg-white: #fff;
$bg-black: #999;
$bg-glay: #333;
$bg-gray-light: #666;

#keyboad-app-all {
  height: 95vh;
  display: flex;
  flex-direction: column;
  background: #f5f5f5;
}

.editor-area {
  flex: 1;
  overflow-y: auto;
  margin-bottom: 6px;
}

.keyboard-area {
  flex-shrink: 0;
}

.editor {
  background: $bg-yellow;
  border: 2px solid $bg-blue;
  position: relative;
  font-family: monospace;
  font-size: 16px;
  line-height: 24px;
  white-space: pre-wrap;
  word-break: break-word;
  
  background-image:
    repeating-linear-gradient(
      to right,
      rgba(0,0,0,0.06) 0,
      rgba(0,0,0,0.06) 1px,
      transparent 1px,
      transparent 1ch
    ),
    repeating-linear-gradient(
      to bottom,
      rgba(0,0,0,0.06) 0,
      rgba(0,0,0,0.06) 1px,
      transparent 1px,
      transparent 24px
    );
}

.editor-wrap {
  display: none;
}

.editor-wrap.active {
  display: block;
  overflow-y: auto;
}

.editor-btn.active {
  background: $bg-glay;
}

.tab {
  display: inline-block;
  width: 4ch;
}

.cursor {
  display: inline-block;
  width: 2px;
  height: 24px;
  background: #000;
  vertical-align: bottom;
  animation: blink 1s steps(2) infinite;
}

@keyframes blink {
  0% { opacity: 1; }
  50% { opacity: 0; }
}

.keyboard {
  background: $bg-boad;
  border: 2px solid $bg-blue;
  margin-bottom: 6px;
}

.keyboard-view { 
  padding: 10px;
  height: 240px;
}

.keyboard-panel {
  display: none;
}

.keyboard-panel.active {
  display: block;
}

.row {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin-bottom: 6px;

  button {
    height: 44px;
    border: 2px solid $bg-black;
    background: $bg-yellow;
    font-size: 14px;
    flex: 1;
  }
}

button.orange {
  background: $bg-orange;
  color: $bg-white;
}

button.red {
  background: $bg-red;
  color: $bg-white;
}

button.gray {
  background: $bg-glay;
  color: $bg-white;
}

button.gray-light {
  background: $bg-gray-light;
  color: $bg-white;
}

button.func.active {  
  background: $bg-red;
  color: $bg-white;
}

.keyboard-switch {
  display: flex;
  gap: 6px;

  button {
    flex: 1;
    height: 44px;
    font-size: 16px;
    background: $bg-blue;
    color: $bg-white;
    border: 2px solid $bg-black;
  }
}

let textBuffer = "\n\n\n\n\n\n\n";
let cursorPos = 0;
let isShift = false;
let history = [];
let historyIndex = -1;
const editors = {};
let currentEditorId = "01";
showEditor("01");
renderEditor();

function saveHistory() {
  const snapshot = {
    textBuffer: textBuffer,
    cursorPos: cursorPos
  };
  history = history.slice(0, historyIndex + 1);
  history.push(snapshot);
  historyIndex++;
}

function initEditor(id) {
  if (!editors[id]) {
    editors[id] = {
      textBuffer: "\n\n\n\n\n\n\n",
      cursorPos: 0,
      history: [],
      historyIndex: -1
    };
  }
}

function visualize(str) {
  return str
    .replace(/&/g, "&")
    .replace(/</g, "<")
    .replace(/>/g, ">")
    .replace(/ /g, '<span class="space"> </span>')
    .replace(/\t/g, '<span class="tab"></span>')
    .replace(/\n/g, "<br>");
}

/* =====  エディタ切り替え ===== */
function showEditor(id) {
  if (currentEditorId) {
    initEditor(currentEditorId);
    const cur = editors[currentEditorId];
    cur.textBuffer = textBuffer;
    cur.cursorPos = cursorPos;
  }
  initEditor(id);
  currentEditorId = id;
  textBuffer = editors[id].textBuffer;
  cursorPos = editors[id].cursorPos;

  $(".editor-wrap").removeClass("active");
  $(`.editor-wrap[data-editor="${id}"]`).addClass("active");

  $(".editor-btn").removeClass("active");
  $(`.editor-btn[data-editor="${id}"]`).addClass("active");

  renderEditor();
}

/* =====  undo redo ===== */
function undo() {
  if (historyIndex <= 0) return;

  historyIndex--;
  const state = history[historyIndex];

  textBuffer = state.textBuffer;
  cursorPos = state.cursorPos;
  renderEditor();
}

function redo() {
  if (historyIndex >= history.length - 1) return;

  historyIndex++;
  const state = history[historyIndex];

  textBuffer = state.textBuffer;
  cursorPos = state.cursorPos;
  renderEditor();
}

/* ===== エディタ描画 ===== */
function renderEditor() {
  const before = visualize(textBuffer.slice(0, cursorPos));
  const after  = visualize(textBuffer.slice(cursorPos));

  $(".editor-wrap.active .editor").html(
    before + '<span class="cursor"></span>' + after
  );

  const cursor = document.querySelector(".cursor");
  if (cursor) {
    cursor.scrollIntoView({ block: "nearest" });
  }
}

/* ===== キーボード切り替え ===== */
$(".keyboard-switch button").on("click", function () {
  const target = $(this).data("target");
  $(".keyboard-panel").removeClass("active");
  if (target === "markpad") 
    $(".panel-markpad").addClass("active");
  if (target === "textpad") 
    $(".panel-text").addClass("active");
  if (target === "numpad") 
    $(".panel-numpad").addClass("active");
});

/* ===== Shift切替 ===== */
function toggleShift() {
  isShift = !isShift;
  
  $(".keyboard button.func[data-key='shift']")
    .toggleClass("active", isShift);
  
  $(".keyboard-panel button").each(function () {
    const $btn = $(this);
    const val   = $btn.data("value");
    const label = $btn.data("label");
    if (!val || !label) return;
    if (isShift) {
      $btn.text(val);
    } else {
      $btn.text(label);
    }
  });
}

/* ===== 文字挿入 ===== */
function insertChar(char) {
  saveHistory();
  textBuffer =
    textBuffer.slice(0, cursorPos) +
    char +
    textBuffer.slice(cursorPos);
  cursorPos += char.length;
  renderEditor();
}

/* ===== Backspace ===== */
function backspace() {
  saveHistory();
  if (cursorPos === 0) return;
  textBuffer =
    textBuffer.slice(0, cursorPos - 1) +
    textBuffer.slice(cursorPos);
  cursorPos--;
  renderEditor();
}

/* ===== カーソル移動 ===== */
function moveCursor(dir) {
  if (dir === "left") {
    cursorPos 
      = Math.max(0, cursorPos - 1);
    renderEditor();
    return;
  }
  if (dir === "right") {
    cursorPos 
      = Math.min(textBuffer.length, cursorPos + 1);
    renderEditor();
    return;
  }
  const lines = textBuffer.split("\n");
  let line = 0;
  let col = cursorPos;
  for (let i = 0; i < lines.length; i++) {
    if (col <= lines[i].length) {
      line = i;
      break;
    }
    col -= lines[i].length + 1;
  }
  if (dir === "up") {
    if (line === 0) return;
    line--;
  }
  if (dir === "down") {
    if (line === lines.length - 1) return;
    line++;
  }
  cursorPos = 0;
  for (let i = 0; i < line; i++) {
    cursorPos += lines[i].length + 1;
  }
  cursorPos += Math.min(col, lines[line].length);

  renderEditor();
}

/* ===== キーボード操作 ===== */
$(".keyboard").on("touchstart mousedown", "button", function (e) {
  e.preventDefault();
  e.stopPropagation();

  const func = $(this).data("key");
  const val  = $(this).data("value");
  const key  = $(this).text();
  
// ===== エディタ切り替え(最優先)=====
if (func === "switch") {
  const id = $(this).data("editor");
  if (!id) return;
  showEditor(id);
  return;
}
  
// ===== 最優先!! undo redo =====
  if (func === "undo") {
    undo();
    return;
  }
  if (func === "redo") {
    redo();
    return;
  }
  
// ===== 最優先!! AC(All Clear)=====
  if (func === "clear") {
    const ok = confirm("すべて消去しますか?");
  if (!ok) return;
    saveHistory();
    textBuffer = "\n\n\n\n\n\n\n";
    cursorPos  = 0;
    renderEditor();
    return;
  }
  
  // --- 機能キー ---
  if (func === "shift") return toggleShift();
  if (func === "space") return insertChar(" ");
  if (func === "back")  return backspace();
  if (func === "enter") return insertChar("\n");
  if (func === "up")    return moveCursor("up");
  if (func === "down")  return moveCursor("down");
  if (func === "left")  return moveCursor("left");
  if (func === "right") return moveCursor("right");
  if (key === "Tab") return insertChar("\t");
  if (val) {
    insertChar($(this).text());
    return;
  }
    insertChar(key);
  });

  /* 初期描画 */
saveHistory();
renderEditor();
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次