Tighten payment sheet UI to match POS single-surface checkout.
Deploy Ladill Events / deploy (push) Successful in 47s

When Paystack Inline is ready: widen desktop panel for channel sidebar, collapse
to a minimal header, hide footer trust line, and re-pin the checkout iframe to
the sheet body so it does not read as a second modal on top of Ladill chrome.
This commit is contained in:
isaacclad
2026-07-21 23:02:59 +00:00
parent b8b6521058
commit daf40f831b
+112 -10
View File
@@ -312,6 +312,19 @@
} catch (e) {}
}
function syncPaystackMountBox() {
try {
var mount = getPaystackMount();
if (!mount || !window.__ladillContainPaystack) return;
var rect = mount.getBoundingClientRect();
var root = document.documentElement;
root.style.setProperty('--ladill-pay-left', Math.max(0, Math.round(rect.left)) + 'px');
root.style.setProperty('--ladill-pay-top', Math.max(0, Math.round(rect.top)) + 'px');
root.style.setProperty('--ladill-pay-width', Math.max(0, Math.round(rect.width)) + 'px');
root.style.setProperty('--ladill-pay-height', Math.max(0, Math.round(rect.height)) + 'px');
} catch (e) {}
}
function setPaystackContainActive(active) {
window.__ladillContainPaystack = !!active;
try {
@@ -321,6 +334,20 @@
installIframeAllowHook();
installAppendHook();
ensurePaystackContained(document);
syncPaystackMountBox();
if (!window.__ladillPayMountResizeBound) {
window.__ladillPayMountResizeBound = true;
window.addEventListener('resize', syncPaystackMountBox, { passive: true });
window.addEventListener('orientationchange', syncPaystackMountBox, { passive: true });
}
} else {
try {
var root = document.documentElement;
root.style.removeProperty('--ladill-pay-left');
root.style.removeProperty('--ladill-pay-top');
root.style.removeProperty('--ladill-pay-width');
root.style.removeProperty('--ladill-pay-height');
} catch (e) {}
}
}
@@ -550,6 +577,13 @@
store.launching = false;
ensurePaystackIframePermissions(document);
ensurePaystackContained(document);
// After Alpine hides the loading overlay, re-measure mount and re-pin iframe.
setTimeout(function () {
ensurePaystackContained(document);
syncPaystackMountBox();
}, 0);
setTimeout(syncPaystackMountBox, 120);
setTimeout(syncPaystackMountBox, 400);
},
onSuccess: function (transaction) {
if (token !== store._launchToken) return;
@@ -647,6 +681,7 @@
overflow: hidden;
background: #fff;
}
/* Paystack draws its own modal chrome inside the iframe — give it the full body. */
[data-ladill-pay-mount] iframe[data-ladill-pay-contained],
[data-ladill-pay-mount] iframe[id^="inline-checkout"],
[data-ladill-pay-mount] iframe[id^="embed-checkout"] {
@@ -662,10 +697,31 @@
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
border-radius: 0 !important;
z-index: 2 !important;
visibility: visible !important;
display: block !important;
background: transparent !important;
background: #fff !important;
color-scheme: light !important;
}
/* If a Paystack frame escapes the mount, pin it over the panel body instead of full viewport. */
html.ladill-pay-inline-active body > iframe[id^="inline-checkout"],
html.ladill-pay-inline-active body > iframe[id^="embed-checkout"] {
position: fixed !important;
/* Fallback rect; JS updates these CSS vars from the mount box. */
left: var(--ladill-pay-left, 0) !important;
top: var(--ladill-pay-top, 0) !important;
width: var(--ladill-pay-width, 100%) !important;
height: var(--ladill-pay-height, 100%) !important;
max-width: none !important;
max-height: none !important;
margin: 0 !important;
border: 0 !important;
border-radius: 0 !important;
z-index: 10000 !important;
visibility: visible !important;
display: block !important;
background: #fff !important;
}
</style>
@endonce
@@ -714,10 +770,18 @@
@click="showSheet = false; window.LadillPayCheckout?.cancel?.()">
</div>
{{-- Always wrap payment: bottom sheet on mobile, centered modal on desktop. --}}
{{--
Single payment surface (same pattern as POS):
- Mobile: bottom sheet
- Desktop: centered modal, wide enough for Paystack channel sidebar
- When Inline is ready: minimal chrome so Paystack fills the body (no double header/footer)
--}}
<div data-ladill-pay-panel
class="relative flex w-full flex-col overflow-hidden rounded-t-2xl bg-white shadow-2xl md:max-w-lg md:rounded-2xl"
style="height: min(90dvh, 720px); max-height: 90dvh; padding-bottom: env(safe-area-inset-bottom, 0px)"
class="relative flex w-full flex-col overflow-hidden rounded-t-2xl bg-white shadow-2xl md:rounded-2xl"
:class="($store.ladillPayCheckout && $store.ladillPayCheckout.inline && !$store.ladillPayCheckout.launching && !$store.ladillPayCheckout.error)
? 'md:max-w-3xl'
: 'md:max-w-lg'"
style="height: min(92dvh, 760px); max-height: 92dvh; padding-bottom: env(safe-area-inset-bottom, 0px)"
x-show="showSheet"
x-transition:enter="transition duration-300 ease-out md:duration-200"
x-transition:enter-start="translate-y-full opacity-0 md:translate-y-0 md:scale-95"
@@ -726,8 +790,16 @@
x-transition:leave-start="translate-y-0 opacity-100 md:scale-100"
x-transition:leave-end="translate-y-full opacity-0 md:translate-y-0 md:scale-95"
@click.stop>
<div class="relative flex shrink-0 flex-col gap-0.5 border-b border-slate-100 px-4 pb-3 pt-5 md:px-5 md:pt-3"
style="padding-top: max(1.25rem, env(safe-area-inset-top, 0px))">
{{-- Full title header only while loading / error / same-origin frame --}}
<div x-show="!$store.ladillPayCheckout
|| $store.ladillPayCheckout.frameable
|| $store.ladillPayCheckout.launching
|| $store.ladillPayCheckout.error
|| !$store.ladillPayCheckout.inline"
class="relative flex shrink-0 flex-col gap-0.5 border-b border-slate-100 px-4 pb-3 pt-5 md:px-5 md:pt-3"
style="padding-top: max(1.25rem, env(safe-area-inset-top, 0px))"
data-ladill-pay-header>
<div class="absolute left-1/2 top-2 h-1 w-10 -translate-x-1/2 rounded-full bg-slate-200 md:hidden" data-ladill-pay-handle aria-hidden="true"></div>
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
@@ -748,10 +820,32 @@
</button>
</div>
</div>
{{-- Body: Paystack mounts here (same area as the spinner). No second full-screen modal. --}}
<div class="relative min-h-0 flex-1 overflow-hidden overscroll-contain"
{{-- Minimal chrome once Paystack owns the body (one close, no second title stack) --}}
<div x-show="$store.ladillPayCheckout
&& $store.ladillPayCheckout.inline
&& !$store.ladillPayCheckout.launching
&& !$store.ladillPayCheckout.error
&& !$store.ladillPayCheckout.frameable"
class="relative z-20 flex shrink-0 items-center justify-between px-3 pb-1 pt-3 md:px-4 md:pt-2"
style="padding-top: max(0.75rem, env(safe-area-inset-top, 0px))"
data-ladill-pay-header-minimal>
<div class="absolute left-1/2 top-2 h-1 w-10 -translate-x-1/2 rounded-full bg-slate-200 md:hidden" data-ladill-pay-handle aria-hidden="true"></div>
<p class="pl-1 text-xs font-medium text-slate-500">{{ $sheetTitle }}</p>
<button type="button"
@click="showSheet = false; window.LadillPayCheckout?.cancel?.()"
class="shrink-0 rounded-full p-1.5 text-slate-400 transition hover:bg-slate-100 hover:text-slate-700"
aria-label="Close">
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/>
</svg>
</button>
</div>
{{-- Body: Paystack mounts here (same area as the spinner). --}}
<div class="relative min-h-0 flex-1 overflow-hidden overscroll-contain bg-white"
data-ladill-pay-body
style="min-height: 28rem">
style="min-height: 22rem">
{{-- Same-origin waiting pages (e.g. MoMo status) --}}
<iframe x-show="$store.ladillPayCheckout && $store.ladillPayCheckout.frameable"
:src="showSheet && $store.ladillPayCheckout && $store.ladillPayCheckout.frameable ? checkoutUrl : ''"
@@ -814,8 +908,16 @@
</button>
</div>
</div>
{{-- Footer only before Paystack owns the body (avoids double trust messaging) --}}
@if (! empty($sheetFooter))
<p class="shrink-0 border-t border-slate-100 bg-slate-50 px-4 py-2 text-center text-[11px] text-slate-500 md:px-5">
<p x-show="!$store.ladillPayCheckout
|| $store.ladillPayCheckout.frameable
|| $store.ladillPayCheckout.launching
|| $store.ladillPayCheckout.error
|| !$store.ladillPayCheckout.inline"
class="shrink-0 border-t border-slate-100 bg-slate-50 px-4 py-2 text-center text-[11px] text-slate-500 md:px-5"
data-ladill-pay-footer>
{{ $sheetFooter }}
</p>
@endif