Skip to content

Commit

Permalink
move context methods into hooks (#2450)
Browse files Browse the repository at this point in the history
* extract useCopyQuery hook

* extract useMergeQuery hook

* extract usePrettifyQuery hook

* extract useAutoCompleteLeafs hook

* add changeset
  • Loading branch information
thomasheyenbrock authored May 31, 2022
1 parent a0b02ed commit 1e6fc68
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 141 deletions.
6 changes: 6 additions & 0 deletions .changeset/old-pillows-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphiql/react': minor
'graphiql': patch
---

Extract the `copy`, `merge`, `prettify`, and `autoCompleteLeafs` functions into hooks and remove these functions from the editor context value
90 changes: 3 additions & 87 deletions packages/graphiql-react/src/editor/context.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { fillLeafs, GetDefaultFieldNamesFn } from '@graphiql/toolkit';
import { DocumentNode, OperationDefinitionNode } from 'graphql';
import { VariableToType } from 'graphql-language-service';
import { ReactNode, useCallback, useMemo, useState } from 'react';
import { useSchemaContext } from '../schema';
import { ReactNode, useMemo, useState } from 'react';

import { createContextHook, createNullableContext } from '../utility/context';
import { useCopyQuery, useMergeQuery, usePrettifyEditors } from './hooks';
import { CodeMirrorEditor } from './types';

export type CodeMirrorEditorWithOperationFacts = CodeMirrorEditor & {
Expand All @@ -16,10 +13,6 @@ export type CodeMirrorEditorWithOperationFacts = CodeMirrorEditor & {
};

export type EditorContextType = {
autoCompleteLeafs(): string | undefined;
copy(): void;
merge(): void;
prettify(): void;
headerEditor: CodeMirrorEditor | null;
queryEditor: CodeMirrorEditorWithOperationFacts | null;
responseEditor: CodeMirrorEditor | null;
Expand All @@ -34,17 +27,7 @@ export const EditorContext = createNullableContext<EditorContextType>(
'EditorContext',
);

type EditorContextProviderProps = {
children: ReactNode;
getDefaultFieldNames?: GetDefaultFieldNamesFn;
onCopyQuery?(query: string): void;
};

export function EditorContextProvider(props: EditorContextProviderProps) {
const { schema } = useSchemaContext({
nonNull: true,
caller: EditorContextProvider,
});
export function EditorContextProvider(props: { children: ReactNode }) {
const [headerEditor, setHeaderEditor] = useState<CodeMirrorEditor | null>(
null,
);
Expand All @@ -59,66 +42,8 @@ export function EditorContextProvider(props: EditorContextProviderProps) {
null,
);

const autoCompleteLeafs = useCallback<
EditorContextType['autoCompleteLeafs']
>(() => {
if (!queryEditor) {
return;
}

const query = queryEditor.getValue();
const { insertions, result } = fillLeafs(
schema,
query,
props.getDefaultFieldNames,
);
if (insertions && insertions.length > 0) {
queryEditor.operation(() => {
const cursor = queryEditor.getCursor();
const cursorIndex = queryEditor.indexFromPos(cursor);
queryEditor.setValue(result || '');
let added = 0;
const markers = insertions.map(({ index, string }) =>
queryEditor.markText(
queryEditor.posFromIndex(index + added),
queryEditor.posFromIndex(index + (added += string.length)),
{
className: 'autoInsertedLeaf',
clearOnEnter: true,
title: 'Automatically added leaf fields',
},
),
);
setTimeout(() => markers.forEach(marker => marker.clear()), 7000);
let newCursorIndex = cursorIndex;
insertions.forEach(({ index, string }) => {
if (index < cursorIndex) {
newCursorIndex += string.length;
}
});
queryEditor.setCursor(queryEditor.posFromIndex(newCursorIndex));
});
}

return result;
}, [props.getDefaultFieldNames, queryEditor, schema]);

const copy = useCopyQuery({ queryEditor, onCopyQuery: props.onCopyQuery });

const merge = useMergeQuery({ queryEditor, schema });

const prettify = usePrettifyEditors({
queryEditor,
variableEditor,
headerEditor,
});

const value = useMemo<EditorContextType>(
() => ({
autoCompleteLeafs,
copy,
merge,
prettify,
headerEditor,
queryEditor,
responseEditor,
Expand All @@ -128,16 +53,7 @@ export function EditorContextProvider(props: EditorContextProviderProps) {
setResponseEditor,
setVariableEditor,
}),
[
autoCompleteLeafs,
copy,
merge,
prettify,
headerEditor,
queryEditor,
responseEditor,
variableEditor,
],
[headerEditor, queryEditor, responseEditor, variableEditor],
);

return (
Expand Down
7 changes: 5 additions & 2 deletions packages/graphiql-react/src/editor/header-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
useChangeHandler,
useCompletion,
useKeyMap,
useMergeQuery,
usePrettifyEditors,
useResizeEditor,
useSynchronizeValue,
} from './hooks';
Expand All @@ -30,13 +32,14 @@ export function useHeaderEditor({
shouldPersistHeaders = false,
value,
}: UseHeaderEditorArgs = {}) {
const { headerEditor, merge, prettify, setHeaderEditor } = useEditorContext({
const { headerEditor, setHeaderEditor } = useEditorContext({
nonNull: true,
caller: useHeaderEditor,
});
const storage = useStorageContext();
const merge = useMergeQuery({ caller: useHeaderEditor });
const prettify = usePrettifyEditors({ caller: useHeaderEditor });
const ref = useRef<HTMLDivElement>(null);

const initialValue = useRef(value ?? storage?.get(STORAGE_KEY) ?? '');

useEffect(() => {
Expand Down
102 changes: 81 additions & 21 deletions packages/graphiql-react/src/editor/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { mergeAst } from '@graphiql/toolkit';
import { fillLeafs, GetDefaultFieldNamesFn, mergeAst } from '@graphiql/toolkit';
import { EditorChange } from 'codemirror';
import copyToClipboard from 'copy-to-clipboard';
import { GraphQLSchema, parse, print } from 'graphql';
import { parse, print } from 'graphql';
import { RefObject, useCallback, useEffect, useRef } from 'react';

import { useExplorerContext } from '../explorer';
import { useSchemaContext } from '../schema';
import { useStorageContext } from '../storage';
import debounce from '../utility/debounce';
import { onHasCompletion } from './completion';
import { CodeMirrorEditorWithOperationFacts } from './context';
import { useEditorContext } from './context';
import { CodeMirrorEditor } from './types';

export function useSynchronizeValue(
Expand Down Expand Up @@ -123,13 +123,19 @@ export function useResizeEditor(
});
}

export type CopyQueryCallback = (query: string) => void;

export function useCopyQuery({
queryEditor,
caller,
onCopyQuery,
}: {
queryEditor: CodeMirrorEditorWithOperationFacts | null;
onCopyQuery?(query: string): void;
}) {
caller?: Function;
onCopyQuery?: CopyQueryCallback;
} = {}) {
const { queryEditor } = useEditorContext({
nonNull: true,
caller: caller || useCopyQuery,
});
return useCallback(() => {
if (!queryEditor) {
return;
Expand All @@ -142,13 +148,12 @@ export function useCopyQuery({
}, [queryEditor, onCopyQuery]);
}

export function useMergeQuery({
queryEditor,
schema,
}: {
queryEditor?: CodeMirrorEditorWithOperationFacts | null;
schema: GraphQLSchema | null | undefined;
}) {
export function useMergeQuery({ caller }: { caller?: Function } = {}) {
const { queryEditor } = useEditorContext({
nonNull: true,
caller: caller || useMergeQuery,
});
const { schema } = useSchemaContext({ nonNull: true, caller: useMergeQuery });
return useCallback(() => {
const documentAST = queryEditor?.documentAST;
const query = queryEditor?.getValue();
Expand All @@ -161,14 +166,14 @@ export function useMergeQuery({
}

export function usePrettifyEditors({
queryEditor,
variableEditor,
headerEditor,
caller,
}: {
queryEditor: CodeMirrorEditorWithOperationFacts | null;
variableEditor: CodeMirrorEditor | null;
headerEditor: CodeMirrorEditor | null;
}) {
caller?: Function;
} = {}) {
const { queryEditor, headerEditor, variableEditor } = useEditorContext({
nonNull: true,
caller: caller || usePrettifyEditors,
});
return useCallback(() => {
if (variableEditor) {
const variableEditorContent = variableEditor.getValue();
Expand Down Expand Up @@ -213,3 +218,58 @@ export function usePrettifyEditors({
}
}, [queryEditor, variableEditor, headerEditor]);
}

export function useAutoCompleteLeafs({
getDefaultFieldNames,
caller,
}: { getDefaultFieldNames?: GetDefaultFieldNamesFn; caller?: Function } = {}) {
const { schema } = useSchemaContext({
nonNull: true,
caller: caller || useAutoCompleteLeafs,
});
const { queryEditor } = useEditorContext({
nonNull: true,
caller: caller || useAutoCompleteLeafs,
});
return useCallback(() => {
if (!queryEditor) {
return;
}

const query = queryEditor.getValue();
const { insertions, result } = fillLeafs(
schema,
query,
getDefaultFieldNames,
);
if (insertions && insertions.length > 0) {
queryEditor.operation(() => {
const cursor = queryEditor.getCursor();
const cursorIndex = queryEditor.indexFromPos(cursor);
queryEditor.setValue(result || '');
let added = 0;
const markers = insertions.map(({ index, string }) =>
queryEditor.markText(
queryEditor.posFromIndex(index + added),
queryEditor.posFromIndex(index + (added += string.length)),
{
className: 'autoInsertedLeaf',
clearOnEnter: true,
title: 'Automatically added leaf fields',
},
),
);
setTimeout(() => markers.forEach(marker => marker.clear()), 7000);
let newCursorIndex = cursorIndex;
insertions.forEach(({ index, string }) => {
if (index < cursorIndex) {
newCursorIndex += string.length;
}
});
queryEditor.setCursor(queryEditor.posFromIndex(newCursorIndex));
});
}

return result;
}, [getDefaultFieldNames, queryEditor, schema]);
}
10 changes: 10 additions & 0 deletions packages/graphiql-react/src/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import {
useEditorContext,
} from './context';
import { useHeaderEditor } from './header-editor';
import {
useAutoCompleteLeafs,
useCopyQuery,
useMergeQuery,
usePrettifyEditors,
} from './hooks';
import { useQueryEditor } from './query-editor';
import { useResponseEditor } from './response-editor';
import { useVariableEditor } from './variable-editor';
Expand All @@ -24,8 +30,12 @@ export {
ImagePreview,
EditorContext,
EditorContextProvider,
useAutoCompleteLeafs,
useCopyQuery,
useEditorContext,
useHeaderEditor,
useMergeQuery,
usePrettifyEditors,
useQueryEditor,
useResponseEditor,
useVariableEditor,
Expand Down
18 changes: 10 additions & 8 deletions packages/graphiql-react/src/editor/query-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ import {
useEditorContext,
} from './context';
import {
CopyQueryCallback,
EditCallback,
EmptyCallback,
useCompletion,
useCopyQuery,
useKeyMap,
useMergeQuery,
usePrettifyEditors,
useResizeEditor,
useSynchronizeValue,
} from './hooks';
Expand All @@ -37,6 +41,7 @@ export type UseQueryEditorArgs = {
externalFragments?: string | FragmentDefinitionNode[];
onEdit?: EditCallback;
onEditOperationName?: EditCallback;
onCopyQuery?: CopyQueryCallback;
onRunQuery?: EmptyCallback;
readOnly?: boolean;
validationRules?: ValidationRule[];
Expand All @@ -49,6 +54,7 @@ export function useQueryEditor({
externalFragments,
onEdit,
onEditOperationName,
onCopyQuery,
onRunQuery,
readOnly = false,
validationRules,
Expand All @@ -58,19 +64,15 @@ export function useQueryEditor({
nonNull: true,
caller: useQueryEditor,
});
const {
copy,
merge,
prettify,
queryEditor,
setQueryEditor,
variableEditor,
} = useEditorContext({
const { queryEditor, setQueryEditor, variableEditor } = useEditorContext({
nonNull: true,
caller: useQueryEditor,
});
const storage = useStorageContext();
const explorer = useExplorerContext();
const copy = useCopyQuery({ caller: useQueryEditor, onCopyQuery });
const merge = useMergeQuery({ caller: useQueryEditor });
const prettify = usePrettifyEditors({ caller: useQueryEditor });
const ref = useRef<HTMLDivElement>(null);
const codeMirrorRef = useRef<CodeMirrorType>();

Expand Down
Loading

0 comments on commit 1e6fc68

Please sign in to comment.