├── .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 |

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 |
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 |
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 |
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 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/package/icons/IconFilter.tsx:
--------------------------------------------------------------------------------
1 | export function IconFilter() {
2 | return (
3 |
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 |
9 | table component
10 |
11 |
12 | for your data-rich
13 |
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 |
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 | (location.hash = id)}>
18 | {value}
19 |
20 |
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 |
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 | }
14 | component="a"
15 | aria-label={`View ${PRODUCT_NAME} package on npm`}
16 | href={NPM_LINK}
17 | target="_blank"
18 | >
19 | {downloads}
20 |
21 | nth
22 |
23 |
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 |
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 |
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 |
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 | }
14 | component="a"
15 | aria-label={`View ${PRODUCT_NAME} source code on GitHub`}
16 | href={REPO_LINK}
17 | target="_blank"
18 | >
19 | Source
20 |
21 | code
22 |
23 |
24 |
25 | }
30 | component="a"
31 | aria-label={`Sponsor the author of ${PRODUCT_NAME} on GitHub Sponsors`}
32 | href={SPONSORS_LINK}
33 | target="_blank"
34 | >
35 | Sponsor
36 |
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 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/app/examples/expanding-rows/RowExpansionExampleInitiallyExpandedRows.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 './RowExpansionExampleInitiallyExpandedRows.module.css';
7 |
8 | const records = companies.slice(0, 5);
9 |
10 | export function RowExpansionExampleInitiallyExpandedRows() {
11 | // example-start
12 | return (
13 | state === 'WY', // 👈 expand 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/default-column-render/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 { DefaultColumnRenderExample } from './DefaultColumnRenderExample';
10 |
11 | const PATH: Route = '/examples/default-column-render';
12 |
13 | export const metadata = getRouteMetadata(PATH);
14 |
15 | export default async function DefaultColumnRenderExamplePage() {
16 | const code = await readCodeFile(`${PATH}/DefaultColumnRenderExample.tsx`);
17 |
18 | return (
19 | <>
20 |
21 |
22 | If you provide a defaultColumnRender prop to the table, it will be used to render all columns that
23 | do not provide a custom render function.
24 |
25 | The defaultColumnRender function receives the current row, its index and accessor name and should
26 | return a ReactNode:
27 |
28 |
29 | The code above will produce the following result:
30 |
31 | Head over to the next example to learn more.
32 |
33 | >
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/package/DataTableFooterCell.tsx:
--------------------------------------------------------------------------------
1 | import { TableTh, type MantineStyleProp, type MantineTheme } from '@mantine/core';
2 | import clsx from 'clsx';
3 | import { useMediaQueryStringOrFunction } from './hooks';
4 | import type { DataTableColumn } from './types';
5 | import { ELLIPSIS, NOWRAP, TEXT_ALIGN_CENTER, TEXT_ALIGN_LEFT, TEXT_ALIGN_RIGHT } from './utilityClasses';
6 |
7 | type DataTableFooterCellProps = {
8 | className: string | undefined;
9 | style: MantineStyleProp | undefined;
10 | visibleMediaQuery: string | ((theme: MantineTheme) => string) | undefined;
11 | title: React.ReactNode | undefined;
12 | } & Pick, 'noWrap' | 'ellipsis' | 'textAlign' | 'width'>;
13 |
14 | export function DataTableFooterCell({
15 | className,
16 | style,
17 | visibleMediaQuery,
18 | title,
19 | noWrap,
20 | ellipsis,
21 | textAlign,
22 | width,
23 | }: DataTableFooterCellProps) {
24 | if (!useMediaQueryStringOrFunction(visibleMediaQuery)) return null;
25 | return (
26 |
46 | {title}
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/app/examples/expanding-rows/RowExpansionExampleCollapseProps.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 './RowExpansionExampleCollapseProps.module.css';
7 |
8 | const records = companies.slice(0, 5);
9 |
10 | export function RowExpansionExampleCollapseProps() {
11 | // example-start
12 | return (
13 | (
28 |
29 |
30 | Postal address:
31 |
32 | {record.streetAddress}, {record.city}, {record.state}
33 |
34 |
35 |
36 | Mission statement:
37 | “{record.missionStatement}”
38 |
39 |
40 | ),
41 | // example-resume
42 | }}
43 | />
44 | );
45 | // example-end
46 | }
47 |
--------------------------------------------------------------------------------
/app/examples/custom-row-or-cell-attributes/CustomRowOrCellAttributesExamples.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { showNotification } from '@mantine/notifications';
4 | import { DataTable } from '__PACKAGE__';
5 | import { companies } from '~/data';
6 |
7 | const records = companies.slice(0, 5);
8 |
9 | export function CustomRowOrCellAttributesExample() {
10 | // example-start custom-row-or-cell-attributes
11 | return (
12 | ({ 'data-cy-city': city }) },
17 | { accessor: 'state', customCellAttributes: ({ state }) => ({ 'data-cy-state': state }) },
18 | ]}
19 | records={records}
20 | customRowAttributes={({ id, name }, recordIndex) => ({
21 | 'data-cy-id': id,
22 | 'data-cy-name': name,
23 | 'data-cy-index': recordIndex,
24 | })}
25 | />
26 | );
27 | // example-end
28 | }
29 |
30 | export function CustomRowOrCellAttributesMiddleClickExample() {
31 | // example-start middle-click
32 | return (
33 | ({
38 | onMouseDown: (e: MouseEvent) => {
39 | if (e.button === 1) {
40 | showNotification({ message: `Middle-click on row ${name}`, withBorder: true });
41 | }
42 | },
43 | })}
44 | />
45 | );
46 | // example-end
47 | }
48 |
--------------------------------------------------------------------------------
/app/examples/sorting/SortingExampleCustomIcons.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { IconChevronUp, IconSelector } from '@tabler/icons-react';
4 | import type { DataTableSortStatus } from '__PACKAGE__';
5 | import { DataTable } from '__PACKAGE__';
6 | import sortBy from 'lodash/sortBy';
7 | import { useEffect, useState } from 'react';
8 | import { companies, type Company } from '~/data';
9 |
10 | export default function SortingExampleCustomIcons() {
11 | const [sortStatus, setSortStatus] = useState>({
12 | columnAccessor: 'name',
13 | direction: 'asc',
14 | });
15 | const [records, setRecords] = useState(sortBy(companies, 'name'));
16 |
17 | useEffect(() => {
18 | const data = sortBy(companies, sortStatus.columnAccessor) as Company[];
19 | // eslint-disable-next-line react-hooks/set-state-in-effect
20 | setRecords(sortStatus.direction === 'desc' ? data.reverse() : data);
21 | }, [sortStatus]);
22 |
23 | return (
24 | ,
38 | unsorted: ,
39 | }}
40 | />
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/components/CheckableSegmentedControl.tsx:
--------------------------------------------------------------------------------
1 | import { Checkbox, Code, SegmentedControl, Text, rem } from '@mantine/core';
2 | import { useId } from '@mantine/hooks';
3 | import clsx from 'clsx';
4 | import classes from './CheckableSegmentedControl.module.css';
5 |
6 | export type CheckableSegmentedControlProps = {
7 | className?: string;
8 | label: string;
9 | documentAs?: string;
10 | data: string[];
11 | checked: boolean;
12 | onCheckedChange: (checked: boolean) => void;
13 | value: string;
14 | onChange: (value: string) => void;
15 | };
16 |
17 | export function CheckableSegmentedControl({
18 | className,
19 | label,
20 | documentAs,
21 | data,
22 | checked,
23 | onCheckedChange,
24 | value,
25 | onChange,
26 | }: CheckableSegmentedControlProps) {
27 | const id = useId();
28 | return (
29 |
30 |
37 | {label}
38 |
39 | {documentAs &&
{documentAs}}
40 |
41 | onCheckedChange(e.target.checked)} />
42 |
50 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/components/NavbarButton.module.css:
--------------------------------------------------------------------------------
1 | .link,
2 | .root {
3 | text-decoration: none;
4 | }
5 |
6 | .root {
7 | display: flex;
8 | align-items: center;
9 | gap: var(--mantine-spacing-xs);
10 | padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
11 | cursor: pointer;
12 | user-select: none;
13 | color: var(--mantine-color-text);
14 | background: light-dark(var(--navbar-button-bg-color-light), var(--navbar-button-bg-color-dark));
15 | transition: background 0.2s;
16 | &:hover {
17 | background: light-dark(var(--navbar-button-hover-bg-color-light), var(--navbar-button-hover-bg-color-dark));
18 | }
19 | &:active {
20 | .icon:not(.expanded),
21 | .dot {
22 | transform: scale(0.9);
23 | }
24 | .text {
25 | transform: translateY(1px);
26 | }
27 | }
28 | }
29 |
30 | .iconWrapper {
31 | flex: 0 0 auto;
32 | width: rem(28px);
33 | height: rem(28px);
34 | border-radius: 50%;
35 | }
36 |
37 | .icon,
38 | .dot {
39 | transition: all 0.15s;
40 | }
41 |
42 | .expanded {
43 | transform: rotateZ(90deg);
44 | }
45 |
46 | .dotWrapper {
47 | padding: rem(2px);
48 | background: light-dark(white, var(--mantine-color-body));
49 | border-radius: 50%;
50 | border: 1px solid;
51 | margin-left: rem(7px);
52 | margin-right: rem(6px);
53 | }
54 |
55 | .dot {
56 | width: rem(8px);
57 | height: rem(8px);
58 | border-radius: 50%;
59 | }
60 |
61 | /* todo remove this firefox fix if the problem is solved in Mantine */
62 | @-moz-document url-prefix() {
63 | .text {
64 | margin-top: 0.125em;
65 | margin-bottom: -0.125em;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/type-definitions/page.tsx:
--------------------------------------------------------------------------------
1 | import type { Route } from 'next';
2 | import { readdir } from 'node:fs/promises';
3 | import { PRODUCT_NAME } from '~/app/config';
4 | import { CodeBlock } from '~/components/CodeBlock';
5 | import { PageNavigation } from '~/components/PageNavigation';
6 | import { PageTitle } from '~/components/PageTitle';
7 | import { Txt } from '~/components/Txt';
8 | import { readCodeFile } from '~/lib/code';
9 | import { allPromiseProps, getRouteMetadata } from '~/lib/utils';
10 |
11 | const PATH: Route = '/type-definitions';
12 | export const metadata = getRouteMetadata(PATH);
13 |
14 | const TYPE_FILES_PATH = 'package/types';
15 |
16 | export default async function TypeDefinitionsPage() {
17 | const filesNames = await readdir(TYPE_FILES_PATH);
18 |
19 | const files = await Promise.all(
20 | filesNames
21 | .filter((name) => name !== 'index.ts')
22 | .map((name) => allPromiseProps({ [name]: readCodeFile(`/../${TYPE_FILES_PATH}/${name}`) }))
23 | );
24 | return (
25 | <>
26 |
27 |
28 | {PRODUCT_NAME} is written in TypeScript and the component properties are well documented with additional JSDoc
29 | annotations, so you can harness the full power of your IDE to build type safe applications with confidence.
30 |
31 | Here are the {PRODUCT_NAME} type files:
32 | {files.map((code) => {
33 | const [name] = Object.keys(code);
34 | return ;
35 | })}
36 |
37 | >
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/app/examples/column-dragging-and-toggling/DraggingTogglingResetExample.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { IconColumnRemove, IconColumns3 } from '@tabler/icons-react';
4 | import { DataTable, useDataTableColumns } from '__PACKAGE__';
5 | import { useContextMenu } from 'mantine-contextmenu';
6 | import { companies } from '~/data';
7 |
8 | export default function DraggingTogglingResetExample() {
9 | const { showContextMenu } = useContextMenu();
10 |
11 | const key = 'toggleable-reset-example';
12 |
13 | const { effectiveColumns, resetColumnsOrder, resetColumnsToggle } = useDataTableColumns({
14 | key,
15 | columns: [
16 | { accessor: 'name', width: '40%', toggleable: true, draggable: true },
17 | { accessor: 'streetAddress', width: '60%', toggleable: true, draggable: true },
18 | { accessor: 'city', width: 160, toggleable: true, draggable: true },
19 | { accessor: 'state', textAlign: 'right' },
20 | ],
21 | });
22 |
23 | return (
24 |
31 | showContextMenu([
32 | {
33 | key: 'reset-toggled-columns',
34 | icon: ,
35 | onClick: resetColumnsToggle,
36 | },
37 | {
38 | key: 'reset-columns-order',
39 | icon: ,
40 | onClick: resetColumnsOrder,
41 | },
42 | ])(event)
43 | }
44 | />
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/app/examples/using-with-auto-animate/page.tsx:
--------------------------------------------------------------------------------
1 | import { Code } from '@mantine/core';
2 | import type { Route } from 'next';
3 | import { CodeBlock } from '~/components/CodeBlock';
4 | import { ExternalLink } from '~/components/ExternalLink';
5 | import { PageNavigation } from '~/components/PageNavigation';
6 | import { PageTitle } from '~/components/PageTitle';
7 | import { Txt } from '~/components/Txt';
8 | import { readCodeFile } from '~/lib/code';
9 | import { getRouteMetadata } from '~/lib/utils';
10 | import { UsingWithAutoAnimateExample } from './UsingWithAutoAnimateExample';
11 |
12 | const PATH: Route = '/examples/using-with-auto-animate';
13 |
14 | export const metadata = getRouteMetadata(PATH);
15 |
16 | export default async function UsingWithAutoAnimateExamplePage() {
17 | const code = await readCodeFile(`${PATH}/UsingWithAutoAnimateExample.tsx`);
18 |
19 | return (
20 | <>
21 |
22 |
23 | The DataTable component exposes a bodyRef property that can be used to pass a ref to
24 | the underlying table tbody element. This ref can be passed to the useAutoAnimate(){' '}
25 | hook from the excellent AutoAnimate library
26 | to animate table rows when they are added, removed or reordered.
27 |
28 |
29 | Here is the code:
30 |
31 | Head over to the next example to learn more.
32 |
33 | >
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/app/examples/basic-usage/page.tsx:
--------------------------------------------------------------------------------
1 | import { Code } from '@mantine/core';
2 | import type { Route } from 'next';
3 | import { PRODUCT_NAME } from '~/app/config';
4 | import { CodeBlock } from '~/components/CodeBlock';
5 | import { PageNavigation } from '~/components/PageNavigation';
6 | import { PageTitle } from '~/components/PageTitle';
7 | import { Txt } from '~/components/Txt';
8 | import { readCodeFile } from '~/lib/code';
9 | import { allPromiseProps, getRouteMetadata } from '~/lib/utils';
10 | import { BasicUsageExample } from './BasicUsageExample';
11 |
12 | const PATH: Route = '/examples/basic-usage';
13 |
14 | export const metadata = getRouteMetadata(PATH);
15 |
16 | export default async function BasicUsageExamplePage() {
17 | const code = await allPromiseProps({
18 | 'BasicUsageExample.tsx': readCodeFile(`${PATH}/BasicUsageExample.tsx`),
19 | 'companies.json': readCodeFile('/../data/companies.json'),
20 | });
21 |
22 | return (
23 | <>
24 |
25 |
26 | In its most basic usage scenario, the DataTable component only requires records and{' '}
27 | columns properties to be set:
28 |
29 |
30 | The code above will produce the following result:
31 |
32 |
33 | However, there’s much more you can do with {PRODUCT_NAME}.
34 |
35 | Head over to the next example to learn about its basic properties.
36 |
37 |
38 | >
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/app/mantine-v6-support/page.tsx:
--------------------------------------------------------------------------------
1 | import type { Route } from 'next';
2 | import { MANTINE_LINK, PRODUCT_NAME, REPO_LINK, V6_WEBSITE_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 { getRouteMetadata } from '~/lib/utils';
8 |
9 | const PATH: Route = '/mantine-v6-support';
10 | export const metadata = getRouteMetadata(PATH);
11 |
12 | export default function MantineV6SupportPage() {
13 | return (
14 | <>
15 |
16 |
17 | If you are still using Mantine V6, you’ll need to use{' '}
18 | {PRODUCT_NAME} V6.
19 |
20 |
21 | {PRODUCT_NAME} V7 was a major release with{' '}
22 | breaking changes and was compatible with{' '}
23 | Mantine V7+.
24 |
25 | One of the breaking changes in Mantine V7 was the migration to native CSS.
26 |
27 | In Mantine V6, the styling was done with CSS-in-JS (Emotion
28 | ), while in Mantine V7 it is done with native CSS.
29 |
30 | Hence, you won’t be able to use {PRODUCT_NAME} V7+ with Mantine V6.
31 |
32 |
33 | >
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/components/VersionBadge.tsx:
--------------------------------------------------------------------------------
1 | import { Badge, Box, Popover, PopoverDropdown, PopoverTarget, Text } from '@mantine/core';
2 | import { IconAlertSquareRoundedFilled, IconChevronDown, IconExternalLink } from '@tabler/icons-react';
3 | import { V6_WEBSITE_LINK } from '~/app/config';
4 | import { ExternalLink } from './ExternalLink';
5 | import classes from './VersionBadge.module.css';
6 |
7 | export function VersionBadge() {
8 | return (
9 |
10 |
11 |
17 |
18 |
19 | }
20 | role="button"
21 | >
22 | {process.env.PACKAGE_VERSION}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | This version works with Mantine V8.x
32 |
33 | (and probably V7.x).
34 |
35 |
36 | If you’re still using Mantine V6, check the old
37 | version{' '}
38 |
39 | here
40 |
41 |
42 | .
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/app/examples/default-column-properties/page.tsx:
--------------------------------------------------------------------------------
1 | import { Code } from '@mantine/core';
2 | import type { Route } from 'next';
3 | import { CodeBlock } from '~/components/CodeBlock';
4 | import { InternalLink } from '~/components/InternalLink';
5 | import { PageNavigation } from '~/components/PageNavigation';
6 | import { PageTitle } from '~/components/PageTitle';
7 | import { Txt } from '~/components/Txt';
8 | import { readCodeFile } from '~/lib/code';
9 | import { getRouteMetadata } from '~/lib/utils';
10 | import { DefaultColumnPropertiesExample } from './DefaultColumnPropertiesExample';
11 |
12 | const PATH: Route = '/examples/default-column-properties';
13 |
14 | export const metadata = getRouteMetadata(PATH);
15 |
16 | export default async function DefaultColumnPropertiesExamplePage() {
17 | const code = await readCodeFile(`${PATH}/DefaultColumnPropertiesExample.tsx`);
18 |
19 | return (
20 | <>
21 |
22 |
23 | If you find yourself repeating the same properties over and over again for multiple columns, you can use{' '}
24 | defaultColumnProps (which accepts a subset of{' '}
25 | column properties) to set them once
26 | and use as a fallback for all columns.
27 |
28 |
29 | Here is the code for the above example:
30 |
31 |
32 | In certain scenarios, you can also use a render fallback function. Head over to the next example to learn more.
33 |
34 |
35 | >
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/app/examples/default-column-render/DefaultColumnRenderExample.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable } from '__PACKAGE__';
4 | import dayjs from 'dayjs';
5 | import relativeTime from 'dayjs/plugin/relativeTime';
6 |
7 | dayjs.extend(relativeTime);
8 |
9 | export function DefaultColumnRenderExample() {
10 | return (
11 | {
34 | const data = row[accessor as keyof typeof row];
35 | return typeof data === 'string' ? data : dayjs(data).fromNow();
36 | }}
37 | />
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/package/DataTableDraggableRow.tsx:
--------------------------------------------------------------------------------
1 | import { TableTr } from '@mantine/core';
2 | import { useMergedRef } from '@mantine/hooks';
3 | import type { Ref } from 'react';
4 | import { useEffect, useRef } from 'react';
5 | import type { DataTableDraggableRowProps } from './types';
6 |
7 | export function DataTableDraggableRow({
8 | className,
9 | children,
10 | isDragging,
11 | ref: refProp,
12 | ...otherProps
13 | }: DataTableDraggableRowProps & {
14 | ref?: Ref;
15 | }) {
16 | const ref = useRef(null);
17 | const mergedRef = useMergedRef(ref, refProp);
18 |
19 | useEffect(() => {
20 | // a simple fix to keep column width as in table
21 | if (!ref.current) return;
22 | if (!isDragging) return;
23 |
24 | const tbody = ref.current.parentElement!;
25 | const table = tbody.parentElement!;
26 | const thead = table.children[0];
27 | const headerRow = thead.children[0];
28 |
29 | for (let index = 0; index < headerRow.children.length; index++) {
30 | const headerCell = headerRow.children[index];
31 | const headerCellDimensions = headerCell.getBoundingClientRect();
32 |
33 | const cell = ref.current.children[index] as HTMLTableCellElement;
34 |
35 | cell.style.height = headerCellDimensions.height + 'px';
36 | cell.style.width = headerCellDimensions.width + 'px';
37 | cell.style.minWidth = headerCellDimensions.width + 'px';
38 | cell.style.maxWidth = headerCellDimensions.width + 'px';
39 | }
40 | }, [isDragging, children]);
41 |
42 | return (
43 |
44 | {children}
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/scripts/generate-docs-data.mjs:
--------------------------------------------------------------------------------
1 | import { faker } from '@faker-js/faker';
2 | import lodash from 'lodash';
3 | import { writeFile } from 'node:fs/promises';
4 |
5 | const companies = [];
6 | const departments = [];
7 | const employees = [];
8 |
9 | for (let i = 0; i < 10; i++) {
10 | const companyId = faker.string.uuid();
11 |
12 | companies.push({
13 | id: companyId,
14 | name: faker.company.name(),
15 | streetAddress: faker.location.streetAddress(),
16 | city: faker.location.city(),
17 | state: faker.location.state({ abbreviated: true }),
18 | missionStatement: lodash.upperFirst(faker.company.buzzPhrase()) + '.',
19 | });
20 |
21 | const departmentCount = faker.number.int({ min: 5, max: 15 });
22 | faker.helpers.uniqueArray(faker.commerce.department, departmentCount).forEach((name) => {
23 | departments.push({ id: faker.string.uuid(), companyId, name });
24 | });
25 | }
26 |
27 | for (let i = 0; i < 500; i++) {
28 | const sex = faker.person.sex();
29 | const firstName = faker.person.firstName(sex);
30 | const lastName = faker.person.lastName(sex);
31 | employees.push({
32 | id: faker.string.uuid(),
33 | sex,
34 | firstName,
35 | lastName,
36 | email: faker.internet.email({ firstName, lastName }),
37 | birthDate: faker.date.birthdate(),
38 | departmentId: faker.helpers.arrayElement(departments).id,
39 | });
40 | }
41 |
42 | const DATA_FOLDER = new URL('.', import.meta.url).pathname + '/../data';
43 | await writeFile(`${DATA_FOLDER}/companies.json`, JSON.stringify(companies, null, 2));
44 | await writeFile(`${DATA_FOLDER}/departments.json`, JSON.stringify(departments, null, 2));
45 | await writeFile(`${DATA_FOLDER}/employees.json`, JSON.stringify(employees, null, 2));
46 |
--------------------------------------------------------------------------------
/app/examples/pagination/PaginationExampleWithPageSizeSelector.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable } from '__PACKAGE__';
4 | import dayjs from 'dayjs';
5 | import { useEffect, useState } from 'react';
6 | import employees from '~/data/employees.json';
7 |
8 | const PAGE_SIZES = [10, 15, 20];
9 |
10 | export default function PaginationExampleWithPageSizeSelector() {
11 | const [pageSize, setPageSize] = useState(PAGE_SIZES[1]);
12 | const [page, setPage] = useState(1);
13 | const [records, setRecords] = useState(employees.slice(0, pageSize));
14 |
15 | useEffect(() => {
16 | // eslint-disable-next-line react-hooks/set-state-in-effect
17 | setPage(1);
18 | }, [pageSize]);
19 |
20 | useEffect(() => {
21 | const from = (page - 1) * pageSize;
22 | const to = from + pageSize;
23 | // eslint-disable-next-line react-hooks/set-state-in-effect
24 | setRecords(employees.slice(from, to));
25 | }, [page, pageSize]);
26 |
27 | return (
28 | dayjs(birthDate).format('MMM D YYYY'),
40 | },
41 | ]}
42 | totalRecords={employees.length}
43 | paginationActiveBackgroundColor="grape"
44 | recordsPerPage={pageSize}
45 | page={page}
46 | onPageChange={(p) => setPage(p)}
47 | recordsPerPageOptions={PAGE_SIZES}
48 | onRecordsPerPageChange={setPageSize}
49 | />
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/components/PageNavigation.tsx:
--------------------------------------------------------------------------------
1 | import { Group, Text, UnstyledButton } from '@mantine/core';
2 | import { IconArrowLeft, IconArrowRight } from '@tabler/icons-react';
3 | import Link from 'next/link';
4 | import { getPageNavigationInfo } from '~/lib/utils';
5 | import classes from './PageNavigation.module.css';
6 |
7 | export type PageNavigationProps = {
8 | of: string;
9 | };
10 |
11 | export function PageNavigation({ of }: PageNavigationProps) {
12 | const { backHref, backTitle, backDescription, nextHref, nextTitle, nextDescription } = getPageNavigationInfo(of);
13 |
14 | return (
15 |
16 |
23 |
24 |
25 |
26 |
27 | Go back
28 |
29 |
30 | {backTitle}
31 |
32 |
33 |
34 |
35 |
42 |
43 |
44 | Up next
45 |
46 | {nextTitle}
47 |
48 |
49 |
50 |
51 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/package/DataTableRowSelectorCell.css:
--------------------------------------------------------------------------------
1 | .mantine-datatable-row-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 | tr[data-selected] & {
17 | background: inherit;
18 | &::before {
19 | content: '';
20 | position: absolute;
21 | top: 0;
22 | right: 0;
23 | bottom: 0;
24 | left: 0;
25 | background: var(--mantine-datatable-selection-color);
26 | }
27 | }
28 | &::after {
29 | content: '';
30 | position: absolute;
31 | top: 0;
32 | right: calc(-1 * var(--mantine-spacing-xs));
33 | bottom: 0;
34 | border-left: 1px solid var(--mantine-datatable-row-border-color);
35 | width: var(--mantine-spacing-xs);
36 | background: var(--mantine-datatable-shadow-background-left);
37 | pointer-events: none;
38 | opacity: 0;
39 | transition: opacity 0.2s;
40 |
41 | @mixin rtl {
42 | right: auto;
43 | left: calc(-1 * var(--mantine-spacing-xs));
44 | border-left: 0;
45 | border-right: 1px solid var(--mantine-datatable-row-border-color);
46 | background: var(--mantine-datatable-shadow-background-right);
47 | }
48 | }
49 |
50 | tr[data-with-row-border] &::after {
51 | top: rem(-1px);
52 | bottom: rem(-1px);
53 | }
54 |
55 | tr:last-of-type &::after {
56 | bottom: 0;
57 | }
58 |
59 | &[data-shadow-visible]::after {
60 | opacity: var(--mantine-datatable-left-shadow-opacity);
61 |
62 | @mixin rtl {
63 | opacity: var(--mantine-datatable-right-shadow-opacity);
64 | }
65 | }
66 | }
67 |
68 | .mantine-datatable-row-selector-cell-checkbox {
69 | cursor: pointer;
70 | }
71 |
--------------------------------------------------------------------------------
/package/DataTableHeaderSelectorCell.tsx:
--------------------------------------------------------------------------------
1 | import type { MantineStyleProp } from '@mantine/core';
2 | import { Checkbox, TableTh, type CheckboxProps } from '@mantine/core';
3 | import clsx from 'clsx';
4 | import type { DataTableSelectionTrigger } from './types';
5 | import { POINTER_CURSOR } from './utilityClasses';
6 |
7 | type DataTableHeaderSelectorCellProps = {
8 | className: string | undefined;
9 | style: MantineStyleProp | undefined;
10 | trigger: DataTableSelectionTrigger;
11 | shadowVisible: boolean;
12 | checked: boolean;
13 | indeterminate: boolean;
14 | checkboxProps: CheckboxProps;
15 | onChange: (() => void) | undefined;
16 | rowSpan: number | undefined;
17 | ref: React.Ref;
18 | };
19 |
20 | export function DataTableHeaderSelectorCell({
21 | className,
22 | style,
23 | trigger,
24 | shadowVisible,
25 | checked,
26 | indeterminate,
27 | checkboxProps,
28 | onChange,
29 | rowSpan,
30 | ref,
31 | }: DataTableHeaderSelectorCellProps) {
32 | const enabled = !checkboxProps.disabled;
33 |
34 | return (
35 |
48 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/app/examples/overriding-the-default-styles/StylingWithClassNamesExample.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable, uniqBy } from '__PACKAGE__';
4 | import { useEffect, useState } from 'react';
5 | import companies from '~/data/companies.json';
6 | import classes from './StylingWithClassNamesExample.module.css';
7 |
8 | const PAGE_SIZE = 4;
9 |
10 | export function StylingWithClassNamesExample() {
11 | const [page, setPage] = useState(1);
12 | const [records, setRecords] = useState(companies.slice(0, PAGE_SIZE));
13 |
14 | useEffect(() => {
15 | const from = (page - 1) * PAGE_SIZE;
16 | const to = from + PAGE_SIZE;
17 | // eslint-disable-next-line react-hooks/set-state-in-effect
18 | setRecords(companies.slice(from, to));
19 | }, [page]);
20 |
21 | // example-start
22 | return (
23 | c.missionStatement.length).reduce((acc, len) => acc + len) / companies.length
40 | }`,
41 | },
42 | { accessor: 'streetAddress' },
43 | { accessor: 'city' },
44 | { accessor: 'state', footer: `${uniqBy(companies, (c) => c.state).length} states` },
45 | ]}
46 | records={records}
47 | totalRecords={companies.length}
48 | recordsPerPage={PAGE_SIZE}
49 | page={page}
50 | onPageChange={(p) => setPage(p)}
51 | // example-resume
52 | />
53 | );
54 | // example-end
55 | }
56 |
--------------------------------------------------------------------------------
/app/examples/pagination/PaginationExampleWithControlProps.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable } from '__PACKAGE__';
4 | import dayjs from 'dayjs';
5 | import { useEffect, useState } from 'react';
6 | import employees from '~/data/employees.json';
7 |
8 | const PAGE_SIZE = 15;
9 |
10 | export default function PaginationExampleWithControlProps() {
11 | const [page, setPage] = useState(1);
12 | const [records, setRecords] = useState(employees.slice(0, PAGE_SIZE));
13 |
14 | useEffect(() => {
15 | const from = (page - 1) * PAGE_SIZE;
16 | const to = from + PAGE_SIZE;
17 | // eslint-disable-next-line react-hooks/set-state-in-effect
18 | setRecords(employees.slice(from, to));
19 | }, [page]);
20 |
21 | return (
22 | // example-start
23 | dayjs(birthDate).format('MMM D YYYY'),
37 | },
38 | ]}
39 | totalRecords={employees.length}
40 | recordsPerPage={PAGE_SIZE}
41 | page={page}
42 | onPageChange={(p) => setPage(p)}
43 | // example-resume
44 | getPaginationControlProps={(control) => {
45 | if (control === 'previous') {
46 | const title = 'Go to previous page';
47 | return { title, 'aria-label': title };
48 | } else if (control === 'next') {
49 | const title = 'Go to next page';
50 | return { title, 'aria-label': title };
51 | }
52 | return {};
53 | }}
54 | />
55 | // example-end
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/app/examples/row-dragging/page.tsx:
--------------------------------------------------------------------------------
1 | import { Code } from '@mantine/core';
2 | import type { Route } from 'next';
3 | import { PRODUCT_NAME, REPO_LINK } from '~/app/config';
4 | import { CodeBlock } from '~/components/CodeBlock';
5 | import { ExternalLink } from '~/components/ExternalLink';
6 | import { PageNavigation } from '~/components/PageNavigation';
7 | import { PageTitle } from '~/components/PageTitle';
8 | import { Txt } from '~/components/Txt';
9 | import { readCodeFile } from '~/lib/code';
10 | import { allPromiseProps, getRouteMetadata } from '~/lib/utils';
11 | import { RowDraggingExample } from './RowDraggingExample';
12 |
13 | const PATH: Route = '/examples/row-dragging';
14 |
15 | export const metadata = getRouteMetadata(PATH);
16 |
17 | export default async function BasicUsageExamplePage() {
18 | const code = await allPromiseProps({
19 | 'RowDraggingExample.tsx': readCodeFile(`${PATH}/RowDraggingExample.tsx`),
20 | 'RowDraggingExample.module.css': readCodeFile(`${PATH}/RowDraggingExample.module.css`),
21 | 'companies.json': readCodeFile('/../data/companies.json'),
22 | });
23 |
24 | return (
25 | <>
26 |
27 |
28 | Starting with v7.11.3, {PRODUCT_NAME} also supports row dragging (implemented with{' '}
29 | @hello-pangea/dnd library in{' '}
30 | this PR).
31 |
32 | Here is how you would implement it in your project:
33 |
34 |
35 | The code above will produce the following result:
36 |
37 |
38 |
39 | >
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/app/docsearch-overrides.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --docsearch-primary-color: var(--mantine-color-blue-6);
3 | --docsearch-logo-color: var(--mantine-color-blue-6);
4 | }
5 |
6 | .DocSearch-Container {
7 | z-index: 1000;
8 | position: fixed;
9 | }
10 |
11 | .DocSearch-Logo .cls-1,
12 | .DocSearch-Logo .cls-2 {
13 | fill: var(--docsearch-logo-color);
14 | }
15 |
16 | .DocSearch-Button {
17 | width: rem(30px);
18 | height: rem(30px);
19 | margin-left: 0;
20 | border-radius: rem(4px);
21 | background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6));
22 | border: rem(1px) solid light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4));
23 | padding: 0 rem(6px);
24 | &:hover {
25 | background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5));
26 | }
27 | &:hover,
28 | &:focus,
29 | &:active {
30 | box-shadow: none;
31 | }
32 | @media (min-width: $mantine-breakpoint-md) {
33 | padding: 0 rem(8px);
34 | width: rem(150px);
35 | }
36 |
37 | .DocSearch-Search-Icon {
38 | width: 0.875rem;
39 | height: 0.875rem;
40 | color: var(--mantine-color-text);
41 | }
42 | }
43 |
44 | .DocSearch-Button-Placeholder {
45 | display: none;
46 | @media (min-width: $mantine-breakpoint-md) {
47 | color: var(--mantine-color-text);
48 | font-size: var(--mantine-font-size-xs);
49 | font-weight: 600;
50 | margin-bottom: rem(-2px);
51 | display: block;
52 | }
53 | }
54 |
55 | .DocSearch-Button-Keys {
56 | justify-content: flex-end;
57 | }
58 |
59 | .DocSearch-Button-Key {
60 | display: none;
61 | @media (min-width: $mantine-breakpoint-md) {
62 | display: flex;
63 | font-size: rem(11px);
64 | padding-top: rem(3px);
65 | }
66 | }
67 |
68 | .DocSearch-Button-Key:last-child {
69 | margin-right: rem(-2px);
70 | }
71 |
72 | .DocSearch-Control-Key-Icon {
73 | margin: rem(-1px) 0 0 rem(1px);
74 | }
75 |
--------------------------------------------------------------------------------
/package/DataTableScrollArea.tsx:
--------------------------------------------------------------------------------
1 | import { Box, ScrollArea, type ScrollAreaProps } from '@mantine/core';
2 | import clsx from 'clsx';
3 |
4 | type DataTableScrollAreaProps = React.PropsWithChildren<{
5 | leftShadowBehind: boolean;
6 | rightShadowBehind: boolean | undefined;
7 | onScrollPositionChange: ScrollAreaProps['onScrollPositionChange'];
8 | viewportRef: React.Ref;
9 | scrollAreaProps: Omit | undefined;
10 | }>;
11 |
12 | export function DataTableScrollArea({
13 | leftShadowBehind,
14 | rightShadowBehind,
15 | onScrollPositionChange,
16 | children,
17 | viewportRef,
18 | scrollAreaProps,
19 | }: DataTableScrollAreaProps) {
20 | return (
21 |
32 | {children}
33 |
34 |
39 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/public/users/codeparrot.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/examples/scrollable-vs-auto-height/ScrollableVsAutoHeightExamples.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable, type DataTableColumn } from '__PACKAGE__';
4 | import { employees, type Employee } from '~/data';
5 |
6 | const records = employees.slice(0, 15);
7 |
8 | const columns: DataTableColumn[] = [
9 | { accessor: 'firstName' },
10 | { accessor: 'lastName' },
11 | { accessor: 'email' },
12 | { accessor: 'department.name', title: 'Department' },
13 | { accessor: 'department.company.name', title: 'Company', noWrap: true },
14 | { accessor: 'department.company.streetAddress', title: 'Address', noWrap: true },
15 | { accessor: 'department.company.city', title: 'City' },
16 | { accessor: 'department.company.state', title: 'State', textAlign: 'right' },
17 | ];
18 |
19 | export function ScrollableExample() {
20 | // example-start scrollable
21 | return (
22 |
32 | );
33 | // example-end
34 | }
35 |
36 | export function AutoHeightExample() {
37 | // example-start auto-height
38 | return (
39 |
48 | );
49 | // example-end
50 | }
51 |
52 | export function ScrollAreaPropsExample() {
53 | return (
54 | // example-start scroll-area-props
55 |
66 | // example-end
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/components/NavbarExamples.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Collapse } from '@mantine/core';
2 | import { useDisclosure } from '@mantine/hooks';
3 | import clsx from 'clsx';
4 | import { usePathname } from 'next/navigation';
5 | import { useEffect, useState } from 'react';
6 | import { NavbarButton, type NavbarButtonProps } from './NavbarButton';
7 | import classes from './NavbarExamples.module.css';
8 |
9 | const EXPANSION_STATE_STORAGE_KEY = `${process.env.PACKAGE_NAME}-examples-expanded`;
10 |
11 | export type NavbarExamplesProps = {
12 | items: NavbarButtonProps[];
13 | };
14 |
15 | const COLOR = 'green';
16 |
17 | export function NavbarExamples({ items }: NavbarExamplesProps) {
18 | const [expanded, { toggle, open }] = useDisclosure(false, {
19 | onOpen: () => localStorage.setItem(EXPANSION_STATE_STORAGE_KEY, 'true'),
20 | onClose: () => localStorage.removeItem(EXPANSION_STATE_STORAGE_KEY),
21 | });
22 |
23 | const pathname = usePathname();
24 |
25 | useEffect(() => {
26 | if (pathname.startsWith('/examples/') || localStorage.getItem(EXPANSION_STATE_STORAGE_KEY)) {
27 | open();
28 | }
29 | // open should not be a dependency...
30 | // eslint-disable-next-line react-hooks/exhaustive-deps
31 | }, [pathname]);
32 |
33 | const [didExpand, setDidExpand] = useState(false);
34 |
35 | return (
36 | <>
37 | {
41 | e.stopPropagation();
42 | toggle();
43 | }}
44 | expanded={expanded}
45 | />
46 | setDidExpand(expanded)} pos="relative">
47 |
52 | {items.map((item) => (
53 |
54 | ))}
55 |
56 | >
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/package/DataTableHeaderCellFilter.tsx:
--------------------------------------------------------------------------------
1 | import { ActionIcon, Popover, PopoverDropdown, PopoverTarget } from '@mantine/core';
2 | import { useClickOutside, useDisclosure } from '@mantine/hooks';
3 | import type { RefObject } from 'react';
4 | import { IconFilter } from './icons/IconFilter';
5 | import { IconFilterFilled } from './icons/IconFilterFilled';
6 | import type { DataTableColumn } from './types';
7 |
8 | type DataTableHeaderCellFilterProps = {
9 | children: DataTableColumn['filter'];
10 | filterPopoverProps: DataTableColumn['filterPopoverProps'];
11 | filterPopoverDisableClickOutside?: DataTableColumn['filterPopoverDisableClickOutside'];
12 | isActive: boolean;
13 | };
14 |
15 | export function DataTableHeaderCellFilter({
16 | children,
17 | isActive,
18 | filterPopoverProps,
19 | filterPopoverDisableClickOutside,
20 | }: DataTableHeaderCellFilterProps) {
21 | const [isOpen, { close, toggle }] = useDisclosure(false);
22 | const Icon = isActive ? IconFilterFilled : IconFilter;
23 | let ref: RefObject | undefined = useClickOutside(close);
24 | if (filterPopoverDisableClickOutside) ref = undefined;
25 |
26 | return (
27 |
28 |
29 | {
35 | e.preventDefault();
36 | toggle();
37 | }}
38 | onKeyDown={(e) => e.stopPropagation()}
39 | >
40 |
41 |
42 |
43 | e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>
44 | {typeof children === 'function' ? children({ close }) : children}
45 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/package/DataTableRowSelectorCell.tsx:
--------------------------------------------------------------------------------
1 | import type { MantineStyleProp } from '@mantine/core';
2 | import { Checkbox, TableTd, type CheckboxProps } from '@mantine/core';
3 | import clsx from 'clsx';
4 | import type { DataTableSelectionTrigger } from './types';
5 | import { POINTER_CURSOR } from './utilityClasses';
6 |
7 | type DataTableRowSelectorCellProps = {
8 | className: string | undefined;
9 | style: MantineStyleProp | undefined;
10 | record: T;
11 | index: number;
12 | trigger: DataTableSelectionTrigger;
13 | withRightShadow: boolean;
14 | checked: boolean;
15 | disabled: boolean;
16 | onChange: React.MouseEventHandler | undefined;
17 | checkboxProps: CheckboxProps | undefined;
18 | getCheckboxProps: (record: T, index: number) => CheckboxProps;
19 | };
20 |
21 | export function DataTableRowSelectorCell({
22 | className,
23 | style,
24 | record,
25 | index,
26 | trigger,
27 | onChange,
28 | withRightShadow,
29 | checkboxProps,
30 | getCheckboxProps,
31 | ...otherProps
32 | }: Readonly>) {
33 | const allCheckboxProps = { ...checkboxProps, ...getCheckboxProps(record, index) };
34 | const enabled = !otherProps.disabled && !allCheckboxProps.disabled;
35 |
36 | const handleClick: React.MouseEventHandler = (e) => {
37 | e.stopPropagation();
38 | if (trigger === 'cell' && enabled) {
39 | onChange?.(e);
40 | }
41 | };
42 |
43 | return (
44 |
54 |
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/app/examples/overriding-the-default-styles/ColorsExample.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable, uniqBy } from '__PACKAGE__';
4 | import sortBy from 'lodash/sortBy';
5 | import { useEffect, useState } from 'react';
6 | import { companies } from '~/data';
7 |
8 | const PAGE_SIZE = 4;
9 |
10 | const allCompanies = sortBy(companies, 'name');
11 |
12 | export function ColorsExample() {
13 | const [page, setPage] = useState(1);
14 | const [records, setRecords] = useState(allCompanies.slice(0, PAGE_SIZE));
15 |
16 | useEffect(() => {
17 | const from = (page - 1) * PAGE_SIZE;
18 | const to = from + PAGE_SIZE;
19 | // eslint-disable-next-line react-hooks/set-state-in-effect
20 | setRecords(allCompanies.slice(from, to));
21 | }, [page]);
22 |
23 | // example-start
24 | return (
25 | c.city).length} cities` },
41 | { accessor: 'state', width: 80, footer: `${uniqBy(allCompanies, (c) => c.state).length} states` },
42 | ]}
43 | totalRecords={allCompanies.length}
44 | recordsPerPage={PAGE_SIZE}
45 | page={page}
46 | onPageChange={(p) => setPage(p)}
47 | // example-resume
48 | />
49 | );
50 | // example-end
51 | }
52 |
--------------------------------------------------------------------------------
/app/examples/pagination/PaginationExample.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable } from '__PACKAGE__';
4 | import dayjs from 'dayjs';
5 | import { useEffect, useState } from 'react';
6 | import employees from '~/data/employees.json';
7 |
8 | const PAGE_SIZE = 15;
9 |
10 | export default function PaginationExample() {
11 | const [page, setPage] = useState(1);
12 | const [records, setRecords] = useState(employees.slice(0, PAGE_SIZE));
13 |
14 | useEffect(() => {
15 | const from = (page - 1) * PAGE_SIZE;
16 | const to = from + PAGE_SIZE;
17 | // eslint-disable-next-line react-hooks/set-state-in-effect
18 | setRecords(employees.slice(from, to));
19 | }, [page]);
20 |
21 | return (
22 | dayjs(birthDate).format('MMM D YYYY'),
35 | },
36 | ]}
37 | totalRecords={employees.length}
38 | recordsPerPage={PAGE_SIZE}
39 | page={page}
40 | onPageChange={(p) => setPage(p)}
41 | // 👇 uncomment the next line to use a custom pagination size
42 | // paginationSize="md"
43 | // 👇 uncomment the next line to use a custom loading text
44 | // loadingText="Loading..."
45 | // 👇 uncomment the next line to display a custom text when no records were found
46 | // noRecordsText="No records found"
47 | // 👇 uncomment the next line to use a custom pagination text
48 | // paginationText={({ from, to, totalRecords }) => `Records ${from} - ${to} of ${totalRecords}`}
49 | // 👇 uncomment the next lines to use custom pagination colors
50 | // paginationActiveBackgroundColor="green"
51 | // paginationActiveTextColor="#e6e348"
52 | />
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/app/examples/disabling-text-selection/page.tsx:
--------------------------------------------------------------------------------
1 | import { Code } from '@mantine/core';
2 | import type { Route } from 'next';
3 | import { InternalLink } from '~/components/InternalLink';
4 | import { PageNavigation } from '~/components/PageNavigation';
5 | import { PageTitle } from '~/components/PageTitle';
6 | import { Txt } from '~/components/Txt';
7 | import { UnorderedList } from '~/components/UnorderedList';
8 | import { readCodeFile } from '~/lib/code';
9 | import { getRouteMetadata } from '~/lib/utils';
10 | import { DisablingTextSelectionExamplePageContent } from './DisablingTextSelectionExamplePageContent';
11 |
12 | const PATH: Route = '/examples/disabling-text-selection';
13 |
14 | export const metadata = getRouteMetadata(PATH);
15 |
16 | export default async function DisablingTextSelectionExamplePage() {
17 | const code = await readCodeFile(`${PATH}/DisablingTextSelectionExample.tsx`);
18 |
19 | return (
20 | <>
21 |
22 | textSelectionDisabled
23 |
24 | The DataTable component conveniently allows you to disable text selection.
25 |
26 | For instance, for usability reasons, it would make sense to disable text selection if you:
27 |
28 |
29 |
30 | work with records selection;
31 |
32 |
33 | handle row clicks;
34 |
35 |
36 | use context-menus on touch devices;
37 |
38 |
39 | use sorting.
40 |
41 |
42 |
43 | Head over to the next example to discover more features.
44 |
45 | >
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/app/examples/column-properties-and-styling/ColumnPropertiesExample.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { DataTable } from '__PACKAGE__';
4 | import dayjs from 'dayjs';
5 | import { employees } from '~/data';
6 |
7 | const records = employees.slice(0, 10);
8 |
9 | export function ColumnPropertiesExample() {
10 | return (
11 | records.indexOf(record) + 1,
23 | },
24 | {
25 | accessor: 'name',
26 | title: 'Full name',
27 | render: ({ firstName, lastName }) => `${firstName} ${lastName}`,
28 | width: 160,
29 | },
30 | { accessor: 'email' },
31 | { accessor: 'department.name', width: 150 },
32 | {
33 | // 👇 using dot-notation to access nested object property
34 | accessor: 'department.company.name',
35 | title: 'Company',
36 | width: 150,
37 | // 👇 truncate with ellipsis if text overflows the available width
38 | ellipsis: true,
39 | },
40 | {
41 | accessor: 'birthDate',
42 | title: 'Birthday',
43 | width: 100,
44 | render: ({ birthDate }) => dayjs(birthDate).format('MMM D'),
45 | // 👇 column is only visible when screen width is over `theme.breakpoints.xs`
46 | visibleMediaQuery: (theme) => `(min-width: ${theme.breakpoints.xs})`,
47 | },
48 | {
49 | // 👇 "virtual column"
50 | accessor: 'age',
51 | width: 60,
52 | textAlign: 'right',
53 | // 👇 column is only visible when screen width is over `theme.breakpoints.xs`
54 | visibleMediaQuery: (theme) => `(min-width: ${theme.breakpoints.xs})`,
55 | render: ({ birthDate }) => dayjs().diff(birthDate, 'years'),
56 | },
57 | ]}
58 | />
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/app/examples/handling-row-clicks/HandlingRowClicksExample.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { Button, Center, Code, Stack, Text } from '@mantine/core';
4 | import { closeAllModals, openModal } from '@mantine/modals';
5 | import { DataTable } from '__PACKAGE__';
6 | import { companies } from '~/data';
7 |
8 | export function HandlingRowClicksExample() {
9 | return (
10 | // example-start
11 | {
17 | openModal({
18 | title: 'Company information',
19 | children: (
20 |
21 |
22 | You clicked on row[{index}], referring to company {record.name}.
23 |
24 | {event.shiftKey && (
25 | <>
26 | You pressed the Shift key when clicking.
27 |
28 | >
29 | )}
30 | {event.ctrlKey && (
31 | <>
32 | You pressed the Ctrl key when clicking.
33 |
34 | >
35 | )}
36 | {event.altKey && (
37 | <>
38 | You pressed the Alt key when clicking.
39 |
40 | >
41 | )}
42 | {event.metaKey && (
43 | <>
44 | You pressed the Meta key when clicking.
45 |
46 | >
47 | )}
48 |
49 |
50 |
53 |
54 |
55 | ),
56 | });
57 | }}
58 | />
59 | // example-end
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/public/users/exdatis-dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/users/exdatis-light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------