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]

Reply via email to