From 51949f4fbf22877d2e5bba0d6ea6443a64007263 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Mon, 30 Oct 2023 06:35:52 -0700 Subject: [PATCH] Refactored some core UI --- src-tauri/plugins/hello-world/index.js | 2 + .../components/EnvironmentActionsDropdown.tsx | 2 +- src-web/components/EnvironmentEditDialog.tsx | 82 +++++++----- src-web/components/Overlay.tsx | 23 +++- .../components/RecentResponsesDropdown.tsx | 4 +- src-web/components/Workspace.tsx | 4 +- src-web/components/core/Button.tsx | 8 +- src-web/components/core/Dialog.tsx | 2 +- src-web/components/core/Dropdown.tsx | 123 +++++++++--------- src-web/components/core/PairEditor.tsx | 6 +- src-web/components/core/Tabs/Tabs.tsx | 10 +- src-web/hooks/Prompt.tsx | 43 +++--- 12 files changed, 175 insertions(+), 134 deletions(-) diff --git a/src-tauri/plugins/hello-world/index.js b/src-tauri/plugins/hello-world/index.js index d84dec2f..bb1ab725 100644 --- a/src-tauri/plugins/hello-world/index.js +++ b/src-tauri/plugins/hello-world/index.js @@ -2,4 +2,6 @@ import { hello } from './hello.js'; export function entrypoint() { hello(); + console.log('Try JSON parse', JSON.parse(`{ "hello": 123 }`).hello); + console.log('Try RegExp', '123'.match(/[\d]+/)); } diff --git a/src-web/components/EnvironmentActionsDropdown.tsx b/src-web/components/EnvironmentActionsDropdown.tsx index e5d7a7f2..2f52bd48 100644 --- a/src-web/components/EnvironmentActionsDropdown.tsx +++ b/src-web/components/EnvironmentActionsDropdown.tsx @@ -67,7 +67,7 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo onSelect: showEnvironmentDialog, }, ], - [activeEnvironment, environments, routes, prompt, createEnvironment, showEnvironmentDialog], + [activeEnvironment, environments, routes, createEnvironment, showEnvironmentDialog], ); return ( diff --git a/src-web/components/EnvironmentEditDialog.tsx b/src-web/components/EnvironmentEditDialog.tsx index d9eb5e3e..5aa318b6 100644 --- a/src-web/components/EnvironmentEditDialog.tsx +++ b/src-web/components/EnvironmentEditDialog.tsx @@ -12,12 +12,13 @@ import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment'; import { HStack, VStack } from './core/Stacks'; import { IconButton } from './core/IconButton'; import { useDeleteEnvironment } from '../hooks/useDeleteEnvironment'; -import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; import type { DropdownItem } from './core/Dropdown'; import { Dropdown } from './core/Dropdown'; import { Icon } from './core/Icon'; import { usePrompt } from '../hooks/usePrompt'; import { InlineCode } from './core/InlineCode'; +import { useWindowSize } from 'react-use'; +import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; export const EnvironmentEditDialog = function () { const routes = useAppRoutes(); @@ -25,36 +26,48 @@ export const EnvironmentEditDialog = function () { const createEnvironment = useCreateEnvironment(); const activeEnvironment = useActiveEnvironment(); + const windowSize = useWindowSize(); + const showSidebar = windowSize.width > 500; + return ( -
-
-
- {environments.map((e) => ( - - ))} -
- -
+
+ {showSidebar && ( + + )} {activeEnvironment != null && }
); @@ -113,6 +126,12 @@ const EnvironmentEditor = function ({ environment }: { environment: Environment [deleteEnvironment, updateEnvironment, environment.name, prompt], ); + const validateName = useCallback((name: string) => { + // Empty just means the variable doesn't have a name yet, and is unusable + if (name === '') return true; + return name.match(/^[a-z_][a-z0-9_]*$/i) != null; + }, []); + return ( @@ -124,6 +143,9 @@ const EnvironmentEditor = function ({ environment }: { environment: Environment void; zIndex?: keyof typeof zIndexes; + variant?: 'default' | 'transparent'; } const zIndexes: Record = { @@ -20,7 +21,14 @@ const zIndexes: Record = { 50: 'z-50', }; -export function Overlay({ zIndex = 30, open, onClose, portalName, children }: Props) { +export function Overlay({ + variant = 'default', + zIndex = 30, + open, + onClose, + portalName, + children, +}: Props) { return ( {open && ( @@ -33,14 +41,19 @@ export function Overlay({ zIndex = 30, open, onClose, portalName, children }: Pr
{/* Add region to still be able to drag the window */} -
- {children} + {variant !== 'transparent' && ( +
+ )} +
{children}
- )} + )} ); } diff --git a/src-web/components/RecentResponsesDropdown.tsx b/src-web/components/RecentResponsesDropdown.tsx index 5ec5af4d..0742b465 100644 --- a/src-web/components/RecentResponsesDropdown.tsx +++ b/src-web/components/RecentResponsesDropdown.tsx @@ -42,9 +42,9 @@ export const RecentResponsesDropdown = function ResponsePane({ ...responses.slice(0, 20).map((r) => ({ key: r.id, label: ( - + - {r.elapsed}ms + {r.elapsed}ms ), leftSlot: activeResponse?.id === r.id ? : , diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx index fa21a09c..3b591ea6 100644 --- a/src-web/components/Workspace.tsx +++ b/src-web/components/Workspace.tsx @@ -83,7 +83,7 @@ export default function Workspace() { document.documentElement.addEventListener('mouseup', moveState.current.up); setIsResizing(true); }, - [setWidth, width], + [setWidth, resetWidth, width, hide, show], ); const sideWidth = hidden ? 0 : width; @@ -121,7 +121,7 @@ export default function Workspace() { )} > {floating ? ( - + (function Button( () => classNames( className, - 'flex-shrink-0 outline-none whitespace-nowrap', - 'focus-visible-or-class:ring', - 'rounded-md flex items-center', + 'whitespace-nowrap outline-none', + 'flex-shrink-0 flex items-center', + 'focus-visible-or-class:ring rounded-md', disabled ? 'pointer-events-none opacity-disabled' : 'pointer-events-auto', colorStyles[color || 'default'], justify === 'start' && 'justify-start', @@ -70,7 +70,7 @@ const _Button = forwardRef(function Button( ) : leftSlot ? (
{leftSlot}
) : null} - {children} +
{children}
{rightSlot &&
{rightSlot}
} {forDropdown && } diff --git a/src-web/components/core/Dialog.tsx b/src-web/components/core/Dialog.tsx index f4cd7730..c69b4c86 100644 --- a/src-web/components/core/Dialog.tsx +++ b/src-web/components/core/Dialog.tsx @@ -59,7 +59,7 @@ export function Dialog({ 'dark:border border-highlight shadow shadow-black/10', size === 'sm' && 'w-[25rem] max-h-[80vh]', size === 'md' && 'w-[45rem] max-h-[80vh]', - size === 'full' && 'w-[calc(100vw-8em)] h-[calc(100vh-8em)]', + size === 'full' && 'w-[95vw] h-[calc(100vh-6em)]', size === 'dynamic' && 'min-w-[30vw] max-w-[80vw]', )} > diff --git a/src-web/components/core/Dropdown.tsx b/src-web/components/core/Dropdown.tsx index f8a0bdfb..3c4765dd 100644 --- a/src-web/components/core/Dropdown.tsx +++ b/src-web/components/core/Dropdown.tsx @@ -1,5 +1,4 @@ import classNames from 'classnames'; -import FocusTrap from 'focus-trap-react'; import { motion } from 'framer-motion'; import type { CSSProperties, HTMLAttributes, MouseEvent, ReactElement, ReactNode } from 'react'; import React, { @@ -13,11 +12,11 @@ import React, { useRef, useState, } from 'react'; -import { useKey, useKeyPressEvent } from 'react-use'; -import { Portal } from '../Portal'; +import { useKey, useKeyPressEvent, useWindowSize } from 'react-use'; import { Button } from './Button'; import { Separator } from './Separator'; import { VStack } from './Stacks'; +import { Overlay } from '../Overlay'; export type DropdownItemSeparator = { type: 'separator'; @@ -65,7 +64,7 @@ export const Dropdown = forwardRef(function Dropdown useImperativeHandle(ref, () => ({ ...menuRef.current, isOpen: open, - toggle (activeIndex?: number) { + toggle(activeIndex?: number) { if (!open) this.open(activeIndex); else setOpen(false); }, @@ -107,10 +106,12 @@ export const Dropdown = forwardRef(function Dropdown buttonRef.current?.setAttribute('aria-expanded', open.toString()); }, [open]); + const windowSize = useWindowSize(); const triggerRect = useMemo(() => { + windowSize; // Make TS happy with this dep if (!open) return null; return buttonRef.current?.getBoundingClientRect(); - }, [open]); + }, [open, windowSize]); return ( <> @@ -267,61 +268,59 @@ const Menu = forwardRef, MenuPro if (items.length === 0) return null; return ( - - -
-
- - - {containerStyles && ( - - {items.map((item, i) => { - if (item.type === 'separator') { - return ; - } - if (item.hidden) { - return null; - } - return ( - - ); - })} - - )} - -
- - + +
+
+ + + {containerStyles && ( + + {items.map((item, i) => { + if (item.type === 'separator') { + return ; + } + if (item.hidden) { + return null; + } + return ( + + ); + })} + + )} + +
+ ); }); @@ -359,6 +358,8 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men onFocus={handleFocus} onClick={handleClick} justify="start" + leftSlot={item.leftSlot &&
{item.leftSlot}
} + rightSlot={item.rightSlot &&
{item.rightSlot}
} className={classNames( className, 'min-w-[8rem] outline-none px-2 mx-1.5 flex text-sm text-gray-700 whitespace-nowrap', @@ -367,7 +368,6 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men )} {...props} > - {item.leftSlot &&
{item.leftSlot}
}
{item.label}
- {item.rightSlot &&
{item.rightSlot}
} ); } diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx index 01e1e547..60ee71ed 100644 --- a/src-web/components/core/PairEditor.tsx +++ b/src-web/components/core/PairEditor.tsx @@ -154,6 +154,8 @@ export const PairEditor = memo(function PairEditor({ 'pb-2 grid overflow-auto max-h-full', // Move over the width of the drag handle '-ml-3', + // Pad to make room for the drag divider + 'pt-0.5', )} > {pairs.map((p, i) => { @@ -171,8 +173,8 @@ export const PairEditor = memo(function PairEditor({ forceUpdateKey={forceUpdateKey} nameAutocomplete={nameAutocomplete} valueAutocomplete={valueAutocomplete} - namePlaceholder={namePlaceholder} - valuePlaceholder={valuePlaceholder} + namePlaceholder={isLast ? namePlaceholder : ''} + valuePlaceholder={isLast ? valuePlaceholder : ''} nameValidate={nameValidate} valueValidate={valueValidate} showDelete={!isLast} diff --git a/src-web/components/core/Tabs/Tabs.tsx b/src-web/components/core/Tabs/Tabs.tsx index bd460555..4c88193e 100644 --- a/src-web/components/core/Tabs/Tabs.tsx +++ b/src-web/components/core/Tabs/Tabs.tsx @@ -102,14 +102,16 @@ export function Tabs({ size="sm" onClick={isActive ? undefined : () => handleTabChange(t.value)} className={btnClassName} + rightSlot={ + + } > {option && 'shortLabel' in option ? option.shortLabel : option?.label ?? 'Unknown'} - ); diff --git a/src-web/hooks/Prompt.tsx b/src-web/hooks/Prompt.tsx index e0759546..74e6cf54 100644 --- a/src-web/hooks/Prompt.tsx +++ b/src-web/hooks/Prompt.tsx @@ -3,7 +3,7 @@ import { useCallback, useState } from 'react'; import { Button } from '../components/core/Button'; import type { InputProps } from '../components/core/Input'; import { Input } from '../components/core/Input'; -import { HStack, VStack } from '../components/core/Stacks'; +import { HStack } from '../components/core/Stacks'; export interface PromptProps { onHide: () => void; @@ -25,26 +25,27 @@ export function Prompt({ onHide, label, name, defaultValue, onResult }: PromptPr ); return ( -
- - - - - - - + + + + + +
); }