{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "order-summary",
  "title": "Order Summary",
  "author": "@fma-ds/ds",
  "description": "What the user is about to confirm before buying: lines, an applicable benefit, discounts, the total and the action.\n\n`OrderSummary` takes the `title`, `totalLabel`, `total` and optional `totalDescription` (values formatted by the app) and the `action` button.\n\n- **Large screens**: a card that sticks while the page scrolls — put it in the side column.\n- **Smaller screens**: a bar fixed to the bottom with the total and the action; the total opens the lines in a bottom sheet. Leave room under the page, e.g. `pb-28 lg:pb-0`.\n\nIts children sit in a `description-list`:\n\n- **`OrderSummaryLine`**: `label`, `value` and a `description` under the label — an item, a fee, who the order is for. `tone=\"discount\"` for a negative value.\n- **`OrderSummaryOption`**: a benefit behind a `Switch`, such as the account balance, its amount struck through while off.\n\nThe total stands apart by size and weight, not only color.\n\nBuilt-in text is in English; translate it with `i18n.labels`.",
  "dependencies": [
    "cn@^0.5.0",
    "lucide-react@^1.61.0"
  ],
  "registryDependencies": [
    "@fma-ds/button",
    "@fma-ds/card",
    "@fma-ds/description-list",
    "@fma-ds/separator",
    "@fma-ds/sheet",
    "@fma-ds/switch"
  ],
  "files": [
    {
      "path": "src/blocks/order-summary/order-summary.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"cn\";\nimport { ChevronUpIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"@/core/button/button\";\nimport { Card, CardContent, CardFooter, CardHeader, CardTitle } from \"@/core/card/card\";\nimport {\n  DescriptionDetails,\n  DescriptionItem,\n  DescriptionList,\n  DescriptionTerm,\n} from \"@/core/description-list/description-list\";\nimport { Separator } from \"@/core/separator/separator\";\nimport { Sheet, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from \"@/core/sheet/sheet\";\nimport { Switch } from \"@/core/switch/switch\";\n\ntype OrderSummaryLabels = {\n  /** The mobile bar's button that opens the details. */\n  details: string;\n};\n\ntype OrderSummaryI18n = { labels?: Partial<OrderSummaryLabels> };\n\nconst defaultOrderSummaryLabels: OrderSummaryLabels = { details: \"View details\" };\n\ntype OrderSummaryProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n  title: React.ReactNode;\n  totalLabel: React.ReactNode;\n  /** The total, formatted by the app. */\n  total: React.ReactNode;\n  /** Under the total: billing period, installments. */\n  totalDescription?: React.ReactNode;\n  /** The confirm button; rendered in the card, the mobile bar and its sheet. */\n  action: React.ReactNode;\n  i18n?: OrderSummaryI18n;\n};\n\nfunction OrderSummaryTotal({\n  label,\n  value,\n  description,\n}: {\n  label: React.ReactNode;\n  value: React.ReactNode;\n  description?: React.ReactNode;\n}) {\n  return (\n    <div data-slot=\"order-summary-total\" className=\"flex items-baseline justify-between gap-4\">\n      <span className=\"text-base font-medium\">{label}</span>\n      <span className=\"flex flex-col items-end\">\n        <span className=\"text-xl font-semibold tabular-nums\">{value}</span>\n        {description && <span className=\"text-xs text-muted-foreground\">{description}</span>}\n      </span>\n    </div>\n  );\n}\n\n/**\n * What the user is about to confirm: the lines (`OrderSummaryLine`,\n * `OrderSummaryOption`), the total and the action.\n * On large screens it's a card that sticks while the page scrolls (place it\n * in the side column); smaller, a bar fixed to the bottom shows the total and\n * the action, and opens the lines in a sheet. Leave room under the page\n * content for the bar (`pb-24 lg:pb-0`).\n */\nfunction OrderSummary({\n  title,\n  totalLabel,\n  total,\n  totalDescription,\n  action,\n  i18n,\n  className,\n  children,\n  ...props\n}: OrderSummaryProps) {\n  const labels = { ...defaultOrderSummaryLabels, ...i18n?.labels };\n  const totalBlock = (\n    <OrderSummaryTotal label={totalLabel} value={total} description={totalDescription} />\n  );\n\n  return (\n    <div data-slot=\"order-summary\" className={cn(\"lg:sticky lg:top-4\", className)} {...props}>\n      <Card className=\"hidden lg:flex\">\n        <CardHeader>\n          <CardTitle>{title}</CardTitle>\n        </CardHeader>\n        <CardContent className=\"flex flex-col gap-4\">\n          <DescriptionList>{children}</DescriptionList>\n          <Separator />\n          {totalBlock}\n        </CardContent>\n        <CardFooter className=\"flex-col items-stretch gap-2 border-t bg-transparent *:w-full\">\n          {action}\n        </CardFooter>\n      </Card>\n\n      <div\n        data-slot=\"order-summary-bar\"\n        className=\"fixed inset-x-0 bottom-0 z-40 flex items-center gap-3 border-t bg-background px-4 py-3 lg:hidden\"\n      >\n        <SheetTrigger>\n          <Button variant=\"ghost\" className=\"h-auto min-w-0 flex-1 justify-start px-2 py-1\">\n            <span className=\"flex min-w-0 flex-col items-start\">\n              <span className=\"text-xs text-muted-foreground\">{totalLabel}</span>\n              <span className=\"text-base font-semibold tabular-nums\">{total}</span>\n            </span>\n            <ChevronUpIcon aria-hidden=\"true\" className=\"text-muted-foreground\" />\n            <span className=\"sr-only\">{labels.details}</span>\n          </Button>\n          <Sheet side=\"bottom\" className=\"max-h-[85svh]\">\n            <SheetHeader>\n              <SheetTitle>{title}</SheetTitle>\n            </SheetHeader>\n            <div className=\"flex flex-col gap-4 overflow-y-auto px-4\">\n              <DescriptionList>{children}</DescriptionList>\n              <Separator />\n              {totalBlock}\n            </div>\n            <SheetFooter>{action}</SheetFooter>\n          </Sheet>\n        </SheetTrigger>\n        <div className=\"shrink-0\">{action}</div>\n      </div>\n    </div>\n  );\n}\n\n/**\n * One line: an item, a fee, a discount (pass a negative value, e.g.\n * \"− R$ 20,00\", and `tone=\"discount\"`), or who the order is for\n * (`description` under the label).\n */\nfunction OrderSummaryLine({\n  label,\n  value,\n  description,\n  tone = \"default\",\n  className,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  label: React.ReactNode;\n  value: React.ReactNode;\n  description?: React.ReactNode;\n  tone?: \"default\" | \"discount\";\n}) {\n  return (\n    <DescriptionItem\n      data-slot=\"order-summary-line\"\n      data-tone={tone}\n      className={className}\n      {...props}\n    >\n      <DescriptionTerm className=\"flex-col items-start gap-0.5 text-foreground\">\n        {label}\n        {description && (\n          <span className=\"text-xs font-normal text-muted-foreground\">{description}</span>\n        )}\n      </DescriptionTerm>\n      <DescriptionDetails className={cn(tone === \"discount\" && \"text-success\")}>\n        {value}\n      </DescriptionDetails>\n    </DescriptionItem>\n  );\n}\n\n/**\n * A benefit the user can apply (account balance, a gift): a switch with its\n * label and the amount it takes off, muted while off.\n */\nfunction OrderSummaryOption({\n  label,\n  description,\n  value,\n  isSelected,\n  defaultSelected,\n  onChange,\n  isDisabled,\n  className,\n}: {\n  label: React.ReactNode;\n  description?: React.ReactNode;\n  value: React.ReactNode;\n  isSelected?: boolean;\n  defaultSelected?: boolean;\n  onChange?: (isSelected: boolean) => void;\n  isDisabled?: boolean;\n  className?: string;\n}) {\n  const id = React.useId();\n  const [uncontrolled, setUncontrolled] = React.useState(defaultSelected ?? false);\n  const selected = isSelected ?? uncontrolled;\n\n  return (\n    <DescriptionItem\n      data-slot=\"order-summary-option\"\n      data-selected={selected || undefined}\n      className={cn(\"items-center\", className)}\n    >\n      <DescriptionTerm className=\"items-start gap-2 text-foreground\">\n        <Switch\n          id={id}\n          isSelected={selected}\n          isDisabled={isDisabled}\n          onChange={(next) => {\n            setUncontrolled(next);\n            onChange?.(next);\n          }}\n          aria-describedby={description ? `${id}-description` : undefined}\n          className=\"mt-0.5\"\n        />\n        <span className=\"flex flex-col gap-0.5\">\n          <label htmlFor={id}>{label}</label>\n          {description && (\n            <span id={`${id}-description`} className=\"text-xs text-muted-foreground\">\n              {description}\n            </span>\n          )}\n        </span>\n      </DescriptionTerm>\n      <DescriptionDetails\n        className={cn(selected ? \"text-success\" : \"text-muted-foreground line-through\")}\n      >\n        {value}\n      </DescriptionDetails>\n    </DescriptionItem>\n  );\n}\n\nexport type { OrderSummaryI18n, OrderSummaryLabels, OrderSummaryProps };\nexport { OrderSummary, OrderSummaryLine, OrderSummaryOption };\n",
      "type": "registry:component",
      "target": "components/fma-ds/order-summary/order-summary.tsx"
    }
  ],
  "meta": {
    "status": "experimental",
    "source": "original",
    "license": "MIT",
    "tags": [
      "block",
      "checkout",
      "summary",
      "order",
      "total"
    ],
    "contentHash": "917b083c47a4"
  },
  "categories": [
    "blocks"
  ],
  "type": "registry:block"
}