This is an automated email from the ASF dual-hosted git repository.
arshad pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/frontend-refactor by this push:
new 76b8f1a106 AMBARI-26543 : Ambari Web React: Implement Modal Manager
(#4053)
76b8f1a106 is described below
commit 76b8f1a1062fa9f18c369f1279314ff34348db60
Author: Himanshu Maurya <[email protected]>
AuthorDate: Tue Sep 9 10:39:18 2025 +0530
AMBARI-26543 : Ambari Web React: Implement Modal Manager (#4053)
---
ambari-web/latest/src/App.tsx | 9 ++++
.../latest/src/{App.tsx => store/CustomModal.tsx} | 35 +++++++++----
ambari-web/latest/src/store/ModalContext.tsx | 56 ++++++++++++++++++++
ambari-web/latest/src/store/ModalManager.ts | 60 ++++++++++++++++++++++
4 files changed, 150 insertions(+), 10 deletions(-)
diff --git a/ambari-web/latest/src/App.tsx b/ambari-web/latest/src/App.tsx
index 227a0ea9ba..91676360a1 100755
--- a/ambari-web/latest/src/App.tsx
+++ b/ambari-web/latest/src/App.tsx
@@ -16,6 +16,10 @@
* limitations under the License.
*/
+import { AppProvider } from "./store/context";
+import CustomModal from "./store/CustomModal";
+import { ModalProvider } from "./store/ModalContext";
+
function App() {
function switchToClassic(){
window.location.href=window.location.href.replace("latest","classic")
@@ -23,6 +27,11 @@ function App() {
return (
<>
+ <AppProvider>
+ <ModalProvider>
+ <CustomModal />
+ </ModalProvider>
+ </AppProvider>
<button className='btn' onClick={switchToClassic}>
Switch to classic UI
</button>
diff --git a/ambari-web/latest/src/App.tsx
b/ambari-web/latest/src/store/CustomModal.tsx
old mode 100755
new mode 100644
similarity index 54%
copy from ambari-web/latest/src/App.tsx
copy to ambari-web/latest/src/store/CustomModal.tsx
index 227a0ea9ba..393951cd54
--- a/ambari-web/latest/src/App.tsx
+++ b/ambari-web/latest/src/store/CustomModal.tsx
@@ -16,18 +16,33 @@
* limitations under the License.
*/
-function App() {
- function switchToClassic(){
- window.location.href=window.location.href.replace("latest","classic")
- }
+import React from "react";
+import { useModal } from "./ModalContext";
+import Modal, { ModalProps } from "../components/Modal";
+import { get } from "lodash";
+
+const CustomModal: React.FC = () => {
+ const { modalStack, hideModal } = useModal();
return (
<>
- <button className='btn' onClick={switchToClassic}>
- Switch to classic UI
- </button>
+ {modalStack.map((modal, index) =>
+ React.isValidElement(modal) ? (
+ <React.Fragment key={index}>{modal}</React.Fragment>
+ ) : (
+ <Modal
+ key={index}
+ {...(modal as Omit<ModalProps, "isOpen">)}
+ isOpen={true}
+ onClose={() => {
+ hideModal();
+ get(modal, "onClose", () => {})();
+ }}
+ />
+ )
+ )}
</>
- )
-}
+ );
+};
-export default App
+export default CustomModal;
diff --git a/ambari-web/latest/src/store/ModalContext.tsx
b/ambari-web/latest/src/store/ModalContext.tsx
new file mode 100644
index 0000000000..a865ac594b
--- /dev/null
+++ b/ambari-web/latest/src/store/ModalContext.tsx
@@ -0,0 +1,56 @@
+/**
+ * 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.
+ */
+
+import React, { createContext, useState, useEffect, ReactNode } from "react";
+import modalManager, { CustomModalProps } from "./ModalManager";
+
+interface ModalContextType {
+ modalStack: CustomModalProps[];
+ hideModal: () => void;
+}
+
+const ModalContext = createContext<ModalContextType | undefined>(undefined);
+
+export const ModalProvider: React.FC<{ children: ReactNode }> = ({
+ children,
+}) => {
+ const [modalStack, setModalStack] = useState<CustomModalProps[]>([]);
+
+ useEffect(() => {
+ const unsubscribe = modalManager.subscribe(setModalStack);
+ return () => unsubscribe();
+ }, []);
+
+ const hideModal = () => {
+ modalManager.hide();
+ };
+
+ return (
+ <ModalContext.Provider value={{ modalStack, hideModal }}>
+ {children}
+ </ModalContext.Provider>
+ );
+};
+
+export const useModal = (): ModalContextType => {
+ const context = React.useContext(ModalContext);
+ if (!context) {
+ throw new Error("useModal must be used within a ModalProvider");
+ }
+ return context;
+};
diff --git a/ambari-web/latest/src/store/ModalManager.ts
b/ambari-web/latest/src/store/ModalManager.ts
new file mode 100644
index 0000000000..2c558606b1
--- /dev/null
+++ b/ambari-web/latest/src/store/ModalManager.ts
@@ -0,0 +1,60 @@
+/**
+ * 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.
+ */
+
+import { ReactNode } from "react";
+import { ModalProps } from "../components/Modal";
+
+export type CustomModalProps = Omit<ModalProps, "isOpen"> | ReactNode;
+
+class ModalManager {
+ private listeners: ((props: CustomModalProps[]) => void)[] = [];
+ private modalStack: CustomModalProps[] = [];
+
+ show(props: CustomModalProps) {
+ this.modalStack.push(props);
+ this.notify();
+ }
+
+ hide() {
+ const modal = this.modalStack.pop();
+ if (this.isModalProps(modal) && modal.onClose) {
+ modal.onClose();
+ }
+ this.notify();
+ }
+
+ subscribe(listener: (props: CustomModalProps[]) => void) {
+ this.listeners.push(listener);
+ return () => {
+ this.listeners = this.listeners.filter((l) => l !== listener);
+ };
+ }
+
+ private notify() {
+ this.listeners.forEach((listener) => listener([...this.modalStack]));
+ }
+
+ private isModalProps(
+ modal: CustomModalProps | undefined
+ ): modal is Omit<ModalProps, "isOpen"> {
+ return (modal as Omit<ModalProps, "isOpen">)?.onClose !== undefined;
+ }
+}
+
+const modalManager = new ModalManager();
+export default modalManager;
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]