ajout de la fonction séparateur à l'éditeur

This commit is contained in:
Esenjin 2025-02-15 14:55:27 +01:00
parent 072e0a7fb1
commit cef00e62e1
2 changed files with 115 additions and 94 deletions

View File

@ -16,7 +16,6 @@
color: var(--text-primary); color: var(--text-primary);
min-height: 300px; min-height: 300px;
font-family: inherit; font-family: inherit;
overflow-x: hidden;
} }
/* Contenu de l'éditeur */ /* Contenu de l'éditeur */
@ -219,6 +218,36 @@
min-width: 200px; min-width: 200px;
} }
/* Style des icônes dans la barre d'outils */
.ql-snow .ql-toolbar button svg {
width: 18px;
height: 18px;
}
/* Style du séparateur dans l'éditeur */
.chapter-divider {
border: none;
border-top: 2px solid var(--accent-primary);
margin: 2em 0;
opacity: 0.5;
transition: opacity var(--transition-fast);
}
.chapter-divider:hover {
opacity: 0.8;
}
/* États au survol des boutons */
.ql-snow .ql-toolbar button:hover .ql-stroke,
.ql-snow .ql-toolbar button.ql-active .ql-stroke {
stroke: var(--accent-primary);
}
.ql-snow .ql-toolbar button:hover .ql-fill,
.ql-snow .ql-toolbar button.ql-active .ql-fill {
fill: var(--accent-primary);
}
/* Responsive */ /* Responsive */
@media (max-width: 768px) { @media (max-width: 768px) {
.ql-snow.ql-toolbar { .ql-snow.ql-toolbar {
@ -249,4 +278,10 @@
width: 100%; width: 100%;
margin: 4px 0; margin: 4px 0;
} }
.tooltip:before {
width: 200px;
white-space: normal;
text-align: center;
}
} }

View File

@ -2,24 +2,61 @@ document.addEventListener('DOMContentLoaded', function() {
let currentChapterId = null; let currentChapterId = null;
const storyId = document.querySelector('input[name="id"]')?.value; const storyId = document.querySelector('input[name="id"]')?.value;
// Initialisation de l'éditeur Quill avec toutes les options // Création de l'icône SVG pour le séparateur
const icons = {
divider: `
<svg viewBox="0 0 18 18">
<line class="ql-stroke" x1="3" x2="15" y1="9" y2="9"></line>
</svg>
`
};
// Ajout de l'icône à Quill
const Block = Quill.import('blots/block');
const icons_list = Quill.import('ui/icons');
icons_list['divider'] = icons.divider;
// Définition du format pour le séparateur
class DividerBlot extends Block {
static create() {
const node = super.create();
node.className = 'chapter-divider';
return node;
}
}
DividerBlot.blotName = 'divider';
DividerBlot.tagName = 'hr';
Quill.register(DividerBlot);
// Configuration et initialisation de Quill
const quill = new Quill('#editor', { const quill = new Quill('#editor', {
theme: 'snow', theme: 'snow',
modules: { modules: {
toolbar: [ toolbar: {
[{ 'header': [1, 2, 3, false] }], container: [
['bold', 'italic', 'underline', 'strike'], [{ 'header': [1, 2, 3, false] }],
[{ 'color': [] }, { 'background': [] }], ['bold', 'italic', 'underline', 'strike'],
[{ 'font': [] }], [{ 'color': [] }, { 'background': [] }],
[{ 'align': [] }], [{ 'font': [] }],
['blockquote', 'code-block'], [{ 'align': [] }],
[{ 'list': 'ordered'}, { 'list': 'bullet' }], ['blockquote', 'code-block'],
[{ 'script': 'sub'}, { 'script': 'super' }], [{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'indent': '-1'}, { 'indent': '+1' }], [{ 'script': 'sub'}, { 'script': 'super' }],
[{ 'direction': 'rtl' }], [{ 'indent': '-1'}, { 'indent': '+1' }],
['link', 'image', 'video'], [{ 'direction': 'rtl' }],
['clean'] ['link', 'image', 'video'],
], ['divider'],
['clean']
],
handlers: {
'divider': function() {
const range = quill.getSelection(true);
quill.insertText(range.index, '\n', Quill.sources.USER);
quill.insertEmbed(range.index + 1, 'divider', true, Quill.sources.USER);
quill.setSelection(range.index + 2, Quill.sources.SILENT);
}
}
},
keyboard: { keyboard: {
bindings: { bindings: {
tab: false, tab: false,
@ -27,20 +64,7 @@ document.addEventListener('DOMContentLoaded', function() {
} }
} }
}, },
placeholder: 'Commencez à écrire votre chapitre ici...', placeholder: 'Commencez à écrire votre chapitre ici...'
formats: [
'header',
'bold', 'italic', 'underline', 'strike',
'color', 'background',
'font',
'align',
'blockquote', 'code-block',
'list', 'bullet',
'script',
'indent',
'direction',
'link', 'image', 'video'
]
}); });
// Gestion des chapitres // Gestion des chapitres
@ -55,7 +79,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (chaptersList) { if (chaptersList) {
new Sortable(chaptersList, { new Sortable(chaptersList, {
animation: 150, animation: 150,
handle: '.chapter-number', // Utiliser le numéro comme poignée de drag handle: '.chapter-number',
ghostClass: 'sortable-ghost', ghostClass: 'sortable-ghost',
onEnd: async function(evt) { onEnd: async function(evt) {
const chapters = Array.from(chaptersList.children).map((item, index) => ({ const chapters = Array.from(chaptersList.children).map((item, index) => ({
@ -79,7 +103,6 @@ document.addEventListener('DOMContentLoaded', function() {
throw new Error('Erreur lors de la réorganisation'); throw new Error('Erreur lors de la réorganisation');
} }
// Mise à jour des numéros de chapitres
chapters.forEach((chapter, index) => { chapters.forEach((chapter, index) => {
const element = document.querySelector(`[data-id="${chapter.id}"] .chapter-number`); const element = document.querySelector(`[data-id="${chapter.id}"] .chapter-number`);
if (element) { if (element) {
@ -116,7 +139,7 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
} }
// Gestion des clics sur la liste des chapitres // Gestion des clics sur la liste des chapitres
if (chaptersList) { if (chaptersList) {
chaptersList.addEventListener('click', async (e) => { chaptersList.addEventListener('click', async (e) => {
const target = e.target; const target = e.target;
@ -139,10 +162,8 @@ document.addEventListener('DOMContentLoaded', function() {
// Gestion du contenu // Gestion du contenu
if (chapter.html) { if (chapter.html) {
// Si on a du HTML, on le charge directement
quill.root.innerHTML = chapter.html; quill.root.innerHTML = chapter.html;
} else if (chapter.content) { } else if (chapter.content) {
// Si on a du contenu au format Delta
try { try {
const content = typeof chapter.content === 'string' const content = typeof chapter.content === 'string'
? JSON.parse(chapter.content) ? JSON.parse(chapter.content)
@ -153,50 +174,18 @@ document.addEventListener('DOMContentLoaded', function() {
quill.setText(chapter.content || ''); quill.setText(chapter.content || '');
} }
} else { } else {
// Contenu vide par défaut
quill.setContents([]); quill.setContents([]);
} }
modal.style.display = 'block'; modal.style.display = 'block';
} catch (error) { } catch (error) {
console.error('Erreur détaillée:', error); console.error('Erreur détaillée:', error);
showNotification('Erreur lors du chargement du chapitre. Veuillez réessayer.', 'error'); showNotification('Erreur lors du chargement du chapitre', 'error');
} }
} }
}); });
} }
// Système de notification
function showNotification(message, type = 'success') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
// Animation d'entrée
setTimeout(() => {
notification.style.opacity = '1';
notification.style.transform = 'translateY(0)';
}, 10);
// Auto-suppression après 3 secondes
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateY(-100%)';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// Vérification des changements non sauvegardés
function hasUnsavedChanges() {
if (!currentChapterId) {
return chapterTitleInput.value !== '' || quill.getLength() > 1;
}
// Pour les chapitres existants, il faudrait comparer avec le contenu original
return true; // Par défaut, on suppose qu'il y a des changements
}
// Sauvegarde d'un chapitre // Sauvegarde d'un chapitre
if (saveChapterBtn) { if (saveChapterBtn) {
saveChapterBtn.addEventListener('click', async () => { saveChapterBtn.addEventListener('click', async () => {
@ -233,32 +222,29 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
} }
// Ajouter le style des notifications function hasUnsavedChanges() {
const style = document.createElement('style'); if (!currentChapterId) {
style.textContent = ` return chapterTitleInput.value !== '' || quill.getLength() > 1;
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 1rem 2rem;
border-radius: var(--radius-sm);
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
opacity: 0;
transform: translateY(-100%);
transition: opacity 0.3s ease, transform 0.3s ease;
z-index: 1000;
} }
return true;
}
.notification.success { function showNotification(message, type = 'success') {
background-color: var(--success-color); const notification = document.createElement('div');
color: white; notification.className = `notification ${type}`;
} notification.textContent = message;
.notification.error { document.body.appendChild(notification);
background-color: var(--error-color);
color: white; setTimeout(() => {
} notification.style.opacity = '1';
`; notification.style.transform = 'translateY(0)';
document.head.appendChild(style); }, 10);
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateY(-100%)';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
}); });