1+ /*
2+ * Copyright (c) 2019-present Sonatype, Inc.
3+ * This program and the accompanying materials are made available under
4+ * the terms of the Eclipse Public License 2.0 which accompanies this
5+ * distribution and is available at https://www.eclipse.org/legal/epl-2.0/.
6+ */
7+ import React , { useState , useRef , useEffect } from 'react' ;
8+ import { NxNavPillMenu , NavPillMenuItem , NxFontAwesomeIcon } from '@sonatype/react-shared-components' ;
9+ import { faArrowUp } from '@fortawesome/free-solid-svg-icons' ;
10+
11+ const items : NavPillMenuItem [ ] = [
12+ { id : 'overview' , label : 'Overview' , scrollTarget : 'overview-section' } ,
13+ { id : 'features' , label : 'Features' , scrollTarget : 'features-section' } ,
14+ { id : 'getting-started' , label : 'Getting Started' , scrollTarget : 'getting-started-section' } ,
15+ { id : 'api' , label : 'API Reference' , scrollTarget : 'api-section' } ,
16+ { id : 'examples' , label : 'Examples' , scrollTarget : 'examples-section' }
17+ ] ;
18+
19+ export default function NxNavPillMenuExample ( ) {
20+ const [ showBackToTop , setShowBackToTop ] = useState ( false ) ;
21+ const scrollContainerRef = useRef < HTMLDivElement > ( null ) ;
22+
23+ const handleItemClick = ( item : NavPillMenuItem ) => {
24+ console . log ( 'Clicked nav pill:' , item . label ) ;
25+ } ;
26+
27+ // Back to top functionality
28+ const scrollToTop = ( ) => {
29+ if ( scrollContainerRef . current ) {
30+ scrollContainerRef . current . scrollTo ( {
31+ top : 0 ,
32+ behavior : 'smooth'
33+ } ) ;
34+ }
35+ } ;
36+
37+ // Monitor scroll position to show/hide back-to-top button
38+ useEffect ( ( ) => {
39+ const handleScroll = ( ) => {
40+ if ( scrollContainerRef . current ) {
41+ const scrollTop = scrollContainerRef . current . scrollTop ;
42+ setShowBackToTop ( scrollTop > 200 ) ; // Show after scrolling 200px
43+ }
44+ } ;
45+
46+ const scrollContainer = scrollContainerRef . current ;
47+ if ( scrollContainer ) {
48+ scrollContainer . addEventListener ( 'scroll' , handleScroll ) ;
49+ return ( ) => scrollContainer . removeEventListener ( 'scroll' , handleScroll ) ;
50+ }
51+
52+ return undefined ; // Explicit return for when scrollContainer is null
53+ } , [ ] ) ;
54+
55+ return (
56+ < div
57+ data-scroll-container = "true"
58+ style = { {
59+ height : '500px' ,
60+ overflow : 'hidden' ,
61+ padding : '20px'
62+ } } >
63+ < div style = { { position : 'relative' } } >
64+ < NxNavPillMenu
65+ items = { items }
66+ onItemClick = { handleItemClick }
67+ scrollBehavior = "smooth"
68+ scrollOffset = { 0 }
69+ />
70+
71+ { /* Scrollable content container */ }
72+ < div
73+ ref = { scrollContainerRef }
74+ data-nav-scroll-area = "true"
75+ style = { {
76+ height : 'calc(480px - 41px)' , // Subtract nav height and container padding
77+ overflowY : 'auto' ,
78+ marginTop : '0px' // No gap between nav and content
79+ } } >
80+ < section id = "overview-section" style = { { padding : '20px' , marginBottom : '20px' , backgroundColor : 'white' , borderRadius : '8px' , border : '1px solid #e0e0e0' } } >
81+ < h3 > Overview Section</ h3 >
82+ < p > This is the overview section. Clicking the "Overview" pill will scroll to this section within this container.</ p >
83+ < p > The navigation bar stays at the top of this container, not the entire page.</ p >
84+ </ section >
85+
86+ < section id = "features-section" style = { { padding : '20px' , marginBottom : '20px' , backgroundColor : 'white' , borderRadius : '8px' , border : '1px solid #e0e0e0' } } >
87+ < h3 > Features Section</ h3 >
88+ < p > This is the features section. Notice how the navigation scrolls within this bounded area.</ p >
89+ < p > This prevents conflicts with the main page scroll.</ p >
90+ </ section >
91+
92+ < section id = "getting-started-section" style = { { padding : '20px' , marginBottom : '20px' , backgroundColor : 'white' , borderRadius : '8px' , border : '1px solid #e0e0e0' } } >
93+ < h3 > Getting Started Section</ h3 >
94+ < p > This is the getting started section. Click the pill to scroll here.</ p >
95+ < p > The scroll behavior is contained within this example area.</ p >
96+ </ section >
97+
98+ < section id = "api-section" style = { { padding : '20px' , marginBottom : '20px' , backgroundColor : 'white' , borderRadius : '8px' , border : '1px solid #e0e0e0' } } >
99+ < h3 > API Reference Section</ h3 >
100+ < p > This is the API reference section with detailed documentation.</ p >
101+ < p > Each section has enough content to demonstrate the scroll behavior.</ p >
102+ </ section >
103+
104+ < section id = "examples-section" style = { { padding : '20px' , marginBottom : '20px' , backgroundColor : 'white' , borderRadius : '8px' , border : '1px solid #e0e0e0' } } >
105+ < h3 > Examples Section</ h3 >
106+ < p > This is the examples section showing various use cases.</ p >
107+ < p > The contained scroll area ensures the sticky navigation works properly.</ p >
108+ </ section >
109+ </ div >
110+
111+ { /* Back to top button - demo pattern for nav pill menu */ }
112+ { showBackToTop && (
113+ < button
114+ onClick = { scrollToTop }
115+ style = { {
116+ position : 'absolute' ,
117+ bottom : '20px' ,
118+ right : '20px' ,
119+ width : '48px' ,
120+ height : '48px' ,
121+ borderRadius : '24px' ,
122+ backgroundColor : 'white' ,
123+ border : 'none' ,
124+ boxShadow : '0 2px 8px rgba(0, 0, 0, 0.1)' , // Lighter shadow
125+ cursor : 'pointer' ,
126+ display : 'flex' ,
127+ alignItems : 'center' ,
128+ justifyContent : 'center' ,
129+ fontSize : '16px' ,
130+ color : 'var(--nx-swatch-indigo-40)' ,
131+ transition : 'all 0.2s ease-in-out' ,
132+ zIndex : 1000
133+ } }
134+ onMouseEnter = { ( e ) => {
135+ e . currentTarget . style . transform = 'translateY(-2px)' ;
136+ e . currentTarget . style . boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)' ;
137+ } }
138+ onMouseLeave = { ( e ) => {
139+ e . currentTarget . style . transform = 'translateY(0)' ;
140+ e . currentTarget . style . boxShadow = '0 2px 8px rgba(0, 0, 0, 0.1)' ;
141+ } }
142+ aria-label = "Back to top"
143+ >
144+ < NxFontAwesomeIcon icon = { faArrowUp } />
145+ </ button >
146+ ) }
147+ </ div >
148+ </ div >
149+ ) ;
150+ }
0 commit comments