fix(editor): placeholder titre "New Page" + aides de bloc au focus seul
- Titre : data-placeholder="New Page" + bascule .empty a l'input (mecanisme des
blocs). Le placeholder s'affiche en gris pale quand le titre est vide, y compris
apres effacement. save() stocke un titre vide (plus de "New page" force), la
sidebar garde son fallback, duplicatePage retombe sur "New Page".
- Blocs : les placeholders ("Press 'space' for AI...", "Heading 1", "List"...)
passent sous :focus-within -> ne s'affichent que quand le bloc est focalise.
Idem pour "Type code..." du bloc code.
v5.1.6
This commit is contained in:
+24
-1
@@ -1,6 +1,29 @@
|
|||||||
# Changelog — FlowDeck
|
# Changelog — FlowDeck
|
||||||
|
|
||||||
## v5.1.5 (2026-09-07) — Placeholder des blocs : disparaît dès la frappe
|
## v5.1.6 (2026-09-07) — Placeholder titre "New Page" + placeholder blocs au focus seul
|
||||||
|
|
||||||
|
> Conforme au comportement Notion : le titre affiche « New Page » quand il est
|
||||||
|
> vide ; les aides de blocs (« Press 'space' for AI… », « Heading 1 »…) ne
|
||||||
|
> s'affichent que lorsque le bloc a le curseur.
|
||||||
|
|
||||||
|
### Titre « New Page » (document vide)
|
||||||
|
- Le placeholder du titre utilisait `:empty` avec le texte figé « New page » et
|
||||||
|
la couleur très sombre `--text-dim`. `:empty` casse dès qu'un `<br>` est
|
||||||
|
injecté dans le contenteditable.
|
||||||
|
- Ajout de `data-placeholder="New Page"` et d'une bascule de classe `.empty`
|
||||||
|
(même mécanisme que les blocs) : le placeholder s'affiche en gris pâle
|
||||||
|
(`--text-secondary`, opacité .75) dès que le titre est vide, y compris après
|
||||||
|
effacement.
|
||||||
|
- `save()` stocke désormais un titre vide (au lieu de forcer « New page ») : le
|
||||||
|
placeholder persiste après autosave ; la sidebar garde son fallback d'affichage.
|
||||||
|
- `duplicatePage()` retombe sur « New Page » si le titre est vide.
|
||||||
|
|
||||||
|
### Aides de blocs affichées seulement au focus
|
||||||
|
- Les placeholders des blocs (« Press 'space' for AI or '/' for commands »,
|
||||||
|
« Heading 1 », « List »…) s'affichaient en permanence quand le bloc était vide.
|
||||||
|
- Les règles passent sous `:focus-within` : l'aide n'apparaît que lorsque le bloc
|
||||||
|
est focalisé (curseur dans le bloc) et disparaît dès qu'on le quitte.
|
||||||
|
- Même comportement pour l'aide « Type code... » du bloc code.
|
||||||
|
|
||||||
> Les textes d'aide « Press 'space' for AI or '/' for commands », « Heading 1 »,
|
> Les textes d'aide « Press 'space' for AI or '/' for commands », « Heading 1 »,
|
||||||
> « List », etc. restaient affichés pendant l'édition.
|
> « List », etc. restaient affichés pendant l'édition.
|
||||||
|
|||||||
+1
-1
@@ -60,7 +60,7 @@ async def lifespan(_app: FastAPI):
|
|||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="FlowDeck",
|
title="FlowDeck",
|
||||||
version="5.1.5",
|
version="5.1.6",
|
||||||
docs_url="/docs" if settings.log_level == "DEBUG" else None,
|
docs_url="/docs" if settings.log_level == "DEBUG" else None,
|
||||||
redoc_url=None,
|
redoc_url=None,
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
|
|||||||
@@ -396,6 +396,7 @@
|
|||||||
class="page-title-input"
|
class="page-title-input"
|
||||||
contenteditable="true"
|
contenteditable="true"
|
||||||
id="_titleEl"
|
id="_titleEl"
|
||||||
|
data-placeholder="New Page"
|
||||||
@input="dirty=true;save()"
|
@input="dirty=true;save()"
|
||||||
@keydown.enter.prevent="focusBlock()"
|
@keydown.enter.prevent="focusBlock()"
|
||||||
@paste.prevent="pastePlain($event)"
|
@paste.prevent="pastePlain($event)"
|
||||||
|
|||||||
@@ -700,6 +700,8 @@
|
|||||||
window.E=this;
|
window.E=this;
|
||||||
this.insertTable=function(){this.sync();var nb={id:genId(),type:'table',content:'',rows:_tblNew(3,3)};this.blocks.push(nb);this.dirty=true;this.autoSave();this.render();setTimeout(function(){_tblFocus(nb.id,0,0);},60);};
|
this.insertTable=function(){this.sync();var nb={id:genId(),type:'table',content:'',rows:_tblNew(3,3)};this.blocks.push(nb);this.dirty=true;this.autoSave();this.render();setTimeout(function(){_tblFocus(nb.id,0,0);},60);};
|
||||||
SM.init();_tblInitDelegate();this.render();
|
SM.init();_tblInitDelegate();this.render();
|
||||||
|
const tEl=document.getElementById('_titleEl');
|
||||||
|
if(tEl){const syncT=()=>{tEl.classList.toggle('empty',!(tEl.textContent||'').trim());};syncT();tEl.addEventListener('input',syncT);tEl.addEventListener('blur',syncT);}
|
||||||
setTimeout(()=>{this.focusBlock();const ct=document.getElementById('_blocksCt');if(!ct)return;
|
setTimeout(()=>{this.focusBlock();const ct=document.getElementById('_blocksCt');if(!ct)return;
|
||||||
ct.addEventListener('keydown',e=>this.onKd(e));
|
ct.addEventListener('keydown',e=>this.onKd(e));
|
||||||
ct.addEventListener('paste',e=>{e.preventDefault();document.execCommand('insertText',false,(e.clipboardData||window.clipboardData).getData('text/plain'));this.dirty=true;this.autoSave();});
|
ct.addEventListener('paste',e=>{e.preventDefault();document.execCommand('insertText',false,(e.clipboardData||window.clipboardData).getData('text/plain'));this.dirty=true;this.autoSave();});
|
||||||
@@ -977,7 +979,7 @@ applyAIBlocks(text){
|
|||||||
doDownload();
|
doDownload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
duplicatePage(){this.moreOpen=false;const csrf=document.cookie.match(/csrf_token=([^;]+)/);fetch(`/board/api/pages?title=${encodeURIComponent(this.pageTitle+' copy')}§ion=Private&project=${encodeURIComponent('{{ workspace_key }}')}`,{method:'POST',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}).then(r=>r.json()).then(d=>{window.location.href=`/pages/${d.id}`;}).catch(()=>{window.showToast('Duplicate failed','error');});},
|
duplicatePage(){this.moreOpen=false;const csrf=document.cookie.match(/csrf_token=([^;]+)/);const t=(this.pageTitle||'').trim()||'New Page';fetch(`/board/api/pages?title=${encodeURIComponent(t+' copy')}§ion=Private&project=${encodeURIComponent('{{ workspace_key }}')}`,{method:'POST',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}).then(r=>r.json()).then(d=>{window.location.href=`/pages/${d.id}`;}).catch(()=>{window.showToast('Duplicate failed','error');});},
|
||||||
movePage(){
|
movePage(){
|
||||||
this.moreOpen=false;
|
this.moreOpen=false;
|
||||||
this.moveOpen = true;
|
this.moveOpen = true;
|
||||||
@@ -1276,7 +1278,7 @@ applyAIBlocks(text){
|
|||||||
if(this.fileData)return;
|
if(this.fileData)return;
|
||||||
if(this.saving){ if(cb) cb(); return; }
|
if(this.saving){ if(cb) cb(); return; }
|
||||||
this.sync();
|
this.sync();
|
||||||
const t=document.getElementById('_titleEl');if(t)this.pageTitle=t.textContent?.trim()||'New page';
|
const t=document.getElementById('_titleEl');if(t)this.pageTitle=t.textContent?.trim()||'';
|
||||||
this.saving=true;
|
this.saving=true;
|
||||||
const csrf=document.cookie.match(/csrf_token=([^;]+)/);
|
const csrf=document.cookie.match(/csrf_token=([^;]+)/);
|
||||||
fetch(`/board/api/pages/${this.pid}/blocks`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''},body:JSON.stringify({title:this.pageTitle,blocks:this.blocks.map(b=>{const c={id:b.id,type:b.type,content:b.content};['checked','expanded','language','icon','src','alt','style','embed_type','collection_id','dbs','file_name','file_size','file_mime','children','rows','align','has_header','colsW','meeting'].forEach(k=>{if(b[k]!=null)c[k]=b[k];});return c;})})})
|
fetch(`/board/api/pages/${this.pid}/blocks`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''},body:JSON.stringify({title:this.pageTitle,blocks:this.blocks.map(b=>{const c={id:b.id,type:b.type,content:b.content};['checked','expanded','language','icon','src','alt','style','embed_type','collection_id','dbs','file_name','file_size','file_mime','children','rows','align','has_header','colsW','meeting'].forEach(k=>{if(b[k]!=null)c[k]=b[k];});return c;})})})
|
||||||
|
|||||||
+7
-6
@@ -2205,9 +2205,11 @@ button.breadcrumb-current { max-width: 260px; }
|
|||||||
border: none;
|
border: none;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
.page-title-input.empty::before,
|
||||||
.page-title-input:empty::before {
|
.page-title-input:empty::before {
|
||||||
content: 'New page';
|
content: attr(data-placeholder);
|
||||||
color: var(--text-dim);
|
color: var(--text-secondary);
|
||||||
|
opacity: .75;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2334,8 +2336,7 @@ button.breadcrumb-current { max-width: 260px; }
|
|||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
.block-content [data-bid].empty::before,
|
.block-content:focus-within [data-bid].empty::before {
|
||||||
.block-content.empty::before {
|
|
||||||
content: attr(data-placeholder);
|
content: attr(data-placeholder);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
opacity: .75;
|
opacity: .75;
|
||||||
@@ -2496,9 +2497,9 @@ button.breadcrumb-current { max-width: 260px; }
|
|||||||
display: block;
|
display: block;
|
||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
}
|
}
|
||||||
.block-code code:empty::before {
|
.block-code:focus-within code:empty::before {
|
||||||
content: 'Type code...';
|
content: 'Type code...';
|
||||||
color: var(--text-dim);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Divider */
|
/* Divider */
|
||||||
|
|||||||
Reference in New Issue
Block a user