Редактор подробного описания
улучшил поведение.
This commit is contained in:
@@ -46,9 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, onUpdated, onUnmounted } from 'vue'
|
||||
|
||||
const STORAGE_KEY = 'taskboard_editor_height'
|
||||
import { ref, watch, onMounted, onUpdated } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -72,22 +70,7 @@ const props = defineProps({
|
||||
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
|
||||
|
||||
const editorRef = ref(null)
|
||||
let resizeObserver = null
|
||||
|
||||
// Получить сохранённую высоту из localStorage
|
||||
function getSavedHeight() {
|
||||
const saved = localStorage.getItem(STORAGE_KEY)
|
||||
if (saved) {
|
||||
const height = parseInt(saved, 10)
|
||||
if (!isNaN(height) && height >= 120) return height
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Сохранить высоту в localStorage
|
||||
function saveHeight(height) {
|
||||
localStorage.setItem(STORAGE_KEY, String(height))
|
||||
}
|
||||
let isInternalChange = false
|
||||
|
||||
// Применить форматирование
|
||||
const applyFormat = (command) => {
|
||||
@@ -104,6 +87,7 @@ const syncContent = () => {
|
||||
html = html.replace(/<\/div><div>/gi, '<br>')
|
||||
html = html.replace(/<div>/gi, '')
|
||||
html = html.replace(/<\/div>/gi, '')
|
||||
isInternalChange = true
|
||||
emit('update:modelValue', html)
|
||||
}
|
||||
|
||||
@@ -145,8 +129,44 @@ const onKeydown = (e) => {
|
||||
}
|
||||
}
|
||||
|
||||
// После пробела или Enter — подсвечиваем ссылки
|
||||
if (props.autoLinkify && (e.key === ' ' || e.key === 'Enter')) {
|
||||
// Перехватываем Enter и вставляем <br> вручную
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
|
||||
const selection = window.getSelection()
|
||||
if (!selection.rangeCount) return
|
||||
|
||||
const range = selection.getRangeAt(0)
|
||||
range.deleteContents()
|
||||
|
||||
// Вставляем <br>
|
||||
const br = document.createElement('br')
|
||||
range.insertNode(br)
|
||||
|
||||
// Если мы в конце контента, добавляем ещё один <br> для видимости
|
||||
const nextSibling = br.nextSibling
|
||||
if (!nextSibling || (nextSibling.nodeType === Node.TEXT_NODE && !nextSibling.textContent)) {
|
||||
const extraBr = document.createElement('br')
|
||||
br.parentNode.insertBefore(extraBr, br.nextSibling)
|
||||
}
|
||||
|
||||
// Перемещаем курсор после <br>
|
||||
range.setStartAfter(br)
|
||||
range.setEndAfter(br)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
|
||||
syncContent()
|
||||
|
||||
// Подсвечиваем ссылки после Enter
|
||||
if (props.autoLinkify) {
|
||||
setTimeout(linkifyContent, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// После пробела — подсвечиваем ссылки
|
||||
if (props.autoLinkify && e.key === ' ') {
|
||||
setTimeout(linkifyContent, 0)
|
||||
}
|
||||
}
|
||||
@@ -205,7 +225,11 @@ const setContent = (html) => {
|
||||
// Следим за внешними изменениями modelValue
|
||||
watch(() => props.modelValue, (newVal, oldVal) => {
|
||||
// Обновляем только если изменение пришло извне
|
||||
if (editorRef.value && editorRef.value.innerHTML !== newVal) {
|
||||
if (isInternalChange) {
|
||||
isInternalChange = false
|
||||
return
|
||||
}
|
||||
if (editorRef.value) {
|
||||
setContent(newVal)
|
||||
}
|
||||
}, { immediate: true })
|
||||
@@ -220,36 +244,10 @@ const refreshIcons = () => {
|
||||
onMounted(() => {
|
||||
refreshIcons()
|
||||
setContent(props.modelValue)
|
||||
|
||||
// Восстановить сохранённую высоту
|
||||
const savedHeight = getSavedHeight()
|
||||
if (savedHeight && editorRef.value) {
|
||||
editorRef.value.style.height = savedHeight + 'px'
|
||||
}
|
||||
|
||||
// Следить за изменением размера и сохранять
|
||||
if (editorRef.value) {
|
||||
resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const height = Math.round(entry.contentRect.height)
|
||||
if (height >= 120) {
|
||||
saveHeight(height)
|
||||
}
|
||||
}
|
||||
})
|
||||
resizeObserver.observe(editorRef.value)
|
||||
}
|
||||
})
|
||||
|
||||
onUpdated(refreshIcons)
|
||||
|
||||
onUnmounted(() => {
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect()
|
||||
resizeObserver = null
|
||||
}
|
||||
})
|
||||
|
||||
// Экспортируем методы для использования извне
|
||||
defineExpose({
|
||||
setContent,
|
||||
@@ -342,6 +340,11 @@ defineExpose({
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.rich-editor::-webkit-resizer {
|
||||
background: linear-gradient(135deg, transparent 60%, rgba(255, 255, 255, 0.1) 60%, rgba(255, 255, 255, 0.1) 75%, transparent 75%, transparent 85%, rgba(255, 255, 255, 0.15) 85%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.rich-editor :deep(a) {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
|
||||
Reference in New Issue
Block a user