select-search: recent-blok voor elke dropdown, bipp-budgetcode levert het niet meer zelf

Elke grote dropdown houdt nu zijn eigen vijf laatst gekozen waarden bij,
bovenaan de lijst onder de kopregel recent. Alles zit onder een
localStorage-sleutel, per veld, met een sleutel die uit name of id volgt
zodat rij 3 en rij 4 van hetzelfde veld één lijst delen. Delete haalt een
regel uit recent, en data-ss-recent="manual" plus window.selectSearch.remember()
is er voor wie pas mag onthouden nadat de server bevestigd heeft.

bipp-budgetcode gebruikt die weg en bouwt zijn eigen recent-optgroup dus
niet langer zelf.

Een optie zonder leesbare tekst telt voortaan als "niets gekozen", ook als
ze een waarde draagt, zoals de <option value="0">&nbsp;</option> van een
OAF-scherm. Zo'n regel stond eerst gewoon in de lijst; wie ze koos kreeg
haar daarna permanent als lege blauwe balk onder recent te zien.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Glenn Goossens
2026-09-01 16:59:22 +02:00
co-authored by Claude Opus 5
parent 4a9ea34649
commit b831ab9f1f
8 changed files with 437 additions and 79 deletions
+45 -8
View File
@@ -22,25 +22,62 @@ Klik een dropdown aan en typ.
- Zit de dropdown in groepen (`<optgroup>`), dan komt elke groepsnaam als grijze kopregel boven
zijn blok te staan. Die regel is geen resultaat: pijltjes slaan hem over en Enter kiest hem
nooit. Opties zonder groep blijven los bovenaan.
- Bovenaan staat onder *recent* wat je in dat veld het laatst gekozen hebt, tot vijf waarden.
Ze staan niet nog eens in hun eigen groep. Zie [Recent](#recent).
- Ook bij zoeken blijven de groepen aaneengesloten en in de volgorde van de dropdown zelf, zodat
een groep als *recent* bovenaan blijft en geen enkele kopregel zich herhaalt. Binnen een groep
komt wat met de zoekterm begint eerst.
*recent* bovenaan blijft en geen enkele kopregel zich herhaalt. Binnen een groep komt wat met
de zoekterm begint eerst.
- Er wordt enkel op het label gezocht, niet op de groepsnaam.
- Een optie met een lege waarde geldt als "niets gekozen". Ze staat niet in de lijst, maar haar
label wordt de grijze placeholder van het veld. Met `data-placeholder` op de `<select>` zet je
een eigen tekst, die wint van dat label.
- Een optie zonder leesbare tekst telt evengoed als "niets gekozen", ook al draagt ze een waarde,
zoals de `<option value="0">&nbsp;</option>` van een OAF-scherm. Een regel die je niet kunt
lezen is geen keuze: ze blijft uit de lijst en dus ook uit *recent*. Is er geen optie met een
lege waarde, dan mikt het kruisje op deze.
- Staat er iets gekozen, dan neemt een kruisje de plek van het pijltje in zodra je er met de muis
over gaat of erin staat. Dat zet de keuze terug op leeg. Het kruisje krijgt dus geen eigen
ruimte: de tekst houdt de volle breedte en er verschuift niets. Backspace in een leeg veld doet
hetzelfde. Zonder lege optie is er niets te wissen en blijft het kruisje weg.
- Staat er `data-removable` op een `<option>` of op een `<optgroup>`, dan haalt `Delete` de
regel die oplicht uit de lijst, zolang je niets getypt hebt. Ben je aan het zoeken, dan blijft
Delete een gewone teksttoets. Het script gooit zelf niets weg: het stuurt een `select-search:remove`
event op de `<select>`, met `detail` `{value, label, index}`. Wie de opties leverde beslist
wat verwijderen betekent en past de `<select>` aan, waarna de lijst zichzelf hertekent met
behoud van de zoekterm en de plek van de selectie.
- `Delete` haalt de regel die oplicht uit haar lijst, zolang je niets getypt hebt. Ben je aan het
zoeken, dan blijft Delete een gewone teksttoets. Op een regel uit *recent* doet het script dat
zelf. Staat er `data-removable` op een `<option>` of op een `<optgroup>`, dan gooit het script
zelf niets weg: het stuurt een `select-search:remove` event op de `<select>`, met `detail`
`{value, label, index}`. Wie de opties leverde beslist wat verwijderen betekent en past de
`<select>` aan, waarna de lijst zichzelf hertekent met behoud van de zoekterm en de plek van
de selectie.
- Boven 200 treffers toont het enkel het aantal. Verfijn dan je zoekterm.
## Recent
Elke dropdown houdt zijn eigen vijf laatst gekozen waarden bij, bovenaan de lijst onder de
kopregel *recent*. Er is geen vervaltermijn: vijf plaatsen verdringen zichzelf al, en een lijst
die na verlof leeg blijkt is enkel verwarrend. `Delete` op zo'n regel haalt ze eruit, waarna ze
weer gewoon tussen de andere opties staat.
De lijst hangt aan het veld, niet aan de pagina of aan de rij. De sleutel komt uit `name` of `id`,
met de volgnummers eruit: `ctl00$body$resultTable$ctl03$interfaceAccountList` en dezelfde dropdown
in rij 4 delen dus één lijst. Heeft een `<select>` geen `name` en geen `id`, dan krijgt hij geen
recent-blok en blijft hij verder gewoon doorzoekbaar.
Alles staat samen onder één localStorage-sleutel, `selectSearchRecent`:
```json
{ "v": 1, "fields": { "<veldsleutel>": { "ts": 0, "items": [["waarde", "label"]] } } }
```
`ts` dient enkel om boven de 40 velden het oudste veld te laten vallen. Lezen en schrijven falen
stil: is de opslag vol of geblokkeerd, dan verdwijnt het recent-blok en werkt de rest gewoon door.
### Voor wie de opties levert
| | |
|---|---|
| `data-ss-recent-key="..."` | Eigen veldsleutel, in plaats van die uit `name` of `id`. Nodig bij een `<select>` zonder allebei. |
| `data-ss-recent="manual"` | Het blok en `Delete` werken, maar kiezen onthoudt niets. Voor wie pas mag onthouden na een bevestiging van de server. |
| `window.selectSearch.remember(selectOfSleutel, value, label)` | Zet een waarde in recent en tekent het blok opnieuw. |
| `window.selectSearch.forget(selectOfSleutel, value)` | Haalt ze er weer uit. |
## Problemen
| Symptoom | Oplossing |