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

Improve checkbox list to make is easier to customize #566

Merged
merged 9 commits into from
Sep 18, 2024
29 changes: 21 additions & 8 deletions demo/src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -569,11 +569,12 @@ function AppContent({ language, onLanguageClick }) {
},
]);

const secondaryAction = () => (
<IconButton aria-label="comment">
<CommentIcon />
</IconButton>
);
const secondaryAction = (item, isItemHovered) =>
isItemHovered && (
<IconButton aria-label="comment">
<CommentIcon />
</IconButton>
);
const defaultTab = (
<div>
<Box mt={3}>
Expand Down Expand Up @@ -613,8 +614,6 @@ function AppContent({ language, onLanguageClick }) {
divider
secondaryAction={secondaryAction}
addSelectAllCheckbox
isCheckboxClickableOnly
enableSecondaryActionOnHover
/>

<Button
Expand All @@ -639,7 +638,6 @@ function AppContent({ language, onLanguageClick }) {
divider
secondaryAction={secondaryAction}
isDndDragAndDropActive
enableSecondaryActionOnHover
onDragEnd={({ source, destination }) => {
if (destination !== null && source.index !== destination.index) {
const res = [...checkBoxListOption];
Expand All @@ -648,6 +646,21 @@ function AppContent({ language, onLanguageClick }) {
setCheckBoxListOption(res);
}
}}
onItemClick={(item) => console.log('clicked', item)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's for the demo, to make a working example of onItemClick

isItemClickable={(item) => item.id.indexOf('i') >= 0}
sx={(item) =>
item.id.indexOf('i') >= 0
? {
label: {
color: 'blue',
},
}
: {
label: {
color: 'red',
},
}
}
/>
<div
style={{
Expand Down
18 changes: 14 additions & 4 deletions src/components/checkBoxList/CheckBoxListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,31 @@ export function CheckBoxListItem<T>({
secondaryAction,
getItemId,
divider,
isCheckboxClickableOnly,
onItemClick,
isItemClickable,
...props
}: CheckBoxListItemProps<T>) {
}: Readonly<CheckBoxListItemProps<T>>) {
const [hover, setHover] = useState<string>('');
return (
<ListItem
secondaryAction={secondaryAction?.(item, hover)}
sx={{ minWidth: 0, ...sx?.checkboxListItem }}
onMouseEnter={() => setHover(getItemId(item))}
onMouseLeave={() => setHover('')}
disablePadding={!isCheckboxClickableOnly}
disablePadding={!!onItemClick}
disableGutters
divider={divider}
>
{isCheckboxClickableOnly ? <ClickableCheckBoxItem {...props} /> : <ClickableRowItem {...props} />}
{!onItemClick ? (
<ClickableCheckBoxItem sx={sx} {...props} />
) : (
<ClickableRowItem
isItemClickable={isItemClickable?.(item)}
onItemClick={() => onItemClick(item)}
sx={sx}
{...props}
/>
)}
</ListItem>
);
}
Expand Down
30 changes: 13 additions & 17 deletions src/components/checkBoxList/CheckBoxListItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ export function CheckBoxListItems<T>({
getItemId,
sx,
secondaryAction,
enableSecondaryActionOnHover,
addSelectAllCheckbox,
selectAllCheckBoxLabel,
getItemLabel,
isDisabled,
isDndDragAndDropActive,
isDragDisable,
divider,
isCheckboxClickableOnly,
onItemClick,
isItemClickable,
...props
}: CheckBoxListItemsProps<T>) {
}: Readonly<CheckBoxListItemsProps<T>>) {
const handleOnchange = useCallback(
(newValues: T[]) => {
if (onSelectionChange) {
Expand Down Expand Up @@ -77,16 +77,8 @@ export function CheckBoxListItems<T>({
if (!secondaryAction) {
return null;
}

if (!enableSecondaryActionOnHover) {
return secondaryAction(item);
}

if (hover === getItemId(item)) {
return secondaryAction(item);
}

return null;
const isItemHovered = hover === getItemId(item);
return secondaryAction(item, isItemHovered);
};

const selectAllLabel = useMemo(
Expand Down Expand Up @@ -126,6 +118,8 @@ export function CheckBoxListItems<T>({
const label = getItemLabel ? getItemLabel(item) : getItemId(item);
const disabled = isDisabled ? isDisabled(item) : false;
const addDivider = divider && index < items.length - 1;
// sx can be dependent on item or not
const calculatedSx = typeof sx === 'function' ? sx(item) : sx;

if (isDndDragAndDropActive) {
return (
Expand All @@ -142,14 +136,15 @@ export function CheckBoxListItems<T>({
checked={isChecked(item)}
label={label}
onClick={() => toggleSelection(getItemId(item))}
sx={sx}
sx={calculatedSx}
disabled={disabled}
getItemId={getItemId}
secondaryAction={handleSecondaryAction}
isDragDisable={isDragDisable}
provided={provided}
divider={addDivider}
isCheckboxClickableOnly={isCheckboxClickableOnly}
onItemClick={onItemClick}
isItemClickable={isItemClickable}
/>
)}
</Draggable>
Expand All @@ -164,10 +159,11 @@ export function CheckBoxListItems<T>({
onClick={() => toggleSelection(getItemId(item))}
disabled={disabled}
getItemId={getItemId}
sx={sx}
sx={calculatedSx}
secondaryAction={handleSecondaryAction}
divider={addDivider}
isCheckboxClickableOnly={isCheckboxClickableOnly}
onItemClick={onItemClick}
isItemClickable={isItemClickable}
/>
);
})}
Expand Down
4 changes: 2 additions & 2 deletions src/components/checkBoxList/ClickableCheckBoxItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
*/
import { Checkbox, ListItemIcon, ListItemText } from '@mui/material';
import OverflowableText from '../overflowableText';
import { ClickableItemProps } from './checkBoxList.type';
import { ClickableCheckBoxItemProps } from './checkBoxList.type';

export function ClickableCheckBoxItem({ sx, label, ...props }: ClickableItemProps) {
export function ClickableCheckBoxItem({ sx, label, ...props }: ClickableCheckBoxItemProps) {
return (
<>
<ListItemIcon sx={{ minWidth: 0, ...sx?.checkBoxIcon }}>
Expand Down
36 changes: 32 additions & 4 deletions src/components/checkBoxList/ClickableRowItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,42 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Checkbox, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
import { ClickableRowItemProps } from './checkBoxList.type';
import OverflowableText from '../overflowableText';
import { ClickableItemProps } from './checkBoxList.type';

export function ClickableRowItem({ sx, disabled, label, onClick, ...props }: ClickableItemProps) {
const styles = {
unclickableItem: {
'&:hover': {
backgroundColor: 'transparent',
},
cursor: 'inherit',
},
};

export function ClickableRowItem({
sx,
disabled,
label,
onClick,
onItemClick,
isItemClickable = true,
...props
}: Readonly<ClickableRowItemProps>) {
const onCheckboxClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
onClick();
};
const handleItemClick = () => isItemClickable && onItemClick();

return (
<ListItemButton sx={{ paddingLeft: 0, ...sx?.checkboxButton }} disabled={disabled} onClick={onClick}>
<ListItemButton
disableTouchRipple={!isItemClickable}
sx={{ paddingLeft: 0, ...sx?.checkboxButton, ...(!isItemClickable && styles.unclickableItem) }}
disabled={disabled}
onClick={handleItemClick}
>
<ListItemIcon sx={{ minWidth: 0, ...sx?.checkBoxIcon }}>
<Checkbox disableRipple sx={{ paddingLeft: 0, ...sx?.checkbox }} {...props} />
<Checkbox disableRipple sx={{ paddingLeft: 0, ...sx?.checkbox }} onClick={onCheckboxClick} {...props} />
</ListItemIcon>
<ListItemText
sx={{
Expand Down
13 changes: 9 additions & 4 deletions src/components/checkBoxList/DraggableCheckBoxListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,37 @@ export function DraggableCheckBoxListItem<T>({
isDragDisable,
provided,
divider,
isCheckboxClickableOnly,
onItemClick,
isItemClickable,
...props
}: DraggableCheckBoxListItemProps<T>) {
}: Readonly<DraggableCheckBoxListItemProps<T>>) {
const [hover, setHover] = useState<string>('');
return (
<ListItem
secondaryAction={secondaryAction?.(item, hover)}
sx={{ minWidth: 0, ...sx?.checkboxListItem }}
onMouseEnter={() => setHover(getItemId(item))}
onMouseLeave={() => setHover('')}
disablePadding={!isCheckboxClickableOnly}
disablePadding={!!onItemClick}
disableGutters
divider={divider}
ref={provided.innerRef}
{...provided.draggableProps}
>
{isCheckboxClickableOnly ? (
{!onItemClick ? (
<DraggableClickableCheckBoxItem
provided={provided}
isHighlighted={hover === getItemId(item) && !isDragDisable}
sx={sx}
{...props}
/>
) : (
<DraggableClickableRowItem
provided={provided}
isHighlighted={hover === getItemId(item) && !isDragDisable}
onItemClick={() => onItemClick(item)}
isItemClickable={isItemClickable?.(item)}
sx={sx}
{...props}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { Checkbox, IconButton, ListItemIcon, ListItemText } from '@mui/material';
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
import OverflowableText from '../overflowableText';
import { DraggableClickableItemProps } from './checkBoxList.type';
import { DraggableClickableCheckBoxItemProps } from './checkBoxList.type';

const styles = {
dragIcon: (theme: any) => ({
Expand All @@ -26,7 +26,7 @@ export function DraggableClickableCheckBoxItem({
isHighlighted,
label,
...props
}: DraggableClickableItemProps) {
}: Readonly<DraggableClickableCheckBoxItemProps>) {
return (
<>
<IconButton
Expand Down
31 changes: 27 additions & 4 deletions src/components/checkBoxList/DraggableClickableRowItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import { Checkbox, IconButton, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
import { DraggableClickableRowItemProps } from './checkBoxList.type';
import OverflowableText from '../overflowableText';
import { DraggableClickableItemProps } from './checkBoxList.type';

const styles = {
dragIcon: (theme: any) => ({
Expand All @@ -17,6 +17,12 @@ const styles = {
borderRadius: theme.spacing(0),
zIndex: 90,
}),
unclickableItem: {
'&:hover': {
backgroundColor: 'transparent',
},
cursor: 'inherit',
},
};

export function DraggableClickableRowItem({
Expand All @@ -26,10 +32,27 @@ export function DraggableClickableRowItem({
provided,
isHighlighted,
label,
onItemClick,
isItemClickable = true,
...props
}: DraggableClickableItemProps) {
}: Readonly<DraggableClickableRowItemProps>) {
const onCheckboxClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
onClick();
};
const handleItemClick = () => isItemClickable && onItemClick();

return (
<ListItemButton sx={{ paddingLeft: 0, ...sx?.checkboxButton }} disabled={disabled} onClick={onClick}>
<ListItemButton
disableTouchRipple={!isItemClickable}
sx={{
paddingLeft: 0,
...sx?.checkboxButton,
...(!isItemClickable && styles.unclickableItem),
}}
disabled={disabled}
onClick={handleItemClick}
>
<IconButton
{...provided.dragHandleProps}
size="small"
Expand All @@ -42,7 +65,7 @@ export function DraggableClickableRowItem({
<DragIndicatorIcon spacing={0} />
</IconButton>
<ListItemIcon sx={{ minWidth: 0, ...sx?.checkBoxIcon }}>
<Checkbox disableRipple sx={{ paddingLeft: 0, ...sx?.checkbox }} {...props} />
<Checkbox disableRipple sx={{ paddingLeft: 0, ...sx?.checkbox }} onClick={onCheckboxClick} {...props} />
</ListItemIcon>
<ListItemText
sx={{
Expand Down
Loading
Loading