{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"data-table","type":"registry:ui","title":"Data Table","description":"A full-featured table built on TanStack Table v9: search, faceted filters, sorting, column visibility, row selection, pagination and CSV or Excel export.","dependencies":["@tanstack/react-table","lucide-react"],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/lib-export.json","https://dashral.com/r/lib-locale.json","https://dashral.com/r/badge.json","https://dashral.com/r/button.json","https://dashral.com/r/checkbox.json","https://dashral.com/r/dropdown-menu.json","https://dashral.com/r/input.json","https://dashral.com/r/select.json","https://dashral.com/r/table.json"],"files":[{"path":"registry/dashral/data-table.tsx","type":"registry:file","target":"components/dashral/data-table.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport {\n  columnFacetingFeature,\n  columnFilteringFeature,\n  columnVisibilityFeature,\n  createColumnHelper,\n  createFacetedRowModel,\n  createFacetedUniqueValues,\n  createFilteredRowModel,\n  createPaginatedRowModel,\n  constructFilterFn,\n  createSortedRowModel,\n  filterFn_includesString,\n  globalFilteringFeature,\n  rowPaginationFeature,\n  rowSelectionFeature,\n  rowSortingFeature,\n  sortFn_alphanumeric,\n  sortFn_basic,\n  sortFn_datetime,\n  sortFn_text,\n  tableFeatures,\n  useTable,\n  type Column,\n  type ColumnDef,\n  type ReactTable,\n  type RowData,\n} from \"@tanstack/react-table\"\nimport {\n  ArrowDown,\n  ArrowUp,\n  ChevronLeft,\n  ChevronRight,\n  ChevronsLeft,\n  ChevronsRight,\n  ChevronsUpDown,\n  Download,\n  PlusCircle,\n  Search,\n  SearchX,\n  Settings2,\n  X,\n} from \"lucide-react\"\n\nimport { exportRows, type ExportCell, type ExportFormat } from \"./lib/export\"\nimport { useLocale } from \"./lib/locale\"\nimport { cn } from \"./lib/utils\"\nimport { Badge } from \"./badge\"\nimport { Button } from \"./button\"\nimport { Checkbox } from \"./checkbox\"\nimport { DropdownMenu } from \"./dropdown-menu\"\nimport { Input } from \"./input\"\nimport { Select } from \"./select\"\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from \"./table\"\n\n/** Keeps rows whose value equals one of the selected options (used by faceted filters). */\nconst filterFn_oneOf = constructFilterFn({\n  filter: (dataValue, filterValue: unknown[]) => filterValue.includes(dataValue),\n  autoRemove: (value) => !Array.isArray(value) || value.length === 0,\n})\n\n/**\n * Features registered for every DataTable (TanStack Table v9 registers features explicitly).\n * Define your columns against this type: `createDataTableColumnHelper<MyRow>()`.\n */\nexport const dataTableFeatures = tableFeatures({\n  columnFilteringFeature,\n  globalFilteringFeature,\n  filteredRowModel: createFilteredRowModel(),\n  filterFns: { includesString: filterFn_includesString, oneOf: filterFn_oneOf },\n  rowSortingFeature,\n  sortedRowModel: createSortedRowModel(),\n  sortFns: { alphanumeric: sortFn_alphanumeric, basic: sortFn_basic, datetime: sortFn_datetime, text: sortFn_text },\n  columnFacetingFeature,\n  facetedRowModel: createFacetedRowModel(),\n  facetedUniqueValues: createFacetedUniqueValues(),\n  rowPaginationFeature,\n  paginatedRowModel: createPaginatedRowModel(),\n  rowSelectionFeature,\n  columnVisibilityFeature,\n})\n\nexport type DataTableFeatures = typeof dataTableFeatures\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type DataTableColumnDef<TData extends RowData> = ColumnDef<DataTableFeatures, TData, any>\nexport type DataTableInstance<TData extends RowData> = ReactTable<DataTableFeatures, TData>\n\nexport function createDataTableColumnHelper<TData extends RowData>() {\n  return createColumnHelper<DataTableFeatures, TData>()\n}\n\nexport type DataTableFilter = {\n  /** Column id to filter. The column needs `filterFn: \"oneOf\"`. */\n  column: string\n  title: string\n  options: { label: string; value: string; icon?: React.ComponentType<{ className?: string }> }[]\n}\n\n/** A selection checkbox, labelled in the current language. */\nfunction SelectCheckbox({\n  checked,\n  onCheckedChange,\n  label,\n}: {\n  checked: boolean | \"indeterminate\"\n  onCheckedChange: (checked: boolean) => void\n  label: \"selectAllRows\" | \"selectRow\"\n}) {\n  const { t } = useLocale()\n  return <Checkbox checked={checked} onCheckedChange={(value) => onCheckedChange(!!value)} aria-label={t(label)} />\n}\n\n/** Checkbox column for row selection. Put it first in your columns array. */\nexport function dataTableSelectColumn<TData extends RowData>(): DataTableColumnDef<TData> {\n  return {\n    id: \"select\",\n    header: ({ table }) => (\n      <SelectCheckbox\n        checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && \"indeterminate\")}\n        onCheckedChange={(value) => table.toggleAllPageRowsSelected(value)}\n        label=\"selectAllRows\"\n      />\n    ),\n    cell: ({ row }) => <SelectCheckbox checked={row.getIsSelected()} onCheckedChange={(value) => row.toggleSelected(value)} label=\"selectRow\" />,\n    enableSorting: false,\n    enableHiding: false,\n    enableGlobalFilter: false,\n  }\n}\n\n/** Sortable column header. Use as `header: ({ column }) => <DataTableColumnHeader column={column} title=\"Name\" />`. */\nexport function DataTableColumnHeader<TData extends RowData, TValue>({\n  column,\n  title,\n  className,\n}: {\n  column: Column<DataTableFeatures, TData, TValue>\n  title: string\n  className?: string\n}) {\n  if (!column.getCanSort()) return <div className={className}>{title}</div>\n\n  const sorted = column.getIsSorted()\n  const Icon = sorted === \"asc\" ? ArrowUp : sorted === \"desc\" ? ArrowDown : ChevronsUpDown\n\n  return (\n    <Button\n      variant=\"ghost\"\n      size=\"sm\"\n      onClick={() => column.toggleSorting(sorted === \"asc\")}\n      className={cn(\"-ms-3 h-8 data-[state=sorted]:text-foreground\", className)}\n      data-state={sorted ? \"sorted\" : undefined}\n    >\n      {title}\n      <Icon className=\"size-3.5\" aria-hidden />\n    </Button>\n  )\n}\n\nfunction DataTableFacetedFilter<TData extends RowData>({\n  table,\n  filter,\n}: {\n  table: DataTableInstance<TData>\n  filter: DataTableFilter\n}) {\n  const { t } = useLocale()\n  const column = table.getColumn(filter.column)\n  if (!column) return null\n\n  const counts = column.getFacetedUniqueValues()\n  const selected = new Set((column.getFilterValue() as string[] | undefined) ?? [])\n\n  function toggle(value: string, checked: boolean) {\n    if (checked) selected.add(value)\n    else selected.delete(value)\n    column!.setFilterValue(selected.size ? [...selected] : undefined)\n  }\n\n  return (\n    <DropdownMenu\n      align=\"start\"\n      className=\"w-52\"\n      trigger={\n        <Button variant=\"outline\" size=\"sm\" className=\"h-8 border-dashed\">\n          <PlusCircle />\n          {filter.title}\n          {selected.size > 0 && (\n            <Badge color=\"neutral\" className=\"ms-1 rounded-sm px-1 font-normal\">\n              {selected.size}\n            </Badge>\n          )}\n        </Button>\n      }\n      items={[\n        { type: \"label\", label: filter.title },\n        { type: \"separator\" },\n        ...filter.options.map((option) => ({\n          type: \"checkbox\" as const,\n          checked: selected.has(option.value),\n          onCheckedChange: (checked: boolean) => toggle(option.value, checked),\n          icon: option.icon && <option.icon className=\"size-4 text-muted-foreground\" />,\n          label: (\n            <>\n              {option.label}\n              <span className=\"ms-auto font-mono text-xs text-muted-foreground tabular-nums\">{counts.get(option.value) ?? 0}</span>\n            </>\n          ),\n        })),\n        ...(selected.size > 0\n          ? [{ type: \"separator\" as const }, { label: t(\"clearFilter\"), onSelect: () => column.setFilterValue(undefined), className: \"justify-center\" }]\n          : []),\n      ]}\n    />\n  )\n}\n\n/** \"createdAt\" / \"created_at\" → \"Created at\" */\nfunction humanize(id: string) {\n  const words = id.replace(/([a-z])([A-Z])/g, \"$1 $2\").replace(/[_-]+/g, \" \").toLowerCase()\n  return words.charAt(0).toUpperCase() + words.slice(1)\n}\n\nconst formatLabels: Record<ExportFormat, string> = { csv: \"CSV\", xlsx: \"Excel (.xlsx)\" }\n\nfunction DataTableExportMenu<TData extends RowData>({ table, options }: { table: DataTableInstance<TData>; options: DataTableExport<TData> }) {\n  const { t } = useLocale()\n  const { filename = \"export\", formats = [\"csv\", \"xlsx\"], columns: overrides = {}, extraColumns = [] } = options\n  const selected = Object.keys(table.state.rowSelection).length\n\n  const download = (format: ExportFormat) => {\n    // Visible data columns (selection and actions columns have no accessor), unless left out.\n    const columns = table.getVisibleLeafColumns().filter((column) => column.accessorFn !== undefined && overrides[column.id] !== false)\n    const header = [\n      ...columns.map((column) => {\n        const override = overrides[column.id]\n        return (override && override.label) ?? (typeof column.columnDef.header === \"string\" ? column.columnDef.header : humanize(column.id))\n      }),\n      ...extraColumns.map((extra) => extra.label),\n    ]\n    // Every row the search and filters leave, in the current order, across all pages; or just the selected ones.\n    const rows = (selected ? table.getSelectedRowModel() : table.getSortedRowModel()).rows.map((row) => [\n      ...columns.map((column) => {\n        const override = overrides[column.id]\n        const value = override && override.value ? override.value(row.original) : row.getValue(column.id)\n        return value === null || value === undefined || value instanceof Date || [\"string\", \"number\", \"boolean\"].includes(typeof value)\n          ? (value as ExportCell)\n          : String(value)\n      }),\n      ...extraColumns.map((extra) => extra.value(row.original)),\n    ])\n    const today = new Date()\n    const stamp = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, \"0\")}-${String(today.getDate()).padStart(2, \"0\")}`\n    exportRows(format, [header, ...rows], `${filename}-${stamp}`)\n  }\n\n  return (\n    <DropdownMenu\n      align=\"end\"\n      className=\"w-48\"\n      trigger={\n        // Icon only on phones; the label is still read out.\n        <Button variant=\"outline\" size=\"sm\" className=\"h-8 max-sm:w-8 max-sm:px-0\">\n          <Download aria-hidden /> <span className=\"max-sm:sr-only\">{t(\"export\")}</span>\n        </Button>\n      }\n      items={[\n        { type: \"label\", label: selected ? t(\"exportSelected\", { count: selected }) : t(\"exportAll\") },\n        { type: \"separator\" },\n        ...formats.map((format) => ({ label: formatLabels[format], onSelect: () => download(format) })),\n      ]}\n    />\n  )\n}\n\nfunction DataTableViewOptions<TData extends RowData>({ table }: { table: DataTableInstance<TData> }) {\n  const { t } = useLocale()\n  // Only data columns can be toggled — display columns (selection, actions) always stay.\n  const columns = table\n    .getAllLeafColumns()\n    .filter((column) => column.accessorFn !== undefined && column.getCanHide())\n\n  return (\n    <DropdownMenu\n      align=\"end\"\n      className=\"w-44\"\n      trigger={\n        // Icon only on phones; the label is still read out.\n        <Button variant=\"outline\" size=\"sm\" className=\"h-8 max-sm:w-8 max-sm:px-0\">\n          <Settings2 aria-hidden /> <span className=\"max-sm:sr-only\">{t(\"columns\")}</span>\n        </Button>\n      }\n      items={[\n        { type: \"label\", label: t(\"toggleColumns\") },\n        { type: \"separator\" },\n        ...columns.map((column) => ({\n          type: \"checkbox\" as const,\n          checked: column.getIsVisible(),\n          onCheckedChange: (value: boolean) => column.toggleVisibility(value),\n          label: typeof column.columnDef.header === \"string\" ? column.columnDef.header : humanize(column.id),\n        })),\n      ]}\n    />\n  )\n}\n\nfunction DataTablePagination<TData extends RowData>({\n  table,\n  pageSizes,\n  selectable,\n}: {\n  table: DataTableInstance<TData>\n  pageSizes: number[]\n  selectable: boolean\n}) {\n  const { t } = useLocale()\n  const { pageIndex, pageSize } = table.state.pagination\n  const selectedCount = Object.keys(table.state.rowSelection).length\n  const filteredCount = table.getFilteredRowModel().rows.length\n  const pageCount = Math.max(table.getPageCount(), 1)\n\n  return (\n    // On a phone: the count on its own line, then rows per page at the left and the page and its buttons at the right.\n    <div className=\"flex flex-wrap items-center justify-between gap-x-6 gap-y-3 px-1 text-sm text-muted-foreground\">\n      <p aria-live=\"polite\">\n        {selectable && selectedCount > 0\n          ? t(\"rowsSelected\", { selected: selectedCount, total: t(\"rows\", { count: filteredCount }) })\n          : t(\"rows\", { count: filteredCount })}\n      </p>\n      <div className=\"flex flex-wrap items-center gap-x-3 gap-y-2 max-sm:w-full max-sm:justify-between sm:gap-x-6\">\n        <div className=\"flex items-center gap-2\">\n          <span className=\"whitespace-nowrap\">{t(\"rowsPerPage\")}</span>\n          <Select\n            value={String(pageSize)}\n            onValueChange={(value) => table.setPageSize(Number(value))}\n            options={pageSizes.map(String)}\n            size=\"sm\"\n            side=\"top\"\n            aria-label={t(\"rowsPerPage\")}\n            className=\"w-[4.5rem]\"\n          />\n        </div>\n        <div className=\"flex items-center gap-3\">\n        <span className=\"whitespace-nowrap tabular-nums\">\n          {t(\"pageOf\", { page: pageIndex + 1, total: pageCount })}\n        </span>\n        <div className=\"flex items-center gap-1\">\n          <Button\n            variant=\"outline\"\n            size=\"icon-sm\"\n            className=\"hidden sm:inline-flex\"\n            onClick={() => table.setPageIndex(0)}\n            disabled={!table.getCanPreviousPage()}\n            aria-label={t(\"firstPage\")}\n          >\n            <ChevronsLeft className=\"rtl:-scale-x-100\" />\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"icon-sm\"\n            onClick={() => table.previousPage()}\n            disabled={!table.getCanPreviousPage()}\n            aria-label={t(\"previousPage\")}\n          >\n            <ChevronLeft className=\"rtl:-scale-x-100\" />\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"icon-sm\"\n            onClick={() => table.nextPage()}\n            disabled={!table.getCanNextPage()}\n            aria-label={t(\"nextPage\")}\n          >\n            <ChevronRight className=\"rtl:-scale-x-100\" />\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"icon-sm\"\n            className=\"hidden sm:inline-flex\"\n            onClick={() => table.setPageIndex(table.getPageCount() - 1)}\n            disabled={!table.getCanNextPage()}\n            aria-label={t(\"lastPage\")}\n          >\n            <ChevronsRight className=\"rtl:-scale-x-100\" />\n          </Button>\n        </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n\nexport type DataTableExport<TData extends RowData> = {\n  /** Without an extension; the date is added, e.g. \"orders-2026-10-15.xlsx\". */\n  filename?: string\n  /** The formats offered, in this order. */\n  formats?: ExportFormat[]\n  /**\n   * Per column id: a heading and a value for the file (e.g. a number instead of the formatted text the cell shows),\n   * or false to leave the column out. By default every visible data column, with its raw value.\n   */\n  columns?: Record<string, false | { label?: string; value?: (row: TData) => ExportCell }>\n  /** Columns only in the file, after the table's, e.g. an email the table shows under the name. */\n  extraColumns?: { label: string; value: (row: TData) => ExportCell }[]\n}\n\nexport type DataTableProps<TData extends RowData> = {\n  columns: DataTableColumnDef<TData>[]\n  data: TData[]\n  /** Stable row id — required for selection to survive sorting and paging. */\n  getRowId?: (row: TData) => string\n  searchPlaceholder?: string\n  filters?: DataTableFilter[]\n  pageSizes?: number[]\n  /** Extra toolbar content, e.g. bulk actions for selected rows. */\n  toolbar?: (table: DataTableInstance<TData>) => React.ReactNode\n  /**\n   * An Export menu (CSV, Excel) in the toolbar. It exports the rows the search, filters and sorting leave (all pages)\n   * or the selected ones, with the visible columns.\n   */\n  exportable?: boolean | DataTableExport<TData>\n  emptyMessage?: string\n  /**\n   * Back to the first page whenever the rows change (the default). Turn it off when rows are edited in place, e.g. a\n   * count typed into a cell: the page stays. The search and filters still start from the first page either way.\n   */\n  autoResetPage?: boolean\n  className?: string\n}\n\nconst defaultPageSizes = [10, 20, 50]\n\n/** A client-side data table: search, faceted filters, sorting, column visibility, selection and pagination. */\nexport function DataTable<TData extends RowData>({\n  columns,\n  data,\n  getRowId,\n  searchPlaceholder,\n  filters = [],\n  pageSizes = defaultPageSizes,\n  toolbar,\n  exportable = false,\n  emptyMessage,\n  autoResetPage = true,\n  className,\n}: DataTableProps<TData>) {\n  const { t } = useLocale()\n  const searchLabel = searchPlaceholder ?? t(\"searchTable\")\n  const table = useTable({\n    features: dataTableFeatures,\n    columns,\n    data,\n    getRowId,\n    globalFilterFn: \"includesString\",\n    initialState: { pagination: { pageIndex: 0, pageSize: pageSizes[0] ?? 10 } },\n    autoResetPageIndex: autoResetPage,\n  })\n\n  // With autoResetPage off, the table resets the page itself where it still should: a new search or filter starts\n  // from the first page, and a page the rows no longer reach falls back to the last one.\n  const filterKey = autoResetPage ? \"\" : JSON.stringify([table.state.columnFilters, table.state.globalFilter ?? \"\"])\n  const lastFilterKey = React.useRef(filterKey)\n  const { pageIndex } = table.state.pagination\n  const pageCount = table.getPageCount()\n  React.useEffect(() => {\n    if (autoResetPage) return\n    if (filterKey !== lastFilterKey.current) {\n      lastFilterKey.current = filterKey\n      table.setPageIndex(0)\n    } else if (pageCount > 0 && pageIndex > pageCount - 1) table.setPageIndex(pageCount - 1)\n  }, [autoResetPage, filterKey, pageIndex, pageCount, table])\n\n  const selectable = columns.some((column) => column.id === \"select\")\n  const isFiltered = table.state.columnFilters.length > 0 || Boolean(table.state.globalFilter)\n\n  return (\n    <div className={cn(\"space-y-4\", className)}>\n      <div className=\"flex flex-wrap items-center gap-2\">\n        <Input\n          prefix={<Search aria-hidden />}\n          value={(table.state.globalFilter as string | undefined) ?? \"\"}\n          onChange={(event) => table.setGlobalFilter(event.target.value)}\n          placeholder={searchLabel}\n          aria-label={searchLabel}\n          className=\"h-8 max-sm:w-full sm:w-64\"\n        />\n        {/* Filters on the left, flowing like the rest of the row. */}\n        <div className=\"contents\">\n          {filters.map((filter) => (\n            <DataTableFacetedFilter key={filter.column} table={table} filter={filter} />\n          ))}\n          {isFiltered && (\n            <Button\n              variant=\"ghost\"\n              size=\"sm\"\n              className=\"h-8\"\n              onClick={() => {\n                table.resetColumnFilters()\n                table.setGlobalFilter(\"\")\n              }}\n            >\n              {t(\"reset\")} <X />\n            </Button>\n          )}\n        </div>\n        {/* Actions always at the right, on every screen (on a row of their own when the filters fill one): export and\n            columns, then your own (e.g. New) last. */}\n        <div className=\"ms-auto flex items-center gap-2\">\n          {exportable && <DataTableExportMenu table={table} options={exportable === true ? {} : exportable} />}\n          <DataTableViewOptions table={table} />\n          {toolbar?.(table)}\n        </div>\n      </div>\n\n      {/* A card of its own, so the table stands out on the page; the table inside scrolls sideways when it must. */}\n      <div className=\"overflow-hidden rounded-lg border bg-card text-card-foreground\">\n        <Table>\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id} className=\"hover:bg-transparent\">\n                {headerGroup.headers.map((header) => {\n                  const sorted = header.column.getIsSorted()\n                  return (\n                    <TableHead\n                      key={header.id}\n                      aria-sort={sorted === \"asc\" ? \"ascending\" : sorted === \"desc\" ? \"descending\" : undefined}\n                      className=\"first:ps-4\"\n                    >\n                      {header.isPlaceholder ? null : <table.FlexRender header={header} />}\n                    </TableHead>\n                  )\n                })}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody>\n            {table.getRowModel().rows.length ? (\n              table.getRowModel().rows.map((row) => (\n                <TableRow key={row.id} data-state={row.getIsSelected() ? \"selected\" : undefined}>\n                  {row.getVisibleCells().map((cell) => (\n                    <TableCell key={cell.id} className=\"first:ps-4\">\n                      <table.FlexRender cell={cell} />\n                    </TableCell>\n                  ))}\n                </TableRow>\n              ))\n            ) : (\n              <TableRow className=\"hover:bg-transparent\">\n                <TableCell colSpan={table.getVisibleLeafColumns().length} className=\"py-10 text-center whitespace-normal\">\n                  {/* An empty state: what happened and, after a search or filter, the way back. */}\n                  <div className=\"flex animate-in flex-col items-center gap-3 fade-in-0 blur-in-[3px] duration-(--duration-standard) ease-enter motion-reduce:animate-none\">\n                    <span aria-hidden className=\"flex size-10 items-center justify-center rounded-full bg-muted text-muted-foreground\">\n                      <SearchX className=\"size-5\" />\n                    </span>\n                    <p className=\"text-sm font-medium\">{emptyMessage ?? t(\"noRows\")}</p>\n                    {isFiltered && (\n                      <>\n                        <p className=\"-mt-2 text-sm text-muted-foreground\">{t(\"tryAnother\")}</p>\n                        <Button\n                          variant=\"outline\"\n                          size=\"sm\"\n                          onClick={() => {\n                            table.resetColumnFilters()\n                            table.setGlobalFilter(\"\")\n                          }}\n                        >\n                          {t(\"clearFilters\")}\n                        </Button>\n                      </>\n                    )}\n                  </div>\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n\n      <DataTablePagination table={table} pageSizes={pageSizes} selectable={selectable} />\n    </div>\n  )\n}\n"}],"cssVars":{"theme":{"ease-standard":"var(--ease-standard)","ease-enter":"var(--ease-enter)","ease-exit":"var(--ease-exit)","ease-spring":"var(--ease-spring)"},"light":{"duration-fast":"160ms","duration-standard":"240ms","duration-considered":"480ms","ease-standard":"cubic-bezier(0.2, 0, 0, 1)","ease-enter":"cubic-bezier(0, 0, 0, 1)","ease-exit":"cubic-bezier(0.3, 0, 1, 1)","ease-spring":"linear(0, 0.071 4%, 0.234 8%, 0.433 12%, 0.626 17%, 0.791 21%, 0.917 25%, 1.004 29%, 1.055 33%, 1.079 38%, 1.083 42%, 1.075 46%, 1.06 50%, 1.043 54%, 1.027 58%, 1.015 62%, 1.005 67%, 0.998 71%, 0.995 75%, 0.993 79%, 0.993 83%, 0.994 88%, 0.995 92%, 0.997 96%, 1)"},"dark":{}}}