react-beautiful-dnd#Droppable JavaScript Examples
The following examples show how to use
react-beautiful-dnd#Droppable.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.js From react-material-ui-table-row-drag-and-drop with Creative Commons Zero v1.0 Universal | 6 votes |
DroppableComponent = (
onDragEnd: (result, provided) => void) => (props) =>
{
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId={'1'} direction="vertical">
{(provided) => {
return (
<TableBody ref={provided.innerRef} {...provided.droppableProps} {...props}>
{props.children}
{provided.placeholder}
</TableBody>
)
}}
</Droppable>
</DragDropContext>
)
}
Example #2
Source File: SortableList.jsx From notence with MIT License | 6 votes |
export default function SortableList({ items, onSort, children }) {
const handleDragEnd = (result) => {
if (!result.destination) {
return;
}
const [startIndex, endIndex] = [result.source.index, result.destination.index];
onSort({ startIndex, endIndex });
};
const renderListItem = (item) => (draggableProvided) => {
const restProps = {
innerRef: draggableProvided.innerRef,
...draggableProvided.draggableProps,
...draggableProvided.dragHandleProps,
};
return children(item, restProps);
};
return (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="sortableList">
{(provided) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<div {...provided.droppableProps} ref={provided.innerRef}>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{renderListItem(item)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #3
Source File: column.test.js From horondi_admin with MIT License | 6 votes |
describe('Dialog Window Wrapper component', () => {
let component;
beforeEach(() => {
component = setUp(testProps);
});
it('Should contain Droppable', () => {
const droppable = component.find(Droppable);
expect(droppable.length).toBe(1);
});
it('Should contain Paper', () => {
const paper = component.find(Paper);
expect(paper.length).toBe(1);
});
it('Should contain Slide', () => {
const slide = component.find(Slide);
expect(slide.length).toBe(0);
});
it('Should contain Droppable', () => {
const droppable = component.find(Droppable);
expect(droppable.children()).toHaveLength(1);
});
it('Should contain Droppable', () => {
component = setUpWithoutColumn(emptyColumn);
const droppable = component.find(Droppable);
const child = droppable.find('div');
const slide = child.find(Slide);
expect(slide).toHaveLength(0);
});
});
Example #4
Source File: index.js From rainbow-modules with MIT License | 6 votes |
DraggableList = (props) => {
const { className, style, onDragEnd, data, keyField, ...rest } = props;
const handleDragEnd = (result) => {
if (!result.destination) {
return;
}
if (result.destination.index === result.source.index) {
return;
}
const newData = reorder(data, result.source.index, result.destination.index);
onDragEnd(newData);
};
if (keyField && typeof keyField === 'string') {
return (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="draggable-list-droppable-container">
{(provided) => {
return (
<Container
className={className}
style={style}
ref={provided.innerRef}
{...provided.droppableProps}
>
<Items data={data} keyField={keyField} {...rest} />
{provided.placeholder}
</Container>
);
}}
</Droppable>
</DragDropContext>
);
}
// eslint-disable-next-line no-console
console.error('The "keyField" is a required prop of the DraggableList component.');
return null;
}
Example #5
Source File: Board.jsx From react_53 with MIT License | 6 votes |
function Board({ title, tasks, columnId }) {
return (
<Droppable droppableId={columnId}>
{(provided) => (
<Paper elevation={6}>
<div className={styles.board}>
<h2 className={styles.title}>{title}</h2>
<div
{...provided.droppableProps}
ref={provided.innerRef}
className={styles.boardTasks}
>
{tasks.map((el, index) => (
<Tile
key={el.id}
title={el.title}
description={el.content}
id={el.id}
index={index}
columnId={columnId}
/>
))}
{provided.placeholder}
</div>
</div>
</Paper>
)}
</Droppable>
);
}
Example #6
Source File: index.jsx From react-antd-admin-template with MIT License | 5 votes |
render() {
const path = this.props.location.pathname;
const openKey = this.state.openKey;
return (
<div className="sidebar-menu-container">
<Scrollbars autoHide autoHideTimeout={1000} autoHideDuration={200}>
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{this.state.menuTreeNode.map((item, index) => (
<Draggable
key={item.key}
draggableId={item.key}
index={index}
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Menu
mode="inline"
theme="dark"
onSelect={this.handleMenuSelect}
selectedKeys={[path]}
defaultOpenKeys={openKey}
>
{item}
</Menu>
</div>
)}
</Draggable>
))}
</div>
)}
</Droppable>
</DragDropContext>
</Scrollbars>
</div>
);
}
Example #7
Source File: column.js From horondi_admin with MIT License | 5 votes |
Column = ({ column }) => {
const commonStyles = useCommonStyles();
const styles = useStyles();
const handleTitle = (title) =>
title === 'available' ? 'Активні' : 'Неактивні';
return (
<Paper elevation={3} className={styles.columnContainer}>
<Typography variant='h3' style={{ padding: '10px' }}>
{handleTitle(column.title)}
</Typography>
<Droppable droppableId={column.title} direction='horizontal'>
{(provided, snapshot) => (
<div
className={styles.taskListContainer}
ref={provided.innerRef}
{...provided.droppableProps}
isDraggingOver={snapshot.isDraggingOver}
>
{column.items.length ? (
column.items.map((slide, index) => (
<Slide key={slide._id} slide={slide} index={index} />
))
) : (
<div>
<p className={commonStyles.noRecords}>Слайди відсутні</p>
<p
className={commonStyles.noRecords}
style={{ fontSize: 'smaller' }}
>
Перетягніть сюди слайд з іншої колонки
</p>
</div>
)}
{provided.placeholder}
</div>
)}
</Droppable>
</Paper>
);
}
Example #8
Source File: QuestionContainerLayout.js From Edlib with GNU General Public License v3.0 | 5 votes |
QuestionContainerLayout = props => {
const {
cards,
onTitleChange,
title,
onQuestionBankSelect,
tags,
onTagsChange,
cardsComponents,
displayDialog,
loadingIcon,
loadingText,
loadingTitle,
editMode,
handleDragEnd,
searchTitle,
placeholder,
} = props;
return (
<div className="questionSetSurface">
<div>
<TextField
placeholder={placeholder}
label={<FormattedMessage id="QUESTIONCONTAINER.TITLE_LABEL" />}
fullWidth={true}
onChange={event => onTitleChange(event.currentTarget.value, true)}
value={title}
InputLabelProps={{
shrink: true,
}}
margin="normal"
inputProps={{
onBlur: event => onTitleChange(event.currentTarget.value, false),
}}
/>
<TagsManager
tags={tags}
onChange={onTagsChange}
/>
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable
droppableId="questionSetDropZone"
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
>
{cardsComponents}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
{typeof onQuestionBankSelect === 'function' && (
<div>
<QuestionBankBrowser
onSelect={onQuestionBankSelect}
cards={cards}
title={searchTitle}
tags={tags}
/>
</div>
)}
<LoadingModal
open={displayDialog}
contentTitle={loadingTitle}
contentIcon={loadingIcon}
contentText={loadingText}
/>
</div>
);
}
Example #9
Source File: DragDrop.js From google-forms with MIT License | 5 votes |
export default function DragAndDrop(){
// constructor(props) {
// super(props);
// this.state = {
// items: [ {id: "item-0", content: "item 0"},
// {id: "item-1", content: "item 1"}
// ,{id: "item-2", content: "item 2"}
// ,{id: "item-3", content: "item 3"}
// ,{id: "item-4", content: "item 4"}]
// };
// this.onDragEnd = this.onDragEnd.bind(this);
// }
const [items, setItems] = React.useState([ {id: "item-0", content: "item 0"},
{id: "item-1", content: "item 1"}
,{id: "item-2", content: "item 2"}
,{id: "item-3", content: "item 3"}
,{id: "item-4", content: "item 4"}])
function onDragEnd(result) {
if (!result.destination) {
return;
}
var itemgg = [...items]
const itemF = reorder(
itemgg,
result.source.index,
result.destination.index
);
setItems(itemF)
}
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{item.content}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #10
Source File: List.js From social-media-strategy-fe with MIT License | 5 votes |
List = ({ list, user }) => {
const { listContainer, postsContainer } = useStyles();
return (
<Draggable key={list.id} draggableId={list.id} index={list.index}>
{(provided, snapshot) => (
<div
className={listContainer}
ref={provided.innerRef}
{...provided.draggableProps}
>
<ListHeader
list={list}
dragHandleProps={provided.dragHandleProps}
user={user}
/>
<Droppable
direction="vertical"
droppableId={String(list.id)}
type="post"
>
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
className={postsContainer}
style={{
background: snapshot.isDraggingOver
? "aliceblue"
: "transparent",
}}
>
<Scrollbars autoHide>
{list.posts?.map((post, index) => (
<Post key={post.id} post={post} />
))}
<CreatePostButton listId={list.id} />
</Scrollbars>
{provided.placeholder}
</div>
)}
</Droppable>
</div>
)}
</Draggable>
);
}
Example #11
Source File: Kanban.js From social-media-strategy-fe with MIT License | 5 votes |
Kanban = () => {
const { lists } = useSelector(state => state.kanban);
const user = useSelector(state => state.user);
const { kanban, loading } = useStyles();
const dispatch = useDispatch();
useEffect(() => {
if (user.okta_uid) {
if (!lists) {
(async () => {
dispatch(await loadListsFromDb(user.okta_uid));
})();
}
}
// eslint-disable-next-line
}, [user]);
const onDragEnd = result => {
const { source, destination, type } = result;
if (
!result.destination ||
(source.droppableId === destination.droppableId &&
source.index === destination.index)
) {
return;
}
if (type === "post") {
if (source.droppableId !== destination.droppableId) {
// if drag post to a different list/column
dispatch(dragPostToDifferentList(lists, source, destination));
} else {
// if drag post to the same list/column
dispatch(dragPostToSameList(lists, source, destination));
}
} else if (type === "list") {
// if drag list
dispatch(dragList(lists, source, destination));
}
};
return lists ? (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable direction="horizontal" droppableId="mainDroppable" type="list">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
className={kanban}
>
{Object.entries(lists)
.sort((a, b) => a.index - b.index)
.map(([listId, list]) => (
<List key={list.id} list={list} user={user} />
))}
{provided.placeholder}
<CreateList />
</div>
)}
</Droppable>
</DragDropContext>
) : (
<div className={loading}>
<CircularProgress />
</div>
);
}
Example #12
Source File: Board.js From TrelloClone with MIT License | 5 votes |
Board = ({ match }) => {
const board = useSelector((state) => state.board.board);
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getBoard(match.params.id));
}, [dispatch, match.params.id]);
useEffect(() => {
if (board?.title) document.title = board.title + ' | TrelloClone';
}, [board?.title]);
if (!isAuthenticated) {
return <Redirect to='/' />;
}
const onDragEnd = (result) => {
const { source, destination, draggableId, type } = result;
if (!destination) {
return;
}
if (type === 'card') {
dispatch(
moveCard(draggableId, {
fromId: source.droppableId,
toId: destination.droppableId,
toIndex: destination.index,
})
);
} else {
dispatch(moveList(draggableId, { toIndex: destination.index }));
}
};
return !board ? (
<Fragment>
<Navbar />
<Box className='board-loading'>
<CircularProgress />
</Box>
</Fragment>
) : (
<div
className='board-and-navbar'
style={{
backgroundImage:
'url(' +
(board.backgroundURL
? board.backgroundURL
: 'https://images.unsplash.com/photo-1598197748967-b4674cb3c266?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2689&q=80') +
')',
}}
>
<Navbar />
<section className='board'>
<div className='board-top'>
<div className='board-top-left'>
<BoardTitle board={board} />
<Members />
</div>
<BoardDrawer />
</div>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId='all-lists' direction='horizontal' type='list'>
{(provided) => (
<div className='lists' ref={provided.innerRef} {...provided.droppableProps}>
{board.lists.map((listId, index) => (
<List key={listId} listId={listId} index={index} />
))}
{provided.placeholder}
<CreateList />
</div>
)}
</Droppable>
</DragDropContext>
</section>
</div>
);
}
Example #13
Source File: List.js From TrelloClone with MIT License | 5 votes |
List = ({ listId, index }) => {
const [addingCard, setAddingCard] = useState(false);
const list = useSelector((state) =>
state.board.board.listObjects.find((object) => object._id === listId)
);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getList(listId));
}, [dispatch, listId]);
const createCardFormRef = useRef(null);
useEffect(() => {
addingCard && createCardFormRef.current.scrollIntoView();
}, [addingCard]);
return !list || (list && list.archived) ? (
''
) : (
<Draggable draggableId={listId} index={index}>
{(provided) => (
<div
className='list-wrapper'
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<div className='list-top'>
<ListTitle list={list} />
<ListMenu listId={listId} />
</div>
<Droppable droppableId={listId} type='card'>
{(provided) => (
<div
className={`list ${addingCard ? 'adding-card' : 'not-adding-card'}`}
{...provided.droppableProps}
ref={provided.innerRef}
>
<div className='cards'>
{list.cards.map((cardId, index) => (
<Card key={cardId} cardId={cardId} list={list} index={index} />
))}
</div>
{provided.placeholder}
{addingCard && (
<div ref={createCardFormRef}>
<CreateCardForm listId={listId} setAdding={setAddingCard} />
</div>
)}
</div>
)}
</Droppable>
{!addingCard && (
<div className='create-card-button'>
<Button variant='contained' onClick={() => setAddingCard(true)}>
+ Add a card
</Button>
</div>
)}
</div>
)}
</Draggable>
);
}
Example #14
Source File: SortableBoard.jsx From notence with MIT License | 5 votes |
export default function SortableBoard({ groups, onChange, onItemCreate, children }) {
const handleDragEnd = (result) => {
if (!result.destination) {
return;
}
onChange(result);
};
const renderItem = (item) => (draggableProvided) => {
const restProps = {
innerRef: draggableProvided.innerRef,
...draggableProvided.draggableProps,
...draggableProvided.dragHandleProps,
};
return children(item, restProps);
};
return (
<DragDropContext onDragEnd={handleDragEnd}>
<BoardWrapper>
{Object.entries(groups).map(([groupId, group]) => (
<Board key={groupId}>
<BoardTitle>{group.name}</BoardTitle>
<Droppable droppableId={groupId}>
{(provided) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<BoardContent {...provided.droppableProps} ref={provided.innerRef}>
{group.items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{renderItem(item)}
</Draggable>
))}
{provided.placeholder}
<AddBtn onClick={() => onItemCreate(groupId)} icon={<PlusOutlined />}>
New
</AddBtn>
</BoardContent>
)}
</Droppable>
</Board>
))}
</BoardWrapper>
</DragDropContext>
);
}
Example #15
Source File: Board.js From trello-clone with MIT License | 4 votes |
Board = (props) => {
const { id } = props.match.params;
const [addingList, setAddingList] = useState(false);
const { data: board, setData: setBoard, loading } = useAxiosGet(
`/boards/${id}/`
);
const { setBoardContext } = useContext(globalContext);
useEffect(() => {
if (board) {
setBoardContext(board, setBoard);
}
}, [board]);
useDocumentTitle(board ? `${board.title} | Trello` : "");
useBlurSetState(".board__create-list-form", addingList, setAddingList);
const [editingTitle, setEditingTitle] = useState(false);
useBlurSetState(".board__title-edit", editingTitle, setEditingTitle);
const [isBackgroundDark, setIsBackgroundDark] = useState(false);
useEffect(handleBackgroundBrightness(board, setIsBackgroundDark), [board]);
if (!board && loading) return null;
if (!board && !loading) return <Error404 />;
return (
<div className="board" style={getBoardStyle(board)}>
{!editingTitle ? (
<p
className="board__title"
onClick={() => setEditingTitle(true)}
style={isBackgroundDark ? { color: "white" } : null}
>
{board.title}
</p>
) : (
<EditBoard
setEditingTitle={setEditingTitle}
board={board}
setBoard={setBoard}
/>
)}
<p className="board__subtitle">{board.owner.title}</p>
<DragDropContext onDragEnd={onDragEnd(board, setBoard)}>
<Droppable
droppableId={"board" + board.id.toString()}
direction="horizontal"
type="list"
>
{(provided) => (
<div
className="board__lists"
ref={provided.innerRef}
{...provided.droppableProps}
>
{board.lists.map((list, index) => (
<List
list={list}
index={index}
key={uuidv4()}
/>
))}
{provided.placeholder}
{addingList ? (
<CreateList
board={board}
setBoard={setBoard}
setAddingList={setAddingList}
/>
) : (
<button
className="btn board__create-list"
onClick={() => setAddingList(true)}
style={
board.lists.length === 0
? { marginLeft: 0 }
: null
}
>
<i className="fal fa-plus"></i>
Add{" "}
{board.lists.length === 0
? "a"
: "another"}{" "}
list
</button>
)}
</div>
)}
</Droppable>
</DragDropContext>
</div>
);
}
Example #16
Source File: List.js From trello-clone with MIT License | 4 votes |
List = ({ list, index }) => {
const { board, setBoard } = useContext(globalContext);
const [addingCard, setAddingCard] = useState(false);
const [cardTitle, setCardTitle] = useState("");
const [editingTitle, setEditingTitle] = useState(false);
useBlurSetState(".list__add-card-form", addingCard, setAddingCard);
useBlurSetState(".list__title-edit", editingTitle, setEditingTitle);
const onAddCard = async (e) => {
e.preventDefault();
if (cardTitle.trim() === "") return;
const { data } = await authAxios.post(`${backendUrl}/boards/items/`, {
list: list.id,
title: cardTitle,
});
setAddingCard(false);
addCard(board, setBoard)(list.id, data);
};
const listCards = useRef(null);
useEffect(() => {
if (addingCard)
listCards.current.scrollTop = listCards.current.scrollHeight;
}, [addingCard]);
useEffect(() => {
if (editingTitle) {
const editListTitle = document.querySelector(".list__title-edit");
editListTitle.focus();
editListTitle.select();
}
}, [editingTitle]);
return (
<Draggable draggableId={"list" + list.id.toString()} index={index}>
{(provided, snapshot) => {
if (
typeof provided.draggableProps.onTransitionEnd ===
"function"
) {
const anim = window?.requestAnimationFrame(() =>
provided.draggableProps.onTransitionEnd({
propertyName: "transform",
})
);
}
return (
<div
className="list"
ref={provided.innerRef}
{...provided.draggableProps}
style={getListStyle(
snapshot.isDragging,
provided.draggableProps.style
)}
>
<div
className="list__title"
{...provided.dragHandleProps}
style={getListTitleStyle(
snapshot.isDragging,
provided.dragHandleProps.style
)}
>
{!editingTitle ? (
<p onClick={() => setEditingTitle(true)}>
{list.title}
</p>
) : (
<EditList
list={list}
setEditingTitle={setEditingTitle}
/>
)}
<i className="far fa-ellipsis-h"></i>
</div>
<Droppable droppableId={list.id.toString()} type="item">
{(provided) => (
<div
className="list__cards"
ref={mergeRefs(
provided.innerRef,
listCards
)}
{...provided.droppableProps}
>
{list.items.map((card, index) => (
<DraggableCard
card={card}
list={list}
index={index}
key={uuidv4()}
/>
))}
{provided.placeholder}
{addingCard && (
<AddCard
onAddCard={onAddCard}
cardTitle={cardTitle}
setCardTitle={setCardTitle}
/>
)}
</div>
)}
</Droppable>
{!addingCard ? (
<button
className="list__add-card"
onClick={() => setAddingCard(true)}
>
Add card
</button>
) : cardTitle.trim() !== "" ? (
<button
className="list__add-card list__add-card--active btn"
onClick={onAddCard}
>
Add
</button>
) : (
<button
className="list__add-card list__add-card--active btn btn--disabled"
disabled
>
Add
</button>
)}
</div>
);
}}
</Draggable>
);
}
Example #17
Source File: FileBlock.js From sailplane-web with GNU General Public License v3.0 | 4 votes |
export function FileBlock({
sharedFs,
ipfs,
directoryContents,
setCurrentDirectory,
currentDirectory,
isEncrypted,
}) {
const isSmallScreen = useIsSmallScreen();
const dropzoneRef = useRef(null);
const fullFileList = sortDirectoryContents(directoryContents);
const pathSplit = currentDirectory.split('/');
const parentSplit = pathSplit.slice(0, pathSplit.length - 1);
const parentPath = parentSplit.join('/');
const [isImageOpen, setIsImageOpen] = useState(false);
const [mainSrcURL, setMainSrcURL] = useState(null);
const [imageTitle, setImageTitle] = useState('');
const [imageCaption, setImageCaption] = useState('');
const updateTime = useRef({});
const imagePath = useRef({});
const dispatch = useDispatch();
const isImageItem = ({ type, name }) => type === 'file' && isImageFileExt(filenameExt(name));
const neighborImagePaths = (path) => {
const imagePaths = fullFileList.filter(isImageItem).map(f => f.path);
const neighborhood = [...new Set([...imagePaths, path])].sort(alphabetical);
const index = neighborhood.indexOf(path);
return {
prev: neighborhood[(index + neighborhood.length - 1) % neighborhood.length],
next: neighborhood[(index + 1) % neighborhood.length],
};
};
const executer = (path, time) => (func) =>
path === imagePath.current && time === updateTime.current && func();
const handleUpdate = (exe) => (current, total) =>
exe(() => setImageCaption(current === total ? '' : `Loading... [${getPercent(current, total)}%]`));
const loadImagePath = async (path) => {
const time = Date.now();
imagePath.current = path;
updateTime.current = time;
const exe = executer(path, time);
setMainSrcURL(null);
exe(() => setImageTitle(sharedFs.current.fs.pathName(path)));
const imageData = await sharedFs.current.cat(path, { handleUpdate: handleUpdate(exe) }).data();
exe(() => setMainSrcURL(window.URL.createObjectURL(new Blob([imageData]))));
};
const setImageOpen = ({ path } = {}) => {
path = path || null;
path ? loadImagePath(path) : setMainSrcURL(path);
setIsImageOpen(Boolean(path));
};
return (
<div style={styles.container}>
{isImageOpen && (
<Lightbox
prevSrc={emptyImageURL}
nextSrc={emptyImageURL}
mainSrc={mainSrcURL}
onMovePrevRequest={() => loadImagePath(neighborImagePaths(imagePath.current)['prev'])}
onMoveNextRequest={() => loadImagePath(neighborImagePaths(imagePath.current)['next'])}
onCloseRequest={() => setImageOpen(false)}
imageTitle={imageTitle}
imageCaption={imageCaption}
/>
)}
<FolderTools
currentDirectory={currentDirectory}
sharedFs={sharedFs}
setCurrentDirectory={setCurrentDirectory}
isEncrypted={isEncrypted}
handleOpenUpload={() => {
dropzoneRef.current.openUpload();
}}
/>
<div style={styles.fileHeader}>
<div style={{...styles.fileHeaderItem, paddingLeft: 12}}>Name</div>
{!isSmallScreen ? (
<>
<div style={{...styles.fileHeaderItem, textAlign: 'right'}}>
Size
</div>
<div style={{...styles.fileHeaderItem, textAlign: 'right'}}>
Modified
</div>
</>
) : null}
<div style={styles.fileHeaderItem} />
</div>
<div style={styles.dropContainer}>
<DropZone
sharedFs={sharedFs}
currentDirectory={currentDirectory}
ref={dropzoneRef}>
<DragDropContext
onDragEnd={async (draggable) => {
const {combine, draggableId} = draggable;
if (combine) {
const fileName = sharedFs.current.fs.pathName(draggableId);
if (
sharedFs.current.fs.content(combine.draggableId) !== 'dir'
) {
return;
}
try {
await sharedFs.current.move(
draggable.draggableId,
draggable.combine.draggableId,
fileName,
);
} catch (e) {
dispatch(
setStatus({
message: `failed to move ${draggable.draggableId}`,
isError: true,
}),
);
delay(2000).then(() => dispatch(setStatus({})));
}
}
}}>
<Droppable
droppableId="droppable-1"
type="fileblock"
isCombineEnabled={true}>
{(provided, snapshot) => (
<div style={styles.filesContainer}>
{currentDirectory !== '/r' ? (
<DraggableFileItem
fileIndex={0}
isParent={true}
key={parentPath}
data={{path: parentPath, type: 'dir'}}
sharedFs={sharedFs}
ipfs={ipfs}
setCurrentDirectory={setCurrentDirectory}
/>
) : null}
<div
ref={provided.innerRef}
style={
{
// backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey',
}
}
{...provided.droppableProps}>
{!directoryContents.length ? (
<FileDragBlock
handleOpenUpload={() => {
dropzoneRef.current.openUpload();
}}
/>
) : (
<div style={styles.files}>
{fullFileList.map((fileItem, index) => (
<DraggableFileItem
fileIndex={index + 1}
key={fileItem.path}
data={fileItem}
sharedFs={sharedFs}
ipfs={ipfs}
setCurrentDirectory={setCurrentDirectory}
onIconClicked={
isImageItem(fileItem) ? () => setImageOpen(fileItem) : null
}
/>
))}
</div>
)}
<span
style={{
display: 'none',
}}>
{provided.placeholder}
</span>
</div>
</div>
)}
</Droppable>
</DragDropContext>
</DropZone>
</div>
<StatusBar />
<ShareDialog sharedFs={sharedFs} />
</div>
);
}
Example #18
Source File: QuestionsTab.js From google-forms with MIT License | 4 votes |
function QuestionsTab(props) {
const [questions, setQuestions]= React.useState([]);
const [openUploadImagePop, setOpenUploadImagePop] = React.useState(false);
const [imageContextData, setImageContextData] = React.useState({question: null, option: null});
const [formData, setFormData] = React.useState({});
const [loadingFormData, setLoadingFormData] = React.useState(true);
React.useEffect(()=>{
if(props.formData.questions !== undefined){
//console.log(props.formData.questions.length);
if(props.formData.questions.length === 0){
setQuestions([{questionText: "Question", options : [{optionText: "Option 1"}], open: false}]);
} else{
setQuestions(props.formData.questions)
}
setLoadingFormData(false)
}
setFormData(props.formData)
}, [props.formData])
function saveQuestions(){
console.log("auto saving questions initiated");
var data = {
formId: formData._id,
name: formData.name,
description: formData.description,
questions: questions
}
formService.autoSave(data)
.then((result) => {
console.log(result);
setQuestions(result.questions)
},
error => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
console.log(resMessage);
}
);
}
function checkImageHereOrNotForQuestion(gg){
// console.log(gg);
if ((gg === undefined)||(gg==="")){
return false;
} else{
return true;
}
}
function checkImageHereOrNotForOption(gg){
// console.log(gg);
if ((gg === undefined)||(gg==="")){
return false;
} else{
return true;
}
}
function addMoreQuestionField(){
expandCloseAll(); //I AM GOD
setQuestions(questions=> [...questions, {questionText: "Question", options : [{optionText: "Option 1"}], open: true}]);
}
function copyQuestion(i){
let qs = [...questions];
expandCloseAll();
const myNewOptions = [];
qs[i].options.forEach(opn => {
if ((opn.optionImage !== undefined)||(opn.optionImage !=="")) {
var opn1new = {
optionText : opn.optionText,
optionImage: opn.optionImage
}
} else{
var opn1new = {
optionText : opn.optionText
}
}
myNewOptions.push(opn1new)
});
const qImage = qs[i].questionImage || "";
var newQuestion = {questionText: qs[i].questionText, questionImage : qImage ,options:myNewOptions, open: true}
setQuestions(questions=> [...questions, newQuestion]);
}
const handleImagePopupOpen = () => {
setOpenUploadImagePop(true);
};
function uploadImage(i, j){
setImageContextData({
question: i,
option: j
});
handleImagePopupOpen();
}
function updateImageLink(link, context){
var optionsOfQuestion = [...questions];
var i = context.question
if (context.option == null) {
optionsOfQuestion[i].questionImage= link;
} else {
var j = context.option
optionsOfQuestion[i].options[j].optionImage = link;
}
setQuestions(optionsOfQuestion);
}
function deleteQuestion(i){
let qs = [...questions];
if(questions.length > 1){
qs.splice(i, 1);
}
setQuestions(qs)
}
function handleOptionValue(text,i, j){
var optionsOfQuestion = [...questions];
optionsOfQuestion[i].options[j].optionText = text;
//newMembersEmail[i]= email;
setQuestions(optionsOfQuestion);
}
function handleQuestionValue(text, i){
var optionsOfQuestion = [...questions];
optionsOfQuestion[i].questionText = text;
setQuestions(optionsOfQuestion);
}
function onDragEnd(result) {
if (!result.destination) {
return;
}
var itemgg = [...questions];
const itemF = reorder(
itemgg,
result.source.index,
result.destination.index
);
setQuestions(itemF);
}
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
function showAsQuestion(i){
let qs = [...questions];
qs[i].open = false;
setQuestions(qs);
}
function addOption(i){
var optionsOfQuestion = [...questions];
if(optionsOfQuestion[i].options.length < 5){
optionsOfQuestion[i].options.push({optionText: "Option " + (optionsOfQuestion[i].options.length + 1)})
} else{
console.log("Max 5 options ");
}
//console.log(optionsOfQuestion);
setQuestions(optionsOfQuestion)
}
function removeOption(i, j){
var optionsOfQuestion = [...questions];
if(optionsOfQuestion[i].options.length > 1){
optionsOfQuestion[i].options.splice(j, 1);
setQuestions(optionsOfQuestion)
console.log(i + "__" + j);
}
}
function expandCloseAll(){
let qs = [...questions];
for (let j = 0; j < qs.length; j++) {
qs[j].open = false;
}
setQuestions(qs);
}
function handleExpand(i){
let qs = [...questions];
for (let j = 0; j < qs.length; j++) {
if(i ===j ){
qs[i].open = true;
} else{
qs[j].open = false;
}
}
setQuestions(qs);
}
function questionsUI(){
return questions.map((ques, i)=> (
<Draggable key={i} draggableId={i + 'id'} index={i}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div>
<div style={{marginBottom: "15px"}}>
<div style={{width:'100%', marginBottom: '-7px' }}>
<DragIndicatorIcon style={{transform: "rotate(-90deg)", color:'#DAE0E2'}} fontSize="small"/>
</div>
<Accordion onChange={()=>{handleExpand(i)}} expanded={questions[i].open}>
<AccordionSummary
aria-controls="panel1a-content"
id="panel1a-header"
elevation={1} style={{width:'100%'}}
>
{ !questions[i].open ? (
<div style={{display: 'flex',flexDirection:'column', alignItems:'flex-start', marginLeft: '3px', paddingTop: '15px', paddingBottom: '15px'}}>
{/* <TextField id="standard-basic" label=" " value="Question" InputProps={{ disableUnderline: true }} /> */}
<Typography variant="subtitle1" style={{marginLeft: '0px'}}>{i+1}. {ques.questionText}</Typography>
{ques.questionImage !==""?(
<div>
<img src={ques.questionImage} width="400px" height="auto" /><br></br><br></br>
</div>
): "" }
{ques.options.map((op, j)=>(
<div key={j}>
<div style={{display: 'flex'}}>
<FormControlLabel disabled control={<Radio style={{marginRight: '3px', }} />} label={
<Typography style={{color: '#555555'}}>
{ques.options[j].optionText}
</Typography>
} />
</div>
<div>
{op.optionImage !==""?(
<img src={op.optionImage} width="160px" height="auto" />
): "" }
</div>
</div>
))}
</div>
): ""}
</AccordionSummary>
<AccordionDetails>
<div style={{display: 'flex',flexDirection:'column', alignItems:'flex-start', marginLeft: '15px', marginTop:'-15px'}}>
<div style={{display:'flex', width: '100%', justifyContent: 'space-between'}}>
<Typography style={{marginTop:'20px'}}>{i+1}.</Typography>
<TextField
fullWidth={true}
placeholder="Question Text"
style={{marginBottom: '18px'}}
rows={2}
rowsMax={20}
multiline={true}
value={ques.questionText}
variant="filled"
onChange={(e)=>{handleQuestionValue(e.target.value, i)}}
/>
<IconButton aria-label="upload image" onClick={()=>{uploadImage(i, null)}}>
<CropOriginalIcon />
</IconButton>
</div>
<div>
{
checkImageHereOrNotForQuestion(ques.questionImage) ? (
<div>
<div style={{width:'150px', display: 'flex', alignItems:'flex-start', paddingLeft:'20px'}}>
<img src={ques.questionImage} width="150px" height="auto"/>
<IconButton style={{marginLeft: '-15px', marginTop: '-15px',zIndex:999, backgroundColor: 'lightgrey', color:'grey'}}
size="small"
onClick={()=>{
updateImageLink("", {question: i, option: null})
}}>
<CloseIcon />
</IconButton>
</div>
</div>
): ""
}
</div>
<div style={{width: '100%'}}>
{ques.options.map((op, j)=>(
<div key={j}>
<div style={{display:'flex', flexDirection:'row', marginLeft:'-12.5px', justifyContent: 'space-between', paddingTop: '5px', paddingBottom: '5px'}}>
<Radio disabled />
<TextField
fullWidth={true}
placeholder="Option text"
style={{marginTop: '5px'}}
value={ques.options[j].optionText}
onChange={(e)=>{handleOptionValue(e.target.value, i, j)}}
/>
<IconButton aria-label="upload image" onClick={()=>{uploadImage(i, j)}}>
<CropOriginalIcon />
</IconButton>
<IconButton aria-label="delete" onClick={()=>{removeOption(i, j)}}>
<CloseIcon />
</IconButton>
</div>
<div>
{
checkImageHereOrNotForOption(op.optionImage) ? (
<div>
<div style={{width:'150px', display: 'flex', alignItems:'flex-start', paddingLeft:'20px'}}>
<img src={op.optionImage} width="90px" height="auto"/>
<IconButton style={{marginLeft: '-15px', marginTop: '-15px',zIndex:999, backgroundColor: 'lightgrey', color:'grey'}}
size="small"
onClick={()=>{
updateImageLink("", {question: i, option: j})
}}
>
<CloseIcon />
</IconButton>
</div>
<br></br>
<br></br>
</div>
): ""
}
</div>
</div>
))}
</div>
{ques.options.length < 5 ? (
<div>
<FormControlLabel disabled control={<Radio />} label={
<Button size="small" onClick={()=>{addOption(i)}} style={{textTransform: 'none', marginLeft:"-5px"}}>
Add Option
</Button>
} />
</div>
): ""}
<br></br>
<br></br>
<Typography variant="body2" style={{color: 'grey'}}>You can add maximum 5 options. If you want to add more then change in settings. Multiple choice single option is availible</Typography>
</div>
</AccordionDetails>
<Divider />
<AccordionActions>
<IconButton aria-label="View" onClick={()=>{showAsQuestion(i)}}>
<VisibilityIcon />
</IconButton>
<IconButton aria-label="Copy" onClick={()=>{copyQuestion(i)}}>
<FilterNoneIcon />
</IconButton>
<Divider orientation="vertical" flexItem/>
<IconButton aria-label="delete" onClick={()=>{deleteQuestion(i)}}>
<DeleteOutlineIcon />
</IconButton>
<IconButton aria-label="Image">
<MoreVertIcon />
</IconButton>
</AccordionActions>
</Accordion>
</div>
</div>
</div>
)}
</Draggable>
)
)
}
return (
<div style={{marginTop:'15px', marginBottom: '7px', paddingBottom:"30px"}}>
<Grid
container
direction="column"
justify="center"
alignItems="center"
>
{loadingFormData ? (<CircularProgress />):""}
<Grid item xs={12} sm={5} style={{width: '100%'}}>
<Grid style={{borderTop: '10px solid teal', borderRadius: 10}}>
<div>
<div>
<Paper elevation={2} style={{width:'100%'}}>
<div style={{display: 'flex',flexDirection:'column', alignItems:'flex-start', marginLeft: '15px', paddingTop: '20px', paddingBottom: '20px'}}>
<Typography variant="h4" style={{fontFamily:'sans-serif Roboto', marginBottom:"15px"}}>
{formData.name}
</Typography>
<Typography variant="subtitle1">{formData.description}</Typography>
</div>
</Paper>
</div>
</div>
</Grid>
<Grid style={{paddingTop: '10px'}}>
<div>
<ImageUplaodModel handleImagePopOpen={openUploadImagePop} handleImagePopClose={()=>{setOpenUploadImagePop(false)}} updateImageLink={updateImageLink} contextData={imageContextData}/>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{questionsUI()}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<div>
<Button
variant="contained"
onClick={addMoreQuestionField}
endIcon={<AddCircleIcon />}
style={{margin: '5px'}}
>Add Question </Button>
<Button
variant="contained"
color="primary"
onClick={saveQuestions}
style={{margin: '15px'}}
endIcon={<SaveIcon />}
>Save Questions </Button>
</div>
</div>
</Grid>
</Grid>
</Grid>
</div>
);
}
Example #19
Source File: TaskBoard.js From fokus with GNU General Public License v3.0 | 4 votes |
export function TaskBoard() {
const tasks = useSelector((state) => state.tasks.taskArray);
const meta = useSelector((state) => state.tasks.meta);
let focussedTask = meta.focussedTaskIndex !== -1 ? tasks[meta.focussedTaskIndex] : null;
const dispatch = useDispatch();
function handleOnDragEnd(result) {
if (!result.destination) return;
let items = [...tasks.map((i) => ({ ...i }))];
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
let i = result.source.index;
let direction = result.destination.index > result.source.index; // direction true means moving right & swapping
// below is logic to reset globalKeys to maintain correct sort order.
while (i != result.destination.index) {
if (direction) {
items[i].globalKey = tasks[i].globalKey;
i++;
} else {
items[i].globalKey = tasks[i].globalKey;
i--;
}
if (i == result.destination.index) {
items[i].globalKey = tasks[i].globalKey;
}
}
if (meta.focussedTaskIndex !== -1) {
let greaterIndex = Math.max(result.destination.index, result.source.index);
let smallerIndex = Math.min(result.destination.index, result.source.index);
if (result.source.index === meta.focussedTaskIndex) {
dispatch(focusOnTask(result.destination.index));
} else if (meta.focussedTaskIndex >= smallerIndex && meta.focussedTaskIndex <= greaterIndex) {
if (result.destination.index > result.source.index) {
dispatch(focusOnTask(meta.focussedTaskIndex - 1)); // -1
} else {
dispatch(focusOnTask(meta.focussedTaskIndex + 1)); // +1
}
}
}
dispatch(updateOrder(items)); // order is imp. focus then updateOrder
}
function getFlipKey() {
let flipKey = "";
tasks.forEach((i) => {
flipKey += `${i.globalKey}`;
});
flipKey += `${meta.completedTaskStartIndex}`;
return flipKey;
}
function isFocussed(id) {
if (focussedTask !== null && focussedTask.id === id) return true;
return false;
}
// input has both onChange and onKeyDown - can be optimised by using one and combining
return (
<TaskBoardContainer>
<TaskInput focussedTaskIndex={meta.focussedTaskIndex} />
{tasks.length === 0 ? <NoTasks /> : tasks.length === meta.completedTasksCount && <NoTasks allCompleted={true} />}
<Flipper flipKey={getFlipKey()}>
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="dropArea">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{tasks.map((i, index) =>
!i.isCompleted ? (
<Draggable isDragDisabled={i.isCompleted} key={i.id} draggableId={`${i.id}`} index={index}>
{(provided2) => (
<TaskCard
focussedTaskIndex={meta.focussedTaskIndex}
focussedTaskGlobalKey={meta.focussedTaskIndex !== -1 ? tasks[meta.focussedTaskIndex].globalKey : -1}
taskIndex={index}
forwardRBDProvided={provided2}
task={i}
isFocussed={isFocussed(i.id)}
/>
)}
</Draggable>
) : (
""
)
)}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
{meta.completedTaskStartIndex !== -1 && <Divider />}
{meta.showCompletedTasks &&
tasks.map((i, index) =>
i.isCompleted ? (
<TaskCard
focussedTaskIndex={meta.focussedTaskIndex}
focussedTaskGlobalKey={meta.focussedTaskIndex !== -1 ? tasks[meta.focussedTaskIndex].globalKey : -1}
taskIndex={index}
key={i.id}
forwardRBDProvided={{ innerRef: null }}
task={i}
isFocussed={isFocussed(i.id)}
/>
) : (
""
)
)}
</Flipper>
<EmptySpace />
</TaskBoardContainer>
);
}
Example #20
Source File: TodoList.jsx From 4IZ268-2021-2022-ZS with MIT License | 4 votes |
function TodoList() {
const [todos, setTodos] = useState([])
const [isOpen, setIsOpen] = useState(false)
const [input, setInput] = useState('')
// restore Todos from localStorage
useEffect(() => {
const restored = localStorage.getItem('todos')
restored && setTodos(JSON.parse(restored))
}, [])
// save Todos to localStorage
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos))
}, [todos])
function handleOpenList() {
setIsOpen(!isOpen)
}
function handleInput(e) {
const input = e.target.value
if (input.length < 30) {
setInput(input)
}
}
function handleSubmit(e) {
const input = e.target.value
if (e.code === 'Enter' && input) {
setTodos([
...todos,
{
task: input,
completed: false,
},
])
setInput('')
}
}
function toggleCompletion(idx) {
const newTodos = [...todos]
newTodos[idx].completed = !newTodos[idx].completed
setTodos(newTodos)
}
function removeTodo(idx) {
const newTodos = [...todos]
newTodos.splice(idx, 1)
setTodos(newTodos)
}
const onDragEnd = useCallback(
(result) => {
// dropped outside the list
if (!result.destination) {
return
}
const newState = reorder(
todos,
result.source.index,
result.destination.index
)
setTodos(newState)
},
[todos]
)
function reorder(list, startIndex, endIndex) {
const result = Array.from(list)
const [removed] = result.splice(startIndex, 1)
result.splice(endIndex, 0, removed)
return result
}
return (
<div className='relative'>
<div
className={`absolute ${
isOpen ? 'bottom-14 opacity-100' : 'bottom-12 opacity-0'
} right-4 px-7 pt-5 pb-8 bg-black/60 backdrop-blur-sm rounded-lg transition-all`}>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId='droppable'>
{(provided, snapshot) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{todos.map((item, idx) => (
<Draggable
key={item.task}
draggableId={item.task}
index={idx}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}>
<Todo
todo={item}
handleCompletion={(idx) => toggleCompletion(idx)}
handleRemoval={(idx) => removeTodo(idx)}
/>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<input
className='w-56 bg-transparent border-b border-solid border-white
opacity-60 hover:opacity-80 focus:opacity-100 focus:outline-none
transition-colors mt-4 mx-4'
type='text'
value={input}
placeholder='New Todo'
onChange={handleInput}
onKeyPress={handleSubmit}
/>
</div>
<button
onClick={handleOpenList}
className='text-xl font-medium px-4 py-3 opacity-70 hover:opacity-90 transition-opacity'>
Todos
</button>
</div>
)
}
Example #21
Source File: components.js From idena-web with MIT License | 4 votes |
export function FlipShuffleStep({
images,
originalOrder,
order,
onShuffle,
onManualShuffle,
onReset,
}) {
const {t} = useTranslation()
return (
<FlipStep alignSelf="stretch">
<FlipStepHeader>
<FlipStepTitle>{t('Shuffle images')}</FlipStepTitle>
<FlipStepSubtitle>
{t('Shuffle images in order to make a nonsense sequence of images')}
</FlipStepSubtitle>
</FlipStepHeader>
<Stack isInline spacing={10} align="center" mx="auto">
<Stack isInline spacing={10} justify="center">
<FlipImageList>
{originalOrder.map((num, idx) => (
<FlipImageListItem
key={num}
src={images[num]}
isFirst={idx === 0}
isLast={idx === images.length - 1}
opacity={0.3}
/>
))}
</FlipImageList>
<FlipImageList>
<DragDropContext
onDragEnd={result => {
if (
result.destination &&
result.destination.index !== result.source.index
) {
onManualShuffle(
reorder(
order,
result.source.index,
result.destination.index
)
)
}
}}
>
<Droppable droppableId="flip-shuffle">
{provided => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{order.map((num, idx) => (
<DraggableItem
key={num}
draggableId={`pic-${num}`}
index={idx}
>
<Box position="relative">
<Flex
align="center"
justify="center"
bg="brandGray.080"
size={8}
rounded="md"
position="absolute"
top={1}
right={1}
zIndex={1}
>
<MoveIcon boxSize={5} color="white" />
</Flex>
<FlipImageListItem
isFirst={idx === 0}
isLast={idx === images.length - 1}
src={images[num]}
/>
</Box>
</DraggableItem>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</FlipImageList>
</Stack>
<Stack spacing={1}>
<IconButton icon={<CycleIcon boxSize={5} />} onClick={onShuffle}>
{t('Shuffle images')}
</IconButton>
<IconButton icon={<UndoIcon boxSize={5} />} onClick={onReset}>
{t('Reset to default')}
</IconButton>
</Stack>
</Stack>
</FlipStep>
)
}
Example #22
Source File: Board.js From ytx-card-game with MIT License | 4 votes |
Board = (props) => {
const { state, dispatch } = useContext(store)
const isGamePaused = () => state.game && state.game.gamePaused
const [allyCards, setAllyCards] = useState({
[ALLY_TYPES.hand]: [],
[ALLY_TYPES.field]: [],
})
useLayoutEffect(() => {
if (state.game) {
if (state.playerNumber === 1) {
setAllyCards({
[ALLY_TYPES.hand]: [...state.game.player1.hand],
[ALLY_TYPES.field]: [...state.game.player1.field],
})
} else {
setAllyCards({
[ALLY_TYPES.hand]: [...state.game.player2.hand],
[ALLY_TYPES.field]: [...state.game.player2.field],
})
}
}
}, [state.game])
const getAllyStateType = (droppableId) => {
return allyCards[droppableId]
}
const invokeCard = (card) => {
console.log('invoke card', card)
if (!card.isInvoked) {
let me
if (state.playerNumber === 1) {
me = state.game.player1
} else {
me = state.game.player2
}
// Invokes a card into the field and updates ally hand with a new deep copy
if (me.field.length >= FIELD_SIZE) {
return dispatch({
type: 'SET_ERROR',
payload: {
error: 'The field is full',
},
})
}
if (card.cost > me.energy) {
return dispatch({
type: 'SET_ERROR',
payload: {
error:
"You don't have enough energy to invoke this card",
},
})
}
card.isInvoked = true
state.socket.emit('invoke-card', {
game: state.game,
card,
})
}
}
const onDragEnd = useCallback(
(result) => {
const { source, destination } = result
if (!destination) {
return
}
let allyState = getAllyStateType(source.droppableId)
const isEnoughEnergyToInvoke =
state.playerNumber === 1
? state.game.player1.energy
: state.game.player2.energy
console.log(
isEnoughEnergyToInvoke,
allyState.cost,
'energy',
state.playerNumber,
state.game.player1.energy,
state.game.player2.energy,
)
if (isEnoughEnergyToInvoke < allyState[source.index].cost) {
return
}
if (source.droppableId === destination.droppableId) {
const items = reorder(
allyState,
source.index,
destination.index,
)
setAllyCards({ ...allyCards, [source.droppableId]: items })
} else {
//invoke card
invokeCard(allyCards[ALLY_TYPES.hand][source.index])
const result = move(
getAllyStateType(source.droppableId),
getAllyStateType(destination.droppableId),
source,
destination,
)
setAllyCards({
[ALLY_TYPES.hand]: result[ALLY_TYPES.hand],
[ALLY_TYPES.field]: result[ALLY_TYPES.field],
})
}
},
[state.game, allyCards],
)
return (
<Page>
<ResultMsg
winner={state.gameOver && state.areYouTheWinner}
loser={state.gameOver && !state.areYouTheWinner}
>
{state.gameOver && state.areYouTheWinner
? 'Congratulations! You are the winner!'
: state.gameOver && !state.areYouTheWinner
? 'You lost! Better luck next time!'
: null}
</ResultMsg>
{/* <p>Turn: {state.game ? state.game.currentTurnNumber : 0}</p>
<p>Timer: {props.turnCountdownTimer}</p> */}
<ExitLink hidden={!state.gameOver} to="/">
Exit
</ExitLink>
{state.game ? (
<Game className="game">
<EnemyDeck>Enemy's <br /> Deck</EnemyDeck>
<YourDeck>Your <br /> Deck</YourDeck>
<EnemyStatsBox
className={
state.isAttackMode
? 'enemy-stats attack-mode'
: 'enemy-stats'
}
onClick={() => {
if (state.isAttackMode) props.attackDirectly()
}}
>
<p>Enemy</p>
<p>
{state.playerNumber === 1
? state.game.player2.life
: state.game.player1.life}
<FaHeart />
</p>
<p>
{state.playerNumber === 1
? state.game.player2.energy
: state.game.player1.energy}
<FaBolt />
</p>
</EnemyStatsBox>
<AllyStatsBox className="my-stats">
<p>You</p>
<p>
{state.playerNumber === 1
? state.game.player1.life
: state.game.player2.life}
<FaHeart />
</p>
<p>
{state.playerNumber === 1
? state.game.player1.energy
: state.game.player2.energy}
<FaBolt />
</p>
</AllyStatsBox>
<CardContainer className="cards-container enemy-cards-container">
{state.visualEnemyHand}
</CardContainer>
<Field className="field">
<DragDropContext onDragEnd={onDragEnd}>
<EnemyField
className={
state.isAttackMode
? 'enemy-field attack-mode'
: 'enemy-field'
}
>
{state.enemyFieldHtml}
</EnemyField>
<FieldContainer top >
<Droppable
droppableId={`${ALLY_TYPES.field}`}
direction="horizontal"
>
{(provided, snapshot) => (
<CardPanel
ref={provided.innerRef}
isDraggingOver={snapshot.isDraggingOver}
>
{allyCards[ALLY_TYPES.field].map(
(allyFieldCard, index) => (
<Draggable
key={index}
draggableId={`allyFieldCard${index}`}
index={index}
isDragDisabled={true}
>
{(
provided,
snapshot,
) => (
<div
ref={
provided.innerRef
}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<BoardCard
{...allyFieldCard}
/>
</div>
)}
</Draggable>
),
)}
{provided.placeholder}
</CardPanel>
)}
</Droppable>
</FieldContainer>
<FieldContainer bottom>
<Droppable
droppableId={`${ALLY_TYPES.hand}`}
direction="horizontal"
>
{(provided, snapshot) => (
<CardPanel
ref={provided.innerRef}
// isDraggingOver={snapshot.isDraggingOver}
outter
>
{allyCards[ALLY_TYPES.hand].map(
(allyHandCard, index) => (
<Draggable
key={index}
draggableId={`allyHand${index}`}
index={index}
isDragDisabled={
state.playerNumber !==
state.game
.currentPlayerTurn
}
>
{(
provided,
snapshot,
) => (
<div
ref={
provided.innerRef
}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<BoardCard
{...allyHandCard}
/>
</div>
)}
</Draggable>
),
)}
{provided.placeholder}
</CardPanel>
)}
</Droppable>
</FieldContainer>
</DragDropContext>
</Field>
<Button
className="end-turn"
disabled={state.isOtherPlayerTurn || isGamePaused()}
onClick={() => {
props.endTurn()
}}
>
End Turn
</Button>
<Button
className="surrender"
style={{
backgroundColor: 'red',
}}
disabled={isGamePaused()}
onClick={() => {
props.surrender()
}}
>
Surrender
</Button>
</Game>
) : (
<p>Game loading...</p>
)}
</Page>
)
}
Example #23
Source File: components.js From idena-web with MIT License | 4 votes |
export function FlipEditorStep({
keywords,
showTranslation,
originalOrder,
images,
onChangeImage,
onChangeOriginalOrder,
onPainting,
}) {
const {t} = useTranslation()
const [currentIndex, setCurrentIdx] = React.useState(0)
const {words, translations} = keywords
const hasBothTranslations = translations.reduce(
(acc, {length}) => !!length && acc,
true
)
return (
<FlipStep>
<FlipStepHeader>
<FlipStepTitle>{t('Select 4 images to tell your story')}</FlipStepTitle>
<FlipStepSubtitle>
{t(`Use keywords for the story`)}{' '}
<Text as="mark">
{formatKeywords(
hasBothTranslations && showTranslation
? translations.map(([{name, desc}]) => ({
name,
desc,
}))
: words
)}
</Text>{' '}
{t(`and template "Before
– Something happens – After"`)}
.
</FlipStepSubtitle>
</FlipStepHeader>
<Stack isInline spacing={10}>
<FlipImageList>
<DragDropContext
onDragEnd={result => {
if (
result.destination &&
result.destination.index !== result.source.index
) {
setCurrentIdx(result.destination.index)
onChangeOriginalOrder(
reorder(
originalOrder,
result.source.index,
result.destination.index
)
)
}
}}
>
<Droppable droppableId="flip-editor">
{provided => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{originalOrder.map((num, idx) => (
<DraggableItem
key={num}
draggableId={`image-${num}`}
index={idx}
>
<SelectableItem
isActive={idx === currentIndex}
isFirst={idx === 0}
isLast={idx === images.length - 1}
onClick={() => setCurrentIdx(idx)}
>
<FlipImageListItem
isFirst={idx === 0}
isLast={idx === images.length - 1}
src={images[num]}
/>
</SelectableItem>
</DraggableItem>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</FlipImageList>
<Box>
{originalOrder.map((num, idx) => (
<FlipEditor
key={num}
idx={num}
visible={currentIndex === idx}
src={images[num]}
onChange={url => {
onChangeImage(url, num)
}}
onChanging={onPainting}
/>
))}
</Box>
</Stack>
</FlipStep>
)
}
Example #24
Source File: stepDndList.js From pathways with GNU General Public License v3.0 | 4 votes |
render() {
const stepData = this.props.steps
const steps = this.props.stepOrder.map((stepId, index) => {
const currentStep = stepData[stepId]
return (
<Step
key={index}
id={stepId}
index={index}
heading={currentStep.heading}
stepType={currentStep.stepType}
selected={currentStep.selected}
rating={currentStep.rating}
selected={currentStep.selected}
/>
)
})
return (
<div className={classes.ControlsArea}>
<div className={classes.StepListArea}>
<div className={classes.StepListTitle}>
<p style={{ fontSize: '40px' }}>Steps</p>
<Button
aria-controls='simple-menu'
aria-haspopup='true'
onClick={this.handleClick}
>
<PlusIcon fontSize='30px' color='#555' />
</Button>
<Menu
id='simple-menu'
anchorEl={this.state.anchorEl}
keepMounted
open={Boolean(this.state.anchorEl)}
onClose={this.handleClose}
>
<MenuItem
onClick={() => this.handleClose('pathway')}
>
<div className={classes.pathwayStep}>
Pathway
</div>
</MenuItem>
<MenuItem
onClick={() => this.handleClose('content')}
>
<div className={classes.contentStep}>
Content
</div>
</MenuItem>
<MenuItem
onClick={() => this.handleClose('shared')}
>
<div className={classes.sharedStep}>
Shared Step
</div>
</MenuItem>
</Menu>
</div>
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId='myNiggaThatsCrazy'>
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={getListStyle(
snapshot.isDraggingOver
)}
>
{steps}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
<div className={classes.MoreControls}>
<div>
<div className={classes.MoreControlsIcon}>
<SaveIcon fontSize='50px' color='#102e4a' />
</div>
<div
className={classes.MoreControlsIcon}
onClick={this.setPathwayDetails}
>
<SettingsIcon fontSize='50px' color='#102e4a' />
</div>
<div className={classes.MoreControlsIcon}>
{/* Clicking on this icon should render a preview of the pathway */}
<PreviewIcon fontSize='50px' color='#102e4a' />
</div>
</div>
<div>
<div className={classes.MoreControlsIcon}>
<DownloadIcon fontSize='50px' color='#102e4a' />
</div>
</div>
</div>
</div>
)
}
Example #25
Source File: tableDrag.js From camel-store-admin with Apache License 2.0 | 4 votes |
render() {
const { columns, dataSource, restProps, total } = this.conversionObject()
const { scroll, pagination } = restProps;
const { pageSize, offsetWidthBody } = this.state
const widthtable = scroll && scroll.x ? ( offsetWidthBody > scroll.x ? offsetWidthBody : scroll.x) : 'auto'
return (
<div className={styles.dragTable}>
{/*<Table*/}
{/*columns={columns}*/}
{/*dataSource={dataSource}*/}
{/*components={this.components}*/}
{/*pagination={false}*/}
{/*size='small'*/}
{/*onRow={(record, index) => ({*/}
{/*index,*/}
{/*moveRow: this.moveRow,*/}
{/*})}*/}
{/*{...restProps}*/}
{/*/>*/}
<DragDropContext onDragEnd={this.onDragEnd}>
<div className="ant-table-wrapper">
<div className="ant-spin-nested-loading">
<div className="ant-spin-container">
<div className="ant-table ant-table-small ant-table-scroll-position-left">
<div className="ant-table-content">
<div className="ant-table-body" id="tableDragbody" style={ scroll && scroll.x ? {overflowX: 'scroll'} : {}}>
<table style={isNaN(widthtable) ? {} : {width:widthtable + 'px'}} id="tableDrag">
<thead className="ant-table-thead">
<tr>
{ columns.map(item => {
return (<th style={item.width ? { width: `${item.width}px`,userSelect:'none'} : {userSelect:'none'}}><div>{item.title}</div></th>)
})}
</tr></thead>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<tbody className="ant-table-tbody" ref={provided.innerRef}>
{dataSource.length > 0 && dataSource.map((item, index) => (
<Draggable key={item.key} draggableId={item.key} index={index}>
{(provided, snapshot) => (
<tr className="ant-table-row ant-table-row-level-0" data-row-key={index}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style
)}
>
{columns.map(item_in => {
return (
<td style={item_in.width ? { width: `${item_in.width}px`,userSelect:'none'} : {userSelect:'none'}}>
{item_in.render && item_in.render(item[item_in.dataIndex],item) || item[item_in.dataIndex]}
</td>
)
})}
</tr>
)}
</Draggable>
))}
{provided.placeholder}
</tbody>
)}
</Droppable>
</table>
</div>
{dataSource.length === 0 && <div className="ant-table-placeholder"><Empty /></div>}
</div>
</div>
</div>
</div>
</div>
</DragDropContext>
{ pagination &&
<Row><Col style={{textAlign:'right', marginTop: 10, marginBottom: 10 }}>
<Pagination pageSize={pageSize} size="small" total={total} showSizeChanger
onChange={this.pageChange} onShowSizeChange={this.pageSizeChange}/>
</Col></Row> }
</div>
);
}
Example #26
Source File: SearchIndex.js From ActiveLearningStudio-react-client with GNU Affero General Public License v3.0 | 4 votes |
function SearchIndex(props) {
const { match } = props;
const playlistBuilder = useSelector((state) => state.playlist.selectedPlaylist);
const origPlaylist = useSelector((state) => state.playlist.playlists);
const searchBuilder = useSelector((state) => state.search);
const [playlist, setPlaylist] = useState();
const [search, setSearch] = useState([]);
const dispatch = useDispatch();
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const move = (source, destination, droppableSource, droppableDestination) => {
const sourceClone = Array.from(source);
const destClone = Array.from(destination);
const [removed] = sourceClone.splice(droppableSource.index, 1);
destClone.splice(droppableDestination.index, 0, removed);
const result = {};
result[droppableSource.droppableId] = sourceClone;
result[droppableDestination.droppableId] = destClone;
return result;
};
useEffect(() => {
setPlaylist(playlistBuilder);
}, [playlistBuilder]);
useEffect(() => {
dispatch(loadProjectPlaylistsAction(match.params.projectId));
}, []);
useEffect(() => {
setSearch(searchBuilder.searchResult);
Swal.close();
}, [searchBuilder.searchResult]);
const onDragEnd = (result) => {
// dropped outside the list
const { source, destination } = result;
if (!result.destination) {
return;
}
if (source.droppableId === destination.droppableId) {
if (source.droppableId === 'droppable2') {
const items = reorder(
playlist.activities,
source.index,
destination.index,
);
const updatedOrder = origPlaylist.map((play) => {
if (String(play.id) === match.params.playlistId) {
return { ...play, activities: items };
}
return play;
});
dispatch(reorderPlaylistsAction(match.params.projectId, origPlaylist, updatedOrder));
setPlaylist({ ...playlist, activities: items });
// eslint-disable-next-line
} }
else if (source.droppableId === 'droppable') {
const resultHorizantal = move(
search,
playlist.activities,
source,
destination,
);
const updatedOrder = origPlaylist.map((play) => {
if (String(play.id) === match.params.playlistId) {
return { ...play, activities: resultHorizantal.droppable2 };
}
return play;
});
dispatch(reorderPlaylistsAction(match.params.projectId, origPlaylist, updatedOrder));
setPlaylist({ ...playlist, activities: resultHorizantal.droppable2 });
// setSearch(resultHorizantal.droppable);
}
};
return (
<div className="search-wizard">
<h2>Search For Activity</h2>
<div className="formik-form">
<SearchActivity />
</div>
<div className="drag-activity-search">
<div className="active-playlist">
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable" className="search-class">
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
// style={getListStyle(snapshot.isDraggingOver)}
>
{searchBuilder.searchQuery ? (
<>
<h2>
Your Search Result for
{' '}
{searchBuilder.searchQuery}
</h2>
<p>Drag & drop activity to build your playlist.</p>
</>
) : <p>Search Activity from above to use Existing.</p>}
{!!search.length > 0
? search.map((res, index) => (
<Draggable
key={res.id}
draggableId={String(res.id)}
index={index}
>
{(provided_) => (
<div
ref={provided_.innerRef}
{...provided_.draggableProps}
{...provided_.dragHandleProps}
>
<div className="box">
<div className="imgbox">
{res.thumb_url ? (
<div
style={{
backgroundImage: res.thumb_url.includes('pexels.com')
? `url(${res.thumb_url})`
: `url(${global.config.resourceUrl}${res.thumb_url})`,
}}
/>
) : (
<div
style={{
// eslint-disable-next-line max-len
backgroundImage: 'https://images.pexels.com/photos/593158/pexels-photo-593158.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280',
}}
/>
)}
</div>
<div className="content">
<div className="search-content">
<a
href={
res.model === 'Activity'
? `/activity/${res.id}/shared`
: res.model === 'Playlist'
? `/playlist/${res.id}/preview/lti`
: `/project/${res.id}/shared`
}
target="_blank"
rel="noreferrer"
>
<h2>{res.title || res.name}</h2>
</a>
<ul>
{res.user && (
<li>
by
{' '}
<span className="author">
{res.user.first_name}
</span>
</li>
)}
</ul>
</div>
</div>
</div>
</div>
)}
</Draggable>
)) : <h2>{!!searchBuilder.searchQuery && <> No result found!</>}</h2>}
</div>
)}
</Droppable>
<Droppable droppableId="droppable2" className="playlist-class">
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
// style={getListStyle(snapshot.isDraggingOver)}
>
<div className="playlist-host">
<h6>{!!playlist && playlist.title}</h6>
{!!playlist && playlist.activities.map((resource, index) => (
<ResourceCard
resource={resource}
key={resource.id}
index={index}
playlist={playlist}
wizard
/>
))}
</div>
</div>
)}
</Droppable>
</DragDropContext>
</div>
</div>
</div>
);
}
Example #27
Source File: projects.js From community-forum-frontend with GNU General Public License v3.0 | 4 votes |
render() {
return (
<div>
<Modal show={this.state.showModal} onHide={this.handleClose} centered>
<div className="modalbody">
<Modal.Body>
<TopicForm
projectID={this.state.projectID}
handleTopicSubmission={this.handleTopicSubmission}
/>
</Modal.Body>
</div>
</Modal>
<DragDropContext onDragEnd={this.onDragEnd}>
<div className="projectCards">
{this.state.categoriesArray.map((categoryID) => {
const category = this.state.categories[categoryID._id];
return (
<Card
className="projectCard"
bg="light"
style={{ width: "21rem" }}
key={category._id}
>
<Card.Header color="#366FF0" className="projectcardheader">
{category.categoryName}
</Card.Header>
<Droppable droppableId={category._id}>
{(provided) => (
<div
className="cardcontent"
ref={provided.innerRef}
{...provided.droppableProps}
>
{category.topicIds.map((topicid, index) => {
const topic = this.state.Topics[topicid];
if (topic) {
return (
<Draggable draggableId={topic._id} index={index}>
{(provided) => (
<Card
onClick={() =>
this.props.handleDiscussionTrue(topic)
}
key={topic._id}
className="topicscard"
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<Card.Title className="topicsheading">
{topic.topicName}
</Card.Title>
<Card.Text className="topicdescription">
{topic.topicDescription}
</Card.Text>
<div>
{topic.topicTags ? (
topic.topicTags.map((k) => {
return (
<Badge
variant="primary"
className="tags"
>
{k}
</Badge>
);
})
) : (
<Badge variant="primary"></Badge>
)}
</div>
</Card>
)}
</Draggable>
);
}
})}
{provided.placeholder}
</div>
)}
</Droppable>
<div
className="addnewcard"
onClick={() => this.handleShow(category._id)}
>
<IconContext.Provider
value={{
style: { verticalAlign: "middle" },
className: "reacticon",
}}
>
<FiPlus />
</IconContext.Provider>{" "}
Add another discussion
</div>
</Card>
);
})}
</div>
</DragDropContext>
</div>
);
}
Example #28
Source File: List.js From Trellis with GNU General Public License v3.0 | 4 votes |
export default function Column({ column, tasks, index }) {
const classes = useStyles()
const [cardTitle, setCardTitle] = useState('')
const [listTitle, setListTitle] = useState(column.name)
const [addCardFlag, setAddCardFlag] = useState(false)
const [editable, setEditable] = useState(false)
const [list, setList] = useState(true)
const [showDelete, setShowDelete] = useState(false)
const { token, user } = useSelector((state) => state.user)
const dispatch = useDispatch()
const handleChange = (e) => {
e.preventDefault()
setCardTitle(e.target.value)
}
const submitHandler = () => {
if (cardTitle === '') return
const text = cardTitle.trim().replace(/\s+/g, ' ')
setCardTitle(text)
const totalTasks = tasks.length
const postCardReq = {
name: text,
boardId: column.boardId,
listId: column._id,
order:
totalTasks === 0 ? 'n' : midString(tasks[totalTasks - 1].order, ''),
}
dispatch(createNewCard(postCardReq, token))
dispatch(
createNewActivity(
{
text: `${user.username} added ${text} to ${column.name}`,
boardId: column.boardId,
},
token,
),
)
setCardTitle('')
}
const handleAddition = () => {
setAddCardFlag(true)
}
const closeButtonHandler = () => {
setAddCardFlag(false)
setCardTitle('')
}
const changedHandler = (e) => {
e.preventDefault()
setListTitle(e.target.value)
}
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
e.preventDefault()
submitHandler()
}
}
const updateListTitle = () => {
const text = listTitle.trim().replace(/\s+/g, ' ')
if (text === '') {
setListTitle(column.name)
setEditable(false)
return
}
setListTitle(text)
dispatch(updateListById(column._id, { name: listTitle }))
// eslint-disable-next-line no-param-reassign
column.name = text
setEditable(false)
}
return (
<div className={classes.wrapper}>
{list && (
<Draggable draggableId={column._id} index={index}>
{(provided) => (
<div {...provided.draggableProps} ref={provided.innerRef}>
<Paper
elevation={0}
onMouseEnter={() => setShowDelete(true)}
onMouseLeave={() => setShowDelete(false)}
className={classes.root}
{...provided.dragHandleProps}
>
<div
className={classes.title}
onClick={() => setEditable(true)}
>
{!editable && (
<div style={{ position: 'relative' }}>
<div>{column.name}</div>
{showDelete && (
<IconButton
size="small"
style={{
right: 0,
top: 0,
position: 'absolute',
backgroundColor: '#EBECF0',
zIndex: 100,
}}
onClick={() => {
setList(false)
dispatch(deleteListById(column._id))
const text = `${user.username} deleted list ${column.name}`
dispatch(
createNewActivity(
{ text, boardId: column.boardId },
token,
),
)
}}
>
<DeleteIcon
fontSize="small"
style={{ backgroundColor: '#EBECF0' }}
/>
</IconButton>
)}
</div>
)}
{editable && (
<div className={classes.editable}>
<InputBase
onChange={changedHandler}
multiline
fullWidth
value={listTitle}
style={{ fontWeight: 'bold' }}
autoFocus
onFocus={(e) => {
const val = e.target.value
e.target.value = ''
e.target.value = val
}}
onBlur={updateListTitle}
/>
</div>
)}
</div>
<Droppable droppableId={column._id} type="card">
{
// eslint-disable-next-line no-shadow
(provided) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
<div className={classes.scroll}>
{/* eslint-disable-next-line no-shadow */}
{tasks.map((task, index) => (
<Card key={task._id} task={task} index={index} />
))}
{addCardFlag && (
<InputCard
value={cardTitle}
changedHandler={handleChange}
itemAdded={submitHandler}
closeHandler={closeButtonHandler}
keyDownHandler={handleKeyDown}
type="card"
btnText="Add Card"
placeholder="Enter a title for this card..."
width="230px"
/>
)}
{provided.placeholder}
</div>
{!addCardFlag && (
<AddItem
handleClick={handleAddition}
icon={<AddIcon />}
btnText="Add another card"
type="card"
width="256px"
/>
)}
</div>
)
}
</Droppable>
</Paper>
</div>
)}
</Draggable>
)}
</div>
)
}
Example #29
Source File: Board.js From Trellis with GNU General Public License v3.0 | 4 votes |
export default function Board() {
const classes = useStyles()
/* eslint-disable-next-line */
var { id, name } = useParams()
const { loading, currBoard, error } = useSelector((state) => state.boards)
const { listLoading, lists } = useSelector((state) => state.lists)
const { cardLoading, cards } = useSelector((state) => state.cards)
const { activities } = useSelector((state) => state.activities)
const { isValid, user, token, tokenRequest } = useSelector(
(state) => state.user,
)
const [initialData, setInitialData] = useState({})
const [initDone, setInitDone] = useState(false)
const addFlag = useRef(true)
const [addListFlag, setAddListFlag] = useState(false)
const [listTitle, setListTitle] = useState('')
const [color, setColor] = useState('white')
const [url, setUrl] = useState('')
const [editable, setEditable] = useState(false)
const [boardTitle, setBoardTitle] = useState('')
const dispatch = useDispatch()
if (!loading && name !== currBoard.name && currBoard.name !== undefined)
name = currBoard.name
else if (name === undefined) name = ''
useEffect(() => {
if (isValid && !error) {
if (id.length === 24) {
dispatch(fetchBoardById(id, token))
dispatch(fetchListsFromBoard(id, token))
dispatch(fetchsCardsFromBoard(id, token))
dispatch(fetchActivitiesFromBoard(id, token))
}
}
}, [dispatch, id, isValid, token, error])
useEffect(() => {
if (!_.isEmpty(currBoard)) {
setColor(currBoard.image.color)
setUrl(currBoard.image.full)
setBoardTitle(currBoard.name)
document.title = `${currBoard.name} | Trellis`
}
}, [currBoard])
useEffect(() => {
if (!listLoading && !cardLoading) {
const prevState = { tasks: {}, columns: {}, columnOrder: [] }
// eslint-disable-next-line no-shadow
const getTaskIds = (id) => {
const filteredTasks = _.filter(cards, { listId: id })
const sortedTasks = _.orderBy(filteredTasks, ['order'], ['asc'])
const taskIds = []
sortedTasks.forEach((task) => taskIds.push(task._id))
return taskIds
}
const setContent = () => {
cards.forEach((card) => (prevState.tasks[card._id] = card))
const sortedLists = _.orderBy(lists, ['order'], ['asc'])
sortedLists.forEach((list) => {
prevState.columns[list._id] = {
...list,
taskIds: getTaskIds(list._id),
}
prevState.columnOrder.push(list._id)
})
}
setContent()
setInitialData({ ...prevState })
setInitDone(true)
}
}, [setInitDone, listLoading, cardLoading, setInitialData, cards, lists])
const onDragEnd = (result) => {
// eslint-disable-next-line no-var
var newOrder
const { destination, source, draggableId, type } = result
if (!destination) return
if (
destination.droppableId === source.droppableId &&
destination.index === source.index
)
return
if (type === 'list') {
const listOrder = initialData.columnOrder
if (destination.index === 0) {
newOrder = midString('', initialData.columns[listOrder[0]].order)
} else if (destination.index === listOrder.length - 1) {
newOrder = midString(
initialData.columns[listOrder[destination.index]].order,
'',
)
} else if (destination.index < source.index) {
newOrder = midString(
initialData.columns[listOrder[destination.index - 1]].order,
initialData.columns[listOrder[destination.index]].order,
)
} else {
newOrder = midString(
initialData.columns[listOrder[destination.index]].order,
initialData.columns[listOrder[destination.index + 1]].order,
)
}
dispatch(updateListById(draggableId, { order: newOrder }))
const newListOrder = Array.from(initialData.columnOrder)
const destinationColumn = initialData.columns[draggableId]
destinationColumn.order = newOrder
newListOrder.splice(source.index, 1)
newListOrder.splice(destination.index, 0, draggableId)
const newData = {
...initialData,
columnOrder: newListOrder,
columns: {
...initialData.columns,
draggableId: destinationColumn,
},
}
setInitialData(newData)
return
}
const startList = initialData.columns[source.droppableId]
const endList = initialData.columns[destination.droppableId]
if (startList === endList) {
const column = startList
if (destination.index === 0)
newOrder = midString('', initialData.tasks[column.taskIds[0]].order)
else if (destination.index === column.taskIds.length - 1)
newOrder = midString(
initialData.tasks[column.taskIds[destination.index]].order,
'',
)
else if (destination.index < source.index)
newOrder = midString(
initialData.tasks[column.taskIds[destination.index - 1]].order,
initialData.tasks[column.taskIds[destination.index]].order,
)
else
newOrder = midString(
initialData.tasks[column.taskIds[destination.index]].order,
initialData.tasks[column.taskIds[destination.index + 1]].order,
)
dispatch(updateCardById(draggableId, { order: newOrder }))
const newTaskIds = Array.from(column.taskIds)
newTaskIds.splice(source.index, 1)
newTaskIds.splice(destination.index, 0, draggableId)
const destinationTask = initialData.tasks[draggableId]
destinationTask.order = newOrder
const newColumn = {
...column,
taskIds: newTaskIds,
}
const newData = {
...initialData,
columns: {
...initialData.columns,
[newColumn._id]: newColumn,
},
tasks: {
...initialData.tasks,
draggableId: destinationTask,
},
}
setInitialData(newData)
return
}
// Move from one list to another
if (endList.taskIds.length === 0) newOrder = 'n'
else if (destination.index === 0) {
newOrder = midString('', initialData.tasks[endList.taskIds[0]].order)
} else if (destination.index === endList.taskIds.length)
newOrder = midString(
initialData.tasks[endList.taskIds[destination.index - 1]].order,
'',
)
else
newOrder = midString(
initialData.tasks[endList.taskIds[destination.index - 1]].order,
initialData.tasks[endList.taskIds[destination.index]].order,
)
dispatch(
updateCardById(draggableId, { order: newOrder, listId: endList._id }),
)
const text = `${user.username} moved ${initialData.tasks[draggableId].name} from ${startList.name} to ${endList.name}`
const recentActivity = activities[activities.length - 1]
if (
recentActivity.text ===
`${user.username} moved ${initialData.tasks[draggableId].name} from ${endList.name} to ${startList.name}` &&
moment(recentActivity.createdAt).fromNow().includes('second')
) {
dispatch(deleteActivityById(recentActivity._id))
} else dispatch(createNewActivity({ text, boardId: currBoard._id }, token))
const startTaskIds = Array.from(startList.taskIds)
startTaskIds.splice(source.index, 1)
const newStartList = {
...startList,
taskIds: startTaskIds,
}
const destinationTask = initialData.tasks[draggableId]
destinationTask.order = newOrder
const endTaskIds = Array.from(endList.taskIds)
endTaskIds.splice(destination.index, 0, draggableId)
const newEndList = {
...endList,
taskIds: endTaskIds,
}
const newData = {
...initialData,
columns: {
...initialData.columns,
[newStartList._id]: newStartList,
[newEndList._id]: newEndList,
},
tasks: {
...initialData.tasks,
draggableId: destinationTask,
},
}
setInitialData(newData)
}
if (id.length < 24) return <h1>Invalid URL</h1>
const handleChange = (e) => {
e.preventDefault()
setListTitle(e.target.value)
}
const submitHandler = () => {
if (listTitle === '') return
const text = listTitle.trim().replace(/\s+/g, ' ')
if (text === '') {
setListTitle(listTitle)
return
}
const totalLists = initialData.columnOrder.length
const postListReq = {
name: text,
boardId: currBoard._id,
order:
totalLists === 0
? 'n'
: midString(
initialData.columns[initialData.columnOrder[totalLists - 1]]
.order,
'',
),
}
dispatch(createNewList(postListReq, token))
dispatch(
createNewActivity(
{
text: `${user.username} added ${listTitle} to this board`,
boardId: currBoard._id,
},
token,
),
)
setListTitle('')
}
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
e.preventDefault()
submitHandler()
}
}
const closeButtonHandler = () => {
setAddListFlag(false)
addFlag.current = true
setListTitle('')
}
const handleAddition = () => {
setAddListFlag(true)
addFlag.current = false
}
const setBackground = (background) => {
if (background.thumb) {
setUrl(background.full)
setColor('white')
dispatch(
updateBoardById(
currBoard._id,
{
image: {
full: background.full,
thumb: background.thumb,
color: 'white',
},
},
token,
),
)
} else {
setColor(background)
setUrl('')
dispatch(
updateBoardById(
currBoard._id,
{
image: {
full: '',
thumb: '',
color: background,
},
},
token,
),
)
}
}
return (
<>
{isValid || tokenRequest ? (
<div
className={classes.root}
style={{
backgroundColor: `${color}`,
backgroundImage: `url(${url})`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
}}
>
<Redirect to={`/b/${id}/${name}`} />
<Header loggedIn />
{editable ? (
<div className={classes.editable}>
<InputBase
onChange={(e) => {
e.preventDefault()
setBoardTitle(e.target.value)
}}
fullWidth
value={boardTitle}
style={{
fontWeight: 'bold',
fontFamily: 'sans-serif',
fontSize: '20px',
}}
autoFocus
onFocus={(e) => {
const val = e.target.value
e.target.value = ''
e.target.value = val
}}
onBlur={() => {
setEditable(false)
const text = boardTitle.trim().replace(/\s+/g, ' ')
if (text === '') {
setBoardTitle(currBoard.name)
return
}
dispatch(updateBoardById(id, { name: text }, token))
currBoard.name = boardTitle
}}
/>
</div>
) : (
<BoardHeader
title={currBoard.name}
showEditable={() => setEditable(true)}
/>
)}
<DragDropContext onDragEnd={onDragEnd}>
<Droppable
droppableId="all-columns"
direction="horizontal"
type="list"
>
{(provided) => (
<div
className={classes.listContainer}
{...provided.droppableProps}
ref={provided.innerRef}
>
{initDone &&
initialData.columnOrder.map((columnId, index) => {
const column = initialData.columns[columnId]
const tasks = column.taskIds.map(
(taskId) => initialData.tasks[taskId],
)
return (
<List
key={column._id}
column={column}
tasks={tasks}
index={index}
/>
)
})}
<div className={classes.wrapper}>
{addFlag.current && (
<AddItem
handleClick={handleAddition}
btnText="Add another list"
type="list"
icon={<AddIcon />}
width="256px"
color="white"
/>
)}
{addListFlag && (
<InputCard
value={listTitle}
changedHandler={handleChange}
itemAdded={submitHandler}
closeHandler={closeButtonHandler}
keyDownHandler={handleKeyDown}
type="list"
btnText="Add List"
placeholder="Enter list title..."
width="230px"
marginLeft="1"
/>
)}
</div>
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<SideMenu
setBackground={setBackground}
board={{ id, color, url, title: boardTitle }}
/>
</div>
) : (
<NotFound />
)}
</>
)
}