Skip to content

Commit

Permalink
feat(mantine, table): allow InlineConfirm actions in table (#3721)
Browse files Browse the repository at this point in the history
chore(mantine, table): add demo for inline confirm
  • Loading branch information
GermainBergeron authored May 17, 2024
1 parent 17059aa commit ef7f822
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ import {Factory, useProps} from '@mantine/core';
import {ForwardedRef, ReactElement, ReactNode} from 'react';

import {CustomComponentThemeExtend, identity} from '../../../utils';
import {InlineConfirm} from '../../inline-confirm';
import {Table} from '../Table';
import {useTableContext} from '../TableContext';
import {TableActionsList} from './TableActionsList';

export type TableActionsStylesNames = 'actionsTarget' | 'actionsDropdown' | 'actionsTooltip';

export type TableActionsItemElements = typeof Table.ActionItem | typeof Table.ActionLabel | typeof Table.ActionDivider;
export type TableActionsItemElements =
| typeof Table.ActionItem
| typeof Table.ActionLabel
| typeof Table.ActionDivider
| typeof InlineConfirm.Target
| typeof InlineConfirm.Prompt;
export type TableActionsItems = TableActionsItemElements | Iterable<TableActionsItemElements> | null;

export interface TableActionsProps<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
useProps,
} from '@mantine/core';
import {Children, Fragment, MouseEventHandler, ReactElement, ReactNode, useState} from 'react';
import {InlineConfirm} from '../../inline-confirm';
import {useTableContext} from '../TableContext';

export type TableActionsListStylesNames = 'actionsTarget' | 'actionsDropdown' | 'actionsTooltip';
Expand Down Expand Up @@ -71,11 +72,15 @@ export const TableActionsList = (props: TableActionsListProps) => {
}

if (variant === 'split') {
const primaryActions = childrenArray.filter((child) => child.props.primary);
const secondaryActions = childrenArray.filter((child) => !child.props.primary);
const primaryActions = childrenArray.filter(
(child) => child.props.primary || child.type === InlineConfirm.Prompt,
);
const secondaryActions = childrenArray.filter(
(child) => !child.props.primary && child.type !== InlineConfirm.Prompt,
);

return (
<>
<InlineConfirm>
{primaryActions}
{secondaryActions.length > 0 ? (
<Menu withinPortal={false} {...others}>
Expand All @@ -93,26 +98,35 @@ export const TableActionsList = (props: TableActionsListProps) => {
</Menu.Dropdown>
</Menu>
) : null}
</>
</InlineConfirm>
);
}

const menuItems = childrenArray.map((child) => (child.props.primary ? <Menu.Item {...child.props} /> : child));
const prompts = childrenArray.filter((child) => child.type === InlineConfirm.Prompt);
const menuItems = childrenArray
.filter((child) => child.type !== InlineConfirm.Prompt)
.map((child) => {
const {primary, ...childProps} = child.props;
return primary ? <Menu.Item {...childProps} /> : child;
});
return (
<Menu opened={opened} onChange={onChange} {...others}>
<Menu.Target>
<Tooltip label={label} {...getStyles('actionsTooltip', {styles, classNames})}>
<ActionIcon
onClick={onClick}
variant="subtle"
{...getStyles('actionsTarget', {styles, classNames})}
>
{icon}
</ActionIcon>
</Tooltip>
</Menu.Target>
<Menu.Dropdown {...getStyles('actionsDropdown', {styles, classNames})}>{menuItems}</Menu.Dropdown>
</Menu>
<InlineConfirm>
{prompts}
<Menu opened={opened} onChange={onChange} {...others}>
<Menu.Target>
<Tooltip label={label} {...getStyles('actionsTooltip', {styles, classNames})}>
<ActionIcon
onClick={onClick}
variant="subtle"
{...getStyles('actionsTarget', {styles, classNames})}
>
{icon}
</ActionIcon>
</Tooltip>
</Menu.Target>
<Menu.Dropdown {...getStyles('actionsDropdown', {styles, classNames})}>{menuItems}</Menu.Dropdown>
</Menu>
</InlineConfirm>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {ColumnDef, createColumnHelper, InlineConfirm, showNotification, Table, useTable} from '@coveord/plasma-mantine';
import {DeleteSize16Px} from '@coveord/plasma-react-icons';
import {faker} from '@faker-js/faker';
import {useMemo} from 'react';

const columnHelper = createColumnHelper<Person>();

/**
* Define your columns outside the component rendering the table
* (or memoize them) to avoid unnecessary render loops
*/
const columns: Array<ColumnDef<Person>> = [
columnHelper.accessor('firstName', {
header: 'First name',
enableSorting: false,
}),
columnHelper.accessor('lastName', {
header: 'Last name',
enableSorting: false,
}),
columnHelper.accessor('age', {
header: 'Age',
enableSorting: false,
}),
];

const Demo = () => {
// How you manage your data and loading state is up to you
// Just make sure data is a stable reference and isn't recreated on every render
// Here for the sake of example we're building 10 rows of mock data
const data = useMemo(() => makeData(10), []);

// `useTable` hook provides a table store.
// The store contains the current state of the table and methods to update it.
const table = useTable<Person>({
initialState: {totalEntries: data.length},
});

return (
<Table<Person> store={table} data={data} columns={columns} getRowId={({id}) => id.toString()}>
<Table.Actions>
{(selectedRow: Person) => (
<>
<InlineConfirm.Target
component={Table.ActionItem}
id="delete"
leftSection={<DeleteSize16Px height={16} />}
>
Delete
</InlineConfirm.Target>
<InlineConfirm.Prompt
id="delete"
label={`Are you sure you want to delete ${selectedRow.firstName} ${selectedRow.lastName}?`}
onConfirm={() => showNotification({message: 'Confirm clicked', autoClose: true})}
onCancel={() => showNotification({message: 'Cancel clicked', autoClose: true})}
/>
</>
)}
</Table.Actions>
<Table.Header />
</Table>
);
};
export default Demo;

export type Person = {
id: string;
firstName: string;
lastName: string;
age: number;
bio: string;
pic: string;
};

const makeData = (len: number): Person[] =>
Array(len)
.fill(0)
.map(() => ({
id: faker.string.uuid(),
pic: faker.image.avatar(),
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
age: faker.number.int(40),
bio: faker.lorem.sentences({min: 1, max: 5}),
}));
4 changes: 4 additions & 0 deletions packages/website/src/pages/layout/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TableLayoutsDemo from '@examples/layout/Table/TableLayouts.demo?demo';
import TableMultiSelectionDemo from '@examples/layout/Table/TableMultiSelection.demo?demo';
import TablePredicateDemo from '@examples/layout/Table/TablePredicate.demo?demo';
import TableReactQuery from '@examples/layout/Table/TableReactQuery.demo?demo';
import TableConfirmAction from '@examples/layout/Table/TableConfirmAction.demo?demo';

import {PageLayout} from '../../building-blocs/PageLayout';

Expand Down Expand Up @@ -55,6 +56,9 @@ const DemoPage = () => (
/>
),
layouts: <TableLayoutsDemo noPadding layout="vertical" title="Table with multiple layouts" />,
confirmAction: (
<TableConfirmAction noPadding layout="vertical" title="Table with inline confirmation in actions" />
),
}}
/>
);
Expand Down

0 comments on commit ef7f822

Please sign in to comment.