react-virtualized#AutoSizer TypeScript Examples

The following examples show how to use react-virtualized#AutoSizer. 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: list.tsx    From gio-design with Apache License 2.0 6 votes vote down vote up
private renderList = () => {
    const { height, disabledOptions, rowHeight, options, value, prefixCls } = this.props;
    const getRowHeight = ({ index }: { index: number }) => {
      if (typeof rowHeight === 'function') {
        return rowHeight(options[index]);
      }
      return rowHeight;
    };
    return (
      <AutoSizer style={{ width: '100%', height: '100%' }}>
        {({ width }) => (
          <List
            value={value}
            width={width}
            height={2000}
            style={{ height: height || '100%', overflow: 'auto', marginBottom: '-4px' }}
            rowCount={options.length}
            rowHeight={typeof rowHeight === 'function' ? getRowHeight : this._cache.rowHeight}
            deferredMeasurementCache={this._cache}
            rowRenderer={this.renderListItem(options)}
            disabledOptions={disabledOptions}
            className={`${prefixCls}-list`}
          />
        )}
      </AutoSizer>
    );
  };
Example #2
Source File: LogViewer.tsx    From kfp-tekton-backend with Apache License 2.0 6 votes vote down vote up
public render(): JSX.Element {
    return (
      <AutoSizer>
        {({ height, width }) => (
          <List
            id='logViewer'
            containerStyle={listContainerStyleOverride}
            width={width}
            height={height}
            rowCount={this.props.logLines.length}
            rowHeight={15}
            className={css.root}
            ref={this._rootRef}
            overscanIndicesGetter={overscanOnBothDirections}
            overscanRowCount={
              400 /* make this large, so selecting maximum 400 lines is supported */
            }
            rowRenderer={this._rowRenderer.bind(this)}
            onScroll={this.handleScroll}
          />
        )}
      </AutoSizer>
    );
  }
Example #3
Source File: FilePreview.tsx    From yana with MIT License 6 votes vote down vote up
FilePreview: React.FC<{
  file: UploadEntity;
  onRemove: () => any;
  onChange: (changed: UploadEntity) => any;
}> = props => {
  const { file } = props;

  return (
    <div className={styles.container}>
      <div>
        {['image/png'].includes(file.file.type) ? (
          <img src={'file:///' + file.file.path.replace(/\\/g, '/')} className={styles.previewImage} />
        ) : (
          <div className={styles.previewImage} />
        )}
      </div>

      <div className={styles.content}>
        <AutoSizer>
          {({ width, height }) => (
            <div>
              <h2 style={{ width, height: '20px' }}>
                <EditableText defaultValue={file.name} onConfirm={name => props.onChange({ ...props.file, name })} />
              </h2>
              <p className={Classes.TEXT_MUTED + ' ' + Classes.TEXT_OVERFLOW_ELLIPSIS} style={{ width }}>
                {file.file.path}
              </p>
            </div>
          )}
        </AutoSizer>
      </div>
    </div>
  );
}
Example #4
Source File: BatchList.tsx    From lightning-terminal with MIT License 5 votes vote down vote up
BatchList: React.FC = () => {
  const { batchStore } = useStore();

  const { Wrapper, Content, More } = Styled;
  return (
    <Wrapper>
      <BatchRowHeader />
      <Content>
        <AutoSizer>
          {({ width, height }) => (
            <Observer>
              {() => (
                <List
                  rowCount={batchStore.sortedBatches.length}
                  rowHeight={ROW_HEIGHT}
                  rowRenderer={({ index, key, style }) => (
                    <BatchRow
                      key={key}
                      style={style}
                      batch={batchStore.sortedBatches[index]}
                    />
                  )}
                  width={width}
                  height={height}
                />
              )}
            </Observer>
          )}
        </AutoSizer>
      </Content>
      {batchStore.hasMoreBatches && (
        <More>
          {batchStore.loading ? (
            <LoaderLines />
          ) : (
            <Button
              borderless
              ghost
              onClick={batchStore.fetchBatches}
              disabled={batchStore.loading}
            >
              Load Older Batches
            </Button>
          )}
        </More>
      )}
    </Wrapper>
  );
}
Example #5
Source File: IconList.tsx    From iconsax-react with MIT License 5 votes vote down vote up
IconList = () => {
  const [filtered, setFiltered] = useState<IIconsArray[]>(icons)
  const [numColumns, setNumColumns] = useState(1)

  const query = searchStore((state) => state.query)

  const onResize = ({ width }: { width: number }) => {
    if (width <= 576) {
      setNumColumns(Math.floor(width / ICON_CONTAINER_SIZE))
    } else {
      setNumColumns(Math.floor(width / ICON_CONTAINER_SIZE))
    }
  }
  useEffect(() => {
    console.log(query)
    const f =
      icons.filter((x) =>
        x.name.toLowerCase().includes(query!.toLowerCase())
      ) || []
    setFiltered(f)
  }, [query])
  return (
    <div className="container flex justify-center m-auto min-h-[400px]">
      <div className="w-full relative mb-10">
        {filtered.length > 0 ? (
          <WindowScroller>
            {({ height, isScrolling, onChildScroll, scrollTop }) => (
              <AutoSizer disableHeight onResize={onResize}>
                {({ width }) => (
                  <List
                    tabIndex={-1}
                    autoHeight
                    width={width}
                    height={height}
                    isScrolling={isScrolling}
                    onScroll={onChildScroll}
                    scrollTop={scrollTop}
                    rowCount={Math.ceil(filtered.length / numColumns)}
                    rowHeight={ICON_CONTAINER_SIZE + 10}
                    rowRenderer={({ key, index: rowIndex, style }) => (
                      <div
                        key={key}
                        className="grid place-items-center"
                        style={{
                          ...style,
                          gridTemplateColumns: `repeat(${numColumns}, 1fr)`,
                        }}
                      >
                        {Array.from(
                          { length: numColumns },
                          (_, columnIndex) => {
                            const icon =
                              filtered[rowIndex * numColumns + columnIndex]
                            if (!icon) {
                              return null
                            }
                            return (
                              <IconItem name={icon.name} Icon={icon.Icon} />
                            )
                          }
                        )}
                      </div>
                    )}
                  />
                )}
              </AutoSizer>
            )}
          </WindowScroller>
        ) : (
          <Empty />
        )}
      </div>
    </div>
  )
}
Example #6
Source File: VirtualizedList.tsx    From console with GNU Affero General Public License v3.0 5 votes vote down vote up
VirtualizedList = ({
  rowRenderFunction,
  totalItems,
  defaultHeight,
}: IVirtualizedList) => {
  const isItemLoaded = (index: any) => !!itemStatusMap[index];

  const loadMoreItems = (startIndex: number, stopIndex: number) => {
    for (let index = startIndex; index <= stopIndex; index++) {
      itemStatusMap[index] = LOADING;
    }

    for (let index = startIndex; index <= stopIndex; index++) {
      itemStatusMap[index] = LOADED;
    }
  };

  const RenderItemLine = ({ index, style }: any) => {
    return <div style={style}>{rowRenderFunction(index)}</div>;
  };

  return (
    <Fragment>
      <InfiniteLoader
        isItemLoaded={isItemLoaded}
        loadMoreItems={loadMoreItems}
        itemCount={totalItems}
      >
        {({ onItemsRendered, ref }) => (
          // @ts-ignore
          <AutoSizer>
            {({ width, height }) => {
              return (
                <List
                  itemSize={defaultHeight || 220}
                  height={height}
                  itemCount={totalItems}
                  width={width}
                  ref={ref}
                  onItemsRendered={onItemsRendered}
                >
                  {RenderItemLine}
                </List>
              );
            }}
          </AutoSizer>
        )}
      </InfiniteLoader>
    </Fragment>
  );
}
Example #7
Source File: SearchResults.tsx    From yana with MIT License 5 votes vote down vote up
SearchResults: React.FC<{
  onClickItem?: (item: DataItem) => void;
  hideItemIds: string[];
  hiddenSearch: SearchQuery;
}> = props => {
  const search = useSearchBar();
  const searchResult = useDataSearch({ ...search.searchQuery, ...props.hiddenSearch }, 30);

  const items = searchResult.items.filter(i => !props.hideItemIds.includes(i.id));

  return (
    <AutoSizer>
      {({ width, height }) => {
        return (
          <List
            rowCount={items.length + (searchResult.nextPageAvailable ? 50 : 0)}
            rowHeight={OVERLAY_SEARCH_ITEM_HEIGHT}
            width={width}
            height={height}
            containerStyle={{ paddingRight: '10px' }}
            rowRenderer={cellProps => {
              const itemId = cellProps.index;

              if (itemId >= items.length) {
                if (searchResult.nextPageAvailable) {
                  searchResult.fetchNextPage();
                  return (
                    <ResultItem
                      key={cellProps.key}
                      containerStyle={cellProps.style}
                      icon={<Spinner size={16} />}
                      title="Loading..."
                      meta=""
                    />
                  );
                } else {
                  return null;
                }
              }

              const item = items[itemId];

              return (
                <ResultItem
                  key={cellProps.key}
                  containerStyle={cellProps.style}
                  icon={(item.icon || (item.kind === DataItemKind.Collection ? 'folder-open' : 'document')) as any}
                  title={item.name}
                  meta={ago(new Date(item.lastChange))}
                  onClick={() => props.onClickItem?.(item)}
                  dataItem={item}
                />
              );
            }}
          />
        );
      }}
    </AutoSizer>
  );
}
Example #8
Source File: ChannelList.tsx    From lightning-terminal with MIT License 5 votes vote down vote up
ChannelList: React.FC = () => {
  const { buildSwapView } = useStore();

  const { Wrapper, ListContainer } = Styled;
  return (
    <Wrapper data-tour="channel-list">
      <ChannelRowHeader />
      <ListContainer>
        <AutoSizer disableHeight>
          {({ width }) => (
            <WindowScroller>
              {({ height, isScrolling, onChildScroll, scrollTop, registerChild }) => (
                <Observer>
                  {() => (
                    <div ref={ref => ref && registerChild(ref)}>
                      <List
                        autoHeight
                        height={height}
                        isScrolling={isScrolling}
                        onScroll={onChildScroll}
                        rowCount={buildSwapView.channels.length}
                        rowHeight={ROW_HEIGHT}
                        rowRenderer={({ index, key, style }) => (
                          <ChannelRow
                            key={key}
                            style={style}
                            channel={buildSwapView.channels[index]}
                          />
                        )}
                        scrollTop={scrollTop}
                        width={width}
                      />
                    </div>
                  )}
                </Observer>
              )}
            </WindowScroller>
          )}
        </AutoSizer>
      </ListContainer>
    </Wrapper>
  );
}
Example #9
Source File: HistoryList.tsx    From lightning-terminal with MIT License 5 votes vote down vote up
HistoryList: React.FC = () => {
  const { swapStore } = useStore();

  const { Wrapper } = Styled;
  return (
    <Wrapper>
      <HistoryRowHeader />
      <ListContainer>
        <AutoSizer disableHeight>
          {({ width }) => (
            <WindowScroller>
              {({ height, isScrolling, onChildScroll, scrollTop, registerChild }) => (
                <Observer>
                  {() => (
                    <div ref={ref => ref && registerChild(ref)}>
                      <List
                        autoHeight
                        height={height}
                        isScrolling={isScrolling}
                        onScroll={onChildScroll}
                        rowCount={swapStore.sortedSwaps.length}
                        rowHeight={ROW_HEIGHT}
                        rowRenderer={({ index, key, style }) => (
                          <HistoryRow
                            key={key}
                            style={style}
                            swap={swapStore.sortedSwaps[index]}
                          />
                        )}
                        scrollTop={scrollTop}
                        width={width}
                      />
                    </div>
                  )}
                </Observer>
              )}
            </WindowScroller>
          )}
        </AutoSizer>
      </ListContainer>
    </Wrapper>
  );
}
Example #10
Source File: SessionList.tsx    From lightning-terminal with MIT License 5 votes vote down vote up
SessionList: React.FC = () => {
  const { sessionStore } = useStore();

  if (sessionStore.sortedSessions.length === 0) return null;

  const { Wrapper } = Styled;
  return (
    <Wrapper>
      <SessionRowHeader />
      <ListContainer>
        <AutoSizer disableHeight>
          {({ width }) => (
            <WindowScroller>
              {({ height, isScrolling, onChildScroll, scrollTop, registerChild }) => (
                <Observer>
                  {() => (
                    <div ref={ref => ref && registerChild(ref)}>
                      <List
                        autoHeight
                        height={height}
                        isScrolling={isScrolling}
                        onScroll={onChildScroll}
                        rowCount={sessionStore.sortedSessions.length}
                        rowHeight={ROW_HEIGHT}
                        rowRenderer={({ index, key, style }) => (
                          <SessionRow
                            key={key}
                            style={style}
                            session={sessionStore.sortedSessions[index]}
                          />
                        )}
                        scrollTop={scrollTop}
                        width={width}
                      />
                    </div>
                  )}
                </Observer>
              )}
            </WindowScroller>
          )}
        </AutoSizer>
      </ListContainer>
    </Wrapper>
  );
}
Example #11
Source File: index.tsx    From metaflow-ui with Apache License 2.0 4 votes vote down vote up
LogList: React.FC<LogProps> = ({ logdata, fixedHeight, onScroll, downloadUrl, setFullscreen }) => {
  const { timezone } = useContext(TimezoneContext);
  const { t } = useTranslation();
  const rows = logdata.logs;
  const [stickBottom, setStickBottom] = useState(true);
  const [cache] = useState(
    new CellMeasurerCache({
      fixedWidth: true,
      minHeight: 25,
    }),
  );
  const _list = useRef<List>(null);
  const search = logdata.localSearch;
  const count = rows.length;

  const okCount = rows.reduce((okAmount, item) => {
    return typeof item === 'object' ? okAmount + 1 : okAmount;
  }, 0);

  const totalHeight = rows.reduce((val, _item, index) => {
    return val + (cache.getHeight(index, 0) || 0);
  }, 0);

  // Clear cached row heights on window resize events
  useEffect(() => {
    const listener = () => {
      cache.clearAll();
    };
    window.addEventListener('resize', listener);
    return () => window.removeEventListener('resize', listener);
  }, []); // eslint-disable-line

  // Force row height calculations after fetch ok

  useEffect(() => {
    if (_list?.current) {
      cache.clearAll();
      _list.current.recomputeRowHeights();
    }
  }, [okCount]); // eslint-disable-line

  useEffect(() => {
    if (stickBottom && _list) {
      _list.current?.scrollToRow(count);
    }
  }, [count, okCount, stickBottom]);

  //
  // Index tracking
  //

  const [scrollIndex, setIndex] = useState(0);
  const [debouncedIndex] = useDebounce(scrollIndex, 300);
  useEffect(() => {
    if (onScroll && !stickBottom) {
      onScroll(debouncedIndex);
    }
  }, [debouncedIndex, onScroll]); // eslint-disable-line

  //
  // Search features
  //

  const searchActive = search.result.active;
  const searchCurrent = search.result.current;
  const searchQuery = search.result.query;

  useEffect(() => {
    if (searchActive && search.result.result[searchCurrent]) {
      _list.current?.scrollToRow(search.result.result[searchCurrent].line);
    }
  }, [searchActive, searchCurrent, searchQuery]); //eslint-disable-line

  return (
    <div style={{ flex: '1 1 0' }} data-testid="loglist-wrapper">
      <LogActionBar
        data={logdata.logs}
        downloadlink={downloadUrl}
        setFullscreen={setFullscreen}
        search={logdata.localSearch}
        spaceAround={!!fixedHeight}
      />

      {rows.length === 0 && ['Ok', 'Error'].includes(logdata.preloadStatus) && logdata.status === 'NotAsked' && (
        <div data-testid="loglist-preload-empty">{t('task.no-preload-logs')}</div>
      )}

      {rows.length === 0 && logdata.status === 'Ok' && <div data-testid="loglist-empty">{t('task.no-logs')}</div>}

      {rows.length > 0 && (
        <LogListContainer data-testid="loglist-container">
          <AutoSizer disableHeight>
            {({ width }) => (
              <List
                ref={_list}
                overscanRowCount={5}
                rowCount={rows.length}
                rowHeight={cache.rowHeight}
                onRowsRendered={(data) => {
                  if (onScroll) {
                    setIndex(data.overscanStartIndex);
                  }
                }}
                deferredMeasurementCache={cache}
                onScroll={(args: { scrollTop: number; clientHeight: number; scrollHeight: number }) => {
                  if (args.scrollTop + args.clientHeight >= args.scrollHeight) {
                    setStickBottom(true);
                  } else if (stickBottom) {
                    setStickBottom(false);
                  }
                }}
                rowRenderer={({ index, style, key, parent }) => (
                  <CellMeasurer cache={cache} columnIndex={0} key={key} rowIndex={index} parent={parent}>
                    {() => {
                      const item = rows[index];

                      return (
                        <LogLine style={style} data-testid="log-line">
                          <LogLineNumber className="logline-number">{index}</LogLineNumber>
                          {getTimestamp(item, timezone)}
                          <LogLineText>
                            {typeof item === 'object' ? getLineText(item as Log, search.result) : 'Loading...'}
                          </LogLineText>
                        </LogLine>
                      );
                    }}
                  </CellMeasurer>
                )}
                height={fixedHeight ? fixedHeight - 16 : totalHeight < LIST_MAX_HEIGHT ? totalHeight : LIST_MAX_HEIGHT}
                width={width}
              />
            )}
          </AutoSizer>

          {!stickBottom && (
            <ScrollToBottomButton onClick={() => setStickBottom(true)} data-testid="loglist-stick-bottom">
              {t('run.scroll-to-bottom')}
            </ScrollToBottomButton>
          )}

          <PollLoader status={logdata.status} preloadStatus={logdata.preloadStatus} />
        </LogListContainer>
      )}
    </div>
  );
}
Example #12
Source File: SearchView.tsx    From yana with MIT License 4 votes vote down vote up
SearchView: React.FC<{
  title: string;
  titleSubtext?: string;
  icon: IconName;
  iconColor?: string;
  hiddenSearch: SearchQuery;
  defaultSearch: SearchQuery;
  onClickItem?: (item: DataItem) => void;
  rightContent?: React.ReactNode;
}> = props => {
  const mainContent = useMainContentContext();
  const dataInterface = useDataInterface();
  const overlaySearch = useOverlaySearch();
  const [hiddenSearch, setHiddenSearch] = useState(props.hiddenSearch);
  const [userSearch, setUserSearch] = useState(props.defaultSearch);
  // const [searchQuery, setSearchQuery] = useState<SearchQuery>({ ...props.hiddenSearch, ...props.defaultSearch });
  const searchQuery = { ...hiddenSearch, ...userSearch };
  const { items, nextPageAvailable, fetchNextPage, isFetching } = useDataSearch(
    Object.keys(searchQuery).length === 0 ? { all: true } : searchQuery,
    200
  );
  const parent = useDataItem(hiddenSearch.parents?.[0]);
  const previews = useDataItemPreviews(items);
  // TODO currently, all previews are loaded at once. Use Grid.onSectionRendered() to only load previews when they enter the viewport

  useEffect(() => setHiddenSearch(q => ({ ...q, ...props.hiddenSearch })), [props.hiddenSearch]);
  useEffect(() => setUserSearch(q => ({ ...q, ...props.defaultSearch })), [props.defaultSearch]);

  return (
    <PageContainer
      header={
        <PageHeader
          title={props.title}
          titleSubtext={props.titleSubtext}
          icon={props.icon}
          iconColor={props.iconColor}
          lowerContent={
            <SearchBar onChangeSearchQuery={setUserSearch}>
              <SearchInput />
            </SearchBar>
          }
          rightContent={
            <>
              {props.rightContent}{' '}
              <Popover content={<SearchSortingMenu searchQuery={hiddenSearch} onChange={setHiddenSearch} />}>
                <Button
                  icon={searchQuery.sortDirection === SearchQuerySortDirection.Ascending ? 'sort-asc' : 'sort-desc'}
                  outlined
                >
                  Sort Items
                </Button>
              </Popover>
              {parent && (
                <>
                  {' '}
                  <Popover
                    interactionKind={'click'}
                    position={'bottom'}
                    captureDismiss={true}
                    content={
                      <DataItemContextMenu
                        item={parent}
                        renderer={Bp3MenuRenderer}
                        mainContent={mainContent}
                        dataInterface={dataInterface}
                        overlaySearch={overlaySearch}
                      />
                    }
                  >
                    <Button outlined rightIcon={'chevron-down'}>
                      More
                    </Button>
                  </Popover>
                </>
              )}
            </>
          }
        />
      }
    >
      <AutoSizer>
        {({ width, height }) => {
          if (width <= searchViewCellDimensions.cellWidth) return null;

          const rowCount = Math.ceil(items.length / Math.floor(width / searchViewCellDimensions.cellWidth));
          const columnCount = Math.floor(width / searchViewCellDimensions.cellWidth);
          return (
            <Grid
              cellRenderer={cellProps => {
                const itemId =
                  cellProps.rowIndex * Math.floor(width / searchViewCellDimensions.cellWidth) + cellProps.columnIndex;
                const additionalLeftMargin = (width - columnCount * searchViewCellDimensions.cellWidth) / 2;

                if (items.length === 0) {
                  return null; // Provoke non ideal state
                }

                if (itemId >= items.length) {
                  if (!nextPageAvailable) {
                    return null;
                  }

                  fetchNextPage();
                  return (
                    <LoadingSearchViewCard
                      key={cellProps.key}
                      additionalLeftMargin={additionalLeftMargin}
                      containerStyle={cellProps.style}
                    />
                  );
                }

                const item = items[itemId];

                return (
                  <SearchViewCard
                    cellProps={cellProps}
                    dataItem={item}
                    additionalLeftMargin={additionalLeftMargin}
                    onClick={props.onClickItem ? () => props.onClickItem?.(item) : undefined}
                    preview={previews[item.id]}
                  />
                );
              }}
              columnWidth={searchViewCellDimensions.cellWidth}
              columnCount={columnCount}
              noContentRenderer={() =>
                isFetching ? (
                  <NonIdealState icon={<Spinner />} title="Loading items..." />
                ) : (
                  <NonIdealState icon={'warning-sign'} title="No items found" />
                )
              }
              overscanColumnCount={0}
              overscanRowCount={6}
              rowHeight={searchViewCellDimensions.cellHeight}
              rowCount={rowCount + (nextPageAvailable ? 12 : 0)}
              onSectionRendered={section => {}}
              height={height}
              width={width}
            />
          );
        }}
      </AutoSizer>
    </PageContainer>
  );
}
Example #13
Source File: index.tsx    From yasd with MIT License 4 votes vote down vote up
Page: React.FC = () => {
  const { setModal } = useModal()
  const { t } = useTranslation()
  const profile = useProfile()
  const [isAutoRefresh, setIsAutoRefresh] = useState<boolean>(true)
  const [group, setGroup] = useState<'recent' | 'active'>('recent')
  const { data: recentRequestsResponse, error: requestsError } =
    useSWR<RecentRequests>(() => '/requests/' + group, fetcher, {
      revalidateOnFocus: false,
      revalidateOnReconnect: false,
      dedupingInterval: 1000,
      refreshInterval: isAutoRefresh
        ? profile?.platform === 'macos'
          ? 2000
          : 4000
        : 0,
    })
  const [requestList, setRequestList] = useState<Array<RequestItem>>([])
  const [activeRequestList, setActiveRequestList] = useState<
    Array<RequestItem>
  >([])
  const currentList = useMemo(
    () => (group === 'recent' ? requestList : activeRequestList),
    [group, requestList, activeRequestList],
  )
  const query = useQuery()
  const sourceIp = useMemo<string | null>(() => query.get('source'), [query])

  useEffect(
    () => {
      if (!recentRequestsResponse?.requests) return

      const pendingList = [...recentRequestsResponse.requests]
      const now = new Date()
      let newList = [...currentList]

      while (pendingList.length) {
        const request = pendingList.pop() as RequestItem
        const existingIndex = newList.findIndex(
          (item) => item.id === request.id,
        )

        if (existingIndex >= 0) {
          Object.assign(newList[existingIndex], {
            ...omit(request, ['id']),
            lastUpdated: now,
          })
        } else {
          if (newList.length && request.id > newList[0].id) {
            newList.unshift({
              ...request,
              lastUpdated: now,
            })
          } else {
            newList.push({
              ...request,
              lastUpdated: now,
            })
          }
        }
      }

      if (group === 'recent') {
        newList = newList
          .filter((request) => {
            if (sourceIp) {
              return sourceIp === request.sourceAddress
            }
            return true
          })
          .slice(0, LIST_ITEMS_MAX)
      } else {
        newList = newList
          .filter((request) => {
            if (sourceIp) {
              return (
                request.lastUpdated === now &&
                sourceIp === request.sourceAddress
              )
            }
            return request.lastUpdated === now
          })
          .sort((a, b) => b.id - a.id)
      }

      if (group === 'recent') {
        setRequestList(newList)
        setActiveRequestList([])
      } else {
        setRequestList([])
        setActiveRequestList(newList)
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [recentRequestsResponse, group, sourceIp],
  )

  const openRequestDetail = useCallback(
    (req: RequestItem) => {
      setModal({
        children({ onClose }) {
          return onClose ? (
            <RequestModal req={req} onClose={onClose} />
          ) : (
            <React.Fragment />
          )
        },
        onClose() {
          // noop
        },
      })
    },
    [setModal],
  )

  const rowRenderer: ListRowRenderer = useCallback(
    ({
      key, // Unique key within array of rows
      index, // Index of row within collection
      isScrolling, // The List is currently being scrolled
      isVisible, // This row is visible within the List (eg it is not an overscanned row)
      style, // Style object to be applied to row (to position it)
    }) => {
      const req = currentList[index]

      return (
        <div
          key={key}
          style={style}
          onClick={() => openRequestDetail(req)}
          tw="flex flex-col justify-center py-2 cursor-pointer hover:bg-gray-100"
          css={css`
            padding-left: calc(env(safe-area-inset-left) + 0.75rem);
            padding-right: calc(env(safe-area-inset-right) + 0.75rem);
          `}
        >
          <ListItem req={req} />
        </div>
      )
    },
    [currentList, openRequestDetail],
  )

  return (
    <FixedFullscreenContainer>
      <React.Fragment>
        <PageTitle
          title={t('home.requests')}
          hasAutoRefresh={true}
          defaultAutoRefreshState={true}
          onAuthRefreshStateChange={(newState) => setIsAutoRefresh(newState)}
        />

        <div tw="flex-1">
          {recentRequestsResponse ? (
            currentList.length ? (
              <AutoSizer>
                {({ width, height }) => {
                  return (
                    <List
                      width={width}
                      height={height}
                      rowCount={currentList.length}
                      rowHeight={64}
                      rowRenderer={rowRenderer}
                      style={{
                        outline: 'none',
                      }}
                      css={css`
                        & > div {
                          ${tw`divide-y divide-gray-200`}
                        }
                      `}
                    />
                  )
                }}
              </AutoSizer>
            ) : (
              <div tw="h-full flex items-center justify-center text-base text-gray-500">
                {t('common.no_data')}
              </div>
            )
          ) : (
            <div tw="h-full flex items-center justify-center text-base text-gray-500">
              {t('common.is_loading')}...
            </div>
          )}
        </div>

        <div
          css={[
            tw`flex divide-x divide-gray-200 border-t border-solid border-gray-200 py-2 px-2`,
            css`
              & > div {
                ${tw`mx-2`}
              }
              & > div:first-of-type {
                margin-left: 0;
              }
            `,
          ]}
        >
          <SelectorGroup
            css={[
              tw`flex justify-center items-center`,
              css`
                & label {
                  ${tw`py-2 px-4 ml-2 my-1 text-sm`}
                }
                & label:first-of-type {
                  margin-left: 0;
                }
              `,
            ]}
            label="choose the dns result group"
            name="selector-group"
            onChange={(event: ChangeEvent<HTMLInputElement>) => {
              setGroup(() => {
                const newState = event.target.value as 'recent' | 'active'
                mutate('/requests/' + newState)
                return newState
              })
            }}
            options={[
              {
                children: t('requests.recent'),
                value: 'recent',
              },
              {
                children: t('requests.active'),
                value: 'active',
              },
            ]}
            value={group}
          />
        </div>
      </React.Fragment>
    </FixedFullscreenContainer>
  )
}
Example #14
Source File: EditorComponent.tsx    From yana with MIT License 4 votes vote down vote up
EditorComponent: React.FC<EditorComponentProps<AtlassianNoteEditorContent>> = props => {
  const settings = useSettings();
  const editorRef = useRef<EditorActions | null>(null);
  const [insertImageFn, setInsertImageFn] = useState<{ fn: (props: InsertedImageProperties) => void } | undefined>();
  const dataInterface = useDataInterface();

  useScreenView('atlassian-note-editor');

  useEffect(() => logger.log('Remount', [props.item.id], { content: props.content }), []);
  useEffect(
    () => () => {
      if (editorRef.current) {
        editorRef.current.getValue().then(adf => {
          props.onDismount({ adf });
        });
      }
    },
    []
  );

  // useEffect(() => {
  //   isChangingNote.current = true;
  //   if (editorRef.current) {
  //     logger.log("Reload content to", [], {content: JSON.stringify(props.content.adf)})
  //     editorRef.current?.replaceDocument(JSON.stringify(props.content.adf));
  //   } else {
  //     logger.error("Could not reload content, editorRef is null", [], {content: JSON.stringify(props.content.adf)})
  //   }
  //   setTimeout(() => isChangingNote.current = false, 1000);
  // }, [props.item.id])

  return (
    <div className={styles.container}>
      <AutoSizer>
        {({ width, height }) => {
          return (
            <div
              className={cxs({
                ' .akEditor': {
                  height: height + 'px',
                  width: width + 'px',
                },
                ' .akEditor > :nth-child(2)': {
                  height: height - 40 + 'px',
                  width: width + 'px',
                  overflow: 'auto',
                },
                ' .ak-editor-content-area': {
                  direction: settings.rtl ? 'rtl' : 'ltr'
                }
              })}
            >
              <IgnoreErrorBoundary>
                <EditorContext>
                  <WithEditorActions
                    render={actions => {
                      logger.log('Render WithEditorActions');
                      editorRef.current = actions;
                      props.onRegister(async () => ({ adf: await actions.getValue() }));
                      return (
                        <Editor
                          allowTables={{
                            advanced: settings.editorAtlassianAdvancedTables,
                          }}
                          codeBlock={{}}
                          media={{
                            allowMediaSingle: true,
                          }}
                          allowRule={true}
                          legacyImageUploadProvider={
                            new Promise(res =>
                              res((e: Event | undefined, insertImageFn: (props: InsertedImageProperties) => void) => {
                                setInsertImageFn({ fn: insertImageFn });
                              })
                            )
                          }
                          allowTasksAndDecisions={true}
                          allowExpand={true}
                          allowFindReplace={{
                            allowMatchCase: true,
                          }}
                          insertMenuItems={[]}
                          quickInsert={true}
                          allowTextColor={true}
                          allowTextAlignment={true}
                          defaultValue={JSON.stringify(props.content.adf)}
                          allowLayouts={{
                            allowBreakout: true,
                            UNSAFE_addSidebarLayouts: true,
                          }}
                          onChange={editorView => {
                            // if (!isChangingNote.current) props.onChange();
                            props.onChange();
                          }}
                        />
                      );
                    }}
                  />
                </EditorContext>
              </IgnoreErrorBoundary>
            </div>
          );
        }}
      </AutoSizer>
      <FindItemsDrawer
        title={'Insert file'}
        icon={'insert'}
        hiddenSearch={{ kind: DataItemKind.MediaItem }}
        isOpen={!!insertImageFn}
        onSetIsOpen={open => !open && setInsertImageFn(undefined)}
        onClickItem={item => {
          (async () => {
            if (insertImageFn) {
              if (isMediaItem(item)) {
                const src = await dataInterface.loadMediaItemContentAsPath(item.id);
                insertImageFn.fn({ src, alt: item.name, title: item.name });
                setInsertImageFn(undefined);
              } else {
                throw Error('Cannot insert data item which is not a media item.');
              }
            }
          })();
        }}
      />
    </div>
  );
}
Example #15
Source File: TableWrapper.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
TableWrapper = ({
  itemActions,
  columns,
  onSelect,
  records,
  isLoading,
  loadingMessage = <Typography component="h3">Loading...</Typography>,
  entityName,
  selectedItems,
  idField,
  classes,
  radioSelection = false,
  customEmptyMessage = "",
  customPaperHeight = "",
  noBackground = false,
  columnsSelector = false,
  textSelectable = false,
  columnsShown = [],
  onColumnChange = (column: string, state: boolean) => {},
  infiniteScrollConfig,
  sortConfig,
  autoScrollToBottom = false,
  disabled = false,
  onSelectAll,
  rowStyle,
  parentClassName = "",
}: TableWrapperProps) => {
  const [columnSelectorOpen, setColumnSelectorOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = React.useState<any>(null);

  const findView = itemActions
    ? itemActions.find((el) => el.type === "view")
    : null;

  const clickAction = (rowItem: any) => {
    if (findView) {
      const valueClick = findView.sendOnlyId ? rowItem[idField] : rowItem;

      let disabled = false;

      if (findView.disableButtonFunction) {
        if (findView.disableButtonFunction(valueClick)) {
          disabled = true;
        }
      }

      if (findView.to && !disabled) {
        history.push(`${findView.to}/${valueClick}`);
        return;
      }

      if (findView.onClick && !disabled) {
        findView.onClick(valueClick);
      }
    }
  };

  const openColumnsSelector = (event: { currentTarget: any }) => {
    setColumnSelectorOpen(!columnSelectorOpen);
    setAnchorEl(event.currentTarget);
  };

  const closeColumnSelector = () => {
    setColumnSelectorOpen(false);
    setAnchorEl(null);
  };

  const columnsSelection = (columns: IColumns[]) => {
    return (
      <Fragment>
        <IconButton
          aria-describedby={"columnsSelector"}
          color="primary"
          onClick={openColumnsSelector}
          size="large"
        >
          <ViewColumnIcon fontSize="inherit" />
        </IconButton>
        <Popover
          anchorEl={anchorEl}
          id={"columnsSelector"}
          open={columnSelectorOpen}
          anchorOrigin={{
            vertical: "bottom",
            horizontal: "left",
          }}
          transformOrigin={{
            vertical: "top",
            horizontal: "left",
          }}
          onClose={closeColumnSelector}
        >
          <div className={classes.shownColumnsLabel}>Shown Columns</div>
          <div className={classes.popoverContent}>
            {columns.map((column: IColumns) => {
              return (
                <CheckboxWrapper
                  key={`tableColumns-${column.label}`}
                  label={column.label}
                  checked={columnsShown.includes(column.elementKey!)}
                  onChange={(e) => {
                    onColumnChange(column.elementKey!, e.target.checked);
                  }}
                  id={`chbox-${column.label}`}
                  name={`chbox-${column.label}`}
                  value={column.label}
                />
              );
            })}
          </div>
        </Popover>
      </Fragment>
    );
  };

  return (
    <Grid item xs={12} className={parentClassName}>
      <Paper
        className={`${classes.paper} ${noBackground ? classes.noBackground : ""}
        ${disabled ? classes.disabled : ""} 
        ${
          customPaperHeight !== ""
            ? customPaperHeight
            : classes.defaultPaperHeight
        }`}
      >
        {isLoading && (
          <Grid container className={classes.loadingBox}>
            <Grid item xs={12} style={{ textAlign: "center" }}>
              {loadingMessage}
            </Grid>
            <Grid item xs={12}>
              <LinearProgress />
            </Grid>
          </Grid>
        )}
        {columnsSelector && !isLoading && records.length > 0 && (
          <div className={classes.overlayColumnSelection}>
            {columnsSelection(columns)}
          </div>
        )}
        {records && !isLoading && records.length > 0 ? (
          // @ts-ignore
          <InfiniteLoader
            isRowLoaded={({ index }) => !!records[index]}
            loadMoreRows={
              infiniteScrollConfig
                ? infiniteScrollConfig.loadMoreRecords
                : () => new Promise(() => true)
            }
            rowCount={
              infiniteScrollConfig
                ? infiniteScrollConfig.recordsCount
                : records.length
            }
          >
            {({ onRowsRendered, registerChild }) => (
              // @ts-ignore
              <AutoSizer>
                {({ width, height }: any) => {
                  const optionsWidth = calculateOptionsSize(
                    width,
                    itemActions
                      ? itemActions.filter((el) => el.type !== "view").length
                      : 0
                  );
                  const hasSelect: boolean = !!(onSelect && selectedItems);
                  const hasOptions: boolean = !!(
                    (itemActions && itemActions.length > 1) ||
                    (itemActions &&
                      itemActions.length === 1 &&
                      itemActions[0].type !== "view")
                  );
                  return (
                    // @ts-ignore
                    <Table
                      ref={registerChild}
                      disableHeader={false}
                      headerClassName={"headerItem"}
                      headerHeight={40}
                      height={height}
                      noRowsRenderer={() => (
                        <Fragment>
                          {customEmptyMessage !== ""
                            ? customEmptyMessage
                            : `There are no ${entityName} yet.`}
                        </Fragment>
                      )}
                      overscanRowCount={10}
                      rowHeight={40}
                      width={width}
                      rowCount={records.length}
                      rowGetter={({ index }) => records[index]}
                      onRowClick={({ rowData }) => {
                        clickAction(rowData);
                      }}
                      rowClassName={`rowLine ${findView ? "canClick" : ""} ${
                        !findView && textSelectable ? "canSelectText" : ""
                      }`}
                      onRowsRendered={onRowsRendered}
                      sort={sortConfig ? sortConfig.triggerSort : undefined}
                      sortBy={sortConfig ? sortConfig.currentSort : undefined}
                      sortDirection={
                        sortConfig ? sortConfig.currentDirection : undefined
                      }
                      scrollToIndex={
                        autoScrollToBottom ? records.length - 1 : -1
                      }
                      rowStyle={(r) => {
                        if (rowStyle) {
                          const returnElement = rowStyle(r);

                          if (typeof returnElement === "string") {
                            return get(TableRowPredefStyles, returnElement, {});
                          }

                          return returnElement;
                        }

                        return {};
                      }}
                    >
                      {hasSelect && (
                        // @ts-ignore
                        <Column
                          headerRenderer={() => (
                            <Fragment>
                              {onSelectAll ? (
                                <div className={classes.checkAllWrapper}>
                                  <CheckboxWrapper
                                    label={""}
                                    onChange={onSelectAll}
                                    value="all"
                                    id={"selectAll"}
                                    name={"selectAll"}
                                    checked={
                                      selectedItems?.length === records.length
                                    }
                                  />
                                </div>
                              ) : (
                                <Fragment>Select</Fragment>
                              )}
                            </Fragment>
                          )}
                          dataKey={`select-${idField}`}
                          width={selectWidth}
                          disableSort
                          cellRenderer={({ rowData }) => {
                            const isSelected = selectedItems
                              ? selectedItems.includes(
                                  isString(rowData) ? rowData : rowData[idField]
                                )
                              : false;

                            return (
                              <Checkbox
                                value={
                                  isString(rowData) ? rowData : rowData[idField]
                                }
                                color="primary"
                                inputProps={{
                                  "aria-label": "secondary checkbox",
                                }}
                                className="TableCheckbox"
                                checked={isSelected}
                                onChange={onSelect}
                                onClick={(e) => {
                                  e.stopPropagation();
                                }}
                                checkedIcon={
                                  <span
                                    className={
                                      radioSelection
                                        ? classes.radioSelectedIcon
                                        : classes.checkedIcon
                                    }
                                  />
                                }
                                icon={
                                  <span
                                    className={
                                      radioSelection
                                        ? classes.radioUnselectedIcon
                                        : classes.unCheckedIcon
                                    }
                                  />
                                }
                              />
                            );
                          }}
                        />
                      )}
                      {generateColumnsMap(
                        columns,
                        width,
                        optionsWidth,
                        hasSelect,
                        hasOptions,
                        selectedItems || [],
                        idField,
                        columnsSelector,
                        columnsShown,
                        sortConfig ? sortConfig.currentSort : "",
                        sortConfig ? sortConfig.currentDirection : undefined
                      )}
                      {hasOptions && (
                        // @ts-ignore
                        <Column
                          dataKey={idField}
                          width={optionsWidth}
                          headerClassName="optionsAlignment"
                          className="optionsAlignment"
                          cellRenderer={({ rowData }) => {
                            const isSelected = selectedItems
                              ? selectedItems.includes(
                                  isString(rowData) ? rowData : rowData[idField]
                                )
                              : false;
                            return elementActions(
                              itemActions || [],
                              rowData,
                              isSelected,
                              idField
                            );
                          }}
                        />
                      )}
                    </Table>
                  );
                }}
              </AutoSizer>
            )}
          </InfiniteLoader>
        ) : (
          <Fragment>
            {!isLoading && (
              <div>
                {customEmptyMessage !== ""
                  ? customEmptyMessage
                  : `There are no ${entityName} yet.`}
              </div>
            )}
          </Fragment>
        )}
      </Paper>
    </Grid>
  );
}
Example #16
Source File: index.tsx    From yasd with MIT License 4 votes vote down vote up
Page: React.FC = () => {
  const { t } = useTranslation()
  const [group, setGroup] = useState<'dynamic' | 'static'>('dynamic')
  const { data: dnsResult, error: dnsResultError } = useSWR<DnsResult>(
    '/dns',
    fetcher,
  )
  const list = useMemo(() => {
    if (group === 'dynamic') {
      return dnsResult?.dnsCache ?? []
    }
    return dnsResult?.local ?? []
  }, [dnsResult, group])

  const flushDns: MouseEventHandler = () => {
    fetcher({
      url: '/dns/flush',
      method: 'POST',
    })
      .then(() => {
        toast.success(t('common.success_interaction'))
        return mutate('/dns')
      })
      .catch((err) => {
        toast.error(t('common.failed_interaction'))
        console.error(err)
      })
  }

  const openIpDetail = (ip: string) => {
    window.open(`https://ip.sb/ip/${ip}`, '_blank', 'noopener noreferrer')
  }

  const rowRenderer: ListRowRenderer = useCallback(
    ({
      key, // Unique key within array of rows
      index, // Index of row within collection
      isScrolling, // The List is currently being scrolled
      isVisible, // This row is visible within the List (eg it is not an overscanned row)
      style, // Style object to be applied to row (to position it)
    }) => {
      if (group === 'dynamic') {
        const record = (list as DnsResult['dnsCache'])[index]

        return (
          <div
            key={key}
            style={style}
            onClick={() => openIpDetail(record.data[0])}
            css={[
              tw`flex flex-col justify-center py-2`,
              tw`cursor-pointer hover:bg-gray-100`,
              css`
                padding-left: calc(env(safe-area-inset-left) + 0.75rem);
                padding-right: calc(env(safe-area-inset-right) + 0.75rem);
              `,
            ]}
          >
            <div tw="text-sm truncate">{record.domain}</div>
            <div tw="text-xs text-gray-700 leading-tight">
              DNS: {record.server}
            </div>
            <div tw="text-xs text-gray-700 leading-tight truncate">
              {t('dns.result')}: {record.data.join(', ')}
            </div>
            <div tw="text-xs text-gray-700 leading-tight truncate">
              {t('dns.path')}: {record.path}
            </div>
          </div>
        )
      } else {
        const record = (list as DnsResult['local'])[index]

        return (
          <div
            key={key}
            style={style}
            css={[
              tw`flex flex-col justify-center py-2`,
              css`
                padding-left: calc(env(safe-area-inset-left) + 0.75rem);
                padding-right: calc(env(safe-area-inset-right) + 0.75rem);
              `,
            ]}
          >
            <div tw="text-sm truncate">{record.domain}</div>
            {!!record.server && (
              <div tw="text-xs text-gray-700 leading-tight">
                DNS: {record.server}
              </div>
            )}
            <div tw="text-xs text-gray-700 leading-tight">
              {t('dns.result')}: {record.data ?? 'N/A'}
            </div>
            <div tw="text-xs text-gray-700 leading-tight">
              {t('dns.source')}: {record.source ?? 'N/A'}
            </div>
            {!!record.comment && (
              <div tw="text-xs text-gray-700 leading-tight">
                {t('dns.comment')}: {record.comment}
              </div>
            )}
          </div>
        )
      }
    },
    [group, list],
  )

  return (
    <FixedFullscreenContainer>
      <PageTitle title="DNS" />

      <div tw="flex-1">
        <AutoSizer>
          {({ width, height }) => {
            return (
              <List
                width={width}
                height={height}
                rowCount={list.length}
                rowHeight={85}
                rowRenderer={rowRenderer}
                style={{
                  outline: 'none',
                }}
                css={css`
                  & > div {
                    ${tw`divide-y divide-gray-200`}
                  }
                `}
              />
            )
          }}
        </AutoSizer>
      </div>

      <div
        css={[
          tw`flex divide-x divide-gray-200 border-t border-solid border-gray-200 py-2 px-2`,
          css`
            & > div {
              ${tw`mx-2`}
            }
            & > div:first-of-type {
              margin-left: 0;
            }
          `,
        ]}
      >
        <SelectorGroup
          css={[
            tw`flex justify-center items-center`,
            css`
              & label {
                ${tw`py-2 px-4 ml-2 my-1 text-sm`}
              }
              & label:first-of-type {
                margin-left: 0;
              }
            `,
          ]}
          label="choose the dns result group"
          name="selector-group"
          onChange={(event: ChangeEvent<HTMLInputElement>) => {
            setGroup(event.target.value as 'dynamic' | 'static')
          }}
          options={[
            {
              children: t('dns.dynamic'),
              value: 'dynamic',
            },
            {
              children: t('dns.static'),
              value: 'static',
            },
          ]}
          value={group}
        />

        <div tw="flex items-center">
          <Button
            tw="font-normal"
            variant="tertiary"
            size="kilo"
            onClick={flushDns}
          >
            {t('dns.flush_dns')}
          </Button>
        </div>
      </div>
    </FixedFullscreenContainer>
  )
}
Example #17
Source File: PodLogs.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
PodLogs = ({
  classes,
  tenant,
  namespace,
  podName,
  propLoading,
}: IPodLogsProps) => {
  const dispatch = useDispatch();
  const loadingTenant = useSelector(
    (state: AppState) => state.tenants.loadingTenant
  );
  const [highlight, setHighlight] = useState<string>("");
  const [logLines, setLogLines] = useState<string[]>([]);
  const [loading, setLoading] = useState<boolean>(true);

  const cache = new CellMeasurerCache({
    minWidth: 5,
    fixedHeight: false,
  });

  useEffect(() => {
    if (propLoading) {
      setLoading(true);
    }
  }, [propLoading]);

  useEffect(() => {
    if (loadingTenant) {
      setLoading(true);
    }
  }, [loadingTenant]);

  const renderLog = (logMessage: string, index: number) => {
    if (!logMessage) {
      return null;
    }
    // remove any non ascii characters, exclude any control codes
    logMessage = logMessage.replace(/([^\x20-\x7F])/g, "");

    // regex for terminal colors like e.g. `[31;4m `
    const tColorRegex = /((\[[0-9;]+m))/g;

    // get substring if there was a match for to split what
    // is going to be colored and what not, here we add color
    // only to the first match.
    let substr = logMessage.replace(tColorRegex, "");

    // in case highlight is set, we select the line that contains the requested string
    let highlightedLine =
      highlight !== ""
        ? logMessage.toLowerCase().includes(highlight.toLowerCase())
        : false;

    // if starts with multiple spaces add padding
    if (substr.startsWith("   ")) {
      return (
        <div
          key={index}
          className={`${highlightedLine ? classes.highlight : ""}`}
        >
          <span className={classes.tab}>{substr}</span>
        </div>
      );
    } else {
      // for all remaining set default class
      return (
        <div
          key={index}
          className={`${highlightedLine ? classes.highlight : ""}`}
        >
          <span className={classes.ansidefault}>{substr}</span>
        </div>
      );
    }
  };

  useEffect(() => {
    if (loading) {
      api
        .invoke(
          "GET",
          `/api/v1/namespaces/${namespace}/tenants/${tenant}/pods/${podName}`
        )
        .then((res: string) => {
          setLogLines(res.split("\n"));
          setLoading(false);
        })
        .catch((err: ErrorResponseHandler) => {
          dispatch(setErrorSnackMessage(err));
          setLoading(false);
        });
    }
  }, [loading, podName, namespace, tenant, dispatch]);

  function cellRenderer({ columnIndex, key, parent, index, style }: any) {
    return (
      // @ts-ignore
      <CellMeasurer
        cache={cache}
        columnIndex={columnIndex}
        key={key}
        parent={parent}
        rowIndex={index}
      >
        <div
          style={{
            ...style,
          }}
        >
          {renderLog(logLines[index], index)}
        </div>
      </CellMeasurer>
    );
  }

  return (
    <React.Fragment>
      <Grid item xs={12} className={classes.actionsTray}>
        <TextField
          placeholder="Highlight Line"
          className={classes.searchField}
          id="search-resource"
          label=""
          onChange={(val) => {
            setHighlight(val.target.value);
          }}
          InputProps={{
            disableUnderline: true,
            startAdornment: (
              <InputAdornment position="start">
                <SearchIcon />
              </InputAdornment>
            ),
          }}
          variant="standard"
        />
      </Grid>
      <Grid item xs={12}>
        <br />
      </Grid>
      <Grid item xs={12}>
        <Paper>
          <div className={classes.logList}>
            {logLines.length >= 1 && (
              // @ts-ignore
              <AutoSizer>
                {({ width, height }) => (
                  // @ts-ignore
                  <List
                    rowHeight={(item) => cache.rowHeight(item)}
                    overscanRowCount={15}
                    rowCount={logLines.length}
                    rowRenderer={cellRenderer}
                    width={width}
                    height={height}
                  />
                )}
              </AutoSizer>
            )}
          </div>
        </Paper>
      </Grid>
    </React.Fragment>
  );
}
Example #18
Source File: InfinityScroll.tsx    From querybook with Apache License 2.0 4 votes vote down vote up
function InfinityScrollComponent<T>({
    labelField = 'name',
    itemClass = '',
    itemHeight = 24,

    elements,
    className,
    onClick,
    itemRenderer,

    onLoadMore,
    hasMore,
    defaultListHeight,
    autoSizerStyles,
}: React.PropsWithChildren<IInfinityScrollProps<T>>) {
    const listRef = useRef<List>();

    const rowRenderer = useCallback(
        ({
            index, // Index of row
            key, // Unique key within array of rendered rows
            style, // Style object to be applied to row (to position it);
        }: // This must be passed through to the rendered row element.
        {
            index: number;
            key: string;
            style: CSSProperties;
        }) => {
            if (index >= elements.length) {
                return (
                    <div
                        key={key}
                        style={style}
                        className="InfiniteScroll-loader flex-center"
                    >
                        <LoadingRow />
                    </div>
                );
            }

            const element = elements[index];

            const content = itemRenderer ? (
                itemRenderer(element)
            ) : (
                <span
                    className={itemClass}
                    onClick={onClick.bind(null, element)}
                >
                    {element[labelField]}
                </span>
            );

            return (
                <div key={key} style={style}>
                    {content}
                </div>
            );
        },
        [itemClass, itemRenderer, elements, labelField, onClick]
    );

    const isRowLoaded = useCallback(
        ({ index }: { index: number }) => index < elements.length,
        [elements.length]
    );
    const handleLoadMoreRows = useCallback(
        () =>
            new Promise<void>(async (resolve) => {
                try {
                    if (onLoadMore) {
                        await onLoadMore();
                    }
                } finally {
                    resolve();
                }
            }),
        [onLoadMore]
    );

    useEffect(() => {
        if (listRef.current) {
            listRef.current.forceUpdateGrid();
        }
    }, [elements]);

    const rowCount = hasMore ? elements.length + 1 : elements.length;

    return (
        <InfiniteLoader
            isRowLoaded={isRowLoaded}
            loadMoreRows={handleLoadMoreRows}
            rowCount={rowCount}
        >
            {({ onRowsRendered, registerChild }) => (
                <AutoSizer style={autoSizerStyles}>
                    {({ height, width }) => (
                        <List
                            className={className}
                            onRowsRendered={onRowsRendered}
                            ref={(ref) => {
                                registerChild(ref);
                                listRef.current = ref;
                            }}
                            height={defaultListHeight ?? height}
                            width={width}
                            rowCount={rowCount}
                            rowHeight={itemHeight}
                            rowRenderer={rowRenderer}
                        />
                    )}
                </AutoSizer>
            )}
        </InfiniteLoader>
    );
}
Example #19
Source File: Timeline.tsx    From metaflow-ui with Apache License 2.0 4 votes vote down vote up
Timeline: React.FC<TimelineProps> = ({
  rows,
  timeline,
  searchStatus,
  footerType = 'minimap',
  paramsString = '',
  customMinimumHeight = 31.25,
  onHandleMove = () => null,
  onMove = () => null,
  onStepRowClick = () => null,
}) => {
  const { t } = useTranslation();

  // Position of each step in timeline. Used to track if we should use sticky header (move to rowDataState?)
  const [stepPositions, setStepPositions] = useState<StepIndex[]>([]);
  // Name of sticky header (if should be visible)
  const [stickyHeader, setStickyHeader] = useState<null | string>(null);
  const [dragging, setDragging] = useState(false);

  // Update step position indexes (for sticky headers). We might wanna do this else where
  useEffect(() => {
    const stepPos: StepIndex[] = [];
    let index = 0;

    for (const current of rows) {
      index++;
      if (current.type === 'step') {
        stepPos.push({ name: current.data.step_name, index });
      }
    }

    setStepPositions(stepPos);
  }, [rows]);

  //
  // Event handling
  //

  const onRowsRendered = (params: RenderedRows) => {
    const stepNeedsSticky = timelineNeedStickyHeader(stepPositions, params.startIndex);

    if (stepNeedsSticky) {
      setStickyHeader(stepNeedsSticky.name);
    } else {
      if (stickyHeader) {
        setStickyHeader(null);
      }
    }
  };

  return (
    <ListContainer customMinHeight={customMinimumHeight}>
      <AutoSizer>
        {({ width, height }) => (
          <>
            <List
              overscanRowCount={10}
              rowCount={rows.length}
              onRowsRendered={onRowsRendered}
              rowHeight={ROW_HEIGHT}
              rowRenderer={createRowRenderer({
                rows,
                timeline,
                searchStatus,
                onStepRowClick,
                paramsString,
                t: t,
                dragging: dragging,
              })}
              height={
                height - SPACE_UNDER_TIMELINE(footerType) > rows.length * ROW_HEIGHT
                  ? rows.length * ROW_HEIGHT
                  : height - SPACE_UNDER_TIMELINE(footerType)
              }
              width={width}
              style={{ transition: 'height 0.25s' }}
            />
            {stickyHeader && timeline.groupingEnabled && (
              <StickyHeader
                stickyStep={stickyHeader}
                items={rows}
                timeline={timeline}
                onToggle={() => onStepRowClick(stickyHeader)}
                t={t}
                dragging={dragging}
              />
            )}

            <div style={{ width: width + 'px' }}>
              <TimelineFooter
                {...(footerType === 'minimap'
                  ? {
                      type: 'minimap',
                      props: {
                        timeline,
                        rows,
                        onMove: onMove,
                        onHandleMove: onHandleMove,
                        onDraggingStateChange: setDragging,
                      },
                    }
                  : {
                      type: 'minimal',
                      props: {
                        startTime: timeline.startTime,
                        visibleStartTime: timeline.visibleStartTime,
                        visibleEndtime: timeline.visibleEndTime,
                      },
                    })}
              />
            </div>
          </>
        )}
      </AutoSizer>
    </ListContainer>
  );
}