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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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>
);
}