diff --git a/gradle/changelog/expose_shortcuts_api.yaml b/gradle/changelog/expose_shortcuts_api.yaml new file mode 100644 index 0000000000..c46141d630 --- /dev/null +++ b/gradle/changelog/expose_shortcuts_api.yaml @@ -0,0 +1,2 @@ +- type: changed + description: Expose api for declaring keyboard shortcuts ([#2139](https://github.com/scm-manager/scm-manager/pull/2139)) diff --git a/scm-ui/ui-components/src/index.ts b/scm-ui/ui-components/src/index.ts index 713580452e..c7f2e5ba48 100644 --- a/scm-ui/ui-components/src/index.ts +++ b/scm-ui/ui-components/src/index.ts @@ -82,6 +82,7 @@ export { default as CardColumn } from "./CardColumn"; export { default as CardColumnSmall } from "./CardColumnSmall"; export { default as CommaSeparatedList } from "./CommaSeparatedList"; export { SplitAndReplace, Replacement } from "@scm-manager/ui-text"; +export { useShortcut } from "@scm-manager/ui-shortcuts"; export { regExpPattern as changesetShortLinkRegex } from "./markdown/remarkChangesetShortLinkParser"; export * from "./markdown/PluginApi"; export * from "./devices"; diff --git a/scm-ui/ui-components/src/table/Column.tsx b/scm-ui/ui-components/src/table/Column.tsx index 488ae0b571..2e4570f2f7 100644 --- a/scm-ui/ui-components/src/table/Column.tsx +++ b/scm-ui/ui-components/src/table/Column.tsx @@ -25,17 +25,20 @@ import React, { FC, ReactNode } from "react"; import { ColumnProps } from "./types"; type Props = ColumnProps & { - children: (row: any, columnIndex: number) => ReactNode; + children: (row: any, columnIndex: number, rowIndex: number) => ReactNode; }; -const Column: FC = ({ row, columnIndex, children }) => { +const Column: FC = ({ row, columnIndex, rowIndex, children }) => { if (row === undefined) { throw new Error("missing row, use column only as child of Table"); } if (columnIndex === undefined) { throw new Error("missing row, use column only as child of Table"); } - return <>{children(row, columnIndex)}; + if (rowIndex === undefined) { + throw new Error("missing row, use column only as child of Table"); + } + return <>{children(row, columnIndex, rowIndex)}; }; export default Column; diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index 518a67b50b..4953808490 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -63,7 +63,7 @@ const Table: FC = ({ data, sortable, children, emptyMessage, className }) {React.Children.map(children, (child, columnIndex) => { const { className: columnClassName, ...childProperties } = (child as ReactElement).props; return ( - {React.cloneElement((child as ReactElement), { ...childProperties, columnIndex, row })} + {React.cloneElement((child as ReactElement), { ...childProperties, columnIndex, rowIndex, row })} ); })} diff --git a/scm-ui/ui-components/src/table/types.ts b/scm-ui/ui-components/src/table/types.ts index 7604756d03..58d4ead500 100644 --- a/scm-ui/ui-components/src/table/types.ts +++ b/scm-ui/ui-components/src/table/types.ts @@ -30,6 +30,7 @@ export type ColumnProps = { header: ReactNode; row?: any; columnIndex?: number; + rowIndex?: number; createComparator?: (props: any, columnIndex: number) => Comparator; ascendingIcon?: string; descendingIcon?: string; diff --git a/scm-ui/ui-shortcuts/package.json b/scm-ui/ui-shortcuts/package.json new file mode 100644 index 0000000000..93c76f4c7f --- /dev/null +++ b/scm-ui/ui-shortcuts/package.json @@ -0,0 +1,42 @@ +{ + "name": "@scm-manager/ui-shortcuts", + "version": "2.39.2-SNAPSHOT", + "license": "MIT", + "private": true, + "main": "build/index.js", + "module": "build/index.mjs", + "types": "build/index.d.ts", + "files": [ + "build" + ], + "scripts": { + "build": "tsup ./src/index.ts -d build --format esm,cjs --dts", + "lint": "eslint src", + "typecheck": "tsc" + }, + "peerDependencies": { + "react": "17" + }, + "dependencies": { + "mousetrap": "1.6.5" + }, + "devDependencies": { + "@types/mousetrap": "1.6.5", + "@scm-manager/babel-preset": "^2.13.1", + "@scm-manager/prettier-config": "^2.10.1", + "@scm-manager/eslint-config": "^2.16.0", + "@scm-manager/tsconfig": "^2.13.0" + }, + "babel": { + "presets": [ + "@scm-manager/babel-preset" + ] + }, + "prettier": "@scm-manager/prettier-config", + "eslintConfig": { + "extends": "@scm-manager/eslint-config" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/scm-ui/ui-shortcuts/src/index.ts b/scm-ui/ui-shortcuts/src/index.ts new file mode 100644 index 0000000000..fb168ce6cd --- /dev/null +++ b/scm-ui/ui-shortcuts/src/index.ts @@ -0,0 +1,27 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export { default as useShortcut } from "./useShortcut"; +export { default as useShortcutDocs, ShortcutDocsContextProvider } from "./useShortcutDocs"; +export { default as usePauseShortcuts } from "./usePauseShortcuts"; diff --git a/scm-ui/ui-webapp/src/shortcuts/usePauseShortcuts.ts b/scm-ui/ui-shortcuts/src/usePauseShortcuts.ts similarity index 96% rename from scm-ui/ui-webapp/src/shortcuts/usePauseShortcuts.ts rename to scm-ui/ui-shortcuts/src/usePauseShortcuts.ts index 451613d2a3..e7fcc09a08 100644 --- a/scm-ui/ui-webapp/src/shortcuts/usePauseShortcuts.ts +++ b/scm-ui/ui-shortcuts/src/usePauseShortcuts.ts @@ -24,6 +24,7 @@ import { useEffect } from "react"; import Mousetrap from "mousetrap"; +import "mousetrap/plugins/pause/mousetrap-pause.min.js"; /** * Pauses or unpauses all shortcuts provided by {@link useShortcut}. diff --git a/scm-ui/ui-webapp/src/shortcuts/useShortcut.ts b/scm-ui/ui-shortcuts/src/useShortcut.ts similarity index 77% rename from scm-ui/ui-webapp/src/shortcuts/useShortcut.ts rename to scm-ui/ui-shortcuts/src/useShortcut.ts index 2e0238ea32..690a800b0b 100644 --- a/scm-ui/ui-webapp/src/shortcuts/useShortcut.ts +++ b/scm-ui/ui-shortcuts/src/useShortcut.ts @@ -33,6 +33,13 @@ export type UseShortcutOptions = { * @default true */ active?: boolean; + + /** + * The translated description used for the shortcut documentation. + * + * If no description is supplied, there will be no entry in the shortcut summary table. + */ + description?: string; }; /** @@ -62,8 +69,7 @@ export type UseShortcutOptions = { * Please also refer to the examples. * * @param key The keycode combination that triggers the callback - * @param callback The function that is executed when the key combination is pressed - * @param description The translated description used for the shortcut documentation + * @param callback The function that is executed when the key combination is pressed, returning `true` additionally executes default browser behaviour * @param options Whether the shortcut is currently active, defaults to true * @example useShortcut("a b", ...) * @example useShortcut("ctrl+shift+k", ...) @@ -72,29 +78,33 @@ export type UseShortcutOptions = { */ export default function useShortcut( key: string, - callback: (e: KeyboardEvent) => void, - description: string, + callback: (e: KeyboardEvent) => void | boolean, options?: UseShortcutOptions ) { const { add, remove } = useShortcutDocs(); useEffect(() => { - const active = !options || options.active === undefined || options.active; + const active = options?.active ?? true; + const description = options?.description; if (active) { - add(key, description); + if (description) { + add(key, description); + } Mousetrap.bind(key, (e) => { - callback(e); + const callbackResult = callback(e); /* - * Returning false disables default event behaviour and stops event bubbling. + * Returning false by default disables standard browser event behaviour and stops event bubbling. * Otherwise, a shortcut that moves focus to an input field would cause the key to be entered into the input at the same time. - * We could move the decision to the callback, but this behaviour is an implementation detail of Mousetrap which we would like to hide. + * Shortcuts can explicitly return `true` to re-enable event bubbling and browser behaviour. */ - return false; + return callbackResult ?? false; }); } return () => { - remove(key); + if (description) { + remove(key); + } Mousetrap.unbind(key); }; - }, [key, callback, add, remove, options, description]); + }, [key, callback, add, remove, options]); } diff --git a/scm-ui/ui-webapp/src/shortcuts/useShortcutDocs.tsx b/scm-ui/ui-shortcuts/src/useShortcutDocs.tsx similarity index 95% rename from scm-ui/ui-webapp/src/shortcuts/useShortcutDocs.tsx rename to scm-ui/ui-shortcuts/src/useShortcutDocs.tsx index 4ae9cbe6fa..cc992be046 100644 --- a/scm-ui/ui-webapp/src/shortcuts/useShortcutDocs.tsx +++ b/scm-ui/ui-shortcuts/src/useShortcutDocs.tsx @@ -22,7 +22,8 @@ * SOFTWARE. */ -import React, { FC, useContext, useMemo, useRef } from "react"; +import type { FC } from "react"; +import React, { useContext, useMemo, useRef } from "react"; export type ShortcutDocsContextType = { docs: Readonly>; diff --git a/scm-ui/ui-shortcuts/tsconfig.json b/scm-ui/ui-shortcuts/tsconfig.json new file mode 100644 index 0000000000..bd1e5ff43c --- /dev/null +++ b/scm-ui/ui-shortcuts/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "@scm-manager/tsconfig", + "include": [ + "./src" + ] +} diff --git a/scm-ui/ui-webapp/package.json b/scm-ui/ui-webapp/package.json index 75aa740032..3d58557579 100644 --- a/scm-ui/ui-webapp/package.json +++ b/scm-ui/ui-webapp/package.json @@ -10,6 +10,7 @@ "@scm-manager/ui-modules": "2.39.2-SNAPSHOT", "@scm-manager/ui-syntaxhighlighting": "2.39.2-SNAPSHOT", "@scm-manager/ui-text": "2.39.2-SNAPSHOT", + "@scm-manager/ui-shortcuts": "2.39.2-SNAPSHOT", "@scm-manager/ui-legacy": "2.39.2-SNAPSHOT", "classnames": "^2.2.5", "history": "^4.10.1", @@ -27,7 +28,6 @@ "string_score": "^0.1.22", "styled-components": "^5.3.5", "systemjs": "0.21.6", - "mousetrap": "^1.6.5", "ua-parser-js": "^1.0.2" }, "scripts": { @@ -50,7 +50,6 @@ "@types/react-router-dom": "^5.3.3", "@types/styled-components": "^5.1.25", "@types/systemjs": "^0.20.6", - "@types/mousetrap": "^1.6.9", "@types/ua-parser-js": "^0.7.36", "fetch-mock": "^7.5.1", "react-test-renderer": "^17.0.1" diff --git a/scm-ui/ui-webapp/public/locales/de/repos.json b/scm-ui/ui-webapp/public/locales/de/repos.json index 744da2f5da..80a9a5a691 100644 --- a/scm-ui/ui-webapp/public/locales/de/repos.json +++ b/scm-ui/ui-webapp/public/locales/de/repos.json @@ -540,10 +540,10 @@ } }, "shortcuts": { - "info": "Info", - "branches": "Branches", - "tags": "Tags", - "code": "Code", - "settings": "Einstellungen" + "info": "Wechsel zur Repository-Info", + "branches": "Wechsel zu den Branches", + "tags": "Wechsel zu den Tags", + "code": "Wechsel zum Code", + "settings": "Wechsel zu den Einstellungen" } } diff --git a/scm-ui/ui-webapp/public/locales/en/repos.json b/scm-ui/ui-webapp/public/locales/en/repos.json index 9acd935234..7e005254ea 100644 --- a/scm-ui/ui-webapp/public/locales/en/repos.json +++ b/scm-ui/ui-webapp/public/locales/en/repos.json @@ -547,10 +547,10 @@ } }, "shortcuts": { - "info": "Info", - "branches": "Branches", - "tags": "Tags", - "code": "Code", - "settings": "Settings" + "info": "Switch to repository info", + "branches": "Switch to branches", + "tags": "Switch to tags", + "code": "Switch to code", + "settings": "Switch to settings" } } diff --git a/scm-ui/ui-webapp/src/containers/App.tsx b/scm-ui/ui-webapp/src/containers/App.tsx index 73140f9aef..e4b5beb40a 100644 --- a/scm-ui/ui-webapp/src/containers/App.tsx +++ b/scm-ui/ui-webapp/src/containers/App.tsx @@ -30,7 +30,7 @@ import { useIndex, useSubject } from "@scm-manager/ui-api"; import { ErrorPage, Footer, Header, Loading } from "@scm-manager/ui-components"; import { binder } from "@scm-manager/ui-extensions"; import usePauseShortcutsWhenModalsActive from "../shortcuts/usePauseShortcutsWhenModalsActive"; -import useShortcut from "../shortcuts/useShortcut"; +import { useShortcut } from "@scm-manager/ui-shortcuts"; import Login from "./Login"; import NavigationBar from "./NavigationBar"; import styled from "styled-components"; @@ -41,6 +41,7 @@ const AppWrapper = styled.div` display: flex; flex-direction: column; `; + const App: FC = () => { const { data: index } = useIndex(); const { isLoading, error, isAuthenticated, isAnonymous, me } = useSubject(); @@ -49,17 +50,21 @@ const App: FC = () => { const history = useHistory(); - useShortcut("option+r", () => history.push("/repos/"), t("shortcuts.repositories"), { + useShortcut("option+r", () => history.push("/repos/"), { active: !!index?._links["repositories"], + description: t("shortcuts.repositories"), }); - useShortcut("option+u", () => history.push("/users/"), t("shortcuts.users"), { + useShortcut("option+u", () => history.push("/users/"), { active: !!index?._links["users"], + description: t("shortcuts.users"), }); - useShortcut("option+g", () => history.push("/groups/"), t("shortcuts.groups"), { + useShortcut("option+g", () => history.push("/groups/"), { active: !!index?._links["groups"], + description: t("shortcuts.groups"), }); - useShortcut("option+a", () => history.push("/admin/"), t("shortcuts.admin"), { + useShortcut("option+a", () => history.push("/admin/"), { active: !!index?._links["config"], + description: t("shortcuts.admin"), }); if (!index) { @@ -85,7 +90,7 @@ const App: FC = () => { } return ( - +
diff --git a/scm-ui/ui-webapp/src/containers/Index.tsx b/scm-ui/ui-webapp/src/containers/Index.tsx index edba6e458d..d02b1ab749 100644 --- a/scm-ui/ui-webapp/src/containers/Index.tsx +++ b/scm-ui/ui-webapp/src/containers/Index.tsx @@ -23,7 +23,7 @@ */ import React, { FC, useState } from "react"; import App from "./App"; -import { ActiveModalCountContextProvider, ErrorBoundary, Header, Loading } from "@scm-manager/ui-components"; +import { ErrorBoundary, Header, Loading } from "@scm-manager/ui-components"; import PluginLoader from "./PluginLoader"; import ScrollToTop from "./ScrollToTop"; import IndexErrorPage from "./IndexErrorPage"; @@ -33,7 +33,6 @@ import i18next from "i18next"; import { binder, extensionPoints } from "@scm-manager/ui-extensions"; import InitializationAdminAccountStep from "./InitializationAdminAccountStep"; import InitializationPluginWizardStep from "./InitializationPluginWizardStep"; -import { ShortcutDocsContextProvider } from "../shortcuts/useShortcutDocs"; const Index: FC = () => { const { isLoading, error, data } = useIndex(); @@ -61,15 +60,11 @@ const Index: FC = () => { return ( - - - - setPluginsLoaded(true)}> - - - - - + + setPluginsLoaded(true)}> + + + ); diff --git a/scm-ui/ui-webapp/src/containers/OmniSearch.tsx b/scm-ui/ui-webapp/src/containers/OmniSearch.tsx index 5c0e8a3649..a10f1acc62 100644 --- a/scm-ui/ui-webapp/src/containers/OmniSearch.tsx +++ b/scm-ui/ui-webapp/src/containers/OmniSearch.tsx @@ -46,7 +46,7 @@ import SyntaxModal from "../search/SyntaxModal"; import SearchErrorNotification from "../search/SearchErrorNotification"; import queryString from "query-string"; import { orderTypes } from "../search/Search"; -import useShortcut from "../shortcuts/useShortcut"; +import { useShortcut } from "@scm-manager/ui-shortcuts"; const Input = styled.input` border-radius: 4px !important; @@ -358,7 +358,9 @@ const OmniSearch: FC = () => { searchTypes.sort(orderTypes(t)); const id = useCallback(namespaceAndName, []); - useShortcut("/", () => searchInputRef.current?.focus(), t("shortcuts.search")); + useShortcut("/", () => searchInputRef.current?.focus(), { + description: t("shortcuts.search"), + }); const entries = useMemo(() => { const newEntries = []; diff --git a/scm-ui/ui-webapp/src/index.tsx b/scm-ui/ui-webapp/src/index.tsx index c4517ad717..0c38d696b7 100644 --- a/scm-ui/ui-webapp/src/index.tsx +++ b/scm-ui/ui-webapp/src/index.tsx @@ -30,16 +30,13 @@ import i18n from "./i18n"; import { BrowserRouter as Router } from "react-router-dom"; -import { urls } from "@scm-manager/ui-components"; +import { ActiveModalCountContextProvider, urls } from "@scm-manager/ui-components"; import { binder, extensionPoints } from "@scm-manager/ui-extensions"; import ChangesetShortLink from "./repos/components/changesets/ChangesetShortLink"; import "./tokenExpired"; import { ApiProvider } from "@scm-manager/ui-api"; -// Used by useShortcut -import "mousetrap"; -// Used by usePauseShortcuts -import "mousetrap/plugins/pause/mousetrap-pause.min"; +import { ShortcutDocsContextProvider } from "@scm-manager/ui-shortcuts"; binder.bind("changeset.description.tokens", ChangesetShortLink); @@ -51,9 +48,13 @@ if (!root) { ReactDOM.render( - - - + + + + + + + , root diff --git a/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx b/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx index d79ce7e41c..883a3ae45e 100644 --- a/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx @@ -62,7 +62,7 @@ import CompareRoot from "../compare/CompareRoot"; import TagRoot from "../tags/container/TagRoot"; import { useIndexLinks, useNamespaceAndNameContext, useRepository } from "@scm-manager/ui-api"; import styled from "styled-components"; -import useShortcut from "../../shortcuts/useShortcut"; +import { useShortcut } from "@scm-manager/ui-shortcuts"; const TagGroup = styled.span` & > * { @@ -111,17 +111,24 @@ const RepositoryRoot = () => { return ""; }, [repository]); - useShortcut("g i", () => history.push(`${url}/info`), t("shortcuts.info")); - useShortcut("g b", () => history.push(`${url}/branches/`), t("shortcuts.branches"), { + useShortcut("g i", () => history.push(`${url}/info`), { + description: t("shortcuts.info"), + }); + useShortcut("g b", () => history.push(`${url}/branches/`), { active: !!repository?._links["branches"], + description: t("shortcuts.branches"), }); - useShortcut("g t", () => history.push(`${url}/tags/`), t("shortcuts.tags"), { + useShortcut("g t", () => history.push(`${url}/tags/`), { active: !!repository?._links["tags"], + description: t("shortcuts.tags"), }); - useShortcut("g c", () => history.push(evaluateDestinationForCodeLink()), t("shortcuts.code"), { + useShortcut("g c", () => history.push(evaluateDestinationForCodeLink()), { active: !!repository?._links[codeLinkname], + description: t("shortcuts.code"), + }); + useShortcut("g s", () => history.push(`${url}/settings/general`), { + description: t("shortcuts.settings"), }); - useShortcut("g s", () => history.push(`${url}/settings/general`), t("shortcuts.settings")); useEffect(() => { if (repository) { diff --git a/scm-ui/ui-webapp/src/shortcuts/ShortcutDocsModal.tsx b/scm-ui/ui-webapp/src/shortcuts/ShortcutDocsModal.tsx index 6403823395..cef2f8ab64 100644 --- a/scm-ui/ui-webapp/src/shortcuts/ShortcutDocsModal.tsx +++ b/scm-ui/ui-webapp/src/shortcuts/ShortcutDocsModal.tsx @@ -22,9 +22,8 @@ * SOFTWARE. */ import React, { useState } from "react"; -import useShortcutDocs from "./useShortcutDocs"; +import { useShortcutDocs, useShortcut } from "@scm-manager/ui-shortcuts"; import { Column, Modal, Table } from "@scm-manager/ui-components"; -import useShortcut from "./useShortcut"; import { useTranslation } from "react-i18next"; import classNames from "classnames"; import splitKeyCombination from "./splitKeyCombination"; diff --git a/scm-ui/ui-webapp/src/shortcuts/usePauseShortcutsWhenModalsActive.ts b/scm-ui/ui-webapp/src/shortcuts/usePauseShortcutsWhenModalsActive.ts index 46b91dc111..315d6e813c 100644 --- a/scm-ui/ui-webapp/src/shortcuts/usePauseShortcutsWhenModalsActive.ts +++ b/scm-ui/ui-webapp/src/shortcuts/usePauseShortcutsWhenModalsActive.ts @@ -23,7 +23,7 @@ */ import { useActiveModals } from "@scm-manager/ui-components"; -import usePauseShortcuts from "./usePauseShortcuts"; +import { usePauseShortcuts } from "@scm-manager/ui-shortcuts"; /** * Keyboard shortcuts are not active in modals using {@link useActiveModals} to determine whether any modals are open. diff --git a/yarn.lock b/yarn.lock index 61435887e3..b33ef59686 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3889,10 +3889,10 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/mousetrap@^1.6.9": - version "1.6.9" - resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.9.tgz#f1ef9adbd1eac3466f21b6988b1c82c633a45340" - integrity sha512-HUAiN65VsRXyFCTicolwb5+I7FM6f72zjMWr+ajGk+YTvzBgXqa2A5U7d+rtsouAkunJ5U4Sb5lNJjo9w+nmXg== +"@types/mousetrap@1.6.5": + version "1.6.5" + resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.5.tgz#e95569aa6273dbe0ed1814f86287547cc06e93c9" + integrity sha512-OwVhKFim9Y/MprzCe4I6a59p31pMy8+LrtP6qS7J0kaOxYmW6VVJPBw5NYm+g7nSbgPUz22FvqU1F1hC5YGTfg== "@types/node-fetch@^2.5.7": version "2.6.2" @@ -13520,7 +13520,7 @@ moo@^0.5.0: resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== -mousetrap@^1.6.5: +mousetrap@1.6.5: version "1.6.5" resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9" integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==