80 lines
3.8 KiB
TypeScript
80 lines
3.8 KiB
TypeScript
/**
|
|
* <map-share-modal> — QR code share modal for rMaps rooms.
|
|
* Dispatches 'modal-close' on dismiss.
|
|
* Set `url` property before appending to DOM.
|
|
*/
|
|
|
|
class MapShareModal extends HTMLElement {
|
|
private _url = "";
|
|
private _room = "";
|
|
|
|
set url(v: string) { this._url = v; }
|
|
set room(v: string) { this._room = v; }
|
|
|
|
connectedCallback() { this.render(); }
|
|
|
|
private esc(s: string): string { const d = document.createElement("div"); d.textContent = s || ""; return d.innerHTML; }
|
|
|
|
private close() {
|
|
this.dispatchEvent(new CustomEvent("modal-close", { bubbles: true, composed: true }));
|
|
this.remove();
|
|
}
|
|
|
|
private async render() {
|
|
this.style.cssText = `position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);`;
|
|
|
|
this.innerHTML = `
|
|
<div style="background:var(--rs-bg-surface);border:1px solid var(--rs-border-strong);border-radius:14px;padding:24px;max-width:360px;width:90%;text-align:center;">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
|
<div style="font-size:16px;font-weight:600;color:var(--rs-text-primary);">Share Room</div>
|
|
<button id="s-close" style="background:none;border:none;color:var(--rs-text-muted);cursor:pointer;font-size:18px;">\u2715</button>
|
|
</div>
|
|
<div id="s-qr" style="margin:16px auto;display:flex;align-items:center;justify-content:center;">
|
|
<div style="font-size:12px;color:var(--rs-text-muted);">Generating QR code...</div>
|
|
</div>
|
|
<div style="background:var(--rs-bg-surface-sunken);border:1px solid var(--rs-border);border-radius:8px;padding:10px;margin:12px 0;font-family:monospace;font-size:11px;color:var(--rs-text-secondary);word-break:break-all;text-align:left;">
|
|
${this.esc(this._url)}
|
|
</div>
|
|
<div style="display:flex;gap:8px;">
|
|
<button id="s-copy" style="flex:1;padding:10px;border-radius:8px;border:1px solid var(--rs-border);background:var(--rs-bg-surface);color:var(--rs-text-secondary);cursor:pointer;font-size:13px;font-weight:500;">\u{1F4CB} Copy Link</button>
|
|
<button id="s-share" style="flex:1;padding:10px;border-radius:8px;border:none;background:#4f46e5;color:#fff;cursor:pointer;font-size:13px;font-weight:600;">\u{1F4E4} Share</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// QR code
|
|
try {
|
|
const QRCode = await import("qrcode");
|
|
const dataUrl = await QRCode.toDataURL(this._url, { width: 200, margin: 2, color: { dark: "#000000", light: "#ffffff" } });
|
|
const qr = this.querySelector("#s-qr");
|
|
if (qr) qr.innerHTML = `<img src="${dataUrl}" alt="QR Code" style="width:200px;height:200px;border-radius:8px;">`;
|
|
} catch {
|
|
const qr = this.querySelector("#s-qr");
|
|
if (qr) qr.innerHTML = `<div style="font-size:12px;color:var(--rs-text-muted);">QR code unavailable</div>`;
|
|
}
|
|
|
|
// Events
|
|
this.querySelector("#s-close")?.addEventListener("click", () => this.close());
|
|
this.addEventListener("click", (e) => { if (e.target === this) this.close(); });
|
|
this.querySelector("#s-copy")?.addEventListener("click", () => {
|
|
navigator.clipboard.writeText(this._url).then(() => {
|
|
const btn = this.querySelector("#s-copy");
|
|
if (btn) { btn.textContent = "\u2713 Copied!"; setTimeout(() => { btn.textContent = "\u{1F4CB} Copy Link"; }, 2000); }
|
|
});
|
|
});
|
|
this.querySelector("#s-share")?.addEventListener("click", () => {
|
|
if (navigator.share) {
|
|
navigator.share({ title: `rMaps: ${this._room}`, url: this._url }).catch(() => {});
|
|
} else {
|
|
navigator.clipboard.writeText(this._url).then(() => {
|
|
const btn = this.querySelector("#s-share");
|
|
if (btn) { btn.textContent = "\u2713 Copied!"; setTimeout(() => { btn.textContent = "\u{1F4E4} Share"; }, 2000); }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
customElements.define("map-share-modal", MapShareModal);
|
|
export { MapShareModal };
|