Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[studio] Add column pinning #3693

Merged
merged 6 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions docs/data/toolpad/studio/components/data-grid/DataGridPinning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import { AppHostProvider } from '@toolpad/studio-runtime';
import { DataGrid } from '@toolpad/studio-components';

const ROWS = [
{
id: '1',
Name: 'John',
Age: 25,
Location: 'New York',
Occupation: 'Farmer',
Email: '[email protected]',
Company: 'ACME',
},
{
id: '2',
Name: 'Jane',
Age: 37,
Location: 'Paris',
Occupation: 'Plumber',
Email: '[email protected]',
Company: 'MUI',
},
{
id: '3',
Name: 'Susan',
Age: 19,
Location: 'London',
Occupation: 'Programmer',
Email: '[email protected]',
Company: 'MUI',
},
];

const COLUMNS = [
{ field: 'Age', type: 'number' },
{ field: 'Occupation', type: 'string', width: 200 },
{ field: 'Email', type: 'string', width: 200 },
{ field: 'Company', type: 'string', width: 200 },
{ field: 'Name', type: 'string', pin: 'left', width: 150 },
{ field: 'Location', type: 'string', pin: 'right', width: 200 },
];

export default function DataGridPinning() {
return (
<AppHostProvider plan="pro">
<DataGrid rows={ROWS} columns={COLUMNS} height={300} />
</AppHostProvider>
);
}
12 changes: 11 additions & 1 deletion docs/data/toolpad/studio/components/data-grid/data-grid.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,20 @@ This prop is used to show – or hide – the toolbar from the data grid. The fo

### Grouping and aggregating (PRO)

When you use the [Toolpad pro plan](/), the DataGrid gains grouping and aggregating capabilities. You can turn this off for individual columns.
When you use the [Toolpad pro plan](/toolpad/studio/getting-started/roadmap/#paid-plan), the DataGrid gains grouping and aggregating capabilities. You can turn this off for individual columns.

{{"demo": "DataGridPro.js", "hideToolbar": true, "bg": "inline"}}

### Column pinning (PRO)

When you use the [Toolpad pro plan](/toolpad/studio/getting-started/roadmap/#paid-plan), the grid columns can be individually pinned. You can also pre-initialize the grid with pinned columns by using the **Pinned** option in the column editor:

{{"component": "modules/components/DocsImage.tsx", "src": "/static/toolpad/docs/studio/components/datagrid/pinning.png", "alt": "Column pinning option", "caption": "Column pinning option", "zoom": false, "width": 284 }}

For example, a table where the **Name** column is pinned to the left, and the **Location** column to the right looks as follows:

{{"demo": "DataGridPinning.js", "hideToolbar": true, "bg": "inline"}}

## API

See the documentation below for a complete reference to all props available to the datagrid component in Toolpad Studio.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 17 additions & 2 deletions packages/toolpad-studio-components/src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
GridValueGetter,
GridToolbarProps,
GridColType,
GridPinnedColumnFields,
} from '@mui/x-data-grid-premium';
import {
Unstable_LicenseInfoProvider as LicenseInfoProvider,
Expand Down Expand Up @@ -472,7 +473,7 @@ export interface SerializableGridColumn
dateTimeFormat?: DateFormat;
codeComponent?: string;
visible?: boolean;
aggregable?: boolean;
pin?: 'left' | 'right';
}

export type SerializableGridColumns = SerializableGridColumn[];
Expand Down Expand Up @@ -1301,6 +1302,19 @@ const DataGridComponent = React.forwardRef(function DataGridComponent(
(columnsProp ?? []).map((column) => [column.field, column.visible ?? true]),
);

const pinnedColumns: GridPinnedColumnFields = React.useMemo(() => {
const result = (columnsProp ?? []).reduce<GridPinnedColumnFields>((acc, column) => {
if (column.pin) {
acc[column.pin] ??= [];
acc[column.pin]?.push(column.field);
}
return acc;
}, {});
result.right ??= [];
result.right.push(ACTIONS_COLUMN_FIELD);
return result;
}, [columnsProp]);

return (
<LicenseInfoProvider info={LICENSE_INFO}>
<DataGridRoot ref={ref} sx={sx}>
Expand Down Expand Up @@ -1330,10 +1344,11 @@ const DataGridComponent = React.forwardRef(function DataGridComponent(
rowSelectionModel={selectionModel}
initialState={{
columns: { columnVisibilityModel },
pinnedColumns: { right: [ACTIONS_COLUMN_FIELD] },
pinnedColumns,
}}
disableAggregation={!isProPlan}
disableRowGrouping={!isProPlan}
disableColumnPinning={!isProPlan}
{...props}
{...dataProviderProps}
sx={{
Expand Down
70 changes: 70 additions & 0 deletions packages/toolpad-studio/src/components/ToggleButtonSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from 'react';
import FormControl from '@mui/material/FormControl';
import FormLabel from '@mui/material/FormLabel';
import ToggleButton, { toggleButtonClasses } from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import { styled } from '@mui/material';

const PropControlToggleButtonGroup = styled(
ToggleButtonGroup,
{},
)(({ fullWidth }) => ({
display: 'flex',
[`& .${toggleButtonClasses.root}`]: fullWidth
? {
flex: 1,
}
: {},
}));

export interface ToggleButtonSelectProps<T extends string = string> {
label?: string;
options?: (T | { value: T; label?: string })[];
value?: T;
onChange?: (value: T) => void;
disabled?: boolean;
fullWidth?: boolean;
}

function ToggleButtonSelect<T extends string = string>({
options,
label,
value,
onChange,
disabled,
fullWidth,
}: ToggleButtonSelectProps<T>) {
const handleChange = React.useCallback(
(event: React.MouseEvent, newValue: T) => {
onChange?.(newValue);
},
[onChange],
);

return (
<FormControl>
<FormLabel>{label}</FormLabel>
<PropControlToggleButtonGroup
color="primary"
value={value}
exclusive
onChange={handleChange}
aria-label="Platform"
disabled={disabled}
fullWidth={fullWidth}
>
{options?.map((option) => {
const optionValue = typeof option === 'string' ? option : option.value;
const optionLabel = (typeof option === 'string' ? option : option.label) || optionValue;
return (
<ToggleButton key={optionValue} value={optionValue}>
{optionLabel}
</ToggleButton>
);
})}
</PropControlToggleButtonGroup>
</FormControl>
);
}

export default ToggleButtonSelect;
4 changes: 3 additions & 1 deletion packages/toolpad-studio/src/server/localMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,9 @@ async function writeThemeFile(root: string, theme: Theme | null) {
async function writeApplicationFile(root: string, application: Application | null) {
const applicationFilePath = getApplicationFile(root);
if (application) {
await updateYamlFile(applicationFilePath, application);
await updateYamlFile(applicationFilePath, application, {
schemaUrl: getSchemaUrl('Application'),
});
} else {
await fs.rm(applicationFilePath, { recursive: true, force: true });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import PropertyControl from '../../components/PropertyControl';
// TODO: this import suggests leaky abstraction
import { usePageEditorState } from '../AppEditor/PageEditor/PageEditorProvider';
import { UpgradeChip } from '../AppEditor/UpgradeNotification';
import ToggleButtonSelect from '../../components/ToggleButtonSelect';

type GridAlignment = SerializableGridColumn['align'];

Expand Down Expand Up @@ -359,10 +360,20 @@ function GridColumnEditor({
}
label="Aggregable"
/>

<ToggleButtonSelect
options={['left', 'center', 'right']}
fullWidth
label="Pinned"
value={editedColumn.pin ?? 'center'}
onChange={(pin) =>
handleColumnChange({ ...editedColumn, pin: pin === 'center' ? undefined : pin })
}
/>
</React.Fragment>
) : (
<Typography variant="body2">
Grouping/aggregation
Grouping/aggregation/pinning
<UpgradeChip
sx={{ ml: 1 }}
url="https://mui.com/toolpad/studio/components/data-grid/#grouping"
Expand Down
Loading