@shamar/wire 0.3.0 → 1.0.1

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 CHANGED
@@ -8,15 +8,7 @@ Existing Alpine UI stays. A piece of the panel moves onto Wire when its markup o
8
8
 
9
9
  ## Install
10
10
 
11
- `@shamar/wire` is not on npm yet. The packages published as 0.3.0 (`@shamar/adonis` and the rest) do not depend on it. The next release publishes this package first, then `@shamar/adonis`, because the host depends on it. Until then, use it from this repository.
12
-
13
- After that release, the panel package pulls Wire in:
14
-
15
- ```bash
16
- pnpm add @shamar/adonis
17
- ```
18
-
19
- `@shamar/adonis` depends on `@shamar/wire` and re-exports `WireKernel`, `WireComponent`, and `WireRequest`. Install the kernel on its own only when you are not using the Adonis host:
11
+ `pnpm add @shamar/adonis` installs the panel and `@shamar/wire`. The host re-exports `WireKernel`, `WireComponent`, and `WireRequest`. Install the kernel on its own only when you are not using the Adonis host:
20
12
 
21
13
  ```bash
22
14
  pnpm add @shamar/wire
@@ -24,6 +16,8 @@ pnpm add @shamar/wire
24
16
 
25
17
  No peer dependencies. Node 20+.
26
18
 
19
+ New versions are published by GitHub Actions, not from a laptop token. npm trusted publishing for `@shamar/wire` accepts publishes from the `publish.yml` workflow in `coolsam726/shamar`. A GitHub release of the monorepo runs it.
20
+
27
21
  ## What a request does
28
22
 
29
23
  1. **Mount.** The server creates the component, copies `data` into a signed snapshot, and returns one HTML island.
package/client/wire.js CHANGED
@@ -297,7 +297,7 @@
297
297
  const style = document.createElement('style');
298
298
  style.textContent =
299
299
  '[wire\\:loading]{display:none !important}.wire-loading [wire\\:loading]{display:revert !important}.wire-loading [wire\\:loading\\.remove]{display:none !important}' +
300
- '.wire-progress{position:fixed;top:0;left:0;height:2px;width:0;opacity:0;z-index:2147483646;pointer-events:none;background:#f59e0b;transition:width .2s ease,opacity .2s ease}' +
300
+ '.wire-progress{position:fixed;top:0;left:0;height:2px;width:0;opacity:0;z-index:2147483646;pointer-events:none;background:var(--color-shamar-accent,#286291);transition:width .2s ease,opacity .2s ease}' +
301
301
  '.wire-progress.is-active{opacity:1}';
302
302
  document.head.appendChild(style);
303
303
 
@@ -481,7 +481,7 @@
481
481
  const show = setTimeout(() => {
482
482
  bar.classList.add('is-active');
483
483
  bar.style.width = '35%';
484
- }, 150);
484
+ }, 80);
485
485
  const tick = setInterval(() => {
486
486
  const width = Number.parseFloat(bar.style.width) || 0;
487
487
  if (width < 90) bar.style.width = `${width + 8}%`;
@@ -609,6 +609,43 @@
609
609
  return null;
610
610
  }
611
611
 
612
+ /**
613
+ * Swap chrome that sits outside the scroll morph root (sidebar roots, topbar
614
+ * menu, etc.) so active nav and Alpine menus match the destination page.
615
+ */
616
+ function syncNavigateRegions(nextDoc) {
617
+ const updated = [];
618
+ for (const current of [...document.querySelectorAll('[data-wire-sync]')]) {
619
+ const key = current.getAttribute('data-wire-sync');
620
+ if (!key) continue;
621
+ const next = nextDoc.querySelector(`[data-wire-sync="${CSS.escape(key)}"]`);
622
+ if (!(next instanceof HTMLElement)) continue;
623
+ if (window.Alpine?.destroyTree) {
624
+ try {
625
+ window.Alpine.destroyTree(current);
626
+ } catch {
627
+ /* ignore */
628
+ }
629
+ }
630
+ const clone = next.cloneNode(true);
631
+ current.replaceWith(clone);
632
+ updated.push(clone);
633
+ }
634
+ return updated;
635
+ }
636
+
637
+ function reviveAlpine(roots) {
638
+ if (!window.Alpine?.initTree) return;
639
+ for (const root of roots) {
640
+ if (!(root instanceof HTMLElement)) continue;
641
+ try {
642
+ window.Alpine.initTree(root);
643
+ } catch {
644
+ /* ignore */
645
+ }
646
+ }
647
+ }
648
+
612
649
  function applyScroll(mode, scrollY, url) {
613
650
  if (mode === 'preserve') return;
614
651
  if (mode === 'restore') {
@@ -671,17 +708,28 @@
671
708
  history.pushState({ wireScroll: 0 }, '', finalUrl);
672
709
  }
673
710
  mergeHead(doc);
711
+ const synced = syncNavigateRegions(doc);
674
712
  const region = sharedRoot(doc);
675
713
  const from = region ? region.current : document.body;
676
714
  const to = region ? region.next : doc.body;
677
715
  const kept = liftPersisted(from);
716
+ // Drop Alpine on the outgoing region before morph. Morph reuses matching
717
+ // nodes (same tag), so without destroyTree, list selection / open menus
718
+ // from the previous page leak into the next (bulk bar, dropdowns, etc.).
719
+ if (window.Alpine?.destroyTree) {
720
+ try {
721
+ window.Alpine.destroyTree(from);
722
+ } catch {
723
+ /* ignore */
724
+ }
725
+ }
678
726
  morph(from, to, from, false);
679
727
  const restored = restorePersisted(from, kept);
680
- if (window.Alpine?.initTree) {
681
- for (const el of from.querySelectorAll('[wire\\:persist]')) {
682
- if (!restored.includes(el)) window.Alpine.initTree(el);
683
- }
728
+ const alpineRoots = [...synced, from];
729
+ for (const el of from.querySelectorAll('[wire\\:persist]')) {
730
+ if (!restored.includes(el)) alpineRoots.push(el);
684
731
  }
732
+ reviveAlpine(alpineRoots);
685
733
  reviveScripts(from, restored);
686
734
  applyScroll(options.scroll, options.scrollY, new URL(finalUrl, window.location.href));
687
735
  document.dispatchEvent(new CustomEvent('wire:navigated', { detail: { url: window.location.href } }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shamar/wire",
3
- "version": "0.3.0",
3
+ "version": "1.0.1",
4
4
  "description": "Server-driven reactive components. Usable on its own or inside a Shamar admin panel.",
5
5
  "license": "MIT",
6
6
  "type": "module",