Skip to content

Commit d7d58d5

Browse files
authored
chore(compass-editor): refactor JSON editor actions container into its own component (#6441)
* Refactor JSON editor action container into its own component * Simplified actions container * Fix types
1 parent 5c6c61b commit d7d58d5

File tree

5 files changed

+119
-95
lines changed

5 files changed

+119
-95
lines changed

packages/compass-editor/src/actions.tsx renamed to packages/compass-editor/src/action-button.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { cx } from '@mongodb-js/compass-components';
22
import { css } from '@mongodb-js/compass-components';
3-
import { Button, Icon } from '@mongodb-js/compass-components';
3+
import { Button, Icon, type IconGlyph } from '@mongodb-js/compass-components';
44
import React, { useCallback, useEffect, useState } from 'react';
5+
import type { EditorView } from '@codemirror/view';
6+
7+
export type Action = {
8+
icon: IconGlyph;
9+
label: string;
10+
action: (editor: EditorView) => boolean | void;
11+
};
512

613
const actionButtonStyle = css({
714
flex: 'none',
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import React, { type RefObject } from 'react';
2+
3+
import { type Action, ActionButton, FormatIcon } from './action-button';
4+
import type { EditorRef } from './types';
5+
import { css, cx, spacing } from '@mongodb-js/compass-components';
6+
7+
type ActionsContainerProps = {
8+
copyable: boolean;
9+
formattable: boolean;
10+
customActions?: Action[];
11+
className?: string;
12+
editorRef: RefObject<EditorRef>;
13+
};
14+
15+
const actionsContainerStyle = css({
16+
position: 'absolute',
17+
top: spacing[1],
18+
right: spacing[2],
19+
display: 'none',
20+
gap: spacing[2],
21+
});
22+
23+
export const ActionsContainer = ({
24+
copyable,
25+
formattable,
26+
customActions,
27+
className,
28+
editorRef,
29+
}: ActionsContainerProps) => {
30+
return (
31+
<div
32+
className={cx(
33+
'multiline-editor-actions',
34+
actionsContainerStyle,
35+
className
36+
)}
37+
>
38+
{copyable && (
39+
<ActionButton
40+
label="Copy"
41+
icon="Copy"
42+
onClick={() => {
43+
return editorRef.current?.copyAll() ?? false;
44+
}}
45+
></ActionButton>
46+
)}
47+
{formattable && (
48+
<ActionButton
49+
label="Format"
50+
icon={
51+
<FormatIcon
52+
size={/* leafygreen small */ 14}
53+
role="presentation"
54+
></FormatIcon>
55+
}
56+
onClick={() => {
57+
return editorRef.current?.prettify() ?? false;
58+
}}
59+
></ActionButton>
60+
)}
61+
{customActions &&
62+
customActions.map((action) => {
63+
return (
64+
<ActionButton
65+
key={action.label}
66+
icon={action.icon}
67+
label={action.label}
68+
onClick={() => {
69+
if (!editorRef.current?.editor) {
70+
return false;
71+
}
72+
return action.action(editorRef.current.editor);
73+
}}
74+
></ActionButton>
75+
);
76+
})}
77+
</div>
78+
);
79+
};

packages/compass-editor/src/editor.tsx

Lines changed: 14 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, {
22
useCallback,
33
useEffect,
44
useLayoutEffect,
5-
useMemo,
65
useImperativeHandle,
76
useRef,
87
useState,
@@ -50,7 +49,6 @@ import {
5049
snippetCompletion,
5150
startCompletion,
5251
} from '@codemirror/autocomplete';
53-
import type { IconGlyph } from '@mongodb-js/compass-components';
5452
import {
5553
css,
5654
cx,
@@ -74,7 +72,9 @@ import { tags as t } from '@lezer/highlight';
7472
import { rgba } from 'polished';
7573

7674
import { prettify as _prettify } from './prettify';
77-
import { ActionButton, FormatIcon } from './actions';
75+
import type { Action } from './action-button';
76+
import { ActionsContainer } from './actions-container';
77+
import type { EditorRef } from './types';
7878

7979
// TODO(COMPASS-8453): Re-enable this once the linked tickets are resolved
8080
// https://github.com/codemirror/dev/issues/1458
@@ -697,19 +697,6 @@ function useCodemirrorExtensionCompartment<T>(
697697
return initialExtensionRef.current;
698698
}
699699

700-
export type EditorRef = {
701-
foldAll: () => boolean;
702-
unfoldAll: () => boolean;
703-
copyAll: () => boolean;
704-
prettify: () => boolean;
705-
applySnippet: (template: string) => boolean;
706-
focus: () => boolean;
707-
cursorDocEnd: () => boolean;
708-
startCompletion: () => boolean;
709-
readonly editorContents: string | null;
710-
readonly editor: EditorView | null;
711-
};
712-
713700
const BaseEditor = React.forwardRef<EditorRef, EditorProps>(function BaseEditor(
714701
{
715702
initialText: _initialText,
@@ -1406,20 +1393,6 @@ const multilineEditorContainerDarkModeStyle = css({
14061393
backgroundColor: editorPalette.dark.backgroundColor,
14071394
});
14081395

1409-
const actionsContainerStyle = css({
1410-
position: 'absolute',
1411-
top: spacing[1],
1412-
right: spacing[2],
1413-
display: 'none',
1414-
gap: spacing[2],
1415-
});
1416-
1417-
export type Action = {
1418-
icon: IconGlyph;
1419-
label: string;
1420-
action: (editor: EditorView) => boolean | void;
1421-
};
1422-
14231396
type MultilineEditorProps = EditorProps & {
14241397
customActions?: Action[];
14251398
copyable?: boolean;
@@ -1485,50 +1458,8 @@ const MultilineEditor = React.forwardRef<EditorRef, MultilineEditorProps>(
14851458
[]
14861459
);
14871460

1488-
const actions = useMemo(() => {
1489-
return [
1490-
copyable && (
1491-
<ActionButton
1492-
key="Copy"
1493-
label="Copy"
1494-
icon="Copy"
1495-
onClick={() => {
1496-
return editorRef.current?.copyAll() ?? false;
1497-
}}
1498-
></ActionButton>
1499-
),
1500-
formattable && (
1501-
<ActionButton
1502-
key="Format"
1503-
label="Format"
1504-
icon={
1505-
<FormatIcon
1506-
size={/* leafygreen small */ 14}
1507-
role="presentation"
1508-
></FormatIcon>
1509-
}
1510-
onClick={() => {
1511-
return editorRef.current?.prettify() ?? false;
1512-
}}
1513-
></ActionButton>
1514-
),
1515-
...(customActions ?? []).map((action) => {
1516-
return (
1517-
<ActionButton
1518-
key={action.label}
1519-
icon={action.icon}
1520-
label={action.label}
1521-
onClick={() => {
1522-
if (!editorRef.current?.editor) {
1523-
return false;
1524-
}
1525-
return action.action(editorRef.current.editor);
1526-
}}
1527-
></ActionButton>
1528-
);
1529-
}),
1530-
];
1531-
}, [copyable, formattable, customActions]);
1461+
const hasCustomActions = customActions && customActions.length > 0;
1462+
const hasActions = copyable || formattable || hasCustomActions;
15321463

15331464
return (
15341465
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
@@ -1537,7 +1468,7 @@ const MultilineEditor = React.forwardRef<EditorRef, MultilineEditorProps>(
15371468
className={cx(
15381469
multilineEditorContainerStyle,
15391470
darkMode && multilineEditorContainerDarkModeStyle,
1540-
!!actions.length && multilineEditorContainerWithActionsStyle,
1471+
hasActions && multilineEditorContainerWithActionsStyle,
15411472
className
15421473
)}
15431474
// We want folks to be able to click into the container element
@@ -1559,16 +1490,14 @@ const MultilineEditor = React.forwardRef<EditorRef, MultilineEditorProps>(
15591490
minLines={10}
15601491
{...props}
15611492
></BaseEditor>
1562-
{actions.length > 0 && (
1563-
<div
1564-
className={cx(
1565-
'multiline-editor-actions',
1566-
actionsContainerStyle,
1567-
actionsClassName
1568-
)}
1569-
>
1570-
{actions}
1571-
</div>
1493+
{hasActions && (
1494+
<ActionsContainer
1495+
copyable={copyable}
1496+
formattable={formattable}
1497+
editorRef={editorRef}
1498+
className={actionsClassName}
1499+
customActions={customActions}
1500+
/>
15721501
)}
15731502
</div>
15741503
);

packages/compass-editor/src/index.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type { CompletionWithServerInfo } from './types';
1+
export type { CompletionWithServerInfo, EditorRef } from './types';
22
export { prettify } from './prettify';
33
export type { FormatOptions } from './prettify';
44
export {
@@ -7,14 +7,8 @@ export {
77
setCodemirrorEditorValue,
88
getCodemirrorEditorValue,
99
} from './editor';
10-
export type {
11-
EditorView,
12-
Command,
13-
Annotation,
14-
Action,
15-
EditorRef,
16-
Completer,
17-
} from './editor';
10+
export type { EditorView, Command, Annotation, Completer } from './editor';
11+
export type { Action } from './action-button';
1812
export { createDocumentAutocompleter } from './codemirror/document-autocompleter';
1913
export { createValidationAutocompleter } from './codemirror/validation-autocompleter';
2014
export { createQueryAutocompleter } from './codemirror/query-autocompleter';

packages/compass-editor/src/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { EditorView } from '@codemirror/view';
2+
13
export type CompletionWithServerInfo = {
24
name?: string;
35
value?: string;
@@ -14,3 +16,16 @@ export type CompletionWithServerInfo = {
1416
/** Optional completion description */
1517
description?: string;
1618
};
19+
20+
export type EditorRef = {
21+
foldAll: () => boolean;
22+
unfoldAll: () => boolean;
23+
copyAll: () => boolean;
24+
prettify: () => boolean;
25+
applySnippet: (template: string) => boolean;
26+
focus: () => boolean;
27+
cursorDocEnd: () => boolean;
28+
startCompletion: () => boolean;
29+
readonly editorContents: string | null;
30+
readonly editor: EditorView | null;
31+
};

0 commit comments

Comments
 (0)