diff --git a/README.md b/README.md index 5c7b79c..42bec38 100644 --- a/README.md +++ b/README.md @@ -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), 138 Tests +npm run test # Vitest-Suite (Engine + Store + IO), 146 Tests npm run build # Typecheck + Produktionsbuild npm run lint # ESLint (flat config) ``` @@ -136,6 +136,17 @@ an der unteren Kante lässt sie sich per **Maus** (natives `resize: vertical`) auf beliebig viele Zeilen aufziehen, um mehr auf einmal zu sehen. `Alt`+`Enter` bzw. `Cmd`+`Enter`, `F4` und `Esc` verhalten sich wie im In-Zellen-Editor. +**Bezüge per Maus einfügen ("Point-Mode")**: sobald der Editor (Zelle oder +Formelleiste) eine Formel (`=…`) enthält, fügt ein **Klick** auf eine Zelle — +in derselben Tabelle **oder einer ganz anderen**, mit `Name!`-Qualifier bei +Bedarf `'in Quotes'` — deren Bezug an der Cursorposition ein; **Ziehen** +fügt stattdessen einen Bereichsbezug (`A1:C3`) ein, live wachsend während +der Zug. Ein weiterer Klick/Zug **ersetzt** den zuletzt eingefügten Bezug, +solange nichts dazwischen getippt wurde — **jede** Eingabe (ein `;` als +Argumenttrennzeichen ebenso wie jedes andere Zeichen) beendet dieses Fenster, +der nächste Klick fügt dann einen neuen Bezug an. Ein Klick während des +Point-Mode ändert nie die Tabellen-/Objektauswahl im Hintergrund. + ### Zell-Formatierung Auswahl (Zelle, Bereich, Mehrfachauswahl, ganze Zeile/Spalte über den Kopf) → im @@ -348,7 +359,7 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`, ## Tests -`npm run test` — Vitest, 138 Tests: +`npm run test` — Vitest, 146 Tests: - **lexer / parser** — Präzedenz & Assoziativität (`^` vor `*/` vor `+-`, `^` rechts, `-2^2 = -(2^2)`), lokale/qualifizierte/quoted Bezüge, `!` **und** `::`, @@ -405,6 +416,11 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`, einem Undo verwirft den Wiederholen-Ast; reine Auswahländerungen sind nie ein Schritt; deckt Kopieren/Einfügen, Ausfüllen und Formatierung ab (nicht nur `setCellInput`); „Auf Beispiel zurücksetzen" beginnt eine neue Historie. +- **Formel-Point-Mode** — Klick fügt einen lokalen Bezug an der Cursorposition + ein; ein zweiter Klick ohne Zwischen-Tippen ersetzt ihn, mit Tippen dazwischen + hängt er einen neuen an; Ziehen fügt/erweitert einen Bereichsbezug; ein Klick + in eine andere Tabelle qualifiziert mit deren Namen (Quoting bei Leerzeichen); + ohne Formel im Puffer greift nichts. - **IO** — JSON verlustfreier Round-Trip (identisch), defekte/leere Datei → Fehler ohne Nebenwirkung, unbekannter Tabellenname → `#REF`, v1→v2 beim Import; CSV Übersicht-Export→Re-Import gleiche Zahlen, RFC-4180-Quoting, diff --git a/src/components/FormulaBar.vue b/src/components/FormulaBar.vue index 2fb1fce..83ef19b 100644 --- a/src/components/FormulaBar.vue +++ b/src/components/FormulaBar.vue @@ -6,6 +6,7 @@ import { cycleReferenceLocks } from '../engine'; const store = useDocument(); const draft = ref(''); const focused = ref(false); +const fieldEl = ref(null); // keep the field in sync with the active cell unless the user is editing it watch( @@ -20,6 +21,31 @@ function commit() { const sel = store.doc.selection; if (sel) store.setCellInput(sel.tableId, sel.anchor.col, sel.anchor.row, draft.value); } +/** Lets the store insert a reference into this field when the user clicks a + * grid cell mid-formula ("point mode") -- see store.pointClick/pointDrag. */ +function registerAsFormulaEditor() { + const sel = store.doc.selection; + if (!sel) return; + store.registerFormulaEditor({ + tableId: sel.tableId, + getState: () => ({ + text: draft.value, + start: fieldEl.value?.selectionStart ?? draft.value.length, + end: fieldEl.value?.selectionEnd ?? draft.value.length, + }), + replace: (start, end, value) => { + draft.value = draft.value.slice(0, start) + value + draft.value.slice(end); + nextTick(() => { + fieldEl.value?.focus(); + fieldEl.value?.setSelectionRange(start + value.length, start + value.length); + }); + }, + }); +} +function onFocus() { + focused.value = true; + registerAsFormulaEditor(); +} function onEnter() { commit(); (document.activeElement as HTMLElement)?.blur(); @@ -27,11 +53,21 @@ function onEnter() { function onBlur() { focused.value = false; commit(); + store.registerFormulaEditor(null); } function onEscape() { draft.value = store.activeCellInput.value; (document.activeElement as HTMLElement)?.blur(); } +/** Actual typing (not a point-mode click inserting a reference programmatically) + * ends the "next click replaces the last reference" window. */ +function onInput() { + store.noteFormulaTyped(); +} +/** Clicking inside the field to reposition the caret should also end that window. */ +function onMousedown() { + store.noteFormulaTyped(); +} /** F4: cycle relative -> absolute -> row-locked -> col-locked (Excel/Numbers * "Bezug sperren") -- for the reference at the caret, or every reference a * real text selection overlaps at once. */ @@ -77,15 +113,18 @@ function onKeydown(e: KeyboardEvent) { fx