diff --git a/frontend/src/Components/Autocomplete.svelte b/frontend/src/Components/Autocomplete.svelte index 08d263fc..74eae485 100644 --- a/frontend/src/Components/Autocomplete.svelte +++ b/frontend/src/Components/Autocomplete.svelte @@ -2,59 +2,114 @@ import { t } from '../translations.svelte.js' import { tick } from "svelte"; + let { - getCandidates = async text => { conole.log('no handler for getCandidates('+text+')'); return {};}, - onSelect = text => [] + getCandidates = dummyGetCandidates, + onCommit = dummyOnCommit, + onSelect = dummyOnSelect, } = $props(); - const ignore = ['Escape','Tab','ArrowUp','ArrowLeft','ArrowRight'] - let options = $state({}); - let text = $state('') + const ignore = ['ArrowLeft','ArrowRight']; + let candidate = $state({ display : '' }); + let selected = $state([]); + let candidates = $derived(getCandidates(candidate.display)); + + async function dummyGetCandidates(text){ + console.warn(`getCandidates(${text}) not overridden!`); + if (!text) return []; + return [ + { + display : 'candidate 1', + explanation : 'candidates need to have a display field' + }, + { + display : 'candidate 2', + additional : 'other fields are optional', + more : 'and may be domain specific' + }, + { display : text } + ]; + } + + function dummyOnCommit(candidate){ + // if Enter is pressed on the input field, this method gets called with + // either the selected candidate or + // an anonymous object with the entered text in the display field + console.warn(`onCommit(${JSON.stringify(candidate)}) not overridden!`); + } + + function dummyOnSelect(candidate){ + console.warn(`${candidate.display} selected, but onSelect not overridden!`) + } async function ondblclick(evt){ const select = evt.target; - const key = select.value; - text = options[key]; - let result = {}; - result[key] = text; - options = {}; - text = ''; - onSelect(result); + const idx = select.value; + candidate = candidates[idx]; + candidates = []; + selected = []; + console.log(candidate); + onSelect(candidate); } - async function onkeyup(evt){ - const select = evt.target; - const key = evt.key; - if (ignore.includes(key)) return; - if (key == 'ArrowDown'){ - if (select.selectedIndex == 0) select.selectedIndex=1; - return; + async function onkeyup(ev){ + if (ignore.includes(ev.key)) return; + if (ev.key == 'ArrowDown'){ + ev.preventDefault(); + selected = selected.length < 1 ? [0] : [selected[0]+1] + if (selected[0] >= candidates.length) selected = [0]; + return false; } - if (key == 'Enter'){ - ondblclick(evt); - return; + if (ev.key == 'ArrowUp'){ + ev.preventDefault(); + selected = selected.length < 1 ? [-1] : [selected[0]-1] + if (selected[0] < 0) selected = [candidates.length-1]; + return false; } - if (key == 'Backspace'){ - text = text.substring(0,text.length-1) - } else if (key.length<2){ - text += evt.key + if (ev.key == 'Enter'|| ev.key == 'Tab'){ + ev.preventDefault(); + if (selected.length>0) { + candidate = candidates[selected[0]]; + candidates = []; + selected = []; + onSelect(candidate); + return false; + } + if (ev.key == 'Enter') { + candidates = []; + selected = []; + onCommit(candidate); + } + return false; } - options = await getCandidates(text); - await tick(); - for (let o of select.getElementsByTagName('option')) o.selected = false; + if (ev.key == 'Escape'){ + ev.preventDefault(); + candidates = []; + selected = []; + return false; + } + + candidates = await getCandidates(candidate.display); + if (selected>candidates.length) selected = candidates.length; + return false; } -{#if options} - -{/if} + +
+ + {#if candidates && candidates.length > 0} + + {/if} +
\ No newline at end of file diff --git a/frontend/src/Components/PermissionEditor.svelte b/frontend/src/Components/PermissionEditor.svelte index 5e53d8d5..f28dc2c8 100644 --- a/frontend/src/Components/PermissionEditor.svelte +++ b/frontend/src/Components/PermissionEditor.svelte @@ -1,8 +1,9 @@