From c82fec24c43ec50b6ec0cd6a4cc934836963131e Mon Sep 17 00:00:00 2001
From: alex <alex@alexloehr.net>
Date: Fri, 06 Jun 2025 10:37:35 +0000
Subject: [PATCH] adding KeyboardToggle.vue

---
 vue/src/components/Pagination.vue                      |   96 ++++++++++++++++++++++++++++----
 vue/src/components/KeyboardToggle.vue                  |   35 +++++++++++
 vue/src/components/icons/IconKeyboardStrikethrough.vue |    5 +
 vue/src/components/icons/IconKeyboard.vue              |    5 +
 vue/src/pages/Users.vue                                |    2 
 vue/src/assets/styles.styl                             |    2 
 6 files changed, 132 insertions(+), 13 deletions(-)

diff --git a/vue/src/assets/styles.styl b/vue/src/assets/styles.styl
index fee8bb7..d6d00b2 100644
--- a/vue/src/assets/styles.styl
+++ b/vue/src/assets/styles.styl
@@ -31,3 +31,5 @@
 
 .nowrap
    white-space nowrap
+
+
diff --git a/vue/src/components/KeyboardToggle.vue b/vue/src/components/KeyboardToggle.vue
new file mode 100644
index 0000000..f683ed1
--- /dev/null
+++ b/vue/src/components/KeyboardToggle.vue
@@ -0,0 +1,35 @@
+<script setup>
+
+import IconKeyboard from "@/components/icons/IconKeyboard.vue"
+import IconKeyboardStrikethrough from "@/components/icons/IconKeyboardStrikethrough.vue"
+import {ref} from "vue"
+
+const props = defineProps({
+   initial: {
+      type: Boolean,
+      default: false
+   },
+})
+const emit = defineEmits(["toggle"])
+const strikethrough = ref(props.initial)
+
+function toggle () {
+   strikethrough.value = !strikethrough.value
+   emit("toggle", strikethrough.value)
+}
+
+</script>
+
+<template>
+
+   <button @click="toggle" type="button">
+      <IconKeyboard v-if="!strikethrough" />
+      <IconKeyboardStrikethrough v-if="strikethrough" />
+   </button>
+
+</template>
+
+<style scoped lang="stylus">
+
+
+</style>
diff --git a/vue/src/components/Pagination.vue b/vue/src/components/Pagination.vue
index fa59d81..ece7eba 100644
--- a/vue/src/components/Pagination.vue
+++ b/vue/src/components/Pagination.vue
@@ -2,6 +2,9 @@
 
 import * as itemOffset from '../lib/itemOffset.js'
 import {onKeyStroke} from "@vueuse/core"
+import IconKeyboard from "@/components/icons/IconKeyboard.vue"
+import KeyboardToggle from "@/components/KeyboardToggle.vue"
+import {ref} from "vue"
 
 const props = defineProps({
    total: Number,
@@ -33,7 +36,7 @@
 
 /////// KEYBOARD ////////////////////////////////////////////////////////////////
 
-onKeyStroke(["ArrowLeft", "ArrowRight", "Home", "End"], (e) => {
+onKeyStroke(["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"], (e) => {
    let multiplier = 1
    switch (e.key) {
       case "ArrowLeft":
@@ -44,42 +47,99 @@
          multiplier = e.shiftKey ? 10 : 1
          goNext(multiplier)
          break
-      case "Home":
+      case "ArrowUp":
          goStart()
          break
-      case "End":
+      case "ArrowDown":
          goEnd()
          break
    }
 })
 
+/////// keyboard ////////////////////////////////////////////////////////////////
+
+const keyboardVisible = ref(false)
+function toggleKeyboard (value) {
+   console.log("toggleKeyboard", value)
+   keyboardVisible.value = value
+}
 
 </script>
 
 <template>
 
    <div class="pagination">
-      <input type="button" @click="goStart()" class="start" value="«">
-      <input type="button" @click="goPrev()" class="prev" value="‹">
-      <span class="current">
-         {{ offset }} - {{ offset + limit }}
-          / {{ total }}
-      </span>
-      <input type="button" @click="goNext()" class="next" value="›">
-      <input type="button" @click="goEnd()" class="end" value="»">
+      <div class="pagination-buttons">
+
+         <input type="button" @click="goStart()" class="start" value="«">
+         <input type="button" @click="goPrev()" class="prev" value="‹">
+         <span class="current">{{ offset }} - {{ offset + limit }} / {{ total }}</span>
+         <input type="button" @click="goNext()" class="next" value="›">
+         <input type="button" @click="goEnd()" class="end" value="»">
+         <KeyboardToggle :initial="false" @toggle="toggleKeyboard" style="color: #666" />
+      </div>
+      <small class="pagination-info" v-show="keyboardVisible">
+         <em>Keyboard control:</em>
+         <span class="spacer" />
+         <kbd class="bigger">&ltri;</kbd> previous page
+         <span class="spacer" />
+         <kbd class="bigger">&rtri;</kbd> next page
+         <span class="spacer" />
+         <kbd>SHIFT</kbd> + <kbd class="bigger">&ltri;</kbd> {{limit * 10}} backward
+         <span class="spacer" />
+         <kbd>SHIFT</kbd> + <kbd class="bigger">&rtri;</kbd> {{limit * 10}} forward
+         <span class="spacer" />
+         <kbd class="bigger">&utri;</kbd> start
+         <span class="spacer" />
+         <kbd class="bigger">&dtri;</kbd> end
+      </small>
    </div>
 
 </template>
 
 <style scoped lang="stylus">
 
+.pagination-info
+   margin-top .5em;
+   display flex;
+   gap .5em
+   padding .33em .66em;
+   align-items center;
+   background-color #f1f1f1
+   .spacer
+      width 2em;
+      height 1px
+      display inline-block;
+      //background-color black
+
+kbd
+   border 1px solid #777
+   border-radius 4px
+   font-size 120%;
+   line-height 50%
+   min-width 1rem;
+   height 1rem
+   padding 3px;
+   display inline-flex;
+   align-items center;
+   justify-content center;
+
+kbd.bigger
+   font-size 220%
+   line-height 0
+
+
 .pagination
+   display flex;
+   flex-direction column
+   align-items center;
+   margin -1em 0 1em 0;
+.pagination-buttons
    font-size 1.33rem
    display flex;
    gap 1em
    align-items center;
    justify-content center;
-   margin -1em 0 1em 0;
 
    & > div
       cursor: pointer
@@ -102,4 +162,16 @@
    border none
    width 1em
 
+
+button
+   display inline-flex;
+   align-items center;
+   justify-content center;
+   border none
+   background-color transparent;
+   cursor pointer
+   &:hover
+      background-color #eee;
+
+
 </style>
diff --git a/vue/src/components/icons/IconKeyboard.vue b/vue/src/components/icons/IconKeyboard.vue
new file mode 100644
index 0000000..559b8fe
--- /dev/null
+++ b/vue/src/components/icons/IconKeyboard.vue
@@ -0,0 +1,5 @@
+<template>
+   <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+      <path fill="currentColor" d="M4 19q-.825 0-1.412-.587T2 17V7q0-.825.588-1.412T4 5h16q.825 0 1.413.588T22 7v10q0 .825-.587 1.413T20 19zm0-2h16V7H4zm4-1h8v-2H8zm-3-3h2v-2H5zm3 0h2v-2H8zm3 0h2v-2h-2zm3 0h2v-2h-2zm3 0h2v-2h-2zM5 10h2V8H5zm3 0h2V8H8zm3 0h2V8h-2zm3 0h2V8h-2zm3 0h2V8h-2zM4 17V7z" />
+   </svg>
+</template>
diff --git a/vue/src/components/icons/IconKeyboardStrikethrough.vue b/vue/src/components/icons/IconKeyboardStrikethrough.vue
new file mode 100644
index 0000000..e975390
--- /dev/null
+++ b/vue/src/components/icons/IconKeyboardStrikethrough.vue
@@ -0,0 +1,5 @@
+<template>
+   <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+      <path fill="currentColor" d="M19.85 22.525L1.475 4.15L2.9 2.725L21.275 21.1zM8 16v-2h6.175l2 2zm-3-3v-2h2v2zm3 0v-2h2v2zm9 0v-2h2v2zM5 10V8h2v2zm9 0V8h2v2zm3 0V8h2v2zm4.4 8.425L20 17V7h-9.975l-2-2H20q.825 0 1.413.588T22 7v10.025q0 .425-.162.775t-.438.625M4 19q-.825 0-1.412-.587T2 17V7q0-.825.588-1.412T4 5h1.175l2 2H4v10h13.175l2 2zm7.025-11H13v1.975zM14 11h2v1.975zm1 1" />
+   </svg>
+</template>
diff --git a/vue/src/pages/Users.vue b/vue/src/pages/Users.vue
index 80b010e..3971e71 100644
--- a/vue/src/pages/Users.vue
+++ b/vue/src/pages/Users.vue
@@ -16,7 +16,7 @@
    data: [],
 })
 const offset = useRouteQuery("offset", 0, {transform: Number})
-const limit = 24
+const limit = 22
 const error = ref(null)
 
 onMounted(() => init(offset.value))

--
Gitblit v1.8.0