/** * WordPress dependencies */ import { Navigator, useNavigator } from '@wordpress/components'; import { getBlockTypes, store as blocksStore } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; // @ts-expect-error: Not typed yet. import { BlockEditorProvider } from '@wordpress/block-editor'; import { useMemo, useEffect, Fragment } from '@wordpress/element'; import { usePrevious } from '@wordpress/compose'; import { generateGlobalStyles, mergeGlobalStyles, } from '@wordpress/global-styles-engine'; import type { GlobalStylesConfig } from '@wordpress/global-styles-engine'; /** * Internal dependencies */ import { GlobalStylesProvider } from './provider'; import ScreenRoot from './screen-root'; import ScreenBlockList from './screen-block-list'; import ScreenBlock from './screen-block'; import ScreenTypography from './screen-typography'; import ScreenTypographyElement from './screen-typography-element'; import ScreenColors from './screen-colors'; import ScreenColorPalette from './screen-color-palette'; import ScreenBackground from './screen-background'; import { ScreenShadows, ScreenShadowsEdit } from './screen-shadows'; import ScreenLayout from './screen-layout'; import ScreenStyleVariations from './screen-style-variations'; import ScreenCSS from './screen-css'; import ScreenRevisions from './screen-revisions'; import FontSizes from './font-sizes/font-sizes'; import FontSize from './font-sizes/font-size'; interface BlockStylesNavigationScreensProps { parentMenu: string; blockStyles: any[]; blockName: string; } function BlockStylesNavigationScreens( { parentMenu, blockStyles, blockName, }: BlockStylesNavigationScreensProps ) { return ( <> { blockStyles.map( ( style, index ) => ( ) ) } ); } interface ContextScreensProps { name?: string; parentMenu?: string; } interface GlobalStylesNavigationScreenProps { path: string; children: React.ReactNode; } function ContextScreens( { name, parentMenu = '' }: ContextScreensProps ) { const blockStyleVariations = useSelect( ( select ) => { if ( ! name ) { return []; } const { getBlockStyles } = select( blocksStore ); return getBlockStyles( name ); }, [ name ] ); if ( ! blockStyleVariations?.length ) { return null; } return ( ); } interface GlobalStylesUIProps { /** User global styles object (what gets edited) */ value: GlobalStylesConfig; /** Base global styles object (theme default) */ baseValue: GlobalStylesConfig; /** Callback when global styles change */ onChange: ( newValue: GlobalStylesConfig ) => void; /** Current navigation path (optional) */ path?: string; /** Callback when navigation path changes (optional) */ onPathChange?: ( path: string ) => void; /** Whether font library is enabled (optional) */ fontLibraryEnabled?: boolean; /** Server CSS styles for BlockEditorProvider (optional) */ serverCSS?: { isGlobalStyles?: boolean }[]; /** Server settings for BlockEditorProvider (optional) */ serverSettings?: { __unstableResolvedAssets: Record< string, unknown > }; } export function GlobalStylesUI( { value, baseValue, onChange, path, onPathChange, fontLibraryEnabled = false, serverCSS, serverSettings, }: GlobalStylesUIProps ) { const blocks = getBlockTypes(); // Compute merged value for CSS generation const mergedValue = useMemo( () => { return mergeGlobalStyles( baseValue, value ); }, [ baseValue, value ] ); const [ globalStylesCSS, globalSettings ] = generateGlobalStyles( mergedValue, [], { styleOptions: { variationStyles: true }, } ); const styles = useMemo( () => [ ...( serverCSS ?? [] ), ...( globalStylesCSS ?? [] ) ], [ serverCSS, globalStylesCSS ] ); const settings = useMemo( () => { return { ...serverSettings, __experimentalFeatures: globalSettings, styles, }; }, [ globalSettings, serverSettings, styles ] ); return ( { ( path || onPathChange ) && ( ) } { blocks.map( ( block ) => ( ) ) } ); } function GlobalStylesNavigationScreen( { path, children, }: GlobalStylesNavigationScreenProps ) { return ( { children } ); } /* * Component that handles path synchronization between external path prop and Navigator's internal path. */ function PathSynchronizer( { path, onPathChange, }: { path?: string; onPathChange?: ( path: string ) => void; } ) { const navigator = useNavigator(); const { path: childPath } = navigator.location; const previousParentPath = usePrevious( path ); const previousChildPath = usePrevious( childPath ); useEffect( () => { // Only sync when parent and child paths are out of sync if ( path && path !== childPath ) { // If parent path changed, update the Navigator if ( path !== previousParentPath ) { navigator.goTo( path ); } // If child path changed, notify parent via onPathChange else if ( childPath !== previousChildPath && onPathChange ) { onPathChange( childPath ?? '/' ); } } }, [ onPathChange, path, previousChildPath, previousParentPath, childPath, navigator, ] ); // This component only handles synchronization logic. It doesn't render anything. // We use it to run the effect inside the Navigator context. return null; }