Skip to content

Commit

Permalink
client: add a ToggleList component
Browse files Browse the repository at this point in the history
Bug: #216
  • Loading branch information
jesec committed Mar 22, 2021
1 parent 9283a1d commit fd01597
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 85 deletions.
4 changes: 0 additions & 4 deletions client/src/javascript/components/general/SortableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ interface SortableListProps {
className: string;
lockedIDs: Array<string>;
items: Array<ListItem>;
isDraggable?: boolean;
renderItem: (item: ListItem, index: number) => ReactNode;
onMouseDown?: (event: MouseEvent) => void;
onMove?: (items: this['items']) => void;
Expand All @@ -27,7 +26,6 @@ const SortableList: FC<SortableListProps> = ({
id: listID,
items,
lockedIDs,
isDraggable,
renderItem,
onMouseDown,
onMove,
Expand Down Expand Up @@ -55,7 +53,6 @@ const SortableList: FC<SortableListProps> = ({
id={id}
index={index}
isLocked={lockedIDs.includes(id)}
isDraggable={isDraggable}
isVisible={visible}
key={id}
onDrop={() => {
Expand Down Expand Up @@ -90,7 +87,6 @@ const SortableList: FC<SortableListProps> = ({
};

SortableList.defaultProps = {
isDraggable: undefined,
onMouseDown: undefined,
onMove: undefined,
onDrop: undefined,
Expand Down
26 changes: 7 additions & 19 deletions client/src/javascript/components/general/SortableListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ interface SortableListItemProps {
isVisible: boolean;
isDragging?: boolean;
isLocked?: boolean;
isDraggable?: boolean;
onDrop: () => void;
onMove: (sourceIndex: number, targetIndex: number) => void;
connectDragPreview: DragElementWrapper<DragPreviewOptions>;
Expand All @@ -31,21 +30,14 @@ const SortableListItem: FC<SortableListItemProps> = (props: SortableListItemProp
});
});

let lockedIcon = null;

if (isLocked) {
lockedIcon = <Lock />;
}

const classes = classnames('sortable-list__item', {
'sortable-list__item--is-dragging': isDragging,
'sortable-list__item--is-locked': isLocked,
});

return connectDragSource(
connectDropTarget(
<div className={classes}>
{lockedIcon}
<div
className={classnames('sortable-list__item', {
'sortable-list__item--is-dragging': isDragging,
'sortable-list__item--is-locked': isLocked,
})}>
{isLocked ? <Lock /> : null}
{children}
</div>,
),
Expand All @@ -60,11 +52,7 @@ export default flow([
return {list, id, index, isVisible};
},

canDrag({isLocked, isDraggable}: SortableListItemProps) {
if (isDraggable != null) {
return isDraggable;
}

canDrag({isLocked}: SortableListItemProps) {
if (isLocked) {
return false;
}
Expand Down
62 changes: 62 additions & 0 deletions client/src/javascript/components/general/ToggleList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import classnames from 'classnames';
import {FC} from 'react';
import {Trans} from '@lingui/react';

import {Checkbox} from '@client/ui';
import {Lock} from '@client/ui/icons';

interface ToggleListProps {
className?: string;
items: Array<{
id: string;
isLocked: boolean;
defaultChecked: boolean;
onClick: (checked: boolean) => void;
}>;
}

const ToggleList: FC<ToggleListProps> = ({className, items}: ToggleListProps) => (
<div css={{width: '100%'}} role="none">
<ul
className={classnames('sortable-list', className)}
css={{
'.sortable-list__item': {
cursor: 'default',
},
}}>
{items.map((item) => {
const {id, isLocked, defaultChecked, onClick} = item;
return (
<li
className={classnames('sortable-list__item', {
'sortable-list__item--is-locked': isLocked,
})}
key={id}>
{isLocked ? <Lock /> : null}
<div className="sortable-list__content sortable-list__content__wrapper">
<span className="sortable-list__content sortable-list__content--primary">
<Trans id={id} />
</span>
{isLocked ? null : (
<span className="sortable-list__content sortable-list__content--secondary">
<Checkbox
defaultChecked={defaultChecked}
id={id}
onClick={(event) => onClick((event.target as HTMLInputElement).checked)}>
<Trans id="settings.ui.torrent.context.menu.items.show" />
</Checkbox>
</span>
)}
</div>
</li>
);
})}
</ul>
</div>
);

ToggleList.defaultProps = {
className: undefined,
};

export default ToggleList;
Original file line number Diff line number Diff line change
@@ -1,78 +1,47 @@
import {FC, useState} from 'react';
import {Trans} from '@lingui/react';
import {FC, useRef} from 'react';

import {Checkbox} from '@client/ui';
import SettingStore from '@client/stores/SettingStore';
import SortableList, {ListItem} from '@client/components/general/SortableList';
import ToggleList from '@client/components/general/ToggleList';
import TorrentContextMenuActions from '@client/constants/TorrentContextMenuActions';

import type {TorrentContextMenuAction} from '@client/constants/TorrentContextMenuActions';

import defaultFloodSettings from '@shared/constants/defaultFloodSettings';

import type {FloodSettings} from '@shared/types/FloodSettings';

interface TorrentContextMenuActionsListProps {
onSettingsChange: (changedSettings: Partial<FloodSettings>) => void;
}

const lockedIDs: Array<TorrentContextMenuAction> = ['start', 'stop', 'setTaxonomy', 'torrentDetails'];

const TorrentContextMenuActionsList: FC<TorrentContextMenuActionsListProps> = ({
onSettingsChange,
}: TorrentContextMenuActionsListProps) => {
const [torrentContextMenuActions, setTorrentContextMenuActions] = useState(
Object.keys(TorrentContextMenuActions).map((key) => ({
id: key,
visible: SettingStore.floodSettings.torrentContextMenuActions.some(
(setting) => setting.id === key && setting.visible,
),
const changedTorrentContextMenuActionsRef = useRef<FloodSettings['torrentContextMenuActions']>(
defaultFloodSettings.torrentContextMenuActions.map(({id, visible: defaultVisible}) => ({
id,
visible:
SettingStore.floodSettings.torrentContextMenuActions.find((setting) => setting.id === id)?.visible ??
defaultVisible,
})),
);

return (
<SortableList
id="torrent-context-menu-items"
className="sortable-list--torrent-context-menu-items"
items={torrentContextMenuActions}
lockedIDs={lockedIDs}
isDraggable={false}
renderItem={(item: ListItem) => {
const {id, visible} = item as FloodSettings['torrentContextMenuActions'][number];
let checkbox = null;

if (!lockedIDs.includes(id)) {
checkbox = (
<span className="sortable-list__content sortable-list__content--secondary">
<Checkbox
defaultChecked={visible}
id={id}
onClick={(event) => {
const newTorrentContextMenuActions = torrentContextMenuActions.map((setting) => ({
id: setting.id,
visible: setting.id === id ? (event.target as HTMLInputElement).checked : setting.visible,
}));

onSettingsChange({
torrentContextMenuActions: newTorrentContextMenuActions as FloodSettings['torrentContextMenuActions'],
});
setTorrentContextMenuActions(newTorrentContextMenuActions);
}}>
<Trans id="settings.ui.torrent.context.menu.items.show" />
</Checkbox>
</span>
);
}

const content = (
<div className="sortable-list__content sortable-list__content__wrapper">
<span className="sortable-list__content sortable-list__content--primary">
<Trans id={TorrentContextMenuActions[id]} />
</span>
{checkbox}
</div>
);

return content;
}}
<ToggleList
items={Object.keys(TorrentContextMenuActions).map((action) => ({
id: TorrentContextMenuActions[action as TorrentContextMenuAction],
isLocked: action === 'start' || action === 'stop' || action === 'setTaxonomy' || action === 'torrentDetails',
defaultChecked: changedTorrentContextMenuActionsRef.current.some(
(setting) => setting.id === action && setting.visible,
),
onClick: () => {
const currentSetting = changedTorrentContextMenuActionsRef.current.find((setting) => setting.id === action);
if (currentSetting != null) {
currentSetting.visible = !currentSetting.visible;
}
onSettingsChange({torrentContextMenuActions: changedTorrentContextMenuActionsRef.current});
},
}))}
/>
);
};
Expand Down
6 changes: 0 additions & 6 deletions client/src/sass/components/_sortable-list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,4 @@ $sortable-list--item--border--preview: darken($sortable-list--item--border, 3%);
}
}
}

&--torrent-context-menu-items {
.sortable-list__item {
cursor: default;
}
}
}

0 comments on commit fd01597

Please sign in to comment.