A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

Restore the floating block toolbar

The block-editor migration shipped a fixed BlockToolbar docked in the
editor header because the floating toolbar appeared not to render. That
diagnosis was wrong on two counts:

- BlockTools was missing __unstableContentRef, so the toolbar popover
had no content element to anchor to and scroll-follow — it could not
position against the selected block.
- It was only ever observed on an unmodified-empty or actively-typing
block, both of which Gutenberg's useShowBlockTools suppresses by
design: an empty default paragraph shows the side inserter instead,
and while typing the block interface is hidden until the next pointer
move. Synthetic test input left the editor in that typing state, so
the toolbar never showed.

Set hasFixedToolbar: false and pass the canvas element to BlockTools via
__unstableContentRef, restoring the original SkyPress behaviour where the
toolbar floats just above the selected block and follows it on scroll.
The header keeps the document-level tools (inserter, undo/redo, the
block-settings cog).

Verified in-browser on /write: selecting a non-empty block floats the
toolbar (Paragraph / Bold / Italic / Link / More / Options) anchored to
it, themed against the paper surface. npm test (592), check, and build
all pass.

+19 -15
+14 -12
src/components/SkyEditor.tsx
··· 11 11 BlockEditorProvider, 12 12 BlockList, 13 13 BlockTools, 14 - BlockToolbar, 15 14 BlockInspector, 16 15 Inserter, 17 16 WritingFlow, ··· 117 116 const [ status, setStatus ] = useState< string >( '' ); 118 117 const [ showInspector, setShowInspector ] = useState( false ); 119 118 const inspectorToggleRef = useRef< HTMLButtonElement >( null ); 119 + // The block canvas, handed to `BlockTools` so the floating block toolbar 120 + // anchors to — and scroll-follows — the selected block. 121 + const contentRef = useRef< HTMLDivElement >( null ); 120 122 // Set by undo/redo so the value-sync effect below forwards the restored tree. 121 123 const pendingForward = useRef( false ); 122 124 ··· 175 177 // Restrict the inserter/transforms to the curated set. The registry is 176 178 // already pruned to it (registerSkyPressBlocks); this scopes the UI too. 177 179 allowedBlockTypes: [ ...ALLOWED_BLOCKS ], 178 - // Render the selected block's tools in a fixed `<BlockToolbar>` we place in 179 - // the header (vs. a popover that floats by the block). The framework 180 - // recommends this for a custom-composed editor — the floating toolbar needs 181 - // iframe/content-ref plumbing that a bespoke inline canvas doesn't provide. 182 - hasFixedToolbar: true, 180 + // Float the selected block's toolbar by the block (vs. docking it in a 181 + // fixed bar). `BlockTools` renders the floating toolbar into the in-editor 182 + // `Popover.Slot`, anchored via `__unstableContentRef` below. It shows on a 183 + // block with content — an unmodified empty paragraph gets the side inserter 184 + // instead (Gutenberg's `useShowBlockTools`). 185 + hasFixedToolbar: false, 183 186 } ), 184 187 [ mediaUpload ] 185 188 ); ··· 215 218 <div className="skypress-editor" onKeyDownCapture={ onKeyDownCapture }> 216 219 <div className="skypress-editor__toolbar"> 217 220 <Inserter rootClientId={ undefined } /> 218 - { /* The selected block's formatting/transform tools (bold, 219 - italic, link, the @-mention format, …). Empty when no 220 - block is selected. */ } 221 - <BlockToolbar hideDragHandle /> 222 221 <div className="skypress-editor__toolbar-spacer" /> 223 222 <Button 224 223 icon={ undoIcon } ··· 252 251 ) } 253 252 </div> 254 253 255 - <div className="skypress-editor__body editor-styles-wrapper"> 256 - <BlockTools> 254 + <div 255 + className="skypress-editor__body editor-styles-wrapper" 256 + ref={ contentRef } 257 + > 258 + <BlockTools __unstableContentRef={ contentRef }> 257 259 <BlockEditorKeyboardShortcuts.Register /> 258 260 <WritingFlow> 259 261 <ObserveTyping>
+1 -1
src/styles/editor-chrome.css
··· 256 256 padding: 1rem var(--studio-gutter) 2rem; 257 257 } 258 258 259 - /* The fixed `<BlockToolbar>` sits in our header, and toolbar popovers (link 259 + /* The floating block toolbar (shown on selection) and toolbar popovers (link 260 260 editing, the "more" format menu) portal into the `Popover.Slot` inside 261 261 `.skypress-editor`. Both re-skin from the tokens here — without this the 262 262 toolbar reads as a light slab in dark mode. */
+4 -2
src/types/wordpress.d.ts
··· 41 41 children?: ReactNode; 42 42 } >; 43 43 export const BlockList: ComponentType< Record< string, unknown > >; 44 - export const BlockTools: ComponentType< { children?: ReactNode } >; 45 - export const BlockToolbar: ComponentType< { hideDragHandle?: boolean } >; 44 + export const BlockTools: ComponentType< { 45 + __unstableContentRef?: { current: HTMLElement | null }; 46 + children?: ReactNode; 47 + } >; 46 48 export const BlockInspector: ComponentType< Record< string, unknown > >; 47 49 export const Inserter: ComponentType< { rootClientId?: string } >; 48 50 export const WritingFlow: ComponentType< { children?: ReactNode } >;