fix: widget embed — full-width support + one-liner script tags via hosted loader
- Standalone page: ?width=full removes max-w-lg container constraint - iframeSnippet: passes &width=full to URL when 100% width selected - public/widget-loader.js: hosted script, reads data-* attrs, no inline code needed - data-mode="floating" → styled FAB button bottom-right - data-mode="trigger" → listens for .hotelsync-book class clicks - Proper modal with close button and backdrop click - Code tab snippets for floating/trigger are now single <script src> lines Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
105
public/widget-loader.js
Normal file
105
public/widget-loader.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/* HotelSync Widget Loader — https://hotelsync.ru */
|
||||
(function () {
|
||||
var s = document.currentScript || (function () {
|
||||
var t = document.getElementsByTagName('script');
|
||||
return t[t.length - 1];
|
||||
})();
|
||||
|
||||
var url = s.getAttribute('data-url') || '';
|
||||
var mode = s.getAttribute('data-mode') || 'floating'; // 'floating' | 'trigger'
|
||||
var btn = s.getAttribute('data-btn') || 'Забронировать';
|
||||
var color = s.getAttribute('data-color') || '#4F46E5';
|
||||
var cls = s.getAttribute('data-class') || 'hotelsync-book';
|
||||
|
||||
function init() {
|
||||
/* ── Modal ── */
|
||||
var modal = document.createElement('div');
|
||||
modal.style.cssText = [
|
||||
'display:none', 'position:fixed', 'inset:0',
|
||||
'background:rgba(0,0,0,.65)', 'z-index:99999',
|
||||
'align-items:center', 'justify-content:center',
|
||||
'padding:16px', 'box-sizing:border-box',
|
||||
].join(';');
|
||||
|
||||
var inner = document.createElement('div');
|
||||
inner.style.cssText = [
|
||||
'position:relative', 'width:100%', 'max-width:520px',
|
||||
'border-radius:20px', 'overflow:hidden',
|
||||
'box-shadow:0 24px 80px rgba(0,0,0,.45)',
|
||||
].join(';');
|
||||
|
||||
var closeBtn = document.createElement('button');
|
||||
closeBtn.innerHTML = '×';
|
||||
closeBtn.style.cssText = [
|
||||
'position:absolute', 'top:10px', 'right:10px', 'z-index:1',
|
||||
'width:32px', 'height:32px', 'border-radius:50%',
|
||||
'background:rgba(0,0,0,.55)', 'color:#fff',
|
||||
'border:none', 'cursor:pointer', 'font-size:20px',
|
||||
'font-weight:700', 'line-height:1',
|
||||
'display:flex', 'align-items:center', 'justify-content:center',
|
||||
].join(';');
|
||||
closeBtn.onclick = function () { modal.style.display = 'none'; };
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.src = url;
|
||||
iframe.setAttribute('allow', 'payment');
|
||||
iframe.style.cssText = 'width:100%;height:680px;border:none;display:block;';
|
||||
|
||||
inner.appendChild(closeBtn);
|
||||
inner.appendChild(iframe);
|
||||
modal.appendChild(inner);
|
||||
modal.addEventListener('click', function (e) {
|
||||
if (e.target === modal) modal.style.display = 'none';
|
||||
});
|
||||
document.body.appendChild(modal);
|
||||
|
||||
window.hsOpenWidget = function () { modal.style.display = 'flex'; };
|
||||
|
||||
/* ── Floating button ── */
|
||||
if (mode === 'floating') {
|
||||
var fab = document.createElement('button');
|
||||
fab.textContent = btn;
|
||||
fab.style.cssText = [
|
||||
'position:fixed', 'bottom:24px', 'right:24px',
|
||||
'background:' + color, 'color:#fff',
|
||||
'border:none', 'padding:14px 26px',
|
||||
'border-radius:50px', 'font-size:15px',
|
||||
'font-family:inherit', 'font-weight:700',
|
||||
'cursor:pointer', 'z-index:99997',
|
||||
'box-shadow:0 4px 20px rgba(0,0,0,.28)',
|
||||
'transition:transform .15s,box-shadow .15s',
|
||||
].join(';');
|
||||
fab.onmouseenter = function () {
|
||||
fab.style.transform = 'scale(1.06)';
|
||||
fab.style.boxShadow = '0 8px 28px rgba(0,0,0,.38)';
|
||||
};
|
||||
fab.onmouseleave = function () {
|
||||
fab.style.transform = 'scale(1)';
|
||||
fab.style.boxShadow = '0 4px 20px rgba(0,0,0,.28)';
|
||||
};
|
||||
fab.onclick = function () { window.hsOpenWidget(); };
|
||||
document.body.appendChild(fab);
|
||||
}
|
||||
|
||||
/* ── Class trigger ── */
|
||||
if (mode === 'trigger') {
|
||||
document.addEventListener('click', function (e) {
|
||||
var el = e.target;
|
||||
while (el) {
|
||||
if (el.classList && el.classList.contains(cls)) {
|
||||
e.preventDefault();
|
||||
window.hsOpenWidget();
|
||||
return;
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user