fix(editor+sidebar): bouton ⋯ retiré, Form via modale app, doublon du dernier fichier du tree
FlowDeck CI / test (push) Canceled after 0s
FlowDeck CI / docker (push) Canceled after 0s
FlowDeck CI / lint (push) Failing after 1m37s

- "Get started with" : suppression du bouton "⋯" et de son menu mort
  (Table/Board/List/Timeline/Calendar/Gallery/Import — aucun handler).
- Pilule Form : createForm() plantait sur page vide ("Cannot read
  properties of undefined (reading 'push')") car this.inlineDBs n'était
  jamais initialisé dans editorState() (seulement via loadInlineDBs si un
  bloc inline_dbs existait déjà). Corrigé : inlineDBs:[] initialisé.
  Le nom du form est demandé via une modale Alpine personnalisée
  (flowdeck-modal*) au lieu du prompt() natif.
- Sidebar tree après drag&drop : refreshSidebarTree() préservait le lien
  "Manage Workspaces" via ul.querySelector('li:last-child') — non scopé,
  il attrapait le dernier fichier d'une sous-liste de dossier imbriquée
  (premier li:last-child du document) et le ré-insérait → doublon du
  dernier fichier. Corrigé : ':scope > li:last-child' (enfant direct).
This commit is contained in:
2026-10-10 20:49:45 -04:00
parent 090174df0a
commit 12beab01c9
3 changed files with 41 additions and 42 deletions
+24 -35
View File
@@ -743,7 +743,7 @@
</button>
<button
class="gs-pill"
@click="createForm()"
@click="openFormModal()"
>
<span class="gs-icon">{{ fd_icon("copy",14) }}</span> Form
</button>
@@ -753,40 +753,6 @@
>
<span class="gs-icon">{{ fd_icon("file",14) }}</span> Templates
</button>
<div style="position: relative">
<button class="gs-pill" @click="gsMoreOpen = !gsMoreOpen">
<span>⋯</span>
</button>
<div
class="gs-more-dropdown"
x-show="gsMoreOpen"
@click.outside="gsMoreOpen = false"
x-transition
>
<div class="gs-more-item" @click="gsMoreOpen=false">
{{ fd_icon("bar-chart",14) }} Table
</div>
<div class="gs-more-item" @click="gsMoreOpen=false">
{{ fd_icon("copy",14) }} Board
</div>
<div class="gs-more-item" @click="gsMoreOpen=false">
{{ fd_icon('list',14) }} List
</div>
<div class="gs-more-item" @click="gsMoreOpen=false">
{{ fd_icon("calendar",14) }} Timeline
</div>
<div class="gs-more-item" @click="gsMoreOpen=false">
{{ fd_icon("calendar",14) }} Calendar
</div>
<div class="gs-more-item" @click="gsMoreOpen=false">
{{ fd_icon("image",14) }} Gallery
</div>
<div class="gs-more-sep"></div>
<div class="gs-more-item" @click="gsMoreOpen=false">
{{ fd_icon("download",14) }} Import
</div>
</div>
</div>
</div>
<div class="editor-statusbar">
@@ -1010,4 +976,27 @@
</div>
</div>
<!-- ═══════════ Get Started Form name modal ═══════════ -->
<div class="flowdeck-modal-overlay"
x-show="formOpen"
style="position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:2100;display:flex;align-items:center;justify-content:center;padding:20px;"
@click.self="formOpen = false"
x-transition.opacity>
<div class="flowdeck-modal" @keydown.escape="formOpen = false">
<div class="flowdeck-modal-header">
<h3>{{ fd_icon("copy",16) }} Create form</h3>
<button class="flowdeck-modal-close" @click="formOpen = false">&times;</button>
</div>
<div class="flowdeck-modal-body">
<p>Enter a name for the new form.</p>
<input class="flowdeck-modal-input" type="text" x-model="formName" x-ref="formNameInput"
placeholder="Form name" @keydown.enter="confirmCreateForm()">
</div>
<div class="flowdeck-modal-footer">
<button class="flowdeck-modal-btn cancel" @click="formOpen = false">Cancel</button>
<button class="flowdeck-modal-btn confirm" @click="confirmCreateForm()">Create</button>
</div>
</div>
</div>
</div>
+4 -2
View File
@@ -2613,8 +2613,10 @@
.then(html => {
const ul = document.getElementById('sidebar-workspace-items');
if (!ul) return;
// Preserve the "Manage Workspaces" link (last <li>)
const manageLink = ul.querySelector('li:last-child');
// Preserve the "Manage Workspaces" link (direct last <li> child only —
// plain 'li:last-child' grabs the last file of a nested folder sublist
// when the tree has folders, splicing it back in → duplicated entry)
const manageLink = ul.querySelector(':scope > li:last-child');
ul.innerHTML = html;
if (manageLink) ul.appendChild(manageLink);
// Le HTML injecté porte des directives Alpine (@contextmenu,
+12 -4
View File
@@ -1480,6 +1480,7 @@ const PD=(()=>{try{const el=document.getElementById('page-data');return el?JSON.
_commentSel:{range:null,bid:null,start:0,end:0},
_notifiedMentions:{},
undoStack:[],redoStack:[],_histCap:100,_histLock:false,
inlineDBs: [], formOpen: false, formName: '',
// ── Cover & icon (v5.10+) ──
coverUrl:'',
coverOpen:false,
@@ -2727,14 +2728,21 @@ applyAIBlocks(text){
}catch(e){}
return this.blocks.length;
},
async createForm(){
const name=prompt('Form name:','New Form');
if(!name||!name.trim())return;
openFormModal(){
this.formName='New Form';
this.formOpen=true;
this.$nextTick(()=>{ const el=this.$refs.formNameInput; if(el) el.focus(); });
},
closeFormModal(){ this.formOpen=false; },
async confirmCreateForm(){
const name=this.formName.trim();
if(!name){ this.showToast('Form name required','error'); return; }
this.formOpen=false;
const csrf = getCsrf();;
try{
const r=await fetch('/db/inline/api',{
method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf},
body:JSON.stringify({name:name.trim(),parent_page_id:this.pid})
body:JSON.stringify({name:name,parent_page_id:this.pid})
});
const d=await r.json();
if(d.id){