mirror of https://github.com/portainer/portainer
feat(kubeconfig): pagination for downloading kubeconfigs EE-2141 (#6895)
* EE-2141 Add pagination to kubeconfig download dialogpull/7057/head
parent
be11dfc231
commit
b6309682ef
|
@ -302,7 +302,19 @@ export function EnvironmentList({ onClickItem, onRefresh }: Props) {
|
|||
)}
|
||||
</div>
|
||||
<div className={styles.kubeconfigButton}>
|
||||
<KubeconfigButton environments={environments} />
|
||||
<KubeconfigButton
|
||||
environments={environments}
|
||||
envQueryParams={{
|
||||
types: platformType,
|
||||
search: debouncedTextFilter,
|
||||
status: statusFilter,
|
||||
tagIds: tagFilter?.length ? tagFilter : undefined,
|
||||
groupIds: groupFilter,
|
||||
sort: sortByFilter,
|
||||
order: sortByDescending ? 'desc' : 'asc',
|
||||
edgeDeviceFilter: 'none',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.filterSearchbar}>
|
||||
<FilterSearchBar
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
import * as kcService from '@/kubernetes/services/kubeconfig.service';
|
||||
import * as notifications from '@/portainer/services/notifications';
|
||||
import { confirmKubeconfigSelection } from '@/portainer/services/modal.service/prompt';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Environment } from '@/portainer/environments/types';
|
||||
import { EnvironmentsQueryParams } from '@/portainer/environments/environment.service/index';
|
||||
import { isKubernetesEnvironment } from '@/portainer/environments/utils';
|
||||
import { trackEvent } from '@/angulartics.matomo/analytics-services';
|
||||
import { Button } from '@/portainer/components/Button';
|
||||
|
||||
interface Props {
|
||||
environments?: Environment[];
|
||||
}
|
||||
import { KubeconfigPrompt } from './KubeconfigPrompt';
|
||||
import '@reach/dialog/styles.css';
|
||||
|
||||
export interface KubeconfigButtonProps {
|
||||
environments: Environment[];
|
||||
envQueryParams: EnvironmentsQueryParams;
|
||||
}
|
||||
export function KubeconfigButton({
|
||||
environments,
|
||||
envQueryParams,
|
||||
}: KubeconfigButtonProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
export function KubeconfigButton({ environments }: Props) {
|
||||
if (!environments) {
|
||||
return null;
|
||||
}
|
||||
|
@ -20,9 +28,12 @@ export function KubeconfigButton({ environments }: Props) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Button onClick={handleClick}>
|
||||
<i className="fas fa-download space-right" /> kubeconfig
|
||||
</Button>
|
||||
<>
|
||||
<Button onClick={handleClick}>
|
||||
<i className="fas fa-download space-right" /> kubeconfig
|
||||
</Button>
|
||||
{prompt()}
|
||||
</>
|
||||
);
|
||||
|
||||
function handleClick() {
|
||||
|
@ -34,48 +45,28 @@ export function KubeconfigButton({ environments }: Props) {
|
|||
category: 'kubernetes',
|
||||
});
|
||||
|
||||
showKubeconfigModal(environments);
|
||||
}
|
||||
}
|
||||
|
||||
function isKubeconfigButtonVisible(environments: Environment[]) {
|
||||
if (window.location.protocol !== 'https:') {
|
||||
return false;
|
||||
}
|
||||
return environments.some((env) => isKubernetesEnvironment(env.Type));
|
||||
}
|
||||
|
||||
async function showKubeconfigModal(environments: Environment[]) {
|
||||
const kubeEnvironments = environments.filter((env) =>
|
||||
isKubernetesEnvironment(env.Type)
|
||||
);
|
||||
const options = kubeEnvironments.map((environment) => ({
|
||||
text: `${environment.Name} (${environment.URL})`,
|
||||
value: `${environment.Id}`,
|
||||
}));
|
||||
|
||||
let expiryMessage = '';
|
||||
try {
|
||||
expiryMessage = await kcService.expiryMessage();
|
||||
} catch (e) {
|
||||
notifications.error('Failed fetching kubeconfig expiry time', e as Error);
|
||||
setIsOpen(true);
|
||||
}
|
||||
|
||||
confirmKubeconfigSelection(
|
||||
options,
|
||||
expiryMessage,
|
||||
async (selectedEnvironmentIDs: string[]) => {
|
||||
if (selectedEnvironmentIDs.length === 0) {
|
||||
notifications.warning('No environment was selected', '');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await kcService.downloadKubeconfigFile(
|
||||
selectedEnvironmentIDs.map((id) => parseInt(id, 10))
|
||||
);
|
||||
} catch (e) {
|
||||
notifications.error('Failed downloading kubeconfig file', e as Error);
|
||||
}
|
||||
function handleClose() {
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
function isKubeconfigButtonVisible(environments: Environment[]) {
|
||||
if (window.location.protocol !== 'https:') {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
return environments.some((env) => isKubernetesEnvironment(env.Type));
|
||||
}
|
||||
|
||||
function prompt() {
|
||||
return (
|
||||
isOpen && (
|
||||
<KubeconfigPrompt
|
||||
envQueryParams={envQueryParams}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
.checkbox {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
import { useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { DialogOverlay } from '@reach/dialog';
|
||||
|
||||
import * as kcService from '@/kubernetes/services/kubeconfig.service';
|
||||
import * as notifications from '@/portainer/services/notifications';
|
||||
import { Button } from '@/portainer/components/Button';
|
||||
import { Checkbox } from '@/portainer/components/form-components/Checkbox';
|
||||
import { EnvironmentType } from '@/portainer/environments/types';
|
||||
import { EnvironmentsQueryParams } from '@/portainer/environments/environment.service/index';
|
||||
import { PaginationControls } from '@/portainer/components/pagination-controls';
|
||||
import { usePaginationLimitState } from '@/portainer/hooks/usePaginationLimitState';
|
||||
import { useEnvironmentList } from '@/portainer/environments/queries/useEnvironmentList';
|
||||
|
||||
import { useSelection } from './KubeconfigSelection';
|
||||
import styles from './KubeconfigPrompt.module.css';
|
||||
import '@reach/dialog/styles.css';
|
||||
|
||||
export interface KubeconfigPromptProps {
|
||||
envQueryParams: EnvironmentsQueryParams;
|
||||
onClose: () => void;
|
||||
}
|
||||
const storageKey = 'home_endpoints';
|
||||
|
||||
export function KubeconfigPrompt({
|
||||
envQueryParams,
|
||||
onClose,
|
||||
}: KubeconfigPromptProps) {
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageLimit, setPageLimit] = usePaginationLimitState(storageKey);
|
||||
const kubeServiceExpiryQuery = useQuery(['kubeServiceExpiry'], async () => {
|
||||
const expiryMessage = await kcService.expiryMessage();
|
||||
return expiryMessage;
|
||||
});
|
||||
const { selection, toggle: toggleSelection, selectionSize } = useSelection();
|
||||
const { environments, totalCount } = useEnvironmentList({
|
||||
...envQueryParams,
|
||||
page,
|
||||
pageLimit,
|
||||
types: [
|
||||
EnvironmentType.KubernetesLocal,
|
||||
EnvironmentType.AgentOnKubernetes,
|
||||
EnvironmentType.EdgeAgentOnKubernetes,
|
||||
],
|
||||
});
|
||||
const isAllPageSelected = environments.every((env) => selection[env.Id]);
|
||||
|
||||
return (
|
||||
<DialogOverlay
|
||||
className={styles.dialog}
|
||||
aria-label="Kubeconfig View"
|
||||
role="dialog"
|
||||
>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" onClick={onClose}>
|
||||
×
|
||||
</button>
|
||||
<h5 className="modal-title">Download kubeconfig file</h5>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<form className="bootbox-form">
|
||||
<div className="bootbox-prompt-message">
|
||||
<span>
|
||||
Select the kubernetes environments to add to the kubeconfig
|
||||
file. You may select across multiple pages.
|
||||
</span>
|
||||
<span className="space-left">
|
||||
{kubeServiceExpiryQuery.data}
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
<br />
|
||||
<Checkbox
|
||||
id="settings-container-truncate-nae"
|
||||
label="Select all (in this page)"
|
||||
checked={isAllPageSelected}
|
||||
onChange={handleSelectAll}
|
||||
/>
|
||||
<div className="datatable">
|
||||
<div className="bootbox-checkbox-list">
|
||||
{environments.map((env) => (
|
||||
<div className={styles.checkbox}>
|
||||
<Checkbox
|
||||
id={`${env.Id}`}
|
||||
label={`${env.Name} (${env.URL})`}
|
||||
checked={!!selection[env.Id]}
|
||||
onChange={() =>
|
||||
toggleSelection(env.Id, !selection[env.Id])
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="footer">
|
||||
<PaginationControls
|
||||
showAll={totalCount <= 100}
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
pageLimit={pageLimit}
|
||||
onPageLimitChange={setPageLimit}
|
||||
totalCount={totalCount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<Button onClick={onClose} color="default">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleDownload}>Download File</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogOverlay>
|
||||
);
|
||||
|
||||
function handleSelectAll() {
|
||||
environments.forEach((env) => toggleSelection(env.Id, !isAllPageSelected));
|
||||
}
|
||||
|
||||
function handleDownload() {
|
||||
confirmKubeconfigSelection();
|
||||
}
|
||||
|
||||
async function confirmKubeconfigSelection() {
|
||||
if (selectionSize === 0) {
|
||||
notifications.warning('No environment was selected', '');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await kcService.downloadKubeconfigFile(
|
||||
Object.keys(selection).map(Number)
|
||||
);
|
||||
onClose();
|
||||
} catch (e) {
|
||||
notifications.error('Failed downloading kubeconfig file', e as Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { useState } from 'react';
|
||||
|
||||
import { EnvironmentId } from '@/portainer/environments/types';
|
||||
|
||||
export function useSelection() {
|
||||
const [selection, setSelection] = useState<Record<EnvironmentId, boolean>>(
|
||||
{}
|
||||
);
|
||||
|
||||
const selectionSize = Object.keys(selection).length;
|
||||
|
||||
return { selection, toggle, selectionSize };
|
||||
|
||||
function toggle(id: EnvironmentId, selected: boolean) {
|
||||
setSelection((prevSelection) => {
|
||||
const newSelection = { ...prevSelection };
|
||||
|
||||
if (!selected) {
|
||||
delete newSelection[id];
|
||||
} else {
|
||||
newSelection[id] = true;
|
||||
}
|
||||
|
||||
return newSelection;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -70,6 +70,7 @@
|
|||
"@lineup-lite/hooks": "^1.6.0",
|
||||
"@nxmix/tokenize-ansi": "^3.0.0",
|
||||
"@open-amt-cloud-toolkit/ui-toolkit-react": "2.0.0",
|
||||
"@reach/dialog": "^0.17.0",
|
||||
"@reach/menu-button": "^0.16.1",
|
||||
"@uirouter/angularjs": "1.0.11",
|
||||
"@uirouter/react": "^1.0.7",
|
||||
|
|
115
yarn.lock
115
yarn.lock
|
@ -1158,6 +1158,13 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.13":
|
||||
version "7.18.3"
|
||||
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
|
||||
integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.3.3":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
|
||||
|
@ -1939,6 +1946,18 @@
|
|||
"@reach/utils" "0.16.0"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@reach/dialog@^0.17.0":
|
||||
version "0.17.0"
|
||||
resolved "https://registry.npmmirror.com/@reach/dialog/-/dialog-0.17.0.tgz#81c48dd4405945dfc6b6c3e5e125db2c4324e9e8"
|
||||
integrity sha512-AnfKXugqDTGbeG3c8xDcrQDE4h9b/vnc27Sa118oQSquz52fneUeX9MeFb5ZEiBJK8T5NJpv7QUTBIKnFCAH5A==
|
||||
dependencies:
|
||||
"@reach/portal" "0.17.0"
|
||||
"@reach/utils" "0.17.0"
|
||||
prop-types "^15.7.2"
|
||||
react-focus-lock "^2.5.2"
|
||||
react-remove-scroll "^2.4.3"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@reach/dropdown@0.16.2":
|
||||
version "0.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@reach/dropdown/-/dropdown-0.16.2.tgz#4aa7df0f716cb448d01bc020d54df595303d5fa6"
|
||||
|
@ -1987,6 +2006,15 @@
|
|||
tiny-warning "^1.0.3"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@reach/portal@0.17.0":
|
||||
version "0.17.0"
|
||||
resolved "https://registry.npmmirror.com/@reach/portal/-/portal-0.17.0.tgz#1dd69ffc8ffc8ba3e26dd127bf1cc4b15f0c6bdc"
|
||||
integrity sha512-+IxsgVycOj+WOeNPL2NdgooUdHPSY285wCtj/iWID6akyr4FgGUK7sMhRM9aGFyrGpx2vzr+eggbUmAVZwOz+A==
|
||||
dependencies:
|
||||
"@reach/utils" "0.17.0"
|
||||
tiny-warning "^1.0.3"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@reach/rect@0.16.0":
|
||||
version "0.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@reach/rect/-/rect-0.16.0.tgz#78cf6acefe2e83d3957fa84f938f6e1fc5700f16"
|
||||
|
@ -2006,6 +2034,14 @@
|
|||
tiny-warning "^1.0.3"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@reach/utils@0.17.0":
|
||||
version "0.17.0"
|
||||
resolved "https://registry.npmmirror.com/@reach/utils/-/utils-0.17.0.tgz#3d1d2ec56d857f04fe092710d8faee2b2b121303"
|
||||
integrity sha512-M5y8fCBbrWeIsxedgcSw6oDlAMQDkl5uv3VnMVJ7guwpf4E48Xlh1v66z/1BgN/WYe2y8mB/ilFD2nysEfdGeA==
|
||||
dependencies:
|
||||
tiny-warning "^1.0.3"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@sgratzl/boxplots@^1.2.2":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@sgratzl/boxplots/-/boxplots-1.3.0.tgz#c9063d98e33a15f880cf4bd3531be71497e2a94e"
|
||||
|
@ -7037,6 +7073,11 @@ detect-newline@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
||||
|
||||
detect-node-es@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
|
||||
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
|
||||
|
||||
detect-node@^2.0.4, detect-node@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
|
||||
|
@ -8613,6 +8654,13 @@ fn.name@1.x.x:
|
|||
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
|
||||
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
|
||||
|
||||
focus-lock@^0.11.2:
|
||||
version "0.11.2"
|
||||
resolved "https://registry.npmmirror.com/focus-lock/-/focus-lock-0.11.2.tgz#aeef3caf1cea757797ac8afdebaec8fd9ab243ed"
|
||||
integrity sha512-pZ2bO++NWLHhiKkgP1bEXHhR1/OjVcSvlCJ98aNJDFeb7H5OOQaO+SKOZle6041O9rv2tmbrO4JzClAvDUHf0g==
|
||||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
follow-redirects@^1.0.0, follow-redirects@^1.14.4:
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
|
@ -8895,6 +8943,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
|||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
get-nonce@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
||||
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
||||
|
||||
get-package-type@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
|
||||
|
@ -14213,6 +14266,13 @@ rc-util@^5.16.1, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.5.0:
|
|||
react-is "^16.12.0"
|
||||
shallowequal "^1.1.0"
|
||||
|
||||
react-clientside-effect@^1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.npmmirror.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz#29f9b14e944a376b03fb650eed2a754dd128ea3a"
|
||||
integrity sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.13"
|
||||
|
||||
react-colorful@^5.1.2:
|
||||
version "5.5.1"
|
||||
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784"
|
||||
|
@ -14282,6 +14342,18 @@ react-feather@^2.0.9:
|
|||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-focus-lock@^2.5.2:
|
||||
version "2.9.1"
|
||||
resolved "https://registry.npmmirror.com/react-focus-lock/-/react-focus-lock-2.9.1.tgz#094cfc19b4f334122c73bb0bff65d77a0c92dd16"
|
||||
integrity sha512-pSWOQrUmiKLkffPO6BpMXN7SNKXMsuOakl652IBuALAu1esk+IcpJyM+ALcYzPTTFz1rD0R54aB9A4HuP5t1Wg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0"
|
||||
focus-lock "^0.11.2"
|
||||
prop-types "^15.6.2"
|
||||
react-clientside-effect "^1.2.6"
|
||||
use-callback-ref "^1.3.0"
|
||||
use-sidecar "^1.1.2"
|
||||
|
||||
react-helmet-async@^1.0.7:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.2.2.tgz#38d58d32ebffbc01ba42b5ad9142f85722492389"
|
||||
|
@ -14352,6 +14424,25 @@ react-refresh@^0.11.0:
|
|||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
|
||||
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
|
||||
|
||||
react-remove-scroll-bar@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmmirror.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.1.tgz#9f13b05b249eaa57c8d646c1ebb83006b3581f5f"
|
||||
integrity sha512-IvGX3mJclEF7+hga8APZczve1UyGMkMG+tjS0o/U1iLgvZRpjFAQEUBJ4JETfvbNlfNnZnoDyWJCICkA15Mghg==
|
||||
dependencies:
|
||||
react-style-singleton "^2.2.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
react-remove-scroll@^2.4.3:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.npmmirror.com/react-remove-scroll/-/react-remove-scroll-2.5.3.tgz#a152196e710e8e5811be39dc352fd8a90b05c961"
|
||||
integrity sha512-NQ1bXrxKrnK5pFo/GhLkXeo3CrK5steI+5L+jynwwIemvZyfXqaL0L5BzwJd7CSwNCU723DZaccvjuyOdoy3Xw==
|
||||
dependencies:
|
||||
react-remove-scroll-bar "^2.3.1"
|
||||
react-style-singleton "^2.2.0"
|
||||
tslib "^2.0.0"
|
||||
use-callback-ref "^1.3.0"
|
||||
use-sidecar "^1.1.2"
|
||||
|
||||
react-router-dom@^6.0.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.2.1.tgz#32ec81829152fbb8a7b045bf593a22eadf019bec"
|
||||
|
@ -14398,6 +14489,15 @@ react-sizeme@^3.0.1:
|
|||
shallowequal "^1.1.0"
|
||||
throttle-debounce "^3.0.1"
|
||||
|
||||
react-style-singleton@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmmirror.com/react-style-singleton/-/react-style-singleton-2.2.0.tgz#70f45f5fef97fdb9a52eed98d1839fa6b9032b22"
|
||||
integrity sha512-nK7mN92DMYZEu3cQcAhfwE48NpzO5RpxjG4okbSqRRbfal9Pk+fG2RdQXTMp+f6all1hB9LIJSt+j7dCYrU11g==
|
||||
dependencies:
|
||||
get-nonce "^1.0.0"
|
||||
invariant "^2.2.4"
|
||||
tslib "^2.0.0"
|
||||
|
||||
react-syntax-highlighter@^13.5.3:
|
||||
version "13.5.3"
|
||||
resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-13.5.3.tgz#9712850f883a3e19eb858cf93fad7bb357eea9c6"
|
||||
|
@ -16944,6 +17044,13 @@ url@^0.11.0:
|
|||
punycode "1.3.2"
|
||||
querystring "0.2.0"
|
||||
|
||||
use-callback-ref@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmmirror.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"
|
||||
integrity sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
use-composed-ref@^1.0.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849"
|
||||
|
@ -16961,6 +17068,14 @@ use-latest@^1.0.0:
|
|||
dependencies:
|
||||
use-isomorphic-layout-effect "^1.0.0"
|
||||
|
||||
use-sidecar@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmmirror.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"
|
||||
integrity sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==
|
||||
dependencies:
|
||||
detect-node-es "^1.1.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
use@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
|
|
Loading…
Reference in New Issue