Skip to content

Commit

Permalink
Improve checkbox list to make is easier to customize (#566)
Browse files Browse the repository at this point in the history
Signed-off-by: LE SAULNIER Kevin <[email protected]>
  • Loading branch information
klesaulnier authored Sep 18, 2024
1 parent 615af38 commit d6ab27b
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 63 deletions.
23 changes: 15 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,15 @@ function AppContent({ language, onLanguageClick }) {
setCheckBoxListOption(res);
}
}}
onItemClick={(item) => console.log('clicked', item)}
isItemClickable={(item) => item.id.indexOf('i') >= 0}
sx={{
items: (item) => ({
label: {
color: item.id.indexOf('i') >= 0 ? 'blue' : '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 calculatedItemSx = typeof sx?.items === 'function' ? sx?.items(item) : sx?.items;

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={calculatedItemSx}
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={calculatedItemSx}
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

0 comments on commit d6ab27b

Please sign in to comment.