@ -2,6 +2,20 @@
import { useState , useEffect } from "react" ;
import { useState , useEffect } from "react" ;
const LS_PREFIX = "bb-block-preview:" ;
function readLocalPreview ( path : string , fallback : boolean ) : boolean {
if ( typeof window === "undefined" ) return fallback ;
const v = localStorage . getItem ( LS_PREFIX + path ) ;
if ( v === null ) return fallback ;
return v === "true" ;
}
function writeLocalPreview ( path : string , value : boolean ) {
localStorage . setItem ( LS_PREFIX + path , String ( value ) ) ;
window . dispatchEvent ( new Event ( "bb-preview-change" ) ) ;
}
interface BlockMeta {
interface BlockMeta {
path : string ;
path : string ;
name : string ;
name : string ;
@ -22,9 +36,14 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: Block
const [ saving , setSaving ] = useState ( false ) ;
const [ saving , setSaving ] = useState ( false ) ;
const [ togglingPreview , setTogglingPreview ] = useState ( false ) ;
const [ togglingPreview , setTogglingPreview ] = useState ( false ) ;
const [ apiDown , setApiDown ] = useState ( false ) ;
const [ apiDown , setApiDown ] = useState ( false ) ;
const [ localPreview , setLocalPreview ] = useState ( defaultIsInPreview ) ;
const apiUrl = process . env . NEXT_PUBLIC_API_URL ;
const apiUrl = process . env . NEXT_PUBLIC_API_URL ;
useEffect ( ( ) = > {
setLocalPreview ( readLocalPreview ( path , defaultIsInPreview ) ) ;
} , [ path , defaultIsInPreview ] ) ;
useEffect ( ( ) = > {
useEffect ( ( ) = > {
if ( ! apiUrl ) { setApiDown ( true ) ; return ; }
if ( ! apiUrl ) { setApiDown ( true ) ; return ; }
fetch ( ` ${ apiUrl } /blocks/by-path?path= ${ encodeURIComponent ( path ) } ` )
fetch ( ` ${ apiUrl } /blocks/by-path?path= ${ encodeURIComponent ( path ) } ` )
@ -58,6 +77,13 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: Block
}
}
async function togglePreview() {
async function togglePreview() {
if ( apiDown ) {
// Fallback: toggle via localStorage
const newVal = ! localPreview ;
setLocalPreview ( newVal ) ;
writeLocalPreview ( path , newVal ) ;
return ;
}
if ( ! meta ) return ;
if ( ! meta ) return ;
setTogglingPreview ( true ) ;
setTogglingPreview ( true ) ;
try {
try {
@ -69,7 +95,7 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: Block
}
}
const version = meta ? . version ? ? defaultVersion ;
const version = meta ? . version ? ? defaultVersion ;
const isInPreview = meta ? . isInPreview ? ? defaultIsInPreview ;
const isInPreview = apiDown ? localPreview : ( meta ? . isInPreview ? ? localPreview ) ;
return (
return (
< div
< div
@ -124,44 +150,31 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: Block
< span style = { { color : "var(--bb-border)" } } > · < / span >
< span style = { { color : "var(--bb-border)" } } > · < / span >
{ /* Preview toggle */ }
{ /* Preview toggle */ }
{ apiDown ? (
< button
onClick = { togglePreview }
disabled = { togglingPreview || ( ! apiDown && ! meta ) }
title = { isInPreview ? "Убрать из превью страницы" : "Включить в превью страницы" }
className = "flex items-center gap-1.5 px-2.5 py-1 rounded font-medium transition-colors cursor-pointer"
style = { isInPreview ? {
background : "#dcfce7" ,
color : "#16a34a" ,
border : "1px solid #86efac" ,
} : {
background : "var(--bb-sidebar-bg)" ,
color : "var(--bb-text-muted)" ,
border : "1px solid var(--bb-border)" ,
} }
>
< span
< span
className = "flex items-center gap-1"
className = "inline-block w-1.5 h-1.5 rounded-full"
style = { { color : isInPreview ? "#16a34a" : "var(--bb-text-muted)" } }
style = { { background : isInPreview ? "#22c55e" : "#d1d5db" } }
>
/ >
< span
{ togglingPreview
className = "inline-block w-1.5 h-1.5 rounded-full"
? '...'
style = { { background : isInPreview ? "#22c55e" : "#d1d5db" } }
: isInPreview
/ >
? "В превью"
{ isInPreview ? "В превью" : "Не в превью" }
: "Не в превью" }
< / span >
< / button >
) : (
< button
onClick = { togglePreview }
disabled = { togglingPreview || ! meta }
title = { isInPreview ? "Убрать из превью страницы" : "Включить в превью страницы" }
className = "flex items-center gap-1.5 px-2.5 py-1 rounded font-medium transition-colors"
style = { isInPreview ? {
background : "#dcfce7" ,
color : "#16a34a" ,
border : "1px solid #86efac" ,
} : {
background : "var(--bb-sidebar-bg)" ,
color : "var(--bb-text-muted)" ,
border : "1px solid var(--bb-border)" ,
} }
>
< span
className = "inline-block w-1.5 h-1.5 rounded-full"
style = { { background : isInPreview ? "#22c55e" : "#d1d5db" } }
/ >
{ togglingPreview
? '...'
: isInPreview
? "В превью"
: "Добавить в превью" }
< / button >
) }
{ apiDown && (
{ apiDown && (
< >
< >