From f9326c58147a18ab6526dd75e10a1542b4ca4d0f Mon Sep 17 00:00:00 2001 From: Mohd Ahmad <61076339+MohdAhmad1@users.noreply.github.com> Date: Tue, 2 Jul 2024 23:31:36 +0530 Subject: [PATCH 01/23] created draggable table --- app/examples/duo/BasicUsageExample.tsx | 12 + app/examples/duo/page.tsx | 40 + package.json | 3 + package/DataTable.tsx | 7 +- package/DataTableDragToggleProvider.tsx | 62 +- package/DataTableHeader.tsx | 7 + package/DataTableRow.tsx | 129 +- package/types/DataTableProps.ts | 5 + pnpm-lock.yaml | 7738 +++++++++++++++++++++++ 9 files changed, 7945 insertions(+), 58 deletions(-) create mode 100644 app/examples/duo/BasicUsageExample.tsx create mode 100644 app/examples/duo/page.tsx create mode 100644 pnpm-lock.yaml diff --git a/app/examples/duo/BasicUsageExample.tsx b/app/examples/duo/BasicUsageExample.tsx new file mode 100644 index 000000000..59e29bc0d --- /dev/null +++ b/app/examples/duo/BasicUsageExample.tsx @@ -0,0 +1,12 @@ +import { DataTable } from '__PACKAGE__'; +import companies from '~/data/companies.json'; + +export function BasicUsageExample() { + return ( + + ); +} diff --git a/app/examples/duo/page.tsx b/app/examples/duo/page.tsx new file mode 100644 index 000000000..1e10e9e77 --- /dev/null +++ b/app/examples/duo/page.tsx @@ -0,0 +1,40 @@ +import { Code } from '@mantine/core'; +import type { Route } from 'next'; +import { PRODUCT_NAME } from '~/app/config'; +import { CodeBlock } from '~/components/CodeBlock'; +import { PageNavigation } from '~/components/PageNavigation'; +import { PageTitle } from '~/components/PageTitle'; +import { Txt } from '~/components/Txt'; +import { readCodeFile } from '~/lib/code'; +import { allPromiseProps, getRouteMetadata } from '~/lib/utils'; +import { BasicUsageExample } from './BasicUsageExample'; + +const PATH: Route = '/examples/basic-usage'; + +export const metadata = getRouteMetadata(PATH); + +export default async function BasicUsageExamplePage() { + const code = await allPromiseProps({ + 'BasicUsageExample.tsx': readCodeFile(`${PATH}/BasicUsageExample.tsx`), + 'companies.json': readCodeFile('/../data/companies.json'), + }); + + return ( + <> + + + In its most basic usage scenario, the DataTable component only requires records and{' '} + columns properties to be set: + + + The code above will produce the following result: + + + However, there’s much more you can do with {PRODUCT_NAME}. +
+ Head over to the next example to learn about its basic properties. +
+ + + ); +} diff --git a/package.json b/package.json index de87d54e2..9df879043 100644 --- a/package.json +++ b/package.json @@ -117,5 +117,8 @@ "@mantine/hooks": ">=7.8", "clsx": ">=2", "react": ">=18.2" + }, + "dependencies": { + "@hello-pangea/dnd": "^16.6.0" } } diff --git a/package/DataTable.tsx b/package/DataTable.tsx index 1b1b92ff9..d16adb0a2 100644 --- a/package/DataTable.tsx +++ b/package/DataTable.tsx @@ -127,6 +127,7 @@ export function DataTable({ classNames, style, styles, + draggableRows, ...otherProps }: DataTableProps) { const { @@ -262,7 +263,7 @@ export function DataTable({ const marginProperties = { m, my, mx, mt, mb, ml, mr }; return ( - + ({ {...otherProps} > {noHeader ? null : ( - + ref={headerRef} selectionColumnHeaderRef={selectionColumnHeaderRef} @@ -354,6 +355,7 @@ export function DataTable({ selectorCellShadowVisible={selectorCellShadowVisible} selectionColumnClassName={selectionColumnClassName} selectionColumnStyle={selectionColumnStyle} + draggableRows={draggableRows} /> )} @@ -424,6 +426,7 @@ export function DataTable({ selectorCellShadowVisible={selectorCellShadowVisible} selectionColumnClassName={selectionColumnClassName} selectionColumnStyle={selectionColumnStyle} + draggableRows={draggableRows} /> ); }) diff --git a/package/DataTableDragToggleProvider.tsx b/package/DataTableDragToggleProvider.tsx index 2ff9ce601..6809a769d 100644 --- a/package/DataTableDragToggleProvider.tsx +++ b/package/DataTableDragToggleProvider.tsx @@ -1,8 +1,9 @@ 'use client'; -import { useState, type Dispatch, type PropsWithChildren, type SetStateAction } from 'react'; +import React, { useState, type Dispatch, type PropsWithChildren, type SetStateAction } from 'react'; import { DataTableColumnsContextProvider } from './DataTableColumns.context'; import { DataTableColumnToggle } from './hooks'; +import { DragDropContext, Droppable } from '@hello-pangea/dnd'; type DataTableColumnsProviderProps = PropsWithChildren<{ columnsOrder: string[]; @@ -15,8 +16,29 @@ type DataTableColumnsProviderProps = PropsWithChildren<{ setColumnWidth: (accessor: string, width: string | number) => void; resetColumnsWidth: () => void; + + draggableRows?: boolean; }>; +function DraggableWrapper(props: React.PropsWithChildren<{ draggableRows?: boolean }>) { + if (!props.draggableRows) { + return props.children; + } + + return ( + + + {(provided) => ( +
+ {props.children} + {provided.placeholder} +
+ )} +
+
+ ); +} + export const DataTableColumnsProvider = (props: DataTableColumnsProviderProps) => { const { children, @@ -53,23 +75,25 @@ export const DataTableColumnsProvider = (props: DataTableColumnsProviderProps) = }; return ( - - {children} - + + + {children} + + ); }; diff --git a/package/DataTableHeader.tsx b/package/DataTableHeader.tsx index ea1109495..661397fff 100644 --- a/package/DataTableHeader.tsx +++ b/package/DataTableHeader.tsx @@ -5,6 +5,7 @@ import { PopoverDropdown, PopoverTarget, Stack, + TableTh, TableThead, TableTr, type CheckboxProps, @@ -39,6 +40,7 @@ type DataTableHeaderProps = { selectorCellShadowVisible: boolean; selectionColumnClassName: string | undefined; selectionColumnStyle: MantineStyleProp; + draggableRows?: boolean; }; export const DataTableHeader = forwardRef(function DataTableHeader( @@ -61,6 +63,7 @@ export const DataTableHeader = forwardRef(function DataTableHeader( selectorCellShadowVisible, selectionColumnClassName, selectionColumnStyle, + draggableRows, }: DataTableHeaderProps, ref: React.ForwardedRef ) { @@ -109,8 +112,12 @@ export const DataTableHeader = forwardRef(function DataTableHeader( ))} )} + {!groups && allRecordsSelectorCell} + + {draggableRows && } + {columns.map(({ hidden, ...columnProps }, index) => { if (hidden) return null; diff --git a/package/DataTableRow.tsx b/package/DataTableRow.tsx index 5b4bdf3a6..e5c959a81 100644 --- a/package/DataTableRow.tsx +++ b/package/DataTableRow.tsx @@ -1,10 +1,22 @@ -import { TableTr, type CheckboxProps, type MantineColor, type MantineStyleProp } from '@mantine/core'; +import { Draggable } from '@hello-pangea/dnd'; +import { + ActionIcon, + TableTd, + TableTr, + TableTrProps, + useComputedColorScheme, + useMantineTheme, + type CheckboxProps, + type MantineColor, + type MantineStyleProp, +} from '@mantine/core'; import clsx from 'clsx'; import { DataTableRowCell } from './DataTableRowCell'; import { DataTableRowExpansion } from './DataTableRowExpansion'; import { DataTableRowSelectorCell } from './DataTableRowSelectorCell'; import { getRowCssVariables } from './cssVariables'; import { useRowExpansion } from './hooks'; +import { IconGripVertical } from './icons/IconGripVertical'; import type { DataTableCellClickHandler, DataTableColumn, @@ -13,6 +25,7 @@ import type { DataTableSelectionTrigger, } from './types'; import { CONTEXT_MENU_CURSOR, POINTER_CURSOR } from './utilityClasses'; +import { useColorScheme } from '@mantine/hooks'; type DataTableRowProps = { record: T; @@ -48,6 +61,7 @@ type DataTableRowProps = { selectorCellShadowVisible: boolean; selectionColumnClassName: string | undefined; selectionColumnStyle: MantineStyleProp | undefined; + draggableRows?: boolean; }; export function DataTableRow({ @@ -78,46 +92,52 @@ export function DataTableRow({ selectorCellShadowVisible, selectionColumnClassName, selectionColumnStyle, -}: DataTableRowProps) { + draggableRows, +}: Readonly>) { return ( <> - { - if (expansion) { - const { isExpandable, isRowExpanded, expandOnClick, expandRow, collapseRow } = expansion; - if (isExpandable({ record, index }) && expandOnClick) { - if (isRowExpanded(record)) { - collapseRow(record); - } else { - expandRow(record); + { + if (expansion) { + const { isExpandable, isRowExpanded, expandOnClick, expandRow, collapseRow } = expansion; + if (isExpandable({ record, index }) && expandOnClick) { + if (isRowExpanded(record)) { + collapseRow(record); + } else { + expandRow(record); + } } } - } - onClick?.({ event: e, record, index }); + onClick?.({ event: e, record, index }); + }, + onDoubleClick: onDoubleClick ? (e) => onDoubleClick({ event: e, record, index }) : undefined, + onContextMenu: onContextMenu ? (e) => onContextMenu({ event: e, record, index }) : undefined, + style: [ + color || backgroundColor + ? (theme) => { + const colorValue = color?.(record, index); + const backgroundColorValue = backgroundColor?.(record, index); + return getRowCssVariables({ theme, color: colorValue, backgroundColor: backgroundColorValue }); + } + : undefined, + style?.(record, index), + ], + ...customAttributes?.(record, index), }} - onDoubleClick={onDoubleClick ? (e) => onDoubleClick({ event: e, record, index }) : undefined} - onContextMenu={onContextMenu ? (e) => onContextMenu({ event: e, record, index }) : undefined} - style={[ - color || backgroundColor - ? (theme) => { - const colorValue = color?.(record, index); - const backgroundColorValue = backgroundColor?.(record, index); - return getRowCssVariables({ theme, color: colorValue, backgroundColor: backgroundColorValue }); - } - : undefined, - style?.(record, index), - ]} - {...customAttributes?.(record, index)} + record={record} + index={index} + draggable={draggableRows} > {selectionVisible && ( @@ -134,6 +154,7 @@ export function DataTableRow({ getCheckboxProps={getSelectionCheckboxProps} /> )} + {columns.map(({ hidden, ...columnProps }, columnIndex) => { if (hidden) return null; @@ -184,7 +205,8 @@ export function DataTableRow({ /> ); })} - + + {expansion && (