Better project selector, Fixes #2, and a bunch more

This commit is contained in:
Gregory Schier
2023-10-26 09:11:44 -07:00
parent 2f64f45aba
commit 2a29c4b551
19 changed files with 126 additions and 86 deletions

View File

@@ -23,6 +23,7 @@ export type ButtonProps = HTMLAttributes<HTMLButtonElement> & {
forDropdown?: boolean;
disabled?: boolean;
title?: string;
leftSlot?: ReactNode;
rightSlot?: ReactNode;
};
@@ -37,6 +38,7 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
type = 'button',
justify = 'center',
size = 'md',
leftSlot,
rightSlot,
disabled,
...props
@@ -63,7 +65,11 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
return (
<button ref={ref} type={type} className={classes} disabled={disabled} {...props}>
{isLoading && <Icon icon="update" size={size} className="animate-spin mr-1" />}
{isLoading ? (
<Icon icon="update" size={size} className="animate-spin mr-1" />
) : leftSlot ? (
<div className="mr-1">{leftSlot}</div>
) : null}
{children}
{rightSlot && <div className="ml-1">{rightSlot}</div>}
{forDropdown && <Icon icon="chevronDown" size={size} className="ml-1 -mr-1" />}

View File

@@ -35,6 +35,7 @@ export interface EditorProps {
onChange?: (value: string) => void;
onFocus?: () => void;
onBlur?: () => void;
onSubmit?: () => void;
singleLine?: boolean;
wrapLines?: boolean;
format?: (v: string) => string;
@@ -56,6 +57,7 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
onChange,
onFocus,
onBlur,
onSubmit,
className,
singleLine,
format,
@@ -77,6 +79,12 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
handleChange.current = onChange;
}, [onChange]);
// Use ref so we can update the onChange handler without re-initializing the editor
const handleSubmit = useRef<EditorProps['onSubmit']>(onSubmit);
useEffect(() => {
handleSubmit.current = onSubmit;
}, [onSubmit]);
// Use ref so we can update the onChange handler without re-initializing the editor
const handleFocus = useRef<EditorProps['onFocus']>(onFocus);
useEffect(() => {
@@ -113,6 +121,7 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
if (cm.current === null) return;
const { view, languageCompartment } = cm.current;
const ext = getLanguageExtension({ contentType, environment, useTemplating, autocomplete });
console.log("EXT", ext);
view.dispatch({ effects: languageCompartment.reconfigure(ext) });
}, [contentType, autocomplete, useTemplating, environment]);
@@ -147,6 +156,7 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
container,
readOnly,
singleLine,
onSubmit: handleSubmit,
onChange: handleChange,
onFocus: handleFocus,
onBlur: handleBlur,
@@ -219,11 +229,13 @@ function getExtensions({
onChange,
onFocus,
onBlur,
onSubmit,
}: Pick<EditorProps, 'singleLine' | 'readOnly'> & {
container: HTMLDivElement | null;
onChange: MutableRefObject<EditorProps['onChange']>;
onFocus: MutableRefObject<EditorProps['onFocus']>;
onBlur: MutableRefObject<EditorProps['onBlur']>;
onSubmit: MutableRefObject<EditorProps['onSubmit']>;
}) {
// TODO: Ensure tooltips render inside the dialog if we are in one.
const parent =
@@ -249,10 +261,8 @@ function getExtensions({
},
keydown: (e) => {
// Submit nearest form on enter if there is one
if (e.key === 'Enter') {
const el = e.currentTarget as HTMLElement;
const form = el.closest('form');
form?.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true }));
if (onSubmit != null && e.key === 'Enter') {
onSubmit.current?.();
}
},
}),

View File

@@ -99,10 +99,10 @@ export function getLanguageExtension({
environment,
autocomplete,
}: { environment: Environment | null } & Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'>) {
if (contentType === 'application/graphql') {
const justContentType = contentType?.split(';')[0] ?? contentType ?? '';
if (justContentType === 'application/graphql') {
return graphql();
}
const justContentType = contentType?.split(';')[0] ?? contentType ?? '';
const base = syntaxExtensions[justContentType] ?? text();
if (!useTemplating) {
return base ? base : [];

View File

@@ -1,7 +1,7 @@
import classNames from 'classnames';
import type { EditorView } from 'codemirror';
import type { HTMLAttributes, ReactNode } from 'react';
import { forwardRef, useCallback, useMemo, useState } from 'react';
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
import type { EditorProps } from './Editor';
import { Editor } from './Editor';
import { IconButton } from './IconButton';
@@ -90,8 +90,17 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
[onChange],
);
const wrapperRef = useRef<HTMLDivElement>(null);
const handleSubmit = useCallback(() => {
const form = wrapperRef.current?.closest('form');
if (!isValid || form == null) return;
form?.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true }));
}, [isValid]);
return (
<VStack className="w-full">
<VStack ref={wrapperRef} className="w-full">
<label
htmlFor={id}
className={classNames(
@@ -119,6 +128,7 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
ref={ref}
id={id}
singleLine
onSubmit={handleSubmit}
type={type === 'password' && !obscured ? 'text' : type}
defaultValue={defaultValue}
forceUpdateKey={forceUpdateKey}

View File

@@ -57,7 +57,7 @@ type BaseStackProps = HTMLAttributes<HTMLElement> & {
as?: ComponentType | 'ul' | 'form';
space?: keyof typeof gapClasses;
alignItems?: 'start' | 'center';
justifyContent?: 'start' | 'center' | 'end';
justifyContent?: 'start' | 'center' | 'end' | 'between';
};
const BaseStack = forwardRef(function BaseStack(
@@ -77,6 +77,7 @@ const BaseStack = forwardRef(function BaseStack(
justifyContent === 'start' && 'justify-start',
justifyContent === 'center' && 'justify-center',
justifyContent === 'end' && 'justify-end',
justifyContent === 'between' && 'justify-between',
)}
{...props}
>