From 272458b4129c50d352f5d2dccce0086de95df4f9 Mon Sep 17 00:00:00 2001 From: cv <34649812+cpvalente@users.noreply.github.com> Date: Thu, 20 May 2021 12:38:48 +0200 Subject: [PATCH 1/5] cleanup buttons --- .../common/components/buttons/CollapseBtn.jsx | 24 ++++++++++--------- .../common/components/buttons/CurrentBtn.jsx | 4 ++-- .../common/components/buttons/ExpandBtn.jsx | 24 ++++++++++--------- .../common/components/buttons/LockIconBtn.jsx | 21 ++++++++++++++++ 4 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 client/src/common/components/buttons/LockIconBtn.jsx 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' + 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='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' }} + /> + + ); +} From f1538def9947a84b9e33ceb15a20ba5757972dcc Mon Sep 17 00:00:00 2001 From: cv <34649812+cpvalente@users.noreply.github.com> Date: Thu, 20 May 2021 12:40:33 +0200 Subject: [PATCH 2/5] list follows cursor - add settings atom with cursor mode - in locked mode eventlist scrolls to cursor - cleanup unused menu items --- client/src/app/context/settingsAtom.js | 34 +++++++++ .../src/features/editors/list/EventList.jsx | 26 ++++++- client/src/features/menu/EventListMenu.jsx | 73 ++++++------------- 3 files changed, 80 insertions(+), 53 deletions(-) create mode 100644 client/src/app/context/settingsAtom.js 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/features/editors/list/EventList.jsx b/client/src/features/editors/list/EventList.jsx index 51c582c90..8b43078fa 100644 --- a/client/src/features/editors/list/EventList.jsx +++ b/client/src/features/editors/list/EventList.jsx @@ -1,18 +1,23 @@ import style from './List.module.css'; -import { Fragment, useEffect, useState } from 'react'; +import { createRef, Fragment, 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(() => { @@ -70,6 +75,21 @@ export default function EventList(props) { }; }, [socket]); + // attach scroll to cursor + useEffect(() => { + if (cursor == null || cursorRef.current == null) return; + + cursorRef.current.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + inline: 'start', + }); + }, [cursor, cursorRef]); + + useEffect(() => { + if (cursorSettings !== 'locked') return; + }, [selected]); + if (events.length < 1) { return ; } @@ -114,6 +134,7 @@ export default function EventList(props) { {cursor === -1 && ( {cursor === index && ( { - 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 (
- - - - - - - - - Upload Excel - Upload CSV - - - - - - - - - - - Download Excel - Download CSV - - - eventsHandler('collapseall')} /> eventsHandler('expandall')} /> + + actionHandler('togglelock')} + active={cursorSettings === 'locked'} + /> +
); From 6f97e0caaaac8266d61747536b669d2953b3895d Mon Sep 17 00:00:00 2001 From: cv <34649812+cpvalente@users.noreply.github.com> Date: Thu, 20 May 2021 12:53:24 +0200 Subject: [PATCH 3/5] style: cursor --- .../src/features/editors/list/EventList.jsx | 50 +++---------------- .../src/features/editors/list/List.module.css | 18 ++++--- 2 files changed, 17 insertions(+), 51 deletions(-) diff --git a/client/src/features/editors/list/EventList.jsx b/client/src/features/editors/list/EventList.jsx index 8b43078fa..11a9ad78a 100644 --- a/client/src/features/editors/list/EventList.jsx +++ b/client/src/features/editors/list/EventList.jsx @@ -1,10 +1,9 @@ import style from './List.module.css'; -import { createRef, Fragment, useEffect, useMemo, 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'; @@ -94,22 +93,6 @@ export default function EventList(props) { 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 @@ -131,18 +114,6 @@ export default function EventList(props) { return (
- - {cursor === -1 && ( - - )} - {(provided) => ( @@ -157,7 +128,10 @@ export default function EventList(props) { 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..339f84d3d 100644 --- a/client/src/features/editors/list/List.module.css +++ b/client/src/features/editors/list/List.module.css @@ -21,11 +21,15 @@ } .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 0%, + #0000 10%, + #0000 90%, + #ff7597 100% + ); + border-radius: 20px; + transition: 0.3s; + transition-property: all; } From cc7b8278b8768a61a32e95d62bad9c0bb899f322 Mon Sep 17 00:00:00 2001 From: cv <34649812+cpvalente@users.noreply.github.com> Date: Thu, 20 May 2021 13:13:30 +0200 Subject: [PATCH 4/5] cursor follows playback --- client/src/features/editors/list/EventList.jsx | 18 ++++++++++++------ server/classes/EventTimer.js | 11 +++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/client/src/features/editors/list/EventList.jsx b/client/src/features/editors/list/EventList.jsx index 11a9ad78a..3d2e874df 100644 --- a/client/src/features/editors/list/EventList.jsx +++ b/client/src/features/editors/list/EventList.jsx @@ -56,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); @@ -69,7 +70,7 @@ export default function EventList(props) { // Clear listener return () => { - socket.off('selected-id'); + socket.off('selected'); socket.off('next-id'); }; }, [socket]); @@ -86,8 +87,11 @@ export default function EventList(props) { }, [cursor, cursorRef]); useEffect(() => { - if (cursorSettings !== 'locked') return; - }, [selected]); + if (cursorSettings !== 'locked' || selected == null) return; + if (selected.index == null) return; + + setCursor(selected.index); + }, [selected, cursorSettings]); if (events.length < 1) { return ; @@ -112,6 +116,8 @@ export default function EventList(props) { console.log('EventList: events in event list', events); let cumulativeDelay = 0; + console.log('debug selected', selected); + return (
@@ -136,7 +142,7 @@ export default function EventList(props) { type={e.type} index={index} data={e} - selected={selected === e.id} + selected={selected?.id === e.id} next={next === e.id} eventsHandler={eventsHandler} delay={cumulativeDelay} 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); }); From 6ae075569676112a2e97e1216088c1af2c2ae6ed Mon Sep 17 00:00:00 2001 From: cv <34649812+cpvalente@users.noreply.github.com> Date: Thu, 20 May 2021 13:42:53 +0200 Subject: [PATCH 5/5] cleanup - style tweaks - container follows cursor --- .../src/features/editors/list/EventList.jsx | 20 +++++++++++-------- .../src/features/editors/list/List.module.css | 13 ++++++------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/client/src/features/editors/list/EventList.jsx b/client/src/features/editors/list/EventList.jsx index 3d2e874df..dc6c0b913 100644 --- a/client/src/features/editors/list/EventList.jsx +++ b/client/src/features/editors/list/EventList.jsx @@ -75,9 +75,18 @@ export default function EventList(props) { }; }, [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', @@ -86,13 +95,6 @@ export default function EventList(props) { }); }, [cursor, cursorRef]); - useEffect(() => { - if (cursorSettings !== 'locked' || selected == null) return; - if (selected.index == null) return; - - setCursor(selected.index); - }, [selected, cursorSettings]); - if (events.length < 1) { return ; } @@ -129,14 +131,16 @@ 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 (