{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "accounting-core-gl",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "ROU asset and liability on diverging curves.",
  "files": [
    {
      "content": "import * as React from \"react\";\nimport { useScrollEdges } from \"@/hooks/ledger/use-scroll-edges\";\nimport { cn } from \"@/lib/ledger/cn\";\nimport type { MoneyValue } from \"@/lib/ledger/money.types\";\nimport { Money } from \"@/components/ledger/money\";\nimport { Rule } from \"@/components/ledger/rule\";\n\nexport type LeasePeriodRow = {\n  period: string;\n  payment: MoneyValue;\n  interest: MoneyValue;\n  principal: MoneyValue;\n  /** They amortize on different curves and diverge. Parallel columns. */\n  liabilityBalance: MoneyValue;\n  rouAmortization: MoneyValue;\n  rouBalance: MoneyValue;\n};\n\nexport interface LeaseScheduleProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  classification: \"operating\" | \"finance\";\n  discountedLiability?: MoneyValue;\n  /** In percent, not as a fraction: `6.25` is 6.25%. */\n  discountRate: number;\n  discountRateSource: string;\n  imputedInterest?: MoneyValue;\n  leaseName: string;\n  /** Undiscounted by year, reconciled to the discounted liability. */\n  maturity?: { year: string; undiscounted: MoneyValue }[];\n  /** Caps the scroller so the header and the period column can freeze. */\n  maxHeight?: number;\n  rows: LeasePeriodRow[];\n}\n\n/**\n * The ROU asset and the lease liability are parallel columns, because they\n * amortize on different curves and diverge — showing one implies the other,\n * and the implication is wrong.\n */\nexport const LeaseSchedule = React.forwardRef<HTMLDivElement, LeaseScheduleProps>(\n  (\n    {\n      className,\n      leaseName,\n      classification,\n      discountRate,\n      discountRateSource,\n      rows,\n      maturity = [],\n      discountedLiability,\n      imputedInterest,\n      maxHeight = 520,\n      ...props\n    },\n    ref,\n  ) => {\n    const scroller = useScrollEdges<HTMLDivElement>();\n    const basis = (\n      <>\n        <span className=\"capitalize\">{classification}</span> lease · discount rate{\" \"}\n        <span className=\"numeric\">{discountRate.toFixed(3)}%</span> ({discountRateSource})\n        {classification === \"operating\"\n          ? \" · single straight-line lease cost in operating expenses\"\n          : \" · amortization and interest presented separately\"}\n      </>\n    );\n    return (\n      <div className={cn(\"space-y-4\", className)} ref={ref} {...props}>\n        <div className=\"rounded-xl border border-border bg-surface\">\n          <div className=\"px-6 pb-4 pt-6\">\n            <p className=\"text-title-sm font-medium\">{leaseName}</p>\n            <p className=\"text-caption text-text-secondary\">{basis}</p>\n          </div>\n          <div className=\"ledger-frozen scrollbar-thin\" ref={scroller} style={{ maxHeight }}>\n            <table className=\"w-full min-w-[900px]\">\n              <caption className=\"sr-only\">{leaseName} lease schedule</caption>\n              <thead>\n                <tr className=\"border-b border-border-subtle\">\n                  <th\n                    className=\"py-3 pl-6 pr-3 text-left text-label font-medium text-text-secondary\"\n                    rowSpan={2}\n                    scope=\"col\"\n                  >\n                    Period\n                  </th>\n                  <th\n                    className=\"px-3 py-3 text-right text-label font-medium text-text-secondary\"\n                    rowSpan={2}\n                    scope=\"col\"\n                  >\n                    Payment\n                  </th>\n                  <th\n                    className=\"border-l border-border-subtle px-3 py-2 text-center text-label font-medium text-text-secondary\"\n                    colSpan={3}\n                    scope=\"col\"\n                  >\n                    Lease liability\n                  </th>\n                  <th\n                    className=\"border-l border-border-subtle py-2 pl-3 pr-6 text-center text-label font-medium text-text-secondary\"\n                    colSpan={2}\n                    scope=\"col\"\n                  >\n                    ROU asset\n                  </th>\n                </tr>\n                <tr className=\"border-b border-border-subtle\">\n                  {[\"Interest\", \"Principal\", \"Balance\", \"Amortization\", \"Balance\"].map((h, i) => (\n                    <th\n                      className={cn(\n                        \"px-3 py-2 text-right text-caption text-text-secondary\",\n                        (i === 0 || i === 3) && \"border-l border-border-subtle\",\n                        i === 4 && \"pr-6\",\n                      )}\n                      /* \"Balance\" appears under both groups — the index disambiguates. */\n                      key={`${h}-${i}`}\n                      scope=\"col\"\n                    >\n                      {h}\n                    </th>\n                  ))}\n                </tr>\n              </thead>\n              <tbody>\n                {rows.map((r) => (\n                  <tr className=\"border-b border-border-subtle\" key={r.period}>\n                    <th className=\"numeric py-2 pl-6 pr-3 text-left text-body font-normal\" scope=\"row\">\n                      {r.period}\n                    </th>\n                    <td className=\"px-3 py-2 text-right\">\n                      <Money accounting value={r.payment} />\n                    </td>\n                    <td className=\"border-l border-border-subtle px-3 py-2 text-right\">\n                      <Money accounting value={r.interest} />\n                    </td>\n                    <td className=\"px-3 py-2 text-right\">\n                      <Money accounting value={r.principal} />\n                    </td>\n                    <td className=\"px-3 py-2 text-right\">\n                      <Money accounting value={r.liabilityBalance} />\n                    </td>\n                    <td className=\"border-l border-border-subtle px-3 py-2 text-right\">\n                      <Money accounting value={r.rouAmortization} />\n                    </td>\n                    <td className=\"py-2 pl-3 pr-6 text-right\">\n                      <Money accounting value={r.rouBalance} />\n                    </td>\n                  </tr>\n                ))}\n              </tbody>\n            </table>\n          </div>\n        </div>\n\n        {maturity.length > 0 && discountedLiability && imputedInterest ? (\n          <div className=\"rounded-xl border border-border bg-surface p-6\">\n            <h3 className=\"text-title-sm font-medium\">Maturity analysis</h3>\n            <table className=\"mt-3 w-full max-w-[420px]\">\n              <tbody>\n                {maturity.map((m) => (\n                  <tr key={m.year}>\n                    <th className=\"py-1 text-left text-body font-normal text-text-secondary\" scope=\"row\">\n                      {m.year}\n                    </th>\n                    <td className=\"py-1 text-right\">\n                      <Money accounting value={m.undiscounted} />\n                    </td>\n                  </tr>\n                ))}\n                <tr>\n                  <td />\n                  <td>\n                    <Rule variant=\"subtotal\" />\n                  </td>\n                </tr>\n                <tr>\n                  <th className=\"py-1 text-left text-body font-normal text-text-secondary\" scope=\"row\">\n                    Less: imputed interest\n                  </th>\n                  <td className=\"py-1 text-right\">\n                    <Money accounting value={imputedInterest} />\n                  </td>\n                </tr>\n                <tr>\n                  <th className=\"py-1 text-left text-label font-medium\" scope=\"row\">\n                    Present value of lease liability\n                  </th>\n                  <td className=\"py-1 text-right\">\n                    <Money accounting size=\"md\" value={discountedLiability} />\n                  </td>\n                </tr>\n                <tr>\n                  <td />\n                  <td>\n                    <Rule variant=\"total\" />\n                  </td>\n                </tr>\n              </tbody>\n            </table>\n          </div>\n        ) : null}\n      </div>\n    );\n  },\n);\nLeaseSchedule.displayName = \"LeaseSchedule\";\n",
      "path": "components/ledger/lease-schedule.tsx",
      "target": "components/ledger/lease-schedule.tsx",
      "type": "registry:component"
    }
  ],
  "meta": {
    "license": "MIT"
  },
  "name": "lease-schedule",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/cn",
    "@lopifi/money",
    "@lopifi/money.types",
    "@lopifi/rule",
    "@lopifi/use-scroll-edges"
  ],
  "title": "LeaseSchedule",
  "type": "registry:component"
}
