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

[TreeView] Add label editing feature #13388

Merged
merged 110 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 102 commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
665a814
isBeingEdited
noraleonte May 27, 2024
605fc23
isItemEditable
noraleonte May 27, 2024
27f52a1
wip
noraleonte May 28, 2024
cd6daf2
wip
noraleonte May 30, 2024
5e60140
wip
noraleonte May 30, 2024
76e625e
wip
noraleonte May 30, 2024
0f8952a
wip
noraleonte Jun 5, 2024
361d587
add docs page
noraleonte Jun 5, 2024
1a9227c
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jun 5, 2024
ba9129e
add callback
noraleonte Jun 6, 2024
c5dc28e
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jun 6, 2024
835efac
scripts
noraleonte Jun 6, 2024
729df78
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jun 14, 2024
b214303
cleanup
noraleonte Jun 14, 2024
1c65359
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jun 14, 2024
bd63f10
add prop to limit expansion to icon
noraleonte Jun 14, 2024
f73d84a
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jun 17, 2024
dfcd092
cleanup
noraleonte Jun 17, 2024
0d6dd39
add demo for editing only leaves
noraleonte Jun 17, 2024
e99136d
refactor to expose methods
noraleonte Jun 17, 2024
c647ca1
wip editing with icons
noraleonte Jun 17, 2024
7e5ed3d
oops
noraleonte Jun 17, 2024
ea9e03c
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jun 26, 2024
1ab683c
update dx and icons demo
noraleonte Jun 28, 2024
9500d6f
add to RichTreeViewPro
noraleonte Jun 28, 2024
116383a
fix ts
noraleonte Jun 28, 2024
e25bf79
scripts
noraleonte Jun 28, 2024
394f293
fix ts
noraleonte Jun 28, 2024
f49b92d
oops
noraleonte Jul 1, 2024
fcc31c1
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 1, 2024
b0dcd3c
fix
noraleonte Jul 1, 2024
619a5b3
fix
noraleonte Jul 1, 2024
d09699b
fix demo
noraleonte Jul 1, 2024
98a35a3
fix width
noraleonte Jul 1, 2024
bb3ad9e
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 2, 2024
16ca9b7
finetune behavior
noraleonte Jul 2, 2024
f031211
finetune docs
noraleonte Jul 2, 2024
68971e0
docs
noraleonte Jul 3, 2024
be9941b
fix
noraleonte Jul 3, 2024
46062b4
fix
noraleonte Jul 3, 2024
190f459
fix demo
noraleonte Jul 3, 2024
1dcbdbf
fix
noraleonte Jul 8, 2024
f3e7488
use optional deps
noraleonte Jul 8, 2024
4fcde1c
Flavien's feedback - partial
noraleonte Jul 11, 2024
4fb5bbf
update md
noraleonte Jul 11, 2024
e889e9e
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 11, 2024
9063556
tweaks to demos
noraleonte Jul 11, 2024
efdacf9
add callback demo
noraleonte Jul 11, 2024
720e12a
document imperative api
noraleonte Jul 11, 2024
23a2d9b
fix typo
noraleonte Jul 11, 2024
f3a74fa
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 11, 2024
a98322e
fix
noraleonte Jul 11, 2024
20b2143
review - Flavien
noraleonte Jul 17, 2024
10530f7
fix
noraleonte Jul 17, 2024
544cb33
scripts
noraleonte Jul 17, 2024
46e4685
add data attr
noraleonte Jul 17, 2024
7e3adb9
add tests
noraleonte Jul 17, 2024
675e81b
new test
noraleonte Jul 22, 2024
af161b7
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 22, 2024
906cbf0
oops
noraleonte Jul 22, 2024
da2d67d
improvements
noraleonte Jul 22, 2024
bfe8826
add custom labelInput slot example
noraleonte Jul 22, 2024
c378f29
oops
noraleonte Jul 22, 2024
1ab464a
adjust height
noraleonte Jul 22, 2024
a032e46
fix title
noraleonte Jul 23, 2024
ffd599d
todos
noraleonte Jul 23, 2024
daad86a
handleRootBlur
noraleonte Jul 23, 2024
fdd7051
small behavior fixes
noraleonte Jul 23, 2024
7bf479c
scripts
noraleonte Jul 23, 2024
137d332
tweak
noraleonte Jul 23, 2024
2971e32
Flavien's review
noraleonte Jul 23, 2024
1073e24
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 24, 2024
4daf624
Flavien's review part 1
noraleonte Jul 24, 2024
11b7b07
Flavien's review part 2
noraleonte Jul 24, 2024
6e87aa8
scripts and small fix
noraleonte Jul 24, 2024
90d0e80
fix
noraleonte Jul 24, 2024
d7435b7
fix styling and layout shift
noraleonte Jul 24, 2024
96c97cc
fix the tests
noraleonte Jul 24, 2024
f3ddf77
oops
noraleonte Jul 24, 2024
8a0e046
fix
noraleonte Jul 24, 2024
a33bd54
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 24, 2024
f6bde80
fix type error
noraleonte Jul 25, 2024
6d3b541
remove stopPropagation
noraleonte Jul 25, 2024
3b4b249
Apply suggestions from code review - Alex
noraleonte Jul 25, 2024
bef6b3c
scripts
noraleonte Jul 25, 2024
c7b2c79
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 25, 2024
f56fe8e
fix
noraleonte Jul 25, 2024
49c1dcf
fix layout shifts
noraleonte Jul 25, 2024
a1b9b8c
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 26, 2024
dac80d5
tweak createRootHandleBlur
noraleonte Jul 26, 2024
ab5397f
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Jul 31, 2024
07da961
fix tests
noraleonte Jul 31, 2024
e5d5d82
review Lukas and Flavien part 1
noraleonte Aug 1, 2024
15e5cc2
review part 2
noraleonte Aug 1, 2024
61b88c0
change type of isItemEditable
noraleonte Aug 1, 2024
328b6ac
review part 3
noraleonte Aug 1, 2024
5907245
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Aug 1, 2024
0b67f35
fix focusing bug
noraleonte Aug 2, 2024
cf52267
support editing for TreeItem
noraleonte Aug 2, 2024
28278ad
fix small oversight
noraleonte Aug 2, 2024
e837a29
fix
noraleonte Aug 2, 2024
ad27ce6
add validation demos
noraleonte Aug 5, 2024
65d2408
add experimentalFeaturs flag
noraleonte Aug 6, 2024
45346c1
fix
noraleonte Aug 6, 2024
c81dca6
update docs structure
noraleonte Aug 6, 2024
5f67ac1
Apply suggestions from code review
noraleonte Aug 6, 2024
ea00e33
fix ci
noraleonte Aug 6, 2024
b98ea6d
Merge branch 'master' of github.com:noraleonte/mui-x into label-editing
noraleonte Aug 6, 2024
5cc6c2b
fix
noraleonte Aug 7, 2024
1107002
fix
noraleonte Aug 7, 2024
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
1 change: 1 addition & 0 deletions docs/data/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ const pages: MuiPage[] = [
{ pathname: '/x/react-tree-view/rich-tree-view/expansion' },
{ pathname: '/x/react-tree-view/rich-tree-view/customization' },
{ pathname: '/x/react-tree-view/rich-tree-view/focus' },
{ pathname: '/x/react-tree-view/rich-tree-view/editing' },
{ pathname: '/x/react-tree-view/rich-tree-view/ordering', plan: 'pro' },
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import { useTreeViewApiRef } from '@mui/x-tree-view/hooks';
import { MUI_X_PRODUCTS } from './products';

export default function ApiMethodUpdateItemLabel() {
const [isLabelUpdated, setIsLabelUpdated] = React.useState(false);
const apiRef = useTreeViewApiRef();

const handleUpdateLabel = () => {
if (isLabelUpdated) {
apiRef.current.updateItemLabel('grid', 'Data Grid');
setIsLabelUpdated(false);
} else {
apiRef.current.updateItemLabel('grid', 'New Label');
setIsLabelUpdated(true);
}
};

return (
<Stack spacing={2}>
<Stack spacing={2} direction="row">
<Button onClick={handleUpdateLabel}>
{isLabelUpdated ? 'Reset Data Grid label' : 'Change Data Grid label'}
</Button>
</Stack>
<Box sx={{ minHeight: 352, minWidth: 260 }}>
<RichTreeView items={MUI_X_PRODUCTS} apiRef={apiRef} />
</Box>
</Stack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import { useTreeViewApiRef } from '@mui/x-tree-view/hooks';
import { MUI_X_PRODUCTS } from './products';

export default function ApiMethodUpdateItemLabel() {
const [isLabelUpdated, setIsLabelUpdated] = React.useState(false);
const apiRef = useTreeViewApiRef();

const handleUpdateLabel = () => {
if (isLabelUpdated) {
apiRef.current!.updateItemLabel('grid', 'Data Grid');
setIsLabelUpdated(false);
} else {
apiRef.current!.updateItemLabel('grid', 'New Label');
setIsLabelUpdated(true);
}
};

return (
<Stack spacing={2}>
<Stack spacing={2} direction="row">
<Button onClick={handleUpdateLabel}>
{isLabelUpdated ? 'Reset Data Grid label' : 'Change Data Grid label'}
</Button>
</Stack>
<Box sx={{ minHeight: 352, minWidth: 260 }}>
<RichTreeView items={MUI_X_PRODUCTS} apiRef={apiRef} />
</Box>
</Stack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Stack spacing={2} direction="row">
<Button onClick={handleUpdateLabel}>
{isLabelUpdated ? 'Reset Data Grid label' : 'Change Data Grid label'}
</Button>
</Stack>
<Box sx={{ minHeight: 352, minWidth: 260 }}>
<RichTreeView items={MUI_X_PRODUCTS} apiRef={apiRef} />
</Box>
45 changes: 45 additions & 0 deletions docs/data/tree-view/rich-tree-view/editing/CustomBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import { useTreeItem2Utils } from '@mui/x-tree-view/hooks';
import { TreeItem2 } from '@mui/x-tree-view/TreeItem2';

import { MUI_X_PRODUCTS } from './products';

const CustomTreeItem2 = React.forwardRef(function CustomTreeItem2(props, ref) {
const {
interactions: { handleCancelItemLabelEditing },
} = useTreeItem2Utils({
itemId: props.itemId,
children: props.children,
});

const handleInputBlur = (event) => {
handleCancelItemLabelEditing(event);
};

return (
<TreeItem2
{...props}
ref={ref}
slotProps={{
labelInput: {
onBlur: handleInputBlur,
},
}}
/>
);
});

export default function CustomBehavior() {
return (
<Box sx={{ minHeight: 352, minWidth: 260 }}>
<RichTreeView
items={MUI_X_PRODUCTS}
slots={{ item: CustomTreeItem2 }}
isItemEditable
defaultExpandedItems={['grid', 'pickers']}
/>
</Box>
);
}
48 changes: 48 additions & 0 deletions docs/data/tree-view/rich-tree-view/editing/CustomBehavior.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import { useTreeItem2Utils } from '@mui/x-tree-view/hooks';
import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2';
import { UseTreeItem2LabelInputSlotProps } from '@mui/x-tree-view/useTreeItem2/useTreeItem2.types';
import { MUI_X_PRODUCTS } from './products';

const CustomTreeItem2 = React.forwardRef(function CustomTreeItem2(
props: TreeItem2Props,
ref: React.Ref<HTMLLIElement>,
) {
const {
interactions: { handleCancelItemLabelEditing },
} = useTreeItem2Utils({
itemId: props.itemId,
children: props.children,
});

const handleInputBlur: UseTreeItem2LabelInputSlotProps['onBlur'] = (event) => {
handleCancelItemLabelEditing(event);
};

return (
<TreeItem2
{...props}
ref={ref}
slotProps={{
labelInput: {
onBlur: handleInputBlur,
},
}}
/>
);
});

export default function CustomBehavior() {
return (
<Box sx={{ minHeight: 352, minWidth: 260 }}>
<RichTreeView
items={MUI_X_PRODUCTS}
slots={{ item: CustomTreeItem2 }}
isItemEditable
defaultExpandedItems={['grid', 'pickers']}
/>
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<RichTreeView
items={MUI_X_PRODUCTS}
slots={{ item: CustomTreeItem2 }}
isItemEditable
defaultExpandedItems={['grid', 'pickers']}
/>
187 changes: 187 additions & 0 deletions docs/data/tree-view/rich-tree-view/editing/CustomLabelInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import CheckIcon from '@mui/icons-material/Check';
import IconButton from '@mui/material/IconButton';
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
import { TreeItem2, TreeItem2Label } from '@mui/x-tree-view/TreeItem2';

import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2/useTreeItem2';
import { useTreeItem2Utils } from '@mui/x-tree-view/hooks/useTreeItem2Utils';

const StyledLabelInput = styled('input')(({ theme }) => ({
...theme.typography.body1,
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadius,
border: 'none',
padding: '0 2px',
boxSizing: 'border-box',
width: 100,
'&:focus': {
outline: `1px solid ${theme.palette.primary.main}`,
},
}));

export const ITEMS = [
{
id: '1',
firstName: 'Jane',
lastName: 'Doe',
editable: true,
children: [
{ id: '1.1', firstName: 'Elena', lastName: 'Kim', editable: true },
{ id: '1.2', firstName: 'Noah', lastName: 'Rodriguez', editable: true },
{ id: '1.3', firstName: 'Maya', lastName: 'Patel', editable: true },
],
},
{
id: '2',
firstName: 'Liam',
lastName: 'Clarke',
editable: true,
children: [
{
id: '2.1',
firstName: 'Ethan',
lastName: 'Lee',
editable: true,
},
{ id: '2.2', firstName: 'Ava', lastName: 'Jones', editable: true },
],
},
];

function Label({ children, ...other }) {
return (
<TreeItem2Label
{...other}
sx={{
display: 'flex',
alignItems: 'center',
gap: 2,
justifyContent: 'space-between',
minHeight: 30,
}}
>
{children}
</TreeItem2Label>
);
}

const LabelInput = React.forwardRef(function LabelInput(
{ item, handleCancelItemLabelEditing, handleSaveItemLabel, ...props },
ref,
) {
const [initialNameValue, setInitialNameValue] = React.useState({
firstName: item.firstName,
lastName: item.lastName,
});
const [nameValue, setNameValue] = React.useState({
firstName: item.firstName,
lastName: item.lastName,
});

const handleFirstNameChange = (event) => {
setNameValue((prev) => ({ ...prev, firstName: event.target.value }));
};
const handleLastNameChange = (event) => {
setNameValue((prev) => ({ ...prev, lastName: event.target.value }));
};

const reset = () => {
setNameValue(initialNameValue);
};
const save = () => {
setInitialNameValue(nameValue);
};

return (
<React.Fragment>
<StyledLabelInput
{...props}
onChange={handleFirstNameChange}
value={nameValue.firstName}
autoFocus
type="text"
ref={ref}
/>
<StyledLabelInput
{...props}
onChange={handleLastNameChange}
value={nameValue.lastName}
type="text"
ref={ref}
/>
<IconButton
color="success"
size="small"
onClick={(event) => {
handleSaveItemLabel(event, `${nameValue.firstName} ${nameValue.lastName}`);
save();
}}
>
<CheckIcon fontSize="small" />
</IconButton>
<IconButton
color="error"
size="small"
onClick={(event) => {
handleCancelItemLabelEditing(event);
reset();
}}
>
<CloseRoundedIcon fontSize="small" />
</IconButton>
</React.Fragment>
);
});

const CustomTreeItem2 = React.forwardRef(function CustomTreeItem2(props, ref) {
const {
interactions: { handleCancelItemLabelEditing, handleSaveItemLabel },
} = useTreeItem2Utils({
itemId: props.itemId,
children: props.children,
});
const { publicAPI } = useTreeItem2(props);

const handleInputBlur = (event) => {
event.defaultMuiPrevented = true;
};

const handleInputKeyDown = (event) => {
event.defaultMuiPrevented = true;
};

return (
<TreeItem2
{...props}
ref={ref}
slots={{ label: Label, labelInput: LabelInput }}
slotProps={{
labelInput: {
item: publicAPI.getItem(props.itemId),
onBlur: handleInputBlur,
onKeyDown: handleInputKeyDown,
handleCancelItemLabelEditing,
handleSaveItemLabel,
},
}}
/>
);
});

export default function CustomLabelInput() {
return (
<Box sx={{ minHeight: 352, minWidth: 340 }}>
<RichTreeView
items={ITEMS}
slots={{ item: CustomTreeItem2 }}
isItemEditable
defaultExpandedItems={['1', '2']}
getItemLabel={(item) => `${item.firstName} ${item.lastName}`}
/>
</Box>
);
}
Loading