logisheets-formula-editor 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # logisheets-formula-editor-react
2
+
3
+ A React component for editing spreadsheet formulas with syntax highlighting and autocomplete, built on [CodeMirror 6](https://codemirror.net/).
4
+
5
+ ## Features
6
+
7
+ - **Token-based syntax highlighting** - Function names, cell references, errors are highlighted
8
+ - **Cell reference coloring** - Each cell reference gets a unique color for easy identification
9
+ - **Formula autocomplete** - Fuzzy matching for function names with descriptions
10
+ - **Robust text editing** - Built on CodeMirror 6 for proper cursor, selection, IME, and accessibility support
11
+ - **External styling support** - Font size, alignment, word wrap configurable via props
12
+ - **Backend-driven tokenization** - Does NOT parse formulas itself, relies on backend API
13
+
14
+ ## Important Design Decision
15
+
16
+ This editor does **NOT** tokenize/parse formulas itself. Instead, it:
17
+
18
+ 1. Sends the formula text to a backend API via `getDisplayUnits` callback
19
+ 2. Receives `FormulaDisplayInfo` containing token positions and cell references
20
+ 3. Renders the syntax highlighting based on that response
21
+
22
+ This design ensures:
23
+
24
+ - Consistent parsing with the spreadsheet engine
25
+ - Ability to handle complex formulas, 3D references, etc.
26
+ - The editor stays lightweight and focused on UI
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ # From the LogiSheets root directory
32
+ yarn install
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```tsx
38
+ import { FormulaEditor, FormulaDisplayInfo, FormulaFunction } from 'logisheets-formula-editor-react'
39
+
40
+ // Define available functions for autocomplete
41
+ const functions: FormulaFunction[] = [
42
+ {
43
+ name: 'SUM',
44
+ description: 'Adds all the numbers in a range',
45
+ args: [
46
+ { argName: 'number1', description: 'First number or range' },
47
+ { argName: 'number2', description: 'Additional numbers', startRepeated: true },
48
+ ],
49
+ argCount: { ge: 1 },
50
+ },
51
+ // ... more functions
52
+ ]
53
+
54
+ // Backend API to fetch display units
55
+ async function getDisplayUnits(formula: string): Promise<FormulaDisplayInfo | undefined> {
56
+ // Call your backend API here
57
+ // In LogiSheets: workbook.getDisplayUnitsOfFormula(formula)
58
+ return await api.getDisplayUnits(formula)
59
+ }
60
+
61
+ function MyComponent() {
62
+ const [value, setValue] = useState('=SUM(A1:B2)')
63
+
64
+ return (
65
+ <FormulaEditor
66
+ value={value}
67
+ onChange={setValue}
68
+ onSubmit={(v) => console.log('Submitted:', v)}
69
+ getDisplayUnits={getDisplayUnits}
70
+ formulaFunctions={functions}
71
+ sheetName="Sheet1"
72
+ config={{
73
+ fontSize: 14,
74
+ textAlign: 'left',
75
+ wordWrap: false,
76
+ }}
77
+ />
78
+ )
79
+ }
80
+ ```
81
+
82
+ ## Props
83
+
84
+ | Prop | Type | Description |
85
+ |------|------|-------------|
86
+
87
+ | `value` | `string` | Controlled value |
88
+ | `defaultValue` | `string` | Initial value (uncontrolled) |
89
+ | `onChange` | `(value: string) => void` | Called on every change |
90
+ | `onBlur` | `(value: string) => void` | Called when editor loses focus |
91
+ | `onSubmit` | `(value: string) => void` | Called on Enter (without modifiers) |
92
+ | `onCancel` | `() => void` | Called on Escape |
93
+ | `getDisplayUnits` | `(formula: string) => Promise<FormulaDisplayInfo>` | **Required.** Backend API for tokenization |
94
+ | `formulaFunctions` | `FormulaFunction[]` | Available functions for autocomplete |
95
+ | `sheetName` | `string` | Current sheet name (for cell ref highlighting) |
96
+ | `config` | `FormulaEditorConfig` | Styling configuration |
97
+
98
+ ## Config Options
99
+
100
+ ```typescript
101
+ interface FormulaEditorConfig {
102
+ fontSize?: number // Default: 14
103
+ fontFamily?: string // Default: 'Consolas, Monaco, monospace'
104
+ lineHeight?: number // Default: 1.4
105
+ textAlign?: 'left' | 'center' | 'right'
106
+ wordWrap?: boolean // Default: false
107
+ placeholder?: string // Default: 'Enter a formula...'
108
+ readOnly?: boolean // Default: false
109
+ autoFocus?: boolean // Default: false
110
+ }
111
+ ```
112
+
113
+ ## Keyboard Shortcuts
114
+
115
+ | Key | Action |
116
+ |-----|--------|
117
+
118
+ | `Enter` | Submit formula / Select autocomplete item |
119
+ | `Escape` | Cancel / Close autocomplete |
120
+ | `Alt+Enter` | Insert line break |
121
+ | `↑ / ↓` | Navigate autocomplete |
122
+ | `Tab` | Select autocomplete item |
123
+ | `Ctrl+Z / Cmd+Z` | Undo |
124
+ | `Ctrl+Shift+Z / Cmd+Shift+Z` | Redo |
125
+
126
+ ## Why CodeMirror 6?
127
+
128
+ We use CodeMirror 6 instead of a custom canvas-based editor because it provides:
129
+
130
+ - Proper IME (Input Method Editor) support for CJK languages
131
+ - Full accessibility (screen readers, keyboard navigation)
132
+ - Correct text selection and cursor behavior
133
+ - History (undo/redo) out of the box
134
+ - Cross-browser compatibility
135
+ - Extensible theming system
136
+
137
+ ## Development
138
+
139
+ ```bash
140
+ # Start the demo app
141
+ cd packages/formula-editor-react
142
+ yarn dev
143
+
144
+ # Open http://localhost:5173
145
+ ```
146
+
147
+ The demo app includes:
148
+
149
+ - Interactive formula editor
150
+ - Configuration controls (font size, alignment, word wrap)
151
+ - Event logging panel
152
+ - Mock implementation of `getDisplayUnits`
153
+
154
+ ## API Types
155
+
156
+ ```typescript
157
+ // Token info from backend
158
+ interface FormulaDisplayInfo {
159
+ tokenUnits: TokenUnit[]
160
+ cellRefs: CellRef[]
161
+ }
162
+
163
+ interface TokenUnit {
164
+ tokenType: 'funcName' | 'funcArg' | 'cellReference' | 'errorConstant' | 'wrongSuffix' | 'other'
165
+ start: number // 0-based index in formula (excluding leading '=')
166
+ end: number // exclusive
167
+ }
168
+
169
+ interface CellRef {
170
+ workbook?: string
171
+ sheet1?: string
172
+ sheet2?: string
173
+ row1?: number
174
+ col1?: number
175
+ row2?: number
176
+ col2?: number
177
+ }
178
+ ```
179
+
180
+ ## Integration with LogiSheets
181
+
182
+ In the main LogiSheets app, the `getDisplayUnits` function would call:
183
+
184
+ ```typescript
185
+ const getDisplayUnits = async (formula: string) => {
186
+ const result = await workbook.getDisplayUnitsOfFormula(formula)
187
+ if (isErrorMessage(result)) return undefined
188
+ return result
189
+ }
190
+ ```
191
+
192
+ This connects the formula editor to the WASM-based lexer in `lexer4fmt`, which properly handles all Excel formula syntax.
@@ -0,0 +1,17 @@
1
+ import { EditorView } from '@codemirror/view';
2
+ import { FormulaEditorProps } from './types';
3
+ export interface FormulaEditorRef {
4
+ focus: () => void;
5
+ blur: () => void;
6
+ getValue: () => string;
7
+ setValue: (value: string) => void;
8
+ /** Insert text at current cursor position */
9
+ insertText: (text: string) => void;
10
+ /** Replace text in a range, useful for replacing previous insertion */
11
+ replaceRange: (from: number, to: number, text: string) => void;
12
+ /** Get current cursor position */
13
+ getCursorPosition: () => number;
14
+ getView: () => EditorView | null;
15
+ }
16
+ export declare const FormulaEditor: import('react').ForwardRefExoticComponent<FormulaEditorProps & import('react').RefAttributes<FormulaEditorRef>>;
17
+ //# sourceMappingURL=FormulaEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormulaEditor.d.ts","sourceRoot":"","sources":["../src/lib/FormulaEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAEhD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAA;AAE/C,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,QAAQ,EAAE,MAAM,MAAM,CAAA;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,6CAA6C;IAC7C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,uEAAuE;IACvE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D,kCAAkC;IAClC,iBAAiB,EAAE,MAAM,MAAM,CAAA;IAC/B,OAAO,EAAE,MAAM,UAAU,GAAG,IAAI,CAAA;CACnC;AAED,eAAO,MAAM,aAAa,iHAgEzB,CAAA"}