Show a range-handling pattern in the function-editing UI

A user function parameter already worked as a range when passed
straight into a range-capable call (SUM/AVG/MIN/MAX/another user
function) -- expandUserFunctions splices the caller's RangeNode in
unchanged, so it behaves exactly like a normal formula (and #VALUE!
outside a function-argument position, also exactly like normal). Added
2 tests confirming this (incl. recalculation on a change inside the
passed range) and a hint under the Formel field in
CustomFunctionsModal.vue showing the pattern (SPANNE(bereich) =
MAX(bereich) - MIN(bereich)), since nothing surfaced that this was
possible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
wkh
2026-09-02 15:59:56 +02:00
co-authored by Claude Sonnet 5
parent 1046b22e96
commit fdde4ed5a7
3 changed files with 47 additions and 4 deletions
+12 -4
View File
@@ -13,7 +13,7 @@ Vorbild von **Apple Numbers**. Zwei Dinge stehen im Mittelpunkt:
```bash
npm install
npm run dev # Vite-Devserver
npm run test # Vitest-Suite (Engine + Store + IO), 179 Tests
npm run test # Vitest-Suite (Engine + Store + IO), 181 Tests
npm run build # Typecheck + Produktionsbuild
npm run lint # ESLint (flat config)
```
@@ -168,7 +168,12 @@ eingebauter Funktionsname: `=MARGE(Umsatz!B2; Umsatz!C2)`.
Formel-Feld optisch identisch zur Formelleiste — **ohne** führendes `=`.
Ein Parametername verhält sich im Funktionskörper wie eine Zelle mit
eigenem Wert; er darf selbst nicht wie ein Zellbezug aussehen (`A1`, `$B2`
o. ä. sind ungültig).
o. ä. sind ungültig). Ein Parameter kann auch ein **Bereich** sein, solange
er im Funktionskörper direkt an eine bereichsfähige Funktion übergeben wird
— z. B. `SPANNE(bereich) = MAX(bereich) - MIN(bereich)`, aufgerufen als
`=SPANNE(Umsatz!B2:E6)`; das Formular zeigt dieses Muster als Hinweis unter
dem Formel-Feld. Wie bei jeder Formel ergibt ein Bereich außerhalb einer
Funktionsargument-Position `#VALUE!`.
- **Validierung beim Speichern**, mit **Inline-Fehlermeldung** direkt im
Formular (kein Toast, nichts geht verloren): Name-Kollision mit einer
eingebauten oder einer anderen benutzerdefinierten Funktion, ungültiger
@@ -406,7 +411,7 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
## Tests
`npm run test` — Vitest, 179 Tests:
`npm run test` — Vitest, 181 Tests:
- **lexer / parser** — Präzedenz & Assoziativität (`^` vor `*/` vor `+-`, `^`
rechts, `-2^2 = -(2^2)`), lokale/qualifizierte/quoted Bezüge, `!` **und** `::`,
@@ -473,7 +478,10 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
auch bei kollidierenden Parameternamen zwischen den Scopes), Arity-Mismatch →
`#ERR`, unbekannter Name → `#NAME?`, Rekursionstiefen-Sicherheitsnetz,
Abhängigkeits-Edges für Zellen, die nur im Funktionskörper vorkommen (nicht
im Aufruf) — inkl. korrekter Neuberechnungs-Reihenfolge. Store-Ebene: Name-/
im Aufruf) — inkl. korrekter Neuberechnungs-Reihenfolge; ein Bereichs-
Parameter funktioniert in Funktionsargument-Position (inkl. Neuberechnung
bei Änderung einer Zelle *innerhalb* des übergebenen Bereichs) und ergibt
`#VALUE!` außerhalb davon. Store-Ebene: Name-/
Parameter-Validierung, Kollision mit eingebauten/anderen Funktionen,
Zyklus-Ablehnung beim Speichern (direkt und über mehrere Stufen), Umbenennen/
Löschen lässt bestehende Aufrufe zu `#NAME?` werden, Undo/Redo einer