Skip to content

Commit 4e82930

Browse files
committed
Refactor code in chip expression editor and rich template editor
1 parent 7c2d492 commit 4e82930

File tree

6 files changed

+333
-322
lines changed

6 files changed

+333
-322
lines changed

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx

Lines changed: 36 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,10 @@ import { HelperPaneToggleButton } from "./HelperPaneToggleButton";
5050
import { HelperPane } from "./HelperPane";
5151
import { listContinuationKeymap } from "../../../ExpandedEditor/utils/templateUtils";
5252
import { markdown } from "@codemirror/lang-markdown";
53+
import { HELPER_PANE_WIDTH } from "../constants";
54+
import { processFunctionWithArguments } from "../utils";
55+
import { useHelperPaneClickOutside, useHelperPane } from "../hooks/useHelperPane";
5356

54-
type HelperPaneState = {
55-
isOpen: boolean;
56-
top: number;
57-
left: number;
58-
}
5957
export type ChipExpressionEditorComponentProps = {
6058
onTokenRemove?: (token: string) => void;
6159
onTokenClick?: (token: string) => void;
@@ -93,11 +91,7 @@ export type ChipExpressionEditorComponentProps = {
9391
enableListContinuation?: boolean;
9492
}
9593

96-
const HELPER_PANE_WIDTH = 300;
97-
9894
export const ChipExpressionEditorComponent = (props: ChipExpressionEditorComponentProps) => {
99-
const [helperPaneState, setHelperPaneState] = useState<HelperPaneState>({ isOpen: false, top: 0, left: 0 });
100-
10195
const editorRef = useRef<HTMLDivElement>(null);
10296
const helperPaneRef = useRef<HTMLDivElement>(null);
10397
const fieldContainerRef = useRef<HTMLDivElement>(null);
@@ -110,6 +104,17 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone
110104
const { expressionEditor } = useFormContext();
111105
const expressionEditorRpcManager = expressionEditor?.rpcManager;
112106

107+
// Helper pane state management
108+
const { helperPaneState, setHelperPaneState, handleManualToggle, handleKeyboardToggle } = useHelperPane(
109+
{
110+
editorRef,
111+
toggleButtonRef: helperPaneToggleButtonRef,
112+
helperPaneWidth: HELPER_PANE_WIDTH,
113+
onStateChange: props.onHelperPaneStateChange
114+
},
115+
() => viewRef.current?.coordsAtPos(viewRef.current.state.selection.main.head) || null
116+
);
117+
113118
const needTokenRefetchListner = buildNeedTokenRefetchListner(() => {
114119
setIsTokenUpdateScheduled(true);
115120
});
@@ -158,46 +163,12 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone
158163
return buildCompletionSource(waitForStateChange);
159164
}, [props.completions]);
160165

161-
const handleHelperPaneKeyboardToggle = () => {
162-
if (!viewRef.current) return;
163-
164-
setHelperPaneState(prev => {
165-
// If helper pane is open, just close it
166-
if (prev.isOpen) {
167-
return { ...prev, isOpen: false };
168-
}
169-
170-
// If helper pane is closed, open it at the cursor position
171-
const view = viewRef.current!;
172-
const cursorCoords = view.coordsAtPos(view.state.selection.main.head);
173-
174-
if (cursorCoords && editorRef.current) {
175-
const editorRect = editorRef.current.getBoundingClientRect();
176-
let top = cursorCoords.bottom - editorRect.top;
177-
let left = cursorCoords.left - editorRect.left;
178-
179-
const viewportWidth = window.innerWidth;
180-
const absoluteLeft = cursorCoords.left;
181-
const overflow = absoluteLeft + HELPER_PANE_WIDTH - viewportWidth;
182-
183-
if (overflow > 0) {
184-
left -= overflow;
185-
}
186-
187-
return { isOpen: true, top, left };
188-
}
189-
190-
// Fallback if cursor coordinates aren't available
191-
return { isOpen: true, top: 0, left: 0 };
192-
});
193-
};
194-
195166
const helperPaneKeymap = buildHelperPaneKeymap(
196167
() => helperPaneState.isOpen,
197168
() => {
198169
setHelperPaneState(prev => ({ ...prev, isOpen: false }));
199170
},
200-
handleHelperPaneKeyboardToggle
171+
handleKeyboardToggle
201172
);
202173

203174
const onHelperItemSelect = async (value: string, options: HelperpaneOnChangeOptions) => {
@@ -219,36 +190,9 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone
219190
// and extracting args
220191
if (newValue.endsWith('()') || newValue.endsWith(')}')) {
221192
if (props.extractArgsFromFunction) {
222-
try {
223-
// Extract the function definition from string templates like "${func()}"
224-
let functionDef = newValue;
225-
let prefix = '';
226-
let suffix = '';
227-
228-
// Check if it's within a string template
229-
const stringTemplateMatch = newValue.match(/^(.*\$\{)([^}]+)(\}.*)$/);
230-
if (stringTemplateMatch) {
231-
prefix = stringTemplateMatch[1];
232-
functionDef = stringTemplateMatch[2];
233-
suffix = stringTemplateMatch[3];
234-
}
235-
236-
let cursorPositionForExtraction = from + prefix.length + functionDef.length - 1;
237-
if (functionDef.endsWith(')}')) {
238-
cursorPositionForExtraction -= 1;
239-
}
240-
241-
const fnSignature = await props.extractArgsFromFunction(functionDef, cursorPositionForExtraction);
242-
243-
if (fnSignature && fnSignature.args && fnSignature.args.length > 0) {
244-
const placeholderArgs = fnSignature.args.map((arg, index) => `$${index + 1}`);
245-
const updatedFunctionDef = functionDef.slice(0, -2) + '(' + placeholderArgs.join(', ') + ')';
246-
finalValue = prefix + updatedFunctionDef + suffix;
247-
cursorPosition = from + prefix.length + updatedFunctionDef.length - 1;
248-
}
249-
} catch (error) {
250-
console.warn('Failed to extract function arguments:', error);
251-
}
193+
const result = await processFunctionWithArguments(newValue, from, props.extractArgsFromFunction);
194+
finalValue = result.finalValue;
195+
cursorPosition = from + result.cursorAdjustment;
252196
}
253197
}
254198

@@ -262,50 +206,6 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone
262206
setHelperPaneState(prev => ({ ...prev, isOpen: !options.closeHelperPane }));
263207
}
264208

265-
const handleHelperPaneManualToggle = () => {
266-
if (
267-
!helperPaneToggleButtonRef?.current ||
268-
!editorRef?.current
269-
) return;
270-
271-
// Save current cursor position before toggling
272-
if (viewRef.current) {
273-
const selection = viewRef.current.state.selection.main;
274-
}
275-
276-
const buttonRect = helperPaneToggleButtonRef.current.getBoundingClientRect();
277-
const editorRect = editorRef.current?.getBoundingClientRect();
278-
let top = buttonRect.bottom - editorRect.top;
279-
let left = buttonRect.left - editorRect.left;
280-
281-
// Add overflow correction for window boundaries
282-
const viewportWidth = window.innerWidth;
283-
const absoluteLeft = buttonRect.left;
284-
const overflow = absoluteLeft + HELPER_PANE_WIDTH - viewportWidth;
285-
286-
if (overflow > 0) {
287-
left -= overflow;
288-
}
289-
290-
setHelperPaneState(prev => ({
291-
...prev,
292-
top,
293-
left,
294-
isOpen: !prev.isOpen
295-
}));
296-
}
297-
298-
// Expose helper pane state to parent component
299-
useEffect(() => {
300-
if (props.onHelperPaneStateChange) {
301-
props.onHelperPaneStateChange({
302-
isOpen: helperPaneState.isOpen,
303-
ref: helperPaneToggleButtonRef,
304-
toggle: handleHelperPaneManualToggle
305-
});
306-
}
307-
}, [helperPaneState.isOpen]);
308-
309209
useEffect(() => {
310210
if (!editorRef.current) return;
311211
const startState = EditorState.create({
@@ -418,40 +318,23 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone
418318
setIsTokenUpdateScheduled(true);
419319
}, [Boolean(props.sanitizedExpression), Boolean(props.rawExpression)]);
420320

421-
useEffect(() => {
422-
const handleClickOutside = (event: MouseEvent) => {
423-
if (!helperPaneState.isOpen) return;
424-
425-
const target = event.target as Element;
426-
const isClickInsideEditor = editorRef.current?.contains(target);
427-
const isClickInsideHelperPane = helperPaneRef.current?.contains(target);
428-
const isClickOnToggleButton = helperPaneToggleButtonRef.current?.contains(target);
429-
const isClickInsideToolbar = props.toolbarRef?.current?.contains(target);
430-
431-
if (!isClickInsideEditor && !isClickInsideHelperPane && !isClickOnToggleButton && !isClickInsideToolbar) {
432-
setHelperPaneState(prev => ({ ...prev, isOpen: false }));
433-
viewRef.current?.dom.blur();
434-
}
435-
};
436-
437-
const handleEscapeKey = (event: KeyboardEvent) => {
438-
if (!helperPaneState.isOpen) return;
439-
if (event.key === 'Escape') {
440-
event.preventDefault();
441-
event.stopPropagation();
442-
setHelperPaneState(prev => ({ ...prev, isOpen: false }));
443-
}
444-
};
445-
446-
if (helperPaneState.isOpen) {
447-
document.addEventListener('mousedown', handleClickOutside);
448-
document.addEventListener('keydown', handleEscapeKey);
321+
// Handle click outside and escape key for helper pane
322+
useHelperPaneClickOutside({
323+
enabled: helperPaneState.isOpen,
324+
refs: {
325+
editor: editorRef,
326+
helperPane: helperPaneRef,
327+
toggleButton: helperPaneToggleButtonRef,
328+
toolbar: props.toolbarRef
329+
},
330+
onClickOutside: () => {
331+
setHelperPaneState(prev => ({ ...prev, isOpen: false }));
332+
viewRef.current?.dom.blur();
333+
},
334+
onEscapeKey: () => {
335+
setHelperPaneState(prev => ({ ...prev, isOpen: false }));
449336
}
450-
return () => {
451-
document.removeEventListener('mousedown', handleClickOutside);
452-
document.removeEventListener('keydown', handleEscapeKey);
453-
};
454-
}, [helperPaneState.isOpen, props.toolbarRef]);
337+
});
455338

456339
const showToggle = props.showHelperPaneToggle !== false && props.isExpandedVersion;
457340

@@ -461,7 +344,7 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone
461344
<HelperPaneToggleButton
462345
ref={helperPaneToggleButtonRef}
463346
isOpen={helperPaneState.isOpen}
464-
onClick={handleHelperPaneManualToggle}
347+
onClick={handleManualToggle}
465348
title="Toggle Helper Panel (Ctrl+/ or Cmd+/)"
466349
/>
467350
)}
@@ -491,7 +374,7 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone
491374
{!props.isExpandedVersion &&
492375
<FloatingToggleButton
493376
ref={helperPaneToggleButtonRef}
494-
onClick={handleHelperPaneManualToggle}
377+
onClick={handleManualToggle}
495378
title={helperPaneState.isOpen ? "Close Helper Panel" : "Open Helper Panel"}
496379
>
497380
{helperPaneState.isOpen ? <CloseHelperIcon /> : <OpenHelperIcon />}

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@ export const ARROW_LEFT_MARKER = '#$ARROWLEFT';
3737
export const FOCUS_MARKER = '#$FOCUS';
3838
export const COMPLETIONS_MARKER = '#$COMPLETIONS';
3939
export const HELPER_MARKER = '#$HELPER';
40+
41+
// Helper pane dimensions
42+
export const HELPER_PANE_WIDTH = 300;

0 commit comments

Comments
 (0)