diff --git a/client/src/app/context/settingsAtom.js b/client/src/app/context/settingsAtom.js
new file mode 100644
index 000000000..c49055d5b
--- /dev/null
+++ b/client/src/app/context/settingsAtom.js
@@ -0,0 +1,34 @@
+const { atom } = require('jotai');
+
+const PATH = 'option-gen';
+const initialValue = {
+ cursor: 'locked',
+};
+
+export const settingsAtom = atom(
+ (get) => {
+ const storedOptions = localStorage.getItem(PATH);
+ if (storedOptions == null) return initialValue;
+
+ return JSON.parse(storedOptions);
+ },
+ (get, set, newValues) => {
+ set(settingsAtom, newValues);
+ localStorage.setItem(PATH, JSON.stringify(newValues));
+ }
+);
+
+// get a single option, if it exists
+export const SelectSetting = (setting) => {
+ return atom((get) => get(settingsAtom)[setting]);
+};
+
+// change a single item in object
+export const HandleOptions = atom(null, (get, set, payload) => {
+ const updatedVal = {
+ ...get(settingsAtom),
+ ...payload,
+ };
+ set(settingsAtom, updatedVal);
+ localStorage.setItem(PATH, JSON.stringify(updatedVal));
+});
diff --git a/client/src/common/components/buttons/CollapseBtn.jsx b/client/src/common/components/buttons/CollapseBtn.jsx
index 490af34bb..77891b087 100644
--- a/client/src/common/components/buttons/CollapseBtn.jsx
+++ b/client/src/common/components/buttons/CollapseBtn.jsx
@@ -1,18 +1,20 @@
-import { Button } from '@chakra-ui/button';
+import { IconButton } from '@chakra-ui/button';
+import { Tooltip } from '@chakra-ui/tooltip';
import { FiChevronsUp } from 'react-icons/fi';
export default function CollapseBtn(props) {
const { clickhandler } = props;
return (
- }
- colorScheme='white'
- variant='outline'
- onClick={clickhandler}
- _focus={{ boxShadow: 'none' }}
- >
- Collapse All
-
+
+ }
+ colorScheme='white'
+ variant='outline'
+ background='#fff1'
+ onClick={clickhandler}
+ _focus={{ boxShadow: 'none' }}
+ />
+
);
}
diff --git a/client/src/common/components/buttons/CurrentBtn.jsx b/client/src/common/components/buttons/CurrentBtn.jsx
index 2bea19028..a15c62aa0 100644
--- a/client/src/common/components/buttons/CurrentBtn.jsx
+++ b/client/src/common/components/buttons/CurrentBtn.jsx
@@ -2,13 +2,13 @@ import { Button } from '@chakra-ui/button';
import { FiTarget } from 'react-icons/fi';
export default function CurrentBtn(props) {
- const { clickhandler } = props;
+ const { clickhandler, active } = props;
return (
}
colorScheme='whiteAlpha'
- variant='outline'
+ variant={active ? 'solid' : 'outline'}
onClick={clickhandler}
_focus={{ boxShadow: 'none' }}
>
diff --git a/client/src/common/components/buttons/ExpandBtn.jsx b/client/src/common/components/buttons/ExpandBtn.jsx
index b0dddf8e7..a692e163b 100644
--- a/client/src/common/components/buttons/ExpandBtn.jsx
+++ b/client/src/common/components/buttons/ExpandBtn.jsx
@@ -1,18 +1,20 @@
-import { Button } from '@chakra-ui/button';
+import { IconButton } from '@chakra-ui/button';
+import { Tooltip } from '@chakra-ui/tooltip';
import { FiChevronsDown } from 'react-icons/fi';
export default function ExpandBtn(props) {
const { clickhandler } = props;
return (
- }
- colorScheme='white'
- variant='outline'
- onClick={clickhandler}
- _focus={{ boxShadow: 'none' }}
- >
- Expand All
-
+
+ }
+ colorScheme='white'
+ variant='outline'
+ background='#fff1'
+ onClick={clickhandler}
+ _focus={{ boxShadow: 'none' }}
+ />
+
);
}
diff --git a/client/src/common/components/buttons/LockIconBtn.jsx b/client/src/common/components/buttons/LockIconBtn.jsx
new file mode 100644
index 000000000..d52030fd1
--- /dev/null
+++ b/client/src/common/components/buttons/LockIconBtn.jsx
@@ -0,0 +1,21 @@
+import { IconButton } from '@chakra-ui/button';
+import { Tooltip } from '@chakra-ui/tooltip';
+import { FiTarget } from 'react-icons/fi';
+
+export default function LockIconBtn(props) {
+ const { clickhandler, active, ref } = props;
+ return (
+
+ }
+ colorScheme='pink'
+ color={'pink.300'}
+ variant={active ? 'solid' : 'outline'}
+ onClick={clickhandler}
+ _focus={{ boxShadow: 'none' }}
+ />
+
+ );
+}
diff --git a/client/src/features/editors/list/EventList.jsx b/client/src/features/editors/list/EventList.jsx
index 51c582c90..dc6c0b913 100644
--- a/client/src/features/editors/list/EventList.jsx
+++ b/client/src/features/editors/list/EventList.jsx
@@ -1,18 +1,22 @@
import style from './List.module.css';
-import { Fragment, useEffect, useState } from 'react';
+import { createRef, useEffect, useMemo, useState } from 'react';
import { useSocket } from 'app/context/socketContext';
import tinykeys from 'tinykeys';
import Empty from 'common/state/Empty';
import EventListItem from './EventListItem';
-import { AnimatePresence, motion } from 'framer-motion';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
+import { useAtom } from 'jotai';
+import { SelectSetting } from 'app/context/settingsAtom';
export default function EventList(props) {
const { events, eventsHandler } = props;
const socket = useSocket();
const [selected, setSelected] = useState(null);
const [next, setNext] = useState(null);
- const [cursor, setCursor] = useState(null);
+ const [cursor, setCursor] = useState(0);
+ const [cursorSettings] = useAtom(useMemo(() => SelectSetting('cursor'), []));
+
+ const cursorRef = createRef();
// Handle keyboard shortcuts
useEffect(() => {
@@ -52,12 +56,13 @@ export default function EventList(props) {
if (socket == null) return;
// ask for playstate
- socket.emit('get-selected-id');
+ socket.emit('get-selected');
socket.emit('get-next-id');
// Handle playstate
- socket.on('selected-id', (data) => {
+ socket.on('selected', (data) => {
setSelected(data);
+ console.log('debug cursor setted', data);
});
socket.on('next-id', (data) => {
setNext(data);
@@ -65,31 +70,35 @@ export default function EventList(props) {
// Clear listener
return () => {
- socket.off('selected-id');
+ socket.off('selected');
socket.off('next-id');
};
}, [socket]);
+ // attach cursor to selected
+ useEffect(() => {
+ if (cursorSettings !== 'locked' || selected == null) return;
+ if (selected.index == null) return;
+
+ setCursor(selected.index);
+ }, [selected, cursorSettings]);
+
+ // attach scroll to cursor
+ useEffect(() => {
+ if (cursor == null || cursorRef.current == null) return;
+ console.log('debug cursor scrolling');
+
+ cursorRef.current.scrollIntoView({
+ behavior: 'smooth',
+ block: 'nearest',
+ inline: 'start',
+ });
+ }, [cursor, cursorRef]);
+
if (events.length < 1) {
return ;
}
- // motion
- const cursorVariants = {
- hidden: {
- scale: 0,
- },
- visible: {
- scale: 1,
- transition: {
- duration: 0.3,
- },
- },
- exit: {
- scale: 0,
- },
- };
-
// DND
const handleOnDragEnd = (result) => {
// drop outside of area
@@ -109,19 +118,10 @@ export default function EventList(props) {
console.log('EventList: events in event list', events);
let cumulativeDelay = 0;
+ console.log('debug selected', selected);
+
return (
-
- {cursor === -1 && (
-
- )}
-
{(provided) => (
@@ -131,33 +131,27 @@ export default function EventList(props) {
ref={provided.innerRef}
>
{events.map((e, index) => {
+ let isCursor = cursor === index;
if (index === 0) cumulativeDelay = 0;
if (e.type === 'delay' && e.duration != null) {
cumulativeDelay += e.duration;
} else if (e.type === 'block') cumulativeDelay = 0;
return (
-
+
-
- {cursor === index && (
-
- )}
-
-
+
);
})}
{provided.placeholder}
diff --git a/client/src/features/editors/list/List.module.css b/client/src/features/editors/list/List.module.css
index e676a162c..6670524f3 100644
--- a/client/src/features/editors/list/List.module.css
+++ b/client/src/features/editors/list/List.module.css
@@ -21,11 +21,14 @@
}
.cursor {
- width: 90%;
- min-height: 2px;
- background-color: #ff7597;
- border-radius: 2px;
- margin: 0 auto;
- /* transition: 1s;
- transition-property: all; */
+ width: 100%;
+ background: linear-gradient(
+ 180deg,
+ #ff7597 2%,
+ #0001 3%,
+ #0001 97%,
+ #ff7597 98%
+ );
+
+ border-radius: 14px;
}
diff --git a/client/src/features/menu/EventListMenu.jsx b/client/src/features/menu/EventListMenu.jsx
index aba254a80..912c47b95 100644
--- a/client/src/features/menu/EventListMenu.jsx
+++ b/client/src/features/menu/EventListMenu.jsx
@@ -1,34 +1,16 @@
-import { memo } from 'react';
-import { FiChevronDown } from 'react-icons/fi';
-import {
- Button,
- ButtonGroup,
- Menu,
- MenuButton,
- MenuItem,
- MenuList,
-} from '@chakra-ui/react';
+import { memo, useMemo } from 'react';
+import { Divider } from '@chakra-ui/react';
import style from './EventListMenu.module.css';
-import MenuActionButtons from '../editors/list/MenuActionButtons';
+import MenuActionButtons from './MenuActionButtons';
import CollapseBtn from 'common/components/buttons/CollapseBtn';
import ExpandBtn from 'common/components/buttons/ExpandBtn';
-import CurrentBtn from 'common/components/buttons/CurrentBtn';
+import { SelectSetting, HandleOptions } from 'app/context/settingsAtom';
+import { useAtom } from 'jotai';
+import LockIconBtn from 'common/components/buttons/LockIconBtn';
const EventListMenu = ({ eventsHandler }) => {
- const buttonProps = {
- size: 'sm',
- variant: 'outline',
- colorScheme: 'whiteAlpha',
- backgroundColor: '#ffffff05',
- _hover: { bg: 'blue.800' },
- _expanded: { bg: 'blue.400' },
- _focus: { boxShadow: 'none' },
- };
-
- const menuStyle = {
- color: 'initial',
- backgroundColor: 'rgba(255,255,255,0.67)',
- };
+ const [cursorSettings] = useAtom(useMemo(() => SelectSetting('cursor'), []));
+ const [, SetOption] = useAtom(HandleOptions);
const actionHandler = (action) => {
switch (action) {
@@ -41,6 +23,13 @@ const EventListMenu = ({ eventsHandler }) => {
case 'block':
eventsHandler('add', { type: action, order: 0 });
break;
+ case 'togglelock':
+ let newSet = 'locked';
+ if (cursorSettings === 'locked') {
+ newSet = 'unlocked';
+ }
+ SetOption({ cursor: newSet });
+ break;
default:
break;
}
@@ -48,36 +37,18 @@ const EventListMenu = ({ eventsHandler }) => {
return (
-
-
-
eventsHandler('collapseall')}
/>
eventsHandler('expandall')} />
+
+ actionHandler('togglelock')}
+ active={cursorSettings === 'locked'}
+ />
+
);
diff --git a/server/classes/EventTimer.js b/server/classes/EventTimer.js
index 960b21af5..6059a7a18 100644
--- a/server/classes/EventTimer.js
+++ b/server/classes/EventTimer.js
@@ -90,6 +90,10 @@ class EventTimer extends Timer {
broadcastState() {
this.io.emit('timer', this.getObject());
this.io.emit('playstate', this.state);
+ this.io.emit('selected', {
+ id: this.selectedEventId,
+ index: this.selectedEventIndex,
+ });
this.io.emit('selected-id', this.selectedEventId);
this.io.emit('next-id', this.nextEventId);
this.io.emit('publicselected-id', this.selectedPublicEventId);
@@ -259,6 +263,13 @@ class EventTimer extends Timer {
/*******************************************/
// selection data
+ socket.on('get-selected', () => {
+ socket.emit('selected', {
+ id: this.selectedEventId,
+ index: this.selectedEventIndex,
+ });
+ });
+
socket.on('get-selected-id', () => {
socket.emit('selected-id', this.selectedEventId);
});