feat: add the ability to drag columns (#3100)

* feat: add the ability to drag columns

* feat: add the ability to drag columns in the logs explorer

* feat: update drag logic

* fix: resolve comments

* feat: resolve comment regarding error handling

---------

Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
dnazarenkoo 2023-07-18 14:48:34 +03:00 committed by GitHub
parent 07833b9859
commit 8f1451e154
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 307 additions and 37 deletions

View File

@ -69,6 +69,7 @@
"papaparse": "5.4.1", "papaparse": "5.4.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-drag-listview": "2.0.0",
"react-force-graph": "^1.41.0", "react-force-graph": "^1.41.0",
"react-grid-layout": "^1.3.4", "react-grid-layout": "^1.3.4",
"react-i18next": "^11.16.1", "react-i18next": "^11.16.1",

View File

@ -1,6 +1,8 @@
/* eslint-disable react/jsx-props-no-spreading */
import { Table } from 'antd'; import { Table } from 'antd';
import type { TableProps } from 'antd/es/table';
import { ColumnsType } from 'antd/lib/table'; import { ColumnsType } from 'antd/lib/table';
import { dragColumnParams } from 'hooks/useDragColumns/configs';
import { import {
SyntheticEvent, SyntheticEvent,
useCallback, useCallback,
@ -8,12 +10,18 @@ import {
useMemo, useMemo,
useState, useState,
} from 'react'; } from 'react';
import ReactDragListView from 'react-drag-listview';
import { ResizeCallbackData } from 'react-resizable'; import { ResizeCallbackData } from 'react-resizable';
import ResizableHeader from './ResizableHeader'; import ResizableHeader from './ResizableHeader';
import { DragSpanStyle } from './styles';
import { ResizeTableProps } from './types';
// eslint-disable-next-line @typescript-eslint/no-explicit-any function ResizeTable({
function ResizeTable({ columns, ...restprops }: TableProps<any>): JSX.Element { columns,
onDragColumn,
...restProps
}: ResizeTableProps): JSX.Element {
const [columnsData, setColumns] = useState<ColumnsType>([]); const [columnsData, setColumns] = useState<ColumnsType>([]);
const handleResize = useCallback( const handleResize = useCallback(
@ -31,16 +39,32 @@ function ResizeTable({ columns, ...restprops }: TableProps<any>): JSX.Element {
[columnsData], [columnsData],
); );
const mergeColumns = useMemo( const mergedColumns = useMemo(
() => () =>
columnsData.map((col, index) => ({ columnsData.map((col, index) => ({
...col, ...col,
...(onDragColumn && {
title: (
<DragSpanStyle className="dragHandler">
{col?.title?.toString() || ''}
</DragSpanStyle>
),
}),
onHeaderCell: (column: ColumnsType<unknown>[number]): unknown => ({ onHeaderCell: (column: ColumnsType<unknown>[number]): unknown => ({
width: column.width, width: column.width,
onResize: handleResize(index), onResize: handleResize(index),
}), }),
})), })) as ColumnsType<any>,
[columnsData, handleResize], [columnsData, onDragColumn, handleResize],
);
const tableParams = useMemo(
() => ({
...restProps,
components: { header: { cell: ResizableHeader } },
columns: mergedColumns,
}),
[mergedColumns, restProps],
); );
useEffect(() => { useEffect(() => {
@ -49,15 +73,17 @@ function ResizeTable({ columns, ...restprops }: TableProps<any>): JSX.Element {
} }
}, [columns]); }, [columns]);
return ( return onDragColumn ? (
<Table <ReactDragListView.DragColumn {...dragColumnParams} onDragEnd={onDragColumn}>
// eslint-disable-next-line react/jsx-props-no-spreading <Table {...tableParams} />
{...restprops} </ReactDragListView.DragColumn>
components={{ header: { cell: ResizableHeader } }} ) : (
// eslint-disable-next-line @typescript-eslint/no-explicit-any <Table {...tableParams} />
columns={mergeColumns as ColumnsType<any>}
/>
); );
} }
ResizeTable.defaultProps = {
onDragColumn: undefined,
};
export default ResizeTable; export default ResizeTable;

View File

@ -2,10 +2,16 @@ import styled from 'styled-components';
export const SpanStyle = styled.span` export const SpanStyle = styled.span`
position: absolute; position: absolute;
right: -5px; right: -0.313rem;
bottom: 0; bottom: 0;
z-index: 1; z-index: 1;
width: 10px; width: 0.625rem;
height: 100%; height: 100%;
cursor: col-resize; cursor: col-resize;
`; `;
export const DragSpanStyle = styled.span`
display: flex;
margin: -1rem;
padding: 1rem;
`;

View File

@ -0,0 +1,6 @@
import { TableProps } from 'antd';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export interface ResizeTableProps extends TableProps<any> {
onDragColumn?: (fromIndex: number, toIndex: number) => void;
}

View File

@ -8,4 +8,6 @@ export enum LOCALSTORAGE {
LOGS_LINES_PER_ROW = 'LOGS_LINES_PER_ROW', LOGS_LINES_PER_ROW = 'LOGS_LINES_PER_ROW',
LOGS_LIST_OPTIONS = 'LOGS_LIST_OPTIONS', LOGS_LIST_OPTIONS = 'LOGS_LIST_OPTIONS',
TRACES_LIST_OPTIONS = 'TRACES_LIST_OPTIONS', TRACES_LIST_OPTIONS = 'TRACES_LIST_OPTIONS',
TRACES_LIST_COLUMNS = 'TRACES_LIST_COLUMNS',
LOGS_LIST_COLUMNS = 'LOGS_LIST_COLUMNS',
} }

View File

@ -0,0 +1,24 @@
import { dragColumnParams } from 'hooks/useDragColumns/configs';
import ReactDragListView from 'react-drag-listview';
import { TableComponents } from 'react-virtuoso';
import { TableStyled } from './styles';
interface LogsCustomTableProps {
handleDragEnd: (fromIndex: number, toIndex: number) => void;
}
export const LogsCustomTable = ({
handleDragEnd,
}: LogsCustomTableProps): TableComponents['Table'] =>
function CustomTable({ style, children }): JSX.Element {
return (
<ReactDragListView.DragColumn
// eslint-disable-next-line react/jsx-props-no-spreading
{...dragColumnParams}
onDragEnd={handleDragEnd}
>
<TableStyled style={style}>{children}</TableStyled>
</ReactDragListView.DragColumn>
);
};

View File

@ -1,22 +1,26 @@
import { ColumnTypeRender } from 'components/Logs/TableView/types'; import { ColumnTypeRender } from 'components/Logs/TableView/types';
import { useTableView } from 'components/Logs/TableView/useTableView'; import { useTableView } from 'components/Logs/TableView/useTableView';
import { cloneElement, ReactElement, ReactNode, useCallback } from 'react'; import { LOCALSTORAGE } from 'constants/localStorage';
import useDragColumns from 'hooks/useDragColumns';
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
import {
cloneElement,
ReactElement,
ReactNode,
useCallback,
useMemo,
} from 'react';
import { TableComponents, TableVirtuoso } from 'react-virtuoso'; import { TableComponents, TableVirtuoso } from 'react-virtuoso';
import { infinityDefaultStyles } from './config'; import { infinityDefaultStyles } from './config';
import { LogsCustomTable } from './LogsCustomTable';
import { import {
TableCellStyled, TableCellStyled,
TableHeaderCellStyled, TableHeaderCellStyled,
TableRowStyled, TableRowStyled,
TableStyled,
} from './styles'; } from './styles';
import { InfinityTableProps } from './types'; import { InfinityTableProps } from './types';
// eslint-disable-next-line react/function-component-definition
const CustomTable: TableComponents['Table'] = ({ style, children }) => (
<TableStyled style={style}>{children}</TableStyled>
);
// eslint-disable-next-line react/function-component-definition // eslint-disable-next-line react/function-component-definition
const CustomTableRow: TableComponents['TableRow'] = ({ const CustomTableRow: TableComponents['TableRow'] = ({
children, children,
@ -31,11 +35,25 @@ function InfinityTable({
}: InfinityTableProps): JSX.Element | null { }: InfinityTableProps): JSX.Element | null {
const { onEndReached } = infitiyTableProps; const { onEndReached } = infitiyTableProps;
const { dataSource, columns } = useTableView(tableViewProps); const { dataSource, columns } = useTableView(tableViewProps);
const { draggedColumns, onDragColumns } = useDragColumns<
Record<string, unknown>
>(LOCALSTORAGE.LOGS_LIST_COLUMNS);
const tableColumns = useMemo(
() => getDraggedColumns<Record<string, unknown>>(columns, draggedColumns),
[columns, draggedColumns],
);
const handleDragEnd = useCallback(
(fromIndex: number, toIndex: number) =>
onDragColumns(tableColumns, fromIndex, toIndex),
[tableColumns, onDragColumns],
);
const itemContent = useCallback( const itemContent = useCallback(
(index: number, log: Record<string, unknown>): JSX.Element => ( (index: number, log: Record<string, unknown>): JSX.Element => (
<> <>
{columns.map((column) => { {tableColumns.map((column) => {
if (!column.render) return <td>Empty</td>; if (!column.render) return <td>Empty</td>;
const element: ColumnTypeRender<Record<string, unknown>> = column.render( const element: ColumnTypeRender<Record<string, unknown>> = column.render(
@ -60,20 +78,29 @@ function InfinityTable({
})} })}
</> </>
), ),
[columns], [tableColumns],
); );
const tableHeader = useCallback( const tableHeader = useCallback(
() => ( () => (
<tr> <tr>
{columns.map((column) => ( {tableColumns.map((column) => {
<TableHeaderCellStyled key={column.key}> const isDragColumn = column.key !== 'expand';
return (
<TableHeaderCellStyled
isDragColumn={isDragColumn}
key={column.key}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(isDragColumn && { className: 'dragHandler' })}
>
{column.title as string} {column.title as string}
</TableHeaderCellStyled> </TableHeaderCellStyled>
))} );
})}
</tr> </tr>
), ),
[columns], [tableColumns],
); );
return ( return (
@ -81,7 +108,8 @@ function InfinityTable({
style={infinityDefaultStyles} style={infinityDefaultStyles}
data={dataSource} data={dataSource}
components={{ components={{
Table: CustomTable, // eslint-disable-next-line react/jsx-props-no-spreading
Table: LogsCustomTable({ handleDragEnd }),
// TODO: fix it in the future // TODO: fix it in the future
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore

View File

@ -1,6 +1,10 @@
import { themeColors } from 'constants/theme'; import { themeColors } from 'constants/theme';
import styled from 'styled-components'; import styled from 'styled-components';
interface TableHeaderCellStyledProps {
isDragColumn: boolean;
}
export const TableStyled = styled.table` export const TableStyled = styled.table`
width: 100%; width: 100%;
border-top: 1px solid rgba(253, 253, 253, 0.12); border-top: 1px solid rgba(253, 253, 253, 0.12);
@ -26,10 +30,12 @@ export const TableRowStyled = styled.tr`
} }
`; `;
export const TableHeaderCellStyled = styled.th` export const TableHeaderCellStyled = styled.th<TableHeaderCellStyledProps>`
padding: 0.5rem; padding: 0.5rem;
border-inline-end: 1px solid rgba(253, 253, 253, 0.12); border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
background-color: #1d1d1d; background-color: #1d1d1d;
${({ isDragColumn }): string => (isDragColumn ? 'cursor: col-resize;' : '')}
&:first-child { &:first-child {
border-start-start-radius: 2px; border-start-start-radius: 2px;
} }

View File

@ -30,6 +30,7 @@ interface UseOptionsMenuProps {
interface UseOptionsMenu { interface UseOptionsMenu {
options: OptionsQuery; options: OptionsQuery;
config: OptionsMenuConfig; config: OptionsMenuConfig;
handleOptionsChange: (newQueryData: OptionsQuery) => void;
} }
const useOptionsMenu = ({ const useOptionsMenu = ({
@ -306,6 +307,7 @@ const useOptionsMenu = ({
return { return {
options: optionsQueryData, options: optionsQueryData,
config: optionsMenuConfig, config: optionsMenuConfig,
handleOptionsChange: handleRedirectWithOptionsData,
}; };
}; };

View File

@ -6,6 +6,8 @@ import { useOptionsMenu } from 'container/OptionsMenu';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { Pagination, URL_PAGINATION } from 'hooks/queryPagination'; import { Pagination, URL_PAGINATION } from 'hooks/queryPagination';
import useDragColumns from 'hooks/useDragColumns';
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
import useUrlQueryData from 'hooks/useUrlQueryData'; import useUrlQueryData from 'hooks/useUrlQueryData';
import history from 'lib/history'; import history from 'lib/history';
import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { RowData } from 'lib/query/createTableColumnsFromQuery';
@ -37,6 +39,10 @@ function ListView(): JSX.Element {
}, },
}); });
const { draggedColumns, onDragColumns } = useDragColumns<RowData>(
LOCALSTORAGE.TRACES_LIST_COLUMNS,
);
const { queryData: paginationQueryData } = useUrlQueryData<Pagination>( const { queryData: paginationQueryData } = useUrlQueryData<Pagination>(
URL_PAGINATION, URL_PAGINATION,
); );
@ -82,9 +88,10 @@ function ListView(): JSX.Element {
queryTableDataResult, queryTableDataResult,
]); ]);
const columns = useMemo(() => getListColumns(options?.selectColumns || []), [ const columns = useMemo(() => {
options?.selectColumns, const updatedColumns = getListColumns(options?.selectColumns || []);
]); return getDraggedColumns(updatedColumns, draggedColumns);
}, [options?.selectColumns, draggedColumns]);
const transformedQueryTableData = useMemo( const transformedQueryTableData = useMemo(
() => transformDataWithDate(queryTableData) || [], () => transformDataWithDate(queryTableData) || [],
@ -106,6 +113,12 @@ function ListView(): JSX.Element {
[], [],
); );
const handleDragColumn = useCallback(
(fromIndex: number, toIndex: number) =>
onDragColumns(columns, fromIndex, toIndex),
[columns, onDragColumns],
);
return ( return (
<Container> <Container>
<TraceExplorerControls <TraceExplorerControls
@ -127,6 +140,7 @@ function ListView(): JSX.Element {
dataSource={transformedQueryTableData} dataSource={transformedQueryTableData}
columns={columns} columns={columns}
onRow={handleRow} onRow={handleRow}
onDragColumn={handleDragColumn}
/> />
)} )}
</Container> </Container>

View File

@ -0,0 +1,7 @@
export const COLUMNS = 'columns';
export const dragColumnParams = {
ignoreSelector: '.react-resizable-handle',
nodeSelector: 'th',
handleSelector: '.dragHandler',
};

View File

@ -0,0 +1,75 @@
import { ColumnsType } from 'antd/es/table';
import getFromLocalstorage from 'api/browser/localstorage/get';
import setToLocalstorage from 'api/browser/localstorage/set';
import { LOCALSTORAGE } from 'constants/localStorage';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { useCallback, useEffect, useMemo } from 'react';
import { COLUMNS } from './configs';
import { UseDragColumns } from './types';
const useDragColumns = <T>(storageKey: LOCALSTORAGE): UseDragColumns<T> => {
const {
query: draggedColumnsQuery,
queryData: draggedColumns,
redirectWithQuery: redirectWithDraggedColumns,
} = useUrlQueryData<ColumnsType<T>>(COLUMNS, []);
const localStorageDraggedColumns = useMemo(
() => getFromLocalstorage(storageKey),
[storageKey],
);
const handleRedirectWithDraggedColumns = useCallback(
(columns: ColumnsType<T>) => {
redirectWithDraggedColumns(columns);
setToLocalstorage(storageKey, JSON.stringify(columns));
},
[storageKey, redirectWithDraggedColumns],
);
const onDragColumns = useCallback(
(columns: ColumnsType<T>, fromIndex: number, toIndex: number): void => {
const columnsData = [...columns];
const item = columnsData.splice(fromIndex, 1)[0];
columnsData.splice(toIndex, 0, item);
handleRedirectWithDraggedColumns(columnsData);
},
[handleRedirectWithDraggedColumns],
);
const redirectWithNewDraggedColumns = useCallback(
async (localStorageColumns: string) => {
let nextDraggedColumns: ColumnsType<T> = [];
try {
const parsedDraggedColumns = await JSON.parse(localStorageColumns);
nextDraggedColumns = parsedDraggedColumns;
} catch (e) {
console.log('error while parsing json');
} finally {
redirectWithDraggedColumns(nextDraggedColumns);
}
},
[redirectWithDraggedColumns],
);
useEffect(() => {
if (draggedColumnsQuery || !localStorageDraggedColumns) return;
redirectWithNewDraggedColumns(localStorageDraggedColumns);
}, [
draggedColumnsQuery,
localStorageDraggedColumns,
redirectWithNewDraggedColumns,
]);
return {
draggedColumns,
onDragColumns,
};
};
export default useDragColumns;

View File

@ -0,0 +1,10 @@
import { ColumnsType } from 'antd/es/table';
export type UseDragColumns<T> = {
draggedColumns: ColumnsType<T>;
onDragColumns: (
columns: ColumnsType<T>,
fromIndex: number,
toIndex: number,
) => void;
};

View File

@ -0,0 +1,37 @@
import { ColumnsType } from 'antd/es/table';
const filterColumns = <T>(
initialColumns: ColumnsType<T>,
findColumns: ColumnsType<T>,
isColumnExist = true,
): ColumnsType<T> =>
initialColumns.filter(({ title: columnTitle }) => {
const column = findColumns.find(({ title }) => title === columnTitle);
return isColumnExist ? !!column : !column;
});
export const getDraggedColumns = <T>(
currentColumns: ColumnsType<T>,
draggedColumns: ColumnsType<T>,
): ColumnsType<T> => {
if (draggedColumns.length) {
const actualDruggedColumns = filterColumns<T>(draggedColumns, currentColumns);
const newColumns = filterColumns<T>(
currentColumns,
actualDruggedColumns,
false,
);
return [...actualDruggedColumns, ...newColumns].reduce((acc, { title }) => {
const column = currentColumns.find(
({ title: columnTitle }) => title === columnTitle,
);
if (column) return [...acc, column];
return acc;
}, [] as ColumnsType<T>);
}
return currentColumns;
};

View File

@ -3719,6 +3719,14 @@ babel-preset-react-app@^10.0.0:
babel-plugin-macros "^3.1.0" babel-plugin-macros "^3.1.0"
babel-plugin-transform-react-remove-prop-types "^0.4.24" babel-plugin-transform-react-remove-prop-types "^0.4.24"
babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
@ -4474,6 +4482,11 @@ core-js-compat@^3.25.1:
dependencies: dependencies:
browserslist "^4.21.5" browserslist "^4.21.5"
core-js@^2.4.0:
version "2.6.12"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.3" version "1.0.3"
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
@ -9999,7 +10012,7 @@ prompts@^2.0.1, prompts@^2.4.1:
kleur "^3.0.3" kleur "^3.0.3"
sisteransi "^1.0.5" sisteransi "^1.0.5"
prop-types@15, prop-types@15.x, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: prop-types@15, prop-types@15.x, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1" version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -10513,6 +10526,14 @@ react-dom@18.2.0:
loose-envify "^1.1.0" loose-envify "^1.1.0"
scheduler "^0.23.0" scheduler "^0.23.0"
react-drag-listview@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-drag-listview/-/react-drag-listview-2.0.0.tgz#b8e7ec5f980ecbbf3abb85f50db0b03cd764edbf"
integrity sha512-7Apx/1Xt4qu+JHHP0rH6aLgZgS7c2MX8ocHVGCi03KfeIWEu0t14MhT3boQKM33l5eJrE/IWfExFTvoYq22fsg==
dependencies:
babel-runtime "^6.26.0"
prop-types "^15.5.8"
react-draggable@^4.0.0, react-draggable@^4.0.3: react-draggable@^4.0.0, react-draggable@^4.0.3:
version "4.4.5" version "4.4.5"
resolved "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz" resolved "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz"
@ -10808,6 +10829,11 @@ regenerator-runtime@0.13.9:
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.11: regenerator-runtime@^0.13.11:
version "0.13.11" version "0.13.11"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"