feat(dashboard): Submit forms on Cmd + Enter (#9623)

**What**
- Changes all forms to only submit on Cmd/Ctrl + Enter instead of just Enter.
- Cleans up the position of submit/cancel buttons in many FocusModals that still had them in the header.
- Fixes responsiveness of multiple forms
- Removes the SplitView component, and replaces its usages with StackedDrawer/Modal to align the UX across the project.

Resolves CC-103, CC-535
This commit is contained in:
Kasper Fabricius Kristensen
2024-10-17 09:38:12 +00:00
committed by GitHub
parent 0be50059f2
commit 1d540af783
120 changed files with 1138 additions and 1056 deletions
@@ -24,6 +24,7 @@ import { Form } from "../../common/form"
import { InlineTip } from "../../common/inline-tip"
import { Skeleton } from "../../common/skeleton"
import { RouteDrawer, useRouteModal } from "../../modals"
import { KeyboundForm } from "../../utilities/keybound-form"
type MetaDataSubmitHook<TRes> = (
params: { metadata?: Record<string, any> | null },
@@ -125,7 +126,7 @@ const InnerForm = <TRes,>({
return (
<RouteDrawer.Form form={form}>
<form
<KeyboundForm
onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden"
>
@@ -277,7 +278,7 @@ const InnerForm = <TRes,>({
</Button>
</div>
</RouteDrawer.Footer>
</form>
</KeyboundForm>
</RouteDrawer.Form>
)
}
@@ -32,7 +32,7 @@ import {
} from "react"
import { useTranslation } from "react-i18next"
import { genericForwardRef } from "../../common/generic-forward-ref"
import { genericForwardRef } from "../../utilities/generic-forward-ref"
type ComboboxOption = {
value: string
@@ -1 +0,0 @@
export * from "./split-view"
@@ -1,103 +0,0 @@
import { Button, clx } from "@medusajs/ui"
import * as Dialog from "@radix-ui/react-dialog"
import {
ComponentPropsWithoutRef,
PropsWithChildren,
createContext,
useContext,
useRef,
} from "react"
type SplitViewContextValue = {
open: boolean
onOpenChange: (open: boolean) => void
}
const SplitViewContext = createContext<SplitViewContextValue | null>(null)
const useSplitViewContext = () => {
const context = useContext(SplitViewContext)
if (!context) {
throw new Error("useSplitViewContext must be used within a SplitView")
}
return context
}
type SplitViewProps = PropsWithChildren<{
open?: boolean
onOpenChange?: (open: boolean) => void
}>
const Root = ({ open, onOpenChange, children }: SplitViewProps) => {
const containerRef = useRef<HTMLDivElement>(null)
return (
<Dialog.Root open={open} onOpenChange={onOpenChange}>
<div ref={containerRef} className="relative size-full overflow-hidden">
{children}
</div>
</Dialog.Root>
)
}
const Content = ({
children,
className,
...props
}: ComponentPropsWithoutRef<"div">) => {
return (
<div
className={clx("relative h-full overflow-y-auto", className)}
{...props}
>
{children}
</div>
)
}
const Drawer = ({ children }: PropsWithChildren) => {
return (
<div>
<Dialog.Overlay
className={clx(
"bg-ui-bg-base absolute inset-0 opacity-40",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
)}
/>
<Dialog.Content
className={clx(
"bg-ui-bg-base border-ui-border-base absolute inset-y-0 right-0 flex w-full max-w-[calc(100%-128px)] flex-1 flex-col border-l focus:outline-none md:max-w-[80%] lg:max-w-[50%]",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-right-1/2 data-[state=open]:slide-in-from-right-1/2 duration-200"
)}
>
{children}
</Dialog.Content>
</div>
)
}
const Close = ({
variant = "secondary",
size = "small",
children,
...props
}: ComponentPropsWithoutRef<typeof Button>) => {
return (
<Dialog.Close asChild>
<Button size={size} variant={variant} {...props}>
{children}
</Button>
</Dialog.Close>
)
}
/**
* SplitView is a layout component that allows you to create a split view layout within a FocusModal.
*/
export const SplitView = Object.assign(Root, {
Content,
Drawer,
Close,
})
@@ -0,0 +1 @@
export * from "./keybound-form"
@@ -0,0 +1,35 @@
import React from "react"
/**
* A form that can only be submitted when using the meta or control key.
*/
export const KeyboundForm = React.forwardRef<
HTMLFormElement,
React.FormHTMLAttributes<HTMLFormElement>
>(({ onSubmit, onKeyDown, ...rest }, ref) => {
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
onSubmit?.(event)
}
const handleKeyDown = (event: React.KeyboardEvent<HTMLFormElement>) => {
if (event.key === "Enter") {
event.preventDefault()
if (event.metaKey || event.ctrlKey) {
handleSubmit(event)
}
}
}
return (
<form
{...rest}
onSubmit={handleSubmit}
onKeyDown={onKeyDown ?? handleKeyDown}
ref={ref}
/>
)
})
KeyboundForm.displayName = "KeyboundForm"
@@ -0,0 +1 @@
export * from "./visually-hidden"
@@ -0,0 +1,5 @@
import { PropsWithChildren } from "react"
export const VisuallyHidden = ({ children }: PropsWithChildren) => {
return <span className="sr-only">{children}</span>
}