Fix autocomplete inside dialog

This commit is contained in:
Gregory Schier
2023-03-03 17:03:20 -08:00
parent 7668a093a9
commit 030ba26c5e
7 changed files with 64 additions and 35 deletions

View File

@@ -9,7 +9,8 @@
<body>
<div id="root"></div>
<div id="radix-portal"></div>
<div id="cm-portal" class="cm-portal" style="pointer-events: auto"></div>
<div id="radix-portal" class="cm-portal"></div>
<script type="module" src="/src-web/main.tsx"></script>
</body>
</html>

View File

@@ -29,24 +29,26 @@ export function Dialog({
<D.Portal container={document.querySelector<HTMLElement>('#radix-portal')}>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
<D.Overlay className="fixed inset-0 bg-gray-900 dark:bg-background opacity-80 shadow-lg" />
<D.Content
className={classnames(
className,
'fixed top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] bg-gray-50',
'w-[20rem] max-h-[80vh] p-5 rounded-lg overflow-auto',
wide && 'w-[80vw] max-w-[50rem]',
)}
>
<D.Close asChild className="ml-auto absolute right-1 top-1">
<IconButton aria-label="Close" icon="x" size="sm" />
</D.Close>
<VStack space={3}>
<HStack items="center" className="pb-3">
<D.Title className="text-xl font-semibold">{title}</D.Title>
</HStack>
{description && <D.Description>{description}</D.Description>}
<div>{children}</div>
</VStack>
<D.Content className={classnames(className, 'dialog-content', 'fixed inset-0')}>
<div
className={classnames(
className,
'absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] bg-gray-50',
'w-[20rem] max-h-[80vh] p-5 rounded-lg overflow-auto',
wide && 'w-[80vw] max-w-[50rem]',
)}
>
<D.Close asChild className="ml-auto absolute right-1 top-1">
<IconButton aria-label="Close" icon="x" size="sm" />
</D.Close>
<VStack space={3}>
<HStack items="center" className="pb-3">
<D.Title className="text-xl font-semibold">{title}</D.Title>
</HStack>
{description && <D.Description>{description}</D.Description>}
<div>{children}</div>
</VStack>
</div>
</D.Content>
</motion.div>
</D.Portal>

View File

@@ -126,26 +126,26 @@
/* <-- */
.cm-editor .cm-tooltip {
@apply shadow-lg bg-background rounded overflow-hidden text-gray-900 border border-gray-100/70;
.cm-tooltip.cm-tooltip {
@apply shadow-lg bg-background rounded overflow-hidden text-gray-900 border border-gray-100/70 z-50;
}
.cm-editor .cm-tooltip * {
.cm-tooltip.cm-tooltip * {
@apply transition-none;
}
.cm-editor .cm-tooltip.cm-tooltip-autocomplete > ul {
.cm-tooltip.cm-tooltip.cm-tooltip-autocomplete > ul {
@apply p-1 max-h-[40vh];
}
.cm-editor .cm-tooltip.cm-tooltip-autocomplete > ul > li {
.cm-tooltip.cm-tooltip.cm-tooltip-autocomplete > ul > li {
@apply cursor-default py-1 px-2 rounded-sm text-gray-500;
}
.cm-editor .cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] {
.cm-tooltip.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] {
@apply bg-gray-50 text-gray-800;
}
.cm-editor .cm-tooltip.cm-tooltip-autocomplete .cm-completionIcon {
.cm-tooltip.cm-tooltip.cm-tooltip-autocomplete .cm-completionIcon {
@apply text-sm;
}

View File

@@ -1,6 +1,6 @@
import { defaultKeymap } from '@codemirror/commands';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import { keymap, placeholder as placeholderExt } from '@codemirror/view';
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 { HTMLAttributes } from 'react';
@@ -13,6 +13,7 @@ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
contentType: string;
valueKey?: string;
placeholder?: string;
tooltipContainer?: HTMLElement;
useTemplating?: boolean;
onChange?: (value: string) => void;
onSubmit?: () => void;
@@ -35,8 +36,16 @@ export default function Editor({
const ref = useRef<HTMLDivElement>(null);
const extensions = useMemo(
() =>
getExtensions({ placeholder, onSubmit, singleLine, onChange, contentType, useTemplating }),
[contentType],
getExtensions({
container: ref.current,
placeholder,
onSubmit,
singleLine,
onChange,
contentType,
useTemplating,
}),
[contentType, ref.current],
);
const newState = (langHolder: Compartment) => {
@@ -95,6 +104,7 @@ export default function Editor({
}
function getExtensions({
container,
singleLine,
placeholder,
onChange,
@@ -104,10 +114,15 @@ function getExtensions({
}: Pick<
Props,
'singleLine' | 'onChange' | 'onSubmit' | 'contentType' | 'useTemplating' | 'placeholder'
>) {
> & { container: HTMLDivElement | null }) {
const ext = getLanguageExtension({ contentType, useTemplating });
// TODO: This is a hack to get the tooltips to render in the correct place when inside a modal dialog
const parent = container?.closest<HTMLDivElement>('.dialog-content') ?? undefined;
return [
...baseExtensions,
tooltips({ parent }),
keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap),
...(singleLine ? [singleLineExt()] : []),
...(!singleLine ? [multiLineExtensions] : []),

View File

@@ -31,6 +31,7 @@ import {
keymap,
lineNumbers,
rectangularSelection,
tooltips,
} from '@codemirror/view';
import { tags as t } from '@lezer/highlight';
import { twig } from './twig/extension';

View File

@@ -8,8 +8,9 @@ import { HStack, VStack } from './Stacks';
export function HeaderEditor() {
const [headers, setHeaders] = useState<HttpHeader[]>([]);
const [newHeader, setNewHeader] = useState<HttpHeader>({ name: '', value: '' });
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
const handleSubmit = (e?: FormEvent) => {
console.log('SUBMIT');
e?.preventDefault();
setHeaders([...headers, newHeader]);
setNewHeader({ name: '', value: '' });
};
@@ -31,9 +32,10 @@ export function HeaderEditor() {
header={header}
onChange={(h) => handleChangeHeader(h, i)}
onDelete={() => handleDelete(i)}
onSubmit={handleSubmit}
/>
))}
<FormRow addSubmit onChange={setNewHeader} header={newHeader} />
<FormRow addSubmit onChange={setNewHeader} header={newHeader} onSubmit={handleSubmit} />
</VStack>
</form>
);
@@ -43,10 +45,12 @@ function FormRow({
header,
addSubmit,
onChange,
onSubmit,
onDelete,
}: {
header: HttpHeader;
addSubmit?: boolean;
onSubmit?: () => void;
onChange: (header: HttpHeader) => void;
onDelete?: () => void;
}) {
@@ -55,9 +59,12 @@ function FormRow({
<HStack space={2}>
<Input
autoFocus
useEditor
useTemplating
name="name"
label="Name"
placeholder="name"
onSubmit={onSubmit}
value={header.name}
hideLabel
onChange={(name) => {
@@ -67,6 +74,9 @@ function FormRow({
<Input
name="value"
label="Value"
useEditor
useTemplating
onSubmit={onSubmit}
placeholder="value"
value={header.value}
hideLabel

View File

@@ -29,7 +29,7 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
{...props}
>
<HStack as={WindowDragRegion} items="center" className="pr-1" justify="end">
<Dialog wide open={open} onOpenChange={setOpen} title="This is the title">
<Dialog wide open={open} onOpenChange={setOpen} title="Edit Headers">
<HeaderEditor />
<Button className="ml-auto mt-5" color="primary" onClick={() => setOpen(false)}>
Save