From f392a9340f16d094284b3f93641d1f0004458cc1 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:58:39 -0400 Subject: [PATCH 1/4] date picker autocomplete --- .../src/_util/document-search-autocomplete.js | 59 +++++++++++++++++++ .../document-search/document-search.html | 22 ++++++- .../models/document-search/document-search.js | 39 +++++++++++- 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/frontend/src/_util/document-search-autocomplete.js b/frontend/src/_util/document-search-autocomplete.js index df006b76..b957b191 100644 --- a/frontend/src/_util/document-search-autocomplete.js +++ b/frontend/src/_util/document-search-autocomplete.js @@ -218,11 +218,70 @@ function applySuggestion(searchText, cursorPos, suggestion) { return null; } +/** + * When the cursor is inside a Date(…) or new Date(…) argument, returns the + * slice indices of that argument so a picker can replace it with a quoted ISO string. + */ +function getDatePickerInsertionRange(searchText, cursorPos) { + const before = searchText.slice(0, cursorPos); + const re = /((?:new\s+)?Date\s*\(\s*)([^)]*)$/i; + const m = before.match(re); + if (!m) { + return null; + } + const innerStart = m.index + m[1].length; + const after = searchText.slice(cursorPos); + let closeIdx = -1; + let parenDepth = 0; + for (let k = 0; k < after.length; k++) { + const ch = after[k]; + if (ch === '(') { + parenDepth++; + } else if (ch === ')') { + if (parenDepth === 0) { + closeIdx = cursorPos + k; + break; + } + parenDepth--; + } + } + const innerEnd = closeIdx >= 0 ? closeIdx : cursorPos; + return { innerStart, innerEnd }; +} + +function dateArgumentSliceToDatetimeLocal(slice) { + const t = slice.trim(); + if (!t) { + return ''; + } + const unquoted = (t.startsWith('"') && t.endsWith('"')) || (t.startsWith('\'') && t.endsWith('\'')) + ? t.slice(1, -1) + : t; + const d = new Date(unquoted); + if (Number.isNaN(d.getTime())) { + return ''; + } + const pad = n => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; +} + +function insertQuotedIsoInDateArgument(searchText, range, isoString) { + const quoted = JSON.stringify(isoString); + const { innerStart, innerEnd } = range; + return { + text: searchText.slice(0, innerStart) + quoted + searchText.slice(innerEnd), + newCursorPos: innerStart + quoted.length + }; +} + module.exports = { buildAutocompleteTrie, getAutocompleteContext, getAutocompleteSuggestions, applySuggestion, + getDatePickerInsertionRange, + dateArgumentSliceToDatetimeLocal, + insertQuotedIsoInDateArgument, QUERY_SELECTORS, VALUE_HELPERS, FUNCTION_HELPERS diff --git a/frontend/src/models/document-search/document-search.html b/frontend/src/models/document-search/document-search.html index f78572f1..00c874e1 100644 --- a/frontend/src/models/document-search/document-search.html +++ b/frontend/src/models/document-search/document-search.html @@ -5,11 +5,28 @@ type="text" placeholder="Filter (supports JS, dates, ObjectIds, and more)" v-model="searchText" - @click="initFilter" + @click="initFilter(); updateAutocomplete()" @input="updateAutocomplete" + @keyup="updateAutocomplete" @keydown="handleKeyDown" /> -