Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(compass-editor): refactor JSON editor actions container into its own component #6441

Merged
merged 3 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Contributor Author

@kraenhansen kraenhansen Nov 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actions.length was actually always > 0, since we didn't trim the array from false values that could arise from the copyable or formattable being false.

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 = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this here to be able to reference it from both editor.tsx and actions-container.tsx without introducing a circular import dependency.

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;
};
Loading