feat(dashboard,ui): Streamline spacing and sizing (#6061)

This commit is contained in:
Kasper Fabricius Kristensen
2024-01-15 11:43:16 +01:00
committed by GitHub
parent 5dacd4ac9f
commit a2c149e7e5
266 changed files with 10738 additions and 4646 deletions

View File

@@ -0,0 +1,37 @@
import { clx } from "@medusajs/ui"
import * as Popover from "@radix-ui/react-popover"
type ComboboxOption = {
value: string
label: string
}
type ComboboxProps = {
size?: "base" | "small"
options: ComboboxOption[]
value: string
}
export const Combobox = ({ size = "base" }: ComboboxProps) => {
return (
<Popover.Root>
<Popover.Trigger asChild>
<button
className={clx(
"bg-ui-bg-field shadow-buttons-neutral transition-fg flex w-full select-none items-center justify-between rounded-md outline-none",
"data-[placeholder]:text-ui-fg-muted text-ui-fg-base",
"hover:bg-ui-bg-field-hover",
"focus:shadow-borders-interactive-with-active data-[state=open]:!shadow-borders-interactive-with-active",
"aria-[invalid=true]:border-ui-border-error aria-[invalid=true]:shadow-borders-error",
"invalid::border-ui-border-error invalid:shadow-borders-error",
"disabled:!bg-ui-bg-disabled disabled:!text-ui-fg-disabled",
{
"h-8 px-2 py-1.5 txt-compact-small": size === "base",
"h-7 px-2 py-1 txt-compact-small": size === "small",
}
)}
></button>
</Popover.Trigger>
</Popover.Root>
)
}

View File

@@ -0,0 +1,54 @@
import { forwardRef } from "react"
import { TrianglesMini } from "@medusajs/icons"
import { clx } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { countries } from "../../../lib/countries"
export const CountrySelect = forwardRef<
HTMLSelectElement,
React.ComponentPropsWithoutRef<"select"> & { placeholder?: string }
>(({ className, disabled, placeholder, ...props }, ref) => {
const { t } = useTranslation()
return (
<div className="relative">
<TrianglesMini
className={clx(
"absolute right-2 top-1/2 -translate-y-1/2 text-ui-fg-muted transition-fg pointer-events-none",
{
"text-ui-fg-disabled": disabled,
}
)}
/>
<select
disabled={disabled}
className={clx(
"appearance-none bg-ui-bg-field shadow-buttons-neutral transition-fg flex w-full select-none items-center justify-between rounded-md outline-none px-2 py-1 txt-compact-small",
"placeholder:text-ui-fg-muted text-ui-fg-base",
"hover:bg-ui-bg-field-hover",
"focus-visible:shadow-borders-interactive-with-active data-[state=open]:!shadow-borders-interactive-with-active",
"aria-[invalid=true]:border-ui-border-error aria-[invalid=true]:shadow-borders-error",
"invalid::border-ui-border-error invalid:shadow-borders-error",
"disabled:!bg-ui-bg-disabled disabled:!text-ui-fg-disabled",
className
)}
{...props}
ref={ref}
>
{/* Add an empty option so the first option is preselected */}
<option value="" disabled hidden className="text-ui-fg-muted">
{placeholder || t("fields.selectCountry")}
</option>
{countries.map((country) => {
return (
<option key={country.iso_2} value={country.iso_2}>
{country.display_name}
</option>
)
})}
</select>
</div>
)
})
CountrySelect.displayName = "CountrySelect"

View File

@@ -0,0 +1 @@
export * from "./country-select"

View File

@@ -15,6 +15,7 @@ export const DebouncedSearch = ({
value: initialValue,
onChange,
debounce = 500,
size = "small",
placeholder,
...props
}: DebouncedSearchProps) => {

View File

@@ -0,0 +1,59 @@
import { ExclamationCircle, MagnifyingGlass } from "@medusajs/icons"
import { Button, Text } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
type NoResultsProps = {
title: string
message?: string
}
export const NoResults = ({ title, message }: NoResultsProps) => {
const { t } = useTranslation()
return (
<div className="flex h-[400px] w-full items-center justify-center">
<div className="flex flex-col items-center gap-y-2">
<MagnifyingGlass />
<Text size="small" leading="compact" weight="plus">
{title}
</Text>
<Text size="small" className="text-ui-fg-subtle">
{message ?? t("general.noResultsMessage")}
</Text>
</div>
</div>
)
}
type NoRecordsProps = {
title?: string
message?: string
action?: {
to: string
label: string
}
}
export const NoRecords = ({ title, message, action }: NoRecordsProps) => {
const { t } = useTranslation()
return (
<div className="flex h-[400px] w-full flex-col items-center justify-center gap-y-6">
<div className="flex flex-col items-center gap-y-2">
<ExclamationCircle />
<Text size="small" leading="compact" weight="plus">
{title ?? t("general.noRecordsTitle")}
</Text>
<Text size="small" className="text-ui-fg-subtle">
{message ?? t("general.noRecordsMessage")}
</Text>
</div>
{action && (
<Link to={action.to}>
<Button variant="secondary">{action.label}</Button>
</Link>
)}
</div>
)
}

View File

@@ -0,0 +1 @@
export * from "./empty-table-content"

View File

@@ -0,0 +1 @@
export * from "./json-view-section"

View File

@@ -21,23 +21,27 @@ type JsonViewProps = {
}
// TODO: Fix the positioning of the copy btn
export const JsonView = ({ data, root }: JsonViewProps) => {
export const JsonViewSection = ({ data, root }: JsonViewProps) => {
const numberOfKeys = Object.keys(data).length
return (
<Container className="flex items-center justify-between py-6">
<Container className="flex items-center justify-between px-6 py-4">
<div className="flex items-center gap-x-4">
<Heading level="h2">JSON</Heading>
<Badge>{numberOfKeys} keys</Badge>
</div>
<Drawer>
<Drawer.Trigger asChild>
<IconButton variant="transparent" className="text-ui-fg-subtle">
<IconButton
size="small"
variant="transparent"
className="text-ui-fg-subtle"
>
<ArrowsPointingOut />
</IconButton>
</Drawer.Trigger>
<Drawer.Content className="border-ui-code-border bg-ui-code-bg-base text-ui-code-text-base dark overflow-hidden border shadow-none max-md:inset-x-2 max-md:max-w-[calc(100%-16px)]">
<div className="bg-ui-code-bg-header border-ui-code-border flex items-center justify-between border-b px-8 py-6">
<div className="bg-ui-code-bg-header border-ui-code-border flex items-center justify-between border-b px-6 py-4">
<div className="flex items-center gap-x-4">
<Heading>JSON</Heading>
<Badge>{numberOfKeys} keys</Badge>
@@ -45,7 +49,11 @@ export const JsonView = ({ data, root }: JsonViewProps) => {
<div className="flex items-center gap-x-2">
<Kbd>esc</Kbd>
<Drawer.Close asChild>
<IconButton variant="transparent" className="text-ui-fg-subtle">
<IconButton
size="small"
variant="transparent"
className="text-ui-fg-subtle"
>
<XMarkMini />
</IconButton>
</Drawer.Close>

View File

@@ -1 +0,0 @@
export * from "./json-view";

View File

@@ -8,7 +8,7 @@ import { StatusBadge, Text } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { Thumbnail } from "../thumbnail"
export const ProductInventoryCell = ({
export const ProductVariantCell = ({
variants,
}: {
variants: ProductVariant[] | null
@@ -23,13 +23,10 @@ export const ProductInventoryCell = ({
)
}
const inventory = variants.reduce((acc, v) => acc + v.inventory_quantity, 0)
return (
<Text size="small" className="text-ui-fg-base">
{t("products.inStockVariants", {
{t("products.variantCount", {
count: variants.length,
inventory: inventory,
})}
</Text>
)

View File

@@ -0,0 +1 @@
export * from "./skeleton"

View File

@@ -0,0 +1,16 @@
import { clx } from "@medusajs/ui"
type SkeletonProps = {
className?: string
}
export const Skeleton = ({ className }: SkeletonProps) => {
return (
<div
className={clx(
"bg-ui-bg-component animate-pulse w-3 h-3 rounded-[4px]",
className
)}
/>
)
}