react-dnd-html5-backend#HTML5Backend TypeScript Examples

The following examples show how to use react-dnd-html5-backend#HTML5Backend. 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: DragAndDropTreeProvider.tsx    From frontend-sample-showcase with MIT License 6 votes vote down vote up
DragAndDropTreeProvider: React.FC = () => {

  // See documentation on DndProvider backends: https://react-dnd.github.io/react-dnd/docs/overview
  return (
    <DndProvider backend={HTML5Backend}>
      <DragAndDropTreeComponent />
    </DndProvider>
  );
}
Example #2
Source File: index.tsx    From anew-server with MIT License 6 votes vote down vote up
render() {
    const { order } = this.state;
    const { children } = this.props;

    const tabs: any[] = [];
    React.Children.forEach(children, (c) => {
      tabs.push(c);
    });

    const orderTabs = tabs.slice().sort((a, b) => {
      const orderA = order.indexOf(a.key);
      const orderB = order.indexOf(b.key);

      if (orderA !== -1 && orderB !== -1) {
        return orderA - orderB;
      }
      if (orderA !== -1) {
        return -1;
      }
      if (orderB !== -1) {
        return 1;
      }

      const ia = tabs.indexOf(a);
      const ib = tabs.indexOf(b);

      return ia - ib;
    });
    return (
      <DndProvider backend={HTML5Backend}>
        <Tabs renderTabBar={this.renderTabBar} {...this.props}>
          {orderTabs}
        </Tabs>
      </DndProvider>
    );
  }
Example #3
Source File: material-ui-dnd-grid.tsx    From ui-schema with MIT License 6 votes vote down vote up
HTML5toTouch: MultiBackendOptions = {
    backends: [
        {
            id: 'html5',
            backend: HTML5Backend,
            transition: PointerTransition,
        },
        {
            id: 'touch',
            backend: TouchBackend,
            options: {enableMouseEvents: true},
            preview: true,
            transition: TouchTransition,
        },
    ],
}
Example #4
Source File: DragSortEditTable.tsx    From datart with Apache License 2.0 6 votes vote down vote up
DragSortEditTable: React.FC<DragEditTableProps> = ({
  components,
  ...restProps
}) => {
  const DragEditComponents = {
    body: {
      row: DraggableAndEditableBodyRow,
      cell: EditableCell,
    },
  };

  const onMoveRow = useCallback((a, b) => {
    // const dragRow = rows[dragIndex];
    // rows.splice(dragIndex, 1);
    // rows.splice(hoverIndex, 0, dragRow);
    // setRows([...rows]);
  }, []);
  return (
    <DndProvider backend={HTML5Backend}>
      <Table
        rowClassName={() => 'editable-row'}
        components={DragEditComponents}
        {...restProps}
      />
    </DndProvider>
  );
}
Example #5
Source File: index.tsx    From calendar-hack with MIT License 6 votes vote down vote up
CustomHTML5toTouch: Backends = {
  backends: [
    {
      backend: HTML5Backend as any,
      transition: MouseTransition,
      // by default, will dispatch a duplicate `mousedown` event when this backend is activated
      //preview: true,
    },
    {
      backend: TouchBackend as any,
      // Note that you can call your backends with options
      options: {
        enableMouseEvents: true,
        skipDispatchOnTransition: true // will not dispatch a duplicate `touchstart` event when this backend is activated
      },
      preview: true,
      transition: TouchTransition,
    }
  ]
}
Example #6
Source File: index.tsx    From gio-design with Apache License 2.0 6 votes vote down vote up
public render() {
    const { collection, getId, template, container } = this.props;
    const { collections, SortableItemClass } = this.state;
    const SortableItem = SortableItemClass;

    const children = collections.map((props: ItemWithId, i: number) => {
      const originalPosition = collection.indexOf(props);
      const key = getId ? getId(props, i) : props.value;
      const canDrag = props.canDrag === false ? props.canDrag : true;
      return (
        <SortableItem
          sortData={props}
          canDrag={canDrag}
          index={i}
          key={key}
          position={originalPosition}
          onHover={this._handleHover}
          onDrop={this._handleDrop}
          onCancel={this._handleCancel}
          template={template}
        />
      );
    });

    return (
      <DndProvider backend={HTML5Backend as any}>
        {React.createElement(
          container,
          _.omit(this.props, ['collection', 'container', 'onSorted', 'template']),
          children
        )}
      </DndProvider>
    );
  }
Example #7
Source File: Programs.tsx    From gear-js with GNU General Public License v3.0 6 votes vote down vote up
Programs: VFC = () => {
  const isUploadedProgramsPage = useMatch(routes.uploadedPrograms);
  const isAllProgramsPage = useMatch(routes.allPrograms);
  const isAllMessagesPage = useMatch(routes.messages);
  let currentPage = SWITCH_PAGE_TYPES.UPLOAD_PROGRAM;

  if (isUploadedProgramsPage) {
    currentPage = SWITCH_PAGE_TYPES.UPLOADED_PROGRAMS;
  } else if (isAllProgramsPage) {
    currentPage = SWITCH_PAGE_TYPES.ALL_PROGRAMS;
  } else if (isAllMessagesPage) {
    currentPage = SWITCH_PAGE_TYPES.ALL_MESSAGES;
  }

  return (
    <div className="main-content-wrapper">
      <ProgramSwitch pageType={currentPage} />
      {currentPage === SWITCH_PAGE_TYPES.UPLOAD_PROGRAM && (
        <>
          <DndProvider backend={HTML5Backend}>
            <Upload />
          </DndProvider>
          <BlockList />
        </>
      )}
      {currentPage === SWITCH_PAGE_TYPES.UPLOADED_PROGRAMS && <Recent />}
      {currentPage === SWITCH_PAGE_TYPES.ALL_PROGRAMS && <All />}
      {currentPage === SWITCH_PAGE_TYPES.ALL_MESSAGES && <Messages />}
    </div>
  );
}
Example #8
Source File: Viewer.tsx    From legend-studio with Apache License 2.0 6 votes vote down vote up
Viewer: React.FC = () => (
  <EditorStoreProvider>
    <ViewerStoreProvider>
      <DndProvider backend={HTML5Backend}>
        <ViewerInner />
      </DndProvider>
    </ViewerStoreProvider>
  </EditorStoreProvider>
)
Example #9
Source File: editor-component.tsx    From utopia with MIT License 6 votes vote down vote up
export function EditorComponent(props: EditorProps) {
  const indexedDBFailed = useEditorState(
    (store) => store.editor.indexedDBFailed,
    'EditorComponent indexedDBFailed',
  )

  return indexedDBFailed ? (
    <FatalIndexedDBErrorComponent />
  ) : (
    <DndProvider backend={HTML5Backend}>
      <EditorComponentInner {...props} />
    </DndProvider>
  )
}
Example #10
Source File: $todoListId.tsx    From remix-hexagonal-architecture with MIT License 6 votes vote down vote up
export default function TodoListPage() {
  const todoListPage = useTodoListPage();

  return (
    <DndProvider backend={HTML5Backend}>
      <TodoList todoListPage={todoListPage} />
    </DndProvider>
  );
}
Example #11
Source File: page-container.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
RenderMainContent = React.memo(
  ({
    noWrapper,
    customMain,
    route,
    layout,
  }: {
    noWrapper: boolean;
    customMain: Function | JSX.Element | null;
    route: RouteConfig;
    layout: {
      [k: string]: any;
    };
  }) => {
    return (
      <ErrorBoundary>
        <DndProvider backend={HTML5Backend}>
          {noWrapper ? (
            <>
              {typeof customMain === 'function' ? customMain() : customMain}
              {renderRoutes(route.routes)}
            </>
          ) : (
            <Card className={layout && layout.fullHeight ? 'h-full overflow-auto' : ''}>
              {typeof customMain === 'function' ? customMain() : customMain}
              {renderRoutes(route.routes)}
            </Card>
          )}
        </DndProvider>
      </ErrorBoundary>
    );
  },
)
Example #12
Source File: index.tsx    From datart with Apache License 2.0 5 votes vote down vote up
DraggableList = props => {
  return (
    <DndProvider backend={HTML5Backend}>
      <DraggableContainer {...props} />
    </DndProvider>
  );
}
Example #13
Source File: QueryEditor.tsx    From legend-studio with Apache License 2.0 5 votes vote down vote up
QueryEditor: React.FC = () => (
  <DndProvider backend={HTML5Backend}>
    <QueryEditorInner />
  </DndProvider>
)
Example #14
Source File: EditorElementFactory.tsx    From next-core with GNU General Public License v3.0 5 votes vote down vote up
export function EditorElementFactory(
  EditorComponent: EditorComponentType,
  options?: EditorElementOptions
): EditorBrickElementConstructor {
  class NewEditorElement extends UpdatingElement {
    static get selfLayout(): EditorSelfLayout {
      return options?.selfLayout;
    }

    @property({ type: Number })
    nodeUid: number;

    @property({ attribute: false })
    editorProps: StoryDoc["editorProps"];

    connectedCallback(): void {
      // Don't override user's style settings.
      // istanbul ignore else
      if (!this.style.display) {
        this.style.display = "block";
      }
      if (options?.brickStyle) {
        for (const [key, value] of Object.entries(options.brickStyle)) {
          (this.style as unknown as Record<string, string>)[key] = value;
        }
      }
      this._render();
    }

    disconnectedCallback(): void {
      ReactDOM.unmountComponentAtNode(this);
    }

    protected _render(): void {
      // istanbul ignore else
      if (this.isConnected && this.nodeUid) {
        ReactDOM.render(
          <BrickWrapper>
            <BuilderProvider>
              <DndProvider backend={HTML5Backend}>
                <EditorComponent
                  nodeUid={this.nodeUid}
                  editorProps={this.editorProps}
                />
              </DndProvider>
            </BuilderProvider>
          </BrickWrapper>,
          this
        );
      }
    }
  }
  return NewEditorElement;
}
Example #15
Source File: Editor.tsx    From legend-studio with Apache License 2.0 5 votes vote down vote up
Editor: React.FC = () => (
  <EditorStoreProvider>
    <DndProvider backend={HTML5Backend}>
      <EditorInner />
    </DndProvider>
  </EditorStoreProvider>
)
Example #16
Source File: App.tsx    From querybook with Apache License 2.0 5 votes vote down vote up
App = () => (
    <DndProvider backend={HTML5Backend}>
        <Provider store={reduxStore}>
            <AppRouter />
        </Provider>
    </DndProvider>
)
Example #17
Source File: Drag.tsx    From gio-design with Apache License 2.0 5 votes vote down vote up
Drag: React.FC<DragListProps> & {
  Item: React.FC<DragItemProps>;
} = (props) => {
  const { onChange, className, style, options: propsOptions, disabled, ...rest } = props;

  const [options, setOptions] = useState(propsOptions);
  useEffect(() => {
    setOptions(propsOptions);
  }, [propsOptions]);
  const prefixCls = `${usePrefixCls(PREFIX)}`;

  const onMoved = (dragIndex: number, hoverIndex: number) => {
    const dragCard = options[dragIndex];
    const updateOptions = update(options, {
      $splice: [
        [dragIndex, 1],
        [hoverIndex, 0, dragCard],
      ],
    });
    setOptions(updateOptions);
    onChange?.(updateOptions);
  };

  return (
    <DndProvider backend={HTML5Backend}>
      <List className={classNames(`${prefixCls}--drag`, className)} style={style}>
        {options?.map((option: OptionProps, index: number) => (
          <DragItem
            {...option}
            {...rest}
            index={index}
            onMoved={onMoved}
            disabled={option?.disabled ?? disabled}
            key={option?.value}
          />
        ))}
      </List>
    </DndProvider>
  );
}
Example #18
Source File: index.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
protected _render(): void {
    // istanbul ignore else
    if (this.isConnected) {
      ReactDOM.render(
        <BrickWrapper>
          <BuilderProvider>
            <DndProvider backend={HTML5Backend}>
              <BuilderContainer
                ref={this._managerRef}
                appId={this.appId}
                dataSource={this.dataSource}
                brickList={this.brickList}
                editorList={this.editorList}
                providerList={this.providerList}
                routeList={this.routeList}
                templateList={this.templateList}
                snippetList={this.snippetList}
                templateSources={this.templateSources}
                storyList={this.storyList}
                processing={this.processing}
                highlightTokens={this.highlightTokens}
                containerForContextModal={this.containerForContextModal}
                migrateClipboard={this.migrateClipboard}
                clipboardData={this.clipboardData}
                showDataView={this.showDataView}
                initialFullscreen={this.fullscreen}
                initialToolboxTab={this.toolboxTab}
                initialHiddenWrapper={this.hiddenWrapper}
                initialEventStreamNodeId={this.eventStreamNodeId}
                initialClipboardType={this.clipboardType}
                initialClipboardSource={this.clipboardSource}
                initialClipboardNodeType={this.clipboardNodeType}
                initialCanvasIndex={this.canvasIndex}
                initialStoryboardQuery={this.storyboardQuery}
                onNodeAdd={this._handleNodeAdd}
                onSnippetApply={this._handleSnippetApply}
                onNodeReorder={this._handleNodeReorder}
                onNodeMove={this._handleNodeMove}
                onNodeClick={this._handleNodeClick}
                onAskForDeletingNode={this._handleAskForDeletingNode}
                onToggleFullscreen={this._handleToggleFullscreen}
                onSwitchToolboxTab={this._handleSwitchToolboxTab}
                onSwitchHiddenWrapper={this._handleSwitchHiddenWrapper}
                onSelectEventStreamNode={this._handleSelectEventsViewNode}
                onClipboardChange={this._handleClipboardChange}
                onNodeCopy={this._handleNodeCopy}
                onNodeCut={this._handleNodeCut}
                onNodeCopyPaste={this._handleNodeCopyPaste}
                onNodeCutPaste={this._handleNodeCutPaste}
                onClipboardClear={this._handleClipboardClear}
                onContextUpdate={this._handleContextUpdate}
                onRouteSelect={this._handleRouteSelect}
                onTemplateSelect={this._handleTemplateSelect}
                onSnippetSelect={this._handleSnippetSelect}
                onCurrentRouteClick={this._handleCurrentRouteClick}
                onCurrentTemplateClick={this._handleCurrentTemplateClick}
                onCurrentSnippetClick={this._handleCurrentSnippetClick}
                onBuildAndPush={this._handleBuildAndPush}
                onPreview={this._handlePreview}
                onAskForAppendingBrick={this._handleAskForAppendingBrick}
                onAskForAppendingRoute={this._handleAskForAppendingRoute}
                onEventNodeClick={this._handleEventNodeClick}
                onConvertToTemplate={this._handleConvertToTemplate}
                onWorkbenchClose={this._handleWorkbenchClose}
                onSwitchCanvasIndex={this._handleSwitchCanvasIndex}
                onStoryboardQueryUpdate={this._handleStoryboardQueryUpdate}
                onClickHighlightToken={this._handleHighlightTokenClick}
              />
            </DndProvider>
          </BuilderProvider>
        </BrickWrapper>,
        this
      );
    }
  }
Example #19
Source File: LayerList.tsx    From datart with Apache License 2.0 4 votes vote down vote up
LayerList: React.FC<{}> = memo(() => {
  const dispatch = useDispatch();
  const allWidgetsInfo = useSelector(selectAllWidgetInfoMap);
  const sortLayoutWidgets = useSelector(selectSortAllWidgets);
  const { boardId } = useContext(BoardContext);
  const [cards, setCards] = useState<NameCard[]>([]);
  useEffect(() => {
    const card = sortLayoutWidgets.map(
      ele =>
        ({
          index: ele.config.index,
          id: ele.id,
          widgetType: ele.config.type,
          name: ele.config.name,
          editing: allWidgetsInfo[ele.id]?.editing || false,
          selected: allWidgetsInfo[ele.id]?.selected || false,
        } as NameCard),
    );
    setCards(card);
  }, [allWidgetsInfo, sortLayoutWidgets]);
  const moveCard = useCallback(
    (
      dragIndex: number,
      hoverIndex: number,
      dragZIndex: number,
      hoverZIndex: number,
    ) => {
      const newCards = JSON.parse(JSON.stringify(cards));

      [newCards[dragIndex], newCards[hoverIndex]] = [
        newCards[hoverIndex],
        newCards[dragIndex],
      ];
      newCards[dragIndex].index = dragZIndex;
      newCards[hoverIndex].index = hoverZIndex;

      setCards(newCards);
    },
    [cards],
  );
  const moveEnd = useCallback(() => {
    const newWidgets: updateWidgetConf[] = [];
    cards.forEach(ele => {
      sortLayoutWidgets.forEach(widget => {
        if (widget.id === ele.id && widget.config.index !== ele.index) {
          const newConfig = produce(widget.config, draft => {
            draft.index = ele.index;
          });
          newWidgets.push({ id: widget.id, config: newConfig });
        }
      });
    });
    if (newWidgets.length) {
      dispatch(editBoardStackActions.updateWidgetsConfig(newWidgets));
    }
  }, [cards, dispatch, sortLayoutWidgets]);
  const clearSelectedWidgets = () => {
    dispatch(editWidgetInfoActions.clearSelectedWidgets());
  };

  const nameList = cards
    .sort((a, b) => b.index - a.index)
    .map((ele, index) => (
      <WidgetWrapProvider
        id={ele.id}
        key={ele.id}
        boardEditing={true}
        boardId={boardId}
      >
        <NameItem
          widgetType={ele.widgetType}
          key={ele.id}
          index={index}
          zIndex={ele.index!}
          card={ele}
          moveCard={moveCard}
          moveEnd={moveEnd}
        ></NameItem>
      </WidgetWrapProvider>
    ));
  return (
    <DndProvider backend={HTML5Backend}>
      <Wrapper onClick={clearSelectedWidgets}>
        <h3 className="title">组件</h3>
        <div className="nameList">{nameList}</div>
        <div className="bottom"></div>
      </Wrapper>
    </DndProvider>
  );
})
Example #20
Source File: BrickTable.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function BrickTable(props: BrickTableProps): React.ReactElement {
  if (props.error) {
    throw props.error;
  }

  const {
    configProps,
    columns,
    rowKey,
    expandIconAsCell,
    expandIconColumnIndex,
    childrenColumnName,
    deleteEnabled,
    scroll,
    optimizedColumns,
    onDelete, // 用于 brick form 中,will be deprecated
    ellipsisInfo,
    showHeader,
  } = props;

  const initData = useMemo(() => {
    return (
      props.dataSource &&
      (rowKey
        ? props.dataSource
        : props.dataSource.map((item, index) =>
            isNil(item.key) ? { ...item, key: index } : item
          ))
    );
  }, [props.dataSource, rowKey]);

  const [data, setData] = useState(initData);
  const rowKeyExpandIconMapRef = useRef<Map<unknown, React.ReactNode>>(
    new Map()
  );
  const columnTitleBrickDataMapRef = useRef<
    Map<CustomColumn, { title: unknown }>
  >(new Map());
  const useBrickItemBrickDataMapRef = useRef<
    Map<UseBrickConf, ItemBrickDataMap>
  >(new Map());
  const itemExpandedRowBrickDataMapRef = useRef<Map<unknown, unknown>>(
    new Map()
  );

  useEffect(() => {
    itemExpandedRowBrickDataMapRef.current.clear();
    setData(initData);
  }, [initData]);

  const expandIconColumnIndexOffset = configProps?.rowSelection ? -1 : 0;
  const customColumns = useMemo(() => {
    if (columns) {
      columnTitleBrickDataMapRef.current.clear();
      useBrickItemBrickDataMapRef.current.clear();
      const customColumns = columns.map((column, index) => {
        const {
          useBrick,
          component,
          valueSuffix,
          cellStatus,
          titleUseBrick,
          headerBrick,
          colSpanKey,
          rowSpanKey,
          ...columnConf
        } = column;
        if (headerBrick?.useBrick || titleUseBrick) {
          if (titleUseBrick) {
            // eslint-disable-next-line no-console
            console.warn(
              "`titleUseBrick` of `<presentational-bricks.brick-table>` is deprecated, use `headerBrick` instead."
            );
          }

          const useBrick = headerBrick?.useBrick || titleUseBrick;
          let data = columnTitleBrickDataMapRef.current.get(column);

          if (!data) {
            data = {
              title: columnConf.title,
            };
            columnTitleBrickDataMapRef.current.set(column, data);
          }

          columnConf.title = getCustomHeader(useBrick, data);
        }

        if (useBrick || component) {
          let itemBrickDataMap: ItemBrickDataMap;

          if (useBrick) {
            itemBrickDataMap =
              useBrickItemBrickDataMapRef.current.get(useBrick);

            if (!itemBrickDataMap) {
              itemBrickDataMap = new Map();
              useBrickItemBrickDataMapRef.current.set(
                useBrick,
                itemBrickDataMap
              );
            }
          }

          columnConf.render = getCustomComp(
            useBrick,
            component,
            itemBrickDataMap
          );
          if (optimizedColumns?.includes(column.dataIndex)) {
            // [only update when record changed](https://ant.design/components/table-cn/#%E4%B8%BA%E4%BB%80%E4%B9%88-%E6%9B%B4%E6%96%B0-state-%E4%BC%9A%E5%AF%BC%E8%87%B4%E5%85%A8%E8%A1%A8%E6%B8%B2%E6%9F%93%EF%BC%9F)
            columnConf.shouldCellUpdate = (record, prevRecord) => {
              return !isEqual(record, prevRecord);
            };
          }
        } else if (valueSuffix) {
          columnConf.render = (value) => value + valueSuffix;
        }
        if (
          !expandIconAsCell &&
          index === expandIconColumnIndex + expandIconColumnIndexOffset
        ) {
          const innerRender = columnConf.render;
          columnConf.render = function ExpandIcon(value, record, index) {
            return (
              <>
                {!record[childrenColumnName] &&
                  rowKeyExpandIconMapRef.current.get(
                    rowKey ? record[rowKey] : record
                  )}
                {innerRender ? innerRender(value, record, index) : value}
              </>
            );
          };
        }
        if (cellStatus || colSpanKey || rowSpanKey) {
          const innerRender = columnConf.render;
          columnConf.render = (value, item, index) => {
            return {
              children: innerRender ? innerRender(value, item, index) : value,
              props: {
                colSpan: item[colSpanKey],
                rowSpan: item[rowSpanKey],
                style: cellStatus && getCellStyle(cellStatus, item, value),
              },
            };
          };
        }

        if (typeof columnConf.dataIndex === "string") {
          columnConf.dataIndex = toPath(columnConf.dataIndex);
        }
        if (columnConf.verticalAlign === "top") {
          columnConf.className
            ? (columnConf.className += " alignTop")
            : (columnConf.className = "alignTop");
        }
        if (columnConf.verticalAlign === "bottom") {
          columnConf.className
            ? (columnConf.className += " alignBottom")
            : (columnConf.className = "alignBottom");
        }
        if (ellipsisInfo) {
          columnConf.className = styles.ellipsisInfoCell;
        }
        return columnConf;
      });

      if (deleteEnabled) {
        const render = (value: any, item: any, index: number) => {
          return (
            <Icon
              onClick={() => onDelete?.(index)}
              component={() => <FontAwesomeIcon icon="trash-alt" />}
              style={{ color: "#167be0" }}
            />
          );
        };

        customColumns.push({
          title: "操作",
          render,
        });
      }

      return customColumns;
    }
  }, [
    columns,
    childrenColumnName,
    expandIconAsCell,
    expandIconColumnIndex,
    expandIconColumnIndexOffset,
    rowKey,
    deleteEnabled,
    onDelete,
    ellipsisInfo,
  ]);

  const expandedRowRender = (record: Record<string, any>, index: number) => {
    let data = itemExpandedRowBrickDataMapRef.current.get(record);

    if (!data) {
      data = {
        rowData: record,
        rowIndex: index,
      };
      itemExpandedRowBrickDataMapRef.current.set(record, data);
    }

    return (
      <BrickAsComponent
        useBrick={props.expandedRowBrick.useBrick}
        data={data}
      />
    );
  };

  const components = {
    body: {
      row: DraggableBodyRow,
    },
  };

  const moveRow = (dragIndex: number, hoverIndex: number) => {
    const dragRow = data[dragIndex];
    const newData = update(data, {
      $splice: [
        [dragIndex, 1],
        [hoverIndex, 0, dragRow],
      ],
    });
    setData(newData);
    props.onDrag && props.onDrag(newData);
  };

  const onExpand = (expanded: boolean, record: Record<string, any>) => {
    props.onExpand && props.onExpand(expanded, record);
  };

  const onExpandedRowsChange = (expandedRows: React.Key[]) => {
    props.onExpandedRowsChange && props.onExpandedRowsChange(expandedRows);
  };

  const getCustomExpandIcon = (iconProps: any) => {
    const { record, expandable, expanded, onExpand } = iconProps;
    let icon = props.expandIcon?.collapsedIcon || downMenuIcon;
    let iconNode: React.ReactNode;
    if (expandable) {
      if (!expanded) {
        icon = props.expandIcon?.expandedIcon || rightMenuIcon;
      }
      iconNode = (
        <span
          className={styles.expandIconSpan}
          data-testid="expand-icon"
          onClick={(e) => {
            onExpand(record, e);
          }}
        >
          <GeneralIcon icon={icon} />
        </span>
      );
    } else {
      iconNode = (
        <span className={styles.expandIconSpan} data-testid="expand-icon">
          <span style={{ visibility: "hidden" }}>
            <GeneralIcon icon={icon} />
          </span>
        </span>
      );
    }

    if (iconNode) {
      if (!expandIconAsCell) {
        rowKeyExpandIconMapRef.current.set(
          rowKey ? record[rowKey] : record,
          iconNode
        );
      }
      return iconNode;
    } else {
      return <></>;
    }
  };

  const pickExpandProps = pickBy(
    {
      expandIconColumnIndex,
      expandIconAsCell,
      expandRowByClick: props.expandRowByClick,
      expandedRowKeys: props.expandedRowKeys,
      defaultExpandAllRows: props.defaultExpandAllRows,
    },
    (item) => !isNil(item)
  );

  let table = (
    <Table
      className={classNames(styles.brickTable, {
        [styles.expandIconCellHidden]: !expandIconAsCell,
        [styles.customDropTable]: props.tableDraggable,
        [styles.tableThTransparent]: props.thTransparent,
        [styles.zebraPatternTable]: data?.length >= 2 && props.zebraPattern,
      })}
      dataSource={data}
      {...(props.tableDraggable
        ? {
            components,
            onRow: (record, index) => ({
              index,
              moveRow: moveRow,
            }),
          }
        : {})}
      columns={customColumns}
      onChange={props.onChange}
      {...(props.expandedRowBrick
        ? {
            expandedRowRender,
          }
        : {})}
      {...pickExpandProps}
      onExpand={onExpand}
      onExpandedRowsChange={onExpandedRowsChange}
      rowKey={rowKey}
      childrenColumnName={childrenColumnName}
      rowClassName={(record, index) =>
        props.zebraPattern && index % 2 ? styles.brickTableOddRow : ""
      }
      expandIcon={getCustomExpandIcon}
      scroll={scroll}
      showHeader={showHeader}
      {...configProps}
    />
  );

  if (props.tableDraggable) {
    table = <DndProvider backend={HTML5Backend}>{table}</DndProvider>;
  }

  if (!props.showCard) {
    return table;
  }

  return <Card bordered={false}> {table} </Card>;
}
Example #21
Source File: new-canvas-controls.tsx    From utopia with MIT License 4 votes vote down vote up
NewCanvasControls = React.memo((props: NewCanvasControlsProps) => {
  const canvasControlProps = useEditorState(
    (store) => ({
      dispatch: store.dispatch,
      editor: store.editor,
      derived: store.derived,
      canvasOffset: store.editor.canvas.roundedCanvasOffset,
      controls: store.derived.controls,
      scale: store.editor.canvas.scale,
      focusedPanel: store.editor.focusedPanel,
      transientCanvasState: store.derived.transientState,
    }),
    'NewCanvasControls',
  )

  const { localSelectedViews, localHighlightedViews, setSelectedViewsLocally } =
    useLocalSelectedHighlightedViews(
      canvasControlProps.editor.selectedViews,
      canvasControlProps.editor.highlightedViews,
      canvasControlProps.transientCanvasState,
    )

  const canvasScrollAnimation = useEditorState(
    (store) => store.editor.canvas.scrollAnimation,
    'NewCanvasControls scrollAnimation',
  )

  // Somehow this being setup and hooked into the div makes the `onDrop` call
  // work properly in `editor-canvas.ts`. I blame React DnD for this.
  const dropSpec: DropTargetHookSpec<FileBrowserItemProps, 'CANVAS', unknown> = {
    accept: 'filebrowser',
    canDrop: () => true,
  }

  const [_, drop] = useDrop(dropSpec)

  const forwardedRef = React.useCallback(
    (node: ConnectableElement) => {
      return drop(node)
    },
    [drop],
  )

  if (isLiveMode(canvasControlProps.editor.mode) && !canvasControlProps.editor.keysPressed.cmd) {
    return null
  } else {
    return (
      <DndProvider backend={HTML5Backend}>
        <div
          key='canvas-controls'
          ref={forwardedRef}
          className={
            canvasControlProps.focusedPanel === 'canvas'
              ? '  canvas-controls focused '
              : ' canvas-controls '
          }
          id='canvas-controls'
          style={{
            pointerEvents: 'initial',
            position: 'absolute',
            top: 0,
            left: 0,
            transform: 'translate3d(0, 0, 0)',
            width: `100%`,
            height: `100%`,
            zoom: canvasControlProps.scale >= 1 ? `${canvasControlProps.scale * 100}%` : 1,
            cursor: props.cursor,
            visibility: canvasScrollAnimation ? 'hidden' : 'initial',
          }}
        >
          <div
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: `${canvasControlProps.scale < 1 ? 100 / canvasControlProps.scale : 100}%`,
              height: `${canvasControlProps.scale < 1 ? 100 / canvasControlProps.scale : 100}%`,
              transformOrigin: 'top left',
              transform: canvasControlProps.scale < 1 ? `scale(${canvasControlProps.scale}) ` : '',
            }}
          >
            <NewCanvasControlsInner
              windowToCanvasPosition={props.windowToCanvasPosition}
              localSelectedViews={localSelectedViews}
              localHighlightedViews={localHighlightedViews}
              setLocalSelectedViews={setSelectedViewsLocally}
              editor={canvasControlProps.editor}
              transientState={canvasControlProps.transientCanvasState}
              dispatch={canvasControlProps.dispatch}
              canvasOffset={canvasControlProps.canvasOffset}
            />
          </div>
          <ElementContextMenu contextMenuInstance='context-menu-canvas' />
        </div>
      </DndProvider>
    )
  }
})
Example #22
Source File: Preview.tsx    From datart with Apache License 2.0 4 votes vote down vote up
StoryPagePreview: React.FC<{
  orgId: string;
  storyId: string;
  allowShare?: boolean;
  allowManage?: boolean;
}> = memo(({ orgId, storyId, allowShare, allowManage }) => {
  const dispatch = useDispatch();
  const history = useHistory();
  const [currentPageIndex, setCurrentPageIndex] = useState(0);
  const publishLoading = useSelector(selectPublishLoading);
  const story = useSelector((state: { storyBoard: StoryBoardState }) =>
    makeSelectStoryBoardById(state, storyId),
  );
  const pageMap = useSelector((state: { storyBoard: StoryBoardState }) =>
    makeSelectStoryPagesById(state, storyId),
  );

  const sortedPages = useMemo(() => {
    const sortedPages = Object.values(pageMap).sort(
      (a, b) => a.config.index - b.config.index,
    );
    return sortedPages;
  }, [pageMap]);

  const onPageClick = useCallback(
    (index: number, pageId: string, multiple: boolean) => {
      setCurrentPageIndex(index);
      dispatch(
        storyActions.changePageSelected({
          storyId,
          pageId,
          multiple: false,
        }),
      );
    },
    [dispatch, storyId],
  );
  const currentPage = useMemo(() => {
    const currentPage = sortedPages[currentPageIndex];
    return currentPage;
  }, [currentPageIndex, sortedPages]);

  const toggleEdit = useCallback(() => {
    history.push(`/organizations/${orgId}/vizs/storyEditor/${storyId}`);
  }, [history, orgId, storyId]);

  const playStory = useCallback(() => {
    window.open(`storyPlayer/${storyId}`, '_blank');
  }, [storyId]);
  const { publishStory } = usePublishBoard(
    storyId,
    'STORYBOARD',
    story?.status || 0,
  );

  const { sizes, setSizes } = useSplitSizes({
    limitedSide: 0,
    range: [150, 768],
  });

  const siderDragEnd = useCallback(
    sizes => {
      setSizes(sizes);
      dispatchResize();
    },

    [setSizes],
  );

  // 点击在加载
  useEffect(() => {
    const curPage = sortedPages[currentPageIndex];
    if (!curPage || !curPage.relId || !curPage.relType) {
      return;
    }
    const { relId, relType } = curPage;
    dispatch(getPageContentDetail({ relId, relType }));
  }, [currentPageIndex, dispatch, sortedPages, pageMap]);

  useEffect(() => {
    dispatch(getStoryDetail(storyId));
  }, [dispatch, storyId]);

  useEffect(() => {
    if (sortedPages.length === 0) {
      return;
    }
    const pageId = sortedPages[currentPageIndex].id;
    dispatch(
      storyActions.changePageSelected({
        storyId,
        pageId,
        multiple: false,
      }),
    );
  }, [dispatch, currentPageIndex, sortedPages, storyId]);

  // 自动加载所有
  // useEffect(() => {
  //   sortedPages.forEach(page => {
  //     try {
  //       const { relId, relType } = page;
  //       dispatch(getPageContentDetail({ relId, relType }));
  //     } catch (error) {}
  //   });
  // }, [dispatch, sortedPages]);

  return (
    <DndProvider backend={HTML5Backend}>
      <StoryContext.Provider
        value={{
          name: story?.name,
          storyId: storyId,
          editing: false,
          orgId: orgId,
          status: 1,
          allowShare: allowShare || false,
        }}
      >
        <Wrapper>
          <StoryHeader
            orgId={orgId}
            name={story?.name}
            playStory={playStory}
            status={story?.status}
            toggleEdit={toggleEdit}
            publishLoading={publishLoading}
            onPublish={publishStory}
            allowShare={allowShare}
            allowManage={allowManage}
          />
          <Container
            sizes={sizes}
            minSize={[256, 0]}
            maxSize={[768, Infinity]}
            gutterSize={0}
            onDragEnd={siderDragEnd}
            className="datart-split"
          >
            <PageListWrapper>
              <PageThumbnailList
                sortedPages={sortedPages}
                onPageClick={onPageClick}
              />
            </PageListWrapper>
            <Content>
              {sortedPages.map(page => (
                <PreviewPage key={page.id} show={page.id === currentPage.id}>
                  <StoryPageItem
                    key={page.id}
                    page={page}
                    showZoomCtrl={true}
                    renderMode="read"
                  />
                </PreviewPage>
              ))}
            </Content>
          </Container>
        </Wrapper>
      </StoryContext.Provider>
    </DndProvider>
  );
})
Example #23
Source File: index.tsx    From datart with Apache License 2.0 4 votes vote down vote up
StoryPlayer: React.FC<{}> = memo(() => {
  useBoardSlice();
  useEditBoardSlice();
  useStoryBoardSlice();
  const dispatch = useDispatch();
  const { storyId } = useParams<{ storyId: string }>();
  useEffect(() => {
    dispatch(getStoryDetail(storyId));
  }, [dispatch, storyId]);
  const domId = useMemo(() => uuidv4(), []);
  const revealRef = useRef<any>();
  const [currentPageIndex, setCurrentPageIndex] = useState(0);
  const fullRef: RefObject<HTMLDivElement> = useRef(null);

  const storyBoard = useSelector((state: { storyBoard: StoryBoardState }) =>
    makeSelectStoryBoardById(state, storyId),
  );
  const pageMap = useSelector((state: { storyBoard: StoryBoardState }) =>
    makeSelectStoryPagesById(state, storyId),
  );

  const sortedPages = useMemo(() => {
    const sortedPages = Object.values(pageMap).sort(
      (a, b) => a.config.index - b.config.index,
    );
    return sortedPages;
  }, [pageMap]);

  const changePage = useCallback(
    e => {
      const { indexh: slideIdx } = e;
      setCurrentPageIndex(slideIdx);
      const pageId = sortedPages[slideIdx].id;
      dispatch(
        storyActions.changePageSelected({
          storyId,
          pageId,
          multiple: false,
        }),
      );
    },
    [dispatch, sortedPages, storyId],
  );

  useEffect(() => {
    if (sortedPages.length === 0) {
      return;
    }
    const pageId = sortedPages[0].id;
    dispatch(
      storyActions.changePageSelected({
        storyId,
        pageId,
        multiple: false,
      }),
    );
  }, [dispatch, sortedPages, storyId]);
  const autoSlide = useMemo(() => {
    if (storyBoard?.config?.autoPlay?.auto) {
      return storyBoard?.config?.autoPlay?.delay * 1000;
    }
    return null;
  }, [storyBoard?.config?.autoPlay?.auto, storyBoard?.config?.autoPlay?.delay]);
  useEffect(() => {
    if (sortedPages.length > 0) {
      revealRef.current = new Reveal(document.getElementById(domId), {
        hash: false,
        history: false,
        controls: true,
        controlsLayout: 'none',
        slideNumber: 'c/t',
        controlsTutorial: false,
        progress: false,
        loop: true,
        width: '100%',
        height: '100%',
        margin: 0,
        minScale: 1,
        maxScale: 1,
        autoSlide: autoSlide,
        transition: 'convex',
        // backgroundTransition: 'fade',
        transitionSpeed: 'slow',
        viewDistance: 100,
        plugins: [RevealZoom],
        keyboard: {
          27: () => {
            // disabled esc
          }, // do something custom when ESC is pressed
        },
      });
      revealRef.current?.initialize();
      if (revealRef.current) {
        revealRef.current.addEventListener('slidechanged', changePage);
      }
      return () => {
        revealRef.current.removeEventListener('slidechanged', changePage);
      };
    }
  }, [domId, changePage, sortedPages.length, autoSlide, dispatch]);

  useEffect(() => {
    const curPage = sortedPages[currentPageIndex];
    if (!curPage || !curPage.relId || !curPage.relType) {
      return;
    }
    const { relId, relType } = curPage;
    dispatch(getPageContentDetail({ relId, relType }));
  }, [currentPageIndex, dispatch, sortedPages, pageMap]);

  return (
    <DndProvider backend={HTML5Backend}>
      <Wrapper ref={fullRef}>
        <Content>
          <div id={domId} className="reveal">
            <div className="slides">
              {sortedPages.map((page, index) => (
                <StoryPageItem
                  key={page.id}
                  page={page}
                  autoFit={false}
                  renderMode="read"
                />
              ))}
            </div>
          </div>
        </Content>
      </Wrapper>
    </DndProvider>
  );
})
Example #24
Source File: index.tsx    From datart with Apache License 2.0 4 votes vote down vote up
StoryEditor: React.FC<{}> = memo(() => {
  useBoardSlice();
  useEditBoardSlice();
  useStoryBoardSlice();
  const dispatch = useDispatch();
  const { orgId, storyId } = useParams<{ orgId: string; storyId: string }>();
  useEffect(() => {
    dispatch(getStoryDetail(storyId));
  }, [dispatch, storyId]);
  const t = useI18NPrefix(`viz.board.setting`);
  const history = useHistory();
  const histState = history.location.state as any;
  const domId = useMemo(() => uuidv4(), []);
  const revealRef = useRef<any>();

  const [currentPageIndex, setCurrentPageIndex] = useState(0);
  const story = useSelector((state: { storyBoard: StoryBoardState }) =>
    makeSelectStoryBoardById(state, storyId),
  );
  const pageMap = useSelector((state: { storyBoard: StoryBoardState }) =>
    makeSelectStoryPagesById(state, storyId),
  );

  const sortedPages = useMemo(() => {
    const sortedPages = Object.values(pageMap).sort(
      (a, b) => a.config.index - b.config.index,
    );
    return sortedPages;
  }, [pageMap]);

  const changePage = useCallback(
    e => {
      const { indexh: slideIdx } = e;
      setCurrentPageIndex(slideIdx);
      const pageId = sortedPages[slideIdx].id;
      dispatch(
        storyActions.changePageSelected({
          storyId,
          pageId,
          multiple: false,
        }),
      );
    },
    [dispatch, sortedPages, storyId],
  );

  const onPageClick = useCallback(
    (index: number, pageId: string, multiple: boolean) => {
      if (!multiple) {
        revealRef.current.slide(index);
      } else {
        dispatch(
          storyActions.changePageSelected({
            storyId,
            pageId,
            multiple: true,
          }),
        );
      }
    },
    [dispatch, storyId],
  );

  const { sizes, setSizes } = useSplitSizes({
    limitedSide: 0,
    range: [150, 768],
  });

  const siderDragEnd = useCallback(
    sizes => {
      setSizes(sizes);
      dispatchResize();
    },

    [setSizes],
  );

  const onDeletePages = useCallback(
    (pageIds: string[]) => {
      Modal.confirm({
        title: pageIds.length > 1 ? t('delPagesTip') : t('delPageTip'),
        onOk: () => {
          pageIds.forEach(pageId => {
            dispatch(deleteStoryPage({ storyId, pageId }));
          });
        },
      });
    },
    [dispatch, storyId, t],
  );

  useEffect(() => {
    if (sortedPages.length === 0) {
      return;
    }
    if (!sortedPages[currentPageIndex]) {
      return;
    }
    const pageId = sortedPages[currentPageIndex].id;
    dispatch(
      storyActions.changePageSelected({
        storyId,
        pageId,
        multiple: false,
      }),
    );
  }, [dispatch, currentPageIndex, sortedPages, storyId]);

  const onCloseEditor = useCallback(() => {
    history.push(`/organizations/${orgId}/vizs/${storyId}`);
  }, [history, orgId, storyId]);
  useEffect(() => {
    if (sortedPages.length > 0) {
      revealRef.current = new Reveal(document.getElementById(domId), {
        hash: false,
        history: false,
        controls: false,
        controlsLayout: 'none',
        slideNumber: 'c/t',
        controlsTutorial: false,
        progress: false,
        loop: true,
        width: '100%',
        height: '100%',
        margin: 0,
        minScale: 1,
        maxScale: 1,
        autoSlide: null,
        transition: 'convex',
        // backgroundTransition: 'fade',
        transitionSpeed: 'slow',
        viewDistance: 100,
        plugins: [RevealZoom],
        keyboard: {
          70: () => {},
        },
      });
      revealRef.current?.initialize();
      if (revealRef.current) {
        revealRef.current.addEventListener('slidechanged', changePage);
      }
      return () => {
        revealRef.current.removeEventListener('slidechanged', changePage);
      };
    }

    // "none" | "fade" | "slide" | "convex" | "concave" | "zoom"
  }, [domId, changePage, sortedPages.length]);

  useEffect(() => {
    const curPage = sortedPages[currentPageIndex];
    if (!curPage || !curPage.relId || !curPage.relType) {
      return;
    }
    const { relId, relType } = curPage;
    dispatch(getPageContentDetail({ relId, relType }));
  }, [currentPageIndex, dispatch, sortedPages]);

  const addPages = useCallback(async () => {
    if (histState && histState.addDashboardId) {
      await dispatch(
        addStoryPages({ storyId, relIds: [histState.addDashboardId] }),
      );

      //react router remove location state
      if (history.location.state && histState.transaction) {
        let state = { ...histState };
        delete state.transaction;
        history.replace({ ...history.location, state });
      }
    }
  }, [dispatch, histState, storyId, history]);

  useEffect(() => {
    addPages();
  }, [addPages]);

  return (
    <DndProvider backend={HTML5Backend}>
      <StoryContext.Provider
        value={{
          name: story.name,
          storyId: storyId,
          editing: false,
          orgId: orgId,
          status: 1,
          allowShare: false,
        }}
      >
        <Wrapper>
          <StoryToolBar onCloseEditor={onCloseEditor} />
          <Container
            sizes={sizes}
            minSize={[256, 0]}
            maxSize={[768, Infinity]}
            gutterSize={0}
            onDragEnd={siderDragEnd}
            className="datart-split"
          >
            <PageListWrapper>
              <PageThumbnailList
                sortedPages={sortedPages}
                onPageClick={onPageClick}
                onDeletePages={onDeletePages}
              />
            </PageListWrapper>
            <Content>
              <div id={domId} className="reveal">
                <div className="slides">
                  {sortedPages.map((page, index) => (
                    <StoryPageItem
                      key={page.id}
                      page={page}
                      renderMode="read"
                    />
                  ))}
                </div>
              </div>
            </Content>
          </Container>
        </Wrapper>
      </StoryContext.Provider>
    </DndProvider>
  );
})
Example #25
Source File: StoryPlayerForShare.tsx    From datart with Apache License 2.0 4 votes vote down vote up
StoryPlayerForShare: React.FC<{
  storyBoard: StoryBoard;
  shareToken: string;
}> = memo(({ storyBoard, shareToken }) => {
  const { id: storyId } = storyBoard;
  const domId = useMemo(() => uuidv4(), []);
  const revealRef = useRef<any>();
  const dispatch = useDispatch();
  const [currentPageIndex, setCurrentPageIndex] = useState(0);
  const fullRef: RefObject<HTMLDivElement> = useRef(null);
  const pageMap = useSelector((state: { storyBoard: StoryBoardState }) =>
    makeSelectStoryPagesById(state, storyId),
  );
  const subVizTokenMap = useSelector(selectSubVizTokenMap);

  const sortedPages = useMemo(() => {
    const sortedPages = Object.values(pageMap).sort(
      (a, b) => a.config.index - b.config.index,
    );
    return sortedPages;
  }, [pageMap]);

  const changePage = useCallback(
    e => {
      const { indexh: slideIdx } = e;
      setCurrentPageIndex(slideIdx);
      const pageId = sortedPages[slideIdx].id;
      dispatch(
        storyActions.changePageSelected({
          storyId,
          pageId,
          multiple: false,
        }),
      );
    },
    [dispatch, sortedPages, storyId],
  );

  useEffect(() => {
    if (sortedPages.length === 0) {
      return;
    }
    const pageId = sortedPages[0].id;
    dispatch(
      storyActions.changePageSelected({
        storyId,
        pageId,
        multiple: false,
      }),
    );
  }, [dispatch, sortedPages, storyId]);
  const autoSlide = useMemo(() => {
    if (storyBoard?.config?.autoPlay?.auto) {
      return storyBoard?.config?.autoPlay?.delay * 1000;
    }
    return null;
  }, [storyBoard?.config?.autoPlay?.auto, storyBoard?.config?.autoPlay?.delay]);
  useEffect(() => {
    if (sortedPages.length > 0) {
      revealRef.current = new Reveal(document.getElementById(domId), {
        hash: false,
        history: false,
        controls: true,
        controlsLayout: 'none',
        slideNumber: 'c/t',
        controlsTutorial: false,
        progress: false,
        loop: true,
        width: '100%',
        height: '100%',
        margin: 0,
        minScale: 1,
        maxScale: 1,
        autoSlide: autoSlide,
        transition: 'convex',
        // backgroundTransition: 'fade',
        transitionSpeed: 'slow',
        viewDistance: 100,
        plugins: [RevealZoom],
        keyboard: {
          27: () => {
            // disabled esc
          }, // do something custom when ESC is pressed
        },
      });
      revealRef.current?.initialize();
      if (revealRef.current) {
        revealRef.current.addEventListener('slidechanged', changePage);
      }
      return () => {
        revealRef.current.removeEventListener('slidechanged', changePage);
      };
    }
  }, [domId, changePage, sortedPages.length, autoSlide, dispatch]);

  useEffect(() => {
    const curPage = sortedPages[currentPageIndex];
    if (!curPage || !curPage.relId || !curPage.relType) {
      return;
    }
    const { relId, relType, id } = curPage;
    const vizToken = subVizTokenMap?.[id];
    if (!vizToken) {
      return;
    }
    dispatch(
      getPageContentDetail({
        shareToken,
        relId,
        relType,
        vizToken: vizToken,
      }),
    );
  }, [
    currentPageIndex,
    dispatch,
    sortedPages,
    pageMap,
    subVizTokenMap,
    shareToken,
  ]);

  return (
    <DndProvider backend={HTML5Backend}>
      <Wrapper ref={fullRef}>
        <Content>
          <div id={domId} className="reveal">
            <div className="slides">
              {sortedPages.map(page => (
                <StoryPageItem key={page.id} page={page} />
              ))}
            </div>
          </div>
        </Content>
      </Wrapper>
    </DndProvider>
  );
})
Example #26
Source File: BoardForShare.tsx    From datart with Apache License 2.0 4 votes vote down vote up
BoardForShare: React.FC<ShareBoardProps> = memo(
  ({
    dashboard,
    renderMode,
    filterSearchUrl,
    allowDownload,
    loadVizData,
    onMakeShareDownloadDataTask,
    onLoadShareTask,
    onDownloadFile,
  }) => {
    const dispatch = useDispatch();

    const shareBoardInfo = useSelector(selectShareBoardInfo);
    const { needFetchItems, hasFetchItems, boardWidthHeight } = shareBoardInfo;

    const [allItemFetched, setAllItemFetched] = useState(false);

    useEffect(() => {
      if (needFetchItems.length === hasFetchItems.length) {
        setAllItemFetched(true);
      }
    }, [hasFetchItems, needFetchItems]);

    // for sever Browser
    const { taskW, taskH } = useMemo(() => {
      const taskWH = {
        taskW: boardWidthHeight[0] || 0,
        taskH: boardWidthHeight[1] || 0,
      };
      if (dashboard) {
        if (dashboard?.config?.type === 'free') {
          const { width, height } = dashboard.config;
          const ratio = width / (height || 1) || 1;
          const targetHeight = taskWH.taskW / ratio;
          taskWH.taskH = targetHeight;
        }
      }
      return taskWH;
    }, [boardWidthHeight, dashboard]);

    const boardDownLoadAction = useCallback(
      (params: { boardId: string }) => async dispatch => {
        const { boardId } = params;
        const { requestParams, fileName } = await dispatch(
          getBoardDownloadParams({ boardId }),
        );
        onMakeShareDownloadDataTask(requestParams, fileName);
      },
      [onMakeShareDownloadDataTask],
    );

    const onShareDownloadData = useCallback(() => {
      dispatch(boardDownLoadAction({ boardId: dashboard.id }));
    }, [boardDownLoadAction, dashboard.id, dispatch]);

    const viewBoard = useMemo(() => {
      let boardType = dashboard?.config?.type;
      if (!dashboard || !boardType) return null;
      return (
        <BoardInitProvider
          board={dashboard}
          editing={false}
          autoFit={false}
          renderMode={renderMode}
          allowDownload={allowDownload}
        >
          <Wrapper>
            <TitleForShare
              onShareDownloadData={onShareDownloadData}
              loadVizData={loadVizData}
            >
              <DownloadTaskContainer
                onLoadTasks={onLoadShareTask}
                onDownloadFile={onDownloadFile}
              ></DownloadTaskContainer>
            </TitleForShare>
            {boardType === 'auto' && <AutoBoardCore boardId={dashboard.id} />}
            {boardType === 'free' && <FreeBoardCore boardId={dashboard.id} />}
            <FullScreenPanel />
          </Wrapper>
        </BoardInitProvider>
      );
    }, [
      allowDownload,
      dashboard,
      loadVizData,
      onDownloadFile,
      onLoadShareTask,
      onShareDownloadData,
      renderMode,
    ]);

    return (
      <DndProvider backend={HTML5Backend}>
        {viewBoard}
        <HeadlessBrowserIdentifier
          renderSign={allItemFetched}
          width={Number(taskW)}
          height={Number(taskH) + TitleHeight}
        />
      </DndProvider>
    );
  },
)
Example #27
Source File: index.tsx    From datart with Apache License 2.0 4 votes vote down vote up
BoardEditor: React.FC<{
  boardId: string;
}> = memo(({ boardId }) => {
  const dispatch = useDispatch();
  const history = useHistory();
  const board = useSelector(selectEditBoard);
  const boardLoading = useSelector(selectEditBoardLoading);
  const boardChartEditorProps = useSelector(selectBoardChartEditorProps);

  const onCloseChartEditor = useCallback(() => {
    dispatch(editDashBoardInfoActions.changeChartEditorProps(undefined));
  }, [dispatch]);

  const onSaveToWidget = useCallback(
    (chartType: WidgetContentChartType, dataChart: DataChart, view) => {
      const widgetId = boardChartEditorProps?.widgetId!;
      dispatch(editHasChartWidget({ widgetId, dataChart, view }));
      onCloseChartEditor();
      dispatch(addVariablesToBoard(view.variables));
    },
    [boardChartEditorProps?.widgetId, dispatch, onCloseChartEditor],
  );

  const boardEditor = useMemo(() => {
    if (!board.id) return null;
    if (board?.id !== boardId) {
      return null;
    }
    const boardType = board.config?.type;

    return (
      <BoardInitProvider
        board={board}
        editing={true}
        autoFit={false}
        allowDownload={false}
        allowShare={false}
        allowManage={false}
        renderMode="edit"
      >
        <EditorHeader />
        {boardType === 'auto' && <AutoEditor />}
        {boardType === 'free' && <FreeEditor />}
        <ControllerWidgetPanel />
        <LinkagePanel />
        <SettingJumpModal />
        {boardChartEditorProps && (
          <ChartEditor
            {...boardChartEditorProps}
            onClose={onCloseChartEditor}
            onSaveInWidget={onSaveToWidget}
          />
        )}
      </BoardInitProvider>
    );
  }, [
    boardChartEditorProps,
    board,
    boardId,
    onCloseChartEditor,
    onSaveToWidget,
  ]);
  const initialization = useCallback(async () => {
    await dispatch(fetchEditBoardDetail(boardId));
    const histState = history.location.state as any;
    try {
      if (histState?.widgetInfo) {
        const widgetInfo = JSON.parse(histState.widgetInfo);

        if (widgetInfo) {
          let subType: 'widgetChart' | 'dataChart' = 'dataChart';
          if (!widgetInfo.dataChart.id) {
            widgetInfo.dataChart.id = 'widget_' + uuidv4();
            subType = 'widgetChart';
          }
          dispatch(
            addChartWidget({
              boardId,
              chartId: widgetInfo.dataChart.id,
              boardType: widgetInfo.dashboardType,
              dataChart: widgetInfo.dataChart,
              view: widgetInfo.dataview,
              subType: subType,
            }),
          );
        }
      }
    } catch (error) {
      console.log(error);
    }
  }, [dispatch, history.location.state, boardId]);

  useEffect(() => {
    initialization();
    return () => {
      // fix issue: #800
      onCloseChartEditor();
      dispatch(clearEditBoardState());
      //销毁时  更新view界面数据
      dispatch(fetchBoardDetail({ dashboardRelId: boardId }));
      //
      boardDrillManager.clearMapByBoardId(EDIT_PREFIX + boardId);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [onCloseChartEditor]);

  return (
    <Wrapper>
      <DndProvider backend={HTML5Backend}>
        {boardEditor}
        {boardLoading && <BoardLoading />}
      </DndProvider>
    </Wrapper>
  );
})
Example #28
Source File: index.tsx    From firetable with Apache License 2.0 4 votes vote down vote up
export default function Table() {
  const classes = useStyles();

  const {
    tableState,
    tableActions,
    dataGridRef,
    sideDrawerRef,
    updateCell,
  } = useFiretableContext();
  const { userDoc } = useAppContext();

  const userDocHiddenFields =
    userDoc.state.doc?.tables?.[formatSubTableName(tableState?.tablePath)]
      ?.hiddenFields ?? [];

  const [columns, setColumns] = useState<FiretableColumn[]>([]);

  useEffect(() => {
    if (!tableState?.loadingColumns && tableState?.columns) {
      const _columns = _orderBy(
        Object.values(tableState?.columns).filter(
          (column: any) => !column.hidden && column.key
        ),
        "index"
      )
        .map((column: any) => ({
          draggable: true,
          editable: true,
          resizable: true,
          frozen: column.fixed,
          headerRenderer: ColumnHeader,
          formatter:
            getFieldProp(
              "TableCell",
              column.config?.renderFieldType ?? column.type
            ) ??
            function InDev() {
              return null;
            },
          editor:
            getFieldProp(
              "TableEditor",
              column.config?.renderFieldType ?? column.type
            ) ??
            function InDev() {
              return null;
            },
          ...column,
          width: column.width ? (column.width > 380 ? 380 : column.width) : 150,
        }))
        .filter((column) => !userDocHiddenFields.includes(column.key));

      setColumns([
        // TODO: ENABLE ONCE BULK ACTIONS READY
        // SelectColumn,
        ..._columns,
        {
          isNew: true,
          key: "new",
          name: "Add column",
          type: FieldType.last,
          index: _columns.length ?? 0,
          width: 204,
          headerRenderer: FinalColumnHeader,
          headerCellClass: "final-column-header",
          cellClass: "final-column-cell",
          formatter: FinalColumn,
          editable: false,
        },
      ]);
    }
  }, [
    tableState?.loadingColumns,
    tableState?.columns,
    JSON.stringify(userDocHiddenFields),
  ]);

  const rows =
    useMemo(
      () =>
        tableState?.rows.map((row) =>
          columns.reduce(
            (acc, currColumn) => {
              if ((currColumn.key as string).includes(".")) {
                return {
                  ...acc,
                  [currColumn.key]: _get(row, currColumn.key),
                };
              } else return acc;
            },
            { ...row, id: row.id as string, ref: row.ref }
          )
        ),
      [columns, tableState?.rows]
    ) ?? [];

  const rowsContainerRef = useRef<HTMLDivElement>(null);
  const [selectedRowsSet, setSelectedRowsSet] = useState<Set<React.Key>>();
  const [selectedRows, setSelectedRows] = useState<any[]>([]);
  // Gets more rows when scrolled down.
  // https://github.com/adazzle/react-data-grid/blob/ead05032da79d7e2b86e37cdb9af27f2a4d80b90/stories/demos/AllFeatures.tsx#L60
  const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {
    const target = event.target as HTMLDivElement;
    const offset = 800;
    const isAtBottom =
      target.clientHeight + target.scrollTop >= target.scrollHeight - offset;

    if (!isAtBottom) return;

    // Prevent calling more rows when they’ve already been called
    if (tableState!.loadingRows) return;

    // Call for 30 more rows. Note we don’t know here if there are no more
    // rows left in the database. This is done in the useTable hook.
    tableActions?.row.more(30);
  };

  const windowSize = useWindowSize();
  if (!windowSize || !windowSize.height) return <></>;

  if (!tableActions || !tableState) return <></>;

  const rowHeight = tableState.config.rowHeight;

  return (
    <>
      {/* <Suspense fallback={<Loading message="Loading header" />}>
        <Hotkeys selectedCell={selectedCell} />
      </Suspense> */}
      <div className={classes.tableWrapper} ref={rowsContainerRef}>
        <TableHeader
          rowHeight={rowHeight}
          updateConfig={tableActions.table.updateConfig}
        />

        {!tableState.loadingColumns ? (
          <DndProvider backend={HTML5Backend}>
            <DataGrid
              onColumnResize={tableActions.column.resize}
              onScroll={handleScroll}
              ref={dataGridRef}
              rows={rows}
              columns={columns}
              rowHeight={rowHeight ?? 43}
              headerRowHeight={44}
              className="rdg-light" // Handle dark mode in MUI theme
              cellNavigationMode="LOOP_OVER_ROW"
              rowKeyGetter={rowKeyGetter}
              selectedRows={selectedRowsSet}
              onSelectedRowsChange={(newSelectedSet) => {
                const newSelectedArray = newSelectedSet
                  ? [...newSelectedSet]
                  : [];
                const prevSelectedRowsArray = selectedRowsSet
                  ? [...selectedRowsSet]
                  : [];
                const addedSelections = _difference(
                  newSelectedArray,
                  prevSelectedRowsArray
                );
                const removedSelections = _difference(
                  prevSelectedRowsArray,
                  newSelectedArray
                );
                addedSelections.forEach((id) => {
                  const newRow = _find(rows, { id });
                  setSelectedRows([...selectedRows, newRow]);
                });
                removedSelections.forEach((rowId) => {
                  setSelectedRows(
                    selectedRows.filter((row) => row.id !== rowId)
                  );
                });
                setSelectedRowsSet(newSelectedSet);
              }}
              onRowsChange={(rows) => {
                //console.log('onRowsChange',rows)
              }}
              onFill={(e) => {
                console.log("onFill", e);
                const { columnKey, sourceRow, targetRows } = e;
                if (updateCell)
                  targetRows.forEach((row) =>
                    updateCell(row.ref, columnKey, sourceRow[columnKey])
                  );
                return [];
              }}
              onPaste={(e) => {
                const copiedValue = e.sourceRow[e.sourceColumnKey];
                if (updateCell) {
                  updateCell(e.targetRow.ref, e.targetColumnKey, copiedValue);
                }
              }}
              onRowClick={(rowIdx, column) => {
                if (sideDrawerRef?.current) {
                  sideDrawerRef.current.setCell({
                    row: rowIdx,
                    column: column.key as string,
                  });
                }
              }}
            />
          </DndProvider>
        ) : (
          <Loading message="Fetching columns" />
        )}
      </div>

      <ColumnMenu />
      <BulkActions
        selectedRows={selectedRows}
        columns={columns}
        clearSelection={() => {
          setSelectedRowsSet(new Set());
          setSelectedRows([]);
        }}
      />
    </>
  );
}
Example #29
Source File: index.tsx    From datart with Apache License 2.0 4 votes vote down vote up
Board: FC<BoardProps> = memo(
  ({
    id,
    hideTitle,
    fetchData = true,
    renderMode,
    filterSearchUrl,
    allowDownload,
    allowShare,
    allowManage,
    autoFit,
    showZoomCtrl,
  }) => {
    const boardId = id;
    const dispatch = useDispatch();
    const editingBoard = useSelector(selectEditBoard);
    const readBoardHide = useMemo(
      () => editingBoard?.id === boardId,
      [boardId, editingBoard.id],
    );
    const { ref, width, height } = useResizeObserver<HTMLDivElement>({
      refreshMode: 'debounce',
      refreshRate: 2000,
    });

    const dashboard = useSelector((state: { board: BoardState }) =>
      makeSelectBoardConfigById()(state, boardId),
    );

    const searchParams = useMemo(() => {
      return filterSearchUrl
        ? urlSearchTransfer.toParams(filterSearchUrl)
        : undefined;
    }, [filterSearchUrl]);

    const viewBoard = useMemo(() => {
      let boardType = dashboard?.config?.type;
      if (dashboard && boardType) {
        return (
          <div className="board-provider">
            <BoardInitProvider
              board={dashboard}
              editing={false}
              autoFit={autoFit}
              renderMode={renderMode}
              allowDownload={allowDownload}
              allowShare={allowShare}
              allowManage={allowManage}
            >
              {!hideTitle && <TitleHeader />}
              {boardType === 'auto' && <AutoBoardCore boardId={dashboard.id} />}
              {boardType === 'free' && (
                <FreeBoardCore
                  boardId={dashboard.id}
                  showZoomCtrl={showZoomCtrl}
                />
              )}
              <FullScreenPanel />
            </BoardInitProvider>
          </div>
        );
      } else {
        return <BoardLoading />;
      }
    }, [
      dashboard,
      autoFit,
      renderMode,
      allowDownload,
      allowShare,
      allowManage,
      hideTitle,
      showZoomCtrl,
    ]);

    useEffect(() => {
      if (width! > 0 && height! > 0 && dashboard?.id && !readBoardHide) {
        dispatch(
          boardActions.changeBoardVisible({ id: dashboard?.id, visible: true }),
        );
      } else {
        dispatch(
          boardActions.changeBoardVisible({
            id: dashboard?.id as string,
            visible: false,
          }),
        );
      }
    }, [readBoardHide, dashboard?.id, dispatch, height, width]);

    useEffect(() => {
      if (boardId && fetchData) {
        dispatch(
          fetchBoardDetail({
            dashboardRelId: boardId,
            filterSearchParams: searchParams,
          }),
        );
      }

      // 销毁组件 清除该对象缓存
      return () => {
        dispatch(boardActions.clearBoardStateById(boardId));
        boardDrillManager.clearMapByBoardId(boardId);
      };
    }, [boardId, dispatch, fetchData, searchParams]);

    return (
      <Wrapper ref={ref} className="dashboard-box">
        <DndProvider backend={HTML5Backend}>{viewBoard}</DndProvider>
      </Wrapper>
    );
  },
)