From d28588dca90a6db9c365b6789e30414ccb92da29 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Sun, 1 Nov 2026 10:44:14 -0700 Subject: [PATCH] case 4756 - Ctrl+; inserts today's date, Ctrl+Shift+; date and time, in any text field Co-Authored-By: Claude Opus 5.5 --- src/App.vue | 3 ++ src/api/insert-date-shortcut.js | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/api/insert-date-shortcut.js diff --git a/src/App.vue b/src/App.vue index 88043dc..cf37496 100644 --- a/src/App.vue +++ b/src/App.vue @@ -242,6 +242,7 @@ import gznotify from "./components/gznotify"; import openObjectHandler from "./api/open-object-handler"; import notifyPoll from "./api/notifypoll"; import { processLogout } from "./api/authutil"; +import insertDateShortcut from "./api/insert-date-shortcut"; export default { components: { gzconfirm, @@ -310,10 +311,12 @@ export default { window.$gz.dialog.wireUpEventHandlers(this); openObjectHandler.wireUpEventHandlers(this); window.$gz.translation.setVuetifyDefaultLanguageElements(this); + insertDateShortcut.wireUp(); }, beforeDestroy() { //UNWIRE ALL EVENT HANDLERS FROM GZEVENTBUS window.$gz.eventBus.$off(); + insertDateShortcut.unwire(); }, mounted() { const vm = this; diff --git a/src/api/insert-date-shortcut.js b/src/api/insert-date-shortcut.js new file mode 100644 index 0000000..5430d92 --- /dev/null +++ b/src/api/insert-date-shortcut.js @@ -0,0 +1,67 @@ +///////////////////////////////////////////////////////////////////// +// case 4756: keyboard shortcut to insert today's date in any text field +// Ctrl+; inserts "2026-10-26 - " +// Ctrl+Shift+; inserts "2026-10-26 14:30 - " +// at the cursor (replacing any selected text) of the text box being typed in, on the browser's clock +// (same keys as Excel and Google Sheets) +// +function pad(n) { + return n < 10 ? "0" + n : "" + n; +} + +export function dateText(now, withTime) { + let text = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad( + now.getDate() + )}`; + if (withTime) { + text += ` ${pad(now.getHours())}:${pad(now.getMinutes())}`; + } + return text + " - "; +} + +function isPlainTextField(el) { + if (!el || el.readOnly || el.disabled) { + return false; + } + //the report editor (Monaco) has its own keyboard handling + if (el.closest && el.closest(".monaco-editor")) { + return false; + } + if (el.tagName === "TEXTAREA") { + return true; + } + return ( + el.tagName === "INPUT" && + ["text", "search", ""].includes( + (el.getAttribute("type") || "").toLowerCase() + ) + ); +} + +export function handleKeydown(e) { + if (!e.ctrlKey || e.altKey || e.metaKey || e.code !== "Semicolon") { + return; + } + const el = document.activeElement; + if (!isPlainTextField(el)) { + return; + } + e.preventDefault(); + el.setRangeText( + dateText(new Date(), e.shiftKey), + el.selectionStart, + el.selectionEnd, + "end" + ); + //let Vue's v-model see the change, as typing would + el.dispatchEvent(new Event("input", { bubbles: true })); +} + +export default { + wireUp() { + document.addEventListener("keydown", handleKeydown); + }, + unwire() { + document.removeEventListener("keydown", handleKeydown); + } +};