feat(comments): full 1:1 zibll comment kernel for posts, forum, shop
Extract shared comment helpers (content/item/form/geo/actions), port GIF [g=] smilies + FixedInput/moveForm UX, wire close_comments/city/quick settings, REST comment get, and align product review chrome with zibll.
This commit is contained in:
1 parent
9edd39c33d
commit
276a2891ba
116 files changed
+2658
-1848
No files matched your search
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Mobile FixedInput + autoheight for comment respond (zibll parity).
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function $(sel, root) {
|
||||
return (root || document).querySelector(sel);
|
||||
}
|
||||
|
||||
function FixedInput(el, action) {
|
||||
if (!el) return;
|
||||
if (!el.dataset.fixedBound) {
|
||||
el.addEventListener('click', function (e) {
|
||||
if (e.target && e.target.closest && e.target.closest('.fixed-body')) {
|
||||
FixedInput(el, 'hide');
|
||||
}
|
||||
});
|
||||
el.dataset.fixedBound = '1';
|
||||
}
|
||||
if (action === 'show') {
|
||||
el.classList.add('show');
|
||||
window.setTimeout(function () {
|
||||
var field = el.querySelector('textarea, input');
|
||||
if (field) field.focus();
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
if (action === 'hide') {
|
||||
var cancel = el.querySelector('#cancel-comment-reply-link');
|
||||
if (cancel) cancel.click();
|
||||
el.querySelectorAll('input,textarea').forEach(function (n) {
|
||||
try { n.blur(); } catch (e) {}
|
||||
});
|
||||
el.querySelectorAll('.dropup.open, .open').forEach(function (n) {
|
||||
n.classList.remove('open');
|
||||
});
|
||||
el.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var trigger = e.target && e.target.closest ? e.target.closest('[fixed-input]') : null;
|
||||
if (!trigger) return;
|
||||
e.preventDefault();
|
||||
var targetSel = trigger.getAttribute('fixed-input') || '#respond';
|
||||
var panel = document.querySelector(targetSel);
|
||||
var comments = document.getElementById('comments');
|
||||
if (comments && comments.scrollIntoView) {
|
||||
comments.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
FixedInput(panel, 'show');
|
||||
});
|
||||
|
||||
// Autoheight textareas.
|
||||
function bindAutoheight(ta) {
|
||||
if (!ta || ta.dataset.autoheightBound) return;
|
||||
ta.dataset.autoheightBound = '1';
|
||||
var max = parseInt(ta.getAttribute('maxheight') || '188', 10) || 188;
|
||||
var resize = function () {
|
||||
ta.style.height = 'auto';
|
||||
var h = Math.min(ta.scrollHeight, max);
|
||||
ta.style.height = h + 'px';
|
||||
ta.style.overflowY = ta.scrollHeight > max ? 'auto' : 'hidden';
|
||||
};
|
||||
ta.addEventListener('input', resize);
|
||||
resize();
|
||||
}
|
||||
|
||||
function scan() {
|
||||
document.querySelectorAll('textarea[autoheight], textarea.grin').forEach(bindAutoheight);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', scan);
|
||||
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
|
||||
window.AksrtFrontend.register('aksrt-comment-fixed-input', function (root) {
|
||||
scan();
|
||||
// Expose for submit hide.
|
||||
root = root || document;
|
||||
var respond = root.querySelector ? root.querySelector('#respond') : document.getElementById('respond');
|
||||
if (respond) {
|
||||
respond.aksrtFixedInput = function (action) {
|
||||
FixedInput(respond, action);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.AksrtFixedInput = FixedInput;
|
||||
})();
|
||||
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Comment moveForm for reply/edit (zibll parity) — REST-backed edit fetch.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var respondId = 'respond';
|
||||
var tempId = 'aksrt-wp-temp-form-div';
|
||||
var editingId = 0;
|
||||
|
||||
function getRespond() {
|
||||
return document.getElementById(respondId);
|
||||
}
|
||||
|
||||
function restRoot() {
|
||||
var d = window.aksrtData || {};
|
||||
return (d.restUrl || d.rest_url || '/wp-json/aksrt/v1').replace(/\/$/, '');
|
||||
}
|
||||
|
||||
function nonce() {
|
||||
var d = window.aksrtData || {};
|
||||
return d.restNonce || d.nonce || '';
|
||||
}
|
||||
|
||||
function notify(msg, type) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg, type || 'info');
|
||||
return;
|
||||
}
|
||||
if (window.notyf) {
|
||||
try { window.notyf[type === 'error' ? 'error' : 'success'](msg); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function setActionText(text) {
|
||||
var el = document.querySelector('#' + respondId + ' .action-text');
|
||||
if (!el) return;
|
||||
if (text) {
|
||||
el.style.display = '';
|
||||
el.textContent = text;
|
||||
} else {
|
||||
el.style.display = 'none';
|
||||
el.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
function ensureParentField(respond) {
|
||||
var form = respond.querySelector('#commentform') || respond;
|
||||
var parent = form.querySelector('[name="comment_parent"]');
|
||||
if (!parent) {
|
||||
parent = document.createElement('input');
|
||||
parent.type = 'hidden';
|
||||
parent.name = 'comment_parent';
|
||||
parent.id = 'comment_parent';
|
||||
parent.value = '0';
|
||||
form.appendChild(parent);
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
function ensureEditField(respond, commentId) {
|
||||
var form = respond.querySelector('#commentform') || respond;
|
||||
var existing = form.querySelector('[name="edit_comment_ID"]');
|
||||
if (commentId) {
|
||||
if (!existing) {
|
||||
existing = document.createElement('input');
|
||||
existing.type = 'hidden';
|
||||
existing.name = 'edit_comment_ID';
|
||||
existing.id = 'edit_comment_ID';
|
||||
form.appendChild(existing);
|
||||
}
|
||||
existing.value = String(commentId);
|
||||
// Hide parent while editing.
|
||||
var parent = form.querySelector('[name="comment_parent"]');
|
||||
if (parent) parent.name = 'comment_parent_bak';
|
||||
} else if (existing) {
|
||||
existing.remove();
|
||||
var bak = form.querySelector('[name="comment_parent_bak"]');
|
||||
if (bak) bak.name = 'comment_parent';
|
||||
}
|
||||
}
|
||||
|
||||
function moveForm(commId, parentId, isEdit) {
|
||||
var respond = getRespond();
|
||||
if (!respond) return false;
|
||||
var comm = document.getElementById(commId);
|
||||
var parent = ensureParentField(respond);
|
||||
var cancel = document.getElementById('cancel-comment-reply-link');
|
||||
var submit = respond.querySelector('#submit');
|
||||
var textarea = respond.querySelector('#comment');
|
||||
|
||||
if (!document.getElementById(tempId)) {
|
||||
var temp = document.createElement('div');
|
||||
temp.id = tempId;
|
||||
temp.style.display = 'none';
|
||||
respond.parentNode.insertBefore(temp, respond);
|
||||
}
|
||||
|
||||
if (comm && comm.parentNode) {
|
||||
comm.parentNode.insertBefore(respond, comm.nextSibling);
|
||||
}
|
||||
|
||||
parent.value = isEdit ? '0' : String(parentId || '0');
|
||||
editingId = isEdit ? parseInt(parentId, 10) || 0 : 0;
|
||||
|
||||
if (cancel) {
|
||||
cancel.style.display = '';
|
||||
cancel.onclick = function () {
|
||||
var t = document.getElementById(tempId);
|
||||
var r = getRespond();
|
||||
if (t && r && t.parentNode) {
|
||||
t.parentNode.insertBefore(r, t);
|
||||
t.parentNode.removeChild(t);
|
||||
}
|
||||
ensureEditField(r, 0);
|
||||
if (parent) parent.value = '0';
|
||||
setActionText('');
|
||||
if (textarea) textarea.value = '';
|
||||
editingId = 0;
|
||||
cancel.style.display = 'none';
|
||||
if (window.AksrtFixedInput) window.AksrtFixedInput(r, 'hide');
|
||||
else if (r) r.classList.remove('show');
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
if (isEdit && editingId) {
|
||||
setActionText('编辑评论');
|
||||
ensureEditField(respond, editingId);
|
||||
if (textarea) {
|
||||
textarea.disabled = true;
|
||||
textarea.value = '加载中…';
|
||||
}
|
||||
if (submit) submit.disabled = true;
|
||||
fetch(restRoot() + '/comment/get?comment_id=' + encodeURIComponent(String(editingId)), {
|
||||
headers: { 'X-WP-Nonce': nonce() },
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (json) {
|
||||
var content = (json && (json.content || (json.data && json.data.content))) || '';
|
||||
if (textarea) {
|
||||
textarea.disabled = false;
|
||||
textarea.value = content;
|
||||
textarea.dispatchEvent(new Event('input'));
|
||||
}
|
||||
if (submit) submit.disabled = false;
|
||||
})
|
||||
.catch(function () {
|
||||
notify('加载评论失败', 'error');
|
||||
if (textarea) {
|
||||
textarea.disabled = false;
|
||||
textarea.value = '';
|
||||
}
|
||||
if (submit) submit.disabled = false;
|
||||
});
|
||||
} else {
|
||||
setActionText('回复评论');
|
||||
ensureEditField(respond, 0);
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
}
|
||||
}
|
||||
|
||||
if (window.AksrtFixedInput) {
|
||||
window.AksrtFixedInput(respond, 'show');
|
||||
} else {
|
||||
respond.classList.add('show');
|
||||
}
|
||||
|
||||
if (comm && comm.scrollIntoView) {
|
||||
comm.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var reply = e.target && e.target.closest ? e.target.closest('.comment-reply-link') : null;
|
||||
var edit = e.target && e.target.closest ? e.target.closest('.comment-edit-link, [data-comment-action="edit"]') : null;
|
||||
if (reply) {
|
||||
e.preventDefault();
|
||||
var cid = reply.getAttribute('data-commentid') || reply.getAttribute('data-comment-id') || '';
|
||||
var postid = reply.getAttribute('data-postid') || '';
|
||||
if (!cid) {
|
||||
var li = reply.closest('[data-comment-id], #comment-' + (cid || ''));
|
||||
if (li && li.getAttribute('data-comment-id')) cid = li.getAttribute('data-comment-id');
|
||||
}
|
||||
if (!cid) {
|
||||
var m = (reply.closest('li[id^="comment-"]') || {}).id || '';
|
||||
var mm = /comment-(\d+)/.exec(m);
|
||||
if (mm) cid = mm[1];
|
||||
}
|
||||
moveForm('div-comment-' + cid, cid, false);
|
||||
return;
|
||||
}
|
||||
if (edit) {
|
||||
e.preventDefault();
|
||||
var eid = edit.getAttribute('data-commentid') || edit.getAttribute('data-comment-id') || '';
|
||||
if (!eid) {
|
||||
var row = edit.closest('[data-comment-id]');
|
||||
if (row) eid = row.getAttribute('data-comment-id');
|
||||
}
|
||||
if (!eid) return;
|
||||
moveForm('div-comment-' + eid, eid, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Dropup toggles (guest / expand).
|
||||
document.addEventListener('click', function (e) {
|
||||
var t = e.target && e.target.closest ? e.target.closest('[data-toggle-class="open"]') : null;
|
||||
if (!t) return;
|
||||
var targetSel = t.getAttribute('data-target');
|
||||
if (!targetSel) return;
|
||||
e.preventDefault();
|
||||
var wrap = t.closest('.dropup, .input-expand-wrap, #comment-user-info') || t.parentElement;
|
||||
var panel = targetSel.charAt(0) === '#' || targetSel.charAt(0) === '.'
|
||||
? (wrap && wrap.querySelector(targetSel)) || document.querySelector(targetSel)
|
||||
: null;
|
||||
if (wrap) wrap.classList.toggle('open');
|
||||
if (panel && panel !== wrap) panel.classList.toggle('open');
|
||||
});
|
||||
|
||||
// Close dropups on outside click.
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target.closest && e.target.closest('.dropup, .input-expand-wrap, [data-toggle-class="open"]')) return;
|
||||
document.querySelectorAll('.dropup.open, .input-expand-wrap.open, #comment-user-info.open').forEach(function (n) {
|
||||
n.classList.remove('open');
|
||||
});
|
||||
});
|
||||
|
||||
// Scroll helper for @parent.
|
||||
document.addEventListener('click', function (e) {
|
||||
var a = e.target && e.target.closest ? e.target.closest('[data-scroll-to]') : null;
|
||||
if (!a) return;
|
||||
e.preventDefault();
|
||||
var sel = a.getAttribute('data-scroll-to');
|
||||
var el = sel ? document.querySelector(sel) : null;
|
||||
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
});
|
||||
|
||||
window.aksrtCommentMoveForm = moveForm;
|
||||
window.aksrtGetEditingCommentId = function () { return editingId; };
|
||||
})();
|
||||
@@ -206,11 +206,17 @@
|
||||
|
||||
var formData = collectFormData();
|
||||
var restRoot = (window.aksrtData && aksrtData.siteUrl) ? String(aksrtData.siteUrl).replace(/\/$/, '') : '';
|
||||
var restBase = (restRoot || '') + '/wp-json/aksrt/v1/comment/submit';
|
||||
var editId = form.querySelector('[name="edit_comment_ID"]');
|
||||
var isEdit = editId && parseInt(editId.value, 10) > 0;
|
||||
var restBase = (restRoot || '') + '/wp-json/aksrt/v1/comment/' + (isEdit ? 'edit' : 'submit');
|
||||
|
||||
// Try REST endpoint for local-insert HTML response
|
||||
var restBody = {};
|
||||
formData.forEach(function (v, k) { restBody[k] = v; });
|
||||
if (isEdit) {
|
||||
restBody.comment_id = parseInt(editId.value, 10);
|
||||
restBody.content = restBody.comment || restBody.content || '';
|
||||
}
|
||||
|
||||
fetch(restBase, {
|
||||
method: 'POST',
|
||||
@@ -223,6 +229,21 @@
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (json) {
|
||||
if (isEdit && json && json.success) {
|
||||
var cid = restBody.comment_id;
|
||||
var contentEl = document.querySelector('#comment-content-' + cid + ', [data-comment-id="' + cid + '"] .comment-content');
|
||||
var rendered = (json.data && json.data.content) || json.content || '';
|
||||
if (contentEl && rendered) contentEl.innerHTML = rendered;
|
||||
var textarea = form.querySelector('#comment');
|
||||
if (textarea) textarea.value = '';
|
||||
if (editId) editId.remove();
|
||||
restoreRespondPosition();
|
||||
setLoading(false);
|
||||
showToast(json.message || '评论已更新', 'success');
|
||||
var respondEl = document.getElementById('respond');
|
||||
if (respondEl && window.AksrtFixedInput) window.AksrtFixedInput(respondEl, 'hide');
|
||||
return;
|
||||
}
|
||||
if (json && json.success && json.html) {
|
||||
// Local insert: prepend new comment HTML
|
||||
localInsertComment(json.html, json.is_reply, json.parent_id, json.needs_moderation);
|
||||
@@ -252,6 +273,18 @@
|
||||
// Move respond back to original position
|
||||
restoreRespondPosition();
|
||||
|
||||
// Hide mobile FixedInput after successful submit.
|
||||
var respondEl = document.getElementById('respond');
|
||||
if (respondEl && window.AksrtFixedInput) {
|
||||
window.AksrtFixedInput(respondEl, 'hide');
|
||||
} else if (respondEl) {
|
||||
respondEl.classList.remove('show');
|
||||
}
|
||||
|
||||
// If editing via moveForm, clear edit field.
|
||||
var editField = form.querySelector('[name="edit_comment_ID"]');
|
||||
if (editField) editField.remove();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user