mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-19 07:54:23 +01:00
GraphQL autocomplete and duplicate request
This commit is contained in:
@@ -162,6 +162,7 @@ function Menu({ className, items, onClose, triggerRect }: MenuProps) {
|
||||
>
|
||||
{containerStyles && (
|
||||
<VStack
|
||||
space={0.5}
|
||||
ref={initMenu}
|
||||
style={menuStyles}
|
||||
tabIndex={-1}
|
||||
|
||||
@@ -151,7 +151,15 @@
|
||||
|
||||
/* NOTE: Extra selector required to override default styles */
|
||||
.cm-tooltip.cm-tooltip {
|
||||
@apply shadow-lg bg-gray-50 rounded overflow-hidden text-gray-900 border border-gray-200 z-50 pointer-events-auto;
|
||||
@apply shadow-lg bg-gray-50 rounded text-gray-700 border border-gray-200 z-50 pointer-events-auto text-sm;
|
||||
|
||||
&.cm-completionInfo-right {
|
||||
@apply ml-1;
|
||||
}
|
||||
|
||||
&.cm-completionInfo-right-narrow {
|
||||
@apply ml-1;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply transition-none;
|
||||
@@ -159,7 +167,7 @@
|
||||
|
||||
&.cm-tooltip-autocomplete {
|
||||
& > ul {
|
||||
@apply p-1 max-h-[20rem];
|
||||
@apply p-1 max-h-[40vh];
|
||||
}
|
||||
|
||||
& > ul > li {
|
||||
@@ -177,5 +185,18 @@
|
||||
.cm-completionIcon {
|
||||
@apply text-sm flex items-center pb-0.5;
|
||||
}
|
||||
|
||||
|
||||
.cm-completionLabel {
|
||||
}
|
||||
|
||||
.cm-completionDetail {
|
||||
@apply ml-auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add default icon. Needs low priority so it can be overwritten */
|
||||
.cm-completionIcon::after {
|
||||
content: '𝑥';
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { defaultKeymap } from '@codemirror/commands';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
import { Compartment, EditorState } from '@codemirror/state';
|
||||
import { keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view';
|
||||
import classnames from 'classnames';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useUnmount } from 'react-use';
|
||||
import { IconButton } from '../IconButton';
|
||||
import './Editor.css';
|
||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
||||
@@ -18,6 +18,7 @@ export interface _EditorProps {
|
||||
className?: string;
|
||||
heightMode?: 'auto' | 'full';
|
||||
contentType?: string;
|
||||
languageExtension?: Extension;
|
||||
autoFocus?: boolean;
|
||||
defaultValue?: string;
|
||||
placeholder?: string;
|
||||
@@ -38,6 +39,7 @@ export function _Editor({
|
||||
placeholder,
|
||||
useTemplating,
|
||||
defaultValue,
|
||||
languageExtension,
|
||||
onChange,
|
||||
onFocus,
|
||||
className,
|
||||
@@ -48,12 +50,6 @@ export function _Editor({
|
||||
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
|
||||
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// Unmount the editor
|
||||
useUnmount(() => {
|
||||
cm.current?.view.destroy();
|
||||
cm.current = null;
|
||||
});
|
||||
|
||||
// Use ref so we can update the onChange handler without re-initializing the editor
|
||||
const handleChange = useRef<_EditorProps['onChange']>(onChange);
|
||||
useEffect(() => {
|
||||
@@ -87,9 +83,11 @@ export function _Editor({
|
||||
// Initialize the editor when ref mounts
|
||||
useEffect(() => {
|
||||
if (wrapperRef.current === null || cm.current !== null) return;
|
||||
let view: EditorView;
|
||||
try {
|
||||
const languageCompartment = new Compartment();
|
||||
const langExt = getLanguageExtension({ contentType, useTemplating, autocomplete });
|
||||
const langExt =
|
||||
languageExtension ?? getLanguageExtension({ contentType, useTemplating, autocomplete });
|
||||
const state = EditorState.create({
|
||||
doc: `${defaultValue ?? ''}`,
|
||||
extensions: [
|
||||
@@ -106,18 +104,18 @@ export function _Editor({
|
||||
}),
|
||||
],
|
||||
});
|
||||
const view = new EditorView({ state, parent: wrapperRef.current });
|
||||
view = new EditorView({ state, parent: wrapperRef.current });
|
||||
cm.current = { view, languageCompartment };
|
||||
syncGutterBg({ parent: wrapperRef.current, className });
|
||||
if (autoFocus) view.focus();
|
||||
} catch (e) {
|
||||
console.log('Failed to initialize Codemirror', e);
|
||||
}
|
||||
}, [wrapperRef.current]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wrapperRef.current === null) return;
|
||||
syncGutterBg({ parent: wrapperRef.current, className });
|
||||
}, [className]);
|
||||
return () => {
|
||||
view.destroy();
|
||||
cm.current = null;
|
||||
};
|
||||
}, [wrapperRef.current, languageExtension]);
|
||||
|
||||
const cmContainer = useMemo(
|
||||
() => (
|
||||
|
||||
@@ -32,7 +32,8 @@ import {
|
||||
rectangularSelection,
|
||||
} from '@codemirror/view';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { graphqlLanguageSupport } from 'cm6-graphql';
|
||||
import { graphql, graphqlLanguageSupport } from 'cm6-graphql';
|
||||
import { render } from 'react-dom';
|
||||
import type { EditorProps } from './index';
|
||||
import { text } from './text/extension';
|
||||
import { twig } from './twig/extension';
|
||||
@@ -97,6 +98,9 @@ export function getLanguageExtension({
|
||||
useTemplating = false,
|
||||
autocomplete,
|
||||
}: Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'>) {
|
||||
if (contentType === 'application/graphql') {
|
||||
return graphql();
|
||||
}
|
||||
const justContentType = contentType?.split(';')[0] ?? contentType ?? '';
|
||||
const base = syntaxExtensions[justContentType] ?? text();
|
||||
if (!useTemplating) {
|
||||
@@ -115,7 +119,14 @@ export const baseExtensions = [
|
||||
// TODO: Figure out how to debounce showing of autocomplete in a good way
|
||||
// debouncedAutocompletionDisplay({ millis: 1000 }),
|
||||
// autocompletion({ closeOnBlur: true, interactionDelay: 200, activateOnTyping: false }),
|
||||
autocompletion({ closeOnBlur: true, interactionDelay: 200 }),
|
||||
autocompletion({
|
||||
// closeOnBlur: false,
|
||||
interactionDelay: 200,
|
||||
compareCompletions: (a, b) => {
|
||||
// Don't sort completions at all, only on boost
|
||||
return (a.boost ?? 0) - (b.boost ?? 0);
|
||||
},
|
||||
}),
|
||||
syntaxHighlighting(myHighlightStyle),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
];
|
||||
|
||||
@@ -24,6 +24,6 @@ export function genericCompletion({ options, minMatch = 1 }: GenericCompletionCo
|
||||
if (!matchedMinimumLength && !context.explicit) return null;
|
||||
|
||||
const optionsWithoutExactMatches = options.filter((o) => o.label !== toMatch.text);
|
||||
return { from: toMatch.from, options: optionsWithoutExactMatches };
|
||||
return { from: toMatch.from, options: optionsWithoutExactMatches, info: 'hello' };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
ClockIcon,
|
||||
CodeIcon,
|
||||
ColorWheelIcon,
|
||||
CopyIcon,
|
||||
Cross2Icon,
|
||||
DividerHorizontalIcon,
|
||||
DotsHorizontalIcon,
|
||||
@@ -40,8 +41,11 @@ const icons = {
|
||||
check: CheckIcon,
|
||||
checkbox: CheckboxIcon,
|
||||
clock: ClockIcon,
|
||||
chevronDown: ChevronDownIcon,
|
||||
code: CodeIcon,
|
||||
colorWheel: ColorWheelIcon,
|
||||
copy: CopyIcon,
|
||||
dividerH: DividerHorizontalIcon,
|
||||
dotsH: DotsHorizontalIcon,
|
||||
dotsV: DotsVerticalIcon,
|
||||
drag: DragHandleDots2Icon,
|
||||
@@ -50,12 +54,10 @@ const icons = {
|
||||
home: HomeIcon,
|
||||
listBullet: ListBulletIcon,
|
||||
magicWand: MagicWandIcon,
|
||||
chevronDown: ChevronDownIcon,
|
||||
magnifyingGlass: MagnifyingGlassIcon,
|
||||
moon: MoonIcon,
|
||||
paperPlane: PaperPlaneIcon,
|
||||
plus: PlusIcon,
|
||||
dividerH: DividerHorizontalIcon,
|
||||
plusCircle: PlusCircledIcon,
|
||||
question: QuestionMarkIcon,
|
||||
rows: RowsIcon,
|
||||
|
||||
@@ -127,7 +127,7 @@ export const PairEditor = memo(function PairEditor({
|
||||
'@container',
|
||||
'pb-2 grid',
|
||||
// NOTE: Add padding to top so overflow doesn't hide drop marker
|
||||
'pt-1',
|
||||
'pt-1 -my-1',
|
||||
)}
|
||||
>
|
||||
{pairs.map((p, i) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { forwardRef } from 'react';
|
||||
|
||||
const gapClasses = {
|
||||
0: 'gap-0',
|
||||
0.5: 'gap-0.5',
|
||||
1: 'gap-1',
|
||||
2: 'gap-2',
|
||||
3: 'gap-3',
|
||||
|
||||
Reference in New Issue
Block a user