feat: menu New page (Empty/Database/Kanban) + slash commands / dans l'éditeur
- Topbar bouton New ▾ avec dropdown: Empty page, Database, Kanban board - page_editor: commandes slash / (/h1, /h2, /list, /todo, /code, /table, /quote...) - Fonctions JS: createEmptyPage, createDatabase, createBoard - State showNewPageMenu dans Alpine.js
This commit is contained in:
+22
-1
@@ -226,7 +226,14 @@
|
||||
<button class="topbar-btn">🔍</button>
|
||||
<button class="topbar-btn share-btn">🔗 Share ▾</button>
|
||||
<button class="topbar-btn">⭐</button>
|
||||
<button class="topbar-btn">⋯</button>
|
||||
<button class="topbar-btn" @click="showNewPageMenu = !showNewPageMenu" style="position:relative;">
|
||||
New ▾
|
||||
<div class="dropdown-panel" :class="{ visible: showNewPageMenu }" style="top:100%; right:0; left:auto; min-width:200px;" @click.stop>
|
||||
<div class="dropdown-option" @click="createEmptyPage()"><span class="option-icon">📄</span> Empty page</div>
|
||||
<div class="dropdown-option" @click="createDatabase()"><span class="option-icon">📊</span> Database</div>
|
||||
<div class="dropdown-option" @click="createBoard()"><span class="option-icon">📋</span> Kanban board</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -338,6 +345,7 @@
|
||||
currentView: 'home',
|
||||
contextMenu: { visible: false, x: 0, y: 0, pageId: null, pageName: '' },
|
||||
contextActions: {},
|
||||
showNewPageMenu: false,
|
||||
|
||||
navigateTo(url) {
|
||||
window.location.href = url;
|
||||
@@ -393,6 +401,19 @@
|
||||
.catch(err => { alert('Failed to create sub-page: ' + err.message); });
|
||||
},
|
||||
|
||||
createEmptyPage() {
|
||||
this.showNewPageMenu = false;
|
||||
this.newPage('Private');
|
||||
},
|
||||
createDatabase() {
|
||||
this.showNewPageMenu = false;
|
||||
alert('Database creation: use a Gitea repo as a database. Navigate to a project board from the dashboard.');
|
||||
},
|
||||
createBoard() {
|
||||
this.showNewPageMenu = false;
|
||||
window.location.href = '/board/bruno/flowdeck';
|
||||
},
|
||||
|
||||
toggleTheme() {
|
||||
const html = document.documentElement;
|
||||
const current = html.getAttribute('data-theme');
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
<div style="padding:4px 12px; background:var(--bg-tertiary); font-size:12px; color:var(--text-dim);">Markdown</div>
|
||||
<textarea x-ref="editor" x-model="content"
|
||||
@input="autoSave()"
|
||||
@keydown="handleSlash($event)"
|
||||
style="flex:1; width:100%; min-height:400px; background:var(--bg-secondary);
|
||||
border:none; padding:12px; color:var(--text-primary); font-family:var(--font-mono);
|
||||
font-size:13px; line-height:1.6; resize:none; outline:none; tab-size:2;"
|
||||
placeholder="Start writing in Markdown..."></textarea>
|
||||
placeholder="Type '/' for commands, or start writing in Markdown..."></textarea>
|
||||
</div>
|
||||
<!-- Preview -->
|
||||
<div style="flex:1; border-left:1px solid var(--border); display:flex; flex-direction:column;">
|
||||
@@ -145,7 +146,43 @@
|
||||
},
|
||||
saveTitle(newTitle) {
|
||||
fetch(`/board/api/pages/{{ page.id }}?title=${encodeURIComponent(newTitle)}`, { method: 'PUT' });
|
||||
}
|
||||
},
|
||||
handleSlash(e) {
|
||||
if (e.key === '/' && !e.ctrlKey && !e.metaKey) {
|
||||
const el = this.$refs.editor;
|
||||
const cursor = el.selectionStart;
|
||||
const lineStart = this.content.lastIndexOf('\n', cursor - 1) + 1;
|
||||
const charsBefore = this.content.substring(lineStart, cursor);
|
||||
// Only trigger at start of line or after whitespace
|
||||
if (charsBefore.trim() === '') {
|
||||
e.preventDefault();
|
||||
this.content = this.content.substring(0, cursor) + '/';
|
||||
// Simple menu: insert template after short delay
|
||||
const cmd = prompt('Slash command:\n /h1 - Heading 1\n /h2 - Heading 2\n /h3 - Heading 3\n /bold - Bold\n /list - Bullet list\n /num - Numbered list\n /code - Code block\n /quote - Quote\n /todo - To-do list\n /table - Table\n /divider - Divider\n /link - Link\n /img - Image\n /date - Date');
|
||||
if (!cmd) { this.content = this.content.substring(0, cursor); return; }
|
||||
const templates = {
|
||||
h1: '# ',
|
||||
h2: '## ',
|
||||
h3: '### ',
|
||||
bold: '**bold text**',
|
||||
list: '- ',
|
||||
num: '1. ',
|
||||
code: '```\ncode\n```',
|
||||
quote: '> ',
|
||||
todo: '- [ ] ',
|
||||
table: '| Col 1 | Col 2 |\n|-------|-------|\n| | |',
|
||||
divider: '---\n',
|
||||
link: '[text](url)',
|
||||
img: '',
|
||||
date: new Date().toISOString().slice(0, 10),
|
||||
};
|
||||
const insert = templates[cmd.replace('/','')] || '';
|
||||
const before = this.content.substring(0, cursor);
|
||||
this.content = before + insert + this.content.substring(cursor + 1);
|
||||
setTimeout(() => { el.focus(); el.selectionStart = el.selectionEnd = before.length + insert.length; }, 50);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user