@@ -1918,6 +1918,11 @@ function RemoteFunctions(config = {}) {
19181918 function ImageRibbonGallery ( element ) {
19191919 this . element = element ;
19201920 this . remove = this . remove . bind ( this ) ;
1921+ this . currentPage = 1 ;
1922+ this . totalPages = 1 ;
1923+ this . allImages = [ ] ;
1924+ this . imagesPerPage = 10 ;
1925+ this . scrollPosition = 0 ;
19211926 this . create ( ) ;
19221927 }
19231928
@@ -2014,6 +2019,25 @@ function RemoteFunctions(config = {}) {
20142019 font-size: 14px !important;
20152020 }
20162021
2022+ .phoenix-ribbon-nav {
2023+ font-size: 18px !important;
2024+ font-weight: 600 !important;
2025+ user-select: none !important;
2026+ transition: all 0.2s ease !important;
2027+ z-index: 10 !important;
2028+ }
2029+
2030+ .phoenix-ribbon-nav:hover {
2031+ background: rgba(21,25,36,0.85) !important;
2032+ border-color: rgba(255,255,255,0.25) !important;
2033+ transform: translateY(-50%) scale(1.05) !important;
2034+ box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
2035+ }
2036+
2037+ .phoenix-ribbon-nav:active {
2038+ transform: translateY(-50%) scale(0.95) !important;
2039+ }
2040+
20172041 .phoenix-ribbon-nav.left {
20182042 left: 18px !important;
20192043 }
@@ -2184,10 +2208,13 @@ function RemoteFunctions(config = {}) {
21842208 ` ;
21852209 } ,
21862210
2187- _fetchImages : function ( searchQuery = 'sunshine' ) {
2211+ _fetchImages : function ( searchQuery = 'sunshine' , page = 1 , append = false ) {
21882212 this . _currentSearchQuery = searchQuery ;
2189- const apiUrl = `https://images.phcode.dev/api/images/search?q=${ encodeURIComponent ( searchQuery ) } &per_page=10` ;
2190- this . _showLoading ( ) ;
2213+ const apiUrl = `https://images.phcode.dev/api/images/search?q=${ encodeURIComponent ( searchQuery ) } &per_page=10&page=${ page } ` ;
2214+
2215+ if ( ! append ) {
2216+ this . _showLoading ( ) ;
2217+ }
21912218
21922219 fetch ( apiUrl )
21932220 . then ( response => {
@@ -2198,17 +2225,103 @@ function RemoteFunctions(config = {}) {
21982225 } )
21992226 . then ( data => {
22002227 if ( data . results && data . results . length > 0 ) {
2201- this . _renderImages ( data . results ) ;
2202- } else {
2228+ if ( append ) {
2229+ this . allImages = this . allImages . concat ( data . results ) ;
2230+ this . _renderImages ( data . results , true ) ; // true means need to append new images at the end
2231+ } else {
2232+ this . allImages = data . results ;
2233+ this . _renderImages ( this . allImages , false ) ; // false means its a new search
2234+ }
2235+ this . totalPages = data . total_pages || 1 ;
2236+ this . currentPage = page ;
2237+ this . _updateNavButtons ( ) ;
2238+ } else if ( ! append ) {
22032239 this . _showError ( 'No images found' ) ;
22042240 }
2241+
2242+ if ( append ) {
2243+ this . _isLoadingMore = false ;
2244+ this . _hideLoadingMore ( ) ;
2245+ }
22052246 } )
22062247 . catch ( error => {
22072248 console . error ( 'Failed to fetch images:' , error ) ;
2208- this . _showError ( 'Failed to load images' ) ;
2249+ if ( ! append ) {
2250+ this . _showError ( 'Failed to load images' ) ;
2251+ } else {
2252+ this . _isLoadingMore = false ;
2253+ this . _hideLoadingMore ( ) ;
2254+ }
22092255 } ) ;
22102256 } ,
22112257
2258+ _handleNavLeft : function ( ) {
2259+ const container = this . _shadow . querySelector ( '.phoenix-ribbon-strip' ) ;
2260+ if ( ! container ) { return ; }
2261+
2262+ const containerWidth = container . clientWidth ;
2263+ const scrollAmount = containerWidth ;
2264+
2265+ this . scrollPosition = Math . max ( 0 , this . scrollPosition - scrollAmount ) ;
2266+ container . scrollTo ( { left : this . scrollPosition , behavior : 'smooth' } ) ;
2267+ this . _updateNavButtons ( ) ;
2268+ } ,
2269+
2270+ _handleNavRight : function ( ) {
2271+ const container = this . _shadow . querySelector ( '.phoenix-ribbon-strip' ) ;
2272+ if ( ! container ) { return ; }
2273+
2274+ const containerWidth = container . clientWidth ;
2275+ const totalWidth = container . scrollWidth ;
2276+ const scrollAmount = containerWidth ;
2277+
2278+ // if we're near the end, we need to load more images
2279+ const nearEnd = ( this . scrollPosition + containerWidth + scrollAmount ) >= totalWidth - 100 ;
2280+ if ( nearEnd && this . currentPage < this . totalPages && ! this . _isLoadingMore ) {
2281+ this . _isLoadingMore = true ;
2282+ this . _showLoadingMore ( ) ;
2283+ this . _fetchImages ( this . _currentSearchQuery , this . currentPage + 1 , true ) ;
2284+ }
2285+
2286+ this . scrollPosition = Math . min ( totalWidth - containerWidth , this . scrollPosition + scrollAmount ) ;
2287+ container . scrollTo ( { left : this . scrollPosition , behavior : 'smooth' } ) ;
2288+ this . _updateNavButtons ( ) ;
2289+ } ,
2290+
2291+ _updateNavButtons : function ( ) {
2292+ // this function is responsible to update the nav buttons
2293+ // because when we're at the very left, then we style the nav-left button differently (reduce opacity)
2294+ // and when we're at the very right and no more pages available, we reduce opacity for nav-right
2295+ const navLeft = this . _shadow . querySelector ( '.phoenix-ribbon-nav.left' ) ;
2296+ const navRight = this . _shadow . querySelector ( '.phoenix-ribbon-nav.right' ) ;
2297+ const container = this . _shadow . querySelector ( '.phoenix-ribbon-strip' ) ;
2298+
2299+ if ( ! navLeft || ! navRight || ! container ) { return ; }
2300+
2301+ // show/hide left button
2302+ if ( this . scrollPosition <= 0 ) {
2303+ navLeft . style . opacity = '0.3' ;
2304+ navLeft . style . pointerEvents = 'none' ;
2305+ } else {
2306+ navLeft . style . opacity = '1' ;
2307+ navLeft . style . pointerEvents = 'auto' ;
2308+ }
2309+
2310+ // show/hide right button
2311+ const containerWidth = container . clientWidth ;
2312+ const totalWidth = container . scrollWidth ;
2313+ const atEnd = ( this . scrollPosition + containerWidth ) >= totalWidth - 10 ;
2314+ const hasMorePages = this . currentPage < this . totalPages ;
2315+
2316+ if ( atEnd && ! hasMorePages ) {
2317+ navRight . style . opacity = '0.3' ;
2318+ navRight . style . pointerEvents = 'none' ;
2319+ } else {
2320+ navRight . style . opacity = '1' ;
2321+ navRight . style . pointerEvents = 'auto' ;
2322+ }
2323+ } ,
2324+
22122325 _showLoading : function ( ) {
22132326 const rowElement = this . _shadow . querySelector ( '.phoenix-ribbon-row' ) ;
22142327 if ( ! rowElement ) { return ; }
@@ -2217,16 +2330,53 @@ function RemoteFunctions(config = {}) {
22172330 rowElement . className = 'phoenix-ribbon-row phoenix-ribbon-loading' ;
22182331 } ,
22192332
2333+ _showLoadingMore : function ( ) {
2334+ const rowElement = this . _shadow . querySelector ( '.phoenix-ribbon-row' ) ;
2335+ if ( ! rowElement ) { return ; }
2336+
2337+ // when loading more images we need to show the message at the end of the image ribbon
2338+ const loadingIndicator = window . document . createElement ( 'div' ) ;
2339+ loadingIndicator . className = 'phoenix-loading-more' ;
2340+ loadingIndicator . style . cssText = `
2341+ display: flex !important;
2342+ align-items: center !important;
2343+ justify-content: center !important;
2344+ min-width: 120px !important;
2345+ height: 116px !important;
2346+ margin-left: 2px !important;
2347+ background: rgba(255,255,255,0.03) !important;
2348+ border-radius: 8px !important;
2349+ color: #e8eaf0 !important;
2350+ font-size: 12px !important;
2351+ border: 1px dashed rgba(255,255,255,0.1) !important;
2352+ ` ;
2353+ loadingIndicator . textContent = 'Loading...' ;
2354+ rowElement . appendChild ( loadingIndicator ) ;
2355+ } ,
2356+
2357+ _hideLoadingMore : function ( ) {
2358+ const loadingIndicator = this . _shadow . querySelector ( '.phoenix-loading-more' ) ;
2359+ if ( loadingIndicator ) {
2360+ loadingIndicator . remove ( ) ;
2361+ }
2362+ } ,
2363+
22202364 _attachEventHandlers : function ( ) {
22212365 const searchInput = this . _shadow . querySelector ( '.phoenix-ribbon-search input' ) ;
22222366 const searchButton = this . _shadow . querySelector ( '.phoenix-ribbon-search-btn' ) ;
22232367 const closeButton = this . _shadow . querySelector ( '.phoenix-ribbon-close' ) ;
2368+ const navLeft = this . _shadow . querySelector ( '.phoenix-ribbon-nav.left' ) ;
2369+ const navRight = this . _shadow . querySelector ( '.phoenix-ribbon-nav.right' ) ;
22242370
22252371 if ( searchInput && searchButton ) {
22262372 const performSearch = ( e ) => {
22272373 e . stopPropagation ( ) ;
22282374 const query = searchInput . value . trim ( ) ;
22292375 if ( query ) {
2376+ // reset pagination when searching
2377+ this . currentPage = 1 ;
2378+ this . allImages = [ ] ;
2379+ this . scrollPosition = 0 ;
22302380 this . _fetchImages ( query ) ;
22312381 }
22322382 } ;
@@ -2250,6 +2400,20 @@ function RemoteFunctions(config = {}) {
22502400 } ) ;
22512401 }
22522402
2403+ if ( navLeft ) {
2404+ navLeft . addEventListener ( 'click' , ( e ) => {
2405+ e . stopPropagation ( ) ;
2406+ this . _handleNavLeft ( ) ;
2407+ } ) ;
2408+ }
2409+
2410+ if ( navRight ) {
2411+ navRight . addEventListener ( 'click' , ( e ) => {
2412+ e . stopPropagation ( ) ;
2413+ this . _handleNavRight ( ) ;
2414+ } ) ;
2415+ }
2416+
22532417 // Prevent clicks anywhere inside the ribbon from bubbling up
22542418 const ribbonContainer = this . _shadow . querySelector ( '.phoenix-image-ribbon' ) ;
22552419 if ( ribbonContainer ) {
@@ -2259,13 +2423,26 @@ function RemoteFunctions(config = {}) {
22592423 }
22602424 } ,
22612425
2262- _renderImages : function ( images ) {
2426+ // append true means load more images (user clicked on nav-right)
2427+ // append false means its a new query
2428+ _renderImages : function ( images , append = false ) {
22632429 const rowElement = this . _shadow . querySelector ( '.phoenix-ribbon-row' ) ;
22642430 if ( ! rowElement ) { return ; }
22652431
2266- // remove the loading state
2267- rowElement . innerHTML = '' ;
2268- rowElement . className = 'phoenix-ribbon-row' ;
2432+ const container = this . _shadow . querySelector ( '.phoenix-ribbon-strip' ) ;
2433+ const savedScrollPosition = container ? container . scrollLeft : 0 ;
2434+
2435+ // if not appending we clear the phoenix ribbon
2436+ if ( ! append ) {
2437+ rowElement . innerHTML = '' ;
2438+ rowElement . className = 'phoenix-ribbon-row' ;
2439+ } else {
2440+ // when appending we add the new images at the end
2441+ const loadingIndicator = this . _shadow . querySelector ( '.phoenix-loading-more' ) ;
2442+ if ( loadingIndicator ) {
2443+ loadingIndicator . remove ( ) ;
2444+ }
2445+ }
22692446
22702447 // Create thumbnails from API data
22712448 images . forEach ( image => {
@@ -2334,6 +2511,12 @@ function RemoteFunctions(config = {}) {
23342511 thumbDiv . appendChild ( useImageBtn ) ;
23352512 rowElement . appendChild ( thumbDiv ) ;
23362513 } ) ;
2514+
2515+ if ( append && container && savedScrollPosition > 0 ) {
2516+ setTimeout ( ( ) => {
2517+ container . scrollLeft = savedScrollPosition ;
2518+ } , 0 ) ;
2519+ }
23372520 } ,
23382521
23392522 _showError : function ( message ) {
@@ -2390,6 +2573,7 @@ function RemoteFunctions(config = {}) {
23902573 window . document . body . appendChild ( this . body ) ;
23912574 this . _attachEventHandlers ( ) ;
23922575 this . _fetchImages ( ) ;
2576+ setTimeout ( ( ) => this . _updateNavButtons ( ) , 0 ) ;
23932577 } ,
23942578
23952579 remove : function ( ) {
0 commit comments