1- import { Fragment , h } from 'preact' ;
1+ import { createContext , Fragment , h } from 'preact' ;
22import { useContext , useEffect , useId , useLayoutEffect , useMemo , useRef , useState } from 'preact/hooks' ;
33import { memo } from 'preact/compat' ;
44import cn from 'classnames' ;
@@ -10,7 +10,7 @@ import { usePlatformName } from '../../settings.provider.js';
1010import { useDropzoneSafeArea } from '../../dropzone.js' ;
1111import { TileRow } from './TileRow.js' ;
1212import { FavoritesContext } from './FavoritesProvider.js' ;
13- import { CustomizerContext } from '../../customizer/CustomizerProvider.js' ;
13+ import { CustomizerContext , CustomizerThemesContext } from '../../customizer/CustomizerProvider.js' ;
1414import { useComputed } from '@preact/signals' ;
1515
1616/**
@@ -25,6 +25,7 @@ export const ROW_CAPACITY = 6;
2525 */
2626const ITEM_HEIGHT = 96 ;
2727const ROW_GAP = 8 ;
28+ export const FavoritesThemeContext = createContext ( /** @type {"light"|"dark" } */ ( 'light' ) ) ;
2829
2930/**
3031 * Favorites Grid.
@@ -49,45 +50,50 @@ export function Favorites({ gridRef, favorites, expansion, toggle, openContextMe
4950 const rowHeight = ITEM_HEIGHT + ROW_GAP ;
5051 const canToggleExpansion = favorites . length >= ROW_CAPACITY ;
5152 const { data } = useContext ( CustomizerContext ) ;
53+ const { main } = useContext ( CustomizerThemesContext ) ;
5254 const kind = useComputed ( ( ) => data . value . background . kind ) ;
5355
5456 return (
55- < div
56- class = { cn ( styles . root , ! canToggleExpansion && styles . noExpansionBtn ) }
57- data-testid = "FavoritesConfigured"
58- data-background-kind = { kind }
59- >
60- < VirtualizedGridRows
61- WIDGET_ID = { WIDGET_ID }
62- favorites = { favorites }
63- rowHeight = { rowHeight }
64- add = { add }
65- expansion = { expansion }
66- openFavorite = { openFavorite }
67- openContextMenu = { openContextMenu }
68- />
69- { canToggleExpansion && (
70- < div
71- className = { cn ( {
72- [ styles . showhide ] : true ,
73- [ styles . showhideVisible ] : canToggleExpansion ,
74- } ) }
75- >
76- < ShowHideButton
77- buttonAttrs = { {
78- 'aria-expanded' : expansion === 'expanded' ,
79- 'aria-pressed' : expansion === 'expanded' ,
80- 'aria-controls' : WIDGET_ID ,
81- id : TOGGLE_ID ,
82- } }
83- text = {
84- expansion === 'expanded' ? t ( 'favorites_show_less' ) : t ( 'favorites_show_more' , { count : String ( hiddenCount ) } )
85- }
86- onClick = { toggle }
87- />
88- </ div >
89- ) }
90- </ div >
57+ < FavoritesThemeContext . Provider value = { main . value } >
58+ < div
59+ class = { cn ( styles . root , ! canToggleExpansion && styles . noExpansionBtn ) }
60+ data-testid = "FavoritesConfigured"
61+ data-background-kind = { kind }
62+ >
63+ < VirtualizedGridRows
64+ WIDGET_ID = { WIDGET_ID }
65+ favorites = { favorites }
66+ rowHeight = { rowHeight }
67+ add = { add }
68+ expansion = { expansion }
69+ openFavorite = { openFavorite }
70+ openContextMenu = { openContextMenu }
71+ />
72+ { canToggleExpansion && (
73+ < div
74+ className = { cn ( {
75+ [ styles . showhide ] : true ,
76+ [ styles . showhideVisible ] : canToggleExpansion ,
77+ } ) }
78+ >
79+ < ShowHideButton
80+ buttonAttrs = { {
81+ 'aria-expanded' : expansion === 'expanded' ,
82+ 'aria-pressed' : expansion === 'expanded' ,
83+ 'aria-controls' : WIDGET_ID ,
84+ id : TOGGLE_ID ,
85+ } }
86+ text = {
87+ expansion === 'expanded'
88+ ? t ( 'favorites_show_less' )
89+ : t ( 'favorites_show_more' , { count : String ( hiddenCount ) } )
90+ }
91+ onClick = { toggle }
92+ />
93+ </ div >
94+ ) }
95+ </ div >
96+ </ FavoritesThemeContext . Provider >
9197 ) ;
9298}
9399
@@ -145,7 +151,7 @@ function VirtualizedGridRows({ WIDGET_ID, rowHeight, favorites, expansion, openF
145151 onContextMenu = { getContextMenuHandler ( openContextMenu ) }
146152 onClick = { getOnClickHandler ( openFavorite , platformName ) }
147153 >
148- { rows . length === 0 && < TileRow key = { 'empty-rows' } items = { [ ] } topOffset = { 0 } add = { add } /> }
154+ { rows . length === 0 && < TileRow key = { 'empty-rows' } items = { [ ] } topOffset = { 0 } add = { add } visibility = { 'visible' } /> }
149155 { rows . length > 0 && < Inner rows = { rows } safeAreaRef = { safeAreaRef } rowHeight = { rowHeight } add = { add } /> }
150156 </ div >
151157 ) ;
@@ -166,6 +172,7 @@ function VirtualizedGridRows({ WIDGET_ID, rowHeight, favorites, expansion, openF
166172function Inner ( { rows, safeAreaRef, rowHeight, add } ) {
167173 const { onConfigChanged, state } = useContext ( FavoritesContext ) ;
168174 const [ expansion , setExpansion ] = useState ( state . config ?. expansion || 'collapsed' ) ;
175+ const documentVisibility = useDocumentVisibility ( ) ;
169176
170177 // force the children to be rendered after the main thread is cleared
171178 useEffect ( ( ) => {
@@ -242,12 +249,17 @@ function Inner({ rows, safeAreaRef, rowHeight, add }) {
242249 { signal : controller . signal } ,
243250 ) ;
244251
245- // when the content-tube grows, re-calc the layout
246- const resizer = new ResizeObserver ( ( ) => {
247- requestAnimationFrame ( ( ) => {
248- updateGlobals ( mainScroller . scrollTop ) ;
249- setVisibleRowsForOffset ( mainScroller . scrollTop ) ;
250- } ) ;
252+ let lastHeight ;
253+ const resizer = new ResizeObserver ( ( entries ) => {
254+ const first = entries [ 0 ] ;
255+ if ( ! first ) return ;
256+ if ( first . contentRect . height !== lastHeight ) {
257+ lastHeight = first . contentRect . height ;
258+ requestAnimationFrame ( ( ) => {
259+ updateGlobals ( mainScroller . scrollTop ) ;
260+ setVisibleRowsForOffset ( mainScroller . scrollTop ) ;
261+ } ) ;
262+ }
251263 } ) ;
252264 resizer . observe ( contentTube ) ;
253265
@@ -285,12 +297,34 @@ function Inner({ rows, safeAreaRef, rowHeight, add }) {
285297 { subsetOfRowsToRender . map ( ( items , rowIndex ) => {
286298 const topOffset = expansion === 'expanded' ? ( start + rowIndex ) * rowHeight : 0 ;
287299 const keyed = `-${ start + rowIndex } -` ;
288- return < TileRow key = { keyed } dropped = { dropped } items = { items } topOffset = { topOffset } add = { add } /> ;
300+ return (
301+ < TileRow key = { keyed } dropped = { dropped } items = { items } topOffset = { topOffset } add = { add } visibility = { documentVisibility } />
302+ ) ;
289303 } ) }
290304 </ Fragment >
291305 ) ;
292306}
293307
308+ function useDocumentVisibility ( ) {
309+ /** @type {Document['visibilityState'] } */
310+ const initial = document . visibilityState ;
311+ const [ documentVisibility , setDocumentVisibility ] = useState ( /** @type {Document['visibilityState'] } */ ( initial ) ) ;
312+
313+ useEffect ( ( ) => {
314+ const handleVisibilityChange = ( ) => {
315+ setDocumentVisibility ( document . visibilityState ) ;
316+ } ;
317+
318+ document . addEventListener ( 'visibilitychange' , handleVisibilityChange ) ;
319+
320+ return ( ) => {
321+ document . removeEventListener ( 'visibilitychange' , handleVisibilityChange ) ;
322+ } ;
323+ } , [ ] ) ;
324+
325+ return documentVisibility ;
326+ }
327+
294328/**
295329 * Handle right-clicks
296330 *
0 commit comments