-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathfield-editor.tsx
115 lines (103 loc) · 3.04 KB
/
field-editor.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { autocompletion } from '@codemirror/autocomplete';
import { EditorView } from '@uiw/react-codemirror';
import { Editor } from '@/components/primitives/editor';
import { Popover, PopoverTrigger } from '@/components/primitives/popover';
import { createAutocompleteSource } from '@/utils/liquid-autocomplete';
import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables';
import { useCallback, useMemo, useRef } from 'react';
import { useVariables } from './hooks/use-variables';
import { createVariablePlugin } from './variable-plugin';
import { variablePillTheme } from './variable-plugin/variable-theme';
import { VariablePopover } from './variable-popover';
type CompletionRange = {
from: number;
to: number;
} | null;
type FieldEditorProps = {
value: string;
onChange: (value: string) => void;
variables: LiquidVariable[];
placeholder?: string;
autoFocus?: boolean;
size?: 'default' | 'lg';
id?: string;
singleLine?: boolean;
indentWithTab?: boolean;
};
const baseExtensions = [EditorView.lineWrapping, variablePillTheme];
export function FieldEditor({
value,
onChange,
variables,
placeholder,
autoFocus,
size = 'default',
id,
singleLine,
indentWithTab,
}: FieldEditorProps) {
const viewRef = useRef<EditorView | null>(null);
const lastCompletionRef = useRef<CompletionRange>(null);
const { selectedVariable, setSelectedVariable, handleVariableSelect, handleVariableUpdate } = useVariables(
viewRef,
onChange
);
const completionSource = useMemo(() => createAutocompleteSource(variables), [variables]);
const autocompletionExtension = useMemo(
() =>
autocompletion({
override: [completionSource],
closeOnBlur: true,
defaultKeymap: true,
activateOnTyping: true,
}),
[completionSource]
);
const variablePlugin = useMemo(
() =>
createVariablePlugin({
viewRef,
lastCompletionRef,
onSelect: handleVariableSelect,
}),
[handleVariableSelect]
);
const extensions = useMemo(
() => [...baseExtensions, autocompletionExtension, variablePlugin],
[autocompletionExtension, variablePlugin]
);
const handleOpenChange = useCallback(
(open: boolean) => {
if (!open) {
setTimeout(() => setSelectedVariable(null), 0);
}
},
[setSelectedVariable]
);
return (
<div className="relative">
<Editor
fontFamily="inherit"
singleLine={singleLine}
indentWithTab={indentWithTab}
size={size}
basicSetup={{
defaultKeymap: true,
}}
className="flex-1"
autoFocus={autoFocus}
placeholder={placeholder}
id={id}
extensions={extensions}
value={value}
onChange={onChange}
/>
<Popover open={!!selectedVariable} onOpenChange={handleOpenChange}>
<PopoverTrigger asChild>
<div />
</PopoverTrigger>
{selectedVariable && <VariablePopover variable={selectedVariable.value} onUpdate={handleVariableUpdate} />}
</Popover>
</div>
);
}