fix: widget modal — clean embed mode, better close button, backdrop blur
- ?embed=true renders widget without page wrapper (white bg, no padding) - widget-loader.js appends &embed=true to modal URL automatically - Close button is now inside dialog (not overlapping iframe), light style - Backdrop blur effect, Escape key closes modal - FAB: subtle translateY on hover instead of scale Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,55 +5,87 @@
|
|||||||
return t[t.length - 1];
|
return t[t.length - 1];
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var url = s.getAttribute('data-url') || '';
|
var rawUrl = s.getAttribute('data-url') || '';
|
||||||
var mode = s.getAttribute('data-mode') || 'floating'; // 'floating' | 'trigger'
|
var mode = s.getAttribute('data-mode') || 'floating';
|
||||||
var btn = s.getAttribute('data-btn') || 'Забронировать';
|
var btn = s.getAttribute('data-btn') || 'Забронировать';
|
||||||
var color = s.getAttribute('data-color') || '#4F46E5';
|
var color = s.getAttribute('data-color') || '#4F46E5';
|
||||||
var cls = s.getAttribute('data-class') || 'hotelsync-book';
|
var cls = s.getAttribute('data-class') || 'hotelsync-book';
|
||||||
|
|
||||||
|
// Append embed param so the page renders without its own wrapper
|
||||||
|
var url = rawUrl + (rawUrl.indexOf('?') >= 0 ? '&' : '?') + 'embed=true';
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
/* ── Modal ── */
|
/* ── Overlay ── */
|
||||||
var modal = document.createElement('div');
|
var overlay = document.createElement('div');
|
||||||
modal.style.cssText = [
|
overlay.style.cssText = [
|
||||||
'display:none', 'position:fixed', 'inset:0',
|
'display:none', 'position:fixed', 'inset:0',
|
||||||
'background:rgba(0,0,0,.65)', 'z-index:99999',
|
'background:rgba(0,0,0,.55)',
|
||||||
|
'backdrop-filter:blur(4px)',
|
||||||
|
'-webkit-backdrop-filter:blur(4px)',
|
||||||
|
'z-index:99998',
|
||||||
'align-items:center', 'justify-content:center',
|
'align-items:center', 'justify-content:center',
|
||||||
'padding:16px', 'box-sizing:border-box',
|
'padding:20px', 'box-sizing:border-box',
|
||||||
].join(';');
|
].join(';');
|
||||||
|
|
||||||
var inner = document.createElement('div');
|
/* ── Dialog box ── */
|
||||||
inner.style.cssText = [
|
var dialog = document.createElement('div');
|
||||||
'position:relative', 'width:100%', 'max-width:520px',
|
dialog.style.cssText = [
|
||||||
'border-radius:20px', 'overflow:hidden',
|
'position:relative',
|
||||||
'box-shadow:0 24px 80px rgba(0,0,0,.45)',
|
'width:100%', 'max-width:480px',
|
||||||
|
'max-height:calc(100vh - 40px)',
|
||||||
|
'border-radius:24px',
|
||||||
|
'overflow:hidden',
|
||||||
|
'box-shadow:0 32px 80px rgba(0,0,0,.4)',
|
||||||
|
'background:#fff',
|
||||||
|
'display:flex', 'flex-direction:column',
|
||||||
].join(';');
|
].join(';');
|
||||||
|
|
||||||
|
/* ── Close button (outside iframe, top-right of dialog) ── */
|
||||||
var closeBtn = document.createElement('button');
|
var closeBtn = document.createElement('button');
|
||||||
closeBtn.innerHTML = '×';
|
closeBtn.innerHTML = '✕';
|
||||||
|
closeBtn.title = 'Закрыть';
|
||||||
closeBtn.style.cssText = [
|
closeBtn.style.cssText = [
|
||||||
'position:absolute', 'top:10px', 'right:10px', 'z-index:1',
|
'position:absolute', 'top:12px', 'right:12px', 'z-index:10',
|
||||||
'width:32px', 'height:32px', 'border-radius:50%',
|
'width:34px', 'height:34px', 'border-radius:50%',
|
||||||
'background:rgba(0,0,0,.55)', 'color:#fff',
|
'background:rgba(255,255,255,.9)',
|
||||||
'border:none', 'cursor:pointer', 'font-size:20px',
|
'backdrop-filter:blur(4px)',
|
||||||
'font-weight:700', 'line-height:1',
|
'color:#374151',
|
||||||
|
'border:1px solid rgba(0,0,0,.1)',
|
||||||
|
'cursor:pointer',
|
||||||
|
'font-size:14px', 'font-weight:700',
|
||||||
'display:flex', 'align-items:center', 'justify-content:center',
|
'display:flex', 'align-items:center', 'justify-content:center',
|
||||||
|
'box-shadow:0 2px 8px rgba(0,0,0,.15)',
|
||||||
|
'transition:background .15s',
|
||||||
].join(';');
|
].join(';');
|
||||||
closeBtn.onclick = function () { modal.style.display = 'none'; };
|
closeBtn.onmouseenter = function () { closeBtn.style.background = '#fff'; };
|
||||||
|
closeBtn.onmouseleave = function () { closeBtn.style.background = 'rgba(255,255,255,.9)'; };
|
||||||
|
closeBtn.onclick = function () { overlay.style.display = 'none'; };
|
||||||
|
|
||||||
|
/* ── Iframe ── */
|
||||||
var iframe = document.createElement('iframe');
|
var iframe = document.createElement('iframe');
|
||||||
iframe.src = url;
|
iframe.src = url;
|
||||||
iframe.setAttribute('allow', 'payment');
|
iframe.setAttribute('allow', 'payment');
|
||||||
iframe.style.cssText = 'width:100%;height:680px;border:none;display:block;';
|
iframe.style.cssText = [
|
||||||
|
'width:100%', 'height:680px',
|
||||||
|
'border:none', 'display:block',
|
||||||
|
'flex:1',
|
||||||
|
].join(';');
|
||||||
|
|
||||||
inner.appendChild(closeBtn);
|
dialog.appendChild(closeBtn);
|
||||||
inner.appendChild(iframe);
|
dialog.appendChild(iframe);
|
||||||
modal.appendChild(inner);
|
overlay.appendChild(dialog);
|
||||||
modal.addEventListener('click', function (e) {
|
|
||||||
if (e.target === modal) modal.style.display = 'none';
|
overlay.addEventListener('click', function (e) {
|
||||||
|
if (e.target === overlay) overlay.style.display = 'none';
|
||||||
});
|
});
|
||||||
document.body.appendChild(modal);
|
|
||||||
|
|
||||||
window.hsOpenWidget = function () { modal.style.display = 'flex'; };
|
// Close on Escape
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Escape') overlay.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
window.hsOpenWidget = function () { overlay.style.display = 'flex'; };
|
||||||
|
|
||||||
/* ── Floating button ── */
|
/* ── Floating button ── */
|
||||||
if (mode === 'floating') {
|
if (mode === 'floating') {
|
||||||
@@ -62,20 +94,21 @@
|
|||||||
fab.style.cssText = [
|
fab.style.cssText = [
|
||||||
'position:fixed', 'bottom:24px', 'right:24px',
|
'position:fixed', 'bottom:24px', 'right:24px',
|
||||||
'background:' + color, 'color:#fff',
|
'background:' + color, 'color:#fff',
|
||||||
'border:none', 'padding:14px 26px',
|
'border:none', 'padding:15px 28px',
|
||||||
'border-radius:50px', 'font-size:15px',
|
'border-radius:50px',
|
||||||
'font-family:inherit', 'font-weight:700',
|
'font-size:15px', 'font-family:inherit', 'font-weight:700',
|
||||||
|
'letter-spacing:.01em',
|
||||||
'cursor:pointer', 'z-index:99997',
|
'cursor:pointer', 'z-index:99997',
|
||||||
'box-shadow:0 4px 20px rgba(0,0,0,.28)',
|
'box-shadow:0 6px 24px rgba(0,0,0,.25)',
|
||||||
'transition:transform .15s,box-shadow .15s',
|
'transition:transform .15s,box-shadow .15s',
|
||||||
].join(';');
|
].join(';');
|
||||||
fab.onmouseenter = function () {
|
fab.onmouseenter = function () {
|
||||||
fab.style.transform = 'scale(1.06)';
|
fab.style.transform = 'translateY(-2px) scale(1.03)';
|
||||||
fab.style.boxShadow = '0 8px 28px rgba(0,0,0,.38)';
|
fab.style.boxShadow = '0 10px 32px rgba(0,0,0,.32)';
|
||||||
};
|
};
|
||||||
fab.onmouseleave = function () {
|
fab.onmouseleave = function () {
|
||||||
fab.style.transform = 'scale(1)';
|
fab.style.transform = 'none';
|
||||||
fab.style.boxShadow = '0 4px 20px rgba(0,0,0,.28)';
|
fab.style.boxShadow = '0 6px 24px rgba(0,0,0,.25)';
|
||||||
};
|
};
|
||||||
fab.onclick = function () { window.hsOpenWidget(); };
|
fab.onclick = function () { window.hsOpenWidget(); };
|
||||||
document.body.appendChild(fab);
|
document.body.appendChild(fab);
|
||||||
|
|||||||
@@ -75,6 +75,22 @@ export function BookingWidgetStandalonePage() {
|
|||||||
}, [slug])
|
}, [slug])
|
||||||
|
|
||||||
const isFullWidth = searchParams.get('width') === 'full'
|
const isFullWidth = searchParams.get('width') === 'full'
|
||||||
|
const isEmbed = searchParams.get('embed') === 'true'
|
||||||
|
|
||||||
|
if (isEmbed) {
|
||||||
|
return (
|
||||||
|
<div className="w-full bg-white">
|
||||||
|
<WidgetPreview
|
||||||
|
settings={settings}
|
||||||
|
slug={slug}
|
||||||
|
realRooms={realRooms.length > 0 ? realRooms : undefined}
|
||||||
|
realCategories={realCategories.length > 0 ? realCategories : undefined}
|
||||||
|
realRentalObjects={realRentalObjects.length > 0 ? realRentalObjects : undefined}
|
||||||
|
paymentEnabled={paymentEnabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={isFullWidth ? 'min-h-screen bg-slate-50' : 'min-h-screen bg-slate-50 flex items-start justify-center py-6 px-3'}>
|
<div className={isFullWidth ? 'min-h-screen bg-slate-50' : 'min-h-screen bg-slate-50 flex items-start justify-center py-6 px-3'}>
|
||||||
|
|||||||
Reference in New Issue
Block a user