@benqoder/beam 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -90
- package/dist/client.d.ts +12 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +275 -123
- package/dist/collect.d.ts +1 -47
- package/dist/collect.d.ts.map +1 -1
- package/dist/collect.js +0 -64
- package/dist/createBeam.d.ts +5 -19
- package/dist/createBeam.d.ts.map +1 -1
- package/dist/createBeam.js +74 -54
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/types.d.ts +48 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/vite.d.ts +0 -12
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +4 -10
- package/package.json +1 -1
- package/src/beam.css +2 -0
- package/dist/DrawerFrame.d.ts +0 -16
- package/dist/DrawerFrame.d.ts.map +0 -1
- package/dist/DrawerFrame.js +0 -12
- package/dist/ModalFrame.d.ts +0 -12
- package/dist/ModalFrame.d.ts.map +0 -1
- package/dist/ModalFrame.js +0 -8
package/dist/client.js
CHANGED
|
@@ -105,16 +105,6 @@ const api = {
|
|
|
105
105
|
// @ts-ignore - capnweb stub methods are dynamically typed
|
|
106
106
|
return session.call(action, data);
|
|
107
107
|
},
|
|
108
|
-
async modal(modalId, params = {}) {
|
|
109
|
-
const session = await ensureConnected();
|
|
110
|
-
// @ts-ignore - capnweb stub methods are dynamically typed
|
|
111
|
-
return session.modal(modalId, params);
|
|
112
|
-
},
|
|
113
|
-
async drawer(drawerId, params = {}) {
|
|
114
|
-
const session = await ensureConnected();
|
|
115
|
-
// @ts-ignore - capnweb stub methods are dynamically typed
|
|
116
|
-
return session.drawer(drawerId, params);
|
|
117
|
-
},
|
|
118
108
|
// Direct access to RPC session for advanced usage (promise pipelining, etc.)
|
|
119
109
|
async getSession() {
|
|
120
110
|
return ensureConnected();
|
|
@@ -456,6 +446,72 @@ function swap(target, html, mode, trigger) {
|
|
|
456
446
|
// Process hungry elements - auto-update elements that match IDs in response
|
|
457
447
|
processHungryElements(html);
|
|
458
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Handle HTML response - supports both single string and array of HTML strings.
|
|
451
|
+
* Target resolution order (server wins, frontend is fallback):
|
|
452
|
+
* 1. Server target from comma-separated list (by index)
|
|
453
|
+
* - Use "!selector" to exclude that selector (blocks frontend fallback too)
|
|
454
|
+
* 2. Frontend target (beam-target) as fallback for remaining items
|
|
455
|
+
* 3. ID from HTML fragment's root element
|
|
456
|
+
* 4. Skip if none found
|
|
457
|
+
*/
|
|
458
|
+
function handleHtmlResponse(response, frontendTarget, frontendSwap, trigger) {
|
|
459
|
+
if (!response.html)
|
|
460
|
+
return;
|
|
461
|
+
const htmlArray = Array.isArray(response.html) ? response.html : [response.html];
|
|
462
|
+
// Server targets take priority, collect exclusions
|
|
463
|
+
const serverTargets = response.target ? response.target.split(',').map(s => s.trim()) : [];
|
|
464
|
+
const excluded = new Set(serverTargets.filter(t => t.startsWith('!')).map(t => t.slice(1)));
|
|
465
|
+
const swapMode = response.swap || frontendSwap;
|
|
466
|
+
htmlArray.forEach((htmlItem, index) => {
|
|
467
|
+
const serverTarget = serverTargets[index];
|
|
468
|
+
// Skip if this is an exclusion marker
|
|
469
|
+
if (serverTarget?.startsWith('!')) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
// Priority 1: Server target (by index)
|
|
473
|
+
let explicitTarget = serverTarget;
|
|
474
|
+
// Priority 2: Frontend target as fallback (only if no server target and not excluded)
|
|
475
|
+
if (!explicitTarget && frontendTarget && !excluded.has(frontendTarget)) {
|
|
476
|
+
explicitTarget = frontendTarget;
|
|
477
|
+
}
|
|
478
|
+
if (explicitTarget) {
|
|
479
|
+
// Explicit target provided - use normal swap
|
|
480
|
+
const target = $(explicitTarget);
|
|
481
|
+
if (target) {
|
|
482
|
+
swap(target, htmlItem, swapMode, trigger);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
console.warn(`[beam] Target "${explicitTarget}" not found on page, skipping`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
// Priority 3: id, beam-id, or beam-item-id on root element
|
|
490
|
+
const temp = document.createElement('div');
|
|
491
|
+
temp.innerHTML = htmlItem.trim();
|
|
492
|
+
const rootEl = temp.firstElementChild;
|
|
493
|
+
// Check id first, then beam-id, then beam-item-id
|
|
494
|
+
const id = rootEl?.id;
|
|
495
|
+
const beamId = rootEl?.getAttribute('beam-id');
|
|
496
|
+
const beamItemId = rootEl?.getAttribute('beam-item-id');
|
|
497
|
+
const selector = id ? `#${id}`
|
|
498
|
+
: beamId ? `[beam-id="${beamId}"]`
|
|
499
|
+
: beamItemId ? `[beam-item-id="${beamItemId}"]`
|
|
500
|
+
: null;
|
|
501
|
+
if (selector && !excluded.has(selector)) {
|
|
502
|
+
const target = $(selector);
|
|
503
|
+
if (target) {
|
|
504
|
+
// Replace entire element using outerHTML (preserves styles/classes)
|
|
505
|
+
target.outerHTML = htmlItem.trim();
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
console.warn(`[beam] Target "${selector}" (from HTML) not found on page, skipping`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// If no id/beam-id/beam-item-id found or excluded, skip silently
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}
|
|
459
515
|
function parseOobSwaps(html) {
|
|
460
516
|
const temp = document.createElement('div');
|
|
461
517
|
temp.innerHTML = html;
|
|
@@ -484,16 +540,20 @@ async function rpc(action, data, el) {
|
|
|
484
540
|
location.href = response.redirect;
|
|
485
541
|
return;
|
|
486
542
|
}
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
543
|
+
// Handle modal (if present)
|
|
544
|
+
if (response.modal) {
|
|
545
|
+
const modalData = typeof response.modal === 'string'
|
|
546
|
+
? { html: response.modal } : response.modal;
|
|
547
|
+
openModal(modalData.html, modalData.size || 'medium', modalData.spacing);
|
|
548
|
+
}
|
|
549
|
+
// Handle drawer (if present)
|
|
550
|
+
if (response.drawer) {
|
|
551
|
+
const drawerData = typeof response.drawer === 'string'
|
|
552
|
+
? { html: response.drawer } : response.drawer;
|
|
553
|
+
openDrawer(drawerData.html, drawerData.position || 'right', drawerData.size || 'medium', drawerData.spacing);
|
|
496
554
|
}
|
|
555
|
+
// Handle HTML (if present) - supports single string or array
|
|
556
|
+
handleHtmlResponse(response, frontendTarget, frontendSwap, el);
|
|
497
557
|
// Execute script (if present)
|
|
498
558
|
if (response.script) {
|
|
499
559
|
executeScript(response.script);
|
|
@@ -583,8 +643,9 @@ document.addEventListener('click', async (e) => {
|
|
|
583
643
|
closeDrawer();
|
|
584
644
|
}
|
|
585
645
|
});
|
|
586
|
-
// ============ MODALS ============
|
|
587
|
-
|
|
646
|
+
// ============ MODALS & DRAWERS ============
|
|
647
|
+
// beam-modal trigger - calls action and opens result in modal
|
|
648
|
+
document.addEventListener('click', async (e) => {
|
|
588
649
|
const target = e.target;
|
|
589
650
|
if (!target?.closest)
|
|
590
651
|
return;
|
|
@@ -594,30 +655,48 @@ document.addEventListener('click', (e) => {
|
|
|
594
655
|
// Check confirmation
|
|
595
656
|
if (!checkConfirm(trigger))
|
|
596
657
|
return;
|
|
597
|
-
const
|
|
658
|
+
const action = trigger.getAttribute('beam-modal');
|
|
659
|
+
if (!action)
|
|
660
|
+
return;
|
|
598
661
|
const size = trigger.getAttribute('beam-size') || 'medium';
|
|
599
662
|
const params = getParams(trigger);
|
|
600
|
-
|
|
601
|
-
|
|
663
|
+
const placeholder = trigger.getAttribute('beam-placeholder');
|
|
664
|
+
// Show placeholder modal while loading
|
|
665
|
+
if (placeholder) {
|
|
666
|
+
openModal(placeholder, size);
|
|
602
667
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
668
|
+
setLoading(trigger, true, action, params);
|
|
669
|
+
try {
|
|
670
|
+
const response = await api.call(action, params);
|
|
671
|
+
// Handle the response - if it returns modal, use that, otherwise use html
|
|
672
|
+
if (response.modal) {
|
|
673
|
+
const modalData = typeof response.modal === 'string'
|
|
674
|
+
? { html: response.modal } : response.modal;
|
|
675
|
+
openModal(modalData.html, modalData.size || size, modalData.spacing);
|
|
676
|
+
}
|
|
677
|
+
else if (response.html) {
|
|
678
|
+
// For modals, use first item if array, otherwise use as-is
|
|
679
|
+
const htmlStr = Array.isArray(response.html) ? response.html[0] : response.html;
|
|
680
|
+
if (htmlStr)
|
|
681
|
+
openModal(htmlStr, size);
|
|
682
|
+
}
|
|
683
|
+
// Execute script if present
|
|
684
|
+
if (response.script) {
|
|
685
|
+
executeScript(response.script);
|
|
686
|
+
}
|
|
613
687
|
}
|
|
614
|
-
|
|
688
|
+
catch (err) {
|
|
615
689
|
closeModal();
|
|
690
|
+
showToast('Failed to open modal.', 'error');
|
|
691
|
+
console.error('Modal error:', err);
|
|
692
|
+
}
|
|
693
|
+
finally {
|
|
694
|
+
setLoading(trigger, false, action, params);
|
|
616
695
|
}
|
|
617
696
|
}
|
|
618
697
|
});
|
|
619
|
-
//
|
|
620
|
-
document.addEventListener('click', (e) => {
|
|
698
|
+
// beam-drawer trigger - calls action and opens result in drawer
|
|
699
|
+
document.addEventListener('click', async (e) => {
|
|
621
700
|
const target = e.target;
|
|
622
701
|
if (!target?.closest)
|
|
623
702
|
return;
|
|
@@ -627,18 +706,69 @@ document.addEventListener('click', (e) => {
|
|
|
627
706
|
// Check confirmation
|
|
628
707
|
if (!checkConfirm(trigger))
|
|
629
708
|
return;
|
|
630
|
-
const
|
|
709
|
+
const action = trigger.getAttribute('beam-drawer');
|
|
710
|
+
if (!action)
|
|
711
|
+
return;
|
|
631
712
|
const position = trigger.getAttribute('beam-position') || 'right';
|
|
632
713
|
const size = trigger.getAttribute('beam-size') || 'medium';
|
|
633
714
|
const params = getParams(trigger);
|
|
634
|
-
|
|
635
|
-
|
|
715
|
+
const placeholder = trigger.getAttribute('beam-placeholder');
|
|
716
|
+
// Show placeholder drawer while loading
|
|
717
|
+
if (placeholder) {
|
|
718
|
+
openDrawer(placeholder, position, size);
|
|
719
|
+
}
|
|
720
|
+
setLoading(trigger, true, action, params);
|
|
721
|
+
try {
|
|
722
|
+
const response = await api.call(action, params);
|
|
723
|
+
// Handle the response - if it returns drawer, use that, otherwise use html
|
|
724
|
+
if (response.drawer) {
|
|
725
|
+
const drawerData = typeof response.drawer === 'string'
|
|
726
|
+
? { html: response.drawer } : response.drawer;
|
|
727
|
+
openDrawer(drawerData.html, drawerData.position || position, drawerData.size || size, drawerData.spacing);
|
|
728
|
+
}
|
|
729
|
+
else if (response.html) {
|
|
730
|
+
// For drawers, use first item if array, otherwise use as-is
|
|
731
|
+
const htmlStr = Array.isArray(response.html) ? response.html[0] : response.html;
|
|
732
|
+
if (htmlStr)
|
|
733
|
+
openDrawer(htmlStr, position, size);
|
|
734
|
+
}
|
|
735
|
+
// Execute script if present
|
|
736
|
+
if (response.script) {
|
|
737
|
+
executeScript(response.script);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
catch (err) {
|
|
741
|
+
closeDrawer();
|
|
742
|
+
showToast('Failed to open drawer.', 'error');
|
|
743
|
+
console.error('Drawer error:', err);
|
|
744
|
+
}
|
|
745
|
+
finally {
|
|
746
|
+
setLoading(trigger, false, action, params);
|
|
636
747
|
}
|
|
637
748
|
}
|
|
749
|
+
});
|
|
750
|
+
// Close handlers
|
|
751
|
+
document.addEventListener('click', (e) => {
|
|
752
|
+
const target = e.target;
|
|
753
|
+
if (!target?.closest)
|
|
754
|
+
return;
|
|
638
755
|
// Close on backdrop click
|
|
756
|
+
if (target.matches?.('#modal-backdrop')) {
|
|
757
|
+
closeModal();
|
|
758
|
+
}
|
|
639
759
|
if (target.matches?.('#drawer-backdrop')) {
|
|
640
760
|
closeDrawer();
|
|
641
761
|
}
|
|
762
|
+
// Close button (handles both modal and drawer)
|
|
763
|
+
const closeBtn = target.closest('[beam-close]');
|
|
764
|
+
if (closeBtn && !closeBtn.hasAttribute('beam-action')) {
|
|
765
|
+
if (activeDrawer) {
|
|
766
|
+
closeDrawer();
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
closeModal();
|
|
770
|
+
}
|
|
771
|
+
}
|
|
642
772
|
});
|
|
643
773
|
document.addEventListener('keydown', (e) => {
|
|
644
774
|
if (e.key === 'Escape') {
|
|
@@ -650,33 +780,30 @@ document.addEventListener('keydown', (e) => {
|
|
|
650
780
|
}
|
|
651
781
|
}
|
|
652
782
|
});
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if (!backdrop) {
|
|
658
|
-
backdrop = document.createElement('div');
|
|
659
|
-
backdrop.id = 'modal-backdrop';
|
|
660
|
-
document.body.appendChild(backdrop);
|
|
661
|
-
}
|
|
662
|
-
const { size } = options;
|
|
663
|
-
backdrop.innerHTML = `
|
|
664
|
-
<div id="modal-content" role="dialog" aria-modal="true" data-size="${size}">
|
|
665
|
-
${html}
|
|
666
|
-
</div>
|
|
667
|
-
`;
|
|
668
|
-
backdrop.offsetHeight;
|
|
669
|
-
backdrop.classList.add('open');
|
|
670
|
-
document.body.classList.add('modal-open');
|
|
671
|
-
activeModal = $('#modal-content');
|
|
672
|
-
const autoFocus = activeModal?.querySelector('[autofocus]');
|
|
673
|
-
const firstInput = activeModal?.querySelector('input, button, textarea, select');
|
|
674
|
-
(autoFocus || firstInput)?.focus();
|
|
675
|
-
}
|
|
676
|
-
catch (err) {
|
|
677
|
-
showToast('Failed to open modal.', 'error');
|
|
678
|
-
console.error('Modal error:', err);
|
|
783
|
+
function openModal(html, size = 'medium', spacing) {
|
|
784
|
+
// Close any existing drawer first (modal takes priority)
|
|
785
|
+
if (activeDrawer) {
|
|
786
|
+
closeDrawer();
|
|
679
787
|
}
|
|
788
|
+
let backdrop = $('#modal-backdrop');
|
|
789
|
+
if (!backdrop) {
|
|
790
|
+
backdrop = document.createElement('div');
|
|
791
|
+
backdrop.id = 'modal-backdrop';
|
|
792
|
+
document.body.appendChild(backdrop);
|
|
793
|
+
}
|
|
794
|
+
const style = spacing !== undefined ? `padding: ${spacing}px;` : '';
|
|
795
|
+
backdrop.innerHTML = `
|
|
796
|
+
<div id="modal-content" role="dialog" aria-modal="true" data-size="${size}" style="${style}">
|
|
797
|
+
${html}
|
|
798
|
+
</div>
|
|
799
|
+
`;
|
|
800
|
+
backdrop.offsetHeight;
|
|
801
|
+
backdrop.classList.add('open');
|
|
802
|
+
document.body.classList.add('modal-open');
|
|
803
|
+
activeModal = $('#modal-content');
|
|
804
|
+
const autoFocus = activeModal?.querySelector('[autofocus]');
|
|
805
|
+
const firstInput = activeModal?.querySelector('input, button, textarea, select');
|
|
806
|
+
(autoFocus || firstInput)?.focus();
|
|
680
807
|
}
|
|
681
808
|
function closeModal() {
|
|
682
809
|
const backdrop = $('#modal-backdrop');
|
|
@@ -689,34 +816,31 @@ function closeModal() {
|
|
|
689
816
|
document.body.classList.remove('modal-open');
|
|
690
817
|
activeModal = null;
|
|
691
818
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
backdrop = document.createElement('div');
|
|
698
|
-
backdrop.id = 'drawer-backdrop';
|
|
699
|
-
document.body.appendChild(backdrop);
|
|
700
|
-
}
|
|
701
|
-
// Set position and size as data attributes for CSS styling
|
|
702
|
-
const { position, size } = options;
|
|
703
|
-
backdrop.innerHTML = `
|
|
704
|
-
<div id="drawer-content" role="dialog" aria-modal="true" data-position="${position}" data-size="${size}">
|
|
705
|
-
${html}
|
|
706
|
-
</div>
|
|
707
|
-
`;
|
|
708
|
-
backdrop.offsetHeight; // Force reflow
|
|
709
|
-
backdrop.classList.add('open');
|
|
710
|
-
document.body.classList.add('drawer-open');
|
|
711
|
-
activeDrawer = $('#drawer-content');
|
|
712
|
-
const autoFocus = activeDrawer?.querySelector('[autofocus]');
|
|
713
|
-
const firstInput = activeDrawer?.querySelector('input, button, textarea, select');
|
|
714
|
-
(autoFocus || firstInput)?.focus();
|
|
715
|
-
}
|
|
716
|
-
catch (err) {
|
|
717
|
-
showToast('Failed to open drawer.', 'error');
|
|
718
|
-
console.error('Drawer error:', err);
|
|
819
|
+
// ============ DRAWERS ============
|
|
820
|
+
function openDrawer(html, position = 'right', size = 'medium', spacing) {
|
|
821
|
+
// Close any existing modal first (drawer takes priority)
|
|
822
|
+
if (activeModal) {
|
|
823
|
+
closeModal();
|
|
719
824
|
}
|
|
825
|
+
let backdrop = $('#drawer-backdrop');
|
|
826
|
+
if (!backdrop) {
|
|
827
|
+
backdrop = document.createElement('div');
|
|
828
|
+
backdrop.id = 'drawer-backdrop';
|
|
829
|
+
document.body.appendChild(backdrop);
|
|
830
|
+
}
|
|
831
|
+
const style = spacing !== undefined ? `padding: ${spacing}px;` : '';
|
|
832
|
+
backdrop.innerHTML = `
|
|
833
|
+
<div id="drawer-content" role="dialog" aria-modal="true" data-position="${position}" data-size="${size}" style="${style}">
|
|
834
|
+
${html}
|
|
835
|
+
</div>
|
|
836
|
+
`;
|
|
837
|
+
backdrop.offsetHeight; // Force reflow
|
|
838
|
+
backdrop.classList.add('open');
|
|
839
|
+
document.body.classList.add('drawer-open');
|
|
840
|
+
activeDrawer = $('#drawer-content');
|
|
841
|
+
const autoFocus = activeDrawer?.querySelector('[autofocus]');
|
|
842
|
+
const firstInput = activeDrawer?.querySelector('input, button, textarea, select');
|
|
843
|
+
(autoFocus || firstInput)?.focus();
|
|
720
844
|
}
|
|
721
845
|
function closeDrawer() {
|
|
722
846
|
const backdrop = $('#drawer-backdrop');
|
|
@@ -1125,9 +1249,14 @@ const infiniteObserver = new IntersectionObserver(async (entries) => {
|
|
|
1125
1249
|
setLoading(sentinel, true, action, params);
|
|
1126
1250
|
try {
|
|
1127
1251
|
const response = await api.call(action, params);
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1252
|
+
if (response.html) {
|
|
1253
|
+
// For infinite scroll, always use the specified target (not auto-detect from HTML)
|
|
1254
|
+
const target = $(targetSelector);
|
|
1255
|
+
if (target) {
|
|
1256
|
+
// Handle single html string for infinite scroll (array not typically used here)
|
|
1257
|
+
const htmlStr = Array.isArray(response.html) ? response.html.join('') : response.html;
|
|
1258
|
+
swap(target, htmlStr, swapMode, sentinel);
|
|
1259
|
+
}
|
|
1131
1260
|
// Save scroll state after content is loaded
|
|
1132
1261
|
requestAnimationFrame(() => {
|
|
1133
1262
|
saveScrollState(targetSelector, action);
|
|
@@ -1186,9 +1315,14 @@ document.addEventListener('click', async (e) => {
|
|
|
1186
1315
|
setLoading(trigger, true, action, params);
|
|
1187
1316
|
try {
|
|
1188
1317
|
const response = await api.call(action, params);
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1318
|
+
if (response.html) {
|
|
1319
|
+
// For load more, always use the specified target (not auto-detect from HTML)
|
|
1320
|
+
const targetEl = $(targetSelector);
|
|
1321
|
+
if (targetEl) {
|
|
1322
|
+
// Handle single html string for load more (array not typically used here)
|
|
1323
|
+
const htmlStr = Array.isArray(response.html) ? response.html.join('') : response.html;
|
|
1324
|
+
swap(targetEl, htmlStr, swapMode, trigger);
|
|
1325
|
+
}
|
|
1192
1326
|
// Save scroll state after content is loaded
|
|
1193
1327
|
requestAnimationFrame(() => {
|
|
1194
1328
|
saveScrollState(targetSelector, action);
|
|
@@ -1372,13 +1506,8 @@ document.addEventListener('click', async (e) => {
|
|
|
1372
1506
|
location.href = response.redirect;
|
|
1373
1507
|
return;
|
|
1374
1508
|
}
|
|
1375
|
-
// Handle HTML (if present)
|
|
1376
|
-
|
|
1377
|
-
const target = $(targetSelector);
|
|
1378
|
-
if (target) {
|
|
1379
|
-
swap(target, response.html, swapMode, link);
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1509
|
+
// Handle HTML (if present) - supports single string or array
|
|
1510
|
+
handleHtmlResponse(response, targetSelector, swapMode, link);
|
|
1382
1511
|
// Execute script (if present)
|
|
1383
1512
|
if (response.script) {
|
|
1384
1513
|
executeScript(response.script);
|
|
@@ -1428,13 +1557,20 @@ document.addEventListener('submit', async (e) => {
|
|
|
1428
1557
|
location.href = response.redirect;
|
|
1429
1558
|
return;
|
|
1430
1559
|
}
|
|
1431
|
-
// Handle
|
|
1432
|
-
if (response.
|
|
1433
|
-
const
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
}
|
|
1560
|
+
// Handle modal (if present)
|
|
1561
|
+
if (response.modal) {
|
|
1562
|
+
const modalData = typeof response.modal === 'string'
|
|
1563
|
+
? { html: response.modal } : response.modal;
|
|
1564
|
+
openModal(modalData.html, modalData.size || 'medium', modalData.spacing);
|
|
1437
1565
|
}
|
|
1566
|
+
// Handle drawer (if present)
|
|
1567
|
+
if (response.drawer) {
|
|
1568
|
+
const drawerData = typeof response.drawer === 'string'
|
|
1569
|
+
? { html: response.drawer } : response.drawer;
|
|
1570
|
+
openDrawer(drawerData.html, drawerData.position || 'right', drawerData.size || 'medium', drawerData.spacing);
|
|
1571
|
+
}
|
|
1572
|
+
// Handle HTML (if present) - supports single string or array
|
|
1573
|
+
handleHtmlResponse(response, targetSelector, swapMode);
|
|
1438
1574
|
// Execute script (if present)
|
|
1439
1575
|
if (response.script) {
|
|
1440
1576
|
executeScript(response.script);
|
|
@@ -1483,7 +1619,10 @@ function setupValidation(el) {
|
|
|
1483
1619
|
if (response.html) {
|
|
1484
1620
|
const target = $(targetSelector);
|
|
1485
1621
|
if (target) {
|
|
1486
|
-
|
|
1622
|
+
// For validation, use first item if array, otherwise use as-is
|
|
1623
|
+
const htmlStr = Array.isArray(response.html) ? response.html[0] : response.html;
|
|
1624
|
+
if (htmlStr)
|
|
1625
|
+
morph(target, htmlStr);
|
|
1487
1626
|
}
|
|
1488
1627
|
}
|
|
1489
1628
|
// Execute script (if present)
|
|
@@ -1533,7 +1672,10 @@ const deferObserver = new IntersectionObserver(async (entries) => {
|
|
|
1533
1672
|
if (response.html) {
|
|
1534
1673
|
const target = targetSelector ? $(targetSelector) : el;
|
|
1535
1674
|
if (target) {
|
|
1536
|
-
|
|
1675
|
+
// For deferred loading, use first item if array, otherwise use as-is
|
|
1676
|
+
const htmlStr = Array.isArray(response.html) ? response.html[0] : response.html;
|
|
1677
|
+
if (htmlStr)
|
|
1678
|
+
swap(target, htmlStr, swapMode);
|
|
1537
1679
|
}
|
|
1538
1680
|
}
|
|
1539
1681
|
// Execute script (if present)
|
|
@@ -1589,7 +1731,10 @@ function startPolling(el) {
|
|
|
1589
1731
|
if (response.html) {
|
|
1590
1732
|
const target = targetSelector ? $(targetSelector) : el;
|
|
1591
1733
|
if (target) {
|
|
1592
|
-
|
|
1734
|
+
// For polling, use first item if array, otherwise use as-is
|
|
1735
|
+
const htmlStr = Array.isArray(response.html) ? response.html[0] : response.html;
|
|
1736
|
+
if (htmlStr)
|
|
1737
|
+
swap(target, htmlStr, swapMode);
|
|
1593
1738
|
}
|
|
1594
1739
|
}
|
|
1595
1740
|
// Execute script (if present)
|
|
@@ -1845,20 +1990,27 @@ window.beam = new Proxy(beamUtils, {
|
|
|
1845
1990
|
location.href = response.redirect;
|
|
1846
1991
|
return response;
|
|
1847
1992
|
}
|
|
1993
|
+
// Handle modal (if present)
|
|
1994
|
+
if (response.modal) {
|
|
1995
|
+
const modalData = typeof response.modal === 'string'
|
|
1996
|
+
? { html: response.modal } : response.modal;
|
|
1997
|
+
openModal(modalData.html, modalData.size || 'medium', modalData.spacing);
|
|
1998
|
+
}
|
|
1999
|
+
// Handle drawer (if present)
|
|
2000
|
+
if (response.drawer) {
|
|
2001
|
+
const drawerData = typeof response.drawer === 'string'
|
|
2002
|
+
? { html: response.drawer } : response.drawer;
|
|
2003
|
+
openDrawer(drawerData.html, drawerData.position || 'right', drawerData.size || 'medium', drawerData.spacing);
|
|
2004
|
+
}
|
|
1848
2005
|
// Normalize options: string is shorthand for { target: string }
|
|
1849
2006
|
const opts = typeof options === 'string'
|
|
1850
2007
|
? { target: options }
|
|
1851
2008
|
: (options || {});
|
|
1852
2009
|
// Server target/swap override frontend options
|
|
1853
|
-
const targetSelector = response.target || opts.target;
|
|
2010
|
+
const targetSelector = response.target || opts.target || null;
|
|
1854
2011
|
const swapMode = response.swap || opts.swap || 'morph';
|
|
1855
|
-
// Handle HTML swap
|
|
1856
|
-
|
|
1857
|
-
const targetEl = document.querySelector(targetSelector);
|
|
1858
|
-
if (targetEl) {
|
|
1859
|
-
swap(targetEl, response.html, swapMode);
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
2012
|
+
// Handle HTML swap - supports single string or array
|
|
2013
|
+
handleHtmlResponse(response, targetSelector, swapMode);
|
|
1862
2014
|
// Execute script if present
|
|
1863
2015
|
if (response.script) {
|
|
1864
2016
|
executeScript(response.script);
|
package/dist/collect.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActionHandler
|
|
1
|
+
import type { ActionHandler } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Type for glob import results from import.meta.glob
|
|
4
4
|
*/
|
|
@@ -18,51 +18,5 @@ type GlobImport = Record<string, Record<string, unknown>>;
|
|
|
18
18
|
* - `./actions/cart.tsx` exports `addToCart` → `addToCart`
|
|
19
19
|
*/
|
|
20
20
|
export declare function collectActions<TEnv = object>(glob: GlobImport): Record<string, ActionHandler<TEnv>>;
|
|
21
|
-
/**
|
|
22
|
-
* Collects modal handlers from glob imports.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```typescript
|
|
26
|
-
* const modals = collectModals<Env>(
|
|
27
|
-
* import.meta.glob('./modals/*.tsx', { eager: true })
|
|
28
|
-
* )
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare function collectModals<TEnv = object>(glob: GlobImport): Record<string, ModalHandler<TEnv>>;
|
|
32
|
-
/**
|
|
33
|
-
* Collects drawer handlers from glob imports.
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```typescript
|
|
37
|
-
* const drawers = collectDrawers<Env>(
|
|
38
|
-
* import.meta.glob('./drawers/*.tsx', { eager: true })
|
|
39
|
-
* )
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
export declare function collectDrawers<TEnv = object>(glob: GlobImport): Record<string, DrawerHandler<TEnv>>;
|
|
43
|
-
/**
|
|
44
|
-
* Collects all handlers (actions, modals, drawers) from glob imports.
|
|
45
|
-
* This is a convenience function that collects all three at once.
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```typescript
|
|
49
|
-
* const { actions, modals, drawers } = collectHandlers<Env>({
|
|
50
|
-
* actions: import.meta.glob('./actions/*.tsx', { eager: true }),
|
|
51
|
-
* modals: import.meta.glob('./modals/*.tsx', { eager: true }),
|
|
52
|
-
* drawers: import.meta.glob('./drawers/*.tsx', { eager: true }),
|
|
53
|
-
* })
|
|
54
|
-
*
|
|
55
|
-
* export const beam = createBeam<Env>({ actions, modals, drawers })
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
export declare function collectHandlers<TEnv = object>(globs: {
|
|
59
|
-
actions?: GlobImport;
|
|
60
|
-
modals?: GlobImport;
|
|
61
|
-
drawers?: GlobImport;
|
|
62
|
-
}): {
|
|
63
|
-
actions: Record<string, ActionHandler<TEnv>>;
|
|
64
|
-
modals: Record<string, ModalHandler<TEnv>>;
|
|
65
|
-
drawers: Record<string, DrawerHandler<TEnv>>;
|
|
66
|
-
};
|
|
67
21
|
export {};
|
|
68
22
|
//# sourceMappingURL=collect.d.ts.map
|
package/dist/collect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../src/collect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../src/collect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C;;GAEG;AACH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAEzD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAG,MAAM,EAC1C,IAAI,EAAE,UAAU,GACf,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAarC"}
|
package/dist/collect.js
CHANGED
|
@@ -24,67 +24,3 @@ export function collectActions(glob) {
|
|
|
24
24
|
}
|
|
25
25
|
return handlers;
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Collects modal handlers from glob imports.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* const modals = collectModals<Env>(
|
|
33
|
-
* import.meta.glob('./modals/*.tsx', { eager: true })
|
|
34
|
-
* )
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export function collectModals(glob) {
|
|
38
|
-
const handlers = {};
|
|
39
|
-
for (const [, module] of Object.entries(glob)) {
|
|
40
|
-
for (const [exportName, exportValue] of Object.entries(module)) {
|
|
41
|
-
if (typeof exportValue === 'function' && exportName !== 'default') {
|
|
42
|
-
handlers[exportName] = exportValue;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return handlers;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Collects drawer handlers from glob imports.
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* ```typescript
|
|
53
|
-
* const drawers = collectDrawers<Env>(
|
|
54
|
-
* import.meta.glob('./drawers/*.tsx', { eager: true })
|
|
55
|
-
* )
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
export function collectDrawers(glob) {
|
|
59
|
-
const handlers = {};
|
|
60
|
-
for (const [, module] of Object.entries(glob)) {
|
|
61
|
-
for (const [exportName, exportValue] of Object.entries(module)) {
|
|
62
|
-
if (typeof exportValue === 'function' && exportName !== 'default') {
|
|
63
|
-
handlers[exportName] = exportValue;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return handlers;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Collects all handlers (actions, modals, drawers) from glob imports.
|
|
71
|
-
* This is a convenience function that collects all three at once.
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```typescript
|
|
75
|
-
* const { actions, modals, drawers } = collectHandlers<Env>({
|
|
76
|
-
* actions: import.meta.glob('./actions/*.tsx', { eager: true }),
|
|
77
|
-
* modals: import.meta.glob('./modals/*.tsx', { eager: true }),
|
|
78
|
-
* drawers: import.meta.glob('./drawers/*.tsx', { eager: true }),
|
|
79
|
-
* })
|
|
80
|
-
*
|
|
81
|
-
* export const beam = createBeam<Env>({ actions, modals, drawers })
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
|
-
export function collectHandlers(globs) {
|
|
85
|
-
return {
|
|
86
|
-
actions: globs.actions ? collectActions(globs.actions) : {},
|
|
87
|
-
modals: globs.modals ? collectModals(globs.modals) : {},
|
|
88
|
-
drawers: globs.drawers ? collectDrawers(globs.drawers) : {},
|
|
89
|
-
};
|
|
90
|
-
}
|