added 引用评论功能增加
This commit is contained in:
1 parent
b9fd4cb7f6
commit
34108e71d0
2 files changed
+237
No files matched your search
@@ -2941,9 +2941,11 @@ img[atk-emoticon] {
|
||||
/* Fix Artalk comment textarea color in light mode */
|
||||
.atk-textarea {
|
||||
color: #1a1a1a !important;
|
||||
background: #fff !important;
|
||||
}
|
||||
[data-theme="dark"] .atk-textarea {
|
||||
color: #e5e7eb !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* About Page Card Style */
|
||||
@@ -2959,6 +2961,108 @@ img[atk-emoticon] {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Selection Popup (Quote Button) */
|
||||
#selection-popup {
|
||||
position: fixed;
|
||||
display: none;
|
||||
background: rgba(30, 30, 30, 0.9);
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
z-index: 2147483647;
|
||||
transform: translate(-50%, -100%);
|
||||
pointer-events: auto;
|
||||
/* Removed backdrop-filter for performance */
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
transition: opacity 0.2s, transform 0.2s;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
white-space: nowrap;
|
||||
will-change: transform, opacity;
|
||||
animation: popIn 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
||||
}
|
||||
|
||||
@keyframes popIn {
|
||||
0% { transform: translate(-50%, -80%) scale(0.9); opacity: 0; }
|
||||
100% { transform: translate(-50%, -100%) scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
#selection-popup i {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#selection-popup::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
border-width: 6px;
|
||||
border-style: solid;
|
||||
border-color: rgba(30, 30, 30, 0.9) transparent transparent transparent;
|
||||
}
|
||||
|
||||
#selection-popup:hover {
|
||||
transform: translate(-50%, -110%) scale(1.05);
|
||||
background: #000;
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,0.25);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #selection-popup {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
color: #000;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #selection-popup::after {
|
||||
border-color: rgba(255, 255, 255, 0.95) transparent transparent transparent;
|
||||
}
|
||||
|
||||
[data-theme="dark"] #selection-popup:hover {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* Artalk Comment Blockquote Redesign & Dark Mode Fix */
|
||||
.atk-content blockquote {
|
||||
margin: 10px 0 6px 0; /* Reduced bottom margin for tighter layout */
|
||||
padding: 12px 16px;
|
||||
background-color: var(--bankuai-color);
|
||||
border-left: 4px solid var(--text-highlight-color);
|
||||
border-radius: 4px;
|
||||
color: var(--text-content-color);
|
||||
position: relative;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.atk-content blockquote + p {
|
||||
margin-top: 4px !important; /* Reduce spacing for paragraph immediately following blockquote */
|
||||
}
|
||||
|
||||
.atk-content blockquote p {
|
||||
margin: 0;
|
||||
color: inherit !important;
|
||||
font-size: 0.95rem !important;
|
||||
line-height: 1.6 !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .atk-content blockquote {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border-left-color: #4b5563; /* Use a slightly lighter gray for border in dark mode */
|
||||
color: var(--text-content-dark-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .atk-content blockquote p {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* Category Page Styles */
|
||||
.category-container {
|
||||
max-width: 100%;
|
||||
|
||||
@@ -366,8 +366,141 @@ function initFloatingTools() {
|
||||
// Listen for PJAX complete to update visibility
|
||||
document.addEventListener('pjax:complete', function() {
|
||||
updateFloatingToolsVisibility();
|
||||
hideSelectionPopup();
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initFloatingTools();
|
||||
initSelectionPopup();
|
||||
});
|
||||
|
||||
// Selection Popup Logic
|
||||
let selectionPopup = null;
|
||||
let selectionTimeout;
|
||||
|
||||
function initSelectionPopup() {
|
||||
if (document.getElementById('selection-popup')) return;
|
||||
|
||||
selectionPopup = document.createElement('div');
|
||||
selectionPopup.id = 'selection-popup';
|
||||
selectionPopup.innerHTML = '<i class="ri-chat-quote-line"></i> <span>引用评论</span>';
|
||||
document.body.appendChild(selectionPopup);
|
||||
|
||||
// Prevent mousedown on popup from clearing selection
|
||||
selectionPopup.addEventListener('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// Handle click on popup
|
||||
selectionPopup.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
let selection = window.getSelection().toString();
|
||||
// Optimize: Remove extra whitespace and limit length
|
||||
if (selection) {
|
||||
// Collapse multiple empty lines into max 2 newlines, and trim each line
|
||||
selection = selection.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(line => line !== '')
|
||||
.join('\n');
|
||||
|
||||
if (selection.length > 500) {
|
||||
selection = selection.substring(0, 500) + '...';
|
||||
}
|
||||
|
||||
scrollToCommentsAndQuote(selection);
|
||||
hideSelectionPopup();
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
});
|
||||
|
||||
// Global listeners for selection
|
||||
document.addEventListener('mouseup', handleSelectionChange);
|
||||
document.addEventListener('keyup', handleSelectionChange);
|
||||
}
|
||||
|
||||
function hideSelectionPopup() {
|
||||
if (selectionPopup) {
|
||||
selectionPopup.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelectionChange(e) {
|
||||
// Debounce to avoid rapid firing
|
||||
clearTimeout(selectionTimeout);
|
||||
selectionTimeout = setTimeout(() => {
|
||||
const selection = window.getSelection();
|
||||
const text = selection.toString().trim();
|
||||
const content = document.querySelector('.post-content'); // Main article container
|
||||
|
||||
if (!selectionPopup || !content) return;
|
||||
|
||||
// Check if selection is empty
|
||||
if (!text || selection.rangeCount === 0) {
|
||||
hideSelectionPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
// Check if the common ancestor is inside .post-content
|
||||
// Optimization: Check container existence before heavy contains check
|
||||
if (!content.contains(range.commonAncestorContainer)) {
|
||||
hideSelectionPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimization: Use requestAnimationFrame for UI updates
|
||||
requestAnimationFrame(() => {
|
||||
// Show first to ensure measurements if needed (though we set top/left directly)
|
||||
selectionPopup.style.display = 'flex';
|
||||
|
||||
// Position logic: Follow mouse if available (mouseup), else fallback to selection rect (keyup)
|
||||
if (e && e.type === 'mouseup') {
|
||||
// Optimization: Avoid getBoundingClientRect on mouseup
|
||||
// Position above the mouse cursor
|
||||
selectionPopup.style.top = `${e.clientY - 40}px`;
|
||||
selectionPopup.style.left = `${e.clientX}px`;
|
||||
} else {
|
||||
// Fallback for keyboard selection (needs rect)
|
||||
const rect = range.getBoundingClientRect();
|
||||
// Check if visible and valid
|
||||
if (rect.width === 0 || rect.height === 0) {
|
||||
hideSelectionPopup();
|
||||
return;
|
||||
}
|
||||
selectionPopup.style.top = `${rect.top}px`;
|
||||
selectionPopup.style.left = `${rect.left + rect.width / 2}px`;
|
||||
}
|
||||
});
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function scrollToCommentsAndQuote(text) {
|
||||
const comments = document.getElementById('Comments');
|
||||
if (comments) {
|
||||
comments.scrollIntoView({ behavior: 'smooth' });
|
||||
|
||||
// Wait for scroll to complete (approximate)
|
||||
setTimeout(() => {
|
||||
// Find Artalk textarea
|
||||
const textarea = document.querySelector('.atk-textarea');
|
||||
if (textarea) {
|
||||
const quote = `> ${text}\n\n`;
|
||||
// Overwrite existing content as requested
|
||||
textarea.value = quote;
|
||||
textarea.focus();
|
||||
|
||||
// Trigger input event for Vue/React binding (Artalk uses Vue)
|
||||
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
textarea.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
if (window.Qmsg) {
|
||||
Qmsg.success('引用成功');
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user