v0.2.11: 커스텀 공유 모달과 레이아웃 수정 반영

Made-with: Cursor
This commit is contained in:
2026-04-17 16:14:34 +09:00
parent 8bbb2c73f1
commit 12a1c450ae
13 changed files with 435 additions and 32 deletions

View File

@@ -699,6 +699,217 @@
});
}
function copyTextToClipboard(text) {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text);
}
return new Promise(function (resolve, reject) {
var textArea = document.createElement("textarea");
textArea.value = text;
textArea.setAttribute("readonly", "readonly");
textArea.style.position = "fixed";
textArea.style.opacity = "0";
textArea.style.pointerEvents = "none";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
var copied = document.execCommand("copy");
document.body.removeChild(textArea);
if (!copied) {
reject(new Error("Copy command was rejected"));
return;
}
resolve();
} catch (error) {
document.body.removeChild(textArea);
reject(error);
}
});
}
function setShareButtonFeedback(button, label) {
if (!button) {
return;
}
var originalLabel = button.dataset.shareOriginalLabel || button.getAttribute("aria-label") || "Share this post";
button.dataset.shareOriginalLabel = originalLabel;
button.setAttribute("aria-label", label);
if (button.dataset.shareFeedbackTimer) {
window.clearTimeout(Number(button.dataset.shareFeedbackTimer));
}
button.dataset.shareFeedbackTimer = String(window.setTimeout(function () {
button.setAttribute("aria-label", originalLabel);
delete button.dataset.shareFeedbackTimer;
}, 1600));
}
var shareModal = document.querySelector("[data-share-modal]");
var shareModalDialog = shareModal ? shareModal.querySelector(".share-modal__dialog") : null;
var shareModalTitle = shareModal ? shareModal.querySelector("[data-share-modal-title]") : null;
var shareModalDescription = shareModal ? shareModal.querySelector("[data-share-modal-description]") : null;
var shareModalImage = shareModal ? shareModal.querySelector("[data-share-modal-image]") : null;
var shareModalCopyButton = shareModal ? shareModal.querySelector("[data-share-copy-button]") : null;
var shareModalCopyLabel = shareModal ? shareModal.querySelector("[data-share-copy-label]") : null;
var activeShareMetadata = null;
function buildShareHrefMap(metadata) {
var encodedUrl = encodeURIComponent(metadata.url || "");
var encodedTitle = encodeURIComponent(metadata.title || "");
return {
x: "https://twitter.com/intent/tweet?text=" + encodedTitle + "&url=" + encodedUrl
};
}
function renderShareModal(metadata) {
if (!shareModal) {
return;
}
activeShareMetadata = metadata;
var hrefMap = buildShareHrefMap(metadata);
if (shareModalTitle) {
shareModalTitle.textContent = metadata.title || document.title || "Post";
}
if (shareModalDescription) {
shareModalDescription.textContent = metadata.description || "이 글을 공유해보세요.";
}
if (shareModalImage) {
if (metadata.image) {
shareModalImage.src = metadata.image;
shareModalImage.alt = metadata.title || "Post image";
shareModalImage.classList.remove("hidden");
} else {
shareModalImage.removeAttribute("src");
shareModalImage.classList.add("hidden");
}
}
Object.keys(hrefMap).forEach(function (key) {
var link = shareModal.querySelector("[data-share-link='" + key + "']");
if (!link) {
return;
}
link.href = hrefMap[key];
});
if (shareModalCopyLabel) {
shareModalCopyLabel.textContent = "주소 복사";
}
}
function openShareModal(metadata) {
if (!shareModal || !metadata || !metadata.url) {
return;
}
renderShareModal(metadata);
shareModal.hidden = false;
body.classList.add("share-modal-open");
window.requestAnimationFrame(function () {
shareModal.classList.add("is-open");
});
}
function closeShareModal() {
if (!shareModal || shareModal.hidden) {
return;
}
shareModal.classList.remove("is-open");
body.classList.remove("share-modal-open");
window.setTimeout(function () {
if (!shareModal.classList.contains("is-open")) {
shareModal.hidden = true;
}
}, 180);
}
function getShareMetadata(button) {
return {
url: (button.dataset.shareUrl || window.location.href || "").trim(),
title: (button.dataset.shareTitle || document.title || "").trim(),
description: (button.dataset.shareDescription || "").trim(),
image: (button.dataset.shareImage || "").trim()
};
}
function initializeShareButtons() {
document.querySelectorAll("[data-post-share-toggle]").forEach(function (button) {
if (button.dataset.shareBound === "true") {
return;
}
button.dataset.shareBound = "true";
button.addEventListener("click", function (event) {
event.preventDefault();
openShareModal(getShareMetadata(button));
});
});
}
if (shareModal) {
shareModal.querySelectorAll("[data-share-modal-close]").forEach(function (button) {
button.addEventListener("click", closeShareModal);
});
shareModal.addEventListener("click", function (event) {
if (shareModalDialog && !shareModalDialog.contains(event.target)) {
closeShareModal();
}
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && !shareModal.hidden) {
closeShareModal();
}
});
if (shareModalCopyButton) {
shareModalCopyButton.addEventListener("click", function () {
if (!activeShareMetadata || !activeShareMetadata.url) {
return;
}
copyTextToClipboard(activeShareMetadata.url)
.then(function () {
setShareButtonFeedback(shareModalCopyButton, "복사 완료");
shareModalCopyButton.classList.add("is-success");
window.setTimeout(function () {
shareModalCopyButton.classList.remove("is-success");
}, 1400);
if (shareModalCopyLabel) {
shareModalCopyLabel.textContent = "복사 완료";
window.setTimeout(function () {
if (shareModalCopyLabel) {
shareModalCopyLabel.textContent = "주소 복사";
}
}, 1400);
}
})
.catch(function () {});
});
}
}
initializeShareButtons();
function updateLoadMoreState(pagination, nextUrl, loading) {
var trigger = pagination.querySelector("[data-load-more-trigger]");
if (!trigger) {
@@ -767,6 +978,7 @@
list.appendChild(item.cloneNode(true));
});
initializeShareButtons();
updateLoadMoreState(pagination, nextPagination ? nextPagination.dataset.nextUrl : "", false);
})
.catch(function () {