From 3106856370d0aefb72b89c0c4b6f96794d002fe2 Mon Sep 17 00:00:00 2001
From: Aman kumar vishwakrma <186990161+amankv1234@users.noreply.github.com>
Date: Wed, 5 Aug 2026 02:20:49 +0530
Subject: [PATCH 1/4] feat: implement QR code extension with dynamic CDN
loading
---
README.md | 19 +++-
package.json | 1 +
src/social-share-button-qr.js | 176 ++++++++++++++++++++++++++++++++++
src/social-share-button.css | 65 +++++++++++++
src/social-share-button.js | 6 ++
5 files changed, 265 insertions(+), 2 deletions(-)
create mode 100644 src/social-share-button-qr.js
diff --git a/README.md b/README.md
index 7a7db56e..0bfc4fd7 100644
--- a/README.md
+++ b/README.md
@@ -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
- 🎯 Zero dependencies - pure vanilla JavaScript
- ⚛️ Framework support: React, Preact, Next.js, Qwik, Vue, Angular, or plain HTML
- 🔄 Auto-detects current URL and page title
@@ -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
@@ -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
+
+
+
+
+
+
+```
+
+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
diff --git a/package.json b/package.json
index 972ad17e..3c2a76ce 100644
--- a/package.json
+++ b/package.json
@@ -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"
diff --git a/src/social-share-button-qr.js b/src/social-share-button-qr.js
new file mode 100644
index 00000000..06b7710d
--- /dev/null
+++ b/src/social-share-button-qr.js
@@ -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");
+ 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;
+ }
+
+ // 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;
+
+ // --- 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();
+ }
+})();
diff --git a/src/social-share-button.css b/src/social-share-button.css
index 0609839b..e01cc916 100644
--- a/src/social-share-button.css
+++ b/src/social-share-button.css
@@ -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;
+}
diff --git a/src/social-share-button.js b/src/social-share-button.js
index 2dd6dd57..54dcf878 100644
--- a/src/social-share-button.js
+++ b/src/social-share-button.js
@@ -31,6 +31,7 @@ class SocialShareButton {
"reddit",
"pinterest",
"discord",
+ "qrcode",
],
theme: options.theme || "dark",
buttonText: options.buttonText || "Share",
@@ -196,6 +197,11 @@ class SocialShareButton {
color: "#5865F2",
icon: '',
},
+ qrcode: {
+ name: "QR Code",
+ color: "#000000",
+ icon: '',
+ },
};
return this.options.platforms
From 1eb67eb8d41dc2487b594b4148e9e8f8c81a89f8 Mon Sep 17 00:00:00 2001
From: Aman kumar vishwakrma <186990161+amankv1234@users.noreply.github.com>
Date: Wed, 5 Aug 2026 10:33:02 +0530
Subject: [PATCH 2/4] fix: address CodeRabbit review comments on QR extension
---
README.md | 36 +++++++--
src/social-share-button-qr.js | 137 +++++++++++++++++++++++++---------
src/social-share-button.css | 2 +-
src/social-share-button.js | 1 -
4 files changed, 133 insertions(+), 43 deletions(-)
diff --git a/README.md b/README.md
index 0bfc4fd7..739771d7 100644
--- a/README.md
+++ b/README.md
@@ -70,8 +70,9 @@ Lightweight social sharing component for web applications. Zero dependencies, fr
## Features
-- 🌐 Multiple platforms: WhatsApp, Facebook, X, LinkedIn, Telegram, Reddit, Email, Pinterest, Discord, QR Code
-- 🎯 Zero dependencies - pure vanilla JavaScript
+- 🌐 Multiple platforms: WhatsApp, Facebook, X, LinkedIn, Telegram, Reddit, Email, Pinterest, Discord
+- 📷 QR Code sharing (optional — requires the `social-share-button-qr.js` extension, which loads `qrcode-generator` at runtime)
+- 🎯 Zero dependencies for the core library — pure vanilla JavaScript (QR extension loads `qrcode-generator` from jsDelivr CDN at runtime)
- ⚛️ Framework support: React, Preact, Next.js, Qwik, Vue, Angular, or plain HTML
- 🔄 Auto-detects current URL and page title
- 📱 Fully responsive and mobile-ready
@@ -527,7 +528,9 @@ new SocialShareButton({
| `onCopy` | function | `null` | Callback when user copies link: `(url) => {}` |
**Available Platforms:**
-`whatsapp`, `facebook`, `twitter`, `linkedin`, `telegram`, `reddit`, `email`, `pinterest`, `discord`, `qrcode`
+`whatsapp`, `facebook`, `twitter`, `linkedin`, `telegram`, `reddit`, `email`, `pinterest`, `discord`
+
+> **QR Code** (`qrcode`) is available as an optional platform. It requires the separate `social-share-button-qr.js` extension — see [Using the QR Code Extension](#using-the-qr-code-extension).
### Customize Share Message/Post Text
@@ -661,18 +664,39 @@ new SocialShareButton({
### 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:
+The QR code feature is an **optional extension**. The core library has zero runtime dependencies; this extension loads `qrcode-generator` from jsDelivr at runtime.
+
+#### Via CDN (HTML)
```html
-
+
+
+```
+
+#### Via npm / bundler
+
+```javascript
+import "@aossie-org/social-share-button/src/social-share-button-qr.js";
+```
+
+The extension will dynamically inject `qrcode-generator` from jsDelivr the first time the QR platform is initialised.
+
+#### Self-hosting under a strict CSP
+
+If your Content Security Policy (CSP) blocks `cdn.jsdelivr.net`, download `qrcode-generator` locally and load it **before** the extension:
+
+```html
+
+
+
```
-When you pass `qrcode` in the `platforms` array, a QR code generation panel will be rendered inline inside the share modal when clicked.
+When you pass `qrcode` in the `platforms` array, a QR code panel will be rendered inline inside the share modal with a canvas preview and a **Download QR** button.
### Using npm Package
diff --git a/src/social-share-button-qr.js b/src/social-share-button-qr.js
index 06b7710d..0bdf8dc8 100644
--- a/src/social-share-button-qr.js
+++ b/src/social-share-button-qr.js
@@ -4,9 +4,65 @@
*/
(function () {
+ /**
+ * Shared bootstrap error helper for the QR extension.
+ * Keeps all console output in one place so it can be easily
+ * swapped for a project-level logger without touching call sites.
+ *
+ * @param {string} message
+ */
+ function _qrWarn(message) {
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
+ console.warn("[SocialShareButton QR] " + message);
+ }
+ }
+
+ /**
+ * Cached Promise for the qrcode-generator CDN load.
+ * Guarantees only one