From 209db31388051399dbc8d642710ffc66cb04aa39 Mon Sep 17 00:00:00 2001
From: nichenqin
Date: Thu, 31 Oct 2024 08:13:57 +0800
Subject: [PATCH 1/4] chore: fix formula variable insertion issue
---
.../components/formula/formula-editor.svelte | 35 ++++++++++---------
1 file changed, 19 insertions(+), 16 deletions(-)
diff --git a/apps/frontend/src/lib/components/formula/formula-editor.svelte b/apps/frontend/src/lib/components/formula/formula-editor.svelte
index bd064b2f5..abbba8165 100644
--- a/apps/frontend/src/lib/components/formula/formula-editor.svelte
+++ b/apps/frontend/src/lib/components/formula/formula-editor.svelte
@@ -311,16 +311,17 @@
editor.dispatch(transaction)
} else {
const fieldWithBrackets = `{{${suggestion}}}`
-
- // 使用正则表达式找到光标位置最近的完整变量
const fullText = editor.state.doc.toString()
- let start = cursor
- let end = cursor
+
+ // 检查光标是否在变量内部
+ let isInsideVariable = false
+ let variableStart = -1
+ let variableEnd = -1
// 向前搜索 {{
for (let i = cursor; i >= 0; i--) {
if (fullText.slice(i, i + 2) === "{{") {
- start = i
+ variableStart = i
break
}
}
@@ -328,30 +329,32 @@
// 向后搜索 }}
for (let i = cursor; i < fullText.length; i++) {
if (fullText.slice(i, i + 2) === "}}") {
- end = i + 2
+ variableEnd = i + 2
break
}
}
- // 检查找到的范围是否是一个有效的变量(不超过最近的逗号)
- const textBetween = fullText.slice(start, end)
- const isValidVariable = textBetween.includes("{{") && textBetween.includes("}}") && !textBetween.includes(",")
+ // 判断光标是否在变量内部
+ if (variableStart !== -1 && variableEnd !== -1) {
+ const textBetween = fullText.slice(variableStart, variableEnd)
+ isInsideVariable = textBetween.includes("{{") && textBetween.includes("}}") && !textBetween.includes(",")
+ }
- if (isValidVariable) {
- // 替换找到的变量
+ // 如果光标在变量内部,替换变量
+ // 如果光标在变量后面或其他位置,直接在当前位置插入
+ if (isInsideVariable && cursor < variableEnd) {
const transaction = editor.state.update({
changes: {
- from: start,
- to: end,
+ from: variableStart,
+ to: variableEnd,
insert: fieldWithBrackets,
},
selection: {
- anchor: start + fieldWithBrackets.length,
+ anchor: variableStart + fieldWithBrackets.length,
},
})
editor.dispatch(transaction)
} else {
- // 不在变量内部或范��无效,直接在当前位置插入新变量
const transaction = editor.state.update({
changes: {
from: cursor,
@@ -391,7 +394,7 @@
{/if}
-
+
{#each suggestions as suggestion}
{@const isSelected = suggestion === selectedSuggestion}
{@const isFunction = functions.includes(suggestion)}
From 4b60be2f318362a67bd813696dbb891cab02fce8 Mon Sep 17 00:00:00 2001
From: nichenqin
Date: Thu, 31 Oct 2024 11:39:31 +0800
Subject: [PATCH 2/4] feat: add formula syntax & examples
---
apps/frontend/package.json | 1 +
.../components/formula/formula-editor.svelte | 136 +++++--
bun.lockb | Bin 589040 -> 589496 bytes
packages/formula/src/formula.visitor.ts | 8 +-
.../src/formula/formula.registry.test.ts | 25 ++
.../formula/src/formula/formula.registry.ts | 357 ++++++++++++++++++
packages/formula/src/formula/registry.ts | 142 -------
packages/formula/src/types.ts | 4 +-
8 files changed, 503 insertions(+), 170 deletions(-)
create mode 100644 packages/formula/src/formula/formula.registry.test.ts
create mode 100644 packages/formula/src/formula/formula.registry.ts
delete mode 100644 packages/formula/src/formula/registry.ts
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 7fc0be1bd..dedb883f2 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -85,6 +85,7 @@
"@codemirror/language": "^6.10.3",
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.34.1",
+ "@floating-ui/dom": "^1.6.12",
"@formkit/auto-animate": "^0.8.2",
"@internationalized/date": "^3.5.6",
"@svelte-put/clickoutside": "^3.0.2",
diff --git a/apps/frontend/src/lib/components/formula/formula-editor.svelte b/apps/frontend/src/lib/components/formula/formula-editor.svelte
index abbba8165..8f3a2c943 100644
--- a/apps/frontend/src/lib/components/formula/formula-editor.svelte
+++ b/apps/frontend/src/lib/components/formula/formula-editor.svelte
@@ -16,6 +16,8 @@
import { derived } from "svelte/store"
import FieldIcon from "../blocks/field-icon/field-icon.svelte"
import { type Field } from "@undb/table"
+ import { computePosition, flip, shift, offset } from "@floating-ui/dom"
+ import { globalFormulaRegistry } from "@undb/formula/src/formula/formula.registry"
const functions = FORMULA_FUNCTIONS
@@ -31,8 +33,15 @@
export let value: string = ""
let editor: EditorView
- let suggestions: string[] = [...functions, ...$fields]
+ let formulaSuggestions: string[] = [...functions]
+ let fieldSuggestions: string[] = [...$fields]
+
+ $: suggestions = [...formulaSuggestions, ...fieldSuggestions]
+
let selectedSuggestion: string = ""
+ let hoverSuggestion: string = ""
+
+ $: hoverFormula = hoverSuggestion ? globalFormulaRegistry.get(hoverSuggestion as FormulaFunction) : undefined
const highlightStyle = HighlightStyle.define([
{ tag: tags.keyword, color: "#5c6bc0" },
@@ -281,8 +290,8 @@
if (!isInsideParens) {
const functionNode = visitor.getNearestFunctionNode()
if (functionNode) {
- const functionStart = functionNode.start.startIndex
- const functionNameLength = functionNode.IDENTIFIER().text.length
+ const functionStart = functionNode.start.tokenIndex
+ const functionNameLength = functionNode.IDENTIFIER().getText().length
const transaction = editor.state.update({
changes: {
from: functionStart,
@@ -383,9 +392,30 @@
errorMessage = (error as Error).message
}
}
+
+ let hoverSuggestionContainer: HTMLElement
+ let editorContainerWrapper: HTMLElement
+
+ function update() {
+ if (hoverSuggestionContainer && editorContainerWrapper && hoverFormula) {
+ computePosition(editorContainerWrapper, hoverSuggestionContainer, {
+ placement: "left-start",
+ middleware: [flip(), shift({ padding: 5 }), offset(10)],
+ }).then(({ x, y }) => {
+ Object.assign(hoverSuggestionContainer.style, {
+ left: `${x}px`,
+ top: `${y}px`,
+ })
+ })
+ }
+ }
+
+ onMount(() => {
+ update()
+ })
-
+
{#if errorMessage}
@@ -395,32 +425,94 @@
{/if}
- {#each suggestions as suggestion}
+ Formula
+ {#each formulaSuggestions as suggestion}
{@const isSelected = suggestion === selectedSuggestion}
- {@const isFunction = functions.includes(suggestion)}
- {@const isField = !isFunction}
-
+
+
+ {#if hoverFormula}
+
+
+
+ {hoverSuggestion}()
+
+
+
+
+
{hoverFormula.description}
+
+
Syntax
+ {#each hoverFormula.syntax as syntax}
+
+ {syntax}
+
+ {/each}
+
+ {#if hoverFormula.examples && hoverFormula.examples.length > 0}
+
Examples
+
+ {#each hoverFormula.examples as example}
+
+ {example[0]}
+ {#if example[1]}
+
+ => {example[1]}
+
+ {/if}
+
+ {/each}
+
+ {/if}
+
+ {/if}
+