├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── improve_docs.md │ ├── feature_request.md │ └── bug_report.md ├── package ├── styles.layer.css ├── types │ ├── DataTableSelectionTrigger.ts │ ├── DataTableColumnTextAlign.ts │ ├── DataTableVerticalAlign.ts │ ├── utils.ts │ ├── DataTableRowExpansionCollapseProps.ts │ ├── DataTableDefaultColumnProps.ts │ ├── DataTableDraggableRowProps.ts │ ├── DataTableRowClickHandler.ts │ ├── DataTableOuterBorderProps.ts │ ├── DataTableColumnProps.ts │ ├── DataTableCellClickHandler.ts │ ├── DataTableSortStatus.ts │ ├── DataTableScrollProps.ts │ ├── DataTablePageSizeSelectorProps.ts │ ├── DataTableEmptyStateProps.ts │ ├── index.ts │ ├── DataTableLoaderProps.ts │ ├── PaginationRenderContext.tsx │ ├── DataTableSortProps.ts │ └── DataTableColumnGroup.ts ├── DataTableEmptyRow.tsx ├── DataTableColumnGroupHeaderCell.css ├── DataTableEmptyRow.css ├── hooks │ ├── useIsomorphicLayoutEffect.ts │ ├── useStableValue.ts │ ├── useMediaQueryStringOrFunction.ts │ ├── useLastSelectionChangeIndex.ts │ ├── index.ts │ ├── useMediaQueriesStringOrFunction.ts │ └── useRowExpansionStatus.ts ├── index.ts ├── DataTableHeaderCellFilter.css ├── DataTableFooter.css ├── DataTableHeader.css ├── DataTableRowExpansion.css ├── icons │ ├── IconX.tsx │ ├── IconSelector.tsx │ ├── IconArrowUp.tsx │ ├── IconArrowsVertical.tsx │ ├── IconFilter.tsx │ ├── IconFilterFilled.tsx │ ├── IconGripVertical.tsx │ └── IconDatabaseOff.tsx ├── DataTableFooterSelectorPlaceholderCell.tsx ├── DataTableLoader.css ├── utilityClasses.ts ├── utilityClasses.css ├── DataTableRow.css ├── styles.css ├── DataTableEmptyState.css ├── DataTableEmptyState.tsx ├── DataTableLoader.tsx ├── DataTablePageSizeSelector.css ├── DataTableRowExpansion.tsx ├── DataTableFooterSelectorPlaceholderCell.css ├── DataTableHeaderSelectorCell.css ├── DataTableColumns.context.ts ├── DataTablePagination.css ├── DataTableFooterCell.tsx ├── DataTableDraggableRow.tsx ├── DataTableRowSelectorCell.css ├── DataTableHeaderSelectorCell.tsx ├── DataTableScrollArea.tsx ├── DataTableHeaderCellFilter.tsx └── DataTableRowSelectorCell.tsx ├── public ├── googlea3479269b2f388cf.html └── users │ ├── dera.webp │ ├── pachtop.png │ ├── zipline.png │ ├── coh3-stats.png │ ├── ganymede.png │ ├── kapa-dark.png │ ├── kapa-light.png │ ├── pipedash.png │ ├── segmentx.png │ ├── markup-dark.png │ ├── markup-light.png │ ├── inventree-dark.png │ ├── inventree-light.png │ ├── leasingsh-ro-dark.png │ ├── leasingsh-ro-light.png │ ├── ccrentals-dark.svg │ ├── ccrentals-light.svg │ ├── codeparrot.svg │ ├── exdatis-dark.svg │ └── exdatis-light.svg ├── app ├── favicon.ico ├── (home) │ ├── hero.png │ ├── HeroImage.tsx │ ├── HeroImage.module.css │ ├── page.module.css │ ├── HomePageTitle.tsx │ ├── HomePageTitle.module.css │ ├── PackageUsersAnchor.tsx │ ├── Feature.module.css │ ├── Feature.tsx │ └── HomePageSubtitle.module.css ├── apple-icon.png ├── opengraph-image.png ├── getting-started │ └── examples │ │ ├── layout.css │ │ └── RootLayout.tsx ├── styling │ └── examples │ │ ├── simple │ │ ├── layout.css │ │ └── RootLayout.tsx │ │ └── fine-grained │ │ ├── postcss.config.js │ │ ├── layout.css │ │ └── RootLayout.tsx ├── examples │ ├── rtl-support │ │ └── RTLSupportExample.module.css │ ├── expanding-rows │ │ ├── RowExpansionExampleWithInlineEditor.module.css │ │ ├── RowExpansionExampleSimple.module.css │ │ ├── RowExpansionExampleCollapseProps.module.css │ │ ├── RowExpansionExampleExpandableRows.module.css │ │ ├── RowExpansionExampleTriggerAlways.module.css │ │ ├── RowExpansionExampleInitiallyExpandedRows.module.css │ │ ├── RowExpansionExampleMultipleExpandedRows.module.css │ │ ├── RowExpansionExampleWithLazyLoading.module.css │ │ ├── RowExpansionExampleControlledMode.module.css │ │ ├── RowExpansionExampleSimple.tsx │ │ ├── RowExpansionExampleTriggerAlways.tsx │ │ ├── RowExpansionExampleMultipleExpandedRows.tsx │ │ ├── RowExpansionExampleExpandableRows.tsx │ │ ├── RowExpansionExampleInitiallyExpandedRows.tsx │ │ └── RowExpansionExampleCollapseProps.tsx │ ├── row-dragging │ │ ├── RowDraggingExample.module.css │ │ └── page.tsx │ ├── complex-usage-scenario │ │ ├── ComplexUsageExample.module.css │ │ └── ComplexUsageExampleWrapper.tsx │ ├── nested-tables │ │ └── NestedTablesExample.module.css │ ├── row-styling │ │ ├── RowStylingWithClassNameExample.module.css │ │ ├── RowStylingWithStyleObjectExample.tsx │ │ ├── RowStylingWithStyleFunctionExample.tsx │ │ ├── RowStylingWithClassNameExample.tsx │ │ └── RowStylingWithColorPropertiesExample.tsx │ ├── nested-tables-with-async-data-loading │ │ └── NestedTablesAsyncExample.module.css │ ├── nested-tables-with-async-data-loading-and-sorting │ │ └── NestedTablesAsyncSortingExample.module.css │ ├── basic-usage │ │ ├── BasicUsageExample.tsx │ │ └── page.tsx │ ├── overriding-the-default-styles │ │ ├── StylingWithClassNameExample.module.css │ │ ├── StylingWithClassNameExample.tsx │ │ ├── StylingWithStyleObjectExample.tsx │ │ ├── StylingWithStyleFunctionExample.tsx │ │ ├── StylingWithStyleObjectsAndFunctionsExample.tsx │ │ ├── StylingWithClassNamesExample.module.css │ │ ├── StylingWithClassNamesExample.tsx │ │ └── ColorsExample.tsx │ ├── empty-state │ │ └── EmptyStateExamples.module.css │ ├── asynchronous-data-loading │ │ └── AsynchronousDataLoadingExamplePageContent.module.css │ ├── column-properties-and-styling │ │ ├── ColumnStylingExample.module.css │ │ └── ColumnPropertiesExample.tsx │ ├── disabling-text-selection │ │ ├── DisablingTextSelectionExample.tsx │ │ ├── DisablingTextSelectionExamplePageContent.tsx │ │ └── page.tsx │ ├── using-with-auto-animate │ │ ├── UsingWithAutoAnimateExample.module.css │ │ └── page.tsx │ ├── basic-table-properties │ │ ├── BasicTablePropertiesPageContent.module.css │ │ └── page.tsx │ ├── scrollable-vs-auto-height │ │ ├── ScrollableVsAutoHeightExamplePageContent.tsx │ │ └── ScrollableVsAutoHeightExamples.tsx │ ├── column-dragging-and-toggling │ │ ├── DraggingExample.tsx │ │ └── DraggingTogglingResetExample.tsx │ ├── non-standard-record-ids │ │ └── NonStandardRecordIdsStringExample.tsx │ ├── sorting │ │ ├── SortingExample.tsx │ │ └── SortingExampleCustomIcons.tsx │ ├── column-grouping │ │ └── ColumnGroupingExample.tsx │ ├── infinite-scrolling │ │ └── page.tsx │ ├── default-column-render │ │ ├── page.tsx │ │ └── DefaultColumnRenderExample.tsx │ ├── custom-row-or-cell-attributes │ │ └── CustomRowOrCellAttributesExamples.tsx │ ├── default-column-properties │ │ └── page.tsx │ ├── pagination │ │ ├── PaginationExampleWithPageSizeSelector.tsx │ │ ├── PaginationExampleWithControlProps.tsx │ │ └── PaginationExample.tsx │ └── handling-row-clicks │ │ └── HandlingRowClicksExample.tsx ├── opengraph-image.alt.txt ├── robots.ts ├── icon.svg ├── sitemap.ts ├── layout.module.css ├── layout.css ├── contribute-and-support │ └── ContributorsImage.tsx ├── hire-the-author │ └── page.module.css ├── manifest.webmanifest │ └── route.ts ├── type-definitions │ └── page.tsx ├── mantine-v6-support │ └── page.tsx └── docsearch-overrides.css ├── pnpm-workspace.yaml ├── scripts ├── tsconfig.tsup.json ├── tsup.dts.ts ├── tsup.cjs.ts ├── tsup.esm.ts └── generate-docs-data.mjs ├── components ├── Txt.module.css ├── HeaderLinkButtons.module.css ├── CodeBlock.module.css ├── UnorderedList.module.css ├── ColorSchemeActionIcon.module.css ├── NavbarExamples.module.css ├── PageTitle.module.css ├── VersionBadge.module.css ├── DocSearchHit.tsx ├── ExternalLink.tsx ├── DocSearchButton.tsx ├── HeaderTitle.tsx ├── UnorderedList.tsx ├── Header.module.css ├── Navbar.module.css ├── PageTitle.tsx ├── NpmNavbarLinkButton.tsx ├── CheckableSegmentedControl.module.css ├── InternalLink.tsx ├── ThemeAttributeSetter.tsx ├── HeaderTitle.module.css ├── PageSubtitle.tsx ├── ShikiCodeHighlightProvider.tsx ├── NpmHeaderLinkButton.tsx ├── PageSubtitle.module.css ├── Footer.module.css ├── AppWrapper.module.css ├── NavbarDynamicLinkButtons.tsx ├── ColorSchemeActionIcon.tsx ├── PageNavigation.module.css ├── Txt.tsx ├── HeaderLinkButtons.tsx ├── Header.tsx ├── TrustedBy.module.css ├── AppWrapper.tsx ├── CheckableSegmentedControl.tsx ├── NavbarButton.module.css ├── VersionBadge.tsx ├── PageNavigation.tsx └── NavbarExamples.tsx ├── lib ├── constants.ts ├── useNpmDownloads.ts └── examples.ts ├── .prettierrc ├── types.d.ts ├── .gitignore ├── .vscode └── settings.json ├── postcss.config.js ├── data ├── nested.ts └── index.ts ├── tsconfig.json ├── eslint.config.mjs ├── LICENSE └── next.config.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: icflorescu 2 | -------------------------------------------------------------------------------- /package/styles.layer.css: -------------------------------------------------------------------------------- 1 | @import './styles.css' layer(mantine-datatable); 2 | -------------------------------------------------------------------------------- /public/googlea3479269b2f388cf.html: -------------------------------------------------------------------------------- 1 | google-site-verification: googlea3479269b2f388cf.html 2 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /app/(home)/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/app/(home)/hero.png -------------------------------------------------------------------------------- /app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/app/apple-icon.png -------------------------------------------------------------------------------- /package/types/DataTableSelectionTrigger.ts: -------------------------------------------------------------------------------- 1 | export type DataTableSelectionTrigger = 'cell' | 'checkbox'; 2 | -------------------------------------------------------------------------------- /public/users/dera.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/dera.webp -------------------------------------------------------------------------------- /app/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/app/opengraph-image.png -------------------------------------------------------------------------------- /package/types/DataTableColumnTextAlign.ts: -------------------------------------------------------------------------------- 1 | export type DataTableColumnTextAlign = 'left' | 'center' | 'right'; 2 | -------------------------------------------------------------------------------- /package/types/DataTableVerticalAlign.ts: -------------------------------------------------------------------------------- 1 | export type DataTableVerticalAlign = 'top' | 'center' | 'bottom'; 2 | -------------------------------------------------------------------------------- /public/users/pachtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/pachtop.png -------------------------------------------------------------------------------- /public/users/zipline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/zipline.png -------------------------------------------------------------------------------- /public/users/coh3-stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/coh3-stats.png -------------------------------------------------------------------------------- /public/users/ganymede.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/ganymede.png -------------------------------------------------------------------------------- /public/users/kapa-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/kapa-dark.png -------------------------------------------------------------------------------- /public/users/kapa-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/kapa-light.png -------------------------------------------------------------------------------- /public/users/pipedash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/pipedash.png -------------------------------------------------------------------------------- /public/users/segmentx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/segmentx.png -------------------------------------------------------------------------------- /public/users/markup-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/markup-dark.png -------------------------------------------------------------------------------- /public/users/markup-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/markup-light.png -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - esbuild 3 | - sharp 4 | - unrs-resolver 5 | packages: 6 | - package 7 | -------------------------------------------------------------------------------- /public/users/inventree-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/inventree-dark.png -------------------------------------------------------------------------------- /public/users/inventree-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/inventree-light.png -------------------------------------------------------------------------------- /public/users/leasingsh-ro-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/leasingsh-ro-dark.png -------------------------------------------------------------------------------- /public/users/leasingsh-ro-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icflorescu/mantine-datatable/HEAD/public/users/leasingsh-ro-light.png -------------------------------------------------------------------------------- /scripts/tsconfig.tsup.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /components/Txt.module.css: -------------------------------------------------------------------------------- 1 | .alertMessage { 2 | line-height: 1.6; 3 | } 4 | 5 | .alertMessageWithoutTitle { 6 | margin-top: 0; 7 | } 8 | -------------------------------------------------------------------------------- /lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const MANTINE_SIZES = ['xs', 'sm', 'md', 'lg', 'xl']; 2 | export const LOADER_TYPES = ['oval', 'bars', 'dots']; 3 | -------------------------------------------------------------------------------- /app/getting-started/examples/layout.css: -------------------------------------------------------------------------------- 1 | /* 👇 Make sure the styles are applied in the correct order */ 2 | @layer mantine, mantine-datatable; 3 | -------------------------------------------------------------------------------- /app/styling/examples/simple/layout.css: -------------------------------------------------------------------------------- 1 | /* 👇 Make sure the styles are applied IN THE CORRECT order */ 2 | @layer mantine, mantine-datatable; 3 | -------------------------------------------------------------------------------- /app/examples/rtl-support/RTLSupportExample.module.css: -------------------------------------------------------------------------------- 1 | .buttonSectionRtl { 2 | margin-left: var(--mantine-spacing-xs); 3 | margin-right: 0; 4 | } 5 | -------------------------------------------------------------------------------- /app/opengraph-image.alt.txt: -------------------------------------------------------------------------------- 1 | Mantine DataTable is a lightweight, dependency-free component that brings datagrid-like functionality to your data-rich user interfaces 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "quoteProps": "as-needed", 5 | "printWidth": 120, 6 | "trailingComma": "es5", 7 | "singleQuote": true 8 | } 9 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleWithInlineEditor.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 3 | } 4 | -------------------------------------------------------------------------------- /package/DataTableEmptyRow.tsx: -------------------------------------------------------------------------------- 1 | export function DataTableEmptyRow() { 2 | return ( 3 | 4 | 5 | 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /package/DataTableColumnGroupHeaderCell.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-column-group-header-cell--needs-border { 2 | border-inline-end: 1px solid var(--mantine-datatable-row-border-color); 3 | } 4 | -------------------------------------------------------------------------------- /package/DataTableEmptyRow.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-empty-row { 2 | &, 3 | .mantine-datatable-table[data-highlight-on-hover] tbody &:hover { 4 | background: transparent; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /components/HeaderLinkButtons.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: none; 3 | @media (min-width: $mantine-breakpoint-sm) { 4 | display: flex; 5 | gap: var(--mantine-spacing-xs); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /package/hooks/useIsomorphicLayoutEffect.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useLayoutEffect } from 'react'; 2 | 3 | export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; 4 | -------------------------------------------------------------------------------- /app/examples/row-dragging/RowDraggingExample.module.css: -------------------------------------------------------------------------------- 1 | .draggableRow[data-is-dragging='true'] td { 2 | opacity: 0.75; 3 | border: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); 4 | } 5 | -------------------------------------------------------------------------------- /scripts/tsup.dts.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: ['package/index.ts'], 5 | dts: { only: true, compilerOptions: { incremental: false } }, 6 | clean: false, 7 | }); 8 | -------------------------------------------------------------------------------- /package/types/utils.ts: -------------------------------------------------------------------------------- 1 | export type WithOptionalProperty = Pick, K> & Omit; 2 | 3 | export type WithRequiredProperty = Type & { 4 | [Property in Key]-?: Type[Property]; 5 | }; 6 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | namespace NodeJS { 2 | interface ProcessEnv extends NodeJS.ProcessEnv { 3 | GITHUB_PAGES: 'TRUE' | 'FALSE'; 4 | PACKAGE_NAME: 'mantine-datatable'; 5 | PACKAGE_VERSION: string; 6 | INITIAL_NPM_DOWNLOADS: string; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package/index.ts: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | export { DataTable } from './DataTable'; 4 | export { DataTableDraggableRow } from './DataTableDraggableRow'; 5 | export * from './hooks/useDataTableColumns'; 6 | export * from './types'; 7 | export * from './utils'; 8 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleSimple.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 4 | } 5 | 6 | .label { 7 | width: rem(130px); 8 | } 9 | -------------------------------------------------------------------------------- /components/CodeBlock.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | overflow: hidden; 3 | border-radius: var(--mantine-radius-sm); 4 | border: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-5)); 5 | margin-block: var(--mantine-spacing-md); 6 | } 7 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleCollapseProps.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 4 | } 5 | 6 | .label { 7 | width: rem(130px); 8 | } 9 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleExpandableRows.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 4 | } 5 | 6 | .label { 7 | width: rem(130px); 8 | } 9 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleTriggerAlways.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 4 | } 5 | 6 | .label { 7 | width: rem(130px); 8 | } 9 | -------------------------------------------------------------------------------- /package/DataTableHeaderCellFilter.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-header-cell-filter-action-icon { 2 | border: 0; 3 | color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); 4 | 5 | &[data-active] { 6 | color: var(--mantine-color-text); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package/types/DataTableRowExpansionCollapseProps.ts: -------------------------------------------------------------------------------- 1 | import type { CollapseProps } from '@mantine/core'; 2 | 3 | export type DataTableRowExpansionCollapseProps = Pick< 4 | CollapseProps, 5 | 'animateOpacity' | 'transitionDuration' | 'transitionTimingFunction' 6 | >; 7 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleInitiallyExpandedRows.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 4 | } 5 | 6 | .label { 7 | width: rem(130px); 8 | } 9 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleMultipleExpandedRows.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 4 | } 5 | 6 | .label { 7 | width: rem(130px); 8 | } 9 | -------------------------------------------------------------------------------- /package/DataTableFooter.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-footer { 2 | z-index: 2; 3 | position: var(--mantine-datatable-footer-position); 4 | bottom: var(--mantine-datatable-footer-bottom); 5 | th { 6 | border-top: rem(1px) solid var(--mantine-datatable-border-color); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package/types/DataTableDefaultColumnProps.ts: -------------------------------------------------------------------------------- 1 | import type { DataTableColumn } from './DataTableColumn'; 2 | 3 | export type DataTableDefaultColumnProps> = Omit< 4 | DataTableColumn, 5 | 'accessor' | 'hidden' | 'visibleMediaQuery' | 'render' 6 | >; 7 | -------------------------------------------------------------------------------- /components/UnorderedList.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | margin: var(--mantine-spacing-md) 0 var(--mantine-spacing-md) var(--mantine-spacing-sm); 3 | padding-left: var(--mantine-spacing-md); 4 | &:not(.compact) { 5 | li { 6 | margin: var(--mantine-spacing-xs) 0; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/tsup.cjs.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: ['package/index.ts'], 5 | format: 'cjs', 6 | tsconfig: 'scripts/tsconfig.tsup.json', 7 | target: 'esnext', 8 | minify: true, 9 | sourcemap: true, 10 | clean: false, 11 | }); 12 | -------------------------------------------------------------------------------- /scripts/tsup.esm.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: ['package/index.ts'], 5 | format: 'esm', 6 | tsconfig: 'scripts/tsconfig.tsup.json', 7 | target: 'esnext', 8 | minify: true, 9 | sourcemap: true, 10 | clean: false, 11 | }); 12 | -------------------------------------------------------------------------------- /components/ColorSchemeActionIcon.module.css: -------------------------------------------------------------------------------- 1 | .dark { 2 | @mixin dark { 3 | display: none; 4 | } 5 | 6 | @mixin light { 7 | display: block; 8 | } 9 | } 10 | 11 | .light { 12 | @mixin light { 13 | display: none; 14 | } 15 | 16 | @mixin dark { 17 | display: block; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/examples/complex-usage-scenario/ComplexUsageExample.module.css: -------------------------------------------------------------------------------- 1 | .modalHeader { 2 | border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); 3 | } 4 | 5 | .modalTitle { 6 | color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-2)); 7 | font-weight: 700; 8 | } 9 | -------------------------------------------------------------------------------- /app/examples/nested-tables/NestedTablesExample.module.css: -------------------------------------------------------------------------------- 1 | .icon { 2 | width: rem(13px); 3 | height: auto; 4 | vertical-align: rem(-1px); 5 | margin-right: rem(8px); 6 | } 7 | 8 | .expandIcon { 9 | transition: transform 0.2s; 10 | } 11 | 12 | .expandIconRotated { 13 | transform: rotate(90deg); 14 | } 15 | -------------------------------------------------------------------------------- /package/DataTableHeader.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-header { 2 | z-index: 2; 3 | position: sticky; 4 | top: 0; 5 | 6 | th { 7 | border-bottom: rem(1px) solid var(--mantine-datatable-border-color); 8 | } 9 | } 10 | 11 | .mantine-datatable-header-column-toggle-checkbox-label { 12 | user-select: none; 13 | } 14 | -------------------------------------------------------------------------------- /public/users/ccrentals-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/users/ccrentals-light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/examples/row-styling/RowStylingWithClassNameExample.module.css: -------------------------------------------------------------------------------- 1 | .redRow { 2 | color: var(--mantine-color-red-6); 3 | background-color: light-dark( 4 | color-mix(in srgb, var(--mantine-color-red-6) 10%, var(--mantine-color-white)), 5 | color-mix(in srgb, var(--mantine-color-red-6) 30%, var(--mantine-color-black)) 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # next.js 5 | /.next/ 6 | /out/ 7 | 8 | # misc 9 | .DS_Store 10 | 11 | # typescript 12 | *.tsbuildinfo 13 | next-env.d.ts 14 | 15 | # service worker 16 | public/sw.js 17 | public/sw.js.map 18 | public/workbox-*.js 19 | public/workbox-*.js.map 20 | 21 | # build output 22 | /dist/ 23 | -------------------------------------------------------------------------------- /components/NavbarExamples.module.css: -------------------------------------------------------------------------------- 1 | .line { 2 | position: absolute; 3 | top: rem(-10px); 4 | bottom: rem(18px); 5 | left: rem(29px); 6 | width: rem(1px); 7 | z-index: -1; 8 | transform: scaleY(0); 9 | transform-origin: top; 10 | transition: transform; 11 | } 12 | 13 | .lineVisible { 14 | transform: scaleY(1); 15 | } 16 | -------------------------------------------------------------------------------- /package/types/DataTableDraggableRowProps.ts: -------------------------------------------------------------------------------- 1 | import type { TableTrProps } from '@mantine/core'; 2 | 3 | export type DataTableDraggableRowProps = { 4 | /** 5 | * Optional class name. 6 | */ 7 | className?: string; 8 | 9 | /** 10 | * Current dragging status. 11 | */ 12 | isDragging?: boolean; 13 | } & TableTrProps; 14 | -------------------------------------------------------------------------------- /app/examples/nested-tables-with-async-data-loading/NestedTablesAsyncExample.module.css: -------------------------------------------------------------------------------- 1 | .icon { 2 | width: rem(13px); 3 | height: auto; 4 | vertical-align: rem(-1px); 5 | margin-right: rem(8px); 6 | } 7 | 8 | .expandIcon { 9 | transition: transform 0.2s; 10 | } 11 | 12 | .expandIconRotated { 13 | transform: rotate(90deg); 14 | } 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/improve_docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Improve docs 3 | about: Improve documentation website or JSDoc comments in the codebase 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the improvement you'd like** 10 | A clear and concise description of what you think is missing or how we could improve the documentation. 11 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleWithLazyLoading.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | position: relative; 4 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 5 | } 6 | 7 | .label { 8 | width: rem(180px); 9 | } 10 | 11 | .number { 12 | width: rem(50px); 13 | } 14 | -------------------------------------------------------------------------------- /components/PageTitle.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); 3 | font-size: 1.5rem; 4 | margin: 0.25em 0 1em; 5 | @media (min-width: $mantine-breakpoint-sm) { 6 | font-size: 1.75rem; 7 | } 8 | @media (min-width: $mantine-breakpoint-md) { 9 | font-size: 2rem; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package/hooks/useStableValue.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | 3 | export interface ReadonlyRef { 4 | readonly current: T; 5 | } 6 | 7 | export function useStableValue(value: T): ReadonlyRef { 8 | const ref = useRef(value); 9 | // eslint-disable-next-line react-hooks/refs 10 | ref.current = value; 11 | return ref; 12 | } 13 | -------------------------------------------------------------------------------- /app/examples/nested-tables-with-async-data-loading-and-sorting/NestedTablesAsyncSortingExample.module.css: -------------------------------------------------------------------------------- 1 | .icon { 2 | width: rem(13px); 3 | height: auto; 4 | vertical-align: rem(-1px); 5 | margin-right: rem(8px); 6 | } 7 | 8 | .expandIcon { 9 | transition: transform 0.2s; 10 | } 11 | 12 | .expandIconRotated { 13 | transform: rotate(90deg); 14 | } 15 | -------------------------------------------------------------------------------- /package/types/DataTableRowClickHandler.ts: -------------------------------------------------------------------------------- 1 | export type DataTableRowClickHandler> = (params: { 2 | /** 3 | * Click event. 4 | */ 5 | event: React.MouseEvent; 6 | 7 | /** 8 | * Clicked record. 9 | */ 10 | record: T; 11 | 12 | /** 13 | * Clicked record index. 14 | */ 15 | index: number; 16 | }) => void; 17 | -------------------------------------------------------------------------------- /components/VersionBadge.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: none; 3 | @media (min-width: $mantine-breakpoint-sm) { 4 | display: flex; 5 | cursor: pointer; 6 | text-transform: none; 7 | &:active { 8 | transform: translateY(1px); 9 | } 10 | } 11 | } 12 | 13 | .linkIcon { 14 | margin-left: rem(2px); 15 | vertical-align: rem(-2px); 16 | } 17 | -------------------------------------------------------------------------------- /app/robots.ts: -------------------------------------------------------------------------------- 1 | import type { MetadataRoute } from 'next'; 2 | import { WEBSITE_LINK } from './config'; 3 | 4 | export const dynamic = 'force-static'; 5 | 6 | export default function robots(): MetadataRoute.Robots { 7 | return { 8 | rules: { 9 | userAgent: '*', 10 | allow: '/', 11 | }, 12 | sitemap: `${WEBSITE_LINK}/sitemap.xml`, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /app/styling/examples/fine-grained/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | // 👇 the postcss-import plugin must come first; 4 | // make sure to install it (`npm i -D postcss-import`) 5 | 'postcss-import': {}, 6 | 'postcss-preset-mantine': {}, 7 | 'postcss-simple-vars': { 8 | // your Mantine variables here... 9 | }, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /app/(home)/HeroImage.tsx: -------------------------------------------------------------------------------- 1 | import { PRODUCT_NAME } from '~/app/config'; 2 | import classes from './HeroImage.module.css'; 3 | import picture from './hero.png'; 4 | 5 | export function HeroImage() { 6 | return ( 7 |
8 | {PRODUCT_NAME} 9 |
10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /app/examples/basic-usage/BasicUsageExample.tsx: -------------------------------------------------------------------------------- 1 | import { DataTable } from '__PACKAGE__'; 2 | import companies from '~/data/companies.json'; 3 | 4 | export function BasicUsageExample() { 5 | return ( 6 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /app/examples/overriding-the-default-styles/StylingWithClassNameExample.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background: light-dark( 3 | color-mix(in srgb, var(--mantine-color-red-6) 5%, var(--mantine-color-body)), 4 | color-mix(in srgb, var(--mantine-color-red-6) 10%, var(--mantine-color-body)) 5 | ); 6 | border: 1px dashed var(--mantine-color-red-6); 7 | border-radius: var(--mantine-radius-md); 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.organizeImports": "explicit", 4 | "source.fixAll.eslint": "explicit" 5 | }, 6 | "files.associations": { 7 | "*.css": "postcss" 8 | }, 9 | "editor.unicodeHighlight.ambiguousCharacters": false, 10 | "postcss.validate": false, 11 | "cssVariables.lookupFiles": ["node_modules/@mantine/core/esm/index.css"] 12 | } 13 | -------------------------------------------------------------------------------- /app/examples/empty-state/EmptyStateExamples.module.css: -------------------------------------------------------------------------------- 1 | .noRecordsBox { 2 | font-size: 0; 3 | color: light-dark(var(--mantine-colors-dark-3), var(--mantine-colors-gray-5)); 4 | border: 2px solid light-dark(var(--mantine-colors-gray-4), var(--mantine-colors-dark-5)); 5 | border-radius: var(--mantine-radius-md); 6 | background: light-dark(var(--mantine-colors-gray-1), var(--mantine-colors-dark-6)); 7 | } 8 | -------------------------------------------------------------------------------- /package/DataTableRowExpansion.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-row-expansion-cell { 2 | padding: 0; 3 | } 4 | 5 | .mantine-datatable-row-expansion-cell-content { 6 | tr:not(:last-of-type) & { 7 | border-bottom: rem(1px) solid var(--mantine-datatable-row-border-color); 8 | } 9 | border-top: rem(1px) solid var(--mantine-datatable-row-border-color); 10 | [data-with-row-border] & { 11 | border-top: 0; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/styling/examples/fine-grained/layout.css: -------------------------------------------------------------------------------- 1 | /* 👇 Expressly import the mantine core styles into mantine-core layer */ 2 | @import '@mantine/core/styles.css' layer(mantine-core); 3 | 4 | /* 👇 Expressly import the mantine-datatable styles into mantine-datatable layer */ 5 | @import '__PACKAGE__/dist/styles.css' layer(mantine-datatable); 6 | 7 | /* 👇 Make sure the styles are applied in the correct order */ 8 | @layer mantine-core, mantine-datatable; 9 | -------------------------------------------------------------------------------- /components/DocSearchHit.tsx: -------------------------------------------------------------------------------- 1 | import type { Route } from 'next'; 2 | import Link from 'next/link'; 3 | import { WEBSITE_LINK } from '~/app/config'; 4 | 5 | type SearhcHitProps = React.PropsWithChildren<{ 6 | hit: { url: string }; 7 | }>; 8 | 9 | const PREFIX_LENGTH = WEBSITE_LINK.length; 10 | 11 | export function DocSearchHit({ hit: { url }, children }: SearhcHitProps) { 12 | return {children}; 13 | } 14 | -------------------------------------------------------------------------------- /app/(home)/HeroImage.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | font-size: 0; 3 | overflow: hidden; 4 | border-radius: var(--mantine-radius-sm); 5 | border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4)); 6 | position: relative; 7 | } 8 | 9 | .rightShadow { 10 | position: absolute; 11 | top: 0; 12 | right: 0; 13 | bottom: 0; 14 | width: rem(10px); 15 | background: linear-gradient(to left, rgba(0, 0, 0, 0.5), transparent); 16 | } 17 | -------------------------------------------------------------------------------- /components/ExternalLink.tsx: -------------------------------------------------------------------------------- 1 | import { Anchor } from '@mantine/core'; 2 | 3 | export type ExternalLinkProps = React.PropsWithChildren<{ 4 | className?: string; 5 | to: string; 6 | rel?: string; 7 | }>; 8 | 9 | export function ExternalLink({ className, to, rel, children }: ExternalLinkProps) { 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /package/icons/IconX.tsx: -------------------------------------------------------------------------------- 1 | export function IconX() { 2 | return ( 3 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /app/(home)/page.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | width: 100%; 3 | 4 | @media (min-width: $mantine-breakpoint-md) { 5 | width: 40%; 6 | flex: 1 1 auto; 7 | } 8 | } 9 | 10 | .buttonHalf { 11 | @media (min-width: rem(420px)) { 12 | width: calc(50% - var(--mantine-spacing-md) / 2); 13 | } 14 | @media (min-width: $mantine-breakpoint-md) { 15 | width: 20%; 16 | flex: 1 1 auto; 17 | } 18 | } 19 | 20 | .buttonLabel { 21 | margin-bottom: rem(-2px); 22 | } 23 | -------------------------------------------------------------------------------- /components/DocSearchButton.tsx: -------------------------------------------------------------------------------- 1 | import { DocSearch } from '@docsearch/react'; 2 | import { DOCSEARCH_API_KEY, DOCSEARCH_APP_ID, DOCSEARCH_INDEX_NAME } from '~/app/config'; 3 | import { DocSearchHit } from './DocSearchHit'; 4 | 5 | export function DocSearchButton() { 6 | return ( 7 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /package/hooks/useMediaQueryStringOrFunction.ts: -------------------------------------------------------------------------------- 1 | import { useMantineTheme, type MantineTheme } from '@mantine/core'; 2 | import { useMediaQuery } from '@mantine/hooks'; 3 | 4 | export function useMediaQueryStringOrFunction(mediaQuery: string | ((theme: MantineTheme) => string) | undefined) { 5 | const theme = useMantineTheme(); 6 | const mediaQueryValue = typeof mediaQuery === 'function' ? mediaQuery(theme) : mediaQuery; 7 | return useMediaQuery(mediaQueryValue || '', true); 8 | } 9 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'postcss-preset-mantine': {}, 5 | 'postcss-simple-vars': { 6 | variables: { 7 | 'mantine-breakpoint-xs': '36em', 8 | 'mantine-breakpoint-sm': '48em', 9 | 'mantine-breakpoint-md': '62em', 10 | 'mantine-breakpoint-lg': '75em', 11 | 'mantine-breakpoint-xl': '88em', 12 | }, 13 | }, 14 | cssnano: { preset: 'default' }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /package/icons/IconSelector.tsx: -------------------------------------------------------------------------------- 1 | export function IconSelector() { 2 | return ( 3 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /package/DataTableFooterSelectorPlaceholderCell.tsx: -------------------------------------------------------------------------------- 1 | import { TableTh } from '@mantine/core'; 2 | 3 | type DataTableFooterSelectorPlaceholderCellProps = { 4 | shadowVisible: boolean; 5 | }; 6 | 7 | export function DataTableFooterSelectorPlaceholderCell({ shadowVisible }: DataTableFooterSelectorPlaceholderCellProps) { 8 | return ( 9 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /package/types/DataTableOuterBorderProps.ts: -------------------------------------------------------------------------------- 1 | import type { MantineSize } from '@mantine/core'; 2 | 3 | export type DataTableOuterBorderProps = 4 | | { 5 | withTableBorder?: never; 6 | borderRadius?: never; 7 | } 8 | | { 9 | /** 10 | * If true, table will have border. 11 | */ 12 | withTableBorder: boolean; 13 | 14 | /** 15 | * Table border radius. 16 | */ 17 | borderRadius?: MantineSize | (string & NonNullable) | number; 18 | }; 19 | -------------------------------------------------------------------------------- /app/styling/examples/fine-grained/RootLayout.tsx: -------------------------------------------------------------------------------- 1 | import { ColorSchemeScript, MantineProvider } from '@mantine/core'; 2 | 3 | import './layout.css'; 4 | 5 | export function RootLayout({ children }: { children: React.ReactNode }) { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /package/icons/IconArrowUp.tsx: -------------------------------------------------------------------------------- 1 | export function IconArrowUp() { 2 | return ( 3 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /components/HeaderTitle.tsx: -------------------------------------------------------------------------------- 1 | import { Title } from '@mantine/core'; 2 | import { IconTable } from '@tabler/icons-react'; 3 | import Link from 'next/link'; 4 | import { PRODUCT_NAME } from '~/app/config'; 5 | import classes from './HeaderTitle.module.css'; 6 | 7 | export function HeaderTitle() { 8 | return ( 9 | 10 | 11 | 12 | {PRODUCT_NAME} 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /package/icons/IconArrowsVertical.tsx: -------------------------------------------------------------------------------- 1 | export function IconArrowsVertical() { 2 | return ( 3 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /package/icons/IconFilter.tsx: -------------------------------------------------------------------------------- 1 | export function IconFilter() { 2 | return ( 3 | 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /app/(home)/HomePageTitle.tsx: -------------------------------------------------------------------------------- 1 | import { Text, Title } from '@mantine/core'; 2 | import classes from './HomePageTitle.module.css'; 3 | 4 | export function HomePageTitle() { 5 | return ( 6 | 7 | The{' '} 8 | <Text className="nowrap" span inherit variant="gradient" gradient={{ from: 'blue', to: 'cyan', deg: 30 }}> 9 | table component 10 | </Text> 11 | <br /> 12 | for your data-rich 13 | <br /> 14 | Mantine applications 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /package/DataTableLoader.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-loader { 2 | z-index: 3; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | right: 0; 7 | bottom: 0; 8 | pointer-events: none; 9 | background: alpha(var(--mantine-datatable-background-color), 0.75); 10 | opacity: 0; 11 | transition: opacity 0.2s; 12 | padding-top: var(--mantine-datatable-header-height, 0); 13 | padding-bottom: var(--mantine-datatable-footer-height, 0); 14 | } 15 | 16 | .mantine-datatable-loader-fetching { 17 | pointer-events: all; 18 | opacity: 1; 19 | } 20 | -------------------------------------------------------------------------------- /package/types/DataTableColumnProps.ts: -------------------------------------------------------------------------------- 1 | import type { DataTableColumn } from './DataTableColumn'; 2 | import type { DataTableColumnGroup } from './DataTableColumnGroup'; 3 | 4 | export type DataTableColumnProps> = 5 | | { 6 | /** 7 | * Grouped columns. 8 | */ 9 | groups: DataTableColumnGroup[]; 10 | 11 | columns?: never; 12 | } 13 | | { 14 | groups?: never; 15 | 16 | /** 17 | * Visible columns. 18 | */ 19 | columns: DataTableColumn[]; 20 | }; 21 | -------------------------------------------------------------------------------- /components/UnorderedList.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import type { PropsWithChildren } from 'react'; 3 | import classes from './UnorderedList.module.css'; 4 | 5 | export type UnorderedListProps = PropsWithChildren<{ 6 | compact?: boolean; 7 | className?: string; 8 | }>; 9 | 10 | export function UnorderedList({ children, compact, className, ...otherProps }: UnorderedListProps) { 11 | return ( 12 |
    13 | {children} 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /app/(home)/HomePageTitle.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-1)); 3 | text-indent: -0.1em; 4 | margin: 0.25em 0 0 0.1em; 5 | line-height: 1.15; 6 | font-size: 8vw; 7 | @media (min-width: $mantine-breakpoint-sm) { 8 | margin-top: 0.125em; 9 | font-size: rem(42px); 10 | } 11 | @media (min-width: $mantine-breakpoint-md) { 12 | font-size: rem(64px); 13 | margin-top: 0.5em; 14 | } 15 | @media (min-width: $mantine-breakpoint-lg) { 16 | font-size: rem(80px); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /components/Header.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background: linear-gradient(var(--mantine-color-body), alpha(var(--mantine-color-body), 0.75)); 3 | backdrop-filter: blur(3px); 4 | 5 | &::after { 6 | position: absolute; 7 | content: ''; 8 | left: 0; 9 | right: 0; 10 | height: rem(6px); 11 | bottom: rem(-7px); 12 | background: linear-gradient(light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.45)), rgba(0, 0, 0, 0)); 13 | transition: opacity 0.2s; 14 | opacity: 0; 15 | } 16 | } 17 | 18 | .scrolled::after { 19 | opacity: 1; 20 | } 21 | -------------------------------------------------------------------------------- /package/utilityClasses.ts: -------------------------------------------------------------------------------- 1 | export const NOWRAP = 'mantine-datatable-nowrap'; 2 | export const ELLIPSIS = 'mantine-datatable-ellipsis'; 3 | export const POINTER_CURSOR = 'mantine-datatable-pointer-cursor'; 4 | export const CONTEXT_MENU_CURSOR = 'mantine-datatable-context-menu-cursor'; 5 | export const TEXT_SELECTION_DISABLED = 'mantine-datatable-text-selection-disabled'; 6 | export const TEXT_ALIGN_LEFT = 'mantine-datatable-text-align-left'; 7 | export const TEXT_ALIGN_CENTER = 'mantine-datatable-text-align-center'; 8 | export const TEXT_ALIGN_RIGHT = 'mantine-datatable-text-align-right'; 9 | -------------------------------------------------------------------------------- /package/hooks/useLastSelectionChangeIndex.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export function useLastSelectionChangeIndex(recordIds: unknown[] | undefined) { 4 | const [lastSelectionChangeIndex, setLastSelectionChangeIndex] = useState(null); 5 | const recordIdsString = recordIds?.join(':') || ''; 6 | useEffect(() => { 7 | // eslint-disable-next-line react-hooks/set-state-in-effect 8 | setLastSelectionChangeIndex(null); 9 | }, [recordIdsString]); 10 | 11 | return { lastSelectionChangeIndex, setLastSelectionChangeIndex }; 12 | } 13 | -------------------------------------------------------------------------------- /components/Navbar.module.css: -------------------------------------------------------------------------------- 1 | .scrollShadow { 2 | position: absolute; 3 | content: ''; 4 | left: 0; 5 | right: 0; 6 | height: rem(6px); 7 | transition: opacity 0.2s ease; 8 | opacity: 0; 9 | } 10 | 11 | .scrollShadowTop { 12 | top: 0; 13 | background: linear-gradient(light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.45)), rgba(0, 0, 0, 0)); 14 | } 15 | 16 | .scrollShadowBottom { 17 | background: linear-gradient(rgba(0, 0, 0, 0), light-dark(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.45))); 18 | bottom: 0; 19 | } 20 | 21 | .scrollShadowVisible { 22 | opacity: 1; 23 | } 24 | -------------------------------------------------------------------------------- /package/types/DataTableCellClickHandler.ts: -------------------------------------------------------------------------------- 1 | import type { DataTableColumn } from './DataTableColumn'; 2 | 3 | export type DataTableCellClickHandler> = (params: { 4 | /** 5 | * Click event. 6 | */ 7 | event: React.MouseEvent; 8 | 9 | /** 10 | * Clicked record. 11 | */ 12 | record: T; 13 | /** 14 | * Clicked record index. 15 | */ 16 | index: number; 17 | /** 18 | * Clicked column information. 19 | */ 20 | column: DataTableColumn; 21 | /** 22 | * Clicked column index. 23 | */ 24 | columnIndex: number; 25 | }) => void; 26 | -------------------------------------------------------------------------------- /package/types/DataTableSortStatus.ts: -------------------------------------------------------------------------------- 1 | export type DataTableSortStatus> = { 2 | /** 3 | * Sort column key for nested values. 4 | * @type {string} 5 | */ 6 | sortKey?: string; 7 | /** 8 | * Sort column accessor. 9 | * You can use dot-notation for nested objects property drilling 10 | * (i.e. `department.name` or `department.company.name`). 11 | */ 12 | columnAccessor: keyof T | (string & NonNullable); 13 | 14 | /** 15 | * Sort direction - `asc` for ascending, `desc` for descending. 16 | */ 17 | direction: 'asc' | 'desc'; 18 | }; 19 | -------------------------------------------------------------------------------- /components/PageTitle.tsx: -------------------------------------------------------------------------------- 1 | import { Title } from '@mantine/core'; 2 | import type { Route } from 'next'; 3 | import { getRouteTitle } from '~/lib/utils'; 4 | import classes from './PageTitle.module.css'; 5 | 6 | export type PageTitleProps = 7 | | { 8 | of: Route; 9 | children?: never; 10 | } 11 | | { 12 | of?: never; 13 | children: React.ReactNode; 14 | }; 15 | 16 | export function PageTitle({ of, children }: PageTitleProps) { 17 | return ( 18 | 19 | {children || getRouteTitle(of!)} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /app/(home)/PackageUsersAnchor.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Anchor } from '@mantine/core'; 4 | 5 | export type PackageUsersAnchorProps = React.PropsWithChildren<{ 6 | className?: string; 7 | }>; 8 | 9 | export function PackageUsersAnchor({ children, className }: PackageUsersAnchorProps) { 10 | const handlePackageUsersLinkClick = () => { 11 | window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); 12 | }; 13 | 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /app/getting-started/examples/RootLayout.tsx: -------------------------------------------------------------------------------- 1 | import { ColorSchemeScript, MantineProvider } from '@mantine/core'; 2 | 3 | import '@mantine/core/styles.layer.css'; 4 | import '__PACKAGE__/styles.layer.css'; 5 | import './layout.css'; 6 | 7 | export default function RootLayout({ children }: { children: React.ReactNode }) { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | {children} 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /components/NpmNavbarLinkButton.tsx: -------------------------------------------------------------------------------- 1 | import { IconBrandNpm } from '@tabler/icons-react'; 2 | import { NPM_LINK, PRODUCT_NAME } from '~/app/config'; 3 | import { useNpmDownloads } from '~/lib/useNpmDownloads'; 4 | import { NavbarButton } from './NavbarButton'; 5 | 6 | export function NpmNavbarLinkButton() { 7 | const downloads = useNpmDownloads(); 8 | return ( 9 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /components/CheckableSegmentedControl.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: flex; 3 | gap: rem(4px); 4 | flex-direction: column; 5 | align-items: flex-start; 6 | @media (min-width: 404px) { 7 | flex-direction: row; 8 | align-items: center; 9 | } 10 | } 11 | 12 | .label { 13 | width: rem(130px); 14 | white-space: nowrap; 15 | overflow: hidden; 16 | text-overflow: ellipsis; 17 | align-self: flex-start; 18 | @media (min-width: 404px) { 19 | align-self: auto; 20 | } 21 | } 22 | 23 | .inputs { 24 | display: flex; 25 | align-items: center; 26 | gap: var(--mantine-spacing-xs); 27 | } 28 | -------------------------------------------------------------------------------- /lib/useNpmDownloads.ts: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import useSWR from 'swr'; 4 | import { DOWNLOADS_REFRESH_INTERVAL } from '~/app/config'; 5 | 6 | const fetcher = (url: string) => fetch(url).then((res) => res.json()); 7 | 8 | export function useNpmDownloads() { 9 | const { data } = useSWR<{ downloads: number }>( 10 | `https://api.npmjs.org/downloads/point/last-month/${process.env.PACKAGE_NAME}`, 11 | fetcher, 12 | { 13 | refreshInterval: DOWNLOADS_REFRESH_INTERVAL, 14 | } 15 | ); 16 | 17 | return `${((data?.downloads || Number(process.env.INITIAL_NPM_DOWNLOADS)) / 1000).toFixed(1)}k/mo`; 18 | } 19 | -------------------------------------------------------------------------------- /package/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useColumnResize'; 2 | export * from './useDataTableColumnReorder'; 3 | export * from './useDataTableColumnResize'; 4 | export * from './useDataTableColumns'; 5 | export * from './useDataTableColumnToggle'; 6 | export * from './useDataTableInjectCssVariables'; 7 | export * from './useIsomorphicLayoutEffect'; 8 | export * from './useLastSelectionChangeIndex'; 9 | export * from './useMediaQueries'; 10 | export * from './useMediaQueriesStringOrFunction'; 11 | export * from './useMediaQueryStringOrFunction'; 12 | export * from './useRowExpansion'; 13 | export * from './useRowExpansionStatus'; 14 | -------------------------------------------------------------------------------- /app/examples/asynchronous-data-loading/AsynchronousDataLoadingExamplePageContent.module.css: -------------------------------------------------------------------------------- 1 | .controlGroups { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | gap: var(--mantine-spacing-sm); 6 | @media (min-width: $mantine-breakpoint-md) { 7 | flex-direction: row; 8 | gap: calc(2 * var(--mantine-spacing-xl)); 9 | } 10 | } 11 | 12 | .controls { 13 | display: flex; 14 | flex-direction: column; 15 | align-items: flex-start; 16 | gap: var(--mantine-spacing-xs); 17 | } 18 | 19 | .control { 20 | display: flex; 21 | align-items: center; 22 | min-height: rem(34px); 23 | } 24 | -------------------------------------------------------------------------------- /lib/examples.ts: -------------------------------------------------------------------------------- 1 | export type DelayOptions = { min: number; max: number }; 2 | import { useCallback, useEffect, useRef } from 'react'; 3 | 4 | export function delay({ min, max }: DelayOptions) { 5 | return new Promise((resolve) => { 6 | setTimeout(resolve, min + Math.round(Math.random() * (max - min))); 7 | }); 8 | } 9 | 10 | export function useIsMounted() { 11 | const isMounted = useRef(false); 12 | 13 | useEffect(() => { 14 | isMounted.current = true; 15 | 16 | return () => { 17 | isMounted.current = false; 18 | }; 19 | }, []); 20 | 21 | return useCallback(() => isMounted.current, []); 22 | } 23 | -------------------------------------------------------------------------------- /app/(home)/Feature.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | @media (min-width: $mantine-breakpoint-xs) { 3 | width: calc(50% - var(--mantine-spacing-xl) / 2); 4 | } 5 | } 6 | 7 | .iconContainer { 8 | color: white; 9 | background: linear-gradient(45deg, var(--mantine-color-blue-6), var(--mantine-color-cyan-6)); 10 | flex-shrink: 0; 11 | border-radius: var(--mantine-radius-md); 12 | } 13 | 14 | .title { 15 | color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-2)); 16 | margin: -0.25em 0 0.25em; 17 | } 18 | 19 | .description { 20 | color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-1)); 21 | } 22 | -------------------------------------------------------------------------------- /data/nested.ts: -------------------------------------------------------------------------------- 1 | import { companies as companyData, departments as departmentData, employees } from '.'; 2 | 3 | // Departments with employees count 4 | export const departments = departmentData.map((department) => ({ 5 | ...department, 6 | employees: employees.filter((employee) => employee.department.id === department.id)?.length || 0, 7 | })); 8 | 9 | // Companies with employees count 10 | export const companies = companyData.map((company) => ({ 11 | ...company, 12 | employees: departments 13 | .filter((department) => department.company.id === company.id) 14 | .reduce((sum, department) => sum + department.employees, 0), 15 | })); 16 | -------------------------------------------------------------------------------- /app/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/InternalLink.tsx: -------------------------------------------------------------------------------- 1 | import { Anchor } from '@mantine/core'; 2 | import { kebabCase } from 'lodash'; 3 | import type { Route } from 'next'; 4 | import Link from 'next/link'; 5 | 6 | export type InternalLinkProps = React.PropsWithChildren<{ 7 | className?: string; 8 | to: Route; 9 | scrollTo?: string; 10 | }>; 11 | 12 | export function InternalLink({ className, to, scrollTo, children }: InternalLinkProps) { 13 | let href = to; 14 | if (scrollTo) href += `/#${kebabCase(scrollTo)}`; 15 | return ( 16 | 17 | {children} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /package/hooks/useMediaQueriesStringOrFunction.ts: -------------------------------------------------------------------------------- 1 | import { useMantineTheme, type MantineTheme } from '@mantine/core'; 2 | import { useMemo } from 'react'; 3 | import { useMediaQueries } from './useMediaQueries'; 4 | 5 | export function useMediaQueriesStringOrFunction(queries: (string | ((theme: MantineTheme) => string) | undefined)[]) { 6 | const theme = useMantineTheme(); 7 | const values = useMemo( 8 | () => queries.map((query) => (typeof query === 'function' ? query(theme) : query) ?? ''), 9 | [queries, theme] 10 | ); 11 | const defaults = useMemo(() => queries.map(() => true), [queries]); 12 | return useMediaQueries(values, defaults); 13 | } 14 | -------------------------------------------------------------------------------- /components/ThemeAttributeSetter.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useComputedColorScheme } from '@mantine/core'; 4 | import { useEffect } from 'react'; 5 | 6 | /** 7 | * We need this component to set the `data-theme` attribute on the `` element because 8 | * some libraries, such as Algolia DocSearch, rely on it to determine the current color scheme. 9 | */ 10 | export function ThemeAttributeSetter() { 11 | const colorScheme = useComputedColorScheme('dark', { getInitialValueInEffect: true }); 12 | 13 | useEffect(() => { 14 | document.documentElement.setAttribute('data-theme', colorScheme); 15 | }, [colorScheme]); 16 | 17 | return null; 18 | } 19 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleControlledMode.module.css: -------------------------------------------------------------------------------- 1 | .details { 2 | font-size: var(--mantine-font-size-sm); 3 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 4 | } 5 | 6 | .label { 7 | width: rem(130px); 8 | } 9 | 10 | .buttonGroup { 11 | border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); 12 | border-radius: var(--mantine-radius-sm); 13 | flex-direction: column; 14 | @media (min-width: $mantine-breakpoint-md) { 15 | flex-direction: row; 16 | } 17 | } 18 | 19 | @media (min-width: $mantine-breakpoint-md) { 20 | .button { 21 | flex: 1 1 33%; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import type { MetadataRoute } from 'next'; 2 | import { ROUTES, WEBSITE_LINK } from './config'; 3 | 4 | export const dynamic = 'force-static'; 5 | 6 | export default function sitemap(): MetadataRoute.Sitemap { 7 | return [ 8 | { 9 | url: `${WEBSITE_LINK}/`, 10 | lastModified: new Date(), 11 | changeFrequency: 'weekly', 12 | priority: 1, 13 | }, 14 | ...ROUTES.filter(({ href }) => href.startsWith('/') && href !== '/').map(({ href }) => ({ 15 | url: `${WEBSITE_LINK}${href}/`, 16 | lastModified: new Date(), 17 | changeFrequency: 'weekly' as const, 18 | priority: 0.7, 19 | })), 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /app/layout.module.css: -------------------------------------------------------------------------------- 1 | .codeRoot { 2 | border-radius: var(--mantine-radius-xs); 3 | border: rem(1px) solid light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); 4 | background: light-dark(rgba(0, 0, 0, 0.03), rgba(255, 255, 255, 0.08)); 5 | padding: 0.25rem 0.16rem 0.1rem; 6 | } 7 | 8 | .codeBlockBox { 9 | border-radius: var(--mantine-radius-sm); 10 | border: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-5)); 11 | margin: 1em 0; 12 | } 13 | 14 | /* todo remove this firefox fix as soon as the problem is solved in Mantine */ 15 | @-moz-document url-prefix() { 16 | .buttonLabel { 17 | margin-bottom: -0.25em; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package/utilityClasses.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-nowrap { 2 | white-space: nowrap; 3 | } 4 | 5 | .mantine-datatable-ellipsis { 6 | overflow: hidden; 7 | text-overflow: ellipsis; 8 | } 9 | 10 | .mantine-datatable-pointer-cursor { 11 | cursor: pointer; 12 | } 13 | 14 | .mantine-datatable-context-menu-cursor { 15 | cursor: context-menu; 16 | } 17 | 18 | .mantine-datatable-text-selection-disabled { 19 | user-select: none; 20 | } 21 | 22 | .mantine-datatable-text-align-left { 23 | text-align: left; 24 | } 25 | 26 | .mantine-datatable-text-align-center { 27 | text-align: center; 28 | } 29 | 30 | .mantine-datatable-text-align-right { 31 | text-align: right; 32 | } 33 | -------------------------------------------------------------------------------- /app/examples/column-properties-and-styling/ColumnStylingExample.module.css: -------------------------------------------------------------------------------- 1 | .idColumnCells { 2 | font-weight: 700; 3 | } 4 | 5 | .birthdayColumnTitle { 6 | color: var(--mantine-color-blue-6); 7 | } 8 | 9 | .male { 10 | font-weight: 700; 11 | color: var(--mantine-color-blue-6); 12 | background: color-mix(in srgb, var(--mantine-color-blue-6), #0000 90%); 13 | } 14 | 15 | .female { 16 | font-weight: 700; 17 | color: var(--mantine-color-red-6); 18 | background: color-mix(in srgb, var(--mantine-color-red-6), #0000 90%); 19 | } 20 | 21 | .ageFooter { 22 | color: var(--mantine-color-red-6); 23 | background: color-mix(in srgb, var(--mantine-color-yellow-6), #0000 80%); 24 | } 25 | -------------------------------------------------------------------------------- /components/HeaderTitle.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: flex; 3 | align-items: center; 4 | gap: var(--mantine-spacing-xs); 5 | color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); 6 | text-decoration: none; 7 | } 8 | 9 | .icon { 10 | width: rem(30px); 11 | height: auto; 12 | background: var(--mantine-color-blue-6); 13 | color: white; 14 | padding: rem(5px); 15 | border-radius: 50%; 16 | } 17 | 18 | .text { 19 | font-size: rem(18px); 20 | margin-bottom: rem(-2px); 21 | 22 | /* todo remove this firefox fix as soon as the problem is solved in Mantine */ 23 | @-moz-document url-prefix() { 24 | margin-bottom: -0.25em; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /components/PageSubtitle.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Title } from '@mantine/core'; 4 | import { IconLink } from '@tabler/icons-react'; 5 | import { kebabCase } from 'lodash'; 6 | import classes from './PageSubtitle.module.css'; 7 | 8 | export type PageSubtitleProps = { 9 | value: string; 10 | }; 11 | 12 | export function PageSubtitle({ value }: PageSubtitleProps) { 13 | const id = kebabCase(value); 14 | 15 | return ( 16 | 17 | <a className={classes.anchor} onClick={() => (location.hash = id)}> 18 | {value} 19 | <IconLink className={classes.icon} /> 20 | </a> 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /package/icons/IconFilterFilled.tsx: -------------------------------------------------------------------------------- 1 | export function IconFilterFilled() { 2 | return ( 3 | 13 | 14 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /components/ShikiCodeHighlightProvider.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { CodeHighlightAdapterProvider, createShikiAdapter } from '@mantine/code-highlight'; 4 | import type { PropsWithChildren } from 'react'; 5 | 6 | async function loadShiki() { 7 | const { createHighlighter } = await import('shiki'); 8 | const shiki = await createHighlighter({ 9 | langs: ['ts', 'tsx', 'js', 'json', 'css', 'shell'], 10 | themes: [], 11 | }); 12 | 13 | return shiki; 14 | } 15 | 16 | const shikiAdapter = createShikiAdapter(loadShiki); 17 | 18 | export function ShikiCodeHighlightProvider({ children }: PropsWithChildren) { 19 | return {children}; 20 | } 21 | -------------------------------------------------------------------------------- /package/types/DataTableScrollProps.ts: -------------------------------------------------------------------------------- 1 | export type DataTableScrollProps = { 2 | /** 3 | * Function to call when the DataTable is scrolled. 4 | */ 5 | onScroll?: (position: { x: number; y: number }) => void; 6 | 7 | /** 8 | * Function to call when the DataTable is scrolled to top. 9 | */ 10 | onScrollToTop?: () => void; 11 | 12 | /** 13 | * Function to call when the DataTable is scrolled to bottom. 14 | */ 15 | onScrollToBottom?: () => void; 16 | 17 | /** 18 | * Function to call when the DataTable is scrolled to left. 19 | */ 20 | onScrollToLeft?: () => void; 21 | 22 | /** 23 | * Function to call when the DataTable is scrolled to right. 24 | */ 25 | onScrollToRight?: () => void; 26 | }; 27 | -------------------------------------------------------------------------------- /package/types/DataTablePageSizeSelectorProps.ts: -------------------------------------------------------------------------------- 1 | export type DataTablePageSizeSelectorProps = 2 | | { 3 | onRecordsPerPageChange?: never; 4 | recordsPerPageOptions?: never; 5 | recordsPerPageLabel?: never; 6 | } 7 | | { 8 | /** 9 | * Callback fired a new page size is selected. 10 | * Receives new page size as argument. 11 | */ 12 | onRecordsPerPageChange: (recordsPerPage: number) => void; 13 | 14 | /** 15 | * Array of page sizes (numbers) to show in records per page selector. 16 | */ 17 | recordsPerPageOptions: number[]; 18 | 19 | /** 20 | * Label for records per page selector. 21 | */ 22 | recordsPerPageLabel?: string; 23 | }; 24 | -------------------------------------------------------------------------------- /package/DataTableRow.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-row { 2 | --mantine-datatable-row-color: var( 3 | light-dark(--mantine-datatable-row-color-light, --mantine-datatable-row-color-dark) 4 | ); 5 | --mantine-datatable-row-background-color: var( 6 | light-dark(--mantine-datatable-row-background-color-light, --mantine-datatable-row-background-color-dark) 7 | ); 8 | 9 | color: var(--mantine-datatable-row-color, inherit); 10 | background: var(--mantine-datatable-row-background-color, inherit); 11 | 12 | &[data-with-row-border]:not(:last-of-type) td { 13 | border-bottom: rem(1px) solid var(--mantine-datatable-row-border-color); 14 | } 15 | 16 | &[data-selected] td { 17 | background: var(--mantine-datatable-selection-color); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/layout.css: -------------------------------------------------------------------------------- 1 | @import '@mantine/core/styles.css' layer(mantine-core); 2 | @import '@mantine/dates/styles.css' layer(mantine-dates); 3 | @import '@mantine/notifications/styles.css' layer(mantine-notifications); 4 | @import '@mantine/code-highlight/styles.css' layer(mantine-code-highlight); 5 | @import 'mantine-contextmenu/dist/styles.css' layer(mantine-contextmenu); 6 | @import '../package/styles.css' layer(mantine-datatable); 7 | @import '@docsearch/css'; 8 | @import './docsearch-overrides.css'; 9 | 10 | @layer mantine-core, mantine-notifications, mantine-code-highlight, mantine-datatable; 11 | 12 | a code { 13 | color: var(--mantine-color-anchor); 14 | } 15 | 16 | .noscroll { 17 | overflow: hidden; 18 | } 19 | 20 | .nowrap { 21 | white-space: nowrap; 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "~/*": ["./*"], 23 | "__PACKAGE__": ["./package/"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /app/examples/complex-usage-scenario/ComplexUsageExampleWrapper.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; 4 | import { useState } from 'react'; 5 | 6 | export function ComplexUsageExampleWrapper({ children }: React.PropsWithChildren) { 7 | const [queryClient] = useState( 8 | () => 9 | new QueryClient({ 10 | defaultOptions: { 11 | queries: { 12 | // With SSR, we usually want to set some default staleTime 13 | // above 0 to avoid refetching immediately on the client 14 | staleTime: 60 * 1000, 15 | }, 16 | }, 17 | }) 18 | ); 19 | 20 | return {children}; 21 | } 22 | -------------------------------------------------------------------------------- /components/NpmHeaderLinkButton.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button } from '@mantine/core'; 2 | import { IconBrandNpm } from '@tabler/icons-react'; 3 | import { NPM_LINK, PRODUCT_NAME } from '~/app/config'; 4 | import { useNpmDownloads } from '~/lib/useNpmDownloads'; 5 | 6 | export function NpmHeaderLinkButton() { 7 | const downloads = useNpmDownloads(); 8 | 9 | return ( 10 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /package/types/DataTableEmptyStateProps.ts: -------------------------------------------------------------------------------- 1 | export type DataTableEmptyStateProps = 2 | | { 3 | /** 4 | * Content to show when no records are available. 5 | * The provided content will be overlaid and centered automatically. 6 | * You can either provide this property or `noRecordsIcon`, but not both. 7 | */ 8 | emptyState?: React.ReactNode; 9 | 10 | noRecordsIcon?: never; 11 | } 12 | | { 13 | emptyState?: never; 14 | 15 | /** 16 | * Icon to show when no records are available. 17 | * The provided icon will be overlaid and centered automatically. 18 | * You can either provide this property or `emptyState`, but not both. 19 | */ 20 | noRecordsIcon?: React.ReactNode; 21 | }; 22 | -------------------------------------------------------------------------------- /package/styles.css: -------------------------------------------------------------------------------- 1 | @import './utilityClasses.css'; 2 | 3 | @import './DataTable.css'; 4 | @import './DataTableEmptyRow.css'; 5 | @import './DataTableEmptyState.css'; 6 | @import './DataTableFooter.css'; 7 | @import './DataTableFooterSelectorPlaceholderCell.css'; 8 | @import './DataTableHeader.css'; 9 | @import './DataTableHeaderCell.css'; 10 | @import './DataTableHeaderCellFilter.css'; 11 | @import './DataTableHeaderSelectorCell.css'; 12 | @import './DataTableColumnGroupHeaderCell.css'; 13 | @import './DataTableLoader.css'; 14 | @import './DataTablePageSizeSelector.css'; 15 | @import './DataTablePagination.css'; 16 | @import './DataTableRow.css'; 17 | @import './DataTableRowExpansion.css'; 18 | @import './DataTableRowSelectorCell.css'; 19 | @import './DataTableScrollArea.css'; 20 | -------------------------------------------------------------------------------- /app/examples/disabling-text-selection/DisablingTextSelectionExample.tsx: -------------------------------------------------------------------------------- 1 | import { DataTable } from '__PACKAGE__'; 2 | import companies from '~/data/companies.json'; 3 | 4 | const records = companies.slice(0, 5); 5 | 6 | export function DisablingTextSelectionExample({ textSelectionDisabled }: { textSelectionDisabled: boolean }) { 7 | // example-start 8 | return ( 9 | // prettier-ignore 10 | 23 | ); 24 | // example-end 25 | } 26 | -------------------------------------------------------------------------------- /app/contribute-and-support/ContributorsImage.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useWindowEvent } from '@mantine/hooks'; 4 | import { useEffect, useState } from 'react'; 5 | import { PRODUCT_NAME } from '~/app/config'; 6 | 7 | export function ContributorsImage() { 8 | const [cols, setCols] = useState(12); 9 | 10 | const adjustCols = () => { 11 | setCols(window.innerWidth < 400 ? 4 : window.innerWidth < 800 ? 8 : 12); 12 | }; 13 | 14 | useWindowEvent('resize', adjustCols); 15 | // eslint-disable-next-line react-hooks/set-state-in-effect 16 | useEffect(adjustCols, []); 17 | 18 | return ( 19 | {`${PRODUCT_NAME} 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /app/examples/overriding-the-default-styles/StylingWithClassNameExample.tsx: -------------------------------------------------------------------------------- 1 | import { DataTable } from '__PACKAGE__'; 2 | import classes from './StylingWithClassNameExample.module.css'; 3 | // example-skip 4 | import companies from '~/data/companies.json'; 5 | 6 | const records = companies.slice(0, 5); 7 | // example-resume 8 | 9 | export function StylingWithClassNameExample() { 10 | return ( 11 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /app/hire-the-author/page.module.css: -------------------------------------------------------------------------------- 1 | .picture { 2 | float: right; 3 | border-radius: var(--mantine-radius-sm); 4 | border: 1px solid var(--mantine-color-blue-filled); 5 | min-width: rem(136px); 6 | width: 30%; 7 | margin: rem(60px) 0 0.5rem 1rem; 8 | filter: saturate(90%); 9 | transition: all 0.2s ease; 10 | @mixin hover { 11 | filter: saturate(100%); 12 | box-shadow: var(--mantine-shadow-sm); 13 | } 14 | @media (min-width: $mantine-breakpoint-xs) { 15 | width: 40%; 16 | margin-top: rem(62px); 17 | } 18 | @media (min-width: $mantine-breakpoint-sm) { 19 | width: 35%; 20 | margin-top: rem(72px); 21 | } 22 | @media (min-width: $mantine-breakpoint-md) { 23 | width: 30%; 24 | } 25 | } 26 | 27 | .badge { 28 | vertical-align: text-bottom; 29 | } 30 | -------------------------------------------------------------------------------- /package/icons/IconGripVertical.tsx: -------------------------------------------------------------------------------- 1 | export function IconGripVertical() { 2 | return ( 3 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /app/examples/overriding-the-default-styles/StylingWithStyleObjectExample.tsx: -------------------------------------------------------------------------------- 1 | import { DataTable } from '__PACKAGE__'; 2 | import companies from '~/data/companies.json'; 3 | 4 | const records = companies.slice(0, 5); 5 | 6 | export function StylingWithStyleObjectExample() { 7 | // example-start 8 | return ( 9 | 25 | ); 26 | // example-end 27 | } 28 | -------------------------------------------------------------------------------- /app/examples/using-with-auto-animate/UsingWithAutoAnimateExample.module.css: -------------------------------------------------------------------------------- 1 | .buttons { 2 | width: 100%; 3 | display: flex; 4 | gap: var(--mantine-spacing-md); 5 | flex-direction: column; 6 | @media (min-width: rem(400px)) { 7 | flex-direction: row; 8 | flex-wrap: wrap; 9 | } 10 | } 11 | 12 | .button { 13 | width: 100%; 14 | @media (min-width: rem(400px)) { 15 | width: calc(50% - (var(--mantine-spacing-md) / 2)); 16 | } 17 | @media (min-width: rem(660px)) { 18 | width: calc((100% - var(--mantine-spacing-md) * 3) / 4); 19 | } 20 | @media (min-width: $mantine-breakpoint-sm) { 21 | width: calc(50% - (var(--mantine-spacing-md) / 2)); 22 | } 23 | @media (min-width: $mantine-breakpoint-md) { 24 | width: calc((100% - var(--mantine-spacing-md) * 3) / 4); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/examples/basic-table-properties/BasicTablePropertiesPageContent.module.css: -------------------------------------------------------------------------------- 1 | .controlGroups { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | gap: var(--mantine-spacing-sm); 6 | @media (min-width: rem(610px)) { 7 | flex-direction: row; 8 | gap: var(--mantine-spacing-xl); 9 | } 10 | @media (min-width: $mantine-breakpoint-sm) { 11 | flex-direction: column; 12 | } 13 | @media (min-width: $mantine-breakpoint-md) { 14 | flex-direction: row; 15 | gap: calc(2 * var(--mantine-spacing-xl)); 16 | } 17 | } 18 | 19 | .controls { 20 | display: flex; 21 | flex-direction: column; 22 | align-items: flex-start; 23 | gap: var(--mantine-spacing-xs); 24 | } 25 | 26 | .control { 27 | display: flex; 28 | align-items: center; 29 | min-height: rem(34px); 30 | } 31 | -------------------------------------------------------------------------------- /package/DataTableEmptyState.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-empty-state { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | flex-direction: column; 8 | pointer-events: none; 9 | color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-3)); 10 | opacity: 0; 11 | transition: opacity 0.2s; 12 | padding-top: var(--mantine-datatable-header-height, 0); 13 | padding-bottom: var(--mantine-datatable-footer-height, 0); 14 | 15 | &[data-active] { 16 | opacity: 1; 17 | } 18 | } 19 | 20 | .mantine-datatable-empty-state-icon { 21 | font-size: 0; 22 | border-radius: 50%; 23 | padding: var(--mantine-spacing-xs); 24 | background: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); 25 | margin-bottom: calc(var(--mantine-spacing-xs) / 2); 26 | } 27 | -------------------------------------------------------------------------------- /components/PageSubtitle.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); 3 | margin: calc(1em - var(--app-shell-header-height) - var(--mantine-spacing-md)) 0 0.5em; 4 | pointer-events: none; 5 | font-size: 1.2rem; 6 | @media (min-width: $mantine-breakpoint-sm) { 7 | font-size: 1.3rem; 8 | } 9 | @media (min-width: $mantine-breakpoint-md) { 10 | font-size: 1.5rem; 11 | } 12 | } 13 | 14 | .anchor { 15 | display: inline-block; 16 | margin-top: calc(var(--app-shell-header-height) + var(--mantine-spacing-md)); 17 | pointer-events: all; 18 | cursor: pointer; 19 | &:hover { 20 | text-decoration: underline; 21 | } 22 | } 23 | 24 | .icon { 25 | height: 0.8em; 26 | width: auto; 27 | vertical-align: rem(-2px); 28 | margin-left: 0.25em; 29 | } 30 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { FlatCompat } from '@eslint/eslintrc'; 2 | import { dirname } from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends('next/core-web-vitals', 'next/typescript'), 14 | { 15 | ignores: ['.next/', 'next-env.d.ts', 'out/', 'dist/', 'public/'], 16 | }, 17 | { 18 | rules: { 19 | 'no-console': 'error', 20 | 'object-shorthand': 'error', 21 | 'no-useless-rename': 'error', 22 | '@typescript-eslint/consistent-type-imports': 'error', 23 | '@next/next/no-img-element': 'off', 24 | }, 25 | }, 26 | ]; 27 | 28 | export default eslintConfig; 29 | -------------------------------------------------------------------------------- /components/Footer.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | z-index: 1; 3 | position: fixed; 4 | bottom: 0; 5 | right: 0; 6 | left: 0; 7 | padding: var(--mantine-spacing-md); 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | gap: var(--mantine-spacing-xs); 12 | background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6)); 13 | @media (min-width: $mantine-breakpoint-sm) { 14 | left: var(--app-shell-navbar-width); 15 | } 16 | @media (min-width: $mantine-breakpoint-md) { 17 | flex-direction: row; 18 | align-items: flex-start; 19 | justify-content: space-between; 20 | } 21 | } 22 | 23 | .imageLinks { 24 | flex: 0 0 auto; 25 | @media (min-width: $mantine-breakpoint-md) { 26 | margin-top: rem(3px); 27 | } 28 | } 29 | 30 | .imageLink { 31 | font-size: 0; 32 | } 33 | -------------------------------------------------------------------------------- /package/hooks/useRowExpansionStatus.ts: -------------------------------------------------------------------------------- 1 | import { useTimeout } from '@mantine/hooks'; 2 | import { useEffect, useState } from 'react'; 3 | 4 | export function useRowExpansionStatus(open: boolean, transitionDuration?: number) { 5 | const [expanded, setExpanded] = useState(open); 6 | const [visible, setVisible] = useState(open); 7 | 8 | const expand = useTimeout(() => setExpanded(true), 0); 9 | const hide = useTimeout(() => setVisible(false), transitionDuration || 200); 10 | 11 | useEffect(() => { 12 | if (open) { 13 | hide.clear(); 14 | // eslint-disable-next-line react-hooks/set-state-in-effect 15 | setVisible(true); 16 | expand.start(); 17 | } else { 18 | expand.clear(); 19 | setExpanded(false); 20 | hide.start(); 21 | } 22 | }, [expand, hide, open]); 23 | 24 | return { expanded, visible }; 25 | } 26 | -------------------------------------------------------------------------------- /app/examples/row-styling/RowStylingWithStyleObjectExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable } from '__PACKAGE__'; 4 | import companies from '~/data/companies.json'; 5 | 6 | const records = companies.slice(0, 5); 7 | 8 | export function RowStylingWithStyleObjectExample() { 9 | // example-start 10 | return ( 11 | (state === 'NH' ? { fontStyle: 'italic', fontWeight: 'bold' } : undefined)} 13 | // example-skip other table props 14 | withTableBorder 15 | columns={[ 16 | { accessor: 'name' }, 17 | { accessor: 'missionStatement', width: 150 }, 18 | { accessor: 'streetAddress' }, 19 | { accessor: 'city' }, 20 | { accessor: 'state' }, 21 | ]} 22 | records={records} 23 | // example-resume 24 | /> 25 | ); 26 | // example-end 27 | } 28 | -------------------------------------------------------------------------------- /components/AppWrapper.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | display: flow-root; 3 | position: relative; 4 | z-index: 2; 5 | pointer-events: none; 6 | } 7 | 8 | .content { 9 | min-height: calc(100dvh - var(--app-wrapper-footer-height) - var(--app-shell-header-height)); 10 | margin-bottom: var(--app-wrapper-footer-height); 11 | padding: var(--mantine-spacing-xs) 0 var(--mantine-spacing-xl); 12 | background: var(--mantine-color-body); 13 | border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); 14 | position: relative; 15 | pointer-events: all; 16 | &::after { 17 | position: absolute; 18 | content: ''; 19 | left: 0; 20 | right: 0; 21 | height: rem(6px); 22 | bottom: rem(-7px); 23 | background: linear-gradient(light-dark(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.3)), rgba(0, 0, 0, 0)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /package/icons/IconDatabaseOff.tsx: -------------------------------------------------------------------------------- 1 | export function IconDatabaseOff() { 2 | return ( 3 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /package/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DataTableCellClickHandler'; 2 | export * from './DataTableColumn'; 3 | export * from './DataTableColumnGroup'; 4 | export * from './DataTableColumnTextAlign'; 5 | export * from './DataTableDefaultColumnProps'; 6 | export * from './DataTableDraggableRowProps'; 7 | export * from './DataTableEmptyStateProps'; 8 | export * from './DataTableOuterBorderProps'; 9 | export * from './DataTablePaginationProps'; 10 | export * from './DataTableProps'; 11 | export * from './DataTableRowClickHandler'; 12 | export * from './DataTableRowExpansionCollapseProps'; 13 | export * from './DataTableRowExpansionProps'; 14 | export * from './DataTableSelectionProps'; 15 | export * from './DataTableSelectionTrigger'; 16 | export * from './DataTableSortProps'; 17 | export * from './DataTableSortStatus'; 18 | export * from './DataTableVerticalAlign'; 19 | export * from './utils'; 20 | -------------------------------------------------------------------------------- /package/DataTableEmptyState.tsx: -------------------------------------------------------------------------------- 1 | import { Center, Text } from '@mantine/core'; 2 | import { IconDatabaseOff } from './icons/IconDatabaseOff'; 3 | 4 | type DataTableEmptyStateProps = React.PropsWithChildren<{ 5 | icon: React.ReactNode | undefined; 6 | text: string; 7 | active: boolean; 8 | }>; 9 | 10 | export function DataTableEmptyState({ icon, text, active, children }: DataTableEmptyStateProps) { 11 | return ( 12 |
13 | {children || ( 14 | <> 15 | {icon || ( 16 |
17 | 18 |
19 | )} 20 | 21 | {text} 22 | 23 | 24 | )} 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /app/examples/row-styling/RowStylingWithStyleFunctionExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable } from '__PACKAGE__'; 4 | import companies from '~/data/companies.json'; 5 | 6 | const records = companies.slice(0, 5); 7 | 8 | export function RowStylingWithStyleFunctionExample() { 9 | // example-start 10 | return ( 11 | 13 | (theme) => ({ color: state === 'MA' ? theme.colors.violet[6] : theme.colors.gray[6] })} 14 | // example-skip other table props 15 | withTableBorder 16 | columns={[ 17 | { accessor: 'name' }, 18 | { accessor: 'missionStatement', width: 150 }, 19 | { accessor: 'streetAddress' }, 20 | { accessor: 'city' }, 21 | { accessor: 'state' }, 22 | ]} 23 | records={records} 24 | // example-resume 25 | /> 26 | ); 27 | // example-end 28 | } 29 | -------------------------------------------------------------------------------- /app/examples/row-styling/RowStylingWithClassNameExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable } from '__PACKAGE__'; 4 | import companies from '~/data/companies.json'; 5 | import classes from './RowStylingWithClassNameExample.module.css'; 6 | 7 | const records = companies.slice(0, 5); 8 | 9 | export function RowStylingWithClassNameExample() { 10 | // example-start 11 | return ( 12 | (state === 'WY' ? classes.redRow : undefined)} 14 | // example-skip other table props 15 | withTableBorder 16 | columns={[ 17 | { accessor: 'name' }, 18 | { accessor: 'missionStatement', width: 150 }, 19 | { accessor: 'streetAddress' }, 20 | { accessor: 'city' }, 21 | { accessor: 'state' }, 22 | ]} 23 | records={records} 24 | // example-resume 25 | /> 26 | ); 27 | // example-end 28 | } 29 | -------------------------------------------------------------------------------- /app/examples/overriding-the-default-styles/StylingWithStyleFunctionExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable } from '__PACKAGE__'; 4 | import companies from '~/data/companies.json'; 5 | 6 | const records = companies.slice(0, 5); 7 | 8 | export function StylingWithStyleFunctionExample() { 9 | // example-start 10 | return ( 11 | ({ 13 | border: `1px solid ${theme.colors.indigo[6]}`, 14 | borderRadius: theme.radius.md, 15 | color: theme.colors.violet[6], 16 | })} 17 | // example-skip 18 | columns={[ 19 | { accessor: 'name' }, 20 | { accessor: 'missionStatement', width: 150 }, 21 | { accessor: 'streetAddress' }, 22 | { accessor: 'city' }, 23 | { accessor: 'state' }, 24 | ]} 25 | records={records} 26 | // example-resume 27 | /> 28 | ); 29 | // example-end 30 | } 31 | -------------------------------------------------------------------------------- /app/(home)/Feature.tsx: -------------------------------------------------------------------------------- 1 | import { Center, Flex, Text } from '@mantine/core'; 2 | import type { WithRequiredProperty } from '__PACKAGE__'; 3 | import classes from './Feature.module.css'; 4 | 5 | export type FeatureProps = WithRequiredProperty< 6 | React.PropsWithChildren<{ 7 | icon: React.FC<{ size?: string | number }>; 8 | title: string; 9 | }>, 10 | 'children' 11 | >; 12 | 13 | export function Feature({ icon: Icon, title, children }: FeatureProps) { 14 | return ( 15 | 16 |
17 | 18 |
19 |
20 | 21 | {title} 22 | 23 | 24 | {children} 25 | 26 |
27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /package/types/DataTableLoaderProps.ts: -------------------------------------------------------------------------------- 1 | import type { MantineColor, MantineLoader, MantineSize } from '@mantine/core'; 2 | 3 | export type DataTableLoaderProps = { 4 | /** 5 | * Loader background blur (in pixels). 6 | */ 7 | loaderBackgroundBlur?: number; 8 | } & ( 9 | | { 10 | loaderSize?: never; 11 | loaderType?: never; 12 | loaderColor?: never; 13 | 14 | /** 15 | * Custom loader component to use instead of default one. 16 | */ 17 | customLoader?: React.ReactNode; 18 | } 19 | | { 20 | /** 21 | * Loader size. 22 | * @default `lg`. 23 | */ 24 | loaderSize?: MantineSize | (string & NonNullable) | number; 25 | 26 | /** 27 | * Loader type. 28 | */ 29 | loaderType?: MantineLoader; 30 | 31 | /** 32 | * Loader color. 33 | */ 34 | loaderColor?: MantineColor; 35 | 36 | customLoader?: never; 37 | } 38 | ); 39 | -------------------------------------------------------------------------------- /app/styling/examples/simple/RootLayout.tsx: -------------------------------------------------------------------------------- 1 | import { ColorSchemeScript, MantineProvider } from '@mantine/core'; 2 | 3 | // 👇 Import the mantine-core layer CSS file; 4 | // this will automatically place it in a `mantine` layer 5 | import '@mantine/core/styles.layer.css'; 6 | 7 | // 👇 Import the mantine-datatable layer CSS file; 8 | // this will automatically place it in a `mantine-datatable` layer 9 | import '__PACKAGE__/styles.layer.css'; 10 | 11 | // 👇 Import your own CSS file; 12 | // make sure to specify the layers order with the `@layer` directive 13 | // inside that file 14 | import './layout.css'; 15 | 16 | export function RootLayout({ children }: { children: React.ReactNode }) { 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | {children} 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /package/DataTableLoader.tsx: -------------------------------------------------------------------------------- 1 | import { Center, Loader, type MantineColor, type MantineLoader, type MantineSize } from '@mantine/core'; 2 | import clsx from 'clsx'; 3 | 4 | type DataTableLoaderProps = { 5 | fetching: boolean | undefined; 6 | customContent: React.ReactNode | undefined; 7 | backgroundBlur: number | undefined; 8 | size: MantineSize | (string & NonNullable) | number | undefined; 9 | type: MantineLoader | undefined; 10 | color: MantineColor | undefined; 11 | }; 12 | 13 | export function DataTableLoader({ fetching, customContent, backgroundBlur, size, type, color }: DataTableLoaderProps) { 14 | return ( 15 |
19 | {fetching && (customContent || )} 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /app/examples/row-styling/RowStylingWithColorPropertiesExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable } from '__PACKAGE__'; 4 | import companies from '~/data/companies.json'; 5 | 6 | const records = companies.slice(0, 5); 7 | 8 | export function RowStylingWithColorPropertiesExample() { 9 | // example-start 10 | return ( 11 | { 13 | if (state === 'MA') return 'violet'; 14 | }} 15 | rowBackgroundColor={({ state }) => { 16 | if (state === 'MA') return { dark: '#232b25', light: '#f0f7f1' }; 17 | }} 18 | // example-skip other table props 19 | withTableBorder 20 | columns={[ 21 | { accessor: 'name' }, 22 | { accessor: 'missionStatement', width: 150 }, 23 | { accessor: 'streetAddress' }, 24 | { accessor: 'city' }, 25 | { accessor: 'state' }, 26 | ]} 27 | records={records} 28 | // example-resume 29 | /> 30 | ); 31 | // example-end 32 | } 33 | -------------------------------------------------------------------------------- /app/manifest.webmanifest/route.ts: -------------------------------------------------------------------------------- 1 | import type { MetadataRoute } from 'next'; 2 | import { PRODUCT_DESCRIPTION, PRODUCT_NAME } from '~/app/config'; 3 | 4 | /** 5 | * todo move this to a manifest.ts file as soon as the bug in Next.js is fixed 6 | * we need to use this instead of a manifest.ts due to a bug in Next.js 7 | * (@see https://github.com/vercel/next.js/issues/56687) 8 | */ 9 | 10 | export const dynamic = 'force-static'; 11 | 12 | const data: MetadataRoute.Manifest = { 13 | name: PRODUCT_NAME, 14 | short_name: PRODUCT_NAME, 15 | description: PRODUCT_DESCRIPTION, 16 | start_url: './', 17 | scope: '.', 18 | display: 'standalone', 19 | background_color: '#ffffff', 20 | theme_color: '#1971c2', 21 | icons: [ 22 | { 23 | src: `${process.env.GITHUB_PAGES === 'TRUE' ? `/${process.env.PACKAGE_NAME}` : ''}/icon.svg`, 24 | type: 'image/svg+xml', 25 | sizes: 'any', 26 | purpose: 'any', 27 | }, 28 | ], 29 | }; 30 | 31 | export const GET = () => Response.json(data); 32 | -------------------------------------------------------------------------------- /components/NavbarDynamicLinkButtons.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@mantine/core'; 2 | import { IconBrandGithub, IconHeartFilled } from '@tabler/icons-react'; 3 | import { PRODUCT_NAME, REPO_LINK, SPONSORS_LINK } from '~/app/config'; 4 | import { NavbarButton } from './NavbarButton'; 5 | import { NpmNavbarLinkButton } from './NpmNavbarLinkButton'; 6 | 7 | export function NavbarDynamicLinkButtons() { 8 | return ( 9 | 10 | 17 | 18 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /package/types/PaginationRenderContext.tsx: -------------------------------------------------------------------------------- 1 | import type { MantineSize, PaginationProps, TextProps } from '@mantine/core'; 2 | import type { JSX } from 'react'; 3 | import type { DataTablePageSizeSelectorProps } from './DataTablePageSizeSelectorProps'; 4 | 5 | export type PaginationRenderContext = { 6 | state: { 7 | paginationSize: MantineSize; 8 | page: number; 9 | totalPages: number; 10 | totalRecords: number | undefined; 11 | recordsPerPage: number | undefined; 12 | recordsLength: number | undefined; 13 | fetching: boolean | undefined; 14 | from?: number; 15 | to?: number; 16 | isWrapped: boolean; 17 | }; 18 | actions: { 19 | setPage: (page: number) => void; 20 | setRecordsPerPage?: (n: number) => void; 21 | }; 22 | Controls: { 23 | Text: (props?: Partial) => JSX.Element; 24 | PageSizeSelector: (props?: Partial) => JSX.Element; 25 | Pagination: (props?: Partial) => JSX.Element; 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /package/types/DataTableSortProps.ts: -------------------------------------------------------------------------------- 1 | import type { DataTableSortStatus } from './DataTableSortStatus'; 2 | 3 | export type DataTableSortProps> = ( 4 | | { 5 | sortStatus?: never; 6 | onSortStatusChange?: never; 7 | } 8 | | { 9 | /** 10 | * Current sort status (sort column accessor & direction). 11 | */ 12 | sortStatus: DataTableSortStatus; 13 | 14 | /** 15 | * Callback fired after change of sort status. 16 | * Receives the new sort status as argument. 17 | */ 18 | onSortStatusChange?: (sortStatus: DataTableSortStatus) => void; 19 | } 20 | ) & { 21 | /** 22 | * Custom sort icons. 23 | */ 24 | sortIcons?: { 25 | /** 26 | * Icon to display when column is sorted ascending. 27 | * Will be rotated 180deg for descending sort 28 | */ 29 | sorted: React.ReactNode; 30 | /** 31 | * Icon to display when column is not sorted. 32 | */ 33 | unsorted: React.ReactNode; 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /components/ColorSchemeActionIcon.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Tooltip, useMantineColorScheme } from '@mantine/core'; 2 | import { useHotkeys, useOs } from '@mantine/hooks'; 3 | import { IconMoon, IconSun } from '@tabler/icons-react'; 4 | import classes from './ColorSchemeActionIcon.module.css'; 5 | 6 | export function ColorSchemeActionIcon() { 7 | const { toggleColorScheme } = useMantineColorScheme(); 8 | useHotkeys([['mod+j', toggleColorScheme]]); 9 | 10 | const os = useOs(); 11 | let label = 'Toggle color scheme'; 12 | if (os === 'macos') { 13 | label += ' (⌘J)'; 14 | } else if (os === 'windows' || os === 'linux') { 15 | label += ' (Ctrl+J)'; 16 | } 17 | 18 | return ( 19 | 20 | toggleColorScheme()} variant="default" size={30} aria-label={label}> 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /app/(home)/HomePageSubtitle.module.css: -------------------------------------------------------------------------------- 1 | .leftIcon { 2 | flex-shrink: 0; 3 | margin-top: rem(1px); 4 | width: rem(18px); 5 | height: auto; 6 | } 7 | 8 | .iconGreen { 9 | color: var(--mantine-color-green-filled); 10 | } 11 | 12 | .iconBlue { 13 | color: var(--mantine-color-blue-filled); 14 | } 15 | 16 | .iconRed { 17 | color: var(--mantine-color-red-filled); 18 | } 19 | 20 | .linkIcon { 21 | width: rem(15px); 22 | height: auto; 23 | vertical-align: rem(-2px); 24 | } 25 | 26 | .scrollDownIcon { 27 | margin-bottom: rem(-1px); 28 | animation: bounce 5s infinite; 29 | } 30 | 31 | @keyframes bounce { 32 | 0%, 33 | 20%, 34 | 100% { 35 | transform: translateY(0); 36 | } 37 | 38 | 7% { 39 | transform: translateY(rem(-5px)); 40 | } 41 | 42 | 10% { 43 | transform: translateY(rem(4px)); 44 | } 45 | 46 | 13% { 47 | transform: translateY(rem(-3px)); 48 | } 49 | 50 | 15% { 51 | transform: translateY(rem(6px)); 52 | } 53 | 54 | 18% { 55 | transform: translateY(rem(1px)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /package/DataTablePageSizeSelector.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-page-size-selector-button-icon { 2 | margin: 0 rem(-4px) 0 rem(2px); 3 | 4 | @mixin rtl { 5 | margin: 0 rem(2px) 0 rem(-4px); 6 | } 7 | } 8 | 9 | .mantine-datatable-page-size-selector-menu-arrow { 10 | z-index: -1; 11 | } 12 | 13 | .mantine-datatable-page-size-selector-active { 14 | --mantine-datatable-pagination-active-text-color: var( 15 | light-dark( 16 | --mantine-datatable-pagination-active-text-color-light, 17 | --mantine-datatable-pagination-active-text-color-dark 18 | ), 19 | var(--mantine-color-white) 20 | ); 21 | --mantine-datatable-pagination-active-background-color: var( 22 | light-dark( 23 | --mantine-datatable-pagination-active-background-color-light, 24 | --mantine-datatable-pagination-active-background-color-dark 25 | ), 26 | var(--mantine-primary-color-filled) 27 | ); 28 | 29 | opacity: 1; 30 | color: var(--mantine-datatable-pagination-active-text-color); 31 | background: var(--mantine-datatable-pagination-active-background-color); 32 | } 33 | -------------------------------------------------------------------------------- /app/examples/scrollable-vs-auto-height/ScrollableVsAutoHeightExamplePageContent.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Center, Paper, Switch } from '@mantine/core'; 4 | import { useState } from 'react'; 5 | import { CodeBlock } from '~/components/CodeBlock'; 6 | import { AutoHeightExample, ScrollableExample } from './ScrollableVsAutoHeightExamples'; 7 | 8 | export function ScrollableVsAutoHeightExamplePageContent({ 9 | code, 10 | }: { 11 | code: Record<'scrollable' | 'auto-height' | 'scroll-area-props', string>; 12 | }) { 13 | const [scrollable, setScrollable] = useState(true); 14 | 15 | return ( 16 | <> 17 | 18 |
19 | setScrollable((value) => !value)} 23 | /> 24 |
25 |
26 | 27 | {scrollable ? : } 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /data/index.ts: -------------------------------------------------------------------------------- 1 | import companyData from './companies.json'; 2 | import departmentData from './departments.json'; 3 | import employeeData from './employees.json'; 4 | 5 | export type Company = { 6 | id: string; 7 | name: string; 8 | streetAddress: string; 9 | city: string; 10 | state: string; 11 | missionStatement: string; 12 | }; 13 | 14 | export type Department = { 15 | id: string; 16 | name: string; 17 | company: Company; 18 | }; 19 | 20 | export type Employee = { 21 | id: string; 22 | sex: 'male' | 'female'; 23 | firstName: string; 24 | lastName: string; 25 | email: string; 26 | birthDate: string; 27 | department: Department; 28 | }; 29 | 30 | export const companies: Company[] = companyData; 31 | 32 | export const departments: Department[] = departmentData.map(({ companyId, ...rest }) => ({ 33 | ...rest, 34 | company: companies.find(({ id }) => id === companyId)!, 35 | })); 36 | 37 | export const employees = employeeData.map(({ departmentId, ...rest }) => ({ 38 | ...rest, 39 | department: departments.find(({ id }) => id === departmentId)!, 40 | })) as Employee[]; 41 | -------------------------------------------------------------------------------- /app/examples/column-dragging-and-toggling/DraggingExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Button, Group, Stack } from '@mantine/core'; 4 | import { DataTable, useDataTableColumns } from '__PACKAGE__'; 5 | import { companies, type Company } from '~/data'; 6 | 7 | export default function DraggingExample() { 8 | const key = 'draggable-example'; 9 | 10 | const { effectiveColumns, resetColumnsOrder } = useDataTableColumns({ 11 | key, 12 | columns: [ 13 | { accessor: 'name', width: '40%', draggable: true }, 14 | { accessor: 'streetAddress', width: '60%', draggable: true }, 15 | { accessor: 'city', width: 160, draggable: true }, 16 | { accessor: 'state', textAlign: 'right' }, 17 | ], 18 | }); 19 | 20 | return ( 21 | 22 | 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /app/examples/overriding-the-default-styles/StylingWithStyleObjectsAndFunctionsExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable } from '__PACKAGE__'; 4 | import companies from '~/data/companies.json'; 5 | 6 | const records = companies.slice(0, 5); 7 | 8 | export function StylingWithStyleObjectsAndFunctionsExample() { 9 | // example-start 10 | return ( 11 | ({ 15 | border: `1px solid ${theme.colors.orange[6]}`, 16 | }), 17 | table: { 18 | color: '#666', 19 | }, 20 | header: { 21 | color: '#A30', 22 | fontSize: '125%', 23 | }, 24 | }} 25 | // example-skip 26 | columns={[ 27 | { accessor: 'name' }, 28 | { accessor: 'missionStatement', width: 150 }, 29 | { accessor: 'streetAddress' }, 30 | { accessor: 'city' }, 31 | { accessor: 'state' }, 32 | ]} 33 | records={records} 34 | // example-resume 35 | /> 36 | ); 37 | // example-end 38 | } 39 | -------------------------------------------------------------------------------- /package/DataTableRowExpansion.tsx: -------------------------------------------------------------------------------- 1 | import { Collapse, TableTd, TableTr } from '@mantine/core'; 2 | import { useRowExpansionStatus } from './hooks'; 3 | import type { DataTableRowExpansionCollapseProps } from './types'; 4 | 5 | type DataTableRowExpansionProps = { 6 | open: boolean; 7 | colSpan: number; 8 | content: () => React.ReactNode; 9 | collapseProps: DataTableRowExpansionCollapseProps | undefined; 10 | }; 11 | 12 | export function DataTableRowExpansion({ open, colSpan, content, collapseProps }: DataTableRowExpansionProps) { 13 | const { expanded, visible } = useRowExpansionStatus(open, collapseProps?.transitionDuration); 14 | 15 | return visible ? ( 16 | <> 17 | {/* add an empty row to maintain striped rows consistency */} 18 | 19 | 20 | 21 | 22 |
{content()}
23 |
24 |
25 |
26 | 27 | ) : null; 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 – 2025 Ionuț-Cristian Florescu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/PageNavigation.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | margin-top: var(--mantine-spacing-xl); 3 | display: flex; 4 | flex-direction: column-reverse; 5 | gap: var(--mantine-spacing-xl); 6 | @media (min-width: $mantine-breakpoint-xs) { 7 | flex-direction: row; 8 | justify-content: space-between; 9 | } 10 | } 11 | 12 | .button { 13 | display: block; 14 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); 15 | color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); 16 | border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); 17 | border-radius: var(--mantine-radius-sm); 18 | &:hover { 19 | color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); 20 | &:not(:active) { 21 | background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5)); 22 | } 23 | } 24 | &:active { 25 | background: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4)); 26 | transform: translateY(1px); 27 | } 28 | @media (min-width: $mantine-breakpoint-xs) { 29 | flex: 0 0 calc(50% - var(--mantine-spacing-xl) / 2); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /package/DataTableFooterSelectorPlaceholderCell.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-footer-selector-placeholder-cell { 2 | position: sticky; 3 | width: 0; 4 | left: 0; 5 | 6 | @mixin rtl { 7 | left: auto; 8 | right: 0; 9 | } 10 | 11 | &::after { 12 | content: ''; 13 | position: absolute; 14 | top: 0; 15 | right: calc(-1 * var(--mantine-spacing-xs)); 16 | bottom: rem(-1px); 17 | border-left: 1px solid var(--mantine-datatable-row-border-color); 18 | width: var(--mantine-spacing-xs); 19 | background: var(--mantine-datatable-shadow-background-left); 20 | pointer-events: none; 21 | opacity: 0; 22 | transition: opacity 0.2s; 23 | 24 | @mixin rtl { 25 | right: auto; 26 | left: calc(-1 * var(--mantine-spacing-xs)); 27 | border-left: 0; 28 | border-right: 1px solid var(--mantine-datatable-row-border-color); 29 | background: var(--mantine-datatable-shadow-background-right); 30 | } 31 | } 32 | 33 | &[data-shadow-visible]::after { 34 | opacity: var(--mantine-datatable-left-shadow-opacity); 35 | 36 | @mixin rtl { 37 | opacity: var(--mantine-datatable-right-shadow-opacity); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | const { name: PACKAGE_NAME, version: PACKAGE_VERSION } = require('./package.json'); 3 | 4 | module.exports = async () => { 5 | const { downloads } = await fetch('https://api.npmjs.org/downloads/point/last-month/mantine-datatable') 6 | .then((res) => res.json()) 7 | .catch(() => ({ downloads: 0 })); 8 | 9 | /** 10 | * @type {import('next').NextConfig} 11 | */ 12 | const config = { 13 | output: 'export', 14 | trailingSlash: true, 15 | images: { unoptimized: true }, 16 | typedRoutes: true, 17 | experimental: { 18 | optimizePackageImports: [ 19 | '@mantine/code-highlight', 20 | '@mantine/core', 21 | '@mantine/dates', 22 | '@mantine/hooks', 23 | '@mantine/modals', 24 | '@mantine/notifications', 25 | ], 26 | }, 27 | env: { 28 | GITHUB_PAGES: String(process.env.GITHUB_PAGES === 'TRUE' || false).toUpperCase(), 29 | PACKAGE_NAME, 30 | PACKAGE_VERSION, 31 | INITIAL_NPM_DOWNLOADS: String(downloads), 32 | }, 33 | }; 34 | 35 | if (process.env.GITHUB_PAGES) config.basePath = '/mantine-datatable'; 36 | 37 | return config; 38 | }; 39 | -------------------------------------------------------------------------------- /components/Txt.tsx: -------------------------------------------------------------------------------- 1 | import { Alert, Text } from '@mantine/core'; 2 | import { IconAlertSquareRoundedFilled, IconBulbFilled, IconInfoCircle } from '@tabler/icons-react'; 3 | import clsx from 'clsx'; 4 | import classes from './Txt.module.css'; 5 | 6 | export type TxtProps = React.PropsWithChildren< 7 | | { 8 | info?: true; 9 | warning?: never; 10 | idea?: never; 11 | title?: React.ReactNode; 12 | } 13 | | { 14 | info?: never; 15 | warning?: true; 16 | idea?: never; 17 | title?: React.ReactNode; 18 | } 19 | | { 20 | info?: never; 21 | warning?: never; 22 | idea?: true; 23 | title?: React.ReactNode; 24 | } 25 | >; 26 | 27 | export function Txt({ info, warning, idea, title, children }: TxtProps) { 28 | return info || warning || idea ? ( 29 | : idea ? : } 34 | title={title} 35 | > 36 | {children} 37 | 38 | ) : ( 39 | {children} 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleSimple.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Box, Group, Stack } from '@mantine/core'; 4 | import { DataTable } from '__PACKAGE__'; 5 | import { companies } from '~/data'; 6 | import classes from './RowExpansionExampleSimple.module.css'; 7 | 8 | const records = companies.slice(0, 5); 9 | 10 | export function RowExpansionExampleSimple() { 11 | // example-start 12 | return ( 13 | ( 20 | 21 | 22 |
Postal address:
23 |
24 | {record.streetAddress}, {record.city}, {record.state} 25 |
26 |
27 | 28 |
Mission statement:
29 | “{record.missionStatement}” 30 |
31 |
32 | ), 33 | }} 34 | /> 35 | ); 36 | // example-end 37 | } 38 | -------------------------------------------------------------------------------- /package/DataTableHeaderSelectorCell.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-header-selector-cell { 2 | position: sticky; 3 | z-index: 1; 4 | width: 44px; 5 | min-width: 44px; 6 | max-width: 44px; 7 | left: 0; 8 | text-align: center; 9 | padding: var(--mantine-spacing-xs); 10 | 11 | @mixin rtl { 12 | left: auto; 13 | right: 0; 14 | } 15 | 16 | &::after { 17 | content: ''; 18 | position: absolute; 19 | top: 0; 20 | right: calc(-1 * var(--mantine-spacing-xs)); 21 | bottom: rem(-1px); 22 | border-left: 1px solid var(--mantine-datatable-row-border-color); 23 | width: var(--mantine-spacing-xs); 24 | background: var(--mantine-datatable-shadow-background-left); 25 | pointer-events: none; 26 | opacity: 0; 27 | transition: opacity 0.2s; 28 | 29 | @mixin rtl { 30 | right: auto; 31 | left: calc(-1 * var(--mantine-spacing-xs)); 32 | border-left: 0; 33 | border-right: 1px solid var(--mantine-datatable-row-border-color); 34 | background: var(--mantine-datatable-shadow-background-right); 35 | } 36 | } 37 | 38 | &[data-shadow-visible]::after { 39 | opacity: var(--mantine-datatable-left-shadow-opacity); 40 | 41 | @mixin rtl { 42 | opacity: var(--mantine-datatable-right-shadow-opacity); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /components/HeaderLinkButtons.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button } from '@mantine/core'; 2 | import { IconBrandGithub, IconHeartFilled } from '@tabler/icons-react'; 3 | import { PRODUCT_NAME, REPO_LINK, SPONSORS_LINK } from '~/app/config'; 4 | import classes from './HeaderLinkButtons.module.css'; 5 | import { NpmHeaderLinkButton } from './NpmHeaderLinkButton'; 6 | 7 | export function HeaderLinkButtons() { 8 | return ( 9 |
10 | 24 | 25 | 37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /app/examples/non-standard-record-ids/NonStandardRecordIdsStringExample.tsx: -------------------------------------------------------------------------------- 1 | import { DataTable } from '__PACKAGE__'; 2 | 3 | export function NonStandardRecordIdsStringExample() { 4 | return ( 5 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /package/DataTableColumns.context.ts: -------------------------------------------------------------------------------- 1 | import { createSafeContext } from '@mantine/core'; 2 | import type { Dispatch, SetStateAction } from 'react'; 3 | import type { DataTableColumnToggle } from './hooks'; 4 | 5 | interface DataTableColumnsContext { 6 | // accessor of the column which is currently dragged 7 | sourceColumn: string; 8 | setSourceColumn: Dispatch>; 9 | 10 | // accessor of the column which is currently hovered 11 | targetColumn: string; 12 | setTargetColumn: Dispatch>; 13 | 14 | // swap the source column with the target column 15 | swapColumns: () => void; 16 | 17 | // reset to the default columns order 18 | resetColumnsOrder: () => void; 19 | 20 | columnsToggle: DataTableColumnToggle[]; 21 | setColumnsToggle: Dispatch>; 22 | resetColumnsToggle: () => void; 23 | 24 | setColumnWidth: (accessor: string, width: string | number) => void; 25 | setMultipleColumnWidths: (updates: Array<{ accessor: string; width: string | number }>) => void; 26 | resetColumnsWidth: () => void; 27 | } 28 | 29 | export const [DataTableColumnsContextProvider, useDataTableColumnsContext] = createSafeContext( 30 | 'useDataTableColumnsContext must be used within DataTableColumnProvider' 31 | ); 32 | -------------------------------------------------------------------------------- /app/examples/overriding-the-default-styles/StylingWithClassNamesExample.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | border: 1px solid var(--mantine-color-red-6); 3 | border-radius: var(--mantine-radius-md); 4 | background: light-dark( 5 | color-mix(in srgb, var(--mantine-color-red-6) 5%, var(--mantine-color-body)), 6 | color-mix(in srgb, var(--mantine-color-red-6) 10%, var(--mantine-color-body)) 7 | ); 8 | } 9 | 10 | .table { 11 | th, 12 | td { 13 | border-color: var(--mantine-color-orange-6); 14 | } 15 | } 16 | 17 | .header { 18 | font-style: italic; 19 | color: var(--mantine-color-red-6); 20 | th { 21 | border-bottom: 1px solid var(--mantine-color-orange-6); 22 | } 23 | } 24 | 25 | .footer { 26 | font-style: italic; 27 | color: var(--mantine-color-red-6); 28 | 29 | th { 30 | border-top: 1px solid var(--mantine-color-orange-6); 31 | } 32 | } 33 | 34 | .pagination { 35 | background: transparent; 36 | color: var(--mantine-color-red-6); 37 | border-top: 1px dashed var(--mantine-color-red-6); 38 | 39 | button { 40 | background: transparent; 41 | color: var(--mantine-color-red-6); 42 | border-color: var(--mantine-color-red-6); 43 | 44 | &[data-active='true'] { 45 | color: white; 46 | border: transparent; 47 | background: var(--mantine-color-red-6); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/examples/sorting/SortingExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable, type DataTableSortStatus } from '__PACKAGE__'; 4 | import sortBy from 'lodash/sortBy'; 5 | import { useEffect, useState } from 'react'; 6 | import { companies, type Company } from '~/data'; 7 | 8 | export default function SortingExample() { 9 | const [sortStatus, setSortStatus] = useState>({ 10 | columnAccessor: 'name', 11 | direction: 'asc', 12 | }); 13 | const [records, setRecords] = useState(sortBy(companies, 'name')); 14 | 15 | useEffect(() => { 16 | const data = sortBy(companies, sortStatus.columnAccessor) as Company[]; 17 | // eslint-disable-next-line react-hooks/set-state-in-effect 18 | setRecords(sortStatus.direction === 'desc' ? data.reverse() : data); 19 | }, [sortStatus]); 20 | 21 | return ( 22 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /components/Header.tsx: -------------------------------------------------------------------------------- 1 | import { AppShellHeader, Burger, Group } from '@mantine/core'; 2 | import clsx from 'clsx'; 3 | import { useIsScrolled } from '~/lib/utils'; 4 | import { ColorSchemeActionIcon } from './ColorSchemeActionIcon'; 5 | import { DocSearchButton } from './DocSearchButton'; 6 | import classes from './Header.module.css'; 7 | import { HeaderLinkButtons } from './HeaderLinkButtons'; 8 | import { HeaderTitle } from './HeaderTitle'; 9 | import { VersionBadge } from './VersionBadge'; 10 | 11 | export type HeaderProps = { 12 | navbarExpanded: boolean; 13 | toggleNavbar(): void; 14 | }; 15 | 16 | export function Header({ navbarExpanded, toggleNavbar }: HeaderProps) { 17 | const isScrolled = useIsScrolled(); 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /package/types/DataTableColumnGroup.ts: -------------------------------------------------------------------------------- 1 | import type { MantineStyleProp } from '@mantine/core'; 2 | import type { DataTableColumn } from './DataTableColumn'; 3 | import type { DataTableColumnTextAlign } from './DataTableColumnTextAlign'; 4 | 5 | export type DataTableColumnGroup> = { 6 | /** 7 | * Used as the `key` prop for the created ``. 8 | */ 9 | id: string; 10 | 11 | /** 12 | * Component to render inside the column group header. 13 | */ 14 | title?: React.ReactNode; 15 | 16 | /** 17 | * Text alignment of the column group header. 18 | * @default `left` 19 | */ 20 | textAlign?: DataTableColumnTextAlign; 21 | 22 | /** 23 | * Columns which are part of the group. 24 | * Optional when groups are provided for multilevel grouping. 25 | */ 26 | columns?: DataTableColumn[]; 27 | 28 | /** 29 | * Nested column groups for multilevel grouping. 30 | * When provided, columns should be omitted. 31 | */ 32 | groups?: DataTableColumnGroup[]; 33 | 34 | /** 35 | * Optional className to apply to the column group header. 36 | */ 37 | className?: string; 38 | 39 | /** 40 | * Optional style to apply to the column group header. 41 | * Can be a style object or a function which receives the current theme and 42 | * returns a style object. 43 | */ 44 | style?: MantineStyleProp; 45 | }; 46 | -------------------------------------------------------------------------------- /app/examples/column-grouping/ColumnGroupingExample.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DataTable } from '__PACKAGE__'; 4 | import { companies } from '~/data'; 5 | 6 | export function ColumnGroupingExample() { 7 | return ( 8 | `(min-width: ${theme.breakpoints.md})` }, 19 | ], 20 | }, 21 | { 22 | id: 'contact-info', 23 | title: 'Contact information', 24 | textAlign: 'center', 25 | style: (theme) => ({ color: theme.colors.blue[6] }), 26 | columns: [{ accessor: 'streetAddress' }, { accessor: 'city' }, { accessor: 'state', textAlign: 'right' }], 27 | }, 28 | // 👇 all columns in this group are hidden, so it will not be rendered 29 | { 30 | id: 'other', 31 | columns: [{ accessor: 'id', hidden: true }], 32 | }, 33 | // 👇 this group has no columns, so it will not be rendered 34 | { 35 | id: 'empty-group', 36 | title: 'Empty group', 37 | columns: [], 38 | }, 39 | ]} 40 | /> 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /app/examples/basic-table-properties/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Route } from 'next'; 2 | import { MANTINE_LINK } from '~/app/config'; 3 | import { ExternalLink } from '~/components/ExternalLink'; 4 | import { PageNavigation } from '~/components/PageNavigation'; 5 | import { PageTitle } from '~/components/PageTitle'; 6 | import { Txt } from '~/components/Txt'; 7 | import { readCodeFile } from '~/lib/code'; 8 | import { getRouteMetadata } from '~/lib/utils'; 9 | import { BasicTablePropertiesPageContent } from './BasicTablePropertiesPageContent'; 10 | 11 | const PATH: Route = '/examples/basic-table-properties'; 12 | 13 | export const metadata = getRouteMetadata(PATH); 14 | 15 | export default async function BasicTablePropertiesExamplePage() { 16 | const code = await readCodeFile(`${PATH}/BasicTablePropertiesExample.tsx`); 17 | 18 | return ( 19 | <> 20 | 21 | 22 | The DataTable component exposes some basic properties of the internal{' '} 23 | Mantine Table component and implements a number 24 | of additional ones. Try customizing some of them interactively below: 25 | 26 | 27 | Head over to the next example to discover more features. 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleTriggerAlways.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Box, Group, Stack } from '@mantine/core'; 4 | import { DataTable } from '__PACKAGE__'; 5 | import { companies } from '~/data'; 6 | import classes from './RowExpansionExampleTriggerAlways.module.css'; 7 | 8 | const records = companies.slice(0, 5); 9 | 10 | export function RowExpansionExampleTriggerAlways() { 11 | // example-start 12 | return ( 13 | ( 22 | 23 | 24 |
Postal address:
25 |
26 | {record.streetAddress}, {record.city}, {record.state} 27 |
28 |
29 | 30 |
Mission statement:
31 | “{record.missionStatement}” 32 |
33 |
34 | ), 35 | // example-resume 36 | }} 37 | /> 38 | ); 39 | // example-end 40 | } 41 | -------------------------------------------------------------------------------- /app/examples/disabling-text-selection/DisablingTextSelectionExamplePageContent.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Center, Paper, Switch } from '@mantine/core'; 4 | import { useCallback, useEffect, useState } from 'react'; 5 | import { CodeBlock } from '~/components/CodeBlock'; 6 | import { DisablingTextSelectionExample } from './DisablingTextSelectionExample'; 7 | 8 | export function DisablingTextSelectionExamplePageContent({ initialCode }: { initialCode: string }) { 9 | const [textSelectionDisabled, setTextSelectionDisabled] = useState(false); 10 | 11 | const adjustCode = useCallback( 12 | () => 13 | initialCode.replace(/( +)textSelectionDisabled=.*\n/, (_, spaces) => 14 | textSelectionDisabled ? `${spaces}textSelectionDisabled\n` : '' 15 | ), 16 | [initialCode, textSelectionDisabled] 17 | ); 18 | 19 | const [code, setCode] = useState(adjustCode()); 20 | 21 | useEffect(() => { 22 | setCode(adjustCode()); 23 | }, [adjustCode]); 24 | 25 | return ( 26 | <> 27 | 28 |
29 | setTextSelectionDisabled((v) => !v)} 33 | /> 34 |
35 |
36 | 37 | 38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleMultipleExpandedRows.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Box, Group, Stack } from '@mantine/core'; 4 | import { DataTable } from '__PACKAGE__'; 5 | import { companies } from '~/data'; 6 | import classes from './RowExpansionExampleMultipleExpandedRows.module.css'; 7 | 8 | const records = companies.slice(0, 5); 9 | 10 | export function RowExpansionExampleMultipleExpandedRows() { 11 | // example-start 12 | return ( 13 | ( 22 | 23 | 24 |
Postal address:
25 |
26 | {record.streetAddress}, {record.city}, {record.state} 27 |
28 |
29 | 30 |
Mission statement:
31 | “{record.missionStatement}” 32 |
33 |
34 | ), 35 | // example-resume 36 | }} 37 | /> 38 | ); 39 | // example-end 40 | } 41 | -------------------------------------------------------------------------------- /components/TrustedBy.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | margin-top: calc(var(--mantine-spacing-lg) * 2); 3 | border-top: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); 4 | } 5 | 6 | .title { 7 | text-align: center; 8 | color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); 9 | font-size: 1em; 10 | margin: var(--mantine-spacing-xl) 0; 11 | @media (min-width: $mantine-breakpoint-sm) { 12 | font-size: 1.25em; 13 | } 14 | } 15 | 16 | .links { 17 | display: flex; 18 | align-items: center; 19 | justify-content: center; 20 | flex-wrap: wrap; 21 | gap: var(--mantine-spacing-lg); 22 | @media (min-width: $mantine-breakpoint-sm) { 23 | gap: var(--mantine-spacing-xl); 24 | } 25 | } 26 | 27 | .link { 28 | display: flex; 29 | align-items: center; 30 | justify-content: center; 31 | gap: var(--mantine-spacing-xs); 32 | height: rem(24px); 33 | text-decoration: none; 34 | @media (min-width: $mantine-breakpoint-sm) { 35 | height: rem(36px); 36 | } 37 | } 38 | 39 | .text { 40 | font-weight: 700; 41 | @media (min-width: $mantine-breakpoint-sm) { 42 | margin-top: rem(-4px); 43 | font-size: 1.25rem; 44 | } 45 | } 46 | 47 | .dark { 48 | @mixin dark { 49 | display: block; 50 | } 51 | 52 | @mixin light { 53 | display: none; 54 | } 55 | } 56 | 57 | .light { 58 | @mixin light { 59 | display: block; 60 | } 61 | 62 | @mixin dark { 63 | display: none; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /package/DataTablePagination.css: -------------------------------------------------------------------------------- 1 | .mantine-datatable-pagination { 2 | background: inherit; 3 | border-top: rem(1px) solid var(--mantine-datatable-border-color); 4 | display: flex; 5 | align-items: center; 6 | justify-content: space-between; 7 | gap: var(--mantine-spacing-xs); 8 | } 9 | 10 | .mantine-datatable-pagination-text { 11 | flex: 1 1 auto; 12 | } 13 | 14 | .mantine-datatable-pagination-pages { 15 | --mantine-datatable-pagination-active-text-color: var( 16 | light-dark( 17 | --mantine-datatable-pagination-active-text-color-light, 18 | --mantine-datatable-pagination-active-text-color-dark 19 | ), 20 | var(--mantine-color-white) 21 | ); 22 | --mantine-datatable-pagination-active-background-color: var( 23 | light-dark( 24 | --mantine-datatable-pagination-active-background-color-light, 25 | --mantine-datatable-pagination-active-background-color-dark 26 | ), 27 | var(--mantine-primary-color-filled) 28 | ); 29 | 30 | opacity: 1; 31 | transition: opacity 0.2s; 32 | } 33 | 34 | .mantine-datatable-pagination-pages-fetching { 35 | opacity: 0; 36 | } 37 | 38 | .mantine-datatable-pagination-pages-control { 39 | color: var(--mantine-datatable-color); 40 | border-color: var(--mantine-datatable-border-color); 41 | 42 | &[data-active] { 43 | color: var(--mantine-datatable-pagination-active-text-color); 44 | background: var(--mantine-datatable-pagination-active-background-color); 45 | border-color: transparent; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/examples/expanding-rows/RowExpansionExampleExpandableRows.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Box, Group, Stack } from '@mantine/core'; 4 | import { DataTable } from '__PACKAGE__'; 5 | import { companies } from '~/data'; 6 | import classes from './RowExpansionExampleExpandableRows.module.css'; 7 | 8 | const records = companies.slice(0, 5); 9 | 10 | export function RowExpansionExampleExpandableRows() { 11 | // example-start 12 | return ( 13 | state === 'WY', // 👈 enable expansion only on rows where state is WY 21 | // example-skip 22 | content: ({ record }) => ( 23 | 24 | 25 |
Postal address:
26 |
27 | {record.streetAddress}, {record.city}, {record.state} 28 |
29 |
30 | 31 |
Mission statement:
32 | “{record.missionStatement}” 33 |
34 |
35 | ), 36 | // example-resume 37 | }} 38 | /> 39 | ); 40 | // example-end 41 | } 42 | -------------------------------------------------------------------------------- /app/examples/infinite-scrolling/page.tsx: -------------------------------------------------------------------------------- 1 | import { Code } from '@mantine/core'; 2 | import type { Route } from 'next'; 3 | import { CodeBlock } from '~/components/CodeBlock'; 4 | import { PageNavigation } from '~/components/PageNavigation'; 5 | import { PageTitle } from '~/components/PageTitle'; 6 | import { Txt } from '~/components/Txt'; 7 | import { readCodeFile } from '~/lib/code'; 8 | import { getRouteMetadata } from '~/lib/utils'; 9 | import { InfiniteScrollingExample } from './InfiniteScrollingExample'; 10 | 11 | const PATH: Route = '/examples/infinite-scrolling'; 12 | 13 | export const metadata = getRouteMetadata(PATH); 14 | 15 | export default async function InfiniteScrollingExamplePage() { 16 | const code = await readCodeFile(`${PATH}/InfiniteScrollingExample.tsx`); 17 | 18 | return ( 19 | <> 20 | 21 | Scroll the table to the bottom to see it in action: 22 | 23 | 24 | The DataTable triggers onScrollToTop, onScrollToBottom, onScrollToRight{' '} 25 | and onScrollToLeft callbacks when user scrolls to the top, bottom, left or right edge of the table. 26 | You can use the onScrollToBottom event to implement infinite scrolling, like so: 27 | 28 | 29 | Head over to the next example to discover more features. 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /components/AppWrapper.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { AppShell, AppShellMain, Container } from '@mantine/core'; 4 | import { useDisclosure, useResizeObserver } from '@mantine/hooks'; 5 | import { useEffect } from 'react'; 6 | import classes from './AppWrapper.module.css'; 7 | import { Footer } from './Footer'; 8 | import { Header } from './Header'; 9 | import { Navbar } from './Navbar'; 10 | import { TrustedBy } from './TrustedBy'; 11 | 12 | export function AppWrapper({ children }: React.PropsWithChildren) { 13 | const [navbarExpanded, { toggle: toggleNavbar, close: collapseNavbar }] = useDisclosure(false); 14 | const [ref] = useResizeObserver(); 15 | 16 | useEffect(() => { 17 | document.body.classList.toggle('noscroll', navbarExpanded); 18 | }, [navbarExpanded]); 19 | 20 | return ( 21 | 27 |
28 | 29 | 30 |
31 | {children} 32 | 33 |
34 |
35 |