diff --git a/airflow/ui/package.json b/airflow/ui/package.json index 0526f20d4d040..322623e9db68b 100644 --- a/airflow/ui/package.json +++ b/airflow/ui/package.json @@ -39,6 +39,7 @@ "react-chartjs-2": "^5.2.0", "react-dom": "^18.3.1", "react-hook-form": "^7.20.0", + "react-hotkeys-hook": "^4.6.1", "react-icons": "^5.4.0", "react-json-view": "^1.21.3", "react-markdown": "^9.0.1", diff --git a/airflow/ui/pnpm-lock.yaml b/airflow/ui/pnpm-lock.yaml index 5b89d9517c81f..20ae58594252c 100644 --- a/airflow/ui/pnpm-lock.yaml +++ b/airflow/ui/pnpm-lock.yaml @@ -77,6 +77,9 @@ importers: react-hook-form: specifier: ^7.20.0 version: 7.53.1(react@18.3.1) + react-hotkeys-hook: + specifier: ^4.6.1 + version: 4.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-icons: specifier: ^5.4.0 version: 5.4.0(react@18.3.1) @@ -3441,6 +3444,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 + react-hotkeys-hook@4.6.1: + resolution: {integrity: sha512-XlZpbKUj9tkfgPgT9gA+1p7Ey6vFIZHttUjPqpTdyT5nqQ8mHL7elxvSbaC+dpSiHUSmr21Ya1mDxBZG3aje4Q==} + peerDependencies: + react: '>=16.8.1' + react-dom: '>=16.8.1' + react-icons@5.4.0: resolution: {integrity: sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==} peerDependencies: @@ -8475,6 +8484,11 @@ snapshots: dependencies: react: 18.3.1 + react-hotkeys-hook@4.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-icons@5.4.0(react@18.3.1): dependencies: react: 18.3.1 diff --git a/airflow/ui/src/components/SearchDags/SearchDagsButton.tsx b/airflow/ui/src/components/SearchDags/SearchDagsButton.tsx index 942e3e5a848dc..83974fd090985 100644 --- a/airflow/ui/src/components/SearchDags/SearchDagsButton.tsx +++ b/airflow/ui/src/components/SearchDags/SearchDagsButton.tsx @@ -16,25 +16,37 @@ * specific language governing permissions and limitations * under the License. */ -import { Button, Box } from "@chakra-ui/react"; +import { Button, Box, Kbd } from "@chakra-ui/react"; import { useState } from "react"; +import { useHotkeys } from "react-hotkeys-hook"; import { MdSearch } from "react-icons/md"; import { Dialog } from "src/components/ui"; +import { getMetaKey } from "src/utils"; import { SearchDags } from "./SearchDags"; export const SearchDagsButton = () => { const [isOpen, setIsOpen] = useState(false); + const metaKey = getMetaKey(); const onOpenChange = () => { setIsOpen(false); }; + useHotkeys( + "mod+k", + () => { + setIsOpen(true); + }, + [isOpen], + { preventDefault: true }, + ); + return ( diff --git a/airflow/ui/src/utils/getMetaKey.ts b/airflow/ui/src/utils/getMetaKey.ts new file mode 100644 index 0000000000000..4e1e867c2c268 --- /dev/null +++ b/airflow/ui/src/utils/getMetaKey.ts @@ -0,0 +1,20 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const getMetaKey = () => (navigator.appVersion.includes("Mac") ? "⌘" : "Ctrl"); diff --git a/airflow/ui/src/utils/index.ts b/airflow/ui/src/utils/index.ts index bdfe8ac8d8002..60357e9470aba 100644 --- a/airflow/ui/src/utils/index.ts +++ b/airflow/ui/src/utils/index.ts @@ -20,3 +20,4 @@ export { capitalize } from "./capitalize"; export { pluralize } from "./pluralize"; export { getDuration } from "./datetime_utils"; +export { getMetaKey } from "./getMetaKey";