react-beautiful-dnd#Draggable TypeScript Examples

The following examples show how to use react-beautiful-dnd#Draggable. 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: StatOptionItem.tsx    From slippi-stats with MIT License 6 votes vote down vote up
StatOptionItem: React.FC<{
  checked: boolean;
  index: number;
  id: string;
  onChange: (checked: boolean) => void;
}> = (props) => {
  const stat = STAT_DEFINITIONS.get(props.id);
  if (!stat) {
    return null;
  }
  return (
    <Draggable draggableId={props.id} index={props.index}>
      {(provided, snapshot) => (
        <div ref={provided.innerRef}>
          <Option
            {...provided.draggableProps}
            isDragging={snapshot.isDragging}
            checked={props.checked}
            onChange={props.onChange}
            draggable={true}
            name={stat.name}
            handleProps={provided.dragHandleProps}
          />
        </div>
      )}
    </Draggable>
  );
}
Example #2
Source File: SelectedDrag.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function Selected ({ address, index, onDeselect }: Props): React.ReactElement<Props> {
  return (
    <Draggable
      draggableId={address}
      index={index}
      key={address}
    >
      {(provided: DraggableProvided, snapshot: DraggableStateSnapshot): React.ReactElement => {
        const element = (
          <div
            // eslint-disable-next-line @typescript-eslint/unbound-method
            ref={provided.innerRef}
            {...provided.draggableProps}
            {...provided.dragHandleProps}
          >
            <AddressToggle
              address={address}
              className={snapshot.isDragging ? 'isDragging' : ''}
              noToggle
              onChange={onDeselect}
            />
          </div>
        );

        return snapshot.isDragging
          ? ReactDOM.createPortal(element, portal)
          : element;
      }}
    </Draggable>
  );
}
Example #3
Source File: Tree.tsx    From react-beautiful-tree with Apache License 2.0 6 votes vote down vote up
renderItem = (flatItem: FlattenedItem, index: number): ReactNode => {
    const { isDragEnabled } = this.props

    const isDragDisabled =
      typeof isDragEnabled === 'function'
        ? !isDragEnabled(flatItem.item)
        : !isDragEnabled

    return (
      <Draggable
        key={flatItem.item.id}
        draggableId={flatItem.item.id.toString()}
        index={index}
        isDragDisabled={isDragDisabled}
      >
        {this.renderDraggableItem(flatItem)}
      </Draggable>
    )
  }
Example #4
Source File: AddButtonMappingDialog.tsx    From Pi-Tool with GNU General Public License v3.0 6 votes vote down vote up
ButtonActionCreator: React.FC<{ buttonDuration: ButtonPress }> = ({ buttonDuration }) => {
    const draggableId = uuidv4();

    return (
        <Droppable droppableId="PALETTE" direction="horizontal" isDropDisabled={true}>
            {(provided, _snapshot) => (
                <div ref={provided.innerRef} {...provided.droppableProps}>
                    <Box width={1} style={{ display: 'inline-flex', verticalAlign: 'middle', alignItems: 'center' }}>
                        <Draggable key={draggableId} draggableId={draggableId} index={buttonDuration as number}>

                            {(provided, snapshot) => (
                                <Box style={{ width: 50, height: 50 }}>
                                    <div
                                        style={{ width: 50, height: 50 }}
                                        ref={provided.innerRef}
                                        {...provided.draggableProps}
                                        {...provided.dragHandleProps}>

                                        <ButtonPressIcon press={buttonDuration} isButton={true} />
                                    </div>
                                    {snapshot.isDragging &&
                                        <ButtonPressIcon press={buttonDuration} isButton={true} />
                                    }
                                </Box>
                            )}
                        </Draggable>


                        <Typography>
                            {buttonDuration === ButtonPress.Short ? "Short button press" : "Long button press"}
                        </Typography>
                    </Box>
                </div>
            )}
        </Droppable>
    );
}
Example #5
Source File: Tree.tsx    From react-beautiful-tree with Apache License 2.0 6 votes vote down vote up
renderVirtualRow = React.memo((props: VirtualRowProps) => {
    const { data: items, index, style, isDragging } = props
    const { isDragEnabled } = this.props
    const flatItem = items[index]
    const isDragDisabled =
      typeof isDragEnabled === 'function'
        ? !isDragEnabled(flatItem.item)
        : !isDragEnabled

    return (
      <Draggable
        draggableId={flatItem.item.id.toString()}
        index={index}
        isDragDisabled={isDragDisabled}
        key={flatItem.item.id}
      >
        {(provided, snapshot) =>
          this.renderVirtualItem({
            snapshot,
            provided,
            flatItem,
            style,
            isDragging,
          })
        }
      </Draggable>
    )
  }, areEqual)
Example #6
Source File: SelectedDrag.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
function Selected({ address, index, onDeselect }: Props): React.ReactElement<Props> {
  return (
    <Draggable draggableId={address} index={index} key={address}>
      {(provided: DraggableProvided, snapshot: DraggableStateSnapshot): React.ReactElement => {
        const element = (
          <div
            // eslint-disable-next-line @typescript-eslint/unbound-method
            ref={provided.innerRef}
            {...provided.draggableProps}
            {...provided.dragHandleProps}
          >
            <AddressToggle
              address={address}
              className={snapshot.isDragging ? 'isDragging' : ''}
              noToggle
              onChange={onDeselect}
            />
          </div>
        );

        return snapshot.isDragging ? ReactDOM.createPortal(element, portal) : element;
      }}
    </Draggable>
  );
}
Example #7
Source File: MediaMenuDraggable.tsx    From sync-party with GNU General Public License v3.0 6 votes vote down vote up
export default function MediaMenuDraggable({
    children,
    mediaItemId,
    index
}: Props): ReactElement {
    return (
        <Draggable draggableId={mediaItemId} index={index}>
            {(provided: DraggableProvided): JSX.Element => (
                <>
                    <div
                        ref={provided.innerRef}
                        {...provided.draggableProps}
                        {...provided.dragHandleProps}
                    >
                        {children}
                    </div>
                </>
            )}
        </Draggable>
    );
}
Example #8
Source File: Task.tsx    From knboard with MIT License 6 votes vote down vote up
Task = ({ task: task, style, index }: Props) => {
  const dispatch = useDispatch();

  const handleClick = () => {
    dispatch(setEditDialogOpen(task.id));
  };

  return (
    <Draggable key={task.id} draggableId={`task-${task.id}`} index={index}>
      {(
        dragProvided: DraggableProvided,
        dragSnapshot: DraggableStateSnapshot
      ) => (
        <Container
          isDragging={dragSnapshot.isDragging}
          isGroupedOver={Boolean(dragSnapshot.combineTargetFor)}
          ref={dragProvided.innerRef}
          {...dragProvided.draggableProps}
          {...dragProvided.dragHandleProps}
          style={getStyle(dragProvided, style)}
          data-is-dragging={dragSnapshot.isDragging}
          data-testid={`task-${task.id}`}
          data-index={index}
          aria-label={`task ${task.title}`}
          onClick={handleClick}
          css={taskContainerStyles}
        >
          <Content>
            <TextContent>{task.title}</TextContent>
            <TaskId>id: {task.id}</TaskId>
            <TaskLabels task={task} />
            <TaskFooter task={task} />
          </Content>
        </Container>
      )}
    </Draggable>
  );
}
Example #9
Source File: index.tsx    From nextjs-hasura-fullstack with MIT License 5 votes vote down vote up
CardTask: React.FC<CardTaskProps> = (props) => {
  const { card, index } = props

  const [deleteCard, { loading }] = useDeleteCardMutation()

  const handleDeleteCard = React.useCallback(async () => {
    try {
      await deleteCard({
        variables: { id: card.id },
        update: (cache, { data }) => {
          if (data?.delete_cards_by_pk) {
            const cacheId = cache.identify(data?.delete_cards_by_pk)
            if (cacheId) {
              cache.modify({
                id: cache.identify({ __typename: 'lists', id: card.list_id }),
                fields: {
                  cards(existingCardRefs = [], { readField, toReference }) {
                    return existingCardRefs.filter(
                      //@ts-ignore
                      (ref) => ref.__ref !== cacheId,
                    )
                  },
                },
              })
              cache.evict({ id: cacheId })
            }
          }
        },
        optimisticResponse: {
          __typename: 'mutation_root',
          delete_cards_by_pk: {
            ...card,
          },
        },
      })
    } catch (err) {
      console.log(`?? [LOG]: removeAction -> err`, err)
    }
  }, [])

  return (
    <Draggable key={card.id} draggableId={`${card.id}`} index={index}>
      {(
        { draggableProps, dragHandleProps, innerRef },
        { isDragging, isDropAnimating },
      ) => (
        <Card
          ref={innerRef}
          {...draggableProps}
          className={`CardTask group ${
            isDragging && `border-purple-600`
          } px-2 py-3 mb-4`}
        >
          <CardBody {...dragHandleProps}>
            <div className={`flex items-center justify-between h-16`}>
              <div>{card.title}</div>
              <div className={`hidden group-hover:block`}>
                <ButtonIcon
                  size="sm"
                  color="danger"
                  variant="light"
                  icon={<XSolid />}
                  onClick={(e) => {
                    e.preventDefault()
                    handleDeleteCard()
                  }}
                />
              </div>
            </div>
            <div>
              <p>{card.description}</p>
            </div>
          </CardBody>
        </Card>
      )}
    </Draggable>
  )
}
Example #10
Source File: Column.tsx    From gant-design with MIT License 5 votes vote down vote up
Column = (props) => {
    const {
        column,
        tasks,
        index,
        hideQuickAdd,
        isColumnDragDisabled,
        isTaskDropDisabled,
        ...nextProps
    } = props
    const { prefixCls, renderHeader, renderExtra, handleAddBtn, idKey, titleKey } = useContext(TaskBoardContext)

    return <Draggable
        index={index}
        draggableId={column[idKey]}
        isDragDisabled={isColumnDragDisabled}
    >
        {(provided, snapshot) => (
            <div
                className={prefixCls + '-column-wrapper'}
                ref={provided.innerRef}
                {...provided.draggableProps}
            >
                <div
                    className={prefixCls + '-column-container'}
                    style={{
                        boxShadow: `${snapshot.isDragging ? 'rgba(0, 0, 0, 0.2) 2px 2px 1px' : ''}`,
                    }}
                >
                    <div {...provided.dragHandleProps}>
                        {renderHeader === null ? null :
                            renderHeader ? renderHeader(column) :
                                <div className={prefixCls + '-column-header-wrapper'}>
                                    <Tooltip title={column[titleKey]} mouseEnterDelay={0.3} placement="topLeft">
                                        <div className={prefixCls + '-column-header-title'}>
                                            {column[titleKey]}{tasks && tasks.length > 0 ? `(${tasks.length})` : null}
                                        </div>
                                    </Tooltip>
                                    <div className={prefixCls + '-column-header-extra'}>
                                        {renderExtra && renderExtra(column)}
                                    </div>
                                </div>
                        }
                    </div>
                    <Droppable
                        droppableId={column[idKey]}
                        type='task'
                        isDropDisabled={isTaskDropDisabled}
                    >
                        {(provided, snapshot) => (
                            <div
                                className={prefixCls + '-task-drop-inner'}
                                ref={provided.innerRef}
                                {...provided.droppableProps}
                            >
                                <TaskList tasks={tasks} column={column} {...nextProps} />
                                {provided.placeholder}
                                {!hideQuickAdd && <div
                                    className={prefixCls + '-quick-add'}
                                    onClick={(e) => {
                                        e.stopPropagation()
                                        handleAddBtn && handleAddBtn(column)
                                    }}>
                                    <Icon type="plus" />
                                </div>}
                            </div>
                        )}
                    </Droppable>
                </div>
            </div>
        )}
    </Draggable>
}
Example #11
Source File: CourseHitItem.tsx    From peterportal-client with MIT License 5 votes vote down vote up
CourseHitItem: FC<CourseHitItemProps> = (props: CourseHitItemProps) => {
  const dispatch = useAppDispatch();
  // do not make course draggable on mobile
  if (isMobile) {
    return <div onMouseDown={() => {
      dispatch(setActiveCourse(props));
      dispatch(setShowAddCourse(true));
      // also hide the search bar to view the roadmap
      dispatch(setShowSearch(false));
    }}
      // use inline style here so dnd can calculate size
      style={{ margin: ' 0rem 2rem 1rem 2rem' }}>
      <Course {...props} />
    </div>
  }
  // course is draggable on desktop
  else {
    return <Draggable
      key={`search-course-${props.index}`}
      draggableId={`search-${props.id}-${props.index}`}
      index={props.index}
    >
      {(provided, snapshot) => {
        return (
          <div
            ref={provided.innerRef}
            {...provided.draggableProps}
            {...provided.dragHandleProps}
            style={{
              // use inline style here so dnd can calculate size
              margin: ' 0rem 2rem 1rem 2rem',
              ...provided.draggableProps.style
            }}
            onMouseDown={() => { dispatch(setActiveCourse(props)) }}
          >
            <Course {...props} />
          </div>
        );
      }}
    </Draggable>
  }
}
Example #12
Source File: SidePanel.tsx    From TabMerger with GNU General Public License v3.0 5 votes vote down vote up
export default function SidePanel(): JSX.Element {
  const { available } = useSelector((state) => state.groups.present);
  const { inputValue, filterChoice } = useSelector((state) => state.header);
  const { dragType, isDragging } = useSelector((state) => state.dnd);

  const { filteredGroups, nonEmptyGroups } = useFilter();
  const groupSearch = inputValue !== "" && filterChoice === "group";
  const tabSearch = inputValue !== "" && filterChoice === "tab";
  const currentGroups = groupSearch ? filteredGroups : tabSearch ? nonEmptyGroups : available;
  const groupDrag = isGroupDrag(dragType);

  const groupsContainerRef = useRef<HTMLDivElement | null>(null);
  const containerHeight = useContainerHeight(groupsContainerRef);

  return (
    <Container>
      {currentGroups.map((group) => group.id).includes(available[0].id) && <Group {...available[0]} />}

      <div ref={groupsContainerRef}>
        <Droppable droppableId="sidePanel" isCombineEnabled={!groupDrag}>
          {(provider) => (
            <DraggableContainer
              ref={provider.innerRef}
              {...provider.droppableProps}
              $height={containerHeight}
              $dragging={isDragging && groupDrag}
            >
              {currentGroups
                .filter((group) => group.id !== available[0].id)
                .map((data, i) => (
                  <Draggable
                    key={data.id + i + 1}
                    draggableId={`group-${i + 1}`}
                    index={i + 1}
                    isDragDisabled={groupSearch}
                  >
                    {(provided, dragSnapshot) => (
                      <div ref={provided.innerRef} {...provided.draggableProps}>
                        <Group {...data} snapshot={dragSnapshot} dragHandleProps={provided.dragHandleProps} />
                      </div>
                    )}
                  </Draggable>
                ))}

              {provider.placeholder}
            </DraggableContainer>
          )}
        </Droppable>
      </div>
    </Container>
  );
}
Example #13
Source File: TabContainer.tsx    From yana with MIT License 5 votes vote down vote up
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 #14
Source File: Form.demo.dnd.tsx    From mantine with MIT License 5 votes vote down vote up
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 #15
Source File: Windows.tsx    From TabMerger with GNU General Public License v3.0 5 votes vote down vote up
export default function Windows(): JSX.Element {
  const { inputValue, filterChoice } = useSelector((state) => state.header);
  const { dragType } = useSelector((state) => state.dnd);

  const {
    active: { id: activeId, index: groupIndex },
    available
  } = useSelector((state) => state.groups.present);

  const typing = inputValue !== "";
  const { filteredTabs } = useFilter();

  const { windows, updatedAt } = available[groupIndex];

  const windowContainerRef = useRef<HTMLDivElement | null>(null);
  const containerHeight = useContainerHeight(windowContainerRef, updatedAt, filterChoice, filteredTabs);

  const isTabSearch = filterChoice === "tab";

  return (
    <div>
      <Information />

      {typing && isTabSearch && <SearchResult />}

      <div ref={windowContainerRef}>
        <Droppable droppableId={"group-" + groupIndex} isDropDisabled={!isWindowDrag(dragType) || groupIndex === 0}>
          {(provider, dropSnapshot) => (
            <WindowsContainer
              ref={provider.innerRef}
              {...provider.droppableProps}
              $draggedOver={dropSnapshot.isDraggingOver}
              $height={containerHeight}
            >
              {windows.map(
                (window, i) =>
                  (!typing || !isTabSearch || filteredTabs[activeId][i]?.length > 0) && (
                    <Draggable
                      key={i}
                      draggableId={`window-${i}-group-${groupIndex}`}
                      index={i}
                      isDragDisabled={typing && isTabSearch}
                    >
                      {(provided, dragSnapshot) => (
                        <div ref={provided.innerRef} {...provided.draggableProps}>
                          <Window
                            {...window}
                            windowIndex={i}
                            snapshot={dragSnapshot}
                            dragHandleProps={provided.dragHandleProps}
                          />
                        </div>
                      )}
                    </Draggable>
                  )
              )}

              {provider.placeholder}
            </WindowsContainer>
          )}
        </Droppable>
      </div>
    </div>
  );
}
Example #16
Source File: index.tsx    From nextjs-hasura-fullstack with MIT License 5 votes vote down vote up
ListBoard: React.FC<ListBoardProps> = ({ list, index }) => {
  return (
    <Draggable draggableId={`${list.id}`} index={index} key={list.id}>
      {({ innerRef, dragHandleProps, draggableProps }, { isDragging }) => {
        return (
          <div
            ref={innerRef}
            {...draggableProps}
            className={`flex flex-col mx-4`}
          >
            <Card
              className={`w-72 bg-white flex flex-col ${
                isDragging && `border-purple-600`
              }`}
            >
              <CardHeader
                action={<ActionDropdown list={list} />}
                {...dragHandleProps}
                className={`hover:bg-purple-50`}
              >
                <ListHeader list={list} />
              </CardHeader>
              <Droppable
                type="CARD"
                direction="vertical"
                key={list.id}
                droppableId={`${list.id}`}
              >
                {(
                  { innerRef, droppableProps, placeholder },
                  { isDraggingOver },
                ) => (
                  <CardBody
                    ref={innerRef}
                    {...droppableProps}
                    className={`${isDraggingOver && `bg-purple-400 border-2`}`}
                  >
                    {list.cards.map((card, index) => (
                      <CardTask key={card.id} card={card} index={index} />
                    ))}
                    {placeholder}
                  </CardBody>
                )}
              </Droppable>

              <CardFooter className={`h-14`}>
                <NewCard
                  lastCard={list.cards[list.cards.length - 1]}
                  list={list}
                />
              </CardFooter>
            </Card>
          </div>
        )
      }}
    </Draggable>
  )
}
Example #17
Source File: FlowList.tsx    From Protoman with MIT License 5 votes vote down vote up
FlowCell: React.FC<CellProps> = ({ flowName, emphasize, handleSelection, handleDelete, handleClone, idx }) => {
  const [menuVisible, setMenuVisible] = React.useState(false);
  function showMenu(): void {
    setMenuVisible(true);
  }

  function hideMenu(): void {
    setMenuVisible(false);
  }

  function deleteFlow(): void {
    handleDelete(flowName);
    hideMenu();
  }

  function cloneFlow(): void {
    handleClone(flowName);
    hideMenu();
  }

  const menu = (
    <>
      <Button type="link" danger onClick={prevent(deleteFlow)}>
        <DeleteOutlined />
        Delete Request
      </Button>
      <Separator />
      <Button type="link" onClick={prevent(cloneFlow)}>
        <SubnodeOutlined />
        Clone Request
      </Button>
    </>
  );

  return (
    <Popover
      placement="rightTop"
      content={menu}
      visible={menuVisible}
      trigger="contextMenu"
      onVisibleChange={setMenuVisible}
    >
      <ClickableItem onClick={(): void => handleSelection(flowName)} onContextMenu={prevent(showMenu)}>
        <Draggable draggableId={flowName} index={idx}>
          {(provided): React.ReactElement => {
            const style: React.CSSProperties = {
              width: '100%',
              height: '100%',
              padding: 8,
              boxSizing: 'border-box',
            };

            const { style: draggableStyle, ...draggableRest } = provided.draggableProps;

            return (
              <div
                ref={provided.innerRef}
                {...provided.dragHandleProps}
                {...draggableRest}
                style={{ ...style, ...draggableStyle }}
              >
                <Typography.Text
                  strong={emphasize}
                  style={{ userSelect: 'none', color: emphasize ? 'rgb(47, 93, 232)' : undefined }}
                >
                  {flowName}
                </Typography.Text>
              </div>
            );
          }}
        </Draggable>
      </ClickableItem>
    </Popover>
  );
}
Example #18
Source File: DraggableTags.tsx    From nebula-studio with Apache License 2.0 5 votes vote down vote up
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 #19
Source File: BoardItem.tsx    From projectboard with MIT License 5 votes vote down vote up
BoardItem = ({ task, index }: Props) => {
  let priorityIcon = (
    <span
      className='inline-block m-0.5 rounded-sm border border-gray-100 hover:border-gray-200 p-0.5'
    >
      <PriorityIcon priority={task.priority} />
    </span>
  );

  // const dispatch = useDispatch<AppDispatch>();
  const updatePriority = (priority: string) => {
    // dispatch(updatePriority(task, priority));
  };

  return (
    <Draggable draggableId={task._id || 'id'} index={index} key={task._id}>
      {(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => {
        let isDragging = snapshot.isDragging && !snapshot.isDropAnimating;
        return (
          <div
            ref={provided.innerRef}
            className={classNames(
              'cursor-default flex flex-col w-full px-4 py-3 mb-2 bg-white rounded focus:outline-none',
              {
                'shadow-modal': isDragging,
              }
            )}
            {...provided.draggableProps}
            {...provided.dragHandleProps}
          >
            <div className='flex justify-between w-full cursor-default'>
              <div className='flex flex-col'>
                <span className='text-xs font-normal text-gray-500 uppercase'>{task._id}</span>
                <span className='mt-1 text-sm font-medium text-gray-700 line-clamp-2 overflow-ellipsis'>{task.title}</span>
              </div>
              <div className='flex-shrink-0'>
                {task.assignee ?
                  <Avatar name={`${task?.assignee?.user?.firstName} ${task?.assignee?.user?.lastName}`} /> :
                  <Avatar />}
              </div>
            </div>
            <div className='mt-2.5 flex items-center'>
              <PriorityMenu
                button={priorityIcon}
                disabled
                // id={`r-priority-${task._id}`}
                filterKeyword={true}
                onSelect={(p: any) => updatePriority(p)}
              />
            </div>
          </div>
        );
      }}

    </Draggable >

  );
}
Example #20
Source File: DragndropPostList.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
DragndropPostListPostListInner = React.memo((props: {
  PostListProps: React.ComponentProps<typeof PostList>;
  dragSelectedTourAnchorProps?: React.ComponentProps<typeof TourAnchor>;
}) => {
  const theme = useTheme();
  const store = useStore();
  return (
    <PostList
      {...props.PostListProps}
      PanelPostProps={{
        margins: theme.spacing(DashboardListPostSpacing, DashboardListPostSpacing, DashboardListPostSpacing, DashboardListPostSpacing + 1),
        ...props.PostListProps.PanelPostProps,
        wrapPost: (post, content, index) => (
          <Draggable
            draggableId={post.ideaId}
            index={index}
          >
            {(providedDraggable, snapshotDraggable) => {
              var inner = content;
              if (props.dragSelectedTourAnchorProps) {
                if (!!props.PostListProps.selected && lastSelectedId !== props.PostListProps.selected) {
                  lastSelectedId = props.PostListProps.selected;
                }
                if (lastSelectedId === post.ideaId) {
                  inner = (
                    <Provider store={ServerAdmin.get().getStore()}>
                      <TourAnchor placement='top' {...props.dragSelectedTourAnchorProps} zIndex={zb => zb.modal - 1}>
                        <Provider store={store}>
                          {inner}
                        </Provider>
                      </TourAnchor>
                    </Provider>
                  );
                }
              }
              inner = (
                <DragndropPostListDraggableInner
                  providedDraggable={providedDraggable}
                  isDragging={snapshotDraggable.isDragging}
                  draggingOver={snapshotDraggable.draggingOver}
                  dropAnimation={snapshotDraggable.dropAnimation}
                  content={inner}
                />
              );
              return inner as any;
            }}
          </Draggable>
        ),
      }}
    />
  );
}, customReactMemoEquals({ nested: new Set(['dragSelectedTourAnchorProps', 'PostListProps']) }))
Example #21
Source File: NetworksDnd.tsx    From devex with GNU General Public License v3.0 5 votes vote down vote up
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 #22
Source File: index.tsx    From S2 with MIT License 5 votes vote down vote up
DimensionItem: FC<DimensionItemProps> = ({
  fieldType,
  item: { id, displayName, checked = true, children = [] },
  expandable,
  expandChildren,
  isDragDisabled,
  selectable,
  index,
  draggingItemId,
  onVisibleItemChange,
}) => {
  return (
    <Draggable draggableId={id} index={index} isDragDisabled={isDragDisabled}>
      {(provided, snapshot) => (
        <div
          {...provided.draggableProps}
          ref={provided.innerRef}
          className={cx(
            getSwitcherClassName(selectable ? 'checkable-list' : 'normal-list'),
            {
              dragging: snapshot.isDragging,
              'disable-dragging': isDragDisabled,
            },
          )}
        >
          <SingleItem
            dragHandleProps={provided.dragHandleProps}
            fieldType={fieldType}
            id={id}
            displayName={displayName}
            checked={checked}
            onVisibleItemChange={onVisibleItemChange}
            selectable={selectable}
            className={cx(selectable ? 'checkable-item' : 'normal-item', {
              'item-collapse': !expandChildren,
            })}
          />

          {expandable &&
            expandChildren &&
            !isEmpty(children) &&
            draggingItemId !== id && (
              <div
                className={cx('child-items', {
                  'item-hidden': !expandChildren,
                })}
              >
                {children.map((item) => (
                  <SingleItem
                    key={item.id}
                    id={item.id}
                    fieldType={fieldType}
                    displayName={item.displayName}
                    disabled={!checked}
                    checked={item.checked}
                    parentId={id}
                    selectable={selectable}
                    onVisibleItemChange={onVisibleItemChange}
                    className="checkable-item"
                  />
                ))}
              </div>
            )}
        </div>
      )}
    </Draggable>
  );
}
Example #23
Source File: index.tsx    From Tiquet with MIT License 5 votes vote down vote up
DraggableTask = ({
  title,
  id,
  index,
  fetchTask,
  deleteTask,
  taskInfo,
  taskInfoLoading,
  priority,
}: DraggableTaskProps): JSX.Element => {
  const handleFetch = (): void => {
    // Check if the task to fetch isn't already fetched.
    const alreadyFetched = id === taskInfo?.id;

    if (!alreadyFetched && !taskInfoLoading) {
      fetchTask(id);
    }
  }

  const handleDeleteTask = (): void => {
    deleteTask(id);
  }

  return (
    <Draggable index={index} draggableId={new Number(id).toString()} key={id}>
      {provided => (
        <div
          ref={provided.innerRef}
          {...provided.draggableProps}
          {...provided.dragHandleProps}
          style={{ ...provided.draggableProps.style }}
        >
          <Task
            id={id}
            title={title}
            priority={priority}
            handleDeleteTask={handleDeleteTask}
            handleInfoClick={handleFetch}
          />
        </div>
      )}
    </Draggable>
  )
}
Example #24
Source File: AddButtonMappingDialog.tsx    From Pi-Tool with GNU General Public License v3.0 5 votes vote down vote up
ButtonActionGrid: React.FC<{ buttonPresses: ButtonPressItems }> = ({ buttonPresses }) => {
    const classes = useStyles();


    return (
        <Paper className={classes.buttonActionGrid} variant="outlined">
            <Droppable
                droppableId="GRID"
                direction="horizontal"
                isDropDisabled={buttonPresses.length >= 8}>
                {(provided, snapshot) => (
                    <div
                        className={classes.buttonActionDroppable}
                        ref={provided.innerRef}
                        style={{ display: 'inline-flex', verticalAlign: 'middle', alignItems: 'center' }}
                        {...provided.droppableProps}>
                        <Box display="flex" flexDirection="row">
                            {buttonPresses.map((item, index) => (
                                <Draggable key={item.id} draggableId={item.id} index={index}>
                                    {(provided, snapshot) => (
                                        <div
                                            style={getStyle(provided.draggableProps.style, snapshot)}
                                            ref={provided.innerRef}
                                            {...provided.draggableProps}
                                            {...provided.dragHandleProps}>

                                            <ButtonPressIcon press={item.press} isButton={true} />
                                        </div>
                                    )}
                                </Draggable>
                            ))}

                        </Box>
                        {provided.placeholder}
                        {/* && (buttonPresses.length == 0 && <Typography>  Drag your your sequence here.</Typography>)} */}
                    </div>
                )}
            </Droppable>
        </Paper>
    );
}
Example #25
Source File: designers.tsx    From ali-react-table with MIT License 5 votes vote down vote up
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 #26
Source File: AccountManagementPage.tsx    From clarity with Apache License 2.0 4 votes vote down vote up
AccountManagementPage = observer((props: Props) => {
  const [openDialog, setOpenDialog] = React.useState(false);
  const [openKeyDialog, setOpenKeyDialog] = React.useState(false);
  const [
    selectedAccount,
    setSelectedAccount
  ] = React.useState<SignKeyPairWithAlias | null>(null);
  const [name, setName] = React.useState('');
  const [publicKey64, setPublicKey64] = React.useState('');
  const [publicKeyHex, setPublicKeyHex] = React.useState('');
  /* Note: 01 prefix denotes algorithm used in key generation */
  const address = '01' + publicKeyHex;
  const [copyStatus, setCopyStatus] = React.useState(false);

  const handleClickOpen = (account: SignKeyPairWithAlias) => {
    setOpenDialog(true);
    setSelectedAccount(account);
    setName(account.name);
  };

  const handleViewKey = async (accountName: string) => {
    let publicKey64 = await props.authContainer.getSelectedAccountKey(
      accountName
    );
    let publicKeyHex = await props.authContainer.getPublicKeyHex(accountName);
    setName(accountName);
    setPublicKey64(publicKey64);
    setPublicKeyHex(publicKeyHex);
    setOpenKeyDialog(true);
  };

  const handleCopyMessage = (event?: React.SyntheticEvent, reason?: string) => {
    if (reason === 'clickaway') {
      return;
    }
    setCopyStatus(false);
  };

  const handleClose = () => {
    setOpenDialog(false);
    setOpenKeyDialog(false);
    setSelectedAccount(null);
  };

  const handleUpdateName = () => {
    if (selectedAccount) {
      props.authContainer.renameUserAccount(selectedAccount.name, name);
      handleClose();
    }
  };

  const onDragEnd = (result: DropResult) => {
    // dropped outside the list
    if (!result.destination) {
      return;
    }

    props.authContainer.reorderAccount(
      result.source.index,
      result.destination.index
    );
  };

  const handleClickRemove = (name: string) => {
    confirm(
      <div className="text-danger">Remove account</div>,
      'Are you sure you want to remove this account?'
    ).then(() => props.authContainer.removeUserAccount(name));
  };

  return (
    <React.Fragment>
      <DragDropContext onDragEnd={result => onDragEnd(result)}>
        <Droppable droppableId="droppable">
          {(provided, snapshot) => (
            <Observer>
              {() => (
                <RootRef rootRef={provided.innerRef}>
                  <List>
                    {props.authContainer.userAccounts.map((item, index) => (
                      <Draggable
                        key={item.name}
                        draggableId={item.name}
                        index={index}
                      >
                        {(provided, snapshot) => (
                          <ListItem
                            innerRef={provided.innerRef}
                            ContainerProps={{
                              ...provided.draggableProps,
                              ...provided.dragHandleProps,
                              style: getItemStyle(
                                snapshot.isDragging,
                                provided.draggableProps.style
                              )
                            }}
                          >
                            <ListItemText primary={item.name} />
                            <ListItemSecondaryAction>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleClickOpen(item);
                                }}
                              >
                                <EditIcon />
                              </IconButton>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleClickRemove(item.name);
                                }}
                              >
                                <DeleteIcon />
                              </IconButton>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleViewKey(item.name);
                                }}
                              >
                                <VpnKeyIcon />
                              </IconButton>
                            </ListItemSecondaryAction>
                          </ListItem>
                        )}
                      </Draggable>
                    ))}
                    {provided.placeholder}
                  </List>
                </RootRef>
              )}
            </Observer>
          )}
        </Droppable>
      </DragDropContext>
      <Dialog
        open={openDialog}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        <DialogTitle id="form-dialog-title">Rename</DialogTitle>
        <DialogContent>
          <Input
            autoFocus
            margin="dense"
            id="name"
            type="text"
            fullWidth
            value={name}
            onChange={e => {
              setName(e.target.value);
            }}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Cancel
          </Button>
          <Button onClick={handleUpdateName} color="primary">
            Update
          </Button>
        </DialogActions>
      </Dialog>

      <Dialog
        fullScreen
        open={openKeyDialog}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        <DialogTitle id="form-dialog-title">Account Details</DialogTitle>
        <DialogContent>
          <List>
            <ListSubheader>
              <Typography variant={'h6'}>{name}</Typography>
            </ListSubheader>
            <ListItem>
              <IconButton
                edge={'start'}
                onClick={() => {
                  copy(address);
                  setCopyStatus(true);
                }}
              >
                <FilterNoneIcon />
              </IconButton>
              <ListItemText
                primary={'Address: ' + address}
                style={{ overflowWrap: 'break-word' }}
              />
            </ListItem>
            <ListItem>
              <IconButton
                edge={'start'}
                onClick={() => {
                  copy(publicKey64);
                  setCopyStatus(true);
                }}
              >
                <FilterNoneIcon />
              </IconButton>
              <ListItemText
                primary={'Public Key: ' + publicKey64}
                style={{ overflowWrap: 'break-word' }}
              />
            </ListItem>
          </List>
          <Snackbar
            open={copyStatus}
            message="Copied!"
            autoHideDuration={1500}
            onClose={handleCopyMessage}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Close
          </Button>
        </DialogActions>
      </Dialog>
    </React.Fragment>
  );
})
Example #27
Source File: DataModelBranch.tsx    From datart with Apache License 2.0 4 votes vote down vote up
DataModelBranch: FC<{
  node: Column;
  onNodeTypeChange: (type: any, name: string) => void;
  onMoveToHierarchy: (node: Column) => void;
  onEditBranch;
  onDelete: (node: Column) => void;
  onDeleteFromHierarchy: (parent: Column) => (node: Column) => void;
}> = memo(
  ({
    node,
    onNodeTypeChange,
    onMoveToHierarchy,
    onEditBranch,
    onDelete,
    onDeleteFromHierarchy,
  }) => {
    const t = useI18NPrefix('view.model');
    const [isHover, setIsHover] = useState(false);

    const renderNode = (node, isDragging) => {
      let icon = (
        <FolderOpenOutlined style={{ alignSelf: 'center', color: YELLOW }} />
      );

      return (
        <>
          <div
            className="content"
            onMouseEnter={() => {
              setIsHover(true);
            }}
            onMouseLeave={() => {
              setIsHover(false);
            }}
          >
            <IW fontSize={FONT_SIZE_HEADING}>{icon}</IW>
            <span>{node.name}</span>
            <div className="action">
              {isHover && !isDragging && (
                <Tooltip title={t('rename')}>
                  <Button
                    type="link"
                    onClick={() => onEditBranch(node)}
                    icon={<EditOutlined />}
                  />
                </Tooltip>
              )}
              {isHover && !isDragging && (
                <Tooltip title={t('delete')}>
                  <Button
                    type="link"
                    onClick={() => onDelete(node)}
                    icon={<DeleteOutlined />}
                  />
                </Tooltip>
              )}
            </div>
          </div>
          <div className="children">
            {node?.children?.map(childNode => (
              <DataModelNode
                className="in-hierarchy"
                node={childNode}
                key={childNode.name}
                onMoveToHierarchy={onMoveToHierarchy}
                onNodeTypeChange={onNodeTypeChange}
                onDeleteFromHierarchy={onDeleteFromHierarchy(node)}
              />
            ))}
          </div>
        </>
      );
    };

    return (
      <Draggable
        key={node?.name}
        draggableId={node?.name}
        index={node?.index}
        isDragDisabled={true}
      >
        {(draggableProvided, draggableSnapshot) => {
          return (
            <StyledDataModelBranch
              ref={draggableProvided.innerRef}
              {...draggableProvided.draggableProps}
              {...draggableProvided.dragHandleProps}
            >
              <Droppable
                droppableId={node?.name}
                type={TreeNodeHierarchy.Branch}
                isCombineEnabled={false}
              >
                {(droppableProvided, droppableSnapshot) => (
                  <div ref={droppableProvided.innerRef}>
                    {renderNode(node, draggableSnapshot.isDragging)}
                    {droppableProvided.placeholder}
                  </div>
                )}
              </Droppable>
            </StyledDataModelBranch>
          );
        }}
      </Draggable>
    );
  },
)
Example #28
Source File: DownloadQueue.tsx    From Tachidesk-WebUI with Mozilla Public License 2.0 4 votes vote down vote up
export default function DownloadQueue() {
    const [, setWsClient] = useState<WebSocket>();
    const [queueState, setQueueState] = useState<IQueue>(initialQueue);
    const { queue, status } = queueState;

    const history = useHistory();

    const theme = useTheme();

    const { setTitle, setAction } = useContext(NavbarContext);

    const toggleQueueStatus = () => {
        if (status === 'Stopped') {
            client.get('/api/v1/downloads/start');
        } else {
            client.get('/api/v1/downloads/stop');
        }
    };

    useEffect(() => {
        setTitle('Download Queue');

        setAction(() => {
            if (status === 'Stopped') {
                return (
                    <IconButton onClick={toggleQueueStatus} size="large">
                        <PlayArrowIcon />
                    </IconButton>
                );
            }
            return (
                <IconButton onClick={toggleQueueStatus} size="large">
                    <PauseIcon />
                </IconButton>
            );
        });
    }, [status]);

    useEffect(() => {
        const wsc = new WebSocket(`${baseWebsocketUrl}/api/v1/downloads`);
        wsc.onmessage = (e) => {
            setQueueState(JSON.parse(e.data));
        };

        setWsClient(wsc);
    }, []);

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const onDragEnd = (result: DropResult) => {
    };

    if (queue.length === 0) {
        return <EmptyView message="No downloads" />;
    }

    return (
        <>
            <DragDropContext onDragEnd={onDragEnd}>
                <Droppable droppableId="droppable">
                    {(provided) => (
                        <List ref={provided.innerRef}>
                            {queue.map((item, index) => (
                                <Draggable
                                    key={`${item.mangaId}-${item.chapterIndex}`}
                                    draggableId={`${item.mangaId}-${item.chapterIndex}`}
                                    index={index}
                                >
                                    {(provided, snapshot) => (
                                        <ListItem
                                            ContainerProps={{ ref: provided.innerRef } as any}
                                            sx={{
                                                display: 'flex',
                                                justifyContent: 'flex-start',
                                                alignItems: 'flex-start',
                                                padding: 2,
                                                margin: '10px',
                                                '&:hover': {
                                                    backgroundColor: 'action.hover',
                                                    transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
                                                },
                                                '&:active': {
                                                    backgroundColor: 'action.selected',
                                                    transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
                                                },
                                            }}
                                            onClick={() => history.push(`/manga/${item.chapter.mangaId}`)}
                                            {...provided.draggableProps}
                                            {...provided.dragHandleProps}
                                            style={getItemStyle(
                                                snapshot.isDragging,
                                                provided.draggableProps.style,
                                                theme.palette,
                                            )}
                                            ref={provided.innerRef}
                                        >
                                            <ListItemIcon sx={{ margin: 'auto 0' }}>
                                                <DragHandleIcon />
                                            </ListItemIcon>
                                            <Box sx={{ display: 'flex' }}>
                                                <Box sx={{ display: 'flex', flexDirection: 'column' }}>
                                                    <Typography variant="h5" component="h2">
                                                        {item.manga.title}
                                                    </Typography>
                                                    <Typography variant="caption" display="block" gutterBottom>
                                                        {`${item.chapter.name} `
                                                        + `(${(item.progress * 100).toFixed(2)}%)`
                                                        + ` => state: ${item.state}`}
                                                    </Typography>
                                                </Box>
                                            </Box>
                                            <IconButton
                                                sx={{ marginLeft: 'auto' }}
                                                onClick={(e) => {
                                                    // deleteCategory(index);
                                                    // prevent parent tags from getting the event
                                                    e.stopPropagation();
                                                }}
                                                size="large"
                                            >
                                                <DeleteIcon />
                                            </IconButton>
                                        </ListItem>
                                    )}
                                </Draggable>

                            ))}
                            {provided.placeholder}
                        </List>
                    )}
                </Droppable>
            </DragDropContext>
        </>
    );
}
Example #29
Source File: index.tsx    From taskcafe with MIT License 4 votes vote down vote up
TaskDetails: React.FC<TaskDetailsProps> = ({
  me,
  onCancelCommentEdit,
  task,
  editableComment = null,
  onDeleteChecklist,
  onToggleTaskWatch,
  onTaskNameChange,
  onCommentShowActions,
  onOpenAddChecklistPopup,
  onChangeChecklistName,
  onCreateComment,
  onChecklistDrop,
  onChecklistItemDrop,
  onToggleTaskComplete,
  onTaskDescriptionChange,
  onChangeItemName,
  onDeleteItem,
  onDeleteTask,
  onCloseModal,
  onUpdateComment,
  onOpenAddMemberPopup,
  onOpenAddLabelPopup,
  onOpenDueDatePopop,
  onAddItem,
  onToggleChecklistItem,
  onMemberProfile,
}) => {
  const { user } = useCurrentUser();
  const [taskName, setTaskName] = useState(task.name);
  const [editTaskDescription, setEditTaskDescription] = useState(() => {
    if (task.description) {
      if (task.description.trim() === '' || task.description.trim() === '\\') {
        return true;
      }
      return false;
    }
    return true;
  });
  const [saveTimeout, setSaveTimeout] = useState<any>(null);
  const [showRaw, setShowRaw] = useState(false);
  const taskDescriptionRef = useRef(task.description ?? '');
  const $noMemberBtn = useRef<HTMLDivElement>(null);
  const $addMemberBtn = useRef<HTMLDivElement>(null);
  const $dueDateBtn = useRef<HTMLDivElement>(null);
  const $detailsTitle = useRef<HTMLTextAreaElement>(null);

  const activityStream: Array<{ id: string; data: { time: string; type: 'comment' | 'activity' } }> = [];

  if (task.activity) {
    task.activity.forEach((activity) => {
      activityStream.push({
        id: activity.id,
        data: {
          time: activity.createdAt,
          type: 'activity',
        },
      });
    });
  }

  if (task.comments) {
    task.comments.forEach((comment) => {
      activityStream.push({
        id: comment.id,
        data: {
          time: comment.createdAt,
          type: 'comment',
        },
      });
    });
  }
  activityStream.sort((a, b) => (dayjs(a.data.time).isAfter(dayjs(b.data.time)) ? 1 : -1));

  const saveDescription = () => {
    onTaskDescriptionChange(task, taskDescriptionRef.current);
  };
  return (
    <Container>
      <LeftSidebar>
        <LeftSidebarContent>
          <LeftSidebarSection>
            <SidebarTitle>TASK GROUP</SidebarTitle>
            <SidebarButton>
              <SidebarButtonText>{task.taskGroup.name}</SidebarButtonText>
            </SidebarButton>
            <DueDateTitle>DUE DATE</DueDateTitle>
            <SidebarButton
              ref={$dueDateBtn}
              onClick={() => {
                if (user) {
                  onOpenDueDatePopop(task, $dueDateBtn);
                }
              }}
            >
              {task.dueDate.at ? (
                <SidebarButtonText>
                  {dayjs(task.dueDate.at).format(task.hasTime ? 'MMM D [at] h:mm A' : 'MMMM D')}
                </SidebarButtonText>
              ) : (
                <SidebarButtonText>No due date</SidebarButtonText>
              )}
            </SidebarButton>
          </LeftSidebarSection>
          <AssignedUsersSection>
            <DueDateTitle>MEMBERS</DueDateTitle>
            {task.assigned && task.assigned.length !== 0 ? (
              <MemberList>
                {task.assigned.map((m) => (
                  <TaskMember
                    key={m.id}
                    member={m}
                    size={32}
                    onMemberProfile={($target) => {
                      if (user) {
                        onMemberProfile($target, m.id);
                      }
                    }}
                  />
                ))}
                <AssignUserIcon
                  ref={$addMemberBtn}
                  onClick={() => {
                    if (user) {
                      onOpenAddMemberPopup(task, $addMemberBtn);
                    }
                  }}
                >
                  <Plus width={16} height={16} />
                </AssignUserIcon>
              </MemberList>
            ) : (
              <AssignUsersButton
                ref={$noMemberBtn}
                onClick={() => {
                  if (user) {
                    onOpenAddMemberPopup(task, $noMemberBtn);
                  }
                }}
              >
                <AssignUserIcon>
                  <User width={16} height={16} />
                </AssignUserIcon>
                <AssignUserLabel>No members</AssignUserLabel>
              </AssignUsersButton>
            )}
          </AssignedUsersSection>
          {user && (
            <ExtraActionsSection>
              <DueDateTitle>ACTIONS</DueDateTitle>
              <ActionButton
                onClick={($target) => {
                  onOpenAddLabelPopup(task, $target);
                }}
                icon={<Tags width={12} height={12} />}
              >
                Labels
              </ActionButton>
              <ActionButton
                onClick={($target) => {
                  onOpenAddChecklistPopup(task, $target);
                }}
                icon={<CheckSquareOutline width={12} height={12} />}
              >
                Checklist
              </ActionButton>
              <ActionButton>Cover</ActionButton>
              <ActionButton
                onClick={() => {
                  onToggleTaskWatch(task, !task.watched);
                }}
                icon={<Eye width={12} height={12} />}
              >
                Watch {task.watched && <WatchedCheckmark width={18} height={18} />}
              </ActionButton>
            </ExtraActionsSection>
          )}
        </LeftSidebarContent>
      </LeftSidebar>
      <ContentContainer>
        <HeaderContainer>
          <HeaderInnerContainer>
            <HeaderLeft>
              <MarkCompleteButton
                disabled={user === null}
                invert={task.complete ?? false}
                onClick={() => {
                  if (user) {
                    onToggleTaskComplete(task);
                  }
                }}
              >
                <Checkmark width={8} height={8} />
                <span>{task.complete ? 'Completed' : 'Mark complete'}</span>
              </MarkCompleteButton>
            </HeaderLeft>
            {user && (
              <HeaderRight>
                <HeaderActionIcon>
                  <Paperclip width={16} height={16} />
                </HeaderActionIcon>
                <HeaderActionIcon>
                  <Clone width={16} height={16} />
                </HeaderActionIcon>
                <HeaderActionIcon>
                  <Share width={16} height={16} />
                </HeaderActionIcon>
                <HeaderActionIcon onClick={() => onDeleteTask(task)}>
                  <Trash width={16} height={16} />
                </HeaderActionIcon>
              </HeaderRight>
            )}
          </HeaderInnerContainer>
          <TaskDetailsTitleWrapper>
            <TaskDetailsTitle
              value={taskName}
              ref={$detailsTitle}
              disabled={user === null}
              onKeyDown={(e) => {
                if (e.keyCode === 13) {
                  e.preventDefault();
                  if ($detailsTitle && $detailsTitle.current) {
                    $detailsTitle.current.blur();
                  }
                }
              }}
              onChange={(e) => {
                setTaskName(e.currentTarget.value);
              }}
              onBlur={() => {
                if (taskName !== task.name) {
                  onTaskNameChange(task, taskName);
                }
              }}
            />
          </TaskDetailsTitleWrapper>
          <Labels>
            {task.labels.length !== 0 && (
              <MetaDetailContent>
                {task.labels.map((label) => {
                  return (
                    <TaskLabelItem
                      key={label.projectLabel.id}
                      label={label}
                      onClick={($target) => {
                        onOpenAddLabelPopup(task, $target);
                      }}
                    />
                  );
                })}
                <TaskDetailsAddLabelIcon>
                  <Plus width={12} height={12} />
                </TaskDetailsAddLabelIcon>
              </MetaDetailContent>
            )}
          </Labels>
        </HeaderContainer>
        <InnerContentContainer>
          <DescriptionContainer>
            {showRaw ? (
              <TaskDetailsEditor value={taskDescriptionRef.current} />
            ) : (
              <EditorContainer
                onClick={(e) => {
                  if (!editTaskDescription) {
                    setEditTaskDescription(true);
                  }
                }}
              >
                <Editor
                  defaultValue={task.description ?? ''}
                  readOnly={user === null || !editTaskDescription}
                  theme={dark}
                  autoFocus
                  onChange={(value) => {
                    setSaveTimeout(() => {
                      clearTimeout(saveTimeout);
                      return setTimeout(saveDescription, 2000);
                    });
                    const text = value();
                    taskDescriptionRef.current = text;
                  }}
                />
              </EditorContainer>
            )}

            <ViewRawButton onClick={() => setShowRaw(!showRaw)}>{showRaw ? 'Show editor' : 'Show raw'}</ViewRawButton>
          </DescriptionContainer>
          <ChecklistSection>
            <DragDropContext onDragEnd={(result) => onDragEnd(result, task, onChecklistDrop, onChecklistItemDrop)}>
              <Droppable direction="vertical" type="checklist" droppableId="root">
                {(dropProvided) => (
                  <ChecklistContainer {...dropProvided.droppableProps} ref={dropProvided.innerRef}>
                    {task.checklists &&
                      task.checklists
                        .slice()
                        .sort((a, b) => a.position - b.position)
                        .map((checklist, idx) => (
                          <Draggable key={checklist.id} draggableId={checklist.id} index={idx}>
                            {(provided) => (
                              <Checklist
                                ref={provided.innerRef}
                                wrapperProps={provided.draggableProps}
                                handleProps={provided.dragHandleProps}
                                key={checklist.id}
                                name={checklist.name}
                                checklistID={checklist.id}
                                items={checklist.items}
                                onDeleteChecklist={onDeleteChecklist}
                                onChangeName={(newName) => onChangeChecklistName(checklist.id, newName)}
                                onToggleItem={onToggleChecklistItem}
                                onDeleteItem={onDeleteItem}
                                onAddItem={(n) => {
                                  if (task.checklists) {
                                    let position = 65535;
                                    const [lastItem] = checklist.items
                                      .sort((a, b) => a.position - b.position)
                                      .slice(-1);
                                    if (lastItem) {
                                      position = lastItem.position * 2 + 1;
                                    }
                                    onAddItem(checklist.id, n, position);
                                  }
                                }}
                                onChangeItemName={onChangeItemName}
                              >
                                <Droppable direction="vertical" type="checklistItem" droppableId={checklist.id}>
                                  {(checklistDrop) => (
                                    <>
                                      <ChecklistItems ref={checklistDrop.innerRef} {...checklistDrop.droppableProps}>
                                        {checklist.items
                                          .slice()
                                          .sort((a, b) => a.position - b.position)
                                          .map((item, itemIdx) => (
                                            <Draggable key={item.id} draggableId={item.id} index={itemIdx}>
                                              {(itemDrop) => (
                                                <ChecklistItem
                                                  key={item.id}
                                                  itemID={item.id}
                                                  checklistID={item.taskChecklistID}
                                                  ref={itemDrop.innerRef}
                                                  wrapperProps={itemDrop.draggableProps}
                                                  handleProps={itemDrop.dragHandleProps}
                                                  name={item.name}
                                                  complete={item.complete}
                                                  onDeleteItem={onDeleteItem}
                                                  onChangeName={onChangeItemName}
                                                  onToggleItem={(itemID, complete) => {
                                                    onToggleChecklistItem(item.id, complete);
                                                  }}
                                                />
                                              )}
                                            </Draggable>
                                          ))}
                                      </ChecklistItems>
                                      {checklistDrop.placeholder}
                                    </>
                                  )}
                                </Droppable>
                              </Checklist>
                            )}
                          </Draggable>
                        ))}
                    {dropProvided.placeholder}
                  </ChecklistContainer>
                )}
              </Droppable>
            </DragDropContext>
          </ChecklistSection>
          <TabBarSection>
            <TabBarItem>Activity</TabBarItem>
          </TabBarSection>
          <ActivitySection>
            {activityStream.map((stream) =>
              stream.data.type === 'comment' ? (
                <StreamComment
                  key={stream.id}
                  onExtraActions={onCommentShowActions}
                  onCancelCommentEdit={onCancelCommentEdit}
                  onUpdateComment={(message) => onUpdateComment(stream.id, message)}
                  editable={stream.id === editableComment}
                  comment={task.comments && task.comments.find((comment) => comment.id === stream.id)}
                />
              ) : (
                <StreamActivity
                  key={stream.id}
                  activity={task.activity && task.activity.find((activity) => activity.id === stream.id)}
                />
              ),
            )}
          </ActivitySection>
        </InnerContentContainer>
        {me && (
          <CommentContainer>
            <CommentCreator
              me={me}
              onCreateComment={(message) => onCreateComment(task, message)}
              onMemberProfile={onMemberProfile}
            />
          </CommentContainer>
        )}
      </ContentContainer>
    </Container>
  );
}