@ngrok/mantle 0.76.1 → 0.76.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.json +1 -1
- package/dist/alert-dialog.d.ts +4 -4
- package/dist/alert.d.ts +1 -1
- package/dist/{button-BYZOBUgj.d.ts → button-BXZ_JTu_.d.ts} +4 -4
- package/dist/button.d.ts +2 -2
- package/dist/checkbox.d.ts +30 -1
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/command.d.ts +7 -7
- package/dist/data-table.d.ts +123 -43
- package/dist/data-table.js +1 -1
- package/dist/data-table.js.map +1 -1
- package/dist/dialog.d.ts +1 -1
- package/dist/empty.js +1 -1
- package/dist/empty.js.map +1 -1
- package/dist/field.d.ts +2 -2
- package/dist/{icon-button-Ct3A7aoj.d.ts → icon-button-ntupABbM.d.ts} +2 -2
- package/dist/{index-BL5WVva_.d.ts → index-CJbKEKr2.d.ts} +4 -2
- package/dist/input.d.ts +3 -3
- package/dist/llms.txt +1 -1
- package/dist/mantle.css +178 -8
- package/dist/sandboxed-on-click.d.ts +1 -0
- package/dist/sandboxed-on-click.js.map +1 -1
- package/dist/sheet.d.ts +2 -2
- package/dist/sheet.js.map +1 -1
- package/dist/slot.d.ts +1 -1
- package/dist/split-button.d.ts +2 -2
- package/dist/table-DWy_oNta.js +2 -0
- package/dist/table-DWy_oNta.js.map +1 -0
- package/dist/table.js +1 -1
- package/dist/tabs.js +1 -1
- package/dist/tabs.js.map +1 -1
- package/dist/theme.d.ts +3 -3
- package/dist/well.js +1 -1
- package/dist/well.js.map +1 -1
- package/package.json +19 -19
- package/dist/table-eyoUW2Uv.js +0 -2
- package/dist/table-eyoUW2Uv.js.map +0 -1
package/dist/data-table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table.js","names":["Table","Row"],"sources":["../src/components/data-table/helpers.ts","../src/components/data-table/data-table.tsx"],"sourcesContent":["import type { SortingMode } from \"../../utils/sorting/direction.js\";\nimport type { SortDirection } from \"./types.js\";\n\nconst alphanumericSortingOrder = [\"unsorted\", \"asc\", \"desc\"] as const satisfies SortDirection[];\n\nconst timeSortingOrder = [\"unsorted\", \"desc\", \"asc\"] as const satisfies SortDirection[];\n\n/**\n * Get the next sort direction based on the current sort direction and sorting mode.\n */\nfunction getNextSortDirection(currentSortDirection: SortDirection, sortingMode: SortingMode) {\n\tconst sortOrder = sortingMode === \"alphanumeric\" ? alphanumericSortingOrder : timeSortingOrder;\n\n\treturn getNextInCircularList(sortOrder, currentSortDirection) ?? \"unsorted\";\n}\n\n/**\n * Get the next item in a circular list.\n * If the current item is not found in the list (or it's empty), return the fallback value.\n */\nfunction getNextInCircularList<T>(list: T[], currentItem: T, fallback?: T | undefined) {\n\tif (list.length === 0) {\n\t\treturn fallback;\n\t}\n\n\tconst currentItemIndex = list.findIndex((item) => item === currentItem);\n\tif (currentItemIndex === -1) {\n\t\treturn fallback;\n\t}\n\n\tconst nextIndex = (currentItemIndex + 1) % list.length;\n\treturn list.at(nextIndex) ?? fallback;\n}\n\nexport {\n\t//,\n\tgetNextSortDirection,\n\tgetNextInCircularList,\n};\n","import {\n\ttype Column,\n\ttype HeaderContext,\n\ttype Table as TableInstance,\n\ttype Row as TableRow,\n\tflexRender,\n} from \"@tanstack/react-table\";\nimport {\n\ttype ComponentProps,\n\ttype ComponentPropsWithoutRef,\n\ttype ComponentRef,\n\tFragment,\n\ttype ReactNode,\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n\tuseMemo,\n} from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { $timeSortingDirection, type SortingMode } from \"../../utils/sorting/direction.js\";\nimport { Button } from \"../button/button.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { SortIcon } from \"../icons/sort.js\";\nimport { Table } from \"../table/table.js\";\nimport { getNextSortDirection } from \"./helpers.js\";\nimport type { SortDirection } from \"./types.js\";\n\ntype DataTableContextShape<TData = unknown> = {\n\ttable: TableInstance<TData>;\n};\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- React context cannot preserve this generic across provider boundaries.\nconst DataTableContext = createContext<DataTableContextShape<any> | null>(null);\n\n/**\n * @private\n */\nfunction useDataTableContext<TData>() {\n\tconst context = useContext(DataTableContext);\n\n\tinvariant(context, \"useDataTableContext should only be used within a DataTable child component\");\n\n\treturn context as DataTableContextShape<TData>;\n}\n\ntype DataTableProps<TData> = ComponentProps<typeof Table.Root> & {\n\ttable: TableInstance<TData>;\n};\n\n/**\n * The root container for a data table. Wraps all other `DataTable`\n * sub-components and provides the table context to its descendants.\n *\n * REQUIRED: Construct a TanStack Table instance via `useReactTable` (from\n * `@tanstack/react-table`, also re-exported from `@ngrok/mantle/data-table`)\n * and pass it through the `table` prop. The instance owns columns, data, and\n * any sorting / filtering / pagination state — the wrapper components read\n * from it.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableroot\n *\n * @example\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n *\n * type Row = { id: string; name: string };\n * const columnHelper = createColumnHelper<Row>();\n * const columns = [\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: () => <DataTable.Header>Name</DataTable.Header>,\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * ];\n *\n * function MyTable({ data }: { data: Row[] }) {\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n * ```\n */\nfunction Root<TData>({ children, table, ...props }: DataTableProps<TData>) {\n\tconst context: DataTableContextShape<TData> = useMemo(() => ({ table }), [table]);\n\n\treturn (\n\t\t<DataTableContext.Provider value={context}>\n\t\t\t<Table.Root data-slot=\"data-table\" {...props}>\n\t\t\t\t<Table.Element>{children}</Table.Element>\n\t\t\t</Table.Root>\n\t\t</DataTableContext.Provider>\n\t);\n}\n\ntype DataTableHeaderSortButtonProps<TData, TValue> = Omit<ComponentProps<typeof Button>, \"icon\"> &\n\tPick<HeaderContext<TData, TValue>, \"column\"> &\n\t(\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * Disable sorting for this column.\n\t\t\t\t * It will prevent the sorting direction from being toggled and any icon\n\t\t\t\t * from being shown.\n\t\t\t\t */\n\t\t\t\tdisableSorting: true;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: undefined;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode?: undefined;\n\t\t }\n\t\t| {\n\t\t\t\tdisableSorting?: false;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: (sortDirection: SortDirection) => ReactNode;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode: SortingMode;\n\t\t }\n\t);\n\n/**\n * A sortable button toggle for a column header in a data table. Renders a sort\n * icon that reflects the current direction, handles ARIA announcements, and\n * cycles through sort states on click.\n *\n * Each click cycles through:\n * - For `\"alphanumeric\"` sorting: `unsorted → ascending → descending → unsorted`\n * - For `\"time\"` sorting: `unsorted → newest-first → oldest-first → unsorted`\n *\n * For right-aligned numeric columns, pass `className=\"justify-end\"` and\n * `iconPlacement=\"start\"` so the sort icon stays paired with the label.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Email\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction HeaderSortButton<TData, TValue>({\n\tchildren,\n\tclassName,\n\tcolumn,\n\tdisableSorting = false,\n\ticonPlacement = \"end\",\n\tsortingMode,\n\tsortIcon: propSortIcon,\n\tonClick,\n\t...props\n}: DataTableHeaderSortButtonProps<TData, TValue>) {\n\tconst rawSortDirection = column.getIsSorted();\n\tconst canSort = !disableSorting && column.getCanSort();\n\n\tconst sortDirection: SortDirection =\n\t\tcanSort && typeof rawSortDirection === \"string\" ? rawSortDirection : \"unsorted\";\n\n\tconst sortIcon = propSortIcon?.(sortDirection) ?? (\n\t\t<DefaultSortIcon mode={sortingMode} direction={sortDirection} />\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tappearance=\"ghost\"\n\t\t\tdata-slot=\"data-table-header-sort-button\"\n\t\t\tclassName={cx(\n\t\t\t\t\"flex justify-start w-full h-full rounded-none not-disabled:active:scale-none text-muted\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tdata-sort-direction={sortDirection}\n\t\t\tdata-table-header-action\n\t\t\ticon={sortIcon}\n\t\t\ticonPlacement={iconPlacement}\n\t\t\tonClick={(event) => {\n\t\t\t\tonClick?.(event);\n\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!canSort || disableSorting || typeof sortingMode === \"undefined\") {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttoggleNextSortingDirection(column, sortingMode);\n\t\t\t}}\n\t\t\tpriority=\"neutral\"\n\t\t\ttype=\"button\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{canSort && sortDirection !== \"unsorted\" && (\n\t\t\t\t<span className=\"sr-only\">\n\t\t\t\t\tColumn sorted in{\" \"}\n\t\t\t\t\t{sortingMode === \"alphanumeric\"\n\t\t\t\t\t\t? sortDirection === \"asc\"\n\t\t\t\t\t\t\t? \"ascending\"\n\t\t\t\t\t\t\t: \"descending\"\n\t\t\t\t\t\t: $timeSortingDirection(sortDirection)}{\" \"}\n\t\t\t\t\torder\n\t\t\t\t</span>\n\t\t\t)}\n\t\t\t{children}\n\t\t</Button>\n\t);\n}\n\ntype DataTableHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A `<th>` optimized for header actions. Wrap each column's header content in\n * this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n * Non-sortable columns can render plain text.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheader\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction Header({ children, className, ...props }: DataTableHeaderProps) {\n\treturn (\n\t\t<Table.Header\n\t\t\tdata-slot=\"data-table-header\"\n\t\t\tclassName={cx(\"has-data-table-header-action:px-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\n/**\n * The `<tbody>` container for rows of data. Typically wraps a map of\n * `DataTable.Row`, with a `DataTable.EmptyRow` fallback when there is no data.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablebody\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nconst Body = forwardRef<\n\tComponentRef<typeof Table.Body>,\n\tComponentPropsWithoutRef<typeof Table.Body>\n>((props, ref) => <Table.Body ref={ref} data-slot=\"data-table-body\" {...props} />);\nBody.displayName = \"DataTableBody\";\n\ntype DataTableHeadProps = Omit<ComponentProps<typeof Table.Head>, \"children\">;\n\n/**\n * The `<thead>` container that renders column headers automatically from\n * `table.getHeaderGroups()`. Does not accept children — headers come from each\n * column's `header` definition on the TanStack Table column config.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablehead\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nfunction Head<TData>(props: DataTableHeadProps) {\n\tconst { table } = useDataTableContext<TData>();\n\n\treturn (\n\t\t<Table.Head data-slot=\"data-table-head\" {...props}>\n\t\t\t{table.getHeaderGroups().map((headerGroup) => (\n\t\t\t\t<Table.Row key={headerGroup.id}>\n\t\t\t\t\t{headerGroup.headers.map((header) => (\n\t\t\t\t\t\t<Fragment key={header.id}>\n\t\t\t\t\t\t\t{header.isPlaceholder ? (\n\t\t\t\t\t\t\t\t<Table.Header key={header.id} />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\tflexRender(header.column.columnDef.header, header.getContext())\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Fragment>\n\t\t\t\t\t))}\n\t\t\t\t</Table.Row>\n\t\t\t))}\n\t\t</Table.Head>\n\t);\n}\n\ntype DataTableRowProps<TData> = Omit<ComponentProps<typeof Table.Row>, \"children\"> & {\n\trow: TableRow<TData>;\n};\n\n/**\n * A single data table body row rendered from a TanStack Table row instance.\n * Does not accept children — cells come from each column's `cell` definition.\n *\n * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n * Pass a different `cursor-*` class via `className` (e.g. `cursor-default`,\n * `cursor-wait`) to override. For keyboard and screen-reader access, also\n * render a `<Link>` inside the primary cell — a `<tr>` is not focusable.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablerow\n *\n * @example\n * ```tsx\n * const navigate = useNavigate();\n *\n * {rows.map((row) => (\n * <DataTable.Row\n * key={row.id}\n * row={row}\n * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n * />\n * ))}\n * ```\n */\nfunction Row<TData>({ className, row, ...props }: DataTableRowProps<TData>) {\n\treturn (\n\t\t<Table.Row\n\t\t\tdata-slot=\"data-table-row\"\n\t\t\tclassName={cx(props.onClick && \"cursor-pointer\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{row.getVisibleCells().map((cell) => (\n\t\t\t\t<Fragment key={cell.id}>\n\t\t\t\t\t{flexRender(cell.column.columnDef.cell, cell.getContext())}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</Table.Row>\n\t);\n}\n\ntype DataTableEmptyRowProps = ComponentProps<typeof Table.Row>;\n\n/**\n * An empty-state row that spans every column. Render this as the `else` branch\n * when `rows.length === 0` to keep the table's frame intact instead of\n * collapsing to an empty `<tbody>`. The cell `colSpan` is computed from the\n * TanStack Table instance via context, so no manual column count is needed.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableemptyrow\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nfunction EmptyRow<TData>({ children, ...props }: DataTableEmptyRowProps) {\n\tconst { table } = useDataTableContext<TData>();\n\tconst numberOfColumns = table.getAllColumns().length;\n\n\treturn (\n\t\t<Table.Row data-slot=\"data-table-empty-row\" {...props}>\n\t\t\t<Table.Cell colSpan={numberOfColumns}>{children}</Table.Cell>\n\t\t</Table.Row>\n\t);\n}\n\n/**\n * Internal: renders the visual indicator on the left edge of the sticky action\n * column — a 1px divider plus a soft shadow gradient that reads as content\n * sliding under the pinned column. Positioned as a 6px strip sitting\n * immediately to the left of its sticky parent cell; `-inset-y-px` lets\n * adjacent rows' strips overlap at row dividers so the effect reads as one\n * continuous column instead of per-row blobs.\n *\n * Rendered as a child `<span>` because box-shadow on `<td>`/`<th>` is\n * unreliable across table layout modes.\n */\nfunction StickyColIndicator() {\n\treturn (\n\t\t<span\n\t\t\taria-hidden\n\t\t\tclassName={cx(\n\t\t\t\t\"pointer-events-none absolute -inset-y-px -left-1.5 w-1.5\",\n\t\t\t\t\"opacity-0 transition-opacity group-data-sticky-active/table:opacity-100\",\n\t\t\t\t// 1px divider painted at the strip's right edge (= the pinned\n\t\t\t\t// cell's left edge).\n\t\t\t\t\"shadow-[1px_0_0_0_var(--border-color-card-muted)]\",\n\t\t\t\t// Soft shadow gradient fading leftward. Uses mantle's shadow\n\t\t\t\t// tokens so the alpha adapts to light/dark themes.\n\t\t\t\t\"bg-linear-to-l to-transparent\",\n\t\t\t\t\"from-[color-mix(in_oklab,var(--shadow-color)_var(--shadow-second-opacity),transparent)]\",\n\t\t\t)}\n\t\t/>\n\t);\n}\n\ntype DataTableActionCellProps = ComponentProps<typeof Table.Cell>;\n\n/**\n * A sticky-right `<td>` for per-row action buttons (typically an `IconButton`\n * that opens a `DropdownMenu`). Pair with `DataTable.ActionHeader`.\n *\n * If the row has `onClick`, pass `onClick={(event) => event.stopPropagation()}`\n * on this cell so clicks on action controls don't bubble and fire the row\n * handler.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => (\n * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n * <DropdownMenu.Root>...</DropdownMenu.Root>\n * </DataTable.ActionCell>\n * ),\n * });\n * ```\n */\nfunction ActionCell({ children, className, ...props }: DataTableActionCellProps) {\n\treturn (\n\t\t<Table.Cell\n\t\t\t// Marks this cell as a sticky right-edge column so Table.Root can suppress\n\t\t\t// its container-level right-side scroll fade (keeping this cell opaque).\n\t\t\tdata-mantle-table-sticky-right\n\t\t\tdata-slot=\"data-table-action-cell\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky cell opaque with the row's current bg\n\t\t\t\t// (including hover state) so scrolling cells don't show through.\n\t\t\t\t// Avoid `display: flex` here — it overrides `display: table-cell`,\n\t\t\t\t// preventing the cell from stretching to the full row height in\n\t\t\t\t// `border-separate` mode.\n\t\t\t\t\"sticky z-10 right-0 text-end align-middle bg-inherit p-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<StickyColIndicator />\n\t\t\t{children}\n\t\t</Table.Cell>\n\t);\n}\n\ntype DataTableActionHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A sticky header cell that pairs with `DataTable.ActionCell`. Use this as the\n * header for the action column so the pinned column visually aligns across the\n * header and every body row when the table scrolls horizontally.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => <DataTable.ActionCell>{...}</DataTable.ActionCell>,\n * })\n * ```\n */\nfunction ActionHeader({ children, className, ...props }: DataTableActionHeaderProps) {\n\tconst { table } = useDataTableContext();\n\tconst hasRows = table.getRowModel().rows.length > 0;\n\n\treturn (\n\t\t<Table.Header\n\t\t\t// Only mark as sticky-right when body rows exist so the empty state\n\t\t\t// doesn't suppress the container's right-side scroll fade.\n\t\t\t{...(hasRows ? { \"data-mantle-table-sticky-right\": true } : {})}\n\t\t\tdata-slot=\"data-table-action-header\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky header opaque with the thead's current bg.\n\t\t\t\thasRows && \"sticky z-10 right-0 bg-inherit\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{hasRows && <StickyColIndicator />}\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\n// Set display names to preserve original component names for debugging\nRoot.displayName = \"DataTable\";\nActionCell.displayName = \"DataTableActionCell\";\nActionHeader.displayName = \"DataTableActionHeader\";\nBody.displayName = \"DataTableBody\";\nEmptyRow.displayName = \"DataTableEmptyRow\";\nHead.displayName = \"DataTableHead\";\nHeader.displayName = \"DataTableHeader\";\nHeaderSortButton.displayName = \"DataTableHeaderSortButton\";\nRow.displayName = \"DataTableRow\";\n\n/**\n * Use `DataTable` for INTERACTIVE tabular data — sorting, filtering, pagination,\n * row selection, and server-side or client-side data. Built on TanStack Table;\n * the consumer MUST construct a `useReactTable` instance from\n * `@tanstack/react-table` and pass it to `DataTable.Root` via the `table` prop.\n * Every TanStack utility (`createColumnHelper`, `getCoreRowModel`,\n * `getSortedRowModel`, `getPaginationRowModel`, `getFilteredRowModel`,\n * `useReactTable`, …) is re-exported from `@ngrok/mantle/data-table` so a single\n * import covers both the wrapper components and the TanStack helpers.\n *\n * For STATIC, layout-driven tables (read-only data dumps, simple key/value\n * displays, plain markup tables with no interactivity), use `Table` instead.\n *\n * @see https://mantle.ngrok.com/components/data-table\n *\n * @example\n * Composition:\n * ```\n * DataTable.Root\n * ├── DataTable.Head\n * │ └── DataTable.Row\n * │ ├── DataTable.Header\n * │ │ └── DataTable.HeaderSortButton\n * │ └── DataTable.ActionHeader\n * └── DataTable.Body\n * ├── DataTable.Row\n * │ ├── DataTable.Cell\n * │ └── DataTable.ActionCell\n * └── DataTable.EmptyRow\n * ```\n *\n * @example\n * Minimal — read-only table with a single sortable column:\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n *\n * type Row = { id: string; name: string };\n *\n * const columnHelper = createColumnHelper<Row>();\n * const columns = [\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * ];\n *\n * function MyTable({ data }: { data: Row[] }) {\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n * ```\n *\n * @example\n * Sortable + filterable + paginated — with a global text filter and page controls:\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * getFilteredRowModel,\n * getPaginationRowModel,\n * getSortedRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n * import { Button } from \"@ngrok/mantle/button\";\n * import { Input } from \"@ngrok/mantle/input\";\n * import { useState } from \"react\";\n *\n * type Payment = { id: string; amount: number; status: \"pending\" | \"succeeded\" | \"failed\"; email: string };\n *\n * const columnHelper = createColumnHelper<Payment>();\n * const columns = [\n * columnHelper.accessor(\"status\", {\n * id: \"status\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Status\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Email\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * columnHelper.accessor(\"amount\", {\n * id: \"amount\",\n * header: (props) => (\n * <DataTable.Header className=\"text-right\">\n * <DataTable.HeaderSortButton\n * column={props.column}\n * sortingMode=\"alphanumeric\"\n * className=\"justify-end\"\n * iconPlacement=\"start\"\n * >\n * Amount\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => (\n * <DataTable.Cell className=\"text-right tabular-nums\">\n * {new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" }).format(props.getValue())}\n * </DataTable.Cell>\n * ),\n * }),\n * ];\n *\n * function PaymentsTable({ data }: { data: Payment[] }) {\n * const [globalFilter, setGlobalFilter] = useState(\"\");\n *\n * const table = useReactTable({\n * data,\n * columns,\n * state: { globalFilter },\n * onGlobalFilterChange: setGlobalFilter,\n * getCoreRowModel: getCoreRowModel(),\n * getSortedRowModel: getSortedRowModel(),\n * getFilteredRowModel: getFilteredRowModel(),\n * getPaginationRowModel: getPaginationRowModel(),\n * });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <div className=\"space-y-4\">\n * <Input\n * placeholder=\"Filter payments…\"\n * value={globalFilter}\n * onChange={(event) => setGlobalFilter(event.target.value)}\n * />\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No payments match.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * <div className=\"flex items-center justify-between gap-2\">\n * <span className=\"text-sm text-muted\">\n * Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}\n * </span>\n * <div className=\"flex gap-2\">\n * <Button\n * type=\"button\"\n * priority=\"neutral\"\n * onClick={() => table.previousPage()}\n * disabled={!table.getCanPreviousPage()}\n * >\n * Previous\n * </Button>\n * <Button\n * type=\"button\"\n * priority=\"neutral\"\n * onClick={() => table.nextPage()}\n * disabled={!table.getCanNextPage()}\n * >\n * Next\n * </Button>\n * </div>\n * </div>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * Row action column — a sticky right-edge cell with a dropdown menu of actions.\n * If the row also has `onClick`, stop propagation on the action cell so clicks\n * don't bubble up and fire the row handler:\n * ```tsx\n * import { DataTable, createColumnHelper } from \"@ngrok/mantle/data-table\";\n * import { DropdownMenu } from \"@ngrok/mantle/dropdown-menu\";\n * import { IconButton } from \"@ngrok/mantle/icon-button\";\n * import { DotsThreeVerticalIcon } from \"@phosphor-icons/react/DotsThreeVertical\";\n *\n * const columnHelper = createColumnHelper<Payment>();\n *\n * const columns = [\n * // …other columns…\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: (props) => (\n * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n * <DropdownMenu.Root>\n * <DropdownMenu.Trigger asChild>\n * <IconButton type=\"button\" label=\"Actions\" icon={<DotsThreeVerticalIcon />} />\n * </DropdownMenu.Trigger>\n * <DropdownMenu.Content align=\"end\">\n * <DropdownMenu.Item onSelect={() => copy(props.row.original.id)}>\n * Copy ID\n * </DropdownMenu.Item>\n * <DropdownMenu.Item onSelect={() => refund(props.row.original.id)}>\n * Refund\n * </DropdownMenu.Item>\n * </DropdownMenu.Content>\n * </DropdownMenu.Root>\n * </DataTable.ActionCell>\n * ),\n * }),\n * ];\n * ```\n *\n * @example\n * Clickable row navigating to a detail page — also render a `<Link>` inside the\n * primary cell so the row is reachable by keyboard and screen readers (a `<tr>`\n * is not focusable):\n * ```tsx\n * import { DataTable } from \"@ngrok/mantle/data-table\";\n * import { Link, href, useNavigate } from \"react-router\";\n *\n * function PaymentsTable({ data }: { data: Payment[] }) {\n * const navigate = useNavigate();\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.map((row) => (\n * <DataTable.Row\n * key={row.id}\n * row={row}\n * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n * />\n * ))}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n *\n * // The primary column's cell renders a <Link> for keyboard / a11y reachability.\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => <DataTable.Header>Email</DataTable.Header>,\n * cell: (props) => (\n * <DataTable.Cell>\n * <Link to={href(\"/payments/:id\", { id: props.row.original.id })}>\n * {props.getValue()}\n * </Link>\n * </DataTable.Cell>\n * ),\n * });\n * ```\n */\nconst DataTable = {\n\t/**\n\t * The root container of the data table component. REQUIRED: pass a\n\t * `useReactTable` instance (from `@tanstack/react-table`, also re-exported\n\t * from `@ngrok/mantle/data-table`) via the `table` prop — every other\n\t * `DataTable.*` part reads from it through context.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableroot\n\t *\n\t * @example\n\t * ```tsx\n\t * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n\t * const rows = table.getRowModel().rows;\n\t *\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * A sticky action cell positioned at the end of each row, typically holding\n\t * an `IconButton` that opens a `DropdownMenu`. Pair with `DataTable.ActionHeader`.\n\t *\n\t * If the row has `onClick`, stop propagation on this cell so clicks on action\n\t * controls don't bubble up and fire the row handler.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionCell,\n\t/**\n\t * A sticky header cell that pairs with `DataTable.ActionCell`, keeping the\n\t * action column aligned across the header and body when scrolling horizontally.\n\t * Use as the `header` for a `columnHelper.display` action column.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionHeader,\n\t/**\n\t * A `<td>` for rendering an individual data cell. Re-exported from\n\t * `Table.Cell`. Every cell rendered by a column's `cell` function should\n\t * be wrapped in this — a raw `<td>` skips mantle typography and padding.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablecell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => <DataTable.Header>Name</DataTable.Header>,\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tCell: Table.Cell,\n\t/**\n\t * The `<tbody>` container for rows of data. Typically wraps a map of\n\t * `DataTable.Row`, with a `DataTable.EmptyRow` fallback when there is\n\t * no data.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablebody\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * ```\n\t */\n\tBody,\n\t/**\n\t * An empty-state row that spans every column. Render this as the `else`\n\t * branch when `rows.length === 0` to keep the table's frame intact instead\n\t * of collapsing to an empty `<tbody>`.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableemptyrow\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * ```\n\t */\n\tEmptyRow,\n\t/**\n\t * The `<thead>` container that renders column headers automatically from\n\t * `table.getHeaderGroups()`. Does not accept children — headers come from\n\t * each column's `header` definition.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablehead\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>...</DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tHead,\n\t/**\n\t * A `<th>` optimized for header actions. Wrap each column's header content\n\t * in this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Name\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeader,\n\t/**\n\t * A sortable button toggle for a column header. Clicks cycle through\n\t * sort directions: for `\"alphanumeric\"`, `unsorted → asc → desc → unsorted`;\n\t * for `\"time\"`, `unsorted → desc (newest-first) → asc → unsorted`.\n\t *\n\t * Pass `className=\"justify-end\"` and `iconPlacement=\"start\"` for\n\t * right-aligned numeric columns so the sort icon stays paired with the label.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"email\", {\n\t * id: \"email\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Email\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeaderSortButton,\n\t/**\n\t * A single data table body row rendered from a TanStack Table row instance.\n\t * Does not accept children — cells come from each column's `cell` definition.\n\t *\n\t * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n\t * Pass a different `cursor-*` class via `className` to override. For keyboard\n\t * and screen-reader access, also render a `<Link>` inside the primary cell.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablerow\n\t *\n\t * @example\n\t * ```tsx\n\t * const navigate = useNavigate();\n\t *\n\t * {rows.map((row) => (\n\t * <DataTable.Row\n\t * key={row.id}\n\t * row={row}\n\t * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n\t * />\n\t * ))}\n\t * ```\n\t */\n\tRow,\n} as const;\n\nexport {\n\t//,\n\tDataTable,\n};\n\ntype DefaultSortIconProps = SvgAttributes & {\n\tdirection: SortDirection | undefined;\n\tmode: SortingMode | undefined;\n};\n\nfunction DefaultSortIcon({ direction, mode, ...props }: DefaultSortIconProps) {\n\tif (direction === \"unsorted\" || !mode || !direction) {\n\t\treturn <svg aria-hidden {...props} />;\n\t}\n\n\treturn <SortIcon mode={mode} direction={direction} {...props} />;\n}\n\n/**\n * Toggle the sorting direction of a column.\n * This ordering is typically toggled by clicking the column header.\n *\n * @example\n * ```md\n * Each click cycles through...\n *\n * For alphanumeric sorting:\n * unsorted ➡️ ascending ➡️ descending ➡️ unsorted ➡️ ...\n *\n * For time sorting:\n * unsorted ➡️ newest-to-oldest ➡️ oldest-to-newest ➡️ unsorted ➡️ ...\n *\n * this is equivalent to the inverse of alphanumeric sorting, or\n * unsorted ➡️ descending ➡️ ascending ➡️ unsorted ➡️ ...\n * ```\n */\nfunction toggleNextSortingDirection<TData, TValue>(\n\tcolumn: Column<TData, TValue>,\n\tsortingMode: SortingMode,\n) {\n\tif (!column.getCanSort()) {\n\t\treturn;\n\t}\n\n\tconst sortDirection = column.getIsSorted();\n\tconst currentSortDirection: SortDirection =\n\t\ttypeof sortDirection === \"string\" ? sortDirection : \"unsorted\";\n\n\tconst nextSortDirection = getNextSortDirection(currentSortDirection, sortingMode);\n\n\tswitch (nextSortDirection) {\n\t\tcase \"unsorted\":\n\t\t\tcolumn.clearSorting();\n\t\t\treturn;\n\t\tcase \"asc\":\n\t\t\tcolumn.toggleSorting(false);\n\t\t\treturn;\n\t\tcase \"desc\":\n\t\t\tcolumn.toggleSorting(true);\n\t\t\treturn;\n\t\tdefault:\n\t\t\treturn;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-table.js","names":["Table","Row"],"sources":["../src/components/data-table/helpers.ts","../src/components/data-table/data-table.tsx"],"sourcesContent":["import type { SortingMode } from \"../../utils/sorting/direction.js\";\nimport type { SortDirection } from \"./types.js\";\n\nconst alphanumericSortingOrder = [\"unsorted\", \"asc\", \"desc\"] as const satisfies SortDirection[];\n\nconst timeSortingOrder = [\"unsorted\", \"desc\", \"asc\"] as const satisfies SortDirection[];\n\n/**\n * Get the next sort direction based on the current sort direction and sorting mode.\n */\nfunction getNextSortDirection(currentSortDirection: SortDirection, sortingMode: SortingMode) {\n\tconst sortOrder = sortingMode === \"alphanumeric\" ? alphanumericSortingOrder : timeSortingOrder;\n\n\treturn getNextInCircularList(sortOrder, currentSortDirection) ?? \"unsorted\";\n}\n\n/**\n * Get the next item in a circular list.\n * If the current item is not found in the list (or it's empty), return the fallback value.\n */\nfunction getNextInCircularList<T>(list: T[], currentItem: T, fallback?: T | undefined) {\n\tif (list.length === 0) {\n\t\treturn fallback;\n\t}\n\n\tconst currentItemIndex = list.findIndex((item) => item === currentItem);\n\tif (currentItemIndex === -1) {\n\t\treturn fallback;\n\t}\n\n\tconst nextIndex = (currentItemIndex + 1) % list.length;\n\treturn list.at(nextIndex) ?? fallback;\n}\n\nexport {\n\t//,\n\tgetNextSortDirection,\n\tgetNextInCircularList,\n};\n","import {\n\ttype Column,\n\ttype HeaderContext,\n\ttype Table as TableInstance,\n\ttype Row as TableRow,\n\tflexRender,\n} from \"@tanstack/react-table\";\nimport {\n\ttype ComponentProps,\n\ttype ComponentPropsWithoutRef,\n\ttype ComponentRef,\n\tFragment,\n\ttype ReactNode,\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n\tuseMemo,\n} from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { $timeSortingDirection, type SortingMode } from \"../../utils/sorting/direction.js\";\nimport { Button } from \"../button/button.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { SortIcon } from \"../icons/sort.js\";\nimport { Table } from \"../table/table.js\";\nimport { getNextSortDirection } from \"./helpers.js\";\nimport type { SortDirection } from \"./types.js\";\n\ntype DataTableContextShape<TData = unknown> = {\n\ttable: TableInstance<TData>;\n};\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- React context cannot preserve this generic across provider boundaries.\nconst DataTableContext = createContext<DataTableContextShape<any> | null>(null);\n\n/**\n * @private\n */\nfunction useDataTableContext<TData>() {\n\tconst context = useContext(DataTableContext);\n\n\tinvariant(context, \"useDataTableContext should only be used within a DataTable child component\");\n\n\treturn context as DataTableContextShape<TData>;\n}\n\ntype DataTableProps<TData> = ComponentProps<typeof Table.Root> & {\n\ttable: TableInstance<TData>;\n};\n\n/**\n * The root container for a data table. Wraps all other `DataTable`\n * sub-components and provides the table context to its descendants.\n *\n * REQUIRED: Construct a TanStack Table instance via `useReactTable` (from\n * `@tanstack/react-table`, also re-exported from `@ngrok/mantle/data-table`)\n * and pass it through the `table` prop. The instance owns columns, data, and\n * any sorting / filtering / pagination state — the wrapper components read\n * from it.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableroot\n *\n * @example\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n *\n * type Row = { id: string; name: string };\n * const columnHelper = createColumnHelper<Row>();\n * const columns = [\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: () => <DataTable.Header>Name</DataTable.Header>,\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * ];\n *\n * function MyTable({ data }: { data: Row[] }) {\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n * ```\n */\nfunction Root<TData>({ children, table, ...props }: DataTableProps<TData>) {\n\tconst context: DataTableContextShape<TData> = useMemo(() => ({ table }), [table]);\n\n\treturn (\n\t\t<DataTableContext.Provider value={context}>\n\t\t\t<Table.Root data-slot=\"data-table\" {...props}>\n\t\t\t\t<Table.Element>{children}</Table.Element>\n\t\t\t</Table.Root>\n\t\t</DataTableContext.Provider>\n\t);\n}\n\ntype DataTableHeaderSortButtonProps<TData, TValue> = Omit<ComponentProps<typeof Button>, \"icon\"> &\n\tPick<HeaderContext<TData, TValue>, \"column\"> &\n\t(\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * Disable sorting for this column.\n\t\t\t\t * It will prevent the sorting direction from being toggled and any icon\n\t\t\t\t * from being shown.\n\t\t\t\t */\n\t\t\t\tdisableSorting: true;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: undefined;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode?: undefined;\n\t\t }\n\t\t| {\n\t\t\t\tdisableSorting?: false;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: (sortDirection: SortDirection) => ReactNode;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode: SortingMode;\n\t\t }\n\t);\n\n/**\n * A sortable button toggle for a column header in a data table. Renders a sort\n * icon that reflects the current direction, handles ARIA announcements, and\n * cycles through sort states on click.\n *\n * Each click cycles through:\n * - For `\"alphanumeric\"` sorting: `unsorted → ascending → descending → unsorted`\n * - For `\"time\"` sorting: `unsorted → newest-first → oldest-first → unsorted`\n *\n * For right-aligned numeric columns, pass `className=\"justify-end\"` and\n * `iconPlacement=\"start\"` so the sort icon stays paired with the label.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Email\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction HeaderSortButton<TData, TValue>({\n\tchildren,\n\tclassName,\n\tcolumn,\n\tdisableSorting = false,\n\ticonPlacement = \"end\",\n\tsortingMode,\n\tsortIcon: propSortIcon,\n\tonClick,\n\t...props\n}: DataTableHeaderSortButtonProps<TData, TValue>) {\n\tconst rawSortDirection = column.getIsSorted();\n\tconst canSort = !disableSorting && column.getCanSort();\n\n\tconst sortDirection: SortDirection =\n\t\tcanSort && typeof rawSortDirection === \"string\" ? rawSortDirection : \"unsorted\";\n\n\tconst sortIcon = propSortIcon?.(sortDirection) ?? (\n\t\t<DefaultSortIcon mode={sortingMode} direction={sortDirection} />\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tappearance=\"ghost\"\n\t\t\tdata-slot=\"data-table-header-sort-button\"\n\t\t\tclassName={cx(\n\t\t\t\t\"flex justify-start w-full h-full rounded-none not-disabled:active:scale-none text-muted\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tdata-sort-direction={sortDirection}\n\t\t\tdata-table-header-action\n\t\t\ticon={sortIcon}\n\t\t\ticonPlacement={iconPlacement}\n\t\t\tonClick={(event) => {\n\t\t\t\tonClick?.(event);\n\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!canSort || disableSorting || typeof sortingMode === \"undefined\") {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttoggleNextSortingDirection(column, sortingMode);\n\t\t\t}}\n\t\t\tpriority=\"neutral\"\n\t\t\ttype=\"button\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{canSort && sortDirection !== \"unsorted\" && (\n\t\t\t\t<span className=\"sr-only\">\n\t\t\t\t\tColumn sorted in{\" \"}\n\t\t\t\t\t{sortingMode === \"alphanumeric\"\n\t\t\t\t\t\t? sortDirection === \"asc\"\n\t\t\t\t\t\t\t? \"ascending\"\n\t\t\t\t\t\t\t: \"descending\"\n\t\t\t\t\t\t: $timeSortingDirection(sortDirection)}{\" \"}\n\t\t\t\t\torder\n\t\t\t\t</span>\n\t\t\t)}\n\t\t\t{children}\n\t\t</Button>\n\t);\n}\n\ntype DataTableHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A `<th>` optimized for header actions. Wrap each column's header content in\n * this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n * Non-sortable columns can render plain text.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheader\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction Header({ children, className, ...props }: DataTableHeaderProps) {\n\treturn (\n\t\t<Table.Header\n\t\t\tdata-slot=\"data-table-header\"\n\t\t\tclassName={cx(\"has-data-table-header-action:px-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\n/**\n * The `<tbody>` container for rows of data. Typically wraps a map of\n * `DataTable.Row`, with a `DataTable.EmptyRow` fallback when there is no data.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablebody\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nconst Body = forwardRef<\n\tComponentRef<typeof Table.Body>,\n\tComponentPropsWithoutRef<typeof Table.Body>\n>((props, ref) => <Table.Body ref={ref} data-slot=\"data-table-body\" {...props} />);\nBody.displayName = \"DataTableBody\";\n\ntype DataTableHeadProps = Omit<ComponentProps<typeof Table.Head>, \"children\">;\n\n/**\n * The `<thead>` container that renders column headers automatically from\n * `table.getHeaderGroups()`. Does not accept children — headers come from each\n * column's `header` definition on the TanStack Table column config.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablehead\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nfunction Head<TData>(props: DataTableHeadProps) {\n\tconst { table } = useDataTableContext<TData>();\n\n\treturn (\n\t\t<Table.Head data-slot=\"data-table-head\" {...props}>\n\t\t\t{table.getHeaderGroups().map((headerGroup) => (\n\t\t\t\t<Table.Row key={headerGroup.id}>\n\t\t\t\t\t{headerGroup.headers.map((header) => (\n\t\t\t\t\t\t<Fragment key={header.id}>\n\t\t\t\t\t\t\t{header.isPlaceholder ? (\n\t\t\t\t\t\t\t\t<Table.Header key={header.id} />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\tflexRender(header.column.columnDef.header, header.getContext())\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Fragment>\n\t\t\t\t\t))}\n\t\t\t\t</Table.Row>\n\t\t\t))}\n\t\t</Table.Head>\n\t);\n}\n\ntype DataTableRowProps<TData> = Omit<ComponentProps<typeof Table.Row>, \"children\"> & {\n\trow: TableRow<TData>;\n};\n\n/**\n * A single data table body row rendered from a TanStack Table row instance.\n * Does not accept children — cells come from each column's `cell` definition.\n *\n * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n * Pass a different `cursor-*` class via `className` (e.g. `cursor-default`,\n * `cursor-wait`) to override. For keyboard and screen-reader access, also\n * render a `<Link>` inside the primary cell — a `<tr>` is not focusable.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablerow\n *\n * @example\n * ```tsx\n * const navigate = useNavigate();\n *\n * {rows.map((row) => (\n * <DataTable.Row\n * key={row.id}\n * row={row}\n * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n * />\n * ))}\n * ```\n */\nfunction Row<TData>({ className, row, ...props }: DataTableRowProps<TData>) {\n\treturn (\n\t\t<Table.Row\n\t\t\tdata-slot=\"data-table-row\"\n\t\t\tclassName={cx(props.onClick && \"cursor-pointer\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{row.getVisibleCells().map((cell) => (\n\t\t\t\t<Fragment key={cell.id}>\n\t\t\t\t\t{flexRender(cell.column.columnDef.cell, cell.getContext())}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</Table.Row>\n\t);\n}\n\ntype DataTableEmptyRowProps = ComponentProps<typeof Table.Row>;\n\n/**\n * An empty-state row that spans every column. Render this as the `else` branch\n * when `rows.length === 0` to keep the table's frame intact instead of\n * collapsing to an empty `<tbody>`. The cell `colSpan` is computed from the\n * TanStack Table instance via context, so no manual column count is needed.\n *\n * Host an `Empty` for a real empty state, and branch on whether a filter is\n * active so the user sees the right message (and a way out when filtered):\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableemptyrow\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * ```tsx\n * import { DataTable } from \"@ngrok/mantle/data-table\";\n * import { Empty } from \"@ngrok/mantle/empty\";\n * import { Button } from \"@ngrok/mantle/button\";\n * import { MagnifyingGlassIcon } from \"@phosphor-icons/react/MagnifyingGlass\";\n * import { TrayIcon } from \"@phosphor-icons/react/Tray\";\n *\n * // `table` is your useReactTable instance; derive everything else from it.\n * const rows = table.getRowModel().rows;\n * const isFiltered = (table.getState().globalFilter ?? \"\") !== \"\";\n *\n * // EmptyRow already spans every column and Empty.Root centers itself — drop a\n * // single Empty.Root in as the child; don't hand-roll a <td> or any centering.\n * <DataTable.Body>\n * {rows.length > 0 ? (\n * rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * ) : isFiltered ? (\n * <DataTable.EmptyRow>\n * <Empty.Root>\n * <Empty.Icon svg={<MagnifyingGlassIcon />} />\n * <Empty.Title>No results match your filter</Empty.Title>\n * <Empty.Actions>\n * <Button\n * type=\"button\"\n * appearance=\"outlined\"\n * priority=\"neutral\"\n * onClick={() => table.setGlobalFilter(\"\")}\n * >\n * Clear filters\n * </Button>\n * </Empty.Actions>\n * </Empty.Root>\n * </DataTable.EmptyRow>\n * ) : (\n * <DataTable.EmptyRow>\n * <Empty.Root>\n * <Empty.Icon svg={<TrayIcon />} />\n * <Empty.Title>No endpoints yet</Empty.Title>\n * </Empty.Root>\n * </DataTable.EmptyRow>\n * )}\n * </DataTable.Body>\n * ```\n */\nfunction EmptyRow<TData>({ children, ...props }: DataTableEmptyRowProps) {\n\tconst { table } = useDataTableContext<TData>();\n\tconst numberOfColumns = table.getAllColumns().length;\n\n\treturn (\n\t\t<Table.Row data-slot=\"data-table-empty-row\" {...props}>\n\t\t\t<Table.Cell colSpan={numberOfColumns}>{children}</Table.Cell>\n\t\t</Table.Row>\n\t);\n}\n\n/**\n * Internal: renders the visual indicator on the left edge of the sticky action\n * column — a 1px divider plus a soft shadow gradient that reads as content\n * sliding under the pinned column. Positioned as a 6px strip sitting\n * immediately to the left of its sticky parent cell; `-inset-y-px` lets\n * adjacent rows' strips overlap at row dividers so the effect reads as one\n * continuous column instead of per-row blobs.\n *\n * Rendered as a child `<span>` because box-shadow on `<td>`/`<th>` is\n * unreliable across table layout modes.\n */\nfunction StickyColIndicator() {\n\treturn (\n\t\t<span\n\t\t\taria-hidden\n\t\t\tclassName={cx(\n\t\t\t\t\"pointer-events-none absolute -inset-y-px -left-1.5 w-1.5\",\n\t\t\t\t\"opacity-0 transition-opacity group-data-sticky-active/table:opacity-100\",\n\t\t\t\t// 1px divider painted at the strip's right edge (= the pinned\n\t\t\t\t// cell's left edge).\n\t\t\t\t\"shadow-[1px_0_0_0_var(--border-color-card-muted)]\",\n\t\t\t\t// Soft shadow gradient fading leftward. Uses mantle's shadow\n\t\t\t\t// tokens so the alpha adapts to light/dark themes.\n\t\t\t\t\"bg-linear-to-l to-transparent\",\n\t\t\t\t\"from-[color-mix(in_oklab,var(--shadow-color)_var(--shadow-second-opacity),transparent)]\",\n\t\t\t)}\n\t\t/>\n\t);\n}\n\ntype DataTableActionCellProps = ComponentProps<typeof Table.Cell>;\n\n/**\n * A sticky-right `<td>` for per-row action buttons (typically an `IconButton`\n * that opens a `DropdownMenu`). Pair with `DataTable.ActionHeader`.\n *\n * If the row has `onClick`, pass `onClick={(event) => event.stopPropagation()}`\n * on this cell so clicks on action controls don't bubble and fire the row\n * handler.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => (\n * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n * <DropdownMenu.Root>...</DropdownMenu.Root>\n * </DataTable.ActionCell>\n * ),\n * });\n * ```\n */\nfunction ActionCell({ children, className, ...props }: DataTableActionCellProps) {\n\treturn (\n\t\t<Table.Cell\n\t\t\t// Marks this cell as a sticky right-edge column so Table.Root can suppress\n\t\t\t// its container-level right-side scroll fade (keeping this cell opaque).\n\t\t\tdata-mantle-table-sticky-right\n\t\t\tdata-slot=\"data-table-action-cell\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky cell opaque with the row's current bg\n\t\t\t\t// (including hover state) so scrolling cells don't show through.\n\t\t\t\t// Avoid `display: flex` here — it overrides `display: table-cell`,\n\t\t\t\t// preventing the cell from stretching to the full row height in\n\t\t\t\t// `border-separate` mode.\n\t\t\t\t\"sticky z-10 right-0 text-end align-middle bg-inherit p-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<StickyColIndicator />\n\t\t\t{children}\n\t\t</Table.Cell>\n\t);\n}\n\ntype DataTableActionHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A sticky header cell that pairs with `DataTable.ActionCell`. Use this as the\n * header for the action column so the pinned column visually aligns across the\n * header and every body row when the table scrolls horizontally.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => <DataTable.ActionCell>{...}</DataTable.ActionCell>,\n * })\n * ```\n */\nfunction ActionHeader({ children, className, ...props }: DataTableActionHeaderProps) {\n\tconst { table } = useDataTableContext();\n\tconst hasRows = table.getRowModel().rows.length > 0;\n\n\treturn (\n\t\t<Table.Header\n\t\t\t// Only mark as sticky-right when body rows exist so the empty state\n\t\t\t// doesn't suppress the container's right-side scroll fade.\n\t\t\t{...(hasRows ? { \"data-mantle-table-sticky-right\": true } : {})}\n\t\t\tdata-slot=\"data-table-action-header\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky header opaque with the thead's current bg.\n\t\t\t\thasRows && \"sticky z-10 right-0 bg-inherit\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{hasRows && <StickyColIndicator />}\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\n// Set display names to preserve original component names for debugging\nRoot.displayName = \"DataTable\";\nActionCell.displayName = \"DataTableActionCell\";\nActionHeader.displayName = \"DataTableActionHeader\";\nBody.displayName = \"DataTableBody\";\nEmptyRow.displayName = \"DataTableEmptyRow\";\nHead.displayName = \"DataTableHead\";\nHeader.displayName = \"DataTableHeader\";\nHeaderSortButton.displayName = \"DataTableHeaderSortButton\";\nRow.displayName = \"DataTableRow\";\n\n/**\n * Use `DataTable` for INTERACTIVE tabular data — sorting, filtering, pagination,\n * row selection, and server-side or client-side data. Built on TanStack Table;\n * the consumer MUST construct a `useReactTable` instance from\n * `@tanstack/react-table` and pass it to `DataTable.Root` via the `table` prop.\n * Every TanStack utility (`createColumnHelper`, `getCoreRowModel`,\n * `getSortedRowModel`, `getPaginationRowModel`, `getFilteredRowModel`,\n * `useReactTable`, …) is re-exported from `@ngrok/mantle/data-table` so a single\n * import covers both the wrapper components and the TanStack helpers.\n *\n * For STATIC, layout-driven tables (read-only data dumps, simple key/value\n * displays, plain markup tables with no interactivity), use `Table` instead.\n *\n * @see https://mantle.ngrok.com/components/data-table\n *\n * @example\n * Composition:\n * ```\n * DataTable.Root\n * ├── DataTable.Head\n * │ └── DataTable.Row\n * │ ├── DataTable.Header\n * │ │ └── DataTable.HeaderSortButton\n * │ └── DataTable.ActionHeader\n * └── DataTable.Body\n * ├── DataTable.Row\n * │ ├── DataTable.Cell\n * │ └── DataTable.ActionCell\n * └── DataTable.EmptyRow\n * ```\n *\n * @example\n * Minimal — read-only table with a single sortable column:\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n *\n * type Row = { id: string; name: string };\n *\n * const columnHelper = createColumnHelper<Row>();\n * const columns = [\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * ];\n *\n * function MyTable({ data }: { data: Row[] }) {\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n * ```\n *\n * @example\n * Sortable, filterable, paginated, with both empty states — a global text\n * filter, the no-data vs. no-results-for-filter empty states (an `Empty`\n * dropped into `DataTable.EmptyRow`), and `CursorPagination` with a page-size\n * dropdown:\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * getFilteredRowModel,\n * getPaginationRowModel,\n * getSortedRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n * import { Button } from \"@ngrok/mantle/button\";\n * import { CursorPagination } from \"@ngrok/mantle/pagination\";\n * import { Empty } from \"@ngrok/mantle/empty\";\n * import { Input } from \"@ngrok/mantle/input\";\n * import { MagnifyingGlassIcon } from \"@phosphor-icons/react/MagnifyingGlass\";\n * import { TrayIcon } from \"@phosphor-icons/react/Tray\";\n * import { useState } from \"react\";\n *\n * type Payment = { id: string; amount: number; status: \"pending\" | \"succeeded\" | \"failed\"; email: string };\n *\n * // `defaultPageSize` seeds an UNCONTROLLED <Select>, so keep it stable — a\n * // module const (or the table's INITIAL page size), never the live page size.\n * const DEFAULT_PAGE_SIZE = 10;\n *\n * const columnHelper = createColumnHelper<Payment>();\n * const columns = [\n * columnHelper.accessor(\"status\", {\n * id: \"status\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Status\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Email\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * columnHelper.accessor(\"amount\", {\n * id: \"amount\",\n * header: (props) => (\n * <DataTable.Header className=\"text-right\">\n * <DataTable.HeaderSortButton\n * column={props.column}\n * sortingMode=\"alphanumeric\"\n * className=\"justify-end\"\n * iconPlacement=\"start\"\n * >\n * Amount\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => (\n * <DataTable.Cell className=\"text-right tabular-nums\">\n * {new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" }).format(props.getValue())}\n * </DataTable.Cell>\n * ),\n * }),\n * ];\n *\n * function PaymentsTable({ data }: { data: Payment[] }) {\n * const [globalFilter, setGlobalFilter] = useState(\"\");\n *\n * const table = useReactTable({\n * data,\n * columns,\n * state: { globalFilter },\n * onGlobalFilterChange: setGlobalFilter,\n * getCoreRowModel: getCoreRowModel(),\n * getSortedRowModel: getSortedRowModel(),\n * getFilteredRowModel: getFilteredRowModel(),\n * getPaginationRowModel: getPaginationRowModel(),\n * initialState: { pagination: { pageSize: DEFAULT_PAGE_SIZE } },\n * });\n * const rows = table.getRowModel().rows;\n * const isFiltered = globalFilter.trim() !== \"\";\n *\n * return (\n * <div className=\"space-y-4\">\n * <Input\n * placeholder=\"Filter payments…\"\n * value={globalFilter}\n * onChange={(event) => setGlobalFilter(event.target.value)}\n * />\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0 ? (\n * rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * ) : isFiltered ? (\n * // No results for the active filter — give the user a way out.\n * <DataTable.EmptyRow>\n * <Empty.Root>\n * <Empty.Icon svg={<MagnifyingGlassIcon />} />\n * <Empty.Title>No payments match your filter</Empty.Title>\n * <Empty.Description>\n * <p>Try a different search, or clear the filter to see everything.</p>\n * </Empty.Description>\n * <Empty.Actions>\n * <Button\n * type=\"button\"\n * appearance=\"outlined\"\n * priority=\"neutral\"\n * onClick={() => setGlobalFilter(\"\")}\n * >\n * Clear filters\n * </Button>\n * </Empty.Actions>\n * </Empty.Root>\n * </DataTable.EmptyRow>\n * ) : (\n * // No data yet — informational, optionally a primary \"create\" action.\n * <DataTable.EmptyRow>\n * <Empty.Root>\n * <Empty.Icon svg={<TrayIcon />} />\n * <Empty.Title>No payments yet</Empty.Title>\n * <Empty.Description>\n * <p>Payments you receive will appear here.</p>\n * </Empty.Description>\n * </Empty.Root>\n * </DataTable.EmptyRow>\n * )}\n * </DataTable.Body>\n * </DataTable.Root>\n * <CursorPagination.Root className=\"flex justify-end\" defaultPageSize={DEFAULT_PAGE_SIZE}>\n * <CursorPagination.PageSizeSelect\n * onChangePageSize={(size) => {\n * table.setPageSize(size);\n * table.setPageIndex(0); // reset to the first page when the size changes\n * }}\n * />\n * <CursorPagination.Buttons\n * hasPreviousPage={table.getCanPreviousPage()}\n * hasNextPage={table.getCanNextPage()}\n * onPreviousPage={() => table.previousPage()}\n * onNextPage={() => table.nextPage()}\n * />\n * </CursorPagination.Root>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * Row action column — a sticky right-edge cell with a dropdown menu of actions.\n * If the row also has `onClick`, stop propagation on the action cell so clicks\n * don't bubble up and fire the row handler:\n * ```tsx\n * import { DataTable, createColumnHelper } from \"@ngrok/mantle/data-table\";\n * import { DropdownMenu } from \"@ngrok/mantle/dropdown-menu\";\n * import { IconButton } from \"@ngrok/mantle/icon-button\";\n * import { DotsThreeVerticalIcon } from \"@phosphor-icons/react/DotsThreeVertical\";\n *\n * const columnHelper = createColumnHelper<Payment>();\n *\n * const columns = [\n * // …other columns…\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: (props) => (\n * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n * <DropdownMenu.Root>\n * <DropdownMenu.Trigger asChild>\n * <IconButton type=\"button\" label=\"Actions\" icon={<DotsThreeVerticalIcon />} />\n * </DropdownMenu.Trigger>\n * <DropdownMenu.Content align=\"end\">\n * <DropdownMenu.Item onSelect={() => copy(props.row.original.id)}>\n * Copy ID\n * </DropdownMenu.Item>\n * <DropdownMenu.Item onSelect={() => refund(props.row.original.id)}>\n * Refund\n * </DropdownMenu.Item>\n * </DropdownMenu.Content>\n * </DropdownMenu.Root>\n * </DataTable.ActionCell>\n * ),\n * }),\n * ];\n * ```\n *\n * @example\n * Clickable row navigating to a detail page — also render a `<Link>` inside the\n * primary cell so the row is reachable by keyboard and screen readers (a `<tr>`\n * is not focusable):\n * ```tsx\n * import { DataTable } from \"@ngrok/mantle/data-table\";\n * import { Link, href, useNavigate } from \"react-router\";\n *\n * function PaymentsTable({ data }: { data: Payment[] }) {\n * const navigate = useNavigate();\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.map((row) => (\n * <DataTable.Row\n * key={row.id}\n * row={row}\n * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n * />\n * ))}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n *\n * // The primary column's cell renders a <Link> for keyboard / a11y reachability.\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => <DataTable.Header>Email</DataTable.Header>,\n * cell: (props) => (\n * <DataTable.Cell>\n * <Link to={href(\"/payments/:id\", { id: props.row.original.id })}>\n * {props.getValue()}\n * </Link>\n * </DataTable.Cell>\n * ),\n * });\n * ```\n */\nconst DataTable = {\n\t/**\n\t * The root container of the data table component. REQUIRED: pass a\n\t * `useReactTable` instance (from `@tanstack/react-table`, also re-exported\n\t * from `@ngrok/mantle/data-table`) via the `table` prop — every other\n\t * `DataTable.*` part reads from it through context.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableroot\n\t *\n\t * @example\n\t * ```tsx\n\t * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n\t * const rows = table.getRowModel().rows;\n\t *\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * A sticky action cell positioned at the end of each row, typically holding\n\t * an `IconButton` that opens a `DropdownMenu`. Pair with `DataTable.ActionHeader`.\n\t *\n\t * If the row has `onClick`, stop propagation on this cell so clicks on action\n\t * controls don't bubble up and fire the row handler.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionCell,\n\t/**\n\t * A sticky header cell that pairs with `DataTable.ActionCell`, keeping the\n\t * action column aligned across the header and body when scrolling horizontally.\n\t * Use as the `header` for a `columnHelper.display` action column.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionHeader,\n\t/**\n\t * A `<td>` for rendering an individual data cell. Re-exported from\n\t * `Table.Cell`. Every cell rendered by a column's `cell` function should\n\t * be wrapped in this — a raw `<td>` skips mantle typography and padding.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablecell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => <DataTable.Header>Name</DataTable.Header>,\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tCell: Table.Cell,\n\t/**\n\t * The `<tbody>` container for rows of data. Typically wraps a map of\n\t * `DataTable.Row`, with a `DataTable.EmptyRow` fallback when there is\n\t * no data.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablebody\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * ```\n\t */\n\tBody,\n\t/**\n\t * An empty-state row that spans every column. Render this as the `else`\n\t * branch when `rows.length === 0` to keep the table's frame intact instead\n\t * of collapsing to an empty `<tbody>`.\n\t *\n\t * Drop an `Empty` in as the child for a real empty state — `EmptyRow` spans\n\t * every column and `Empty.Root` centers itself, so no `<td>` or centering\n\t * markup is needed.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableemptyrow\n\t * @see https://mantle.ngrok.com/components/empty\n\t *\n\t * @example\n\t * ```tsx\n\t * import { DataTable } from \"@ngrok/mantle/data-table\";\n\t * import { Empty } from \"@ngrok/mantle/empty\";\n\t * import { TrayIcon } from \"@phosphor-icons/react/Tray\";\n\t *\n\t * <DataTable.EmptyRow>\n\t * <Empty.Root>\n\t * <Empty.Icon svg={<TrayIcon />} />\n\t * <Empty.Title>No endpoints yet</Empty.Title>\n\t * </Empty.Root>\n\t * </DataTable.EmptyRow>\n\t * ```\n\t */\n\tEmptyRow,\n\t/**\n\t * The `<thead>` container that renders column headers automatically from\n\t * `table.getHeaderGroups()`. Does not accept children — headers come from\n\t * each column's `header` definition.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablehead\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>...</DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tHead,\n\t/**\n\t * A `<th>` optimized for header actions. Wrap each column's header content\n\t * in this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Name\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeader,\n\t/**\n\t * A sortable button toggle for a column header. Clicks cycle through\n\t * sort directions: for `\"alphanumeric\"`, `unsorted → asc → desc → unsorted`;\n\t * for `\"time\"`, `unsorted → desc (newest-first) → asc → unsorted`.\n\t *\n\t * Pass `className=\"justify-end\"` and `iconPlacement=\"start\"` for\n\t * right-aligned numeric columns so the sort icon stays paired with the label.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"email\", {\n\t * id: \"email\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Email\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeaderSortButton,\n\t/**\n\t * A single data table body row rendered from a TanStack Table row instance.\n\t * Does not accept children — cells come from each column's `cell` definition.\n\t *\n\t * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n\t * Pass a different `cursor-*` class via `className` to override. For keyboard\n\t * and screen-reader access, also render a `<Link>` inside the primary cell.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablerow\n\t *\n\t * @example\n\t * ```tsx\n\t * const navigate = useNavigate();\n\t *\n\t * {rows.map((row) => (\n\t * <DataTable.Row\n\t * key={row.id}\n\t * row={row}\n\t * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n\t * />\n\t * ))}\n\t * ```\n\t */\n\tRow,\n} as const;\n\nexport {\n\t//,\n\tDataTable,\n};\n\ntype DefaultSortIconProps = SvgAttributes & {\n\tdirection: SortDirection | undefined;\n\tmode: SortingMode | undefined;\n};\n\nfunction DefaultSortIcon({ direction, mode, ...props }: DefaultSortIconProps) {\n\tif (direction === \"unsorted\" || !mode || !direction) {\n\t\treturn <svg aria-hidden {...props} />;\n\t}\n\n\treturn <SortIcon mode={mode} direction={direction} {...props} />;\n}\n\n/**\n * Toggle the sorting direction of a column.\n * This ordering is typically toggled by clicking the column header.\n *\n * @example\n * ```md\n * Each click cycles through...\n *\n * For alphanumeric sorting:\n * unsorted ➡️ ascending ➡️ descending ➡️ unsorted ➡️ ...\n *\n * For time sorting:\n * unsorted ➡️ newest-to-oldest ➡️ oldest-to-newest ➡️ unsorted ➡️ ...\n *\n * this is equivalent to the inverse of alphanumeric sorting, or\n * unsorted ➡️ descending ➡️ ascending ➡️ unsorted ➡️ ...\n * ```\n */\nfunction toggleNextSortingDirection<TData, TValue>(\n\tcolumn: Column<TData, TValue>,\n\tsortingMode: SortingMode,\n) {\n\tif (!column.getCanSort()) {\n\t\treturn;\n\t}\n\n\tconst sortDirection = column.getIsSorted();\n\tconst currentSortDirection: SortDirection =\n\t\ttypeof sortDirection === \"string\" ? sortDirection : \"unsorted\";\n\n\tconst nextSortDirection = getNextSortDirection(currentSortDirection, sortingMode);\n\n\tswitch (nextSortDirection) {\n\t\tcase \"unsorted\":\n\t\t\tcolumn.clearSorting();\n\t\t\treturn;\n\t\tcase \"asc\":\n\t\t\tcolumn.toggleSorting(false);\n\t\t\treturn;\n\t\tcase \"desc\":\n\t\t\tcolumn.toggleSorting(true);\n\t\t\treturn;\n\t\tdefault:\n\t\t\treturn;\n\t}\n}\n"],"mappings":"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"}
|
package/dist/dialog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as isDialogOverlayTarget, t as Root } from "./primitive-FoWela9a.js";
|
|
2
|
-
import { n as IconButtonProps } from "./icon-button-
|
|
2
|
+
import { n as IconButtonProps } from "./icon-button-ntupABbM.js";
|
|
3
3
|
import { ComponentProps } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/components/dialog/dialog.d.ts
|
package/dist/empty.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./cx-CBSnSC36.js";import{t}from"./svg-only-Cz1cby8y.js";import{t as n}from"./slot-CV5fmqFr.js";import{jsx as r}from"react/jsx-runtime";const i=({asChild:t,children:i,className:a,...o})=>r(t?n:`div`,{"data-slot":`empty`,className:e(`mx-auto flex max-w-lg flex-col items-center
|
|
1
|
+
import{t as e}from"./cx-CBSnSC36.js";import{t}from"./svg-only-Cz1cby8y.js";import{t as n}from"./slot-CV5fmqFr.js";import{jsx as r}from"react/jsx-runtime";const i=({asChild:t,children:i,className:a,...o})=>r(t?n:`div`,{"data-slot":`empty`,className:e(`mx-auto flex max-w-lg flex-col items-center p-6 text-center`,a),...o,children:i});i.displayName=`Empty`;const a=({className:n,svg:i,...a})=>r(t,{"data-slot":`empty-icon`,className:e(`mb-1 size-10 text-neutral-400`,n),svg:i,...a});a.displayName=`EmptyIcon`;const o=({asChild:t,children:i,className:a,...o})=>r(t?n:`h3`,{"data-slot":`empty-title`,className:e(`text-strong text-sm font-medium font-sans`,a),...o,children:i});o.displayName=`EmptyTitle`;const s=({asChild:t,children:i,className:a,...o})=>r(t?n:`div`,{"data-slot":`empty-description`,className:e(`text-muted space-y-4 text-sm font-sans`,a),...o,children:i});s.displayName=`EmptyDescription`;const c=({asChild:t,children:i,className:a,...o})=>r(t?n:`div`,{"data-slot":`empty-actions`,className:e(`mt-4 flex items-center gap-2`,a),...o,children:i});c.displayName=`EmptyActions`;const l={Root:i,Icon:a,Title:o,Description:s,Actions:c};export{l as Empty};
|
|
2
2
|
//# sourceMappingURL=empty.js.map
|
package/dist/empty.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empty.js","names":[],"sources":["../src/components/empty/empty.tsx"],"sourcesContent":["import type { ComponentProps, HTMLAttributes, ReactNode } from \"react\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { SvgOnly } from \"../icon/svg-only.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { Slot } from \"../slot/index.js\";\n\n/**\n * The root container for an empty state. Centers content horizontally\n * with consistent vertical padding and max-width.\n *\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * ```tsx\n * <Empty.Root>\n * <Empty.Icon svg={<GhostIcon />} />\n * <Empty.Title>No results found</Empty.Title>\n * <Empty.Description>Try adjusting your search or filters.</Empty.Description>\n * <Empty.Actions>\n * <Button>Clear filters</Button>\n * </Empty.Actions>\n * </Empty.Root>\n * ```\n */\nconst Root = ({ asChild, children, className, ...props }: ComponentProps<\"div\"> & WithAsChild) => {\n\tconst Comp = asChild ? Slot : \"div\";\n\n\treturn (\n\t\t<Comp\n\t\t\tdata-slot=\"empty\"\n\t\t\tclassName={cx(\"mx-auto flex max-w-lg flex-col items-center
|
|
1
|
+
{"version":3,"file":"empty.js","names":[],"sources":["../src/components/empty/empty.tsx"],"sourcesContent":["import type { ComponentProps, HTMLAttributes, ReactNode } from \"react\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { SvgOnly } from \"../icon/svg-only.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { Slot } from \"../slot/index.js\";\n\n/**\n * The root container for an empty state. Centers content horizontally\n * with consistent vertical padding and max-width.\n *\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * ```tsx\n * <Empty.Root>\n * <Empty.Icon svg={<GhostIcon />} />\n * <Empty.Title>No results found</Empty.Title>\n * <Empty.Description>Try adjusting your search or filters.</Empty.Description>\n * <Empty.Actions>\n * <Button>Clear filters</Button>\n * </Empty.Actions>\n * </Empty.Root>\n * ```\n */\nconst Root = ({ asChild, children, className, ...props }: ComponentProps<\"div\"> & WithAsChild) => {\n\tconst Comp = asChild ? Slot : \"div\";\n\n\treturn (\n\t\t<Comp\n\t\t\tdata-slot=\"empty\"\n\t\t\tclassName={cx(\"mx-auto flex max-w-lg flex-col items-center p-6 text-center\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Comp>\n\t);\n};\nRoot.displayName = \"Empty\";\n\ntype EmptyIconProps = Omit<SvgAttributes, \"children\"> & {\n\t/**\n\t * A single SVG icon element.\n\t */\n\tsvg: ReactNode;\n};\n\n/**\n * Renders a large icon for the empty state. Pass a single SVG icon element\n * via the `svg` prop (e.g. from `@phosphor-icons/react`).\n *\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * ```tsx\n * <Empty.Root>\n * <Empty.Icon svg={<GhostIcon />} />\n * <Empty.Title>No endpoints yet</Empty.Title>\n * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n * <Empty.Actions>\n * <Button>Create endpoint</Button>\n * </Empty.Actions>\n * </Empty.Root>\n * ```\n */\nconst Icon = ({ className, svg, ...props }: EmptyIconProps) => {\n\treturn (\n\t\t<SvgOnly\n\t\t\tdata-slot=\"empty-icon\"\n\t\t\tclassName={cx(\"mb-1 size-10 text-neutral-400\", className)}\n\t\t\tsvg={svg}\n\t\t\t{...props}\n\t\t/>\n\t);\n};\nIcon.displayName = \"EmptyIcon\";\n\n/**\n * The heading text for the empty state. Renders as an `h3` by default. Use the\n * `asChild` prop to render as a different heading level (e.g. `h1`, `h2`).\n *\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * ```tsx\n * <Empty.Root>\n * <Empty.Icon svg={<GhostIcon />} />\n * <Empty.Title>No endpoints yet</Empty.Title>\n * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n * <Empty.Actions>\n * <Button>Create endpoint</Button>\n * </Empty.Actions>\n * </Empty.Root>\n *\n * <Empty.Title asChild>\n * <h2>No results found</h2>\n * </Empty.Title>\n * ```\n */\nconst Title = ({\n\tasChild,\n\tchildren,\n\tclassName,\n\t...props\n}: HTMLAttributes<HTMLHeadingElement> & WithAsChild) => {\n\tconst Comp = asChild ? Slot : \"h3\";\n\n\treturn (\n\t\t<Comp\n\t\t\tdata-slot=\"empty-title\"\n\t\t\tclassName={cx(\"text-strong text-sm font-medium font-sans\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Comp>\n\t);\n};\nTitle.displayName = \"EmptyTitle\";\n\n/**\n * Supporting descriptive text below the title. Renders as a `div` with\n * `space-y-4` so multiple paragraphs can be placed inside. Use the `asChild`\n * prop to render as a different element.\n *\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * ```tsx\n * <Empty.Root>\n * <Empty.Icon svg={<GhostIcon />} />\n * <Empty.Title>No endpoints yet</Empty.Title>\n * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n * <Empty.Actions>\n * <Button>Create endpoint</Button>\n * </Empty.Actions>\n * </Empty.Root>\n *\n * <Empty.Description>\n * <p>Something went wrong.</p>\n * <p>Please try again in a few minutes.</p>\n * </Empty.Description>\n * ```\n */\nconst Description = ({\n\tasChild,\n\tchildren,\n\tclassName,\n\t...props\n}: ComponentProps<\"div\"> & WithAsChild) => {\n\tconst Comp = asChild ? Slot : \"div\";\n\n\treturn (\n\t\t<Comp\n\t\t\tdata-slot=\"empty-description\"\n\t\t\tclassName={cx(\"text-muted space-y-4 text-sm font-sans\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Comp>\n\t);\n};\nDescription.displayName = \"EmptyDescription\";\n\n/**\n * A container for action buttons or links in the empty state.\n *\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * ```tsx\n * <Empty.Root>\n * <Empty.Icon svg={<GhostIcon />} />\n * <Empty.Title>No endpoints yet</Empty.Title>\n * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n * <Empty.Actions>\n * <Button>Create endpoint</Button>\n * <Button appearance=\"outlined\">Go back</Button>\n * </Empty.Actions>\n * </Empty.Root>\n * ```\n */\nconst Actions = ({\n\tasChild,\n\tchildren,\n\tclassName,\n\t...props\n}: ComponentProps<\"div\"> & WithAsChild) => {\n\tconst Comp = asChild ? Slot : \"div\";\n\n\treturn (\n\t\t<Comp\n\t\t\tdata-slot=\"empty-actions\"\n\t\t\tclassName={cx(\"mt-4 flex items-center gap-2\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Comp>\n\t);\n};\nActions.displayName = \"EmptyActions\";\n\n/**\n * Compound component for rendering empty states. Use with `Empty.Root`,\n * `Empty.Icon`, `Empty.Title`, `Empty.Description`, and `Empty.Actions`.\n *\n * @see https://mantle.ngrok.com/components/empty\n *\n * @example\n * Composition:\n * ```\n * Empty.Root\n * ├── Empty.Icon\n * ├── Empty.Title\n * ├── Empty.Description\n * └── Empty.Actions\n * ```\n *\n * @example\n * ```tsx\n * <Empty.Root>\n * <Empty.Icon svg={<GhostIcon />} />\n * <Empty.Title>No endpoints yet</Empty.Title>\n * <Empty.Description>\n * Create your first endpoint to get started.\n * </Empty.Description>\n * <Empty.Actions>\n * <Button>Create endpoint</Button>\n * </Empty.Actions>\n * </Empty.Root>\n * ```\n */\nconst Empty = {\n\t/**\n\t * The root container for an empty state. Centers content vertically and\n\t * horizontally with consistent padding and max-width.\n\t *\n\t * @see https://mantle.ngrok.com/components/empty\n\t *\n\t * @example\n\t * ```tsx\n\t * <Empty.Root>\n\t * <Empty.Icon svg={<GhostIcon />} />\n\t * <Empty.Title>No endpoints yet</Empty.Title>\n\t * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n\t * <Empty.Actions>\n\t * <Button>Create endpoint</Button>\n\t * </Empty.Actions>\n\t * </Empty.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * Renders a large icon for the empty state. Pass a single SVG icon element\n\t * via the `svg` prop.\n\t *\n\t * @see https://mantle.ngrok.com/components/empty\n\t *\n\t * @example\n\t * ```tsx\n\t * <Empty.Root>\n\t * <Empty.Icon svg={<GhostIcon />} />\n\t * <Empty.Title>No endpoints yet</Empty.Title>\n\t * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n\t * <Empty.Actions>\n\t * <Button>Create endpoint</Button>\n\t * </Empty.Actions>\n\t * </Empty.Root>\n\t * ```\n\t */\n\tIcon,\n\t/**\n\t * The heading text for the empty state. Renders as an `h3` by default.\n\t * Use `asChild` to render as a different heading level.\n\t *\n\t * @see https://mantle.ngrok.com/components/empty\n\t *\n\t * @example\n\t * ```tsx\n\t * <Empty.Root>\n\t * <Empty.Icon svg={<GhostIcon />} />\n\t * <Empty.Title>No endpoints yet</Empty.Title>\n\t * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n\t * <Empty.Actions>\n\t * <Button>Create endpoint</Button>\n\t * </Empty.Actions>\n\t * </Empty.Root>\n\t *\n\t * <Empty.Title asChild>\n\t * <h2>No results found</h2>\n\t * </Empty.Title>\n\t * ```\n\t */\n\tTitle,\n\t/**\n\t * Supporting descriptive text below the title. Renders as a `div` with\n\t * `space-y-4` for multiple paragraphs. Use `asChild` to render as a\n\t * different element.\n\t *\n\t * @see https://mantle.ngrok.com/components/empty\n\t *\n\t * @example\n\t * ```tsx\n\t * <Empty.Root>\n\t * <Empty.Icon svg={<GhostIcon />} />\n\t * <Empty.Title>No endpoints yet</Empty.Title>\n\t * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n\t * <Empty.Actions>\n\t * <Button>Create endpoint</Button>\n\t * </Empty.Actions>\n\t * </Empty.Root>\n\t * ```\n\t */\n\tDescription,\n\t/**\n\t * A container for action buttons or links in the empty state.\n\t *\n\t * @see https://mantle.ngrok.com/components/empty\n\t *\n\t * @example\n\t * ```tsx\n\t * <Empty.Root>\n\t * <Empty.Icon svg={<GhostIcon />} />\n\t * <Empty.Title>No endpoints yet</Empty.Title>\n\t * <Empty.Description>Create your first endpoint to get started.</Empty.Description>\n\t * <Empty.Actions>\n\t * <Button>Create endpoint</Button>\n\t * </Empty.Actions>\n\t * </Empty.Root>\n\t * ```\n\t */\n\tActions,\n} as const;\n\nexport {\n\t//,\n\tEmpty,\n};\n"],"mappings":"0JAyBA,MAAM,GAAQ,CAAE,UAAS,WAAU,YAAW,GAAG,KAI/C,EAHY,EAAU,EAAO,MAG7B,CACC,YAAU,QACV,UAAW,EAAG,8DAA+D,CAAS,EACtF,GAAI,EAEH,UACI,CAAA,EAGR,EAAK,YAAc,QA2BnB,MAAM,GAAQ,CAAE,YAAW,MAAK,GAAG,KAEjC,EAAC,EAAD,CACC,YAAU,aACV,UAAW,EAAG,gCAAiC,CAAS,EACnD,MACL,GAAI,CACJ,CAAA,EAGH,EAAK,YAAc,YAwBnB,MAAM,GAAS,CACd,UACA,WACA,YACA,GAAG,KAKF,EAHY,EAAU,EAAO,KAG7B,CACC,YAAU,cACV,UAAW,EAAG,4CAA6C,CAAS,EACpE,GAAI,EAEH,UACI,CAAA,EAGR,EAAM,YAAc,aA0BpB,MAAM,GAAe,CACpB,UACA,WACA,YACA,GAAG,KAKF,EAHY,EAAU,EAAO,MAG7B,CACC,YAAU,oBACV,UAAW,EAAG,yCAA0C,CAAS,EACjE,GAAI,EAEH,UACI,CAAA,EAGR,EAAY,YAAc,mBAoB1B,MAAM,GAAW,CAChB,UACA,WACA,YACA,GAAG,KAKF,EAHY,EAAU,EAAO,MAG7B,CACC,YAAU,gBACV,UAAW,EAAG,+BAAgC,CAAS,EACvD,GAAI,EAEH,UACI,CAAA,EAGR,EAAQ,YAAc,eAgCtB,MAAM,EAAQ,CAmBb,OAmBA,OAuBA,QAoBA,cAkBA,SACD"}
|
package/dist/field.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as WithAsChild } from "./as-child-uN_018tj.js";
|
|
2
|
-
import { n as IconButtonProps } from "./icon-button-
|
|
2
|
+
import { n as IconButtonProps } from "./icon-button-ntupABbM.js";
|
|
3
3
|
import { a as ValidationState, c as parseValidation, i as ValidationProp, l as resolveValidation, n as ParsedValidation, o as WithValidation, r as Validation, s as isAriaInvalid, t as AriaInvalid } from "./validation-xyX_6kph.js";
|
|
4
|
-
import { t as Slot } from "./index-
|
|
4
|
+
import { t as Slot } from "./index-CJbKEKr2.js";
|
|
5
5
|
import { ComponentProps, ReactElement, ReactNode } from "react";
|
|
6
6
|
|
|
7
7
|
//#region src/components/field/field-context.d.ts
|
|
@@ -6,7 +6,7 @@ import { ButtonHTMLAttributes, ReactNode } from "react";
|
|
|
6
6
|
declare const iconButtonVariants: (props?: ({
|
|
7
7
|
appearance?: "ghost" | "outlined" | null | undefined;
|
|
8
8
|
isLoading?: boolean | null | undefined;
|
|
9
|
-
size?: "
|
|
9
|
+
size?: "xs" | "sm" | "md" | null | undefined;
|
|
10
10
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
11
|
type IconButtonVariants = VariantProps<typeof iconButtonVariants>;
|
|
12
12
|
/**
|
|
@@ -93,4 +93,4 @@ type IconButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & WithAsChild & I
|
|
|
93
93
|
declare const IconButton: import("react").ForwardRefExoticComponent<IconButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
94
94
|
//#endregion
|
|
95
95
|
export { IconButtonProps as n, IconButton as t };
|
|
96
|
-
//# sourceMappingURL=icon-button-
|
|
96
|
+
//# sourceMappingURL=icon-button-ntupABbM.d.ts.map
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
* </Slot>
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
|
-
declare const Slot: import("react").ForwardRefExoticComponent<Omit<import("
|
|
16
|
+
declare const Slot: import("react").ForwardRefExoticComponent<Omit<import("react").HTMLAttributes<HTMLElement> & {
|
|
17
|
+
children?: import("react").ReactNode;
|
|
18
|
+
} & import("react").RefAttributes<HTMLElement>, "ref"> & import("react").RefAttributes<HTMLElement>>;
|
|
17
19
|
//#endregion
|
|
18
20
|
export { Slot as t };
|
|
19
|
-
//# sourceMappingURL=index-
|
|
21
|
+
//# sourceMappingURL=index-CJbKEKr2.d.ts.map
|
package/dist/input.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "
|
|
|
68
68
|
* />
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
|
-
declare const Input: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "
|
|
71
|
+
declare const Input: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithAutoComplete & WithInputType & WithValidation & {
|
|
72
72
|
children?: import("react").ReactNode | undefined;
|
|
73
73
|
} & import("react").RefAttributes<HTMLInputElement>>;
|
|
74
74
|
type InputCaptureProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & BaseProps;
|
|
@@ -86,7 +86,7 @@ type InputCaptureProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComple
|
|
|
86
86
|
* </Input>
|
|
87
87
|
* ```
|
|
88
88
|
*/
|
|
89
|
-
declare const InputCapture: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "
|
|
89
|
+
declare const InputCapture: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithAutoComplete & WithInputType & WithValidation & import("react").RefAttributes<HTMLInputElement>>;
|
|
90
90
|
//#endregion
|
|
91
91
|
//#region src/components/input/password-input.d.ts
|
|
92
92
|
type PasswordInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithValidation & WithAutoComplete & {
|
|
@@ -158,7 +158,7 @@ type PasswordInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoCompl
|
|
|
158
158
|
* }
|
|
159
159
|
* ```
|
|
160
160
|
*/
|
|
161
|
-
declare const PasswordInput: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "
|
|
161
|
+
declare const PasswordInput: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithValidation & WithAutoComplete & {
|
|
162
162
|
/**
|
|
163
163
|
* Callback for when the visibility of the password value changes.
|
|
164
164
|
*/
|
package/dist/llms.txt
CHANGED
package/dist/mantle.css
CHANGED
|
@@ -515,12 +515,84 @@ MARK: UTILITIES
|
|
|
515
515
|
background-attachment: local, local, scroll, scroll;
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
/*
|
|
519
|
+
* Animatable mask-edge colors for the `scroll-fade-*` utilities. Registered as
|
|
520
|
+
* <color> so they can be animated by a scroll-driven animation; default black
|
|
521
|
+
* means "fully opaque in the mask" = no fade, which is also the resting value
|
|
522
|
+
* wherever `animation-timeline: scroll()` is unsupported.
|
|
523
|
+
*/
|
|
524
|
+
@property --_scroll-fade-left {
|
|
525
|
+
syntax: "<color>";
|
|
526
|
+
inherits: false;
|
|
527
|
+
initial-value: black;
|
|
528
|
+
}
|
|
529
|
+
@property --_scroll-fade-right {
|
|
530
|
+
syntax: "<color>";
|
|
531
|
+
inherits: false;
|
|
532
|
+
initial-value: black;
|
|
533
|
+
}
|
|
534
|
+
@property --_scroll-fade-top {
|
|
535
|
+
syntax: "<color>";
|
|
536
|
+
inherits: false;
|
|
537
|
+
initial-value: black;
|
|
538
|
+
}
|
|
539
|
+
@property --_scroll-fade-bottom {
|
|
540
|
+
syntax: "<color>";
|
|
541
|
+
inherits: false;
|
|
542
|
+
initial-value: black;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/*
|
|
546
|
+
* Each edge gets its own keyframe + scroll range so both edges can fade
|
|
547
|
+
* independently (e.g. both faded mid-scroll). Black <-> transparent only
|
|
548
|
+
* changes the mask alpha.
|
|
549
|
+
*/
|
|
550
|
+
@keyframes scroll-fade-x-start {
|
|
551
|
+
from {
|
|
552
|
+
--_scroll-fade-left: black;
|
|
553
|
+
}
|
|
554
|
+
to {
|
|
555
|
+
--_scroll-fade-left: transparent;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
@keyframes scroll-fade-x-end {
|
|
559
|
+
from {
|
|
560
|
+
--_scroll-fade-right: transparent;
|
|
561
|
+
}
|
|
562
|
+
to {
|
|
563
|
+
--_scroll-fade-right: black;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
@keyframes scroll-fade-y-start {
|
|
567
|
+
from {
|
|
568
|
+
--_scroll-fade-top: black;
|
|
569
|
+
}
|
|
570
|
+
to {
|
|
571
|
+
--_scroll-fade-top: transparent;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
@keyframes scroll-fade-y-end {
|
|
575
|
+
from {
|
|
576
|
+
--_scroll-fade-bottom: transparent;
|
|
577
|
+
}
|
|
578
|
+
to {
|
|
579
|
+
--_scroll-fade-bottom: black;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
518
583
|
@utility scroll-fade-x {
|
|
519
|
-
/*
|
|
520
|
-
|
|
521
|
-
|
|
584
|
+
/*
|
|
585
|
+
* Fades the inline-start/end edges of a horizontally-scrolling container to
|
|
586
|
+
* signal more content in that direction. The element is its own scroll
|
|
587
|
+
* timeline, so an edge with nothing beyond it — at the start, at the end, or
|
|
588
|
+
* with no overflow at all (inactive timeline => initial value) — renders
|
|
589
|
+
* cleanly with no fade. Consumers can pin an edge opaque by overriding
|
|
590
|
+
* `--_fade-left` / `--_fade-right` (the animation drives the upstream
|
|
591
|
+
* `--_scroll-fade-*` vars, so a static override always wins).
|
|
592
|
+
*/
|
|
593
|
+
--_fade-left: var(--_scroll-fade-left);
|
|
594
|
+
--_fade-right: var(--_scroll-fade-right);
|
|
522
595
|
|
|
523
|
-
/* Mask fades the element's edges to transparent instead of painting over them with a background color */
|
|
524
596
|
mask-image: linear-gradient(
|
|
525
597
|
to right,
|
|
526
598
|
var(--_fade-left),
|
|
@@ -529,12 +601,110 @@ MARK: UTILITIES
|
|
|
529
601
|
var(--_fade-right)
|
|
530
602
|
);
|
|
531
603
|
|
|
532
|
-
|
|
533
|
-
|
|
604
|
+
@supports (animation-timeline: scroll()) {
|
|
605
|
+
animation-name: scroll-fade-x-start, scroll-fade-x-end;
|
|
606
|
+
animation-fill-mode: both;
|
|
607
|
+
/* Linear so the fade tracks scroll position 1:1 across the range. */
|
|
608
|
+
animation-timing-function: linear;
|
|
609
|
+
/* Pixel-precise edge snap, independent of total scroll distance. */
|
|
610
|
+
animation-range:
|
|
611
|
+
0 2px,
|
|
612
|
+
calc(100% - 2px) 100%;
|
|
613
|
+
animation-timeline: scroll(self inline), scroll(self inline);
|
|
614
|
+
/* Driven by the scroll timeline; a non-auto duration is required by some engines. */
|
|
615
|
+
animation-duration: 1ms;
|
|
534
616
|
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
@utility scroll-fade-y {
|
|
620
|
+
/* Vertical sibling of `scroll-fade-x` — see it for how the masking works. */
|
|
621
|
+
--_fade-top: var(--_scroll-fade-top);
|
|
622
|
+
--_fade-bottom: var(--_scroll-fade-bottom);
|
|
623
|
+
|
|
624
|
+
mask-image: linear-gradient(
|
|
625
|
+
to bottom,
|
|
626
|
+
var(--_fade-top),
|
|
627
|
+
black 1.5rem,
|
|
628
|
+
black calc(100% - 1.5rem),
|
|
629
|
+
var(--_fade-bottom)
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
@supports (animation-timeline: scroll()) {
|
|
633
|
+
animation-name: scroll-fade-y-start, scroll-fade-y-end;
|
|
634
|
+
animation-fill-mode: both;
|
|
635
|
+
animation-timing-function: linear;
|
|
636
|
+
animation-range:
|
|
637
|
+
0 2px,
|
|
638
|
+
calc(100% - 2px) 100%;
|
|
639
|
+
animation-timeline: scroll(self block), scroll(self block);
|
|
640
|
+
animation-duration: 1ms;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/*
|
|
645
|
+
* Single-edge variants of `scroll-fade-x` / `scroll-fade-y`, for when only one
|
|
646
|
+
* edge should fade (e.g. a sidebar that fades content scrolling under a sticky
|
|
647
|
+
* header but stays flush at the bottom). Each masks and drives only its own
|
|
648
|
+
* edge; the opposite edge stays fully opaque. Same scroll-driven mechanics and
|
|
649
|
+
* fallback behavior as the axis utilities — see `scroll-fade-x` for details.
|
|
650
|
+
*/
|
|
651
|
+
@utility scroll-fade-t {
|
|
652
|
+
--_fade-top: var(--_scroll-fade-top);
|
|
653
|
+
|
|
654
|
+
mask-image: linear-gradient(to bottom, var(--_fade-top), black 1.5rem);
|
|
655
|
+
|
|
656
|
+
@supports (animation-timeline: scroll()) {
|
|
657
|
+
animation-name: scroll-fade-y-start;
|
|
658
|
+
animation-fill-mode: both;
|
|
659
|
+
animation-timing-function: linear;
|
|
660
|
+
animation-range: 0 2px;
|
|
661
|
+
animation-timeline: scroll(self block);
|
|
662
|
+
animation-duration: 1ms;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
@utility scroll-fade-b {
|
|
667
|
+
--_fade-bottom: var(--_scroll-fade-bottom);
|
|
668
|
+
|
|
669
|
+
mask-image: linear-gradient(to bottom, black calc(100% - 1.5rem), var(--_fade-bottom));
|
|
670
|
+
|
|
671
|
+
@supports (animation-timeline: scroll()) {
|
|
672
|
+
animation-name: scroll-fade-y-end;
|
|
673
|
+
animation-fill-mode: both;
|
|
674
|
+
animation-timing-function: linear;
|
|
675
|
+
animation-range: calc(100% - 2px) 100%;
|
|
676
|
+
animation-timeline: scroll(self block);
|
|
677
|
+
animation-duration: 1ms;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
@utility scroll-fade-l {
|
|
682
|
+
--_fade-left: var(--_scroll-fade-left);
|
|
683
|
+
|
|
684
|
+
mask-image: linear-gradient(to right, var(--_fade-left), black 40px);
|
|
685
|
+
|
|
686
|
+
@supports (animation-timeline: scroll()) {
|
|
687
|
+
animation-name: scroll-fade-x-start;
|
|
688
|
+
animation-fill-mode: both;
|
|
689
|
+
animation-timing-function: linear;
|
|
690
|
+
animation-range: 0 2px;
|
|
691
|
+
animation-timeline: scroll(self inline);
|
|
692
|
+
animation-duration: 1ms;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
@utility scroll-fade-r {
|
|
697
|
+
--_fade-right: var(--_scroll-fade-right);
|
|
698
|
+
|
|
699
|
+
mask-image: linear-gradient(to right, black calc(100% - 40px), var(--_fade-right));
|
|
535
700
|
|
|
536
|
-
|
|
537
|
-
|
|
701
|
+
@supports (animation-timeline: scroll()) {
|
|
702
|
+
animation-name: scroll-fade-x-end;
|
|
703
|
+
animation-fill-mode: both;
|
|
704
|
+
animation-timing-function: linear;
|
|
705
|
+
animation-range: calc(100% - 2px) 100%;
|
|
706
|
+
animation-timeline: scroll(self inline);
|
|
707
|
+
animation-duration: 1ms;
|
|
538
708
|
}
|
|
539
709
|
}
|
|
540
710
|
|
|
@@ -60,6 +60,7 @@ type Props = ComponentProps<"div"> & WithAsChild & BaseProps;
|
|
|
60
60
|
* </SandboxedOnClick>
|
|
61
61
|
* </TableRowCell>
|
|
62
62
|
* </TableRow>
|
|
63
|
+
* ```
|
|
63
64
|
*/
|
|
64
65
|
declare const SandboxedOnClick: import("react").ForwardRefExoticComponent<Omit<Props, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
65
66
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandboxed-on-click.js","names":[],"sources":["../src/components/sandboxed-on-click/sandboxed-on-click.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ComponentRef, HTMLAttributes, MouseEventHandler } from \"react\";\nimport { forwardRef } from \"react\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport { Slot } from \"../slot/index.js\";\n\ntype BaseProps = {\n\t/**\n\t * Only call `event.preventDefault()` in the `onClick` handler if the user\n\t * has not set `allowClickEventDefault` to `true`. This allows the user to\n\t * control whether or not the default behavior of the click event should be\n\t * allowed.\n\t *\n\t * This is useful for links or buttons that should navigate or perform some\n\t * action on click.\n\t *\n\t * @default false\n\t */\n\tallowClickEventDefault?: boolean;\n};\n\ntype EventProps = BaseProps & {\n\t/**\n\t * The click event handler.\n\t */\n\tonClick?: MouseEventHandler<HTMLElement>;\n};\n\n/**\n * Props for the sandboxed onClick container. Spread this on the element you want\n * to prevent the click event from bubbling out of.\n *\n * @see https://mantle.ngrok.com/components/sandboxed-on-click#sandboxedonclick\n */\nconst sandboxedOnClickProps = ({ allowClickEventDefault = false, onClick }: EventProps = {}) =>\n\t({\n\t\t/**\n\t\t * Marking an element with the role presentation indicates to assistive\n\t\t * technology that this element should be ignored; it exists to support the\n\t\t * web application and is not meant for humans to interact with directly.\n\t\t *\n\t\t * @see https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/65be35b0f6c6cf8b79e9a748cb657a64b78c6535/docs/rules/no-noninteractive-element-interactions.md#case-this-element-is-catching-bubbled-events-from-elements-that-it-contains\n\t\t * @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/presentation_role\n\t\t */\n\t\trole: \"presentation\",\n\t\tonClick: (event) => {\n\t\t\t/**\n\t\t\t * we _always_ want to stop propagation to prevent the event from bubbling\n\t\t\t * out of the sandboxed container\n\t\t\t */\n\t\t\tevent.stopPropagation();\n\n\t\t\t/**\n\t\t\t * Only call `event.preventDefault()` if the user has not set\n\t\t\t * `allowClickEventDefault` to true. This allows the user to control\n\t\t\t * whether or not the default behavior of the click event should be\n\t\t\t * allowed.\n\t\t\t *\n\t\t\t * This is useful for links or buttons that should navigate or perform\n\t\t\t * some action on click.\n\t\t\t */\n\t\t\tif (!allowClickEventDefault) {\n\t\t\t\tevent.preventDefault();\n\t\t\t}\n\t\t\tonClick?.(event);\n\t\t},\n\t}) as const satisfies HTMLAttributes<HTMLElement>;\n\ntype Props = ComponentProps<\"div\"> & WithAsChild & BaseProps;\n\n/**\n * A container that prevents the click event from bubbling out of it.\n *\n * @see https://mantle.ngrok.com/components/sandboxed-on-click#sandboxedonclick\n *\n * @example\n * ```tsx\n * <TableRow onClick={() => navigate(\"/somewhere\")}>\n * <TableRowCell>\n * <SandboxedOnClick allowClickEventDefault>\n * <Anchor href=\"https://ngrok.com/docs\">\n * See ngrok docs\n * </Anchor>\n * </SandboxedOnClick>\n * </TableRowCell>\n * </TableRow>\n */\nconst SandboxedOnClick = forwardRef<ComponentRef<\"div\">, Props>(\n\t(\n\t\t{\n\t\t\t//,\n\t\t\tallowClickEventDefault = false,\n\t\t\tasChild = false,\n\t\t\tchildren,\n\t\t\tonClick,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t\t{...sandboxedOnClickProps({ allowClickEventDefault, onClick })}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</Component>\n\t\t);\n\t},\n);\nSandboxedOnClick.displayName = \"SandboxedOnClick\";\n\nexport {\n\t//,\n\tSandboxedOnClick,\n\tsandboxedOnClickProps,\n};\n"],"mappings":"kHAmCA,MAAM,GAAyB,CAAE,yBAAyB,GAAO,WAAwB,CAAC,KACxF,CASA,KAAM,eACN,QAAU,GAAU,CAKnB,EAAM,gBAAgB,EAWjB,GACJ,EAAM,eAAe,EAEtB,IAAU,CAAK,CAChB,CACD,
|
|
1
|
+
{"version":3,"file":"sandboxed-on-click.js","names":[],"sources":["../src/components/sandboxed-on-click/sandboxed-on-click.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ComponentRef, HTMLAttributes, MouseEventHandler } from \"react\";\nimport { forwardRef } from \"react\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport { Slot } from \"../slot/index.js\";\n\ntype BaseProps = {\n\t/**\n\t * Only call `event.preventDefault()` in the `onClick` handler if the user\n\t * has not set `allowClickEventDefault` to `true`. This allows the user to\n\t * control whether or not the default behavior of the click event should be\n\t * allowed.\n\t *\n\t * This is useful for links or buttons that should navigate or perform some\n\t * action on click.\n\t *\n\t * @default false\n\t */\n\tallowClickEventDefault?: boolean;\n};\n\ntype EventProps = BaseProps & {\n\t/**\n\t * The click event handler.\n\t */\n\tonClick?: MouseEventHandler<HTMLElement>;\n};\n\n/**\n * Props for the sandboxed onClick container. Spread this on the element you want\n * to prevent the click event from bubbling out of.\n *\n * @see https://mantle.ngrok.com/components/sandboxed-on-click#sandboxedonclick\n */\nconst sandboxedOnClickProps = ({ allowClickEventDefault = false, onClick }: EventProps = {}) =>\n\t({\n\t\t/**\n\t\t * Marking an element with the role presentation indicates to assistive\n\t\t * technology that this element should be ignored; it exists to support the\n\t\t * web application and is not meant for humans to interact with directly.\n\t\t *\n\t\t * @see https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/65be35b0f6c6cf8b79e9a748cb657a64b78c6535/docs/rules/no-noninteractive-element-interactions.md#case-this-element-is-catching-bubbled-events-from-elements-that-it-contains\n\t\t * @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/presentation_role\n\t\t */\n\t\trole: \"presentation\",\n\t\tonClick: (event) => {\n\t\t\t/**\n\t\t\t * we _always_ want to stop propagation to prevent the event from bubbling\n\t\t\t * out of the sandboxed container\n\t\t\t */\n\t\t\tevent.stopPropagation();\n\n\t\t\t/**\n\t\t\t * Only call `event.preventDefault()` if the user has not set\n\t\t\t * `allowClickEventDefault` to true. This allows the user to control\n\t\t\t * whether or not the default behavior of the click event should be\n\t\t\t * allowed.\n\t\t\t *\n\t\t\t * This is useful for links or buttons that should navigate or perform\n\t\t\t * some action on click.\n\t\t\t */\n\t\t\tif (!allowClickEventDefault) {\n\t\t\t\tevent.preventDefault();\n\t\t\t}\n\t\t\tonClick?.(event);\n\t\t},\n\t}) as const satisfies HTMLAttributes<HTMLElement>;\n\ntype Props = ComponentProps<\"div\"> & WithAsChild & BaseProps;\n\n/**\n * A container that prevents the click event from bubbling out of it.\n *\n * @see https://mantle.ngrok.com/components/sandboxed-on-click#sandboxedonclick\n *\n * @example\n * ```tsx\n * <TableRow onClick={() => navigate(\"/somewhere\")}>\n * <TableRowCell>\n * <SandboxedOnClick allowClickEventDefault>\n * <Anchor href=\"https://ngrok.com/docs\">\n * See ngrok docs\n * </Anchor>\n * </SandboxedOnClick>\n * </TableRowCell>\n * </TableRow>\n * ```\n */\nconst SandboxedOnClick = forwardRef<ComponentRef<\"div\">, Props>(\n\t(\n\t\t{\n\t\t\t//,\n\t\t\tallowClickEventDefault = false,\n\t\t\tasChild = false,\n\t\t\tchildren,\n\t\t\tonClick,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t\t{...sandboxedOnClickProps({ allowClickEventDefault, onClick })}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</Component>\n\t\t);\n\t},\n);\nSandboxedOnClick.displayName = \"SandboxedOnClick\";\n\nexport {\n\t//,\n\tSandboxedOnClick,\n\tsandboxedOnClickProps,\n};\n"],"mappings":"kHAmCA,MAAM,GAAyB,CAAE,yBAAyB,GAAO,WAAwB,CAAC,KACxF,CASA,KAAM,eACN,QAAU,GAAU,CAKnB,EAAM,gBAAgB,EAWjB,GACJ,EAAM,eAAe,EAEtB,IAAU,CAAK,CAChB,CACD,GAsBK,EAAmB,GAEvB,CAEC,yBAAyB,GACzB,UAAU,GACV,WACA,UACA,GAAG,GAEJ,IAKC,EAHiB,EAAU,EAAO,MAGlC,CACM,MACL,GAAI,EACJ,GAAI,EAAsB,CAAE,yBAAwB,SAAQ,CAAC,EAE5D,UACS,CAAA,CAGd,EACA,EAAiB,YAAc"}
|