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:
@@ -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>
|
||||
}
|
||||
Reference in New Issue
Block a user