diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx index bff2facde..a9f16b977 100644 --- a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx +++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx @@ -39,7 +39,6 @@ export default function PipelineConsole() { , - "separator", { text: "View as plain text", icon: DOCUMENT, diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx index 218ff9752..eaa534d7b 100644 --- a/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx +++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx @@ -14,7 +14,7 @@ export default function StageNodeLink({ agent }: StageNodeLinkProps) { } return ( -
  • +
  • ) => { setMainViewVisibility(e.target.value as MainViewVisibility); }; @@ -121,6 +126,7 @@ export default function StagesCustomization() { +
    ); } diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx index 1a674418a..2e742f1f1 100644 --- a/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx +++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx @@ -30,6 +30,11 @@ interface LayoutPreferences { setTreeViewWidth: (width: number) => void; setStageViewWidth: (width: number) => void; setStageViewHeight: (height: number) => void; + /** + * Returns true if the current window width is less than the mobile breakpoint. + * Used for disabling customization options in favor of a mobile-friendly layout. + */ + isMobile: boolean; } // CONTEXT @@ -65,20 +70,27 @@ const loadFromLocalStorage = (key: string, fallback: T): T => { return fallback; }; +const MOBILE_BREAKPOINT = 700; + // PROVIDER export const LayoutPreferencesProvider = ({ children, }: { children: ReactNode; }) => { - const [stageViewPosition, setStageViewPositionState] = + const [windowWidth, setWindowWidth] = useState( + typeof window !== "undefined" ? window.innerWidth : 1000, + ); + + const [persistedStageViewPosition, setStageViewPositionState] = useState( loadFromLocalStorage(LS_KEYS.stageViewPosition, StageViewPosition.TOP), ); - const [mainViewVisibility, setMainViewVisibilityState] = + const [persistedMainViewVisibility, setMainViewVisibilityState] = useState( loadFromLocalStorage(LS_KEYS.mainViewVisibility, MainViewVisibility.BOTH), ); + const [treeViewWidth, setTreeViewWidthState] = useState( loadFromLocalStorage(LS_KEYS.treeViewWidth, 300), ); @@ -89,14 +101,33 @@ export const LayoutPreferencesProvider = ({ loadFromLocalStorage(LS_KEYS.stageViewHeight, 250), ); - // Save to localStorage + // Handle responsive override + const isMobile = windowWidth < MOBILE_BREAKPOINT; + const stageViewPosition = isMobile + ? StageViewPosition.TOP + : persistedStageViewPosition; + const mainViewVisibility = isMobile + ? MainViewVisibility.GRAPH_ONLY + : persistedMainViewVisibility; + + // Save to localStorage only when not in mobile mode useEffect(() => { - window.localStorage.setItem(LS_KEYS.stageViewPosition, stageViewPosition); - }, [stageViewPosition]); + if (!isMobile) { + window.localStorage.setItem( + LS_KEYS.stageViewPosition, + persistedStageViewPosition, + ); + } + }, [persistedStageViewPosition, isMobile]); useEffect(() => { - window.localStorage.setItem(LS_KEYS.mainViewVisibility, mainViewVisibility); - }, [mainViewVisibility]); + if (!isMobile) { + window.localStorage.setItem( + LS_KEYS.mainViewVisibility, + persistedMainViewVisibility, + ); + } + }, [persistedMainViewVisibility, isMobile]); useEffect(() => { window.localStorage.setItem( @@ -119,7 +150,13 @@ export const LayoutPreferencesProvider = ({ ); }, [stageViewHeight]); - // Setter wrappers + // Update window width on resize + useEffect(() => { + const handleResize = () => setWindowWidth(window.innerWidth); + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + const setStageViewPosition = (position: StageViewPosition) => setStageViewPositionState(position); const setMainViewVisibility = (visibility: MainViewVisibility) => @@ -142,6 +179,7 @@ export const LayoutPreferencesProvider = ({ setTreeViewWidth, setStageViewWidth, setStageViewHeight, + isMobile, }} > {children} diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss b/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss index d4e8cd3bc..de68970ee 100644 --- a/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss +++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss @@ -3,6 +3,7 @@ display: grid; gap: var(--section-padding); animation: fade-in 0.1s ease-in-out both; + align-items: start; } @keyframes fade-in { @@ -12,7 +13,7 @@ } .pgv-split-view__side-panel { - position: relative; + display: contents; } .pgv-split-view__divider { diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx index b767df0d6..140a5de64 100644 --- a/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx +++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx @@ -80,7 +80,7 @@ export default function StageDetails({ stage }: StageDetailsProps) {
  • {stage.pauseDurationMillis !== 0 && ( -
  • +