Skip to content

Commit

Permalink
chore(compass-editor): refactor JSON editor actions container into it…
Browse files Browse the repository at this point in the history
…s own component (#6441)

* Refactor JSON editor action container into its own component

* Simplified actions container

* Fix types
  • Loading branch information
kraenhansen authored Nov 5, 2024
1 parent 5c6c61b commit d7d58d5
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { cx } from '@mongodb-js/compass-components';
import { css } from '@mongodb-js/compass-components';
import { Button, Icon } from '@mongodb-js/compass-components';
import { Button, Icon, type IconGlyph } from '@mongodb-js/compass-components';
import React, { useCallback, useEffect, useState } from 'react';
import type { EditorView } from '@codemirror/view';

export type Action = {
icon: IconGlyph;
label: string;
action: (editor: EditorView) => boolean | void;
};

const actionButtonStyle = css({
flex: 'none',
Expand Down
79 changes: 79 additions & 0 deletions packages/compass-editor/src/actions-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { type RefObject } from 'react';

import { type Action, ActionButton, FormatIcon } from './action-button';
import type { EditorRef } from './types';
import { css, cx, spacing } from '@mongodb-js/compass-components';

type ActionsContainerProps = {
copyable: boolean;
formattable: boolean;
customActions?: Action[];
className?: string;
editorRef: RefObject<EditorRef>;
};

const actionsContainerStyle = css({
position: 'absolute',
top: spacing[1],
right: spacing[2],
display: 'none',
gap: spacing[2],
});

export const ActionsContainer = ({
copyable,
formattable,
customActions,
className,
editorRef,
}: ActionsContainerProps) => {
return (
<div
className={cx(
'multiline-editor-actions',
actionsContainerStyle,
className
)}
>
{copyable && (
<ActionButton
label="Copy"
icon="Copy"
onClick={() => {
return editorRef.current?.copyAll() ?? false;
}}
></ActionButton>
)}
{formattable && (
<ActionButton
label="Format"
icon={
<FormatIcon
size={/* leafygreen small */ 14}
role="presentation"
></FormatIcon>
}
onClick={() => {
return editorRef.current?.prettify() ?? false;
}}
></ActionButton>
)}
{customActions &&
customActions.map((action) => {
return (
<ActionButton
key={action.label}
icon={action.icon}
label={action.label}
onClick={() => {
if (!editorRef.current?.editor) {
return false;
}
return action.action(editorRef.current.editor);
}}
></ActionButton>
);
})}
</div>
);
};
99 changes: 14 additions & 85 deletions packages/compass-editor/src/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useImperativeHandle,
useRef,
useState,
Expand Down Expand Up @@ -50,7 +49,6 @@ import {
snippetCompletion,
startCompletion,
} from '@codemirror/autocomplete';
import type { IconGlyph } from '@mongodb-js/compass-components';
import {
css,
cx,
Expand All @@ -74,7 +72,9 @@ import { tags as t } from '@lezer/highlight';
import { rgba } from 'polished';

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

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

export type EditorRef = {
foldAll: () => boolean;
unfoldAll: () => boolean;
copyAll: () => boolean;
prettify: () => boolean;
applySnippet: (template: string) => boolean;
focus: () => boolean;
cursorDocEnd: () => boolean;
startCompletion: () => boolean;
readonly editorContents: string | null;
readonly editor: EditorView | null;
};

const BaseEditor = React.forwardRef<EditorRef, EditorProps>(function BaseEditor(
{
initialText: _initialText,
Expand Down Expand Up @@ -1406,20 +1393,6 @@ const multilineEditorContainerDarkModeStyle = css({
backgroundColor: editorPalette.dark.backgroundColor,
});

const actionsContainerStyle = css({
position: 'absolute',
top: spacing[1],
right: spacing[2],
display: 'none',
gap: spacing[2],
});

export type Action = {
icon: IconGlyph;
label: string;
action: (editor: EditorView) => boolean | void;
};

type MultilineEditorProps = EditorProps & {
customActions?: Action[];
copyable?: boolean;
Expand Down Expand Up @@ -1485,50 +1458,8 @@ const MultilineEditor = React.forwardRef<EditorRef, MultilineEditorProps>(
[]
);

const actions = useMemo(() => {
return [
copyable && (
<ActionButton
key="Copy"
label="Copy"
icon="Copy"
onClick={() => {
return editorRef.current?.copyAll() ?? false;
}}
></ActionButton>
),
formattable && (
<ActionButton
key="Format"
label="Format"
icon={
<FormatIcon
size={/* leafygreen small */ 14}
role="presentation"
></FormatIcon>
}
onClick={() => {
return editorRef.current?.prettify() ?? false;
}}
></ActionButton>
),
...(customActions ?? []).map((action) => {
return (
<ActionButton
key={action.label}
icon={action.icon}
label={action.label}
onClick={() => {
if (!editorRef.current?.editor) {
return false;
}
return action.action(editorRef.current.editor);
}}
></ActionButton>
);
}),
];
}, [copyable, formattable, customActions]);
const hasCustomActions = customActions && customActions.length > 0;
const hasActions = copyable || formattable || hasCustomActions;

return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
Expand All @@ -1537,7 +1468,7 @@ const MultilineEditor = React.forwardRef<EditorRef, MultilineEditorProps>(
className={cx(
multilineEditorContainerStyle,
darkMode && multilineEditorContainerDarkModeStyle,
!!actions.length && multilineEditorContainerWithActionsStyle,
hasActions && multilineEditorContainerWithActionsStyle,
className
)}
// We want folks to be able to click into the container element
Expand All @@ -1559,16 +1490,14 @@ const MultilineEditor = React.forwardRef<EditorRef, MultilineEditorProps>(
minLines={10}
{...props}
></BaseEditor>
{actions.length > 0 && (
<div
className={cx(
'multiline-editor-actions',
actionsContainerStyle,
actionsClassName
)}
>
{actions}
</div>
{hasActions && (
<ActionsContainer
copyable={copyable}
formattable={formattable}
editorRef={editorRef}
className={actionsClassName}
customActions={customActions}
/>
)}
</div>
);
Expand Down
12 changes: 3 additions & 9 deletions packages/compass-editor/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { CompletionWithServerInfo } from './types';
export type { CompletionWithServerInfo, EditorRef } from './types';
export { prettify } from './prettify';
export type { FormatOptions } from './prettify';
export {
Expand All @@ -7,14 +7,8 @@ export {
setCodemirrorEditorValue,
getCodemirrorEditorValue,
} from './editor';
export type {
EditorView,
Command,
Annotation,
Action,
EditorRef,
Completer,
} from './editor';
export type { EditorView, Command, Annotation, Completer } from './editor';
export type { Action } from './action-button';
export { createDocumentAutocompleter } from './codemirror/document-autocompleter';
export { createValidationAutocompleter } from './codemirror/validation-autocompleter';
export { createQueryAutocompleter } from './codemirror/query-autocompleter';
Expand Down
15 changes: 15 additions & 0 deletions packages/compass-editor/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { EditorView } from '@codemirror/view';

export type CompletionWithServerInfo = {
name?: string;
value?: string;
Expand All @@ -14,3 +16,16 @@ export type CompletionWithServerInfo = {
/** Optional completion description */
description?: string;
};

export type EditorRef = {
foldAll: () => boolean;
unfoldAll: () => boolean;
copyAll: () => boolean;
prettify: () => boolean;
applySnippet: (template: string) => boolean;
focus: () => boolean;
cursorDocEnd: () => boolean;
startCompletion: () => boolean;
readonly editorContents: string | null;
readonly editor: EditorView | null;
};

0 comments on commit d7d58d5

Please sign in to comment.