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:
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>
|
||||
)
|
||||
}
|
||||
@@ -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'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>
|
||||
)
|
||||
}
|
||||
@@ -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
Reference in new issue
Block a user