docs: migrate UI docs (#13245)

* docs: create a new UI docs project (#13233)

* docs: create a new UI docs project

* fix installation errors

* docs: migrate UI docs content to new project (#13241)

* Fix content

* added examples for some components

* finish adding examples

* lint fix

* fix build errors

* delete empty files

* path fixes + refactor

* fix build error
This commit is contained in:
Shahed Nasser authored and GitHub committed 2025-08-20 11:42:25 +03:00
1 parent 5b7a041246
commit d1a1135328
748 files changed
+13079 -7319

No files matched your search

@@ -0,0 +1,5 @@
import { Alert } from "@medusajs/ui"
export default function AlertDemo() {
return <Alert>You are viewing Medusa docs.</Alert>
}
@@ -0,0 +1,5 @@
import { Alert } from "@medusajs/ui"
export default function AlertDismissable() {
return <Alert dismissible={true}>You are viewing Medusa docs.</Alert>
}
@@ -0,0 +1,5 @@
import { Alert } from "@medusajs/ui"
export default function AlertError() {
return <Alert variant="error">An error occured while updating data.</Alert>
}
@@ -0,0 +1,5 @@
import { Alert } from "@medusajs/ui"
export default function AlertSuccess() {
return <Alert variant="success">Data updated successfully!</Alert>
}
@@ -0,0 +1,5 @@
import { Alert } from "@medusajs/ui"
export default function AlertWarning() {
return <Alert variant="warning">Be careful!</Alert>
}
@@ -0,0 +1,11 @@
import { Avatar } from "@medusajs/ui"
export default function AvatarAccessible() {
return (
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
aria-label="Medusa User"
/>
)
}
@@ -0,0 +1,14 @@
import { Avatar } from "@medusajs/ui"
export default function AvatarCustomStyle() {
return (
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
style={{
boxShadow: "0 0 0 3px #fdba74, 0 1px 2px 0 rgba(0,0,0,0.05)",
border: "none",
}}
/>
)
}
@@ -0,0 +1,10 @@
import { Avatar } from "@medusajs/ui"
export default function AvatarDemo() {
return (
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
/>
)
}
@@ -0,0 +1,5 @@
import { Avatar } from "@medusajs/ui"
export default function AvatarFallback() {
return <Avatar fallback="JD" />
}
@@ -0,0 +1,38 @@
import { Avatar } from "@medusajs/ui"
export default function AvatarSizes() {
return (
<div className="flex gap-4 items-center">
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
size="2xsmall"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
size="xsmall"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
size="small"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
size="base"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
size="large"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
size="xlarge"
/>
</div>
)
}
@@ -0,0 +1,18 @@
import { Avatar } from "@medusajs/ui"
export default function AvatarVariants() {
return (
<div className="flex gap-4">
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
variant="rounded"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/10656202?v=4"
fallback="M"
variant="squared"
/>
</div>
)
}
@@ -0,0 +1,14 @@
import { Badge } from "@medusajs/ui"
export default function BadgeAllColors() {
return (
<div className="flex gap-3">
<Badge color="grey">Grey</Badge>
<Badge color="red">Red</Badge>
<Badge color="green">Green</Badge>
<Badge color="blue">Blue</Badge>
<Badge color="orange">Orange</Badge>
<Badge color="purple">Purple</Badge>
</div>
)
}
@@ -0,0 +1,10 @@
import { Badge } from "@medusajs/ui"
export default function BadgeAllRounded() {
return (
<div className="flex gap-3">
<Badge rounded="base">Base Rounded</Badge>
<Badge rounded="full">Full Rounded</Badge>
</div>
)
}
@@ -0,0 +1,13 @@
import { Badge } from "@medusajs/ui"
export default function BadgeAllSizes() {
return (
<div className="flex gap-3 items-center">
<Badge size="2xsmall">2xsmall</Badge>
<Badge size="xsmall">xsmall</Badge>
<Badge size="small">small</Badge>
<Badge size="base">base</Badge>
<Badge size="large">large</Badge>
</div>
)
}
@@ -0,0 +1,5 @@
import { Badge } from "@medusajs/ui"
export default function BadgeDemo() {
return <Badge>Badge</Badge>
}
@@ -0,0 +1,12 @@
import { Button } from "@medusajs/ui"
export default function ButtonAllSizes() {
return (
<div className="flex gap-4 items-center">
<Button size="small">Small</Button>
<Button size="base">Base</Button>
<Button size="large">Large</Button>
<Button size="xlarge">XLarge</Button>
</div>
)
}
@@ -0,0 +1,12 @@
import { Button } from "@medusajs/ui"
export default function ButtonAllVariants() {
return (
<div className="flex gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="transparent">Transparent</Button>
<Button variant="danger">Danger</Button>
</div>
)
}
@@ -0,0 +1,11 @@
import { Button } from "@medusajs/ui"
export default function ButtonAsLink() {
return (
<Button asChild>
<a href="https://medusajs.com" target="_blank" rel="noopener noreferrer">
Open Medusa Website
</a>
</Button>
)
}
@@ -0,0 +1,5 @@
import { Button } from "@medusajs/ui"
export default function ButtonDemo() {
return <Button>Button</Button>
}
@@ -0,0 +1,5 @@
import { Button } from "@medusajs/ui"
export default function ButtonLoading() {
return <Button isLoading={true}>Button</Button>
}
@@ -0,0 +1,10 @@
import { PlusMini } from "@medusajs/icons"
import { Button } from "@medusajs/ui"
export default function ButtonWithIcon() {
return (
<Button>
Button <PlusMini />
</Button>
)
}
@@ -0,0 +1,14 @@
import { Calendar } from "@medusajs/ui"
import { useState } from "react"
export default function CalendarControlled() {
const [date, setDate] = useState<Date | null>(null)
return (
<div className="flex flex-col gap-2">
<Calendar value={date} onChange={setDate} />
<span className="txt-small text-ui-fg-muted">
Selected: {date?.toDateString() ?? "None"}
</span>
</div>
)
}
@@ -0,0 +1,8 @@
import { Calendar } from "@medusajs/ui"
import * as React from "react"
export default function CalendarDemo() {
const [date, setDate] = React.useState<Date | null>()
return <Calendar value={date} onChange={setDate} />
}
@@ -0,0 +1,15 @@
import { Calendar } from "@medusajs/ui"
export default function CalendarMinMax() {
const min = new Date()
const max = new Date()
max.setDate(max.getDate() + 10)
return (
<div className="flex flex-col gap-2">
<span className="txt-small text-ui-fg-muted">
Selectable dates: {min.toDateString()} to {max.toDateString()}
</span>
<Calendar minValue={min} maxValue={max} />
</div>
)
}
@@ -0,0 +1,17 @@
import { Calendar } from "@medusajs/ui"
function isUnavailable(date: Date) {
// Disable all Sundays
return date.getDay() === 0
}
export default function CalendarUnavailable() {
return (
<div className="flex flex-col gap-2">
<span className="txt-small text-ui-fg-muted">
All Sundays are unavailable for selection.
</span>
<Calendar isDateUnavailable={isUnavailable} />
</div>
)
}
@@ -0,0 +1,24 @@
import { Checkbox, Label } from "@medusajs/ui"
export default function CheckboxAllStates() {
return (
<div className="flex flex-col gap-6">
<div className="flex items-center gap-1">
<Checkbox id="default" />
<Label htmlFor="default">Default</Label>
</div>
<div className="flex items-center gap-1">
<Checkbox id="checked" checked />
<Label htmlFor="checked">Checked</Label>
</div>
<div className="flex items-center gap-1">
<Checkbox id="disabled" disabled />
<Label htmlFor="disabled">Disabled</Label>
</div>
<div className="flex items-center gap-1">
<Checkbox id="indeterminate" checked="indeterminate" />
<Label htmlFor="indeterminate">Indeterminate</Label>
</div>
</div>
)
}
@@ -0,0 +1,44 @@
import { Checkbox, CheckboxCheckedState, Label } from "@medusajs/ui"
import { useState } from "react"
export default function CheckboxControlled() {
const [checked, setChecked] = useState<CheckboxCheckedState>(false)
const handleToggle = () => {
switch (checked) {
case "indeterminate":
setChecked(true)
return
case true:
setChecked(false)
return
default:
setChecked("indeterminate")
}
}
return (
<div className="flex flex-col gap-6 items-center">
<span className="txt-small text-center w-3/4">
The following checkbox will move from unchecked, to indeterminate, and
finally checked each time you click it
</span>
<div className="flex items-center gap-2">
<Checkbox
id="controlled-checkbox"
checked={checked}
onCheckedChange={handleToggle}
/>
<Label htmlFor="controlled-checkbox">
Controlled Checkbox: (
{checked === "indeterminate"
? "Indeterminate"
: checked
? "Checked"
: "Unchecked"}
)
</Label>
</div>
</div>
)
}
@@ -0,0 +1,12 @@
import { Checkbox, Label } from "@medusajs/ui"
export default function CheckboxDemo() {
return (
<div className="flex items-center space-x-2">
<Checkbox id="billing-shipping" />
<Label htmlFor="billing-shipping">
Billing address same as shipping address
</Label>
</div>
)
}
@@ -0,0 +1,30 @@
import { CodeBlock, Label } from "@medusajs/ui"
const snippets = [
{
label: "cURL",
language: "bash",
code: `curl 'http://localhost:9000/store/products/PRODUCT_ID'\n -H 'x-publishable-key: YOUR_API_KEY'`,
hideLineNumbers: true,
},
{
label: "Medusa JS SDK",
language: "jsx",
code: `const { product } = await medusa.store.products.retrieve("prod_123")\nconsole.log(product.id)`,
},
]
export default function CodeBlockDemo() {
return (
<div className="w-full">
<CodeBlock snippets={snippets}>
<CodeBlock.Header>
<CodeBlock.Header.Meta>
<Label weight={"plus"}>/product-detail.js</Label>
</CodeBlock.Header.Meta>
</CodeBlock.Header>
<CodeBlock.Body />
</CodeBlock>
</div>
)
}
@@ -0,0 +1,25 @@
import { CodeBlock, Label } from "@medusajs/ui"
const snippets = [
{
label: "Medusa JS SDK",
language: "jsx",
code: `console.log("Hello, World!")`,
hideCopy: true,
},
]
export default function CodeBlockNoCopy() {
return (
<div className="w-full">
<CodeBlock snippets={snippets}>
<CodeBlock.Header>
<CodeBlock.Header.Meta>
<Label weight={"plus"}>/product-detail.js</Label>
</CodeBlock.Header.Meta>
</CodeBlock.Header>
<CodeBlock.Body />
</CodeBlock>
</div>
)
}
@@ -0,0 +1,19 @@
import { CodeBlock } from "@medusajs/ui"
const snippets = [
{
label: "Medusa JS SDK",
language: "jsx",
code: `console.log("Hello, World!")`,
},
]
export default function CodeBlockNoHeader() {
return (
<div className="w-full">
<CodeBlock snippets={snippets}>
<CodeBlock.Body />
</CodeBlock>
</div>
)
}
@@ -0,0 +1,25 @@
import { CodeBlock, Label } from "@medusajs/ui"
const snippets = [
{
label: "Medusa JS SDK",
language: "jsx",
code: `console.log("Hello, ")\n\nconsole.log("World!")`,
hideLineNumbers: true,
},
]
export default function CodeBlockNoLines() {
return (
<div className="w-full">
<CodeBlock snippets={snippets}>
<CodeBlock.Header>
<CodeBlock.Header.Meta>
<Label weight={"plus"}>/product-detail.js</Label>
</CodeBlock.Header.Meta>
</CodeBlock.Header>
<CodeBlock.Body />
</CodeBlock>
</div>
)
}
@@ -0,0 +1,24 @@
import { CodeBlock, Label } from "@medusajs/ui"
const snippets = [
{
label: "Medusa JS SDK",
language: "jsx",
code: `console.log("Hello, World!")`,
},
]
export default function CodeBlockSingle() {
return (
<div className="w-full">
<CodeBlock snippets={snippets}>
<CodeBlock.Header hideLabels={true}>
<CodeBlock.Header.Meta>
<Label weight={"plus"}>/product-detail.js</Label>
</CodeBlock.Header.Meta>
</CodeBlock.Header>
<CodeBlock.Body />
</CodeBlock>
</div>
)
}
@@ -0,0 +1,42 @@
import { Checkbox, CommandBar, Label, Text } from "@medusajs/ui"
import * as React from "react"
export default function CommandBarDemo() {
const [selected, setSelected] = React.useState<boolean>(false)
return (
<div className="flex justify-center gap-y-2 flex-col">
<div className="flex items-center gap-x-2">
<Checkbox
checked={selected}
onCheckedChange={(checked) =>
setSelected(checked === true ? true : false)
}
/>
<Label>Item One</Label>
</div>
<div><Text size="small" className="text-ui-fg-muted">Check the box to view the command bar</Text></div>
<CommandBar open={selected}>
<CommandBar.Bar>
<CommandBar.Value>1 selected</CommandBar.Value>
<CommandBar.Seperator />
<CommandBar.Command
action={() => {
alert("Delete")
}}
label="Delete"
shortcut="d"
/>
<CommandBar.Seperator />
<CommandBar.Command
action={() => {
alert("Edit")
}}
label="Edit"
shortcut="e"
/>
</CommandBar.Bar>
</CommandBar>
</div>
)
}
@@ -0,0 +1,16 @@
import { Badge, Command } from "@medusajs/ui"
export default function CommandDemo() {
return (
<div className="w-full">
<Command>
<Badge color="green">Get</Badge>
<code>localhost:9000/store/products</code>
<Command.Copy
content="localhost:9000/store/products"
className="ml-auto"
/>
</Command>
</div>
)
}
@@ -0,0 +1,5 @@
import { Container } from "@medusajs/ui"
export default function ContainerDemo() {
return <Container>Content</Container>
}
@@ -0,0 +1,22 @@
import { Container, Heading } from "@medusajs/ui"
export default function ContainerLayout() {
return (
<div className="flex h-full w-full">
<div className="border-ui-border-base w-full max-w-[216px] border-r p-4">
<Heading level="h3">Menubar</Heading>
</div>
<div className="flex w-full flex-col gap-y-2 px-8 pb-8 pt-6">
<Container>
<Heading>Section 1</Heading>
</Container>
<Container>
<Heading>Section 2</Heading>
</Container>
<Container>
<Heading>Section 3</Heading>
</Container>
</div>
</div>
)
}
@@ -0,0 +1,15 @@
import { PlusMini } from "@medusajs/icons"
import { Copy, IconButton, Text } from "@medusajs/ui"
export default function CopyAsChild() {
return (
<div className="flex items-center gap-x-2">
<Text>Copy command</Text>
<Copy content="yarn add @medusajs/ui" asChild>
<IconButton>
<PlusMini />
</IconButton>
</Copy>
</div>
)
}
@@ -0,0 +1,9 @@
import { Code, Copy } from "@medusajs/ui"
export default function CopyDemo() {
return (
<Copy content="yarn add @medusajs/ui">
<Code>yarn add @medusajs/ui</Code>
</Copy>
)
}
+5
View File
@@ -0,0 +1,5 @@
import { Copy } from "@medusajs/ui"
export default function CopyDemo() {
return <Copy content="yarn add @medusajs/ui" />
}
@@ -0,0 +1,9 @@
import { CurrencyInput } from "@medusajs/ui"
export default function CurrencyInputBase() {
return (
<div className="max-w-[250px]">
<CurrencyInput size="base" symbol="$" code="usd" />
</div>
)
}
@@ -0,0 +1,29 @@
import { useState } from "react"
import { CurrencyInput } from "@medusajs/ui"
export default function CurrencyInputControlled() {
const [value, setValue] = useState<string | undefined>("")
const formatValue = (val: string | undefined) => {
if (!val) {
return ""
}
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(parseFloat(val))
}
return (
<div className="max-w-[250px]">
<CurrencyInput
symbol="$"
code="usd"
value={value}
onValueChange={setValue}
aria-label="Amount"
/>
<div className="mt-2 text-xs text-ui-fg-muted">
Value: {formatValue(value)}
</div>
</div>
)
}
@@ -0,0 +1,9 @@
import { CurrencyInput } from "@medusajs/ui"
export default function CurrencyInputDemo() {
return (
<div className="max-w-[250px]">
<CurrencyInput symbol="$" code="usd" />
</div>
)
}
@@ -0,0 +1,15 @@
import { CurrencyInput } from "@medusajs/ui"
export default function CurrencyInputDisabled() {
return (
<div className="max-w-[250px]">
<CurrencyInput
symbol="€"
code="eur"
disabled
value={"100"}
aria-label="Amount"
/>
</div>
)
}
@@ -0,0 +1,27 @@
import { useState } from "react"
import { CurrencyInput } from "@medusajs/ui"
export default function CurrencyInputError() {
const [value, setValue] = useState<string | undefined>("0")
const [touched, setTouched] = useState(false)
const isError = touched && (!value || parseFloat(value) <= 0)
return (
<div className="max-w-[250px]">
<CurrencyInput
symbol="$"
code="usd"
value={value}
onValueChange={(val) => setValue(val)}
aria-label="Amount"
aria-invalid={isError}
onBlur={() => setTouched(true)}
min={0.01}
/>
{isError && (
<div className="mt-2 text-xs text-ui-fg-error">
Amount must be greater than 0
</div>
)}
</div>
)
}
@@ -0,0 +1,9 @@
import { CurrencyInput } from "@medusajs/ui"
export default function CurrencyInputSmall() {
return (
<div className="max-w-[250px]">
<CurrencyInput size="small" symbol="$" code="usd" />
</div>
)
}
@@ -0,0 +1,76 @@
import { DataTable, DataTableRowSelectionState, Heading, createDataTableColumnHelper, createDataTableCommandHelper, useDataTable } from "@medusajs/ui"
import { useState } from "react"
let products = [
{
id: "1",
title: "Shirt",
price: 10,
},
{
id: "2",
title: "Pants",
price: 20,
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
// Commands requires a select column.
columnHelper.select(),
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
]
const commandHelper = createDataTableCommandHelper()
const useCommands = () => {
return [
commandHelper.command({
label: "Delete",
shortcut: "D",
action: async (selection) => {
const productsToDeleteIds = Object.keys(selection)
alert(`You deleted product(s) with IDs: ${productsToDeleteIds.join()}`)
}
})
]
}
export default function ProductTable () {
const [rowSelection, setRowSelection] = useState<DataTableRowSelectionState>({})
const commands = useCommands()
const instance = useDataTable({
data: products,
columns,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
commands,
rowSelection: {
state: rowSelection,
onRowSelectionChange: setRowSelection,
},
});
return (
<DataTable instance={instance}>
<DataTable.Toolbar className="flex justify-between items-center">
<Heading>Products</Heading>
</DataTable.Toolbar>
<DataTable.Table />
{/** This component will the command bar when the user has selected at least one row. **/}
<DataTable.CommandBar selectedLabel={(count) => `${count} selected`} />
</DataTable>
);
};
@@ -0,0 +1,59 @@
import { createDataTableColumnHelper, useDataTable, DataTable, Heading, Badge } from "@medusajs/ui"
const products = [
{
id: "1",
title: "Shirt",
price: 10,
is_active: true
},
{
id: "2",
title: "Pants",
price: 20,
is_active: false
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
columnHelper.accessor("is_active", {
header: "Status",
cell: ({ getValue }) => {
const isActive = getValue()
return (
<Badge color={isActive ? "green" : "grey"} size="xsmall">
{isActive ? "Active" : "Inactive"}
</Badge>
)
}
})
]
export default function ProductTable () {
const table = useDataTable({
columns,
data: products,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
})
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center">
<Heading>Products</Heading>
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
)
}
@@ -0,0 +1,46 @@
import { createDataTableColumnHelper, useDataTable, DataTable, Heading } from "@medusajs/ui"
const products = [
{
id: "1",
title: "Shirt",
price: 10
},
{
id: "2",
title: "Pants",
price: 20
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
]
export default function ProductTable () {
const table = useDataTable({
columns,
data: products,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
})
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center">
<Heading>Products</Heading>
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
)
}
@@ -0,0 +1,137 @@
import { DataTable, DataTableFilteringState, Heading, createDataTableColumnHelper, createDataTableFilterHelper, useDataTable } from "@medusajs/ui"
import { useMemo, useState } from "react"
const products = [
{
id: "1",
title: "Shirt",
price: 10,
created_at: new Date()
},
{
id: "2",
title: "Pants",
price: 20,
created_at: new Date("2026-01-01")
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
columnHelper.accessor("created_at", {
header: "Created At",
cell: ({ getValue }) => {
return getValue().toLocaleString()
}
}),
]
const filterHelper = createDataTableFilterHelper<typeof products[0]>()
const filters = [
filterHelper.accessor("created_at", {
type: "date",
label: "Created At",
format: "date",
formatDateValue: (date) => date.toLocaleString(),
rangeOptionStartLabel: "From",
rangeOptionEndLabel: "To",
rangeOptionLabel: "Between",
options: [
{
label: "Today",
value: {
$gte: new Date(new Date().setHours(0, 0, 0, 0)).toString(),
$lte: new Date(new Date().setHours(23, 59, 59, 999)).toString()
}
},
{
label: "Yesterday",
value: {
$gte: new Date(new Date().setHours(0, 0, 0, 0) - 24 * 60 * 60 * 1000).toString(),
$lte: new Date(new Date().setHours(0, 0, 0, 0)).toString()
}
},
{
label: "Last Week",
value: {
$gte: new Date(new Date().setHours(0, 0, 0, 0) - 7 * 24 * 60 * 60 * 1000).toString(),
$lte: new Date(new Date().setHours(0, 0, 0, 0)).toString()
},
},
]
}),
]
export default function ProductTable () {
const [filtering, setFiltering] = useState<DataTableFilteringState>({})
const shownProducts = useMemo(() => {
return products.filter((product) => {
return Object.entries(filtering).every(([key, value]) => {
if (!value) {
return true
}
if (typeof value === "string") {
// @ts-ignore
return product[key].toString().toLowerCase().includes(value.toString().toLowerCase())
}
if (Array.isArray(value)) {
// @ts-ignore
return value.includes(product[key].toLowerCase())
}
if (typeof value === "object") {
// @ts-ignore
const date = new Date(product[key])
let matching = false
if ("$gte" in value && value.$gte) {
matching = date >= new Date(value.$gte as number)
}
if ("$lte" in value && value.$lte) {
matching = date <= new Date(value.$lte as number)
}
if ("$lt" in value && value.$lt) {
matching = date < new Date(value.$lt as number)
}
if ("$gt" in value && value.$gt) {
matching = date > new Date(value.$gt as number)
}
return matching
}
})
})
}, [filtering])
const table = useDataTable({
data: shownProducts,
columns,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
filtering: {
state: filtering,
onFilteringChange: setFiltering,
},
filters
});
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex justify-between items-center">
<Heading>Products</Heading>
{/** This component will render a menu that allows the user to choose which filters to apply to the table data. **/}
<DataTable.FilterMenu tooltip="Filter" />
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
);
};
@@ -0,0 +1,107 @@
import { DataTable, DataTableFilteringState, Heading, createDataTableColumnHelper, createDataTableFilterHelper, useDataTable } from "@medusajs/ui"
import { useMemo, useState } from "react"
const products = [
{
id: "1",
title: "Shirt",
price: 10
},
{
id: "2",
title: "Pants",
price: 20
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
]
const filterHelper = createDataTableFilterHelper<typeof products[0]>()
const filters = [
filterHelper.accessor("title", {
type: "select",
label: "Title",
options: products.map((product) => ({
label: product.title,
value: product.title.toLowerCase()
}))
}),
]
export default function ProductTable () {
const [filtering, setFiltering] = useState<DataTableFilteringState>({
title: ["shirt"]
})
const shownProducts = useMemo(() => {
return products.filter((product) => {
return Object.entries(filtering).every(([key, value]) => {
if (!value) {
return true
}
if (typeof value === "string") {
// @ts-ignore
return product[key].toString().toLowerCase().includes(value.toString().toLowerCase())
}
if (Array.isArray(value)) {
// @ts-ignore
return value.includes(product[key].toLowerCase())
}
if (typeof value === "object") {
// @ts-ignore
const date = new Date(product[key])
let matching = false
if ("$gte" in value && value.$gte) {
matching = date >= new Date(value.$gte as number)
}
if ("$lte" in value && value.$lte) {
matching = date <= new Date(value.$lte as number)
}
if ("$lt" in value && value.$lt) {
matching = date < new Date(value.$lt as number)
}
if ("$gt" in value && value.$gt) {
matching = date > new Date(value.$gt as number)
}
return matching
}
})
})
}, [filtering])
const table = useDataTable({
data: shownProducts,
columns,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
filtering: {
state: filtering,
onFilteringChange: setFiltering,
},
filters
});
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex justify-between items-center">
<Heading>Products</Heading>
{/** This component will render a menu that allows the user to choose which filters to apply to the table data. **/}
<DataTable.FilterMenu tooltip="Filter" />
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
);
};
@@ -0,0 +1,105 @@
import { DataTable, DataTableFilteringState, Heading, createDataTableColumnHelper, createDataTableFilterHelper, useDataTable } from "@medusajs/ui"
import { useMemo, useState } from "react"
const products = [
{
id: "1",
title: "Shirt",
price: 10
},
{
id: "2",
title: "Pants",
price: 20
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
]
const filterHelper = createDataTableFilterHelper<typeof products[0]>()
const filters = [
filterHelper.accessor("title", {
type: "select",
label: "Title",
options: products.map((product) => ({
label: product.title,
value: product.title.toLowerCase()
}))
}),
]
export default function ProductTable () {
const [filtering, setFiltering] = useState<DataTableFilteringState>({})
const shownProducts = useMemo(() => {
return products.filter((product) => {
return Object.entries(filtering).every(([key, value]) => {
if (!value) {
return true
}
if (typeof value === "string") {
// @ts-ignore
return product[key].toString().toLowerCase().includes(value.toString().toLowerCase())
}
if (Array.isArray(value)) {
// @ts-ignore
return value.includes(product[key].toLowerCase())
}
if (typeof value === "object") {
// @ts-ignore
const date = new Date(product[key])
let matching = false
if ("$gte" in value && value.$gte) {
matching = date >= new Date(value.$gte as number)
}
if ("$lte" in value && value.$lte) {
matching = date <= new Date(value.$lte as number)
}
if ("$lt" in value && value.$lt) {
matching = date < new Date(value.$lt as number)
}
if ("$gt" in value && value.$gt) {
matching = date > new Date(value.$gt as number)
}
return matching
}
})
})
}, [filtering])
const table = useDataTable({
data: shownProducts,
columns,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
filtering: {
state: filtering,
onFilteringChange: setFiltering,
},
filters
});
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex justify-between items-center">
<Heading>Products</Heading>
{/** This component will render a menu that allows the user to choose which filters to apply to the table data. **/}
<DataTable.FilterMenu tooltip="Filter" />
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
);
};
@@ -0,0 +1,143 @@
import { DataTable, Heading, createDataTableColumnHelper, useDataTable, type DataTablePaginationState } from "@medusajs/ui"
import { useMemo, useState } from "react"
const products = [
{
id: "1",
title: "Shirt",
price: 10
},
{
id: "2",
title: "Pants",
price: 20
},
{
id: "3",
title: "Hat",
price: 15
},
{
id: "4",
title: "Socks",
price: 5
},
{
id: "5",
title: "Shoes",
price: 50
},
{
id: "6",
title: "Jacket",
price: 100
},
{
id: "7",
title: "Scarf",
price: 25
},
{
id: "8",
title: "Gloves",
price: 12
},
{
id: "9",
title: "Belt",
price: 18
},
{
id: "10",
title: "Sunglasses",
price: 30
},
{
id: "11",
title: "Watch",
price: 200
},
{
id: "12",
title: "Tie",
price: 20
},
{
id: "13",
title: "Sweater",
price: 40
},
{
id: "14",
title: "Jeans",
price: 60
},
{
id: "15",
title: "Shorts",
price: 25
},
{
id: "16",
title: "Blouse",
price: 35
},
{
id: "17",
title: "Dress",
price: 80
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
]
const PAGE_SIZE = 10;
export default function ProductTable () {
const [pagination, setPagination] = useState<DataTablePaginationState>({
pageSize: PAGE_SIZE,
pageIndex: 0,
})
const shownProducts = useMemo(() => {
return products.slice(
pagination.pageIndex * pagination.pageSize,
(pagination.pageIndex + 1) * pagination.pageSize
)
}, [pagination])
const table = useDataTable({
data: shownProducts,
columns,
rowCount: products.length,
getRowId: (product) => product.id,
pagination: {
// Pass the pagination state and updater to the table instance
state: pagination,
onPaginationChange: setPagination,
},
isLoading: false,
});
return (
<DataTable instance={table}>
<DataTable.Toolbar>
<Heading>Products</Heading>
</DataTable.Toolbar>
<DataTable.Table />
{/** This component will render the pagination controls **/}
<DataTable.Pagination />
</DataTable>
);
};
@@ -0,0 +1,60 @@
import {
createDataTableColumnHelper,
useDataTable,
DataTable,
Heading,
usePrompt,
} from "@medusajs/ui"
const products = [
{
id: "1",
title: "Shirt",
price: 10,
},
{
id: "2",
title: "Pants",
price: 20,
},
]
const columnHelper = createDataTableColumnHelper<(typeof products)[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
]
export default function ProductTable() {
const prompt = usePrompt()
const table = useDataTable({
columns,
data: products,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
onRowClick: async (event, row) => {
await prompt({
title: "Row Clicked",
description: `You clicked row #${row.id}`,
})
},
})
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center">
<Heading>Products</Heading>
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
)
}
@@ -0,0 +1,60 @@
import { createDataTableColumnHelper, useDataTable, DataTable, Heading } from "@medusajs/ui"
import { useMemo, useState } from "react"
const products = [
{
id: "1",
title: "Shirt",
price: 10
},
{
id: "2",
title: "Pants",
price: 20
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "Price",
enableSorting: true,
}),
]
export default function ProductTable () {
const [search, setSearch] = useState<string>("")
const shownProducts = useMemo(() => {
return products.filter((product) => product.title.toLowerCase().includes(search.toLowerCase()))
}, [search])
const table = useDataTable({
columns,
data: shownProducts,
getRowId: (product) => product.id,
rowCount: products.length,
isLoading: false,
// Pass the state and onSearchChange to the table instance.
search: {
state: search,
onSearchChange: setSearch
}
})
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center">
<Heading>Products</Heading>
{/* This component renders the search bar */}
<DataTable.Search placeholder="Search..." />
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
)
}
@@ -0,0 +1,85 @@
import { DataTable, DataTableSortingState, Heading, createDataTableColumnHelper, useDataTable } from "@medusajs/ui"
import { useMemo, useState } from "react"
const products = [
{
id: "1",
title: "Shirt",
price: 10
},
{
id: "2",
title: "Pants",
price: 20
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
// Enables sorting for the column.
enableSorting: true,
// If omitted, the header will be used instead if it's a string,
// otherwise the accessor key (id) will be used.
sortLabel: "Title",
// If omitted the default value will be "A-Z"
sortAscLabel: "A-Z",
// If omitted the default value will be "Z-A"
sortDescLabel: "Z-A",
}),
columnHelper.accessor("price", {
header: "Price",
}),
]
export default function ProductTable () {
const [sorting, setSorting] = useState<DataTableSortingState | null>({
id: "title",
desc: false,
})
const shownProducts = useMemo(() => {
if (!sorting) {
return products
}
return products.slice().sort((a, b) => {
// @ts-ignore
const aVal = a[sorting.id]
// @ts-ignore
const bVal = b[sorting.id]
if (aVal < bVal) {
return sorting.desc ? 1 : -1
}
if (aVal > bVal) {
return sorting.desc ? -1 : 1
}
return 0
})
}, [sorting])
const table = useDataTable({
data: shownProducts,
columns,
getRowId: (product) => product.id,
rowCount: products.length,
sorting: {
// Pass the pagination state and updater to the table instance
state: sorting,
onSortingChange: setSorting,
},
isLoading: false,
})
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex justify-between items-center">
<Heading>Products</Heading>
{/** This component will render a menu that allows the user to choose which column to sort by and in what direction. **/}
<DataTable.SortingMenu tooltip="Sort" />
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
);
};
@@ -0,0 +1,82 @@
import { DataTable, DataTableSortingState, Heading, createDataTableColumnHelper, useDataTable } from "@medusajs/ui"
import { useMemo, useState } from "react"
const products = [
{
id: "1",
title: "Shirt",
price: 10
},
{
id: "2",
title: "Pants",
price: 20
}
]
const columnHelper = createDataTableColumnHelper<typeof products[0]>()
const columns = [
columnHelper.accessor("title", {
header: "Title",
// Enables sorting for the column.
enableSorting: true,
// If omitted, the header will be used instead if it's a string,
// otherwise the accessor key (id) will be used.
sortLabel: "Title",
// If omitted the default value will be "A-Z"
sortAscLabel: "A-Z",
// If omitted the default value will be "Z-A"
sortDescLabel: "Z-A",
}),
columnHelper.accessor("price", {
header: "Price",
}),
]
export default function ProductTable () {
const [sorting, setSorting] = useState<DataTableSortingState | null>(null);
const shownProducts = useMemo(() => {
if (!sorting) {
return products
}
return products.slice().sort((a, b) => {
// @ts-ignore
const aVal = a[sorting.id]
// @ts-ignore
const bVal = b[sorting.id]
if (aVal < bVal) {
return sorting.desc ? 1 : -1
}
if (aVal > bVal) {
return sorting.desc ? -1 : 1
}
return 0
})
}, [sorting])
const table = useDataTable({
data: shownProducts,
columns,
getRowId: (product) => product.id,
rowCount: products.length,
sorting: {
// Pass the pagination state and updater to the table instance
state: sorting,
onSortingChange: setSorting,
},
isLoading: false,
})
return (
<DataTable instance={table}>
<DataTable.Toolbar className="flex justify-between items-center">
<Heading>Products</Heading>
{/** This component will render a menu that allows the user to choose which column to sort by and in what direction. **/}
<DataTable.SortingMenu tooltip="Sort" />
</DataTable.Toolbar>
<DataTable.Table />
</DataTable>
);
};
@@ -0,0 +1,25 @@
"use client"
import { DatePicker } from "@medusajs/ui"
export default function DatePickerBusinessHours() {
return (
<div className="w-[300px]">
<DatePicker
granularity="hour"
defaultValue={new Date()}
aria-label="Select date and hour for business scheduling"
isDateUnavailable={(date) => {
// Disable weekends and holidays
const day = date.getDay()
const isWeekend = day === 0 || day === 6
// Example: Disable specific holiday (Christmas)
const isChristmas = date.getMonth() === 11 && date.getDate() === 25
return isWeekend || isChristmas
}}
/>
</div>
)
}
@@ -0,0 +1,21 @@
"use client"
import { DatePicker } from "@medusajs/ui"
import { useState } from "react"
export default function DatePickerControlled() {
const [date, setDate] = useState<Date | null>(new Date())
return (
<div className="space-y-4 w-[300px]">
<DatePicker
value={date}
onChange={setDate}
aria-label="Select a date"
/>
<div className="text-ui-fg-subtle text-ui-body-small">
Selected date: {date ? date.toLocaleDateString() : "None"}
</div>
</div>
)
}
@@ -0,0 +1,9 @@
import { DatePicker } from "@medusajs/ui"
export default function DatePickerDemo() {
return (
<div className="w-[250px]">
<DatePicker />
</div>
)
}
@@ -0,0 +1,20 @@
"use client"
import { DatePicker } from "@medusajs/ui"
export default function DatePickerDisabledDates() {
// Disable weekends (Saturday and Sunday)
const isWeekend = (date: Date) => {
const day = date.getDay()
return day === 0 || day === 6 // Sunday = 0, Saturday = 6
}
return (
<div className="w-[300px]">
<DatePicker
isDateUnavailable={isWeekend}
aria-label="Select a weekday (weekends disabled)"
/>
</div>
)
}
@@ -0,0 +1,71 @@
"use client"
import { DatePicker, Button, Label } from "@medusajs/ui"
import { useState } from "react"
export default function DatePickerForm() {
const [eventDate, setEventDate] = useState<Date | null>(null)
const [reminderDate, setReminderDate] = useState<Date | null>(null)
const [submitted, setSubmitted] = useState(false)
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
setSubmitted(true)
// Here you would typically send data to your API
setTimeout(() => setSubmitted(false), 5000)
}
const isFormValid = eventDate && reminderDate
return (
<div className="p-6 max-w-md border border-ui-border-base rounded-lg bg-ui-bg-base">
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2">
<h3 className="text-ui-fg-base font-medium">Schedule Event</h3>
<p className="text-ui-fg-subtle text-ui-body-small">
Set up your event and reminder dates
</p>
</div>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="event-date">Event Date & Time</Label>
<DatePicker
id="event-date"
value={eventDate}
onChange={setEventDate}
minValue={new Date()}
aria-label="Select event date and time"
/>
</div>
<div className="space-y-2">
<Label htmlFor="reminder-date">Reminder Date</Label>
<DatePicker
id="reminder-date"
value={reminderDate}
onChange={setReminderDate}
minValue={new Date()}
maxValue={eventDate || undefined}
aria-label="Select reminder date"
/>
</div>
</div>
<Button
type="submit"
disabled={!isFormValid || submitted}
className="w-full"
>
{submitted ? "Event Scheduled!" : "Schedule Event"}
</Button>
</form>
{submitted && (
<div className="mt-4 text-ui-fg-subtle text-ui-body-small">
Event scheduled for {eventDate?.toLocaleString()} with a reminder on {reminderDate?.toLocaleDateString()}.
</div>
)}
</div>
)
}
@@ -0,0 +1,47 @@
"use client"
import { DatePicker } from "@medusajs/ui"
export default function DatePickerGranularity() {
const defaultDate = new Date()
return (
<div className="space-y-6 max-w-md">
<div className="space-y-2">
<div className="text-ui-fg-base text-ui-body-small font-medium">Date Only</div>
<DatePicker
granularity="day"
defaultValue={defaultDate}
aria-label="Select day only"
/>
</div>
<div className="space-y-2">
<div className="text-ui-fg-base text-ui-body-small font-medium">Date and Time with Hour Precision</div>
<DatePicker
granularity="hour"
defaultValue={defaultDate}
aria-label="Select date and hour"
/>
</div>
<div className="space-y-2">
<div className="text-ui-fg-base text-ui-body-small font-medium">Date and Time with Minute Precision</div>
<DatePicker
granularity="minute"
defaultValue={defaultDate}
aria-label="Select date and time with minutes"
/>
</div>
<div className="space-y-2">
<div className="text-ui-fg-base text-ui-body-small font-medium">Date and Time with Second Precision</div>
<DatePicker
granularity="second"
defaultValue={defaultDate}
aria-label="Select date and time with seconds"
/>
</div>
</div>
)
}
@@ -0,0 +1,19 @@
"use client"
import { DatePicker } from "@medusajs/ui"
export default function DatePickerMinMax() {
const today = new Date()
const maxDate = new Date()
maxDate.setDate(today.getDate() + 30) // 30 days from today
return (
<div className="w-[300px]">
<DatePicker
minValue={today}
maxValue={maxDate}
aria-label="Select a date within the next 30 days"
/>
</div>
)
}
@@ -0,0 +1,15 @@
"use client"
import { DatePicker } from "@medusajs/ui"
export default function DatePickerWithTime() {
return (
<div className="w-[300px]">
<DatePicker
granularity="minute"
defaultValue={new Date()}
aria-label="Select date and time"
/>
</div>
)
}
@@ -0,0 +1,25 @@
import { Button, Drawer, Text } from "@medusajs/ui"
export default function DrawerDemo() {
return (
<Drawer>
<Drawer.Trigger asChild>
<Button>Edit Variant</Button>
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Edit Variant</Drawer.Title>
</Drawer.Header>
<Drawer.Body className="p-4">
<Text>This is where you edit the variant&apos;s details</Text>
</Drawer.Body>
<Drawer.Footer>
<Drawer.Close asChild>
<Button variant="secondary">Cancel</Button>
</Drawer.Close>
<Button>Save</Button>
</Drawer.Footer>
</Drawer.Content>
</Drawer>
)
}
@@ -0,0 +1,51 @@
import { useState } from "react"
import { Button, Drawer, Input, Label } from "@medusajs/ui"
export default function DrawerWithForm() {
const [name, setName] = useState("")
const [open, setOpen] = useState(false)
const [submitted, setSubmitted] = useState(false)
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setSubmitted(true)
setOpen(false)
}
return (
<div className="flex flex-col gap-2 items-center">
<Drawer open={open} onOpenChange={setOpen}>
<Drawer.Trigger asChild>
<Button>Open Drawer</Button>
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Simple Form</Drawer.Title>
</Drawer.Header>
<form onSubmit={handleSubmit} className="flex flex-col flex-1">
<Drawer.Body>
<Label htmlFor="name">Name</Label>
<Input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
/>
</Drawer.Body>
<Drawer.Footer>
<Drawer.Close asChild>
<Button variant="secondary" type="button">
Cancel
</Button>
</Drawer.Close>
<Button type="submit">Submit</Button>
</Drawer.Footer>
</form>
</Drawer.Content>
</Drawer>
{submitted && (
<div className="text-ui-fg-muted">Form submitted with name {name}</div>
)}
</div>
)
}
@@ -0,0 +1,29 @@
import { EllipsisHorizontal, PencilSquare, Plus, Trash } from "@medusajs/icons"
import { DropdownMenu, IconButton } from "@medusajs/ui"
export default function DropdownMenuDemo() {
return (
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<IconButton>
<EllipsisHorizontal />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item className="gap-x-2">
<PencilSquare className="text-ui-fg-subtle" />
Edit
</DropdownMenu.Item>
<DropdownMenu.Item className="gap-x-2">
<Plus className="text-ui-fg-subtle" />
Add
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item className="gap-x-2">
<Trash className="text-ui-fg-subtle" />
Delete
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
)
}
@@ -0,0 +1,21 @@
import { DropdownMenu, IconButton } from "@medusajs/ui"
import { Trash, BarsThree } from "@medusajs/icons"
export default function DropdownMenuDisabledAndIcons() {
return (
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<IconButton>
<BarsThree />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Edit</DropdownMenu.Item>
<DropdownMenu.Item disabled>
<Trash className="mr-2" />
Delete
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
)
}
@@ -0,0 +1,65 @@
import { useEffect, useCallback } from "react"
import { DropdownMenu, IconButton, toast, Toaster } from "@medusajs/ui"
import { Keyboard } from "@medusajs/icons"
function getOsShortcut() {
const isMacOs =
typeof navigator !== "undefined"
? navigator.userAgent.toLowerCase().indexOf("mac") !== 0
: true
return isMacOs ? "⌘" : "Ctrl"
}
export default function DropdownMenuWithShortcuts() {
const osShortcut = getOsShortcut()
const handleEdit = useCallback(() => {
toast.success("Success", {
description: "Edit shortcut triggered!",
})
}, [])
const handleDelete = useCallback(() => {
toast.success("Success", {
description: "Delete shortcut triggered!",
})
}, [])
useEffect(() => {
function handleKeydown(e: KeyboardEvent) {
if (e.metaKey && e.key.toLowerCase() === "e") {
e.preventDefault()
handleEdit()
}
if (e.metaKey && e.key.toLowerCase() === "d") {
e.preventDefault()
handleDelete()
}
}
window.addEventListener("keydown", handleKeydown)
return () => window.removeEventListener("keydown", handleKeydown)
}, [handleEdit, handleDelete])
return (
<>
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<IconButton>
<Keyboard />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item onSelect={handleEdit}>
Edit
<DropdownMenu.Shortcut>{osShortcut}E</DropdownMenu.Shortcut>
</DropdownMenu.Item>
<DropdownMenu.Item onSelect={handleDelete}>
Delete
<DropdownMenu.Shortcut>{osShortcut}D</DropdownMenu.Shortcut>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
<Toaster />
</>
)
}
@@ -0,0 +1,49 @@
import { EllipsisHorizontal } from "@medusajs/icons"
import { DropdownMenu, IconButton } from "@medusajs/ui"
import React from "react"
type SortingState = "asc" | "desc" | "alpha" | "alpha-reverse" | "none"
export default function DropdownMenuSorting() {
const [sort, setSort] = React.useState<SortingState>("none")
return (
<div className="flex flex-col items-center gap-y-2">
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<IconButton>
<EllipsisHorizontal />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content className="w-[300px]">
<DropdownMenu.RadioGroup
value={sort}
onValueChange={(v) => setSort(v as SortingState)}
>
<DropdownMenu.RadioItem value="none">
No Sorting
</DropdownMenu.RadioItem>
<DropdownMenu.Separator />
<DropdownMenu.RadioItem value="alpha">
Alphabetical
<DropdownMenu.Hint>A-Z</DropdownMenu.Hint>
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="alpha-reverse">
Reverse Alphabetical
<DropdownMenu.Hint>Z-A</DropdownMenu.Hint>
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="asc">
Created At - Ascending
<DropdownMenu.Hint>1 - 30</DropdownMenu.Hint>
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="desc">
Created At - Descending
<DropdownMenu.Hint>30 - 1</DropdownMenu.Hint>
</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
</DropdownMenu.Content>
</DropdownMenu>
<span className="txt-small text-ui-fg-muted">Sorting: {sort}</span>
</div>
)
}
@@ -0,0 +1,27 @@
import { DropdownMenu, IconButton } from "@medusajs/ui"
import { BarsArrowDown } from "@medusajs/icons"
export default function DropdownMenuSubmenu() {
return (
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<IconButton>
<BarsArrowDown />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Edit</DropdownMenu.Item>
<DropdownMenu.SubMenu>
<DropdownMenu.SubMenuTrigger>
More Actions
</DropdownMenu.SubMenuTrigger>
<DropdownMenu.SubMenuContent>
<DropdownMenu.Item>Duplicate</DropdownMenu.Item>
<DropdownMenu.Item>Archive</DropdownMenu.Item>
</DropdownMenu.SubMenuContent>
</DropdownMenu.SubMenu>
<DropdownMenu.Item>Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
)
}
@@ -0,0 +1,39 @@
import { Button, FocusModal, Heading, Input, Label, Text } from "@medusajs/ui"
import { useState } from "react"
export default function FocusModalControlled() {
const [open, setOpen] = useState(false)
return (
<div>
<Button onClick={() => setOpen(true)}>Edit Variant</Button>
<FocusModal open={open} onOpenChange={setOpen}>
<FocusModal.Content>
<FocusModal.Header>
<FocusModal.Title>Edit Variant</FocusModal.Title>
</FocusModal.Header>
<FocusModal.Body className="flex flex-col items-center py-16">
<div className="flex w-full max-w-lg flex-col gap-y-8">
<div className="flex flex-col gap-y-1">
<Heading>Create API key</Heading>
<Text className="text-ui-fg-subtle">
Create and manage API keys. You can create multiple keys to
organize your applications.
</Text>
</div>
<div className="flex flex-col gap-y-2">
<Label htmlFor="key_name" className="text-ui-fg-subtle">
Key name
</Label>
<Input id="key_name" placeholder="my_app" />
</div>
</div>
</FocusModal.Body>
<FocusModal.Footer>
<Button onClick={() => setOpen(false)}>Save</Button>
</FocusModal.Footer>
</FocusModal.Content>
</FocusModal>
</div>
)
}
@@ -0,0 +1,36 @@
import { Button, FocusModal, Heading, Input, Label, Text } from "@medusajs/ui"
export default function FocusModalDemo() {
return (
<FocusModal>
<FocusModal.Trigger asChild>
<Button>Edit Variant</Button>
</FocusModal.Trigger>
<FocusModal.Content>
<FocusModal.Header>
<FocusModal.Title>Edit Variant</FocusModal.Title>
</FocusModal.Header>
<FocusModal.Body className="flex flex-col items-center py-16">
<div className="flex w-full max-w-lg flex-col gap-y-8">
<div className="flex flex-col gap-y-1">
<Heading>Create API key</Heading>
<Text className="text-ui-fg-subtle">
Create and manage API keys. You can create multiple keys to
organize your applications.
</Text>
</div>
<div className="flex flex-col gap-y-2">
<Label htmlFor="key_name" className="text-ui-fg-subtle">
Key name
</Label>
<Input id="key_name" placeholder="my_app" />
</div>
</div>
</FocusModal.Body>
<FocusModal.Footer>
<Button>Save</Button>
</FocusModal.Footer>
</FocusModal.Content>
</FocusModal>
)
}
@@ -0,0 +1,48 @@
import { Button, FocusModal, Input, Label } from "@medusajs/ui"
import { useState } from "react"
export default function FocusModalForm() {
const [open, setOpen] = useState(false)
const [value, setValue] = useState("")
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setOpen(false)
}
return (
<div className="flex flex-col gap-2 items-center">
<FocusModal open={open} onOpenChange={setOpen}>
<FocusModal.Trigger asChild>
<Button>Create Item</Button>
</FocusModal.Trigger>
<FocusModal.Content>
<FocusModal.Header>
<FocusModal.Title>Create Item</FocusModal.Title>
</FocusModal.Header>
<form onSubmit={handleSubmit} className="flex flex-col flex-1">
<FocusModal.Body>
<div className="p-6">
<Label htmlFor="name">Name</Label>
<Input
id="name"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter your name"
/>
</div>
</FocusModal.Body>
<FocusModal.Footer>
<Button type="submit">Submit</Button>
</FocusModal.Footer>
</form>
</FocusModal.Content>
</FocusModal>
{value && (
<div className="text-ui-fg-muted">
Form submitted with name: {value}
</div>
)}
</div>
)
}
@@ -0,0 +1,32 @@
import { Button, FocusModal } from "@medusajs/ui"
export default function NestedFocusModals() {
return (
<FocusModal>
<FocusModal.Trigger asChild>
<Button>Open Outer Modal</Button>
</FocusModal.Trigger>
<FocusModal.Content>
<FocusModal.Header>
<FocusModal.Title>Outer Modal</FocusModal.Title>
</FocusModal.Header>
<FocusModal.Body className="p-6 flex flex-col space-y-2">
<p>This is the outer modal.</p>
<FocusModal>
<FocusModal.Trigger asChild>
<Button variant="secondary">Open Nested Modal</Button>
</FocusModal.Trigger>
<FocusModal.Content>
<FocusModal.Header>
<FocusModal.Title>Nested Modal</FocusModal.Title>
</FocusModal.Header>
<FocusModal.Body className="p-6">
<p>This is a nested focus modal for additional information.</p>
</FocusModal.Body>
</FocusModal.Content>
</FocusModal>
</FocusModal.Body>
</FocusModal.Content>
</FocusModal>
)
}
@@ -0,0 +1,11 @@
import { Heading } from "@medusajs/ui"
export default function HeadingDemo() {
return (
<div className="flex flex-col items-center">
<Heading level="h1">This is an H1 heading</Heading>
<Heading level="h2">This is an H2 heading</Heading>
<Heading level="h3">This is an H3 heading</Heading>
</div>
)
}
@@ -0,0 +1,27 @@
import { IconBadge } from "@medusajs/ui"
import { BuildingTax } from "@medusajs/icons"
export default function IconBadgeAllColors() {
return (
<div className="flex gap-3">
<IconBadge color="grey">
<BuildingTax />
</IconBadge>
<IconBadge color="purple">
<BuildingTax />
</IconBadge>
<IconBadge color="orange">
<BuildingTax />
</IconBadge>
<IconBadge color="red">
<BuildingTax />
</IconBadge>
<IconBadge color="blue">
<BuildingTax />
</IconBadge>
<IconBadge color="green">
<BuildingTax />
</IconBadge>
</div>
)
}
@@ -0,0 +1,15 @@
import { IconBadge } from "@medusajs/ui"
import { BuildingTax } from "@medusajs/icons"
export default function IconBadgeAllSizes() {
return (
<div className="flex gap-3 items-center">
<IconBadge size="base">
<BuildingTax />
</IconBadge>
<IconBadge size="large">
<BuildingTax />
</IconBadge>
</div>
)
}
@@ -0,0 +1,10 @@
import { BuildingTax } from "@medusajs/icons"
import { IconBadge } from "@medusajs/ui"
export default function IconBadgeDemo() {
return (
<IconBadge>
<BuildingTax />
</IconBadge>
)
}
@@ -0,0 +1,27 @@
import { IconButton } from "@medusajs/ui"
import { PlusMini } from "@medusajs/icons"
export default function IconButtonAllSizes() {
return (
<div className="flex gap-2 items-center">
<IconButton size="2xsmall">
<PlusMini />
</IconButton>
<IconButton size="xsmall">
<PlusMini />
</IconButton>
<IconButton size="small">
<PlusMini />
</IconButton>
<IconButton size="base">
<PlusMini />
</IconButton>
<IconButton size="large">
<PlusMini />
</IconButton>
<IconButton size="xlarge">
<PlusMini />
</IconButton>
</div>
)
}
@@ -0,0 +1,15 @@
import { IconButton } from "@medusajs/ui"
import { PlusMini } from "@medusajs/icons"
export default function IconButtonAllVariants() {
return (
<div className="flex gap-2">
<IconButton variant="primary">
<PlusMini />
</IconButton>
<IconButton variant="transparent">
<PlusMini />
</IconButton>
</div>
)
}
@@ -0,0 +1,10 @@
import { PlusMini } from "@medusajs/icons"
import { IconButton } from "@medusajs/ui"
export default function IconButtonDemo() {
return (
<IconButton>
<PlusMini />
</IconButton>
)
}
@@ -0,0 +1,10 @@
import { PlusMini } from "@medusajs/icons"
import { IconButton } from "@medusajs/ui"
export default function IconButtonDisabled() {
return (
<IconButton disabled>
<PlusMini />
</IconButton>
)
}
@@ -0,0 +1,10 @@
import { PlusMini } from "@medusajs/icons"
import { IconButton } from "@medusajs/ui"
export default function IconButtonLoading() {
return (
<IconButton isLoading className="relative">
<PlusMini />
</IconButton>
)
}
@@ -0,0 +1,5 @@
import { Sun } from "@medusajs/icons"
export default function Component() {
return <Sun className="text-ui-fg-interactive" />
}
@@ -0,0 +1,5 @@
import { Sun } from "@medusajs/icons"
export default function Component() {
return <Sun color="#FDB813" />
}
@@ -0,0 +1,11 @@
import { InlineTip } from "@medusajs/ui"
export default function InlineTipDemo() {
return (
<InlineTip
label="Tip"
>
Medusa UI is a package of React components to be used in Medusa Admin customizations.
</InlineTip>
)
}
@@ -0,0 +1,12 @@
import { InlineTip } from "@medusajs/ui"
export default function InlineTipError() {
return (
<InlineTip
label="Error"
variant="error"
>
An error occurred. Please try again.
</InlineTip>
)
}
@@ -0,0 +1,12 @@
import { InlineTip } from "@medusajs/ui"
export default function InlineTipSuccess() {
return (
<InlineTip
label="Success"
variant="success"
>
Product created successfully!
</InlineTip>
)
}
@@ -0,0 +1,12 @@
import { InlineTip } from "@medusajs/ui"
export default function InlineTipWarning() {
return (
<InlineTip
label="Warning"
variant="warning"
>
This action cannot be undone.
</InlineTip>
)
}
@@ -0,0 +1,18 @@
import { Input } from "@medusajs/ui"
import { useState } from "react"
export default function InputControlled() {
const [value, setValue] = useState("")
return (
<div className="flex flex-col items-center gap-2">
<Input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter name"
id="controlled-input"
/>
{value && <span>Hello, {value}!</span>}
</div>
)
}
@@ -0,0 +1,9 @@
import { Input } from "@medusajs/ui"
export default function InputDemo() {
return (
<div className="w-[250px]">
<Input placeholder="Sales Channel Name" id="sales-channel-name" />
</div>
)
}
@@ -0,0 +1,9 @@
import { Input } from "@medusajs/ui"
export default function InputDisabled() {
return (
<div className="w-[250px]">
<Input placeholder="Disabled" id="disabled-input" disabled />
</div>
)
}
@@ -0,0 +1,13 @@
import { Input } from "@medusajs/ui"
export default function InputError() {
return (
<div className="w-[250px]">
<Input
placeholder="Sales Channel Name"
id="sales-channel-name"
aria-invalid={true}
/>
</div>
)
}
@@ -0,0 +1,9 @@
import { Input } from "@medusajs/ui"
export default function InputPassword() {
return (
<div className="w-[250px]">
<Input id="password" type="password" defaultValue="supersecret" />
</div>
)
}
@@ -0,0 +1,9 @@
import { Input } from "@medusajs/ui"
export default function InputSearch() {
return (
<div className="w-[250px]">
<Input placeholder="Search" id="search-input" type="search" />
</div>
)
}
@@ -0,0 +1,9 @@
import { Input } from "@medusajs/ui"
export default function InputSmall() {
return (
<div className="w-[250px]">
<Input placeholder="First name" id="first-name" size="small" />
</div>
)
}
+5
View File
@@ -0,0 +1,5 @@
import { Kbd } from "@medusajs/ui"
export default function KbdDemo() {
return <Kbd>⌘ + K</Kbd>
}
@@ -0,0 +1,40 @@
import { Label } from "@medusajs/ui"
export default function LabelAllSizes() {
return (
<div className="flex gap-8 items-center">
<div className="flex flex-col gap-1">
<Label size="xsmall" weight="regular">
XSmall - Regular
</Label>
<Label size="xsmall" weight="plus">
XSmall - Plus
</Label>
</div>
<div className="flex flex-col gap-1">
<Label size="small" weight="regular">
Small - Regular
</Label>
<Label size="small" weight="plus">
Small - Plus
</Label>
</div>
<div className="flex flex-col gap-1">
<Label size="base" weight="regular">
Base - Regular
</Label>
<Label size="base" weight="plus">
Base - Plus
</Label>
</div>
<div className="flex flex-col gap-1">
<Label size="large" weight="regular">
Large - Regular
</Label>
<Label size="large" weight="plus">
Large - Plus
</Label>
</div>
</div>
)
}
Loaded 100 of 748 files, more files were not shown because too many files have changed in this diff. Show more