react-beautiful-dnd#DragDropContext TypeScript Examples
The following examples show how to use
react-beautiful-dnd#DragDropContext.
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: bodywrapper.tsx From gant-design with MIT License | 6 votes |
BodyWrapper = ({ children, ...props }) => {
const { onDragEnd } = useContext(TableBodyWrapperContext)
return (
<DragDropContext onDragEnd={onDragEnd} >
<Droppable droppableId='droppable'>
{
(provided, snapshot) => {
return (
<tbody {...props} ref={provided.innerRef} {...provided.droppableProps}>
{children}
{provided.placeholder}
</tbody>
)
}
}
</Droppable>
</DragDropContext>
)
}
Example #2
Source File: DndContextProvider.tsx From calories-in with MIT License | 6 votes |
function DndContextProvider({ children }: Props) {
const dietFormActions = useDietFormActions()
const onDragEnd = (dropResult: DropResult) => {
const { source, destination, type } = dropResult
if (!destination) {
return
}
if (type === 'variantsList') {
dietFormActions.moveVariantForm(source.index, destination.index)
} else if (type === 'mealsList') {
dietFormActions.moveMealForm(source.index, destination.index)
} else if (type === 'ingredientsList') {
dietFormActions.moveIngredientForm(
source.droppableId,
source.index,
destination.droppableId,
destination.index
)
}
}
return <DragDropContext onDragEnd={onDragEnd}>{children}</DragDropContext>
}
Example #3
Source File: FlowList.tsx From Protoman with MIT License | 6 votes |
FlowList: React.FunctionComponent<Props> = ({ collectionName }) => {
const dispatch = useDispatch();
const collection = useSelector((s: AppState) => getByKey(s.collections, collectionName));
const flowNames = useSelector((s: AppState) => collection?.flows?.map(([n]) => n));
const isCurrentCollection = useSelector((s: AppState) => s.currentCollection === collectionName);
const currentFlow = useSelector((s: AppState) => s.currentFlow);
function handleSelection(flowName: string): void {
dispatch(selectFlow(collectionName, flowName));
}
function validateFlowName(flowName: string): boolean {
return !collection?.flows?.map(([n]) => n)?.includes(flowName);
}
function handleDelete(flowName: string): void {
const flowCount = collection?.flows?.length || 0;
if (flowCount > 1) {
dispatch(deleteFlow(collectionName, flowName));
} else {
message.error("Can't delete the last request");
}
}
function handleClone(originalFlowName: string): void {
//check if this clone already exists
const tmpName = originalFlowName.concat('_clone');
let tmpNameIdx = 1;
while (!validateFlowName(`${tmpName}${tmpNameIdx}`)) tmpNameIdx++;
dispatch(cloneFlow(collectionName, originalFlowName, `${tmpName}${tmpNameIdx}`));
}
function handleDragEnd(result: DropResult): void {
console.log(result);
if (!result.destination || result.source.droppableId != result.destination.droppableId) return;
const src = result.source.index;
const dst = result.destination.index;
dispatch(reorderFlow(collectionName, src, dst));
}
return (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId={collectionName}>
{(provided): React.ReactElement => (
<div {...provided.droppableProps} ref={provided.innerRef}>
<List
dataSource={flowNames}
rowKey={(name): string => name}
renderItem={(flowName, idx): React.ReactNode => (
<FlowCell
idx={idx}
flowName={flowName}
emphasize={isCurrentCollection && currentFlow === flowName}
handleSelection={handleSelection}
handleDelete={handleDelete}
handleClone={handleClone}
/>
)}
/>
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #4
Source File: TasksBoard.tsx From projectboard with MIT License | 5 votes |
TasksBoard = () => {
const backlogTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks.backlog);
const todoTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks.todo);
const inProgressTasks: Array<Task> = useSelector(
(state: RootState) => state.taskList?.tasks?.in_progress
);
const doneTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks?.done);
const canceledTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks?.cancelled);
const todoSorted: Array<Task> = todoTasks.sort((prev: Task, next: Task) => prev.order - next.order);
const backlogSorted: Array<Task> = backlogTasks.sort((prev: Task, next: Task) => prev.order - next.order);
const inProgressSorted: Array<Task> = inProgressTasks.sort(
(prev: Task, next: Task) => prev.order - next.order
);
const doneSorted: Array<Task> = doneTasks.sort((prev: Task, next: Task) => prev.order - next.order);
const cancelledSorted: Array<Task> = canceledTasks.sort(
(prev: Task, next: Task) => prev.order - next.order
);
const match = useRouteMatch<MatchParams>();
const { getAccessTokenSilently } = useAuth0();
// dispatch
const dispatch = useDispatch<AppDispatch>();
const onDragEnd = async (
{ source, destination, draggableId }: DropResult,
provided: ResponderProvided
) => {
if (source.droppableId === destination?.droppableId) return;
if (!source || !destination) return;
const token = await getAccessTokenSilently();
dispatch(
changeStatusOfTaskBoard(
draggableId,
source.droppableId,
destination.droppableId,
source.index,
destination.index,
match.params.projectId,
token
)
);
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<div className="flex flex-1 pt-6 pl-8 overflow-scroll bg-gray-100">
<IssueCol title={'Backlog'} status={Status.BACKLOG} tasks={backlogSorted} />
<IssueCol title={'Todo'} status={Status.TODO} tasks={todoSorted} />
<IssueCol title={'In Progress'} status={Status.IN_PROGRESS} tasks={inProgressSorted} />
<IssueCol title={'Done'} status={Status.DONE} tasks={doneSorted} />
<IssueCol title={'Canceled'} status={Status.CANCELED} tasks={cancelledSorted} />
</div>
</DragDropContext>
);
}
Example #5
Source File: Todos.tsx From max-todos with MIT License | 5 votes |
Todos = () => {
const { todos, moveTodo } = useContext(MainContext)!;
const [deleteSnackOpen, setDeleteSnackOpen] = useState(false);
const [editSnackOpen, setEditSnackOpen] = useState(false);
const [dragging, setDragging] = useState(false);
const onDragEnd = (x: DropResult) => {
if (!x.destination) return console.log(x);
moveTodo(x.source.index, x.destination.index);
setTimeout(() => setDragging(false), 200);
};
return (
<>
<DragDropContext
onBeforeDragStart={() => setDragging(true)}
onDragEnd={onDragEnd}
>
<Droppable droppableId="0">
{(p) => (
<div {...p.droppableProps} ref={p.innerRef}>
<FlipMove disableAllAnimations={dragging}>
{todos.map((todo, i) => {
return (
<Todo
todo={todo}
key={todo.id}
onDelete={() => setDeleteSnackOpen(true)}
index={i}
onEdit={() => setEditSnackOpen(true)}
/>
);
})}
</FlipMove>
{p.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<Snackbar
open={deleteSnackOpen}
autoHideDuration={4000}
onClose={() => setDeleteSnackOpen(false)}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
>
<Alert
elevation={6}
variant="filled"
onClose={() => setDeleteSnackOpen(false)}
severity="success"
>
Successfully deleted item!
</Alert>
</Snackbar>
<Snackbar
open={editSnackOpen}
autoHideDuration={4000}
onClose={() => setEditSnackOpen(false)}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
>
<Alert
elevation={6}
variant="filled"
onClose={() => setEditSnackOpen(false)}
severity="success"
>
Successfully edited item!
</Alert>
</Snackbar>
</>
);
}
Example #6
Source File: designers.tsx From ali-react-table with MIT License | 5 votes |
export function CheckedDimList({ style, className, pivot }: CheckDimListProps) {
const dimMap = new Map(pivot.allDimensions.map((dim) => [dim.code, dim]))
return (
<CheckedDimListDiv style={style} className={className}>
<DragDropContext
onDragEnd={(result) => {
if (!result.destination) {
return
}
const i = result.source.index
const j = result.destination.index
const nextCodes = pivot.dimCodes.slice()
const [code] = nextCodes.splice(i, 1)
nextCodes.splice(j, 0, code)
pivot.changeDimCodes(nextCodes)
}}
>
<Droppable droppableId="dimension-list" direction="horizontal">
{(dropProvided, snapshot) => (
<div className="tag-list" ref={dropProvided.innerRef}>
{pivot.dimCodes.map((dimCode, index) => {
const allValues = pivot.dimValues[dimCode]
const values = pivot.filters[dimCode]
return (
<Draggable key={dimCode} draggableId={dimCode} index={index}>
{(dragProvided, snapshot) => (
<Overlay.Popup
key={dimCode}
trigger={
<div
key={dimCode}
ref={dragProvided.innerRef}
{...dragProvided.draggableProps}
{...dragProvided.dragHandleProps}
className={cx('tag', {
active: values.length < allValues.length,
empty: allValues.length > 0 && values.length === 0,
})}
>
{dimMap.get(dimCode).name}
<Filter16 className="filter-icon" />
</div>
}
triggerType="click"
>
<CheckedDimFilterPopup pivot={pivot} allValues={allValues} values={values} dimCode={dimCode} />
</Overlay.Popup>
)}
</Draggable>
)
})}
{dropProvided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</CheckedDimListDiv>
)
}
Example #7
Source File: NetworksDnd.tsx From devex with GNU General Public License v3.0 | 5 votes |
NetworksDnd: React.FC<IProps> = ({ cards, setCards, deleteNode, editNode }) => {
const onDragEnd = (result: any) => {
if (!result.destination) {
return
}
const reorderedCards = reorder(
cards,
result.source.index,
result.destination.index
)
setCards(reorderedCards)
}
return <Container>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
className='dnd'
{...provided.droppableProps}
ref={provided.innerRef}
style={getListStyle(snapshot.isDraggingOver)}
>
{cards.map((card, index) => (
<Draggable key={card.id} draggableId={card.id.toString()} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style
)}
>
<NetworkCard
key={card.url}
url={card.url}
name={card.name}
deleteNode={deleteNode}
editNode={editNode}
/>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</Container>
}
Example #8
Source File: Form.demo.dnd.tsx From mantine with MIT License | 5 votes |
function Demo() {
const form = useForm({
initialValues: {
employees: formList([
{ name: 'John Doe', email: '[email protected]' },
{ name: 'Bill Love', email: '[email protected]' },
{ name: 'Nancy Eagle', email: '[email protected]' },
{ name: 'Lim Notch', email: '[email protected]' },
{ name: 'Susan Seven', email: '[email protected]' },
]),
},
});
const fields = form.values.employees.map((_, index) => (
<Draggable key={index} index={index} draggableId={index.toString()}>
{(provided) => (
<Group ref={provided.innerRef} mt="xs" {...provided.draggableProps}>
<Center {...provided.dragHandleProps}>
<GripVertical size={18} />
</Center>
<TextInput
placeholder="John Doe"
{...form.getListInputProps('employees', index, 'name')}
/>
<TextInput
placeholder="[email protected]"
{...form.getListInputProps('employees', index, 'email')}
/>
</Group>
)}
</Draggable>
));
return (
<Box sx={{ maxWidth: 500 }} mx="auto">
<DragDropContext
onDragEnd={({ destination, source }) =>
form.reorderListItem('employees', { from: source.index, to: destination.index })
}
>
<Droppable droppableId="dnd-list" direction="vertical">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{fields}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<Group position="center" mt="md">
<Button onClick={() => form.addListItem('employees', { name: '', email: '' })}>
Add employee
</Button>
</Group>
<Text size="sm" weight={500} mt="md">
Form values:
</Text>
<Code block>{JSON.stringify(form.values, null, 2)}</Code>
</Box>
);
}
Example #9
Source File: App.tsx From TabMerger with GNU General Public License v3.0 | 5 votes |
App = (): JSX.Element => {
const dispatch = useDispatch();
const { filterChoice } = useSelector((state) => state.header);
const { active, available } = useSelector((state) => state.groups.present);
const { filteredGroups } = useFilter();
useInitState({ available, active });
useUpdateWindows();
useUpdateInfo();
useExecuteCommand();
const { onBeforeCapture, onDragStart, onDragEnd } = useDnd();
return (
<Container>
<GlobalStyle />
<ErrorBoundary
FallbackComponent={ErrorBoundaryFallback}
onReset={() => {
// Revert 2 steps back
dispatch(ActionCreators.jump(-2));
}}
>
<Header />
<DragDropContext onBeforeCapture={onBeforeCapture} onDragStart={onDragStart} onDragEnd={onDragEnd}>
{(filterChoice === "tab" || (filterChoice === "group" && filteredGroups.length > 0)) && (
<MainArea>
<SidePanel />
<Windows />
</MainArea>
)}
</DragDropContext>
</ErrorBoundary>
</Container>
);
}
Example #10
Source File: Tree.tsx From react-beautiful-tree with Apache License 2.0 | 5 votes |
render() {
const {
isNestingEnabled,
isVirtualizationEnabled,
virtualItemHeight,
} = this.props
const { flattenedTree } = this.state
const renderedItems = this.renderItems()
return (
<DragDropContext
onDragStart={this.onDragStart}
onDragEnd={this.onDragEnd}
onDragUpdate={this.onDragUpdate}
>
<Droppable
droppableId="tree"
isCombineEnabled={isNestingEnabled}
ignoreContainerClipping
mode={isVirtualizationEnabled ? 'virtual' : 'standard'}
renderClone={
isVirtualizationEnabled
? (provided, snapshot, rubric) =>
this.renderVirtualItem({
provided,
snapshot,
flatItem: flattenedTree[rubric.source.index],
})
: undefined
}
>
{(provided: DroppableProvided) => {
const finalProvided: DroppableProvided = this.patchDroppableProvided(
provided
)
return isVirtualizationEnabled ? (
<AutoSizer defaultHeight={1} defaultWidth={1}>
{({ height, width }: { height: number; width: number }) => (
<FixedSizeList
height={height}
itemCount={flattenedTree.length}
itemSize={virtualItemHeight}
width={width}
outerRef={provided.innerRef}
itemData={flattenedTree}
>
{this.renderVirtualRow}
</FixedSizeList>
)}
</AutoSizer>
) : (
<div
ref={finalProvided.innerRef}
style={{ pointerEvents: 'auto' }}
onTouchMove={this.onPointerMove}
onMouseMove={this.onPointerMove}
{...finalProvided.droppableProps}
>
{renderedItems}
{provided.placeholder}
</div>
)
}}
</Droppable>
</DragDropContext>
)
}
Example #11
Source File: index.tsx From Tiquet with MIT License | 5 votes |
Board = ({
fetchBoard,
moveTask,
board,
match,
resetState,
fetchPriorities,
deleteList,
sortList,
}: IProps): JSX.Element => {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const boardId: number = match.params.id;
trackPageView('Board');
fetchPriorities();
fetchBoard(boardId)
.then(() => setIsLoading(false));
return () => {
resetState();
}
}, []);
const onDragEnd = (e: any): void => {
const { draggableId, destination, source } = e;
const originListId = parseInt(source.droppableId);
const destinationListId = parseInt(destination.droppableId);
const taskId = parseInt(draggableId);
if (originListId != destinationListId) {
trackEvent({
category: 'Lists',
action: 'Task moved to another list',
});
moveTask(originListId, destinationListId, taskId, destination.index);
} else {
trackEvent({
category: 'Tasks',
action: 'Updated position',
});
sortList(originListId, taskId, source.index, destination.index);
}
}
const onListDelete = (listId: number) => {
trackEvent({
category: 'Lists',
action: 'List deleted',
});
deleteList(listId);
};
return (
<div className="board">
<Loading display={isLoading} />
<TaskDescription />
<h1 className="board__title">{board.title}</h1>
<div className="board__columns">
<DragDropContext onDragEnd={result => onDragEnd(result)} >
{board.lists.map(list => (
<List
key={list.id}
onDelete={() => onListDelete(list.id)}
{...list}
/>
))}
</DragDropContext>
<CreateList />
</div>
</div>
);
}
Example #12
Source File: TabContainer.tsx From yana with MIT License | 5 votes |
TabContainer: React.FC<{}> = props => {
const theme = useTheme();
const mainContent = useMainContentContext();
return (
<DragDropContext
onDragEnd={(result, provided) => {
if (result.destination?.index !== undefined) {
mainContent.reorderTab(result.source.index, result.destination.index);
}
}}
>
<Droppable droppableId="tabs-droppable" direction="horizontal">
{(provided, snapshot) => (
<div className={styles.tabsContainer} ref={provided.innerRef} {...provided.droppableProps}>
{mainContent.tabs.map((tab, idx) => {
const id = tab.dataItem?.id ?? tab.page ?? 'unknown';
const name = tab.dataItem?.name ?? (tab.page ? pages[tab.page]?.title : 'Unknown name');
return (
<Draggable key={id} draggableId={id} index={idx} disableInteractiveElementBlocking={true}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className={cx(
styles.tab,
snapshot.isDragging && styles.draggingTab,
cxs({
color:
mainContent.openTabId === idx
? 'white'
: Color(theme.topBarColor).mix(Color('#ffffff'), 0.5).toString(),
borderBottom: mainContent.openTabId === idx ? `4px solid ${theme.primaryColor}` : undefined,
fontWeight: mainContent.openTabId === idx ? 'bold' : 'normal',
backgroundColor: theme.topBarColor,
':hover': {
borderBottom: mainContent.openTabId !== idx ? `4px solid white` : undefined,
},
})
)}
onClick={() => mainContent.activateTab(idx)}
>
{name.substr(0, 12)}
{name.length > 12 ? '...' : ''}
<div
className={cx(
styles.closeContainer,
cxs({
backgroundColor: theme.topBarColor,
})
)}
onClick={e => {
e.stopPropagation();
mainContent.closeTab(idx);
}}
>
<Icon icon={'cross'} />
</div>
</div>
)}
</Draggable>
);
})}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #13
Source File: DraggableTags.tsx From nebula-studio with Apache License 2.0 | 5 votes |
render() {
const list = this.props.data.map(item => ({
id: `field-${item}`,
content: (
<Tag className={styles.dragItem} closable={true} onClose={() => this.props.removeData(item)}>
{item}
</Tag>
),
}));
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable" direction="horizontal">
{(provided, _snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={{
display: 'flex',
overflow: 'auto',
flexWrap: 'wrap',
}}
>
{list.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style,
)}
>
{item.content}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #14
Source File: StatOptionList.tsx From slippi-stats with MIT License | 5 votes |
StatOptionList: React.FC<StatOptionListProps> = (props) => {
const statOptions = props.value;
const onDragEnd = (result: any) => {
const { destination, source } = result;
if (!destination) {
return;
}
if (destination.droppableId === source.droppableId && destination.index === source.index) {
return;
}
const newArray = reorder(props.value, source.index, destination.index);
props.onChange(newArray);
};
const toggle = (statId: string) => {
const optionIndex = statOptions.findIndex((o) => o.statId === statId);
if (optionIndex === -1) {
return;
}
const newOptions = Array.from(statOptions);
const option = newOptions[optionIndex];
option.enabled = !option.enabled;
props.onChange(newOptions);
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="stat-option-list">
{(provided) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{statOptions.map((option, i) => {
return (
<StatOptionItem
key={option.statId}
index={i}
id={option.statId}
checked={option.enabled}
onChange={() => toggle(option.statId)}
/>
);
})}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #15
Source File: index.tsx From peterportal-client with MIT License | 4 votes |
RoadmapPage: FC = () => {
const dispatch = useAppDispatch();
const showSearch = useAppSelector(state => state.roadmap.showSearch);
const onDragEnd = useCallback((result: DropResult) => {
if (result.reason === 'DROP') {
// no destination or dragging to search bar
if (!result.destination || result.destination.droppableId === 'search') {
// removing from quarter
if (result.source.droppableId != 'search') {
let [yearIndex, quarterIndex] = result.source.droppableId.split('-');
dispatch(deleteCourse({
yearIndex: parseInt(yearIndex),
quarterIndex: parseInt(quarterIndex),
courseIndex: result.source.index
}))
}
return;
}
let movePayload = {
from: {
yearIndex: -1,
quarterIndex: -1,
courseIndex: -1,
},
to: {
yearIndex: -1,
quarterIndex: -1,
courseIndex: -1
}
};
console.log(result.source.droppableId, '=>', result.destination.droppableId)
// roadmap to roadmap has source
if (result.source.droppableId != 'search') {
let [yearIndex, quarterIndex] = result.source.droppableId.split('-');
movePayload.from.yearIndex = parseInt(yearIndex);
movePayload.from.quarterIndex = parseInt(quarterIndex);
movePayload.from.courseIndex = result.source.index;
}
// search to roadmap has no source (use activeCourse in global state)
// both have destination
let [yearIndex, quarterIndex] = result.destination.droppableId.split('-');
movePayload.to.yearIndex = parseInt(yearIndex);
movePayload.to.quarterIndex = parseInt(quarterIndex);
movePayload.to.courseIndex = result.destination.index;
console.log(movePayload);
dispatch(moveCourse(movePayload));
}
}, []);
const onDragUpdate = useCallback((initial: DragUpdate) => {
console.log(initial)
}, []);
// do not conditionally renderer because it would remount planner which would discard unsaved changes
const mobileVersion = <>
<div className={`main-wrapper mobile ${showSearch ? 'hide' : ''}`}>
<Planner />
</div>
<div className={`sidebar-wrapper mobile ${!showSearch ? 'hide' : ''}`}>
<SearchSidebar />
</div>
</>
const desktopVersion = <>
<div className='main-wrapper'>
<Planner />
</div>
<div className='sidebar-wrapper'>
<SearchSidebar />
</div>
</>
return (
<>
<div className='roadmap-page'>
<AddCoursePopup />
<DragDropContext onDragEnd={onDragEnd} onDragUpdate={onDragUpdate}>
{isMobile && mobileVersion}
{!isMobile && desktopVersion}
</DragDropContext>
</div>
</>
);
}
Example #16
Source File: index.tsx From scorpio-h5-design with MIT License | 4 votes |
export default function() {
const [isOrdering, setIsOrdering] = useBoolean(false);
const {
isDraging, pageSchema, selectPageIndex, dragingComponentIndex, selectComponent,
onDragEnter, onDragLeave, onDrop, onSelectComponent, onSortEnd, showSelectComponentBorder,
} = useModel('bridge');
let components:any[] = [];
if (pageSchema.length > 0 && selectPageIndex !== -1) {
components = pageSchema[selectPageIndex].components;
}
return (
<DragDropContext
onDragStart={()=>{
setIsOrdering.setTrue();
}}
onDragEnd={(result: DropResult)=>{
onSortEnd(result, components);
setIsOrdering.setFalse();
}}
>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{components.length > 0 && components.map((item: any, index: any) => (
<Draggable key={item.uuid} draggableId={item.uuid} index={index}>
{(provided, snapshot) => (
<>
<div
className={classnames(
'h5-canvas-empty-block',
{
show: isDraging,
over: dragingComponentIndex === index,
}
)}
onDragEnter={() => { onDragEnter(index); }}
onDragLeave={() => { onDragLeave(); }}
onDragOver={(event) => { event.preventDefault(); }}
onDrop={(event)=>{onDrop(event, index);}}
/>
<div
className={classnames(
'h5-canvas-block',
{
'blur': !snapshot.isDragging && isOrdering,
'isSelected': selectComponent?.uuid === item.uuid,
'border': selectComponent?.uuid === item.uuid && showSelectComponentBorder,
}
)}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
onClick={() => { onSelectComponent(item.uuid); }}
>
<DynamicComponent
id={item._id}
uuid={item.uuid}
isSelected={selectComponent?.uuid === item.uuid}
componentProps={item.props}
containerProps={item.containerProps}
/>
</div>
</>
)}
</Draggable>
))}
{provided.placeholder}
<div
className={classnames(
'h5-canvas-empty-block',
{
show: isDraging,
over: dragingComponentIndex === components.length,
}
)}
onDragEnter={() => { onDragEnter(components.length); }}
onDragLeave={() => { onDragLeave(); }}
onDragOver={(event) => { event.preventDefault(); }}
onDrop={(event)=>{onDrop(event, components.length);}}
/>
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #17
Source File: AppTable.tsx From flame with MIT License | 4 votes |
AppTable = (props: Props): JSX.Element => {
const {
apps: { apps },
config: { config },
} = useSelector((state: State) => state);
const dispatch = useDispatch();
const { pinApp, deleteApp, reorderApps, createNotification, updateApp } =
bindActionCreators(actionCreators, dispatch);
const [localApps, setLocalApps] = useState<App[]>([]);
// Copy apps array
useEffect(() => {
setLocalApps([...apps]);
}, [apps]);
const dragEndHanlder = (result: DropResult): void => {
if (config.useOrdering !== 'orderId') {
createNotification({
title: 'Error',
message: 'Custom order is disabled',
});
return;
}
if (!result.destination) {
return;
}
const tmpApps = [...localApps];
const [movedApp] = tmpApps.splice(result.source.index, 1);
tmpApps.splice(result.destination.index, 0, movedApp);
setLocalApps(tmpApps);
reorderApps(tmpApps);
};
// Action handlers
const deleteAppHandler = (id: number, name: string) => {
const proceed = window.confirm(`Are you sure you want to delete ${name}?`);
if (proceed) {
deleteApp(id);
}
};
const updateAppHandler = (id: number) => {
const app = apps.find((a) => a.id === id) as App;
props.openFormForUpdating(app);
};
const pinAppHandler = (id: number) => {
const app = apps.find((a) => a.id === id) as App;
pinApp(app);
};
const changeAppVisibiltyHandler = (id: number) => {
const app = apps.find((a) => a.id === id) as App;
updateApp(id, { ...app, isPublic: !app.isPublic });
};
return (
<Fragment>
<Message isPrimary={false}>
{config.useOrdering === 'orderId' ? (
<p>You can drag and drop single rows to reorder application</p>
) : (
<p>
Custom order is disabled. You can change it in the{' '}
<Link to="/settings/general">settings</Link>
</p>
)}
</Message>
<DragDropContext onDragEnd={dragEndHanlder}>
<Droppable droppableId="apps">
{(provided) => (
<Table
headers={['Name', 'URL', 'Icon', 'Visibility', 'Actions']}
innerRef={provided.innerRef}
>
{localApps.map((app: App, index): JSX.Element => {
return (
<Draggable
key={app.id}
draggableId={app.id.toString()}
index={index}
>
{(provided, snapshot) => {
const style = {
border: snapshot.isDragging
? '1px solid var(--color-accent)'
: 'none',
borderRadius: '4px',
...provided.draggableProps.style,
};
return (
<tr
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
style={style}
>
<td style={{ width: '200px' }}>{app.name}</td>
<td style={{ width: '200px' }}>{app.url}</td>
<td style={{ width: '200px' }}>{app.icon}</td>
<td style={{ width: '200px' }}>
{app.isPublic ? 'Visible' : 'Hidden'}
</td>
{!snapshot.isDragging && (
<TableActions
entity={app}
deleteHandler={deleteAppHandler}
updateHandler={updateAppHandler}
pinHanlder={pinAppHandler}
changeVisibilty={changeAppVisibiltyHandler}
/>
)}
</tr>
);
}}
</Draggable>
);
})}
</Table>
)}
</Droppable>
</DragDropContext>
</Fragment>
);
}
Example #18
Source File: Array.tsx From payload with MIT License | 4 votes |
ArrayFieldType: React.FC<Props> = (props) => {
const {
name,
path: pathFromProps,
fields,
fieldTypes,
validate = array,
required,
maxRows,
minRows,
permissions,
admin: {
readOnly,
description,
condition,
className,
},
} = props;
// Handle labeling for Arrays, Global Arrays, and Blocks
const getLabels = (p: Props) => {
if (p?.labels) return p.labels;
if (p?.label) return { singular: p.label, plural: undefined };
return { singular: 'Row', plural: 'Rows' };
};
const labels = getLabels(props);
// eslint-disable-next-line react/destructuring-assignment
const label = props?.label ?? props?.labels?.singular;
const [rows, dispatchRows] = useReducer(reducer, []);
const formContext = useForm();
const { user } = useAuth();
const { id } = useDocumentInfo();
const locale = useLocale();
const operation = useOperation();
const { dispatchFields } = formContext;
const path = pathFromProps || name;
const memoizedValidate = useCallback((value, options) => {
return validate(value, { ...options, minRows, maxRows, required });
}, [maxRows, minRows, required, validate]);
const [disableFormData, setDisableFormData] = useState(false);
const {
showError,
errorMessage,
value,
setValue,
} = useField({
path,
validate: memoizedValidate,
disableFormData,
condition,
});
const addRow = useCallback(async (rowIndex) => {
const subFieldState = await buildStateFromSchema({ fieldSchema: fields, operation, id, user, locale });
dispatchFields({ type: 'ADD_ROW', rowIndex, subFieldState, path });
dispatchRows({ type: 'ADD', rowIndex });
setValue(value as number + 1);
}, [dispatchRows, dispatchFields, fields, path, setValue, value, operation, id, user, locale]);
const removeRow = useCallback((rowIndex) => {
dispatchRows({ type: 'REMOVE', rowIndex });
dispatchFields({ type: 'REMOVE_ROW', rowIndex, path });
setValue(value as number - 1);
}, [dispatchRows, dispatchFields, path, value, setValue]);
const moveRow = useCallback((moveFromIndex, moveToIndex) => {
dispatchRows({ type: 'MOVE', moveFromIndex, moveToIndex });
dispatchFields({ type: 'MOVE_ROW', moveFromIndex, moveToIndex, path });
}, [dispatchRows, dispatchFields, path]);
const onDragEnd = useCallback((result) => {
if (!result.destination) return;
const sourceIndex = result.source.index;
const destinationIndex = result.destination.index;
moveRow(sourceIndex, destinationIndex);
}, [moveRow]);
useEffect(() => {
const data = formContext.getDataByPath(path);
dispatchRows({ type: 'SET_ALL', data: data || [] });
}, [formContext, path]);
useEffect(() => {
setValue(rows?.length || 0, true);
if (rows?.length === 0) {
setDisableFormData(false);
} else {
setDisableFormData(true);
}
}, [rows, setValue]);
const hasMaxRows = maxRows && rows.length >= maxRows;
const classes = [
baseClass,
className,
].filter(Boolean).join(' ');
return (
<DragDropContext onDragEnd={onDragEnd}>
<div
className={classes}
>
<div className={`${baseClass}__error-wrap`}>
<Error
showError={showError}
message={errorMessage}
/>
</div>
<header className={`${baseClass}__header`}>
<h3>{label}</h3>
<FieldDescription
value={value}
description={description}
/>
</header>
<Droppable droppableId="array-drop">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
>
{rows.length > 0 && rows.map((row, i) => (
<DraggableSection
readOnly={readOnly}
key={row.id}
id={row.id}
blockType="array"
label={labels.singular}
rowCount={rows.length}
rowIndex={i}
addRow={addRow}
removeRow={removeRow}
moveRow={moveRow}
parentPath={path}
fieldTypes={fieldTypes}
fieldSchema={fields}
permissions={permissions}
hasMaxRows={hasMaxRows}
/>
))}
{(rows.length < minRows || (required && rows.length === 0)) && (
<Banner type="error">
This field requires at least
{' '}
{minRows
? `${minRows} ${labels.plural}`
: `1 ${labels.singular}`}
</Banner>
)}
{(rows.length === 0 && readOnly) && (
<Banner>
This field has no
{' '}
{labels.plural}
.
</Banner>
)}
{provided.placeholder}
</div>
)}
</Droppable>
{(!readOnly && (!hasMaxRows)) && (
<div className={`${baseClass}__add-button-wrap`}>
<Button
onClick={() => addRow(value)}
buttonStyle="icon-label"
icon="plus"
iconStyle="with-border"
iconPosition="left"
>
{`Add ${labels.singular}`}
</Button>
</div>
)}
</div>
</DragDropContext>
);
}
Example #19
Source File: Categories.tsx From revite with GNU Affero General Public License v3.0 | 4 votes |
Categories = observer(({ server }: Props) => {
const [status, setStatus] = useState<EditStatus>("saved");
const [categories, setCategories] = useState<API.Category[]>(
server.categories ?? [],
);
useAutosave(
async () => {
setStatus("saving");
await server.edit({ categories });
setStatus("saved");
},
categories,
server.categories,
() => setStatus("editing"),
);
const defaultCategory = useMemo(() => {
return {
title: "Uncategorized",
channels: [...server.channels]
.filter((x) => x)
.map((x) => x!._id)
.filter(
(x) => !categories.find((cat) => cat.channels.includes(x)),
),
id: "none",
};
}, [categories, server.channels]);
return (
<>
<Header>
<h1>
<Text id={`app.settings.server_pages.categories.title`} />
</h1>
<SaveStatus status={status} />
</Header>
<DragDropContext
onDragEnd={(target) => {
const { destination, source, draggableId, type } = target;
if (
!destination ||
(destination.droppableId === source.droppableId &&
destination.index === source.index)
) {
return;
}
if (type === "column") {
if (destination.index === 0) return;
// Remove from array.
const cat = categories.find(
(x) => x.id === draggableId,
);
const arr = categories.filter(
(x) => x.id !== draggableId,
);
// Insert at new position.
arr.splice(destination.index - 1, 0, cat!);
setCategories(arr);
} else {
setCategories(
categories.map((category) => {
if (category.id === destination.droppableId) {
const channels = category.channels.filter(
(x) => x !== draggableId,
);
channels.splice(
destination.index,
0,
draggableId,
);
return {
...category,
channels,
};
} else if (category.id === source.droppableId) {
return {
...category,
channels: category.channels.filter(
(x) => x !== draggableId,
),
};
}
return category;
}),
);
}
}}>
<FullSize>
<Droppable
droppableId="categories"
direction="horizontal"
type="column">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}>
<KanbanBoard>
<ListElement
category={defaultCategory}
server={server}
index={0}
addChannel={noop}
/>
{categories.map((category, index) => (
<ListElement
draggable
category={category}
server={server}
index={index + 1}
key={category.id}
setTitle={(title) => {
setCategories(
categories.map((x) =>
x.id === category.id
? {
...x,
title,
}
: x,
),
);
}}
deleteSelf={() =>
setCategories(
categories.filter(
(x) =>
x.id !==
category.id,
),
)
}
addChannel={(channel) => {
setCategories(
categories.map((x) =>
x.id === category.id
? {
...x,
channels: [
...x.channels,
channel._id,
],
}
: x,
),
);
}}
/>
))}
<KanbanList last>
<div class="inner">
<KanbanListHeader
onClick={() =>
setCategories([
...categories,
{
id: ulid(),
title: "New Category",
channels: [],
},
])
}>
<Plus size={24} />
</KanbanListHeader>
</div>
</KanbanList>
{provided.placeholder}
</KanbanBoard>
</div>
)}
</Droppable>
</FullSize>
</DragDropContext>
</>
);
})
Example #20
Source File: Board.tsx From knboard with MIT License | 4 votes |
Board = () => {
const detail = useSelector((state: RootState) => state.board.detail);
const error = useSelector((state: RootState) => state.board.detailError);
const columns = useSelector(columnSelectors.selectAll);
const tasksByColumn = useSelector((state: RootState) => state.task.byColumn);
const tasksById = useSelector((state: RootState) => state.task.byId);
const dispatch = useDispatch();
const { id } = useParams();
React.useEffect(() => {
if (id) {
dispatch(fetchBoardById({ boardId: id }));
}
}, [id]);
const onDragEnd = (result: DropResult) => {
// dropped nowhere
if (!result.destination) {
return;
}
const source: DraggableLocation = result.source;
const destination: DraggableLocation = result.destination;
// did not move anywhere - can bail early
if (
source.droppableId === destination.droppableId &&
source.index === destination.index
) {
return;
}
// reordering column
if (result.type === "COLUMN") {
const newOrdered: IColumn[] = reorder(
columns,
source.index,
destination.index
);
dispatch(updateColumns(newOrdered));
return;
}
const data = reorderTasks({
tasksByColumn,
source,
destination,
});
dispatch(updateTasksByColumn(data.tasksByColumn));
};
const detailDataExists = detail?.id.toString() === id;
if (error) {
return <PageError>{error}</PageError>;
}
if (!detailDataExists) {
return <Spinner loading={!detailDataExists} />;
}
return (
<>
<SEO title={detail?.name} />
{columns.length !== 0 ? (
<BoardContainer data-testid="board-container">
<ColumnsBlock>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable
droppableId="board"
type="COLUMN"
direction="horizontal"
>
{(provided: DroppableProvided) => (
<ColumnContainer
ref={provided.innerRef}
{...provided.droppableProps}
>
{columns.map((column: IColumn, index: number) => (
<Column
key={column.id}
id={column.id}
title={column.title}
index={index}
tasks={tasksByColumn[column.id].map(
(taskId) => tasksById[taskId]
)}
/>
))}
{provided.placeholder}
</ColumnContainer>
)}
</Droppable>
</DragDropContext>
</ColumnsBlock>
<RightMargin />
</BoardContainer>
) : (
<EmptyBoard>This board is empty.</EmptyBoard>
)}
</>
);
}
Example #21
Source File: index.tsx From nanolooker with MIT License | 4 votes |
CryptocurrencyPreferences: React.FC<Props> = ({ isDetailed }) => {
const { t } = useTranslation();
const {
cryptocurrency,
addCryptocurrency,
removeCryptocurrency,
reorderCryptocurrency,
} = React.useContext(PreferencesContext);
const [search, setSearch] = React.useState<string>("");
const onSearch = (value: string) => {
setSearch(value);
};
const onSelect = (value: string) => {
const { symbol = "" } = dataSource.find(({ name }) => name === value) || {};
addCryptocurrency(symbol);
setSearch("");
};
const options = dataSource.map(({ name, symbol }) => (
<Option
key={name}
value={name}
symbol={symbol}
disabled={cryptocurrency.includes(symbol) || symbol === "nano"}
>
<img
src={`/cryptocurrencies/logo/${symbol}.png`}
alt={name}
width="16px"
height="16px"
style={{ marginRight: "6px" }}
/>
{name}
</Option>
));
const reorder = (list: string[], startIndex: number, endIndex: number) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const onDragEnd = (result: any) => {
const items = reorder(
cryptocurrency,
result.source?.index || 0,
result.destination?.index || 0,
);
reorderCryptocurrency(items);
};
return (
<Row>
<Col xs={24}>
<Text
className={isDetailed ? "preference-detailed-title" : ""}
style={{
display: "block",
marginBottom: "6px",
}}
>
{t("preferences.watch")}
</Text>
</Col>
{isDetailed ? (
<Col xs={24} style={{ marginBottom: "6px" }}>
<Text>{t("preferences.watchDetailed")}</Text>
</Col>
) : null}
<Col xs={24} md={isDetailed ? 12 : 24}>
<AutoComplete
value={search}
style={{ width: "100%" }}
filterOption={(value = "", option) => {
const { value: name, symbol } = option as any;
return (
name.toLowerCase().includes(value.toLowerCase()) ||
symbol.toLowerCase().includes(value.toLowerCase())
);
}}
onSearch={onSearch}
onSelect={onSelect}
placeholder={t("preferences.watchSearch")}
>
{options}
</AutoComplete>
{cryptocurrency.length ? (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId={`hello${isDetailed ? "-detailed" : ""}`}>
{(provided, snapshot) => (
<ul
style={{
margin: 0,
padding: "6px",
marginTop: "6px",
backgroundColor: snapshot.isDraggingOver
? "#1890ff24"
: "#f6f6f6",
listStyle: "none",
}}
ref={provided.innerRef}
{...provided.droppableProps}
>
{cryptocurrency.map((symbol, index) => {
const { name = "" } =
dataSource.find(
({ symbol: sourceSymbol }) => sourceSymbol === symbol,
) || {};
return (
<Draggable draggableId={name} index={index} key={name}>
{provided => {
// https://github.com/atlassian/react-beautiful-dnd/issues/1662#issuecomment-708538811
if (
typeof provided.draggableProps.onTransitionEnd ===
"function"
) {
window?.requestAnimationFrame(() =>
// @ts-ignore
provided?.draggableProps?.onTransitionEnd?.({
propertyName: "transform",
}),
);
}
return (
<li
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
padding: "6px",
marginTop: "-1px",
backgroundColor: "#fff",
border: "1px solid #d9d9d9",
...(index !== cryptocurrency.length - 1
? { marginBottom: "6px" }
: { marginBottom: "-1px" }),
}}
>
<span>
<img
src={`/cryptocurrencies/logo/${symbol}.png`}
alt={name}
width="16px"
height="16px"
style={{ marginRight: "6px" }}
/>
{name}
</span>
<DeleteButton
onClick={(e: Event) => {
e.stopPropagation();
removeCryptocurrency(symbol);
}}
/>
</div>
</li>
);
}}
</Draggable>
);
})}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
) : null}
</Col>
</Row>
);
}
Example #22
Source File: DataModelTree.tsx From datart with Apache License 2.0 | 4 votes |
DataModelTree: FC = memo(() => {
const t = useI18NPrefix('view');
const { actions } = useViewSlice();
const dispatch = useDispatch();
const [openStateModal, contextHolder] = useStateModal({});
const currentEditingView = useSelector(selectCurrentEditingView);
const stage = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'stage' }),
) as ViewViewModelStages;
const [hierarchy, setHierarchy] = useState<Nullable<Model>>();
useEffect(() => {
setHierarchy(currentEditingView?.model?.hierarchy);
}, [currentEditingView?.model?.hierarchy]);
const tableColumns = useMemo<Column[]>(() => {
return Object.entries(hierarchy || {})
.map(([name, column], index) => {
return Object.assign({ index }, column, { name });
})
.sort(dataModelColumnSorter);
}, [hierarchy]);
const handleDeleteBranch = (node: Column) => {
const newHierarchy = deleteBranch(tableColumns, node);
handleDataModelHierarchyChange(newHierarchy);
};
const handleDeleteFromBranch = (parent: Column) => (node: Column) => {
const newHierarchy = deleteFromBranch(tableColumns, parent, node);
handleDataModelHierarchyChange(newHierarchy);
};
const handleNodeTypeChange = (type, name) => {
const targetNode = tableColumns?.find(n => n.name === name);
if (targetNode) {
let newNode;
if (type.includes('category')) {
const category = type.split('-')[1];
newNode = { ...targetNode, category };
} else {
newNode = { ...targetNode, type: type };
}
const newHierarchy = updateNode(
tableColumns,
newNode,
tableColumns?.findIndex(n => n.name === name),
);
handleDataModelHierarchyChange(newHierarchy);
return;
}
const targetBranch = tableColumns?.find(b =>
b?.children?.find(bn => bn.name === name),
);
if (!!targetBranch) {
const newNodeIndex = targetBranch.children?.findIndex(
bn => bn.name === name,
);
if (newNodeIndex !== undefined && newNodeIndex > -1) {
const newTargetBranch = CloneValueDeep(targetBranch);
if (newTargetBranch.children) {
let newNode = newTargetBranch.children[newNodeIndex];
if (type.includes('category')) {
const category = type.split('-')[1];
newNode = { ...newNode, category };
} else {
newNode = { ...newNode, type: type };
}
newTargetBranch.children[newNodeIndex] = newNode;
const newHierarchy = updateNode(
tableColumns,
newTargetBranch,
tableColumns.findIndex(n => n.name === newTargetBranch.name),
);
handleDataModelHierarchyChange(newHierarchy);
}
}
}
};
const handleDataModelHierarchyChange = hierarchy => {
setHierarchy(hierarchy);
dispatch(
actions.changeCurrentEditingView({
model: {
...currentEditingView?.model,
hierarchy,
version: APP_CURRENT_VERSION,
},
}),
);
};
const handleDragEnd = result => {
if (Boolean(result.destination) && isEmpty(result?.combine)) {
const newHierarchy = reorderNode(
CloneValueDeep(tableColumns),
{ name: result.draggableId },
{
name: result.destination.droppableId,
index: result.destination.index,
},
);
return handleDataModelHierarchyChange(newHierarchy);
}
if (!Boolean(result.destination) && !isEmpty(result?.combine)) {
const clonedTableColumns = CloneValueDeep(tableColumns);
const sourceNode = clonedTableColumns?.find(
c => c.name === result.draggableId,
);
const targetNode = clonedTableColumns?.find(
c => c.name === result.combine.draggableId,
);
if (
sourceNode &&
sourceNode.role !== ColumnRole.Hierarchy &&
targetNode &&
targetNode.role !== ColumnRole.Hierarchy &&
ALLOW_COMBINE_COLUMN_TYPES.includes(sourceNode.type) &&
ALLOW_COMBINE_COLUMN_TYPES.includes(targetNode.type)
) {
return openCreateHierarchyModal(sourceNode, targetNode);
} else if (
sourceNode &&
sourceNode.role !== ColumnRole.Hierarchy &&
targetNode &&
targetNode.role === ColumnRole.Hierarchy &&
ALLOW_COMBINE_COLUMN_TYPES.includes(sourceNode.type)
) {
const newHierarchy = reorderNode(
clonedTableColumns,
{ name: result.draggableId },
{
name: result.combine.draggableId,
index: -1,
},
);
return handleDataModelHierarchyChange(newHierarchy);
}
}
};
const openCreateHierarchyModal = (...nodes: Column[]) => {
return (openStateModal as Function)({
title: t('model.newHierarchy'),
modalSize: StateModalSize.XSMALL,
onOk: hierarchyName => {
if (!hierarchyName) {
return;
}
const hierarchyNode: Column = {
name: hierarchyName,
type: DataViewFieldType.STRING,
role: ColumnRole.Hierarchy,
children: nodes,
};
const newHierarchy = insertNode(tableColumns, hierarchyNode, nodes);
handleDataModelHierarchyChange(newHierarchy);
},
content: onChangeEvent => {
const allNodeNames = tableColumns?.flatMap(c => {
if (!isEmptyArray(c.children)) {
return [c.name].concat(c.children?.map(cc => cc.name) || []);
}
return c.name;
});
return (
<StyledFormItem
label={t('model.hierarchyName')}
name="hierarchyName"
rules={[
{ required: true },
({ getFieldValue }) => ({
validator(_, value) {
if (!allNodeNames.includes(getFieldValue('hierarchyName'))) {
return Promise.resolve(value);
}
return Promise.reject(new Error('名称重复,请检查!'));
},
}),
]}
>
<Input onChange={e => onChangeEvent(e.target?.value)} />
</StyledFormItem>
);
},
});
};
const openMoveToHierarchyModal = (node: Column) => {
const currentHierarchies = tableColumns?.filter(
c =>
c.role === ColumnRole.Hierarchy &&
!c?.children?.find(cn => cn.name === node.name),
);
return (openStateModal as Function)({
title: t('model.addToHierarchy'),
modalSize: StateModalSize.XSMALL,
onOk: hierarchyName => {
if (currentHierarchies?.find(h => h.name === hierarchyName)) {
let newHierarchy = moveNode(
tableColumns,
node,
currentHierarchies,
hierarchyName,
);
handleDataModelHierarchyChange(newHierarchy);
}
},
content: onChangeEvent => {
return (
<StyledFormItem
label={t('model.hierarchyName')}
name="hierarchyName"
rules={[{ required: true }]}
>
<Select defaultActiveFirstOption onChange={onChangeEvent}>
{currentHierarchies?.map(n => (
<Select.Option value={n.name}>{n.name}</Select.Option>
))}
</Select>
</StyledFormItem>
);
},
});
};
const openEditBranchModal = (node: Column) => {
const allNodeNames = tableColumns
?.flatMap(c => {
if (!isEmptyArray(c.children)) {
return c.children?.map(cc => cc.name);
}
return c.name;
})
.filter(n => n !== node.name);
return (openStateModal as Function)({
title: t('model.rename'),
modalSize: StateModalSize.XSMALL,
onOk: newName => {
if (!newName) {
return;
}
const newHierarchy = updateNode(
tableColumns,
{ ...node, name: newName },
tableColumns.findIndex(n => n.name === node.name),
);
handleDataModelHierarchyChange(newHierarchy);
},
content: onChangeEvent => {
return (
<StyledFormItem
label={t('model.rename')}
initialValue={node?.name}
name="rename"
rules={[
{ required: true },
({ getFieldValue }) => ({
validator(_, value) {
if (!allNodeNames.includes(getFieldValue('rename'))) {
return Promise.resolve(value);
}
return Promise.reject(new Error('名称重复,请检查!'));
},
}),
]}
>
<Input
onChange={e => {
onChangeEvent(e.target?.value);
}}
/>
</StyledFormItem>
);
},
});
};
const reorderNode = (
columns: Column[],
source: { name: string },
target: { name: string; index: number },
) => {
let sourceItem: Column | undefined;
const removeIndex = columns.findIndex(c => c.name === source.name);
if (removeIndex > -1) {
sourceItem = columns.splice(removeIndex, 1)?.[0];
} else {
const branchNode = columns.filter(
c =>
c.role === ColumnRole.Hierarchy &&
c.children?.find(cc => cc.name === source.name),
)?.[0];
if (!branchNode) {
return toModel(columns);
}
const removeIndex = branchNode.children!.findIndex(
c => c.name === source.name,
);
if (removeIndex === -1) {
return toModel(columns);
}
sourceItem = branchNode.children?.splice(removeIndex, 1)?.[0];
}
if (!sourceItem) {
return toModel(columns);
}
if (target.name === ROOT_CONTAINER_ID) {
columns.splice(target.index, 0, sourceItem);
} else {
const branchNode = columns.filter(
c => c.role === ColumnRole.Hierarchy && c.name === target.name,
)?.[0];
if (!branchNode) {
return toModel(columns);
}
if (target.index === -1) {
branchNode.children!.push(sourceItem);
} else {
branchNode.children!.splice(target.index, 0, sourceItem);
}
}
return toModel(columns);
};
const insertNode = (columns: Column[], newNode, nodes: Column[]) => {
const newColumns = columns.filter(
c => !nodes.map(n => n.name).includes(c.name),
);
newColumns.unshift(newNode);
return toModel(newColumns);
};
const updateNode = (columns: Column[], newNode, columnIndexes) => {
columns[columnIndexes] = newNode;
return toModel(columns);
};
const deleteBranch = (columns: Column[], node: Column) => {
const deletedBranchIndex = columns.findIndex(c => c.name === node.name);
if (deletedBranchIndex > -1) {
const branch = columns[deletedBranchIndex];
const children = branch?.children || [];
columns.splice(deletedBranchIndex, 1);
return toModel(columns, ...children);
}
};
const deleteFromBranch = (
columns: Column[],
parent: Column,
node: Column,
) => {
const branchNode = columns.find(c => c.name === parent.name);
if (branchNode) {
branchNode.children = branchNode.children?.filter(
c => c.name !== node.name,
);
return toModel(columns, node);
}
};
const moveNode = (
columns: Column[],
node: Column,
currentHierarchies: Column[],
hierarchyName,
) => {
const nodeIndex = columns?.findIndex(c => c.name === node.name);
if (nodeIndex !== undefined && nodeIndex > -1) {
columns.splice(nodeIndex, 1);
} else {
const branch = columns?.find(c =>
c.children?.find(cc => cc.name === node.name),
);
if (branch) {
branch.children =
branch.children?.filter(bc => bc.name !== node.name) || [];
}
}
const targetHierarchy = currentHierarchies?.find(
h => h.name === hierarchyName,
);
const clonedHierarchy = CloneValueDeep(targetHierarchy!);
clonedHierarchy.children = (clonedHierarchy.children || []).concat([node]);
return updateNode(
columns,
clonedHierarchy,
columns.findIndex(c => c.name === clonedHierarchy.name),
);
};
return (
<Container title="model" loading={stage === ViewViewModelStages.Running}>
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable
droppableId={ROOT_CONTAINER_ID}
type={TreeNodeHierarchy.Root}
isCombineEnabled={true}
>
{(droppableProvided, droppableSnapshot) => (
<StyledDroppableContainer
ref={droppableProvided.innerRef}
isDraggingOver={droppableSnapshot.isDraggingOver}
>
{tableColumns.map(col => {
return col.role === ColumnRole.Hierarchy ? (
<DataModelBranch
node={col}
key={col.name}
onNodeTypeChange={handleNodeTypeChange}
onMoveToHierarchy={openMoveToHierarchyModal}
onEditBranch={openEditBranchModal}
onDelete={handleDeleteBranch}
onDeleteFromHierarchy={handleDeleteFromBranch}
/>
) : (
<DataModelNode
node={col}
key={col.name}
onCreateHierarchy={openCreateHierarchyModal}
onNodeTypeChange={handleNodeTypeChange}
onMoveToHierarchy={openMoveToHierarchyModal}
/>
);
})}
{droppableProvided.placeholder}
</StyledDroppableContainer>
)}
</Droppable>
</DragDropContext>
{contextHolder}
</Container>
);
})
Example #23
Source File: index.tsx From nextjs-hasura-fullstack with MIT License | 4 votes |
BoardPage: React.FC = () => {
const router = useRouter()
const { setLastPosition } = useLastPositionNumber()
const { data, loading } = useBoardQuery({
variables: {
id: Number(router.query.boardId),
},
skip: !router.query.boardId,
})
const [updateList] = useUpdateListMutation()
const [updateCard] = useUpdateCardMutation()
const [moveCard] = useMoveCardMutation()
const renderLists = React.useMemo(() => {
return data?.boards_by_pk?.lists || []
}, [data])
React.useEffect(() => {
setLastPosition(renderLists[renderLists.length - 1]?.position || 1)
}, [renderLists])
const onDragEnd = async (result: DropResult, provided: ResponderProvided) => {
const { source, destination, type } = result
// dropped outside the list
if (!destination) {
return
}
if (
source.droppableId === destination.droppableId &&
source.index === destination.index
) {
return
}
// DragDrop a "List"
if (type === 'LIST') {
const updatedPosition = getUpdatePositionReorder(
renderLists,
source.index,
destination.index,
)
const sourceList = renderLists[source.index]
updateList({
variables: {
id: sourceList.id,
name: sourceList.name,
position: updatedPosition,
},
update: (cache, { data }) => {
if (data?.update_lists_by_pk) {
const cacheId = cache.identify({
__typename: 'boards',
id: sourceList.board_id,
})
if (cacheId) {
cache.modify({
id: cacheId,
fields: {
lists(existingListRefs = []) {
return reorder(
existingListRefs,
source.index,
destination.index,
)
},
},
})
}
}
},
optimisticResponse: (variables) => {
return {
__typename: 'mutation_root',
update_lists_by_pk: {
...sourceList,
position: updatedPosition,
},
}
},
})
}
if (type === 'CARD') {
/* same list: reorder card */
if (source.droppableId === destination.droppableId) {
const listCards = renderLists.find(
(item) => item.id === Number(source.droppableId),
)?.cards
if (!listCards) {
return
}
const updatedPosition = getUpdatePositionReorder(
listCards,
source.index,
destination.index,
)
const sourceCard = listCards[source.index]
await updateCard({
variables: {
id: sourceCard.id,
title: sourceCard.title,
position: updatedPosition,
},
update: (cache, { data }) => {
if (data?.update_cards_by_pk) {
const cacheId = cache.identify({
__typename: 'lists',
id: sourceCard.list_id,
})
if (cacheId) {
cache.modify({
id: cacheId,
fields: {
cards(existingCardRefs = []) {
return reorder(
existingCardRefs,
source.index,
destination.index,
)
},
},
})
}
}
},
optimisticResponse: (variables) => {
return {
__typename: 'mutation_root',
update_cards_by_pk: {
...sourceCard,
position: updatedPosition,
},
}
},
})
} else {
/**
* Diferent list: move card
*/
const sourceListCards = renderLists.find(
(item) => item.id === Number(source.droppableId),
)?.cards
const destinationListCards = renderLists.find(
(item) => item.id === Number(destination.droppableId),
)?.cards
if (!sourceListCards || !destinationListCards) {
return
}
const updatedPosition = getUpdatePositionMove(
sourceListCards,
destinationListCards,
source.index,
destination.index,
)
const sourceCard = sourceListCards[source.index]
await moveCard({
variables: {
id: sourceCard.id,
list_id: Number(destination.droppableId),
title: sourceCard.title,
position: updatedPosition,
},
update: (cache, { data }) => {
if (data?.update_cards_by_pk) {
const cardCacheId = cache.identify(data.update_cards_by_pk)
if (!cardCacheId) {
return
}
const cacheId = cache.identify({
__typename: 'lists',
id: source.droppableId,
})
if (cacheId) {
cache.modify({
id: cacheId,
fields: {
cards(existingRefs = []) {
const next = existingRefs.filter(
(listRef: { __ref: string }) =>
listRef.__ref !== cardCacheId,
)
return next
},
},
})
}
const cacheIdDestination = cache.identify({
__typename: 'lists',
id: destination.droppableId,
})
if (cacheIdDestination) {
cache.modify({
id: cacheIdDestination,
fields: {
cards(existingCardRefs = [], { toReference }) {
const moveRef = toReference(cardCacheId)
if (moveRef) {
const next = produce(
existingCardRefs as any[],
(draftState) => {
draftState.splice(destination.index, 0, moveRef)
},
)
return next
}
},
},
})
}
}
},
optimisticResponse: () => ({
__typename: 'mutation_root',
update_cards_by_pk: {
...sourceCard,
position: updatedPosition,
},
}),
})
}
}
}
let listRender
if (loading) {
listRender = (
<div className={`flex flex-no-wrap min-w-max-content`}>
{[...Array(3)].map((item, idx) => (
<div
key={idx}
className="flex flex-col max-w-sm p-4 mx-4 space-y-3 border border-gray-300 rounded-md shadow w-72"
>
<div className="flex space-x-4 animate-pulse">
<div className="flex-1 py-1 space-y-4">
<div className="w-3/4 h-4 bg-gray-400 rounded"></div>
{/* <div className="space-y-2">
<div className="h-4 bg-gray-400 rounded"></div>
<div className="w-5/6 h-4 bg-gray-400 rounded"></div>
</div> */}
</div>
</div>
{[...Array(4)].map((item, index) => (
<div
key={index}
className="flex flex-col max-w-sm p-4 border border-gray-300 rounded-md shadow"
>
<div className="flex space-x-4 animate-pulse">
<div className="flex-1 py-1 space-y-2">
<div className="h-4 bg-gray-400 rounded"></div>
<div className="w-5/6 h-4 bg-gray-400 rounded"></div>
</div>
</div>
</div>
))}
<div className="flex flex-col max-w-sm border-t border-gray-300 rounded-md shadow">
<div className="flex space-x-4 animate-pulse">
<div className="flex-1 py-1 space-y-2">
<div className="h-4 bg-gray-400 rounded"></div>
</div>
</div>
</div>
</div>
))}
{/* */}
</div>
)
} else {
listRender = (
<div className={`flex flex-no-wrap h-screen w-max-content`}>
<DragDropContext
onDragStart={(source) => {
console.log(`?? [LOG]: source`, source)
}}
// onDragUpdate={onDragUpdate}
onDragEnd={onDragEnd}
>
{/* List */}
<Droppable
droppableId="board"
type="LIST"
direction="horizontal"
// ignoreContainerClipping={true}
>
{(
{ innerRef, droppableProps, placeholder },
{ isDraggingOver },
) => {
return (
<div
ref={innerRef}
{...droppableProps}
className={`inline-flex ${isDraggingOver && ``}`}
style={{
overflowAnchor: 'none',
}}
>
{renderLists.map((list, index) => (
<ListBoard list={list} index={index} key={index} />
))}
{placeholder}
</div>
)
}}
</Droppable>
</DragDropContext>
<div className={``}>
<NewList lastId={renderLists[renderLists.length - 1]?.id || 0} />
</div>
</div>
)
}
return (
<div className={`h-screen space-x-2 -mt-14`}>
<Scrollbar noScrollY className={``}>
<div className={`pt-14`}>
<div className={`mt-4 mb-8`}>
<h2
className={`text-3xl font-semibold text-center text-cool-gray-100`}
>
{data?.boards_by_pk?.icon} {data?.boards_by_pk?.name}
</h2>
</div>
{listRender}
</div>
</Scrollbar>
</div>
)
}
Example #24
Source File: MediaMenu.tsx From sync-party with GNU General Public License v3.0 | 4 votes |
export default function MediaMenu({
socket,
setPlayerFocused,
emitPlayWish,
freezeUiVisible,
isPlaying,
playerState
}: Props): JSX.Element {
const party: ClientParty | null = useSelector(
(state: RootAppState) => state.globalState.party
);
const playingItem: MediaItem | null = useSelector(
(state: RootAppState) => state.globalState.playingItem
);
const uiVisible = useSelector(
(state: RootAppState) => state.globalState.uiVisible
);
const uiFocused = useSelector(
(state: RootAppState) => state.globalState.uiFocused
);
const [hoverTimestamp, setHoverTimestamp] = useState(0);
const hoverTimestampRef = useRef(hoverTimestamp);
hoverTimestampRef.current = hoverTimestamp;
const [addMediaIsActive, setAddMediaIsActive] = useState(false);
const [partyItemsSet, setPartyItemsSet] = useState<Set<string>>(new Set());
const partyItemListRef = useRef<HTMLDivElement | null>(null);
const dispatch = useDispatch();
const { t } = useTranslation();
// Collect items in active party in a set for faster checks
useEffect(() => {
if (party) {
const itemsSet = new Set<string>();
party.items.forEach((item) => {
itemsSet.add(item.id);
});
setPartyItemsSet(itemsSet);
}
}, [party]);
const handleRemoveItemFromParty = async (
item: MediaItem
): Promise<void> => {
if (socket && party) {
if (
party.items.length === 1 &&
playerState.playingItem &&
playerState.playingItem.id === item.id
) {
dispatch(
setGlobalState({
errorMessage: t(`errors.removeLastItemError`)
})
);
return Promise.reject();
}
try {
const response = await Axios.delete('/api/partyItems', {
data: { itemId: item.id, partyId: party.id },
...axiosConfig()
});
if (response.data.success) {
const currentIndex = playerState.playlistIndex;
if (
playerState.playingItem &&
playerState.playingItem.id === item.id
) {
emitPlayWish(
party.items[
currentIndex < party.items.length - 1
? currentIndex + 1
: currentIndex - 1
],
playerState.isPlaying,
null,
false,
0
);
}
socket.emit('partyUpdate', {
partyId: party.id
});
setPlayerFocused(true);
} else {
dispatch(
setGlobalState({
errorMessage: t(
`apiResponseMessages.${response.data.msg}`
)
})
);
}
} catch (error) {
dispatch(
setGlobalState({
errorMessage: t(`errors.removeItemError`)
})
);
}
}
};
const handleItemEditSave = async (item: MediaItem): Promise<void> => {
if (socket && party) {
try {
const response = await Axios.put(
'/api/mediaItem/' + item.id,
item,
axiosConfig()
);
if (response.data.success) {
await getUpdatedUserItems(dispatch, t);
socket.emit('partyUpdate', { partyId: party.id });
setPlayerFocused(true);
} else {
dispatch(
setGlobalState({
errorMessage: t(
`apiResponseMessages.${response.data.msg}`
)
})
);
}
} catch (error) {
dispatch(
setGlobalState({
errorMessage: t(`errors.itemSaveError`)
})
);
}
}
};
const handleItemClick = (chosenItem: MediaItem): void => {
if (party) {
const newSource = party.items.filter((mediaItem: MediaItem) => {
return mediaItem.id === chosenItem.id;
})[0];
emitPlayWish(
newSource,
true,
playerState.playingItem ? playerState.playingItem.id : null,
true,
0
);
}
};
const onDragEnd = async (result: DropResult): Promise<void> => {
if (socket && party) {
// Dropped outside the list
if (!result.destination) {
return;
}
const orderedItems = reorderItems(
party.items,
result.source.index,
result.destination.index
);
try {
const response = await Axios.put(
'/api/partyItems',
{ orderedItems: orderedItems, partyId: party.id },
axiosConfig()
);
if (response.data.success) {
socket.emit('partyUpdate', { partyId: party.id });
} else {
dispatch(
setGlobalState({
errorMessage: t(
`apiResponseMessages.${response.data.msg}`
)
})
);
}
} catch (error) {
dispatch(
setGlobalState({
errorMessage: t(`errors.reorderError`)
})
);
}
}
};
return (
<div
className={
'mediaMenu fixed top-0 right-0 flex flex-col mt-16 p-2 border border-gray-500 rounded m-2 shadow-md backgroundShade z-50' +
(uiVisible || !playingItem || !playingItem.url ? '' : ' hidden')
}
onMouseOver={(): void => {
const now = Date.now();
if (hoverTimestampRef.current + 10000 < now) {
freezeUiVisible(true);
setHoverTimestamp(now);
}
}}
onMouseLeave={(): void => {
if (!uiFocused.chat && !addMediaIsActive) {
freezeUiVisible(false);
setHoverTimestamp(0);
}
}}
>
{party && party.items.length ? (
<div className="partyItemList" ref={partyItemListRef}>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided: DroppableProvided): JSX.Element => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{party.items.map(
(
mediaItem: MediaItem,
index: number
) => {
const isCurrentlyPlayingItem =
playingItem &&
playingItem.id === mediaItem.id
? true
: false;
const alreadyPlayed =
party.metadata.played &&
party.metadata.played[
mediaItem.id
]
? true
: false;
return (
<MediaMenuDraggable
key={mediaItem.id}
mediaItemId={mediaItem.id}
index={index}
>
<ItemListed
item={mediaItem}
isPlaying={isPlaying}
isCurrentlyPlayingItem={
isCurrentlyPlayingItem
}
alreadyPlayed={
alreadyPlayed
}
nameEditingAllowed={
false
}
handleItemClick={(): void =>
handleItemClick(
mediaItem
)
}
onRemoveButtonClick={(
item: MediaItem
): void => {
handleRemoveItemFromParty(
item
);
}}
handleItemSave={(
item: MediaItem
): void => {
handleItemEditSave(
item
);
}}
setPlayerFocused={
setPlayerFocused
}
partyItemListRef={
partyItemListRef
}
></ItemListed>
</MediaMenuDraggable>
);
}
)}
</div>
)}
</Droppable>
</DragDropContext>
</div>
) : (
<div className="mb-2">There's nothing.</div>
)}
<AddMedia
isActive={addMediaIsActive}
partyItemsSet={partyItemsSet}
setAddMediaIsActive={setAddMediaIsActive}
handleItemEditSave={handleItemEditSave}
setPlayerFocused={(focused: boolean): void =>
setPlayerFocused(focused)
}
socket={socket}
></AddMedia>
</div>
);
}
Example #25
Source File: board.tsx From wikitrivia with MIT License | 4 votes |
export default function Board(props: Props) {
const { highscore, resetGame, state, setState, updateHighscore } = props;
const [isDragging, setIsDragging] = React.useState(false);
async function onDragStart() {
setIsDragging(true);
navigator.vibrate(20);
}
async function onDragEnd(result: DropResult) {
setIsDragging(false);
const { source, destination } = result;
if (
!destination ||
state.next === null ||
(source.droppableId === "next" && destination.droppableId === "next")
) {
return;
}
const item = { ...state.next };
if (source.droppableId === "next" && destination.droppableId === "played") {
const newDeck = [...state.deck];
const newPlayed = [...state.played];
const { correct, delta } = checkCorrect(
newPlayed,
item,
destination.index
);
newPlayed.splice(destination.index, 0, {
...state.next,
played: { correct },
});
const newNext = state.nextButOne;
const newNextButOne = getRandomItem(
newDeck,
newNext ? [...newPlayed, newNext] : newPlayed
);
const newImageCache = [preloadImage(newNextButOne.image)];
setState({
...state,
deck: newDeck,
imageCache: newImageCache,
next: newNext,
nextButOne: newNextButOne,
played: newPlayed,
lives: correct ? state.lives : state.lives - 1,
badlyPlaced: correct
? null
: {
index: destination.index,
rendered: false,
delta,
},
});
} else if (
source.droppableId === "played" &&
destination.droppableId === "played"
) {
const newPlayed = [...state.played];
const [item] = newPlayed.splice(source.index, 1);
newPlayed.splice(destination.index, 0, item);
setState({
...state,
played: newPlayed,
badlyPlaced: null,
});
}
}
// Ensure that newly placed items are rendered as draggables before trying to
// move them to the right place if needed.
React.useLayoutEffect(() => {
if (
state.badlyPlaced &&
state.badlyPlaced.index !== null &&
!state.badlyPlaced.rendered
) {
setState({
...state,
badlyPlaced: { ...state.badlyPlaced, rendered: true },
});
}
}, [setState, state]);
const score = React.useMemo(() => {
return state.played.filter((item) => item.played.correct).length - 1;
}, [state.played]);
React.useLayoutEffect(() => {
if (score > highscore) {
updateHighscore(score);
}
}, [score, highscore, updateHighscore]);
return (
<DragDropContext
onDragEnd={onDragEnd}
onDragStart={onDragStart}
sensors={[useAutoMoveSensor.bind(null, state)]}
>
<div className={styles.wrapper}>
<div className={styles.top}>
<Hearts lives={state.lives} />
{state.lives > 0 ? (
<>
<NextItemList next={state.next} />
</>
) : (
<GameOver
highscore={highscore}
resetGame={resetGame}
score={score}
/>
)}
</div>
<div id="bottom" className={styles.bottom}>
<PlayedItemList
badlyPlacedIndex={
state.badlyPlaced === null ? null : state.badlyPlaced.index
}
isDragging={isDragging}
items={state.played}
/>
</div>
</div>
</DragDropContext>
);
}
Example #26
Source File: StatDisplayList.tsx From slippi-stats with MIT License | 4 votes |
StatDisplayList: React.FC<StatDisplayListProps> = (props) => {
const { theme, stats, setStats } = props;
const [items, setItems] = React.useState<string[]>(stats.split(","));
React.useEffect(() => {
setItems(stats.split(","));
}, [stats]);
const updateStats = (statIds: string[]) => {
// First update the local state
setItems(statIds);
// Then update the URL state
setStats(statIds.join(","));
};
const onDragEnd = (result: any) => {
// dropped outside the list
if (!result.destination) {
return;
}
const newItems = reorder(items, result.source.index, result.destination.index);
updateStats(newItems);
};
const onRemove = (statId: string) => {
const newItems = items.filter((s) => s !== statId);
updateStats(newItems);
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(dropProvided, dropSnapshot) => (
<div
{...dropProvided.droppableProps}
ref={dropProvided.innerRef}
css={css`
margin: -1rem 0;
`}
>
{items.map((item, index) => {
const key = item ? item : "divider";
return (
<Draggable key={key} draggableId={key} index={index}>
{(dragProvided, dragSnapshot) => {
const additionalStyles = item ? null : dragProvided.dragHandleProps;
return (
<StatDisplayItem
ref={dragProvided.innerRef}
hasItem={Boolean(item)}
isDraggingOver={dropSnapshot.isDraggingOver}
{...dragProvided.draggableProps}
{...additionalStyles}
style={dragProvided.draggableProps.style}
>
{item ? (
<div
css={css`
position: relative;
`}
>
<Statistic statId={item} theme={theme} {...dragProvided.dragHandleProps} />
<div className="remove" onClick={() => onRemove(item)}>
✕
<span
css={css`
margin-left: 1rem;
`}
>
REMOVE
</span>
</div>
</div>
) : (
<Divider />
)}
</StatDisplayItem>
);
}}
</Draggable>
);
})}
{dropProvided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Example #27
Source File: designers.tsx From ali-react-table with MIT License | 4 votes |
export function PrimaryColumnTitle({ pivot }: { pivot: Pivot }) {
const [visible, setVisible] = useState(false)
const onClose = () => setVisible(false)
const [state, setState] = useState(() => toJS(pivot))
useEffect(() => {
if (visible) {
setState(toJS(pivot))
}
}, [visible])
const dimMap = new Map(pivot.allDimensions.map((dim) => [dim.code, dim]))
const onOk = () => {
pivot.changeDimCodes(state.dimCodes, pivot.dimValues)
onClose()
}
useEffect(() => {
if (visible) {
}
}, [visible])
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
<b>数据维度</b>
<div
style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', cursor: 'pointer', userSelect: 'none' }}
onClick={() => setVisible(true)}
>
<Settings16 style={{ fill: 'currentColor' }} />
<span style={{ marginLeft: 4 }}>设置</span>
<Dialog closeable={false} title="数据维度配置" visible={visible} onOk={onOk} onCancel={onClose}>
<IndicatorChooseDiv>
<IndicatorsPartDiv>
<div className="title">
选择维度({state.dimCodes.length}/{pivot.allDimensions.length})
</div>
<ul className="ind-list">
{pivot.allDimensions.map((dim) => (
<li key={dim.code}>
<Checkbox
className="ind-item clickable-ind-item"
checked={state.dimCodes.includes(dim.code)}
onChange={(checked) => {
setState((prev) =>
produce(prev, (draft) => {
if (checked) {
draft.dimCodes.push(dim.code)
} else {
draft.dimCodes.splice(draft.dimCodes.indexOf(dim.code), 1)
}
}),
)
}}
>
{dim.name}
</Checkbox>
</li>
))}
</ul>
</IndicatorsPartDiv>
<IndicatorsPartDiv style={{ borderLeft: '1px solid var(--border-color)' }}>
<div className="title">已选({state.dimCodes.length})</div>
<DragDropContext
onDragEnd={(result) => {
if (result.destination == null) {
return
}
setState((prev) =>
produce(prev, (draft) => {
const i = result.source.index
const j = result.destination.index
const [code] = draft.dimCodes.splice(i, 1)
draft.dimCodes.splice(j, 0, code)
}),
)
}}
>
<Droppable droppableId="selected-dimensions" direction="vertical">
{(dropProvided, snapshot) => (
<ul className="ind-list" ref={dropProvided.innerRef}>
{state.dimCodes.map((dimCode, index) => (
<Draggable key={dimCode} index={index} draggableId={dimCode}>
{(dragProvided, snapshot) => {
return (
<li ref={dragProvided.innerRef} {...dragProvided.draggableProps}>
<div className={cx('ind-item draggable-ind-item', { dragging: snapshot.isDragging })}>
{dimMap.get(dimCode).name}
<div {...dragProvided.dragHandleProps} style={{ marginLeft: 'auto' }}>
<DragVertical16 style={{ fill: 'currentColor' }} />
</div>
</div>
</li>
)
}}
</Draggable>
))}
{dropProvided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
</IndicatorsPartDiv>
</IndicatorChooseDiv>
</Dialog>
</div>
</div>
)
}
Example #28
Source File: BuildOrder.tsx From sc2-planner with MIT License | 4 votes |
render(): JSX.Element {
// Convert build order items to div elements
// Hide element if no build order items are present
if (this.props.gamelogic.bo.length === 0) {
return <div />
}
const buildOrder = this.props.gamelogic.bo.map((item, index) => {
const image = getImageOfItem(item)
return <img key={`${item.name}_${index}`} src={image} alt={item.name} />
})
const getItemClass = (dragging: boolean, index: number) => {
// Build order is invalid after this index, mark background or border red
// Set color based on if it is dragging
const classes: string[] = []
if (dragging) {
if (index >= this.props.gamelogic.boIndex) {
classes.push(CLASSES.boItemInvalidDragging)
} else {
classes.push(CLASSES.boItemDragging)
}
} else if (index === this.props.hoverIndex) {
classes.push(CLASSES.boItemHighlighting)
} else {
if (index >= this.props.gamelogic.boIndex) {
classes.push(CLASSES.boItemInvalid)
}
classes.push(CLASSES.boItem)
}
if (includes(this.props.highlightedIndexes, index)) {
classes.push(CLASSES.boItemDim)
}
return classes.join(" ")
}
const buildOrderItems: JSX.Element[] = []
let separatorClass =
this.props.insertIndex === 0 ? CLASSES.boItemSeparatorSelected : CLASSES.boItemSeparator
buildOrderItems.push(
<div
id={"separator_0"}
key={"separator0"}
className={separatorClass}
onClick={(_e) => {
this.props.changeInsertIndex(0)
}}
/>
)
this.props.gamelogic.bo.forEach((item, index) => {
buildOrderItems.push(
<Draggable key={`${index}`} draggableId={`${index}`} index={index}>
{(provided, snapshot) => (
<div
id={`bo_${item.name}_${index}`}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className={getItemClass(snapshot.isDragging, index)}
onMouseEnter={(_e) => this.onMouseEnter(index)}
onMouseLeave={(_e) => this.onMouseLeave()}
onClick={(e) => {
this.props.removeClick(e, index)
}}
>
{buildOrder[index]}
</div>
)}
</Draggable>
)
separatorClass =
this.props.insertIndex === index + 1
? CLASSES.boItemSeparatorSelected
: CLASSES.boItemSeparator
buildOrderItems.push(
<div
id={`separator_${index + 1}`}
key={`separator${index + 1}`}
className={separatorClass}
onClick={(_e) => {
this.props.changeInsertIndex(index + 1)
}}
/>
)
})
return (
<div id={"buildorder"} className={CLASSES.bo}>
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable" direction="horizontal">
{(provided, _snapshot) => (
<div
className={
(this.props.multilineBuildOrder ? "flex-wrap flex-row" : "") +
" flex"
}
ref={provided.innerRef}
{...provided.droppableProps}
>
{buildOrderItems}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
)
}
Example #29
Source File: ArrayContainer.tsx From firecms with MIT License | 4 votes |
/**
* @category Form custom fields
*/
export function ArrayContainer<T>({
name,
value,
disabled,
buildEntry,
onInternalIdAdded,
includeAddButton
}: ArrayContainerProps<T>) {
const hasValue = value && Array.isArray(value) && value.length > 0;
const internalIdsMap: Record<string, number> = useMemo(() =>
hasValue
? value.map((v, index) => {
if (!v) return {};
return ({
[getHashValue(v) + index]: getRandomId()
});
}).reduce((a, b) => ({ ...a, ...b }), {})
: {},
[value, hasValue]);
const internalIdsRef = useRef<Record<string, number>>(internalIdsMap);
const [internalIds, setInternalIds] = useState<number[]>(
hasValue
? Object.values(internalIdsRef.current)
: []);
useEffect(() => {
if (hasValue && value && value.length !== internalIds.length) {
const newInternalIds = value.map((v, index) => {
const hashValue = getHashValue(v) + index;
if (hashValue in internalIdsRef.current) {
return internalIdsRef.current[hashValue];
} else {
const newInternalId = getRandomId();
internalIdsRef.current[hashValue] = newInternalId;
return newInternalId;
}
});
setInternalIds(newInternalIds);
}
}, [hasValue, internalIds.length, value]);
return <FieldArray
name={name}
validateOnChange={true}
render={arrayHelpers => {
const insertInEnd = () => {
if (disabled) return;
const id = getRandomId();
const newIds: number[] = [...internalIds, id];
if (onInternalIdAdded)
onInternalIdAdded(id);
setInternalIds(newIds);
arrayHelpers.push(null);
};
const remove = (index: number) => {
const newValue = [...internalIds];
newValue.splice(index, 1);
setInternalIds(newValue);
arrayHelpers.remove(index);
};
const onDragEnd = (result: any) => {
// dropped outside the list
if (!result.destination) {
return;
}
const sourceIndex = result.source.index;
const destinationIndex = result.destination.index;
const newIds = [...internalIds];
const temp = newIds[sourceIndex];
newIds[sourceIndex] = newIds[destinationIndex];
newIds[destinationIndex] = temp;
setInternalIds(newIds);
arrayHelpers.move(sourceIndex, destinationIndex);
}
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId={`droppable_${name}`}>
{(droppableProvided, droppableSnapshot) => (
<div
{...droppableProvided.droppableProps}
ref={droppableProvided.innerRef}>
{hasValue && internalIds.map((internalId: number, index: number) => {
return (
<Draggable
key={`array_field_${name}_${internalId}}`}
draggableId={`array_field_${name}_${internalId}}`}
isDragDisabled={disabled}
index={index}>
{(provided, snapshot) => (
<Box
ref={provided.innerRef}
{...provided.draggableProps}
style={
provided.draggableProps.style
}
sx={{
marginBottom: 1,
borderRadius: "4px",
opacity: 1
}}
>
<Box key={`field_${index}`}
display="flex">
<Box flexGrow={1}
width={"100%"}
key={`field_${name}_entryValue`}>
{buildEntry(index, internalId)}
</Box>
<Box width={"36px"}
display="flex"
flexDirection="column"
alignItems="center">
<div
{...provided.dragHandleProps}>
<DragHandleIcon
fontSize={"small"}
color={disabled ? "disabled" : "inherit"}
sx={{ cursor: disabled ? "inherit" : "move" }}/>
</div>
{!disabled &&
<IconButton
size="small"
aria-label="remove"
onClick={() => remove(index)}>
<ClearIcon
fontSize={"small"}/>
</IconButton>}
</Box>
</Box>
</Box>
)}
</Draggable>);
})}
{droppableProvided.placeholder}
{includeAddButton && !disabled && <Box p={1}
justifyContent="center"
textAlign={"left"}>
<Button variant="outlined"
color="primary"
disabled={disabled}
onClick={insertInEnd}>
Add
</Button>
</Box>}
</div>
)}
</Droppable>
</DragDropContext>
);
}}
/>;
}