mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-05 15:33:59 +00:00
feat: expand collapse
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
const { atom } = require('jotai');
|
||||
|
||||
const PATH = 'option-collapse';
|
||||
|
||||
// collapse options object, initialised from local storage
|
||||
// REFRACT: When do we read from local storage?
|
||||
// on every render?
|
||||
|
||||
export const collapseAtom = atom(JSON.parse(localStorage.getItem(PATH)) || {});
|
||||
|
||||
// get a single option, if it exists
|
||||
export const SelectCollapse = (id) => {
|
||||
return atom((get) => get(collapseAtom)[id]);
|
||||
};
|
||||
|
||||
// change a single item in object
|
||||
export const HandleCollapse = atom(null, (get, set, payload) => {
|
||||
const updatedVal = {
|
||||
...get(collapseAtom),
|
||||
...payload,
|
||||
};
|
||||
set(collapseAtom, updatedVal);
|
||||
localStorage.setItem(PATH, JSON.stringify(updatedVal));
|
||||
});
|
||||
|
||||
// change collapsed in several items
|
||||
export const BatchOperation = atom(null, (get, set, payload) => {
|
||||
let prevOptions = get(collapseAtom);
|
||||
|
||||
let newOptions = {};
|
||||
|
||||
for (const item of payload.items) {
|
||||
newOptions[item.id] = payload.isCollapsed;
|
||||
}
|
||||
|
||||
if (payload.clear) {
|
||||
// clear object
|
||||
prevOptions = {};
|
||||
// clear localstorage
|
||||
localStorage.removeItem(PATH);
|
||||
}
|
||||
|
||||
const options = { ...prevOptions, ...newOptions };
|
||||
|
||||
set(collapseAtom, options);
|
||||
|
||||
localStorage.setItem(PATH, JSON.stringify(options));
|
||||
});
|
||||
|
||||
// change collapsed in all items
|
||||
export const setAll = async (items, isCollapsed) => {
|
||||
// clear storage
|
||||
localStorage.removeItem(PATH);
|
||||
|
||||
// clear local object
|
||||
console.log('debug collapse here', isCollapsed);
|
||||
|
||||
// call batch
|
||||
BatchOperation({ items: items, isCollapsed: isCollapsed });
|
||||
};
|
||||
@@ -7,7 +7,7 @@ export default function CollapseBtn(props) {
|
||||
<Button
|
||||
size={props.size || 'xs'}
|
||||
leftIcon={<FiChevronsUp />}
|
||||
colorScheme='whiteAlpha'
|
||||
colorScheme='white'
|
||||
variant='outline'
|
||||
onClick={clickhandler}
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function ExpandBtn(props) {
|
||||
<Button
|
||||
size={props.size || 'xs'}
|
||||
leftIcon={<FiChevronsDown />}
|
||||
colorScheme='whiteAlpha'
|
||||
colorScheme='white'
|
||||
variant='outline'
|
||||
onClick={clickhandler}
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon from '@chakra-ui/icon';
|
||||
import { FiChevronDown, FiChevronUp, FiMoreVertical } from 'react-icons/fi';
|
||||
import { useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import EventTimes from 'common/components/eventTimes/EventTimes';
|
||||
import EventTimesVertical from 'common/components/eventTimes/EventTimesVertical';
|
||||
@@ -10,6 +10,8 @@ import VisibleIconBtn from 'common/components/buttons/VisibleIconBtn';
|
||||
import DeleteIconBtn from 'common/components/buttons/DeleteIconBtn';
|
||||
import { millisToMinutes } from 'common/dateConfig';
|
||||
import style from './EventBlock.module.css';
|
||||
import { SelectCollapse, HandleCollapse } from 'app/context/collapseAtom';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
const ExpandedBlock = (props) => {
|
||||
const { provided, data, next, delay, delayValue, actionHandler } = props;
|
||||
@@ -85,7 +87,7 @@ const ExpandedBlock = (props) => {
|
||||
as={FiChevronUp}
|
||||
marginTop='0.2em'
|
||||
gridArea='more'
|
||||
onClick={() => props.setExpanded(false)}
|
||||
onClick={() => props.setCollapsed(true)}
|
||||
/>
|
||||
<div className={style.actionOverlay}>
|
||||
<VisibleIconBtn actionHandler={actionHandler} active={data.isPublic} />
|
||||
@@ -138,7 +140,7 @@ const CollapsedBlock = (props) => {
|
||||
as={FiChevronDown}
|
||||
marginTop='0.2em'
|
||||
gridArea='more'
|
||||
onClick={() => props.setExpanded(true)}
|
||||
onClick={() => props.setCollapsed(false)}
|
||||
/>
|
||||
<div className={style.actionOverlay}>
|
||||
<VisibleIconBtn actionHandler={actionHandler} active={data.isPublic} />
|
||||
@@ -156,17 +158,27 @@ const CollapsedBlock = (props) => {
|
||||
export default function EventBlock(props) {
|
||||
const { data, selected, delay, index, actionHandler } = props;
|
||||
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
// const [collapsed, setCollapsed] = useState(checkLocalStorage(data.id));
|
||||
|
||||
// const collapsed = useSelector(itemsAtom, (state) => state === data.id);
|
||||
const [collapsed] = useAtom(
|
||||
useMemo(() => SelectCollapse(data.id), [data.id])
|
||||
);
|
||||
const [, setCollapsed] = useAtom(HandleCollapse);
|
||||
|
||||
// TODO: should this go inside useEffect()
|
||||
// Would I then need to add this to state?
|
||||
const isSelected = selected ? style.active : '';
|
||||
const isExpanded = expanded ? style.expanded : style.collapsed;
|
||||
const classSelect = `${style.event} ${isExpanded} ${isSelected}`;
|
||||
const isCollapsed = collapsed ? style.collapsed : style.expanded;
|
||||
const classSelect = `${style.event} ${isCollapsed} ${isSelected}`;
|
||||
|
||||
// Calculate delay in min
|
||||
const delayValue = delay > 0 ? millisToMinutes(delay) : null;
|
||||
|
||||
const handleCollapse = (isCollapsed) => {
|
||||
setCollapsed({ [data.id]: isCollapsed });
|
||||
};
|
||||
|
||||
return (
|
||||
<Draggable key={data.id} draggableId={data.id} index={index}>
|
||||
{(provided) => (
|
||||
@@ -175,17 +187,7 @@ export default function EventBlock(props) {
|
||||
{...provided.draggableProps}
|
||||
ref={provided.innerRef}
|
||||
>
|
||||
{expanded ? (
|
||||
<ExpandedBlock
|
||||
provided={provided}
|
||||
data={data}
|
||||
next={props.next}
|
||||
delay={delay}
|
||||
delayValue={delayValue}
|
||||
actionHandler={actionHandler}
|
||||
setExpanded={setExpanded}
|
||||
/>
|
||||
) : (
|
||||
{collapsed ? (
|
||||
<CollapsedBlock
|
||||
provided={provided}
|
||||
data={data}
|
||||
@@ -193,7 +195,17 @@ export default function EventBlock(props) {
|
||||
delay={delay}
|
||||
delayValue={delayValue}
|
||||
actionHandler={actionHandler}
|
||||
setExpanded={setExpanded}
|
||||
setCollapsed={handleCollapse}
|
||||
/>
|
||||
) : (
|
||||
<ExpandedBlock
|
||||
provided={provided}
|
||||
data={data}
|
||||
next={props.next}
|
||||
delay={delay}
|
||||
delayValue={delayValue}
|
||||
actionHandler={actionHandler}
|
||||
setCollapsed={handleCollapse}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -15,8 +15,11 @@ import { showErrorToast } from 'common/helpers/toastManager';
|
||||
import { useFetch } from 'app/hooks/useFetch.js';
|
||||
import Empty from 'common/state/Empty';
|
||||
import { EVENTS_TABLE } from 'app/api/apiConstants';
|
||||
import { BatchOperation } from 'app/context/collapseAtom';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export default function EventListWrapper() {
|
||||
const [, setCollapsed] = useAtom(BatchOperation);
|
||||
const queryClient = useQueryClient();
|
||||
const { data, status, isError, refetch } = useFetch(
|
||||
EVENTS_TABLE,
|
||||
@@ -283,8 +286,17 @@ export default function EventListWrapper() {
|
||||
}
|
||||
}
|
||||
console.log('debug m apply', Date.now() - t);
|
||||
|
||||
break;
|
||||
|
||||
case 'collapseall':
|
||||
if (data == null) return;
|
||||
setCollapsed({ clear: true, items: data, isCollapsed: true });
|
||||
break;
|
||||
case 'expandall':
|
||||
if (data == null) return;
|
||||
setCollapsed({ clear: true, items: data, isCollapsed: false });
|
||||
break;
|
||||
|
||||
default:
|
||||
showErrorToast('Unrecognised request', action);
|
||||
break;
|
||||
|
||||
@@ -73,8 +73,11 @@ const EventListMenu = ({ eventsHandler }) => {
|
||||
</MenuList>
|
||||
</Menu>
|
||||
<CurrentBtn size='sm' />
|
||||
<CollapseBtn size='sm' />
|
||||
<ExpandBtn size='sm' />
|
||||
<CollapseBtn
|
||||
size='sm'
|
||||
clickhandler={() => eventsHandler('collapseall')}
|
||||
/>
|
||||
<ExpandBtn size='sm' clickhandler={() => eventsHandler('expandall')} />
|
||||
<MenuActionButtons actionHandler={actionHandler} size='sm' />
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user