chore(ui): move toast to top right (#13092)

* chore(ui): display toast in top right corner

* fix: error as well

* refactor: feedback

* chore: move the rest of the modal controls to footer

---------

Co-authored-by: William Bouchard <46496014+willbouch@users.noreply.github.com>
This commit is contained in:
Frane Polić
2025-08-20 08:39:45 +02:00
committed by GitHub
parent 2f594291ad
commit 5b7a041246
10 changed files with 140 additions and 110 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/ui": patch
---
chore(ui): display toast in top right corner

View File

@@ -148,14 +148,6 @@ export const EditCategoryProductsForm = ({
{form.formState.errors.product_ids.message}
</Hint>
)}
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isMutating}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
@@ -178,6 +170,16 @@ export const EditCategoryProductsForm = ({
search="autofocus"
/>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isMutating}>
{t("actions.save")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -157,14 +157,6 @@ export const AddProductsToCollectionForm = ({
{form.formState.errors.add && (
<Hint variant="error">{form.formState.errors.add.message}</Hint>
)}
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isMutating}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
@@ -187,6 +179,16 @@ export const AddProductsToCollectionForm = ({
search="autofocus"
/>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isMutating}>
{t("actions.save")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -46,25 +46,13 @@ export const CreateCollectionForm = () => {
return (
<RouteFocusModal.Form form={form}>
<KeyboundForm onSubmit={handleSubmit}>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
variant="primary"
type="submit"
isLoading={isPending}
>
{t("actions.create")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-col items-center p-16">
<KeyboundForm
onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden"
>
<RouteFocusModal.Header />
<RouteFocusModal.Body className="flex size-full flex-col items-center p-16">
<div className="flex w-full max-w-[720px] flex-col gap-y-8">
<div>
<Heading>{t("collections.createCollection")}</Heading>
@@ -111,6 +99,21 @@ export const CreateCollectionForm = () => {
</div>
</div>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
variant="primary"
type="submit"
isLoading={isPending}
>
{t("actions.create")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -137,19 +137,6 @@ export const AddCustomersForm = ({
{form.formState.errors.customer_ids.message}
</Hint>
)}
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
type="submit"
variant="primary"
size="small"
isLoading={isPending}
>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
@@ -176,6 +163,21 @@ export const AddCustomersForm = ({
}}
/>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
type="submit"
variant="primary"
size="small"
isLoading={isPending}
>
{t("actions.save")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -44,12 +44,12 @@ export function OrderCreateShipmentForm({
const handleSubmit = form.handleSubmit(async (data) => {
const addedLabels = data.labels
.filter((l) => !!l.tracking_number)
.map((l) => ({
tracking_number: l.tracking_number,
tracking_url: "#",
label_url: "#",
}))
.filter((l) => !!l.tracking_number)
.map((l) => ({
tracking_number: l.tracking_number,
tracking_url: "#",
label_url: "#",
}))
await createShipment(
{
@@ -78,18 +78,8 @@ export function OrderCreateShipmentForm({
onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden"
>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isMutating}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Header />
<RouteFocusModal.Body className="flex h-full w-full flex-col items-center divide-y overflow-y-auto">
<div className="flex size-full flex-col items-center overflow-auto p-16">
<div className="flex w-full max-w-[736px] flex-col justify-center px-2 pb-2">
@@ -166,6 +156,16 @@ export function OrderCreateShipmentForm({
</div>
</div>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isMutating}>
{t("actions.save")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -310,18 +310,7 @@ export function ManageVariantInventoryItemsForm({
return (
<RouteFocusModal.Form form={form}>
<KeyboundForm className="flex h-full flex-col" onSubmit={handleSubmit}>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Header />
<RouteFocusModal.Body className="flex justify-center overflow-auto">
<div className="flex w-full flex-col gap-y-8 px-6 pt-12 md:w-[720px] md:pt-24">
<Heading>
@@ -371,6 +360,16 @@ export function ManageVariantInventoryItemsForm({
</div>
</div>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -129,18 +129,8 @@ export const AddCountriesForm = ({ region }: AddCountriesFormProps) => {
onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden"
>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" isLoading={isLoading} type="submit">
{t("actions.add")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Header />
<RouteFocusModal.Body className="overflow-hidden">
<_DataTable
table={table}
@@ -158,6 +148,16 @@ export const AddCountriesForm = ({ region }: AddCountriesFormProps) => {
prefix={PREFIX}
/>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" isLoading={isLoading} type="submit">
{t("actions.add")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -186,18 +186,7 @@ export const CreateRegionForm = ({
className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit}
>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPendingRegion}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Header />
<RouteFocusModal.Body className="flex overflow-hidden">
<div
className={clx(
@@ -434,6 +423,16 @@ export const CreateRegionForm = ({
</div>
</div>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPendingRegion}>
{t("actions.save")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -3,6 +3,8 @@ import { ToastAction, ToastVariant, ToasterPosition } from "@/types"
import * as React from "react"
import { ExternalToast, toast as toastFn } from "sonner"
const DEFAULT_TOAST_POSITION = "top-right"
interface BaseToastProps {
id?: string | number
position?: ToasterPosition
@@ -16,7 +18,11 @@ interface ToastProps extends BaseToastProps {
action?: ToastAction
}
function create(variant: ToastVariant, title: React.ReactNode, props: ToastProps = {}) {
function create(
variant: ToastVariant,
title: React.ReactNode,
props: ToastProps = {}
) {
const external: ExternalToast = {
position: props.position,
duration: props.duration,
@@ -50,13 +56,15 @@ function message(
/**
* The props of the toast.
*/
props: ToastProps = {}
props: ToastProps = {
position: DEFAULT_TOAST_POSITION,
}
) {
return create("message", title, props)
}
function custom() {
return create("message", "Custom",)
return create("message", "Custom")
}
interface VariantToastProps extends Omit<ToastProps, "icon"> {}
@@ -68,7 +76,9 @@ function info(
/**
* The props of the toast.
*/
props: VariantToastProps = {}
props: VariantToastProps = {
position: DEFAULT_TOAST_POSITION,
}
) {
return create("info", title, props)
}
@@ -80,7 +90,9 @@ function error(
/**
* The props of the toast.
*/
props: VariantToastProps = {}
props: VariantToastProps = {
position: DEFAULT_TOAST_POSITION,
}
) {
return create("error", title, props)
}
@@ -92,7 +104,9 @@ function success(
/**
* The props of the toast.
*/
props: VariantToastProps = { }
props: VariantToastProps = {
position: DEFAULT_TOAST_POSITION,
}
) {
return create("success", title, props)
}
@@ -104,7 +118,9 @@ function warning(
/**
* The props of the toast.
*/
props: VariantToastProps = {}
props: VariantToastProps = {
position: DEFAULT_TOAST_POSITION,
}
) {
return create("warning", title, props)
}
@@ -116,7 +132,9 @@ function loading(
/**
* The props of the toast.
*/
props: VariantToastProps = {}
props: VariantToastProps = {
position: DEFAULT_TOAST_POSITION,
}
) {
return create("loading", title, { ...props, dismissable: false })
}