Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Lightweight social sharing component for web applications. Zero dependencies, fr

## Features

- 🌐 Multiple platforms: WhatsApp, Facebook, X, LinkedIn, Telegram, Reddit, Email, Pinterest, Discord
- 🌐 Multiple platforms: WhatsApp, Facebook, X, LinkedIn, Telegram, Reddit, Email, Pinterest, Discord, QR Code
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- 🎯 Zero dependencies - pure vanilla JavaScript
- ⚛️ Framework support: React, Preact, Next.js, Qwik, Vue, Angular, or plain HTML
- 🔄 Auto-detects current URL and page title
Expand Down Expand Up @@ -527,7 +527,7 @@ new SocialShareButton({
| `onCopy` | function | `null` | Callback when user copies link: `(url) => {}` |

**Available Platforms:**
`whatsapp`, `facebook`, `twitter`, `linkedin`, `telegram`, `reddit`, `email`, `pinterest`, `discord`
`whatsapp`, `facebook`, `twitter`, `linkedin`, `telegram`, `reddit`, `email`, `pinterest`, `discord`, `qrcode`

### Customize Share Message/Post Text

Expand Down Expand Up @@ -659,6 +659,21 @@ new SocialShareButton({

## Advanced Usage

### Using the QR Code Extension

To enable the QR Code feature, you must include the extension script in your HTML along with the main script:

```html
<!-- Main CSS and JS -->
<link rel="stylesheet" href="path/to/social-share-button.css" />
<script src="path/to/social-share-button.js"></script>

<!-- QR Code Extension -->
<script src="path/to/social-share-button-qr.js"></script>
```

When you pass `qrcode` in the `platforms` array, a QR code generation panel will be rendered inline inside the share modal when clicked.

### Using npm Package

```javascript
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"src/social-share-button.js",
"src/social-share-button.css",
"src/social-share-button-react.jsx",
"src/social-share-button-qr.js",
"src/social-share-analytics.js",
"README.md",
"LICENSE"
Expand Down
176 changes: 176 additions & 0 deletions src/social-share-button-qr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/**
* SocialShareButton QR Code Extension
* Dynamically loads Kazuhiko Arase's qrcode-generator from CDN
*/

(function () {
function applyQRPatch() {
if (typeof window === "undefined" || !window.SocialShareButton) {
console.warn("SocialShareButton core must be loaded before QR extension");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
return;
}

// Guard against double-patching
if (window.SocialShareButton._qrPatched) return;
window.SocialShareButton._qrPatched = true;

var originalShare = window.SocialShareButton.prototype.share;
var originalCloseModal = window.SocialShareButton.prototype.closeModal;

window.SocialShareButton.prototype.share = function (platform) {
if (platform === "qrcode") {
this._emit("social_share_click", "share", { platform });
this.renderQRPanel();
this._emit("social_share_success", "share", { platform });

if (this.options.onShare) {
this.options.onShare(platform, this.options.url);
}
return;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

// Delegate all other platforms to the original handler
return originalShare.call(this, platform);
};

window.SocialShareButton.prototype.renderQRPanel = function () {
if (!this.modal) return;

// Do not render twice
if (this.modal.querySelector(".social-share-qr-panel")) return;

if (typeof window.qrcode === "undefined") {
console.error("qrcode-generator failed to load.");
return;
}

// --- Generate QR data ---
var typeNumber = 0; // 0 = auto-detect
var errorCorrectionLevel = "M";
var qr = window.qrcode(typeNumber, errorCorrectionLevel);
qr.addData(this.options.url);
qr.make();

var moduleCount = qr.getModuleCount();
var cellSize = Math.max(3, Math.floor(180 / moduleCount));
var margin = 2;
var size = moduleCount * cellSize + margin * 2 * cellSize;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// --- Build DOM ---
var qrPanel = document.createElement("div");
qrPanel.className = "social-share-qr-panel";

var title = document.createElement("h4");
title.textContent = "Scan QR Code";

var canvas = document.createElement("canvas");
canvas.className = "social-share-qr-canvas";
canvas.width = size;
canvas.height = size;

var ctx = canvas.getContext("2d");

// White background
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, size, size);

// Dark modules
ctx.fillStyle = "#000000";
for (var row = 0; row < moduleCount; row++) {
for (var col = 0; col < moduleCount; col++) {
if (qr.isDark(row, col)) {
ctx.fillRect(
(col + margin) * cellSize,
(row + margin) * cellSize,
cellSize,
cellSize
);
}
}
}

var downloadBtn = document.createElement("button");
downloadBtn.className = "social-share-qr-download";
downloadBtn.textContent = "Download QR";

var self = this;
var downloadHandler = function () {
var dataUrl = canvas.toDataURL("image/png");
var a = document.createElement("a");
a.href = dataUrl;
a.download = "share-qrcode.png";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};

downloadBtn.addEventListener("click", downloadHandler);
// Register in central listener list so destroy() cleans it up
this.addEventListener(downloadBtn, "click", downloadHandler);

this._qrDownloadHandler = downloadHandler;
this._qrDownloadBtn = downloadBtn;

qrPanel.appendChild(title);
qrPanel.appendChild(canvas);
qrPanel.appendChild(downloadBtn);

// Insert right after the platforms row
var platformsContainer = this.modal.querySelector(".social-share-platforms");
if (platformsContainer && platformsContainer.parentNode) {
platformsContainer.parentNode.insertBefore(qrPanel, platformsContainer.nextSibling);
} else {
var content = this.modal.querySelector(".social-share-modal-content");
if (content) content.appendChild(qrPanel);
}
};

window.SocialShareButton.prototype.closeModal = function () {
if (this.modal) {
var qrPanel = this.modal.querySelector(".social-share-qr-panel");
if (qrPanel) {
if (this._qrDownloadBtn && this._qrDownloadHandler) {
this._qrDownloadBtn.removeEventListener("click", this._qrDownloadHandler);
// Purge from central registry
this.listeners = this.listeners.filter(
function (l) { return l.handler !== this._qrDownloadHandler; },
this
);
this._qrDownloadBtn = null;
this._qrDownloadHandler = null;
}
qrPanel.remove();
}
}
return originalCloseModal.call(this);
};
} // end applyQRPatch

function loadQRCodeGenerator(callback) {
if (typeof window.qrcode !== "undefined") {
callback();
return;
}

var script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js";
script.onload = callback;
script.onerror = function () {
console.error("Failed to load qrcode-generator from CDN.");
};
document.head.appendChild(script);
}

function init() {
loadQRCodeGenerator(function () {
applyQRPatch();
});
}

// Wait for all deferred scripts to finish evaluating before loading and patching.
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
})();
65 changes: 65 additions & 0 deletions src/social-share-button.css
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,68 @@
display: none !important;
}
}
/* -----------------------------------------------------------------------------
QR CODE PANEL
----------------------------------------------------------------------------- */

.social-share-qr-panel {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.social-share-modal-overlay.light .social-share-qr-panel {
border-top-color: rgba(0, 0, 0, 0.1);
}

.social-share-qr-panel h4 {
margin: 0 0 12px 0;
color: #fff;
font-size: 14px;
font-weight: 500;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}

.social-share-modal-overlay.light .social-share-qr-panel h4 {
color: #333;
}

.social-share-qr-canvas {
background: #fff;
padding: 8px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
margin-bottom: 16px;
}

.social-share-modal-overlay.light .social-share-qr-canvas {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.social-share-qr-download {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
padding: 8px 16px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}

.social-share-qr-download:hover {
background: rgba(255, 255, 255, 0.15);
}

.social-share-modal-overlay.light .social-share-qr-download {
background: #f0f0f0;
border-color: #ddd;
color: #333;
}

.social-share-modal-overlay.light .social-share-qr-download:hover {
background: #e4e4e4;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
6 changes: 6 additions & 0 deletions src/social-share-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class SocialShareButton {
"reddit",
"pinterest",
"discord",
"qrcode",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
],
theme: options.theme || "dark",
buttonText: options.buttonText || "Share",
Expand Down Expand Up @@ -196,6 +197,11 @@ class SocialShareButton {
color: "#5865F2",
icon: '<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 11.756 11.756 0 0 0-.617-1.25.077.077 0 0 0-.079-.037 19.736 19.736 0 0 0-4.885 1.515.069.069 0 0 0-.032.027C.533 9.048-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.419-2.157 2.419zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.419-2.157 2.419z"/>',
},
qrcode: {
name: "QR Code",
color: "#000000",
icon: '<path d="M3 3h8v8H3V3zm2 2v4h4V5H5zm8-2h8v8h-8V3zm2 2v4h4V5h-4zM3 13h8v8H3v-8zm2 2v4h4v-4H5zm13-2h-3v2h3v-2zm-3 2v2h-2v-2h2zm0 2h3v2h-3v-2zm-2 2v2h-2v-2h2zm4 0h3v2h-3v-2zm-2-4h2v2h-2v-2z"/>',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
};

return this.options.platforms
Expand Down
Loading