many fixes in design and add shirt story type for creation book
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Quill Editor v2.0.3
|
||||
* Quill Editor v2.0.2
|
||||
* https://quilljs.com
|
||||
* Copyright (c) 2017-2024, Slab
|
||||
* Copyright (c) 2014, Jason Chen
|
||||
|
||||
@@ -42,4 +42,7 @@
|
||||
|
||||
.writer-editor-container.fullscreen {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.ql-editor p {
|
||||
text-indent: 2em;
|
||||
}
|
||||
+148
-64
@@ -1,73 +1,157 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const quill = window.quillEditorInstance;
|
||||
const textarea = window.quillTextarea;
|
||||
if (!quill || !textarea) return;
|
||||
// Поддержка WriterEditor (для глав и рассказов)
|
||||
const writerEditor = window.writerEditor;
|
||||
if (writerEditor) {
|
||||
const textarea = writerEditor.textarea;
|
||||
if (!textarea) return;
|
||||
|
||||
let lastSavedContent = textarea.value;
|
||||
let saveTimeout;
|
||||
// Для рассказов textarea может быть hidden input
|
||||
const contentField = textarea.tagName === 'TEXTAREA' ? textarea :
|
||||
(document.getElementById('story-content') ? document.getElementById('story-content') : textarea);
|
||||
|
||||
function showMessage(message, isError = false) {
|
||||
let msgEl = document.getElementById('autosave-message');
|
||||
if (!msgEl) {
|
||||
msgEl = document.createElement('div');
|
||||
msgEl.id = 'autosave-message';
|
||||
msgEl.style.cssText = `
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
right: 10px;
|
||||
padding: 8px 12px;
|
||||
background: ${isError ? '#dc3545' : '#28a745'};
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
z-index: 10000;
|
||||
font-size: 0.8rem;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
`;
|
||||
document.body.appendChild(msgEl);
|
||||
}
|
||||
msgEl.textContent = message;
|
||||
msgEl.style.background = isError ? '#dc3545' : '#28a745';
|
||||
msgEl.style.display = 'block';
|
||||
setTimeout(() => msgEl.style.display = 'none', 2000);
|
||||
}
|
||||
let lastSavedContent = contentField.value;
|
||||
let saveTimeout;
|
||||
|
||||
const autoSave = () => {
|
||||
const currentContent = textarea.value;
|
||||
if (currentContent === lastSavedContent) return;
|
||||
|
||||
const form = document.getElementById('chapter-form');
|
||||
const formData = new FormData(form);
|
||||
formData.append('autosave', 'true');
|
||||
|
||||
showMessage('Сохранение...');
|
||||
|
||||
fetch(window.location.href, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
lastSavedContent = currentContent;
|
||||
showMessage('Автосохранено: ' + new Date().toLocaleTimeString());
|
||||
} else {
|
||||
throw new Error(data.error || 'Ошибка сервера');
|
||||
function showMessage(message, isError = false) {
|
||||
let msgEl = document.getElementById('autosave-message');
|
||||
if (!msgEl) {
|
||||
msgEl = document.createElement('div');
|
||||
msgEl.id = 'autosave-message';
|
||||
msgEl.style.cssText = `
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
right: 10px;
|
||||
padding: 8px 12px;
|
||||
background: ${isError ? '#dc3545' : '#28a745'};
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
z-index: 10000;
|
||||
font-size: 0.8rem;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
`;
|
||||
document.body.appendChild(msgEl);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
showMessage('Ошибка автосохранения: ' + err.message, true);
|
||||
msgEl.textContent = message;
|
||||
msgEl.style.background = isError ? '#dc3545' : '#28a745';
|
||||
msgEl.style.display = 'block';
|
||||
setTimeout(() => msgEl.style.display = 'none', 2000);
|
||||
}
|
||||
|
||||
const autoSave = () => {
|
||||
const currentContent = contentField.value;
|
||||
if (currentContent === lastSavedContent) return;
|
||||
|
||||
const form = writerEditor.form;
|
||||
if (!form) return;
|
||||
|
||||
const formData = new FormData(form);
|
||||
formData.append('autosave', 'true');
|
||||
formData.append('content', currentContent);
|
||||
|
||||
showMessage('Сохранение...');
|
||||
|
||||
fetch(window.location.href, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
lastSavedContent = currentContent;
|
||||
showMessage('Автосохранено: ' + new Date().toLocaleTimeString());
|
||||
} else {
|
||||
throw new Error(data.error || 'Ошибка сервера');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
showMessage('Ошибка автосохранения: ' + err.message, true);
|
||||
});
|
||||
};
|
||||
|
||||
writerEditor.quill.on('text-change', () => {
|
||||
clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(autoSave, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
quill.on('text-change', () => {
|
||||
clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(autoSave, 2000);
|
||||
});
|
||||
// Периодическая автосохранение
|
||||
setInterval(autoSave, 30000);
|
||||
} else {
|
||||
// Старая поддержка через window.quillEditorInstance
|
||||
const quill = window.quillEditorInstance;
|
||||
const textarea = window.quillTextarea;
|
||||
if (!quill || !textarea) return;
|
||||
|
||||
// Периодическая автосохранение
|
||||
setInterval(autoSave, 30000);
|
||||
let lastSavedContent = textarea.value;
|
||||
let saveTimeout;
|
||||
|
||||
function showMessage(message, isError = false) {
|
||||
let msgEl = document.getElementById('autosave-message');
|
||||
if (!msgEl) {
|
||||
msgEl = document.createElement('div');
|
||||
msgEl.id = 'autosave-message';
|
||||
msgEl.style.cssText = `
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
right: 10px;
|
||||
padding: 8px 12px;
|
||||
background: ${isError ? '#dc3545' : '#28a745'};
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
z-index: 10000;
|
||||
font-size: 0.8rem;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
`;
|
||||
document.body.appendChild(msgEl);
|
||||
}
|
||||
msgEl.textContent = message;
|
||||
msgEl.style.background = isError ? '#dc3545' : '#28a745';
|
||||
msgEl.style.display = 'block';
|
||||
setTimeout(() => msgEl.style.display = 'none', 2000);
|
||||
}
|
||||
|
||||
const autoSave = () => {
|
||||
const currentContent = textarea.value;
|
||||
if (currentContent === lastSavedContent) return;
|
||||
|
||||
const form = document.getElementById('chapter-form');
|
||||
const formData = new FormData(form);
|
||||
formData.append('autosave', 'true');
|
||||
formData.append('content', currentContent);
|
||||
|
||||
showMessage('Сохранение...');
|
||||
|
||||
fetch(window.location.href, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
lastSavedContent = currentContent;
|
||||
showMessage('Автосохранено: ' + new Date().toLocaleTimeString());
|
||||
} else {
|
||||
throw new Error(data.error || 'Ошибка сервера');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
showMessage('Ошибка автосохранения: ' + err.message, true);
|
||||
});
|
||||
};
|
||||
|
||||
quill.on('text-change', () => {
|
||||
clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(autoSave, 2000);
|
||||
});
|
||||
|
||||
// Периодическая автосохранение
|
||||
setInterval(autoSave, 30000);
|
||||
}
|
||||
});
|
||||
|
||||
+109
-24
@@ -1,4 +1,34 @@
|
||||
// editor.js
|
||||
|
||||
// Глобальная функция переключения редактора рассказа
|
||||
function toggleStoryEditor() {
|
||||
const checkbox = document.getElementById('is_short_story');
|
||||
const storyEditorCard = document.getElementById('story-editor-card');
|
||||
const chaptersCard = document.getElementById('chapters-card');
|
||||
|
||||
if (!checkbox || !storyEditorCard) return;
|
||||
|
||||
const isChecked = checkbox.checked;
|
||||
|
||||
if (isChecked) {
|
||||
storyEditorCard.style.display = 'block';
|
||||
chaptersCard.style.display = 'none';
|
||||
if (!window.writerEditor) {
|
||||
const textarea = document.getElementById('content');
|
||||
if (textarea) {
|
||||
window.writerEditor = new WriterEditor('#book-form', 'story-editor', 'content', true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
storyEditorCard.style.display = 'none';
|
||||
chaptersCard.style.display = 'block';
|
||||
if (window.writerEditor) {
|
||||
window.writerEditor.quill.destroy();
|
||||
window.writerEditor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DialogueFormatter {
|
||||
constructor(quill) {
|
||||
this.quill = quill;
|
||||
@@ -79,10 +109,11 @@ class DialogueFormatter {
|
||||
}
|
||||
|
||||
class WriterEditor {
|
||||
constructor(formSelector = '#chapter-form', editorContainerId = 'quill-editor', textareaId = 'content') {
|
||||
constructor(formSelector = '#chapter-form', editorContainerId = 'quill-editor', textareaId = 'content', isShortStory = false) {
|
||||
this.form = document.querySelector(formSelector);
|
||||
this.editorContainer = document.getElementById(editorContainerId);
|
||||
this.textarea = document.getElementById(textareaId);
|
||||
this.isShortStory = isShortStory;
|
||||
this.isFullscreen = false;
|
||||
this.originalStyles = {};
|
||||
this.init();
|
||||
@@ -91,26 +122,37 @@ class WriterEditor {
|
||||
init() {
|
||||
if (!this.editorContainer || !this.textarea || !this.form) return;
|
||||
|
||||
// Упрощённый тулбар для рассказов
|
||||
const toolbarOptions = this.isShortStory
|
||||
? [
|
||||
[{ 'header': [1, 2, 3, false] }],
|
||||
['bold', 'italic', 'underline'],
|
||||
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
|
||||
['link', 'blockquote', 'code-block'],
|
||||
['clean']
|
||||
]
|
||||
: [
|
||||
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
|
||||
['bold','italic','underline','strike'],
|
||||
[{ 'align': [] }],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
['blockquote','code-block'],
|
||||
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
|
||||
[{ 'script': 'sub'}, { 'script': 'super' }],
|
||||
[{ 'indent': '-1'}, { 'indent': '+1' }],
|
||||
['dialogue', 'undodialogue'],
|
||||
[{ 'size': ['small', false, 'large', 'huge'] }],
|
||||
[{ 'font': [] }],
|
||||
['link','image','video'],
|
||||
['clean'],
|
||||
['fullscreen']
|
||||
];
|
||||
|
||||
this.quill = new Quill(this.editorContainer, {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: {
|
||||
container: [
|
||||
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
|
||||
['bold','italic','underline','strike'],
|
||||
[{ 'align': [] }],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
['blockquote','code-block'],
|
||||
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
|
||||
[{ 'script': 'sub'}, { 'script': 'super' }],
|
||||
[{ 'indent': '-1'}, { 'indent': '+1' }],
|
||||
['dialogue', 'undodialogue'],
|
||||
[{ 'size': ['small', false, 'large', 'huge'] }],
|
||||
[{ 'font': [] }],
|
||||
['link','image','video'],
|
||||
['clean'],
|
||||
['fullscreen'] // Добавляем кнопку полноэкранного режима
|
||||
],
|
||||
container: toolbarOptions,
|
||||
handlers: {
|
||||
'dialogue': () => {
|
||||
if (this.dialogueFormatter) {
|
||||
@@ -142,17 +184,18 @@ class WriterEditor {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
placeholder: 'Введите текст главы...'
|
||||
}, // Упрощённый тулбар для рассказов
|
||||
placeholder: this.isShortStory ? 'Напишите здесь ваш рассказ...' : 'Введите текст главы...'
|
||||
});
|
||||
|
||||
this.addCustomButtonsToToolbar();
|
||||
this.dialogueFormatter = new DialogueFormatter(this.quill);
|
||||
this.dialogueFormatter = this.isShortStory ? null : new DialogueFormatter(this.quill);
|
||||
|
||||
const rawContent = this.editorContainer.dataset.content || '';
|
||||
if (rawContent.trim()) this.quill.root.innerHTML = rawContent.trim();
|
||||
|
||||
setTimeout(() => this.formatExistingDialogues(), 100);
|
||||
if (!this.isShortStory) {
|
||||
setTimeout(() => this.formatExistingDialogues(), 100);
|
||||
}
|
||||
|
||||
const sync = () => {
|
||||
let html = this.quill.root.innerHTML;
|
||||
@@ -161,7 +204,10 @@ class WriterEditor {
|
||||
};
|
||||
|
||||
this.quill.on('text-change', sync);
|
||||
this.form.addEventListener('submit', sync);
|
||||
const submitHandler = function(e) {
|
||||
sync();
|
||||
};
|
||||
this.form.addEventListener('submit', submitHandler);
|
||||
|
||||
// Обработчик изменения ориентации для мобильных устройств
|
||||
window.addEventListener('orientationchange', () => {
|
||||
@@ -349,8 +395,47 @@ class WriterEditor {
|
||||
totalOffset += line.length + 1;
|
||||
});
|
||||
}
|
||||
|
||||
addCustomButtonsToToolbar() {
|
||||
if (this.isShortStory) return;
|
||||
|
||||
const toolbar = this.quill.container.previousSibling;
|
||||
if (!toolbar) return;
|
||||
|
||||
const dialogueBtn = toolbar.querySelector('.ql-dialogue');
|
||||
const undoDialogueBtn = toolbar.querySelector('.ql-undodialogue');
|
||||
const fullscreenBtn = toolbar.querySelector('.ql-fullscreen');
|
||||
|
||||
if (dialogueBtn) {
|
||||
dialogueBtn.innerHTML = '—';
|
||||
dialogueBtn.title = 'Форматировать диалоги (—)';
|
||||
dialogueBtn.style.fontWeight = 'bold';
|
||||
}
|
||||
|
||||
if (undoDialogueBtn) {
|
||||
undoDialogueBtn.innerHTML = '-';
|
||||
undoDialogueBtn.title = 'Убрать форматирование диалогов (-)';
|
||||
undoDialogueBtn.style.fontWeight = 'bold';
|
||||
}
|
||||
|
||||
if (fullscreenBtn) {
|
||||
fullscreenBtn.innerHTML = '⛶';
|
||||
fullscreenBtn.title = 'Полноэкранный режим';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.writerEditor = new WriterEditor();
|
||||
// Проверяем, это редактор рассказа или главы
|
||||
const storyEditor = document.getElementById('story-editor');
|
||||
const quillEditor = document.getElementById('quill-editor');
|
||||
|
||||
if (storyEditor) {
|
||||
const textarea = document.getElementById('story-content');
|
||||
if (textarea) {
|
||||
window.writerEditor = new WriterEditor('#book-form', 'story-editor', 'story-content', true);
|
||||
}
|
||||
} else if (quillEditor) {
|
||||
window.writerEditor = new WriterEditor();
|
||||
}
|
||||
});
|
||||
Vendored
+9
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user