phoenix_live_view 1.2.0-rc.3 → 1.2.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
@@ -1,6 +1,6 @@
1
1
  # Phoenix LiveView
2
2
 
3
- [![Actions Status](https://github.com/phoenixframework/phoenix_live_view/workflows/CI/badge.svg)](https://github.com/phoenixframework/phoenix_live_view/actions?query=workflow%3ACI) [![Hex.pm](https://img.shields.io/hexpm/v/phoenix_live_view.svg)](https://hex.pm/packages/phoenix_live_view) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/phoenix_live_view)
3
+ [![Actions Status](https://github.com/phoenixframework/phoenix_live_view/workflows/CI/badge.svg)](https://github.com/phoenixframework/phoenix_live_view/actions?query=workflow%3ACI) [![Hex.pm](https://img.shields.io/hexpm/v/phoenix_live_view.svg)](https://hex.pm/packages/phoenix_live_view) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://phoenix-live-view.hexdocs.pm)
4
4
 
5
5
  Phoenix LiveView enables rich, real-time user experiences with server-rendered HTML.
6
6
 
@@ -34,7 +34,7 @@ model while keeping your code closer to your data (and ultimately your source of
34
34
 
35
35
  * **Diffs over the wire:** Instead of sending "HTML over the wire", LiveView knows exactly which parts of your templates change, sending minimal diffs over the wire after the initial render, reducing latency and bandwidth usage. The client leverages this information and optimizes the browser with 5-10x faster updates, compared to solutions that replace whole HTML fragments.
36
36
 
37
- * **Live form validation:** LiveView supports real-time form validation out of the box. Create rich user interfaces with features like uploads, nested inputs, and [specialized recovery](https://hexdocs.pm/phoenix_live_view/form-bindings.html#recovery-following-crashes-or-disconnects).
37
+ * **Live form validation:** LiveView supports real-time form validation out of the box. Create rich user interfaces with features like uploads, nested inputs, and [specialized recovery](https://phoenix-live-view.hexdocs.pm/form-bindings.html#recovery-following-crashes-or-disconnects).
38
38
 
39
39
  * **File uploads:** Real-time file uploads with progress indicators and image previews. Process your uploads on the fly or submit them to your desired cloud service.
40
40
 
@@ -52,9 +52,9 @@ model while keeping your code closer to your data (and ultimately your source of
52
52
 
53
53
  ## Learning
54
54
 
55
- Check our [comprehensive docs](https://hexdocs.pm/phoenix_live_view) to get started.
55
+ Check our [comprehensive docs](https://phoenix-live-view.hexdocs.pm) to get started.
56
56
 
57
- The Phoenix framework documentation also keeps a list of [community resources](https://hexdocs.pm/phoenix/community.html), including books, videos, and other materials, and some include LiveView too.
57
+ The Phoenix framework documentation also keeps a list of [community resources](https://phoenix.hexdocs.pm/community.html), including books, videos, and other materials, and some include LiveView too.
58
58
 
59
59
  Also follow these announcements from the Phoenix team on LiveView for more examples and rationale:
60
60
 
@@ -103,7 +103,7 @@ which provides quick times for "First Meaningful Paint", in addition to
103
103
  helping search and indexing engines.
104
104
 
105
105
  Then LiveView uses a persistent connection between client and server.
106
- This allows LiveView applications to react faster to user events as
106
+ This allows LiveView applications to react faster to user events, as
107
107
  there is less work to be done and less data to be sent compared to
108
108
  stateless requests that have to authenticate, decode, load, and encode
109
109
  data on every request.
@@ -1,3 +1,3 @@
1
1
  ## LiveView JavaScript Client
2
2
 
3
- This is the documentation for the LiveView JavaScript client. It is a more low-level API documentation for advanced users. For a higher-level overview, [see the page on JavaScript interoperability](https://hexdocs.pm/phoenix_live_view/js-interop.html) instead.
3
+ This is the documentation for the LiveView JavaScript client. It is a more low-level API documentation for advanced users. For a higher-level overview, [see the page on JavaScript interoperability](https://phoenix-live-view.hexdocs.pm/js-interop.html) instead.
@@ -411,7 +411,9 @@ const DOM = {
411
411
  // we also clear the throttle timeout to prevent the callback
412
412
  // from being called again after the timeout fires
413
413
  clearTimeout(this.private(el, THROTTLED));
414
- this.triggerCycle(el, DEBOUNCE_TRIGGER);
414
+ if (asyncFilter()) {
415
+ this.triggerCycle(el, DEBOUNCE_TRIGGER);
416
+ }
415
417
  });
416
418
  }
417
419
  }
@@ -792,7 +792,7 @@ export default class DOMPatch {
792
792
  private transitionPendingRemoves() {
793
793
  const { pendingRemoves, liveSocket } = this;
794
794
  if (pendingRemoves.length > 0) {
795
- liveSocket.transitionRemoves(pendingRemoves, () => {
795
+ liveSocket.transitionRemoves(pendingRemoves, this.view, () => {
796
796
  pendingRemoves.forEach((el) => {
797
797
  const child = DOM.firstPhxChild(el);
798
798
  if (child) {
@@ -1,9 +1,9 @@
1
1
  /*
2
2
  * This is the documentation for the LiveView JavaScript client.
3
3
  * It is a more low-level API documentation for advanced users.
4
- * For a higher-level overview, [see the page on JavaScript interoperability](https://hexdocs.pm/phoenix_live_view/js-interop.html) instead.
4
+ * For a higher-level overview, [see the page on JavaScript interoperability](https://phoenix-live-view.hexdocs.pm/js-interop.html) instead.
5
5
  *
6
- * The main documentation can be found at `https://hexdocs.pm/phoenix_live_view`.
6
+ * The main documentation can be found at `https://phoenix-live-view.hexdocs.pm`.
7
7
  *
8
8
  * @packageDocumentation
9
9
  */
@@ -92,7 +92,7 @@ export interface LiveSocketOptions {
92
92
  /**
93
93
  * Callbacks for LiveView hooks.
94
94
  *
95
- * See [Client hooks via `phx-hook`](https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks-via-phx-hook) for more information.
95
+ * See [Client hooks via `phx-hook`](https://phoenix-live-view.hexdocs.pm/js-interop.html#client-hooks-via-phx-hook) for more information.
96
96
  */
97
97
  hooks?: HooksOptions;
98
98
  /** Callbacks for LiveView uploaders. */
@@ -425,7 +425,7 @@ export default class LiveSocket {
425
425
  * Enables debugging.
426
426
  *
427
427
  * When debugging is enabled, the LiveView client will log debug information to the console.
428
- * See [Debugging client events](https://hexdocs.pm/phoenix_live_view/js-interop.html#debugging-client-events) for more information.
428
+ * See [Debugging client events](https://phoenix-live-view.hexdocs.pm/js-interop.html#debugging-client-events) for more information.
429
429
  */
430
430
  enableDebug(): void {
431
431
  this.sessionStorage.setItem(PHX_LV_DEBUG, "true");
@@ -458,7 +458,7 @@ export default class LiveSocket {
458
458
  * Enables latency simulation.
459
459
  *
460
460
  * When latency simulation is enabled, the LiveView client will add a delay to requests and responses from the server.
461
- * See [Simulating Latency](https://hexdocs.pm/phoenix_live_view/js-interop.html#simulating-latency) for more information.
461
+ * See [Simulating Latency](https://phoenix-live-view.hexdocs.pm/js-interop.html#simulating-latency) for more information.
462
462
  */
463
463
  enableLatencySim(upperBoundMs: number): void {
464
464
  this.enableDebug();
@@ -547,7 +547,7 @@ export default class LiveSocket {
547
547
  /**
548
548
  * Executes an encoded JS command, targeting the given element.
549
549
  *
550
- * See [`Phoenix.LiveView.JS`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html) for more information.
550
+ * See [`Phoenix.LiveView.JS`](https://phoenix-live-view.hexdocs.pm/Phoenix.LiveView.JS.html) for more information.
551
551
  */
552
552
  execJS(
553
553
  el: Element,
@@ -562,7 +562,7 @@ export default class LiveSocket {
562
562
  * Returns an object with methods to manipulate the DOM and execute JavaScript.
563
563
  * The applied changes integrate with server DOM patching.
564
564
  *
565
- * See [JavaScript interoperability](https://hexdocs.pm/phoenix_live_view/js-interop.html) for more information.
565
+ * See [JavaScript interoperability](https://phoenix-live-view.hexdocs.pm/js-interop.html) for more information.
566
566
  */
567
567
  js(): LiveSocketJSCommands {
568
568
  return jsCommands(this, "js");
@@ -823,12 +823,15 @@ export default class LiveSocket {
823
823
  ).filter((el) => !DOM.isChildOfAny(el, stickies));
824
824
 
825
825
  const newMainEl = DOM.cloneNode(this.outgoingMainEl, "");
826
- this.main.showLoader(this.loaderTimeout);
827
- this.main.destroy();
826
+ const oldMainView = this.main;
827
+ oldMainView.showLoader(this.loaderTimeout);
828
+ oldMainView.destroy();
828
829
 
829
830
  this.main = this.newRootView(newMainEl, flash, liveReferer);
830
831
  this.main.setRedirect(href);
831
- this.transitionRemoves(removeEls);
832
+ // the old view is destroyed at this point; pass it explicitly so the
833
+ // phx-remove commands execute in the context of the outgoing view
834
+ this.transitionRemoves(removeEls, oldMainView);
832
835
  this.main.join((joinCount, onDone) => {
833
836
  if (joinCount === 1 && this.commitPendingLink(linkRef)) {
834
837
  this.requestDOMUpdate(() => {
@@ -845,7 +848,7 @@ export default class LiveSocket {
845
848
  }
846
849
 
847
850
  /** @internal */
848
- transitionRemoves(elements, callback?) {
851
+ transitionRemoves(elements, view: View, callback?) {
849
852
  const removeAttr = this.binding("remove");
850
853
  const silenceEvents = (e) => {
851
854
  e.preventDefault();
@@ -857,7 +860,8 @@ export default class LiveSocket {
857
860
  for (const event of this.boundEventNames) {
858
861
  el.addEventListener(event, silenceEvents, true);
859
862
  }
860
- this.execJS(el, el.getAttribute(removeAttr), "remove");
863
+ const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
864
+ JS.exec(e, "remove", el.getAttribute(removeAttr), view, el);
861
865
  });
862
866
  // remove the silenced listeners when transitions are done in case the element is re-used
863
867
  // and call caller's callback as soon as we are done with transitions
@@ -885,12 +889,15 @@ export default class LiveSocket {
885
889
 
886
890
  /** @internal */
887
891
  owner(childEl: Element, callback?: (view: View) => any) {
888
- let view: View;
892
+ let view: View | undefined;
889
893
  const viewEl = DOM.closestViewEl(childEl);
890
894
  if (viewEl) {
891
- // it can happen that we find a view that is already destroyed;
892
- // in that case we DO NOT want to fallback to the main element
893
- view = this.getViewByEl(viewEl);
895
+ // resolve the view by element identity instead of id; during live
896
+ // navigation the new view is registered under the same id while the
897
+ // old DOM is still attached, and events from the old DOM must not be
898
+ // routed to the new view. A destroyed view removes its element binding,
899
+ // in which case we DO NOT want to fallback to the main element
900
+ view = DOM.private(viewEl, "view");
894
901
  } else {
895
902
  if (!childEl.isConnected) {
896
903
  // if the element is not part of the DOM any more
@@ -408,6 +408,7 @@ export default class View {
408
408
  if (container) {
409
409
  const [tag, attrs] = container;
410
410
  this.el = DOM.replaceRootContainer(this.el, tag, attrs);
411
+ DOM.putPrivate(this.el, "view", this);
411
412
  }
412
413
  this.childJoins = 0;
413
414
  this.joinPending = true;
@@ -516,7 +517,10 @@ export default class View {
516
517
  if (!el) {
517
518
  throw new Error("unable to find root element for view");
518
519
  }
520
+ // child views are initially bound to an element inside the join HTML
521
+ // fragment (see onJoinComplete), so the binding must move to the real el
519
522
  this.el = el;
523
+ DOM.putPrivate(this.el, "view", this);
520
524
  this.el.setAttribute(PHX_ROOT_ID, this.root.id);
521
525
  }
522
526
 
@@ -746,6 +750,7 @@ export default class View {
746
750
  });
747
751
 
748
752
  // because we work with a template element, we must manually copy the attributes
753
+ // and bind the template root to this view,
749
754
  // otherwise the owner / target helpers don't work properly
750
755
  const rootEl = template.content.firstElementChild!;
751
756
  rootEl.id = this.id;
@@ -753,6 +758,7 @@ export default class View {
753
758
  rootEl.setAttribute(PHX_SESSION, this.getSession());
754
759
  rootEl.setAttribute(PHX_STATIC, this.getStatic() ?? "");
755
760
  this.parent && rootEl.setAttribute(PHX_PARENT_ID, this.parent.id);
761
+ DOM.putPrivate(rootEl, "view", this);
756
762
 
757
763
  // we go over all form elements in the new HTML for the LV
758
764
  // and look for old forms in the `formsForRecovery` object;
@@ -1152,6 +1158,10 @@ export default class View {
1152
1158
  }
1153
1159
 
1154
1160
  onJoinError(resp) {
1161
+ if (resp.events) {
1162
+ this.liveSocket.dispatchEvents(resp.events);
1163
+ }
1164
+
1155
1165
  if (resp.reason === "reload") {
1156
1166
  this.log("error", () => [
1157
1167
  `failed mount with ${resp.status}. Falling back to page reload`,
@@ -84,10 +84,12 @@ export interface HookInterface<E extends HTMLElement = HTMLElement> {
84
84
  * Use the {@link pushEvent | promise-returning version} to handle errors.
85
85
  *
86
86
  * @param event - The event name.
87
- * @param payload - The payload to send to the server. Defaults to an empty object.
87
+ * @param payload - The payload to send to the server. Must be a serializable
88
+ * value (typically JSON-serializable, depends on the Socket configuration).
89
+ * Defaults to an empty object.
88
90
  * @param onReply - A callback to handle the server's reply.
89
91
  */
90
- pushEvent(event: string, payload: any, onReply: OnReply): void;
92
+ pushEvent(event: string, payload: unknown, onReply: OnReply): void;
91
93
  /**
92
94
  * Pushes an event to the server and returns a Promise that resolves with the server's reply.
93
95
  *
@@ -95,10 +97,12 @@ export interface HookInterface<E extends HTMLElement = HTMLElement> {
95
97
  * such as a disconnected state, timeout, or the server rejecting the event.
96
98
  *
97
99
  * @param event - The event name.
98
- * @param [payload] - The payload to send to the server. Defaults to an empty object.
100
+ * @param [payload] - The payload to send to the server. Must be a serializable
101
+ * value (typically JSON-serializable, depends on the Socket configuration).
102
+ * Defaults to an empty object.
99
103
  * @returns A promise that fulfills or rejects with the server's reply.
100
104
  */
101
- pushEvent(event: string, payload?: any): Promise<any>;
105
+ pushEvent(event: string, payload?: unknown): Promise<any>;
102
106
 
103
107
  /**
104
108
  * Pushes a targeted event to the server and invokes a callback with the server's reply.
@@ -115,13 +119,15 @@ export interface HookInterface<E extends HTMLElement = HTMLElement> {
115
119
  *
116
120
  * @param selectorOrTarget - The selector, element, or CID to target.
117
121
  * @param event - The event name.
118
- * @param payload - The payload to send to the server. Defaults to an empty object.
122
+ * @param payload - The payload to send to the server. Must be a serializable
123
+ * value (typically JSON-serializable, depends on the Socket configuration).
124
+ * Defaults to an empty object.
119
125
  * @param onReply - A callback to handle the server's reply.
120
126
  */
121
127
  pushEventTo(
122
128
  selectorOrTarget: PhxTarget,
123
129
  event: string,
124
- payload: object,
130
+ payload: unknown,
125
131
  onReply: OnReply,
126
132
  ): void;
127
133
  /**
@@ -142,13 +148,15 @@ export interface HookInterface<E extends HTMLElement = HTMLElement> {
142
148
  *
143
149
  * @param selectorOrTarget - The selector, element, or CID to target.
144
150
  * @param event - The event name.
145
- * @param [payload] - The payload to send to the server. Defaults to an empty object.
151
+ * @param [payload] - The payload to send to the server. Must be a serializable
152
+ * value (typically JSON-serializable, depends on the Socket configuration).
153
+ * Defaults to an empty object.
146
154
  * @returns A promise that resolves when the event has been handled by all targets.
147
155
  */
148
156
  pushEventTo(
149
157
  selectorOrTarget: PhxTarget,
150
158
  event: string,
151
- payload?: object,
159
+ payload?: unknown,
152
160
  ): Promise<PromiseSettledResult<{ reply: any; ref: number }>[]>;
153
161
 
154
162
  /**
@@ -445,11 +453,11 @@ export class ViewHook<E extends HTMLElement = HTMLElement>
445
453
  };
446
454
  }
447
455
 
448
- pushEvent(event: string, payload: any, onReply: OnReply): void;
449
- pushEvent(event: string, payload?: any): Promise<any>;
456
+ pushEvent(event: string, payload: unknown, onReply: OnReply): void;
457
+ pushEvent(event: string, payload?: unknown): Promise<any>;
450
458
  pushEvent(
451
459
  event: string,
452
- payload?: any,
460
+ payload?: unknown,
453
461
  onReply?: OnReply,
454
462
  ): Promise<any> | void {
455
463
  const promise = this.__view().pushHookEvent(
@@ -471,18 +479,18 @@ export class ViewHook<E extends HTMLElement = HTMLElement>
471
479
  pushEventTo(
472
480
  selectorOrTarget: PhxTarget,
473
481
  event: string,
474
- payload: object,
482
+ payload: unknown,
475
483
  onReply: OnReply,
476
484
  ): void;
477
485
  pushEventTo(
478
486
  selectorOrTarget: PhxTarget,
479
487
  event: string,
480
- payload?: object,
488
+ payload?: unknown,
481
489
  ): Promise<PromiseSettledResult<{ reply: any; ref: number }>[]>;
482
490
  pushEventTo(
483
491
  selectorOrTarget: PhxTarget,
484
492
  event: string,
485
- payload?: object,
493
+ payload?: unknown,
486
494
  onReply?: OnReply,
487
495
  ): Promise<PromiseSettledResult<{ reply: any; ref: number }>[]> | void {
488
496
  if (onReply === undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phoenix_live_view",
3
- "version": "1.2.0-rc.3",
3
+ "version": "1.2.1",
4
4
  "description": "The Phoenix LiveView JavaScript client.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  "@babel/preset-env": "7.27.2",
38
38
  "@babel/preset-typescript": "^7.27.1",
39
39
  "@eslint/js": "^9.29.0",
40
- "@playwright/test": "^1.59.1",
40
+ "@playwright/test": "^1.60.0",
41
41
  "@types/jest": "^30.0.0",
42
42
  "@types/phoenix": "^1.6.6",
43
43
  "css.escape": "^1.5.1",
@@ -671,7 +671,9 @@ var DOM = {
671
671
  if (this.once(el, "bind-debounce")) {
672
672
  el.addEventListener("blur", () => {
673
673
  clearTimeout(this.private(el, THROTTLED));
674
- this.triggerCycle(el, DEBOUNCE_TRIGGER);
674
+ if (asyncFilter()) {
675
+ this.triggerCycle(el, DEBOUNCE_TRIGGER);
676
+ }
675
677
  });
676
678
  }
677
679
  }
@@ -2825,7 +2827,7 @@ var DOMPatch = class {
2825
2827
  transitionPendingRemoves() {
2826
2828
  const { pendingRemoves, liveSocket } = this;
2827
2829
  if (pendingRemoves.length > 0) {
2828
- liveSocket.transitionRemoves(pendingRemoves, () => {
2830
+ liveSocket.transitionRemoves(pendingRemoves, this.view, () => {
2829
2831
  pendingRemoves.forEach((el) => {
2830
2832
  const child = dom_default.firstPhxChild(el);
2831
2833
  if (child) {
@@ -4453,6 +4455,7 @@ var View = class _View {
4453
4455
  if (container) {
4454
4456
  const [tag, attrs] = container;
4455
4457
  this.el = dom_default.replaceRootContainer(this.el, tag, attrs);
4458
+ dom_default.putPrivate(this.el, "view", this);
4456
4459
  }
4457
4460
  this.childJoins = 0;
4458
4461
  this.joinPending = true;
@@ -4539,6 +4542,7 @@ var View = class _View {
4539
4542
  throw new Error("unable to find root element for view");
4540
4543
  }
4541
4544
  this.el = el;
4545
+ dom_default.putPrivate(this.el, "view", this);
4542
4546
  this.el.setAttribute(PHX_ROOT_ID, this.root.id);
4543
4547
  }
4544
4548
  // this is invoked for dead and live views, so we must filter by
@@ -4727,6 +4731,7 @@ var View = class _View {
4727
4731
  rootEl.setAttribute(PHX_SESSION, this.getSession());
4728
4732
  rootEl.setAttribute(PHX_STATIC, this.getStatic() ?? "");
4729
4733
  this.parent && rootEl.setAttribute(PHX_PARENT_ID, this.parent.id);
4734
+ dom_default.putPrivate(rootEl, "view", this);
4730
4735
  const formsToRecover = (
4731
4736
  // we go over all forms in the new DOM; because this is only the HTML for the current
4732
4737
  // view, we can be sure that all forms are owned by this view:
@@ -5021,6 +5026,9 @@ var View = class _View {
5021
5026
  });
5022
5027
  }
5023
5028
  onJoinError(resp) {
5029
+ if (resp.events) {
5030
+ this.liveSocket.dispatchEvents(resp.events);
5031
+ }
5024
5032
  if (resp.reason === "reload") {
5025
5033
  this.log("error", () => [
5026
5034
  `failed mount with ${resp.status}. Falling back to page reload`,
@@ -6086,7 +6094,7 @@ var LiveSocket = class {
6086
6094
  * Returns the version of the LiveView client.
6087
6095
  */
6088
6096
  version() {
6089
- return "1.2.0-rc.3";
6097
+ return "1.2.1";
6090
6098
  }
6091
6099
  /**
6092
6100
  * Returns true if profiling is enabled. See {@link enableProfiling} and {@link disableProfiling}.
@@ -6110,7 +6118,7 @@ var LiveSocket = class {
6110
6118
  * Enables debugging.
6111
6119
  *
6112
6120
  * When debugging is enabled, the LiveView client will log debug information to the console.
6113
- * See [Debugging client events](https://hexdocs.pm/phoenix_live_view/js-interop.html#debugging-client-events) for more information.
6121
+ * See [Debugging client events](https://phoenix-live-view.hexdocs.pm/js-interop.html#debugging-client-events) for more information.
6114
6122
  */
6115
6123
  enableDebug() {
6116
6124
  this.sessionStorage.setItem(PHX_LV_DEBUG, "true");
@@ -6139,7 +6147,7 @@ var LiveSocket = class {
6139
6147
  * Enables latency simulation.
6140
6148
  *
6141
6149
  * When latency simulation is enabled, the LiveView client will add a delay to requests and responses from the server.
6142
- * See [Simulating Latency](https://hexdocs.pm/phoenix_live_view/js-interop.html#simulating-latency) for more information.
6150
+ * See [Simulating Latency](https://phoenix-live-view.hexdocs.pm/js-interop.html#simulating-latency) for more information.
6143
6151
  */
6144
6152
  enableLatencySim(upperBoundMs) {
6145
6153
  this.enableDebug();
@@ -6214,7 +6222,7 @@ var LiveSocket = class {
6214
6222
  /**
6215
6223
  * Executes an encoded JS command, targeting the given element.
6216
6224
  *
6217
- * See [`Phoenix.LiveView.JS`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html) for more information.
6225
+ * See [`Phoenix.LiveView.JS`](https://phoenix-live-view.hexdocs.pm/Phoenix.LiveView.JS.html) for more information.
6218
6226
  */
6219
6227
  execJS(el, encodedJS, eventType = null) {
6220
6228
  const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
@@ -6224,7 +6232,7 @@ var LiveSocket = class {
6224
6232
  * Returns an object with methods to manipulate the DOM and execute JavaScript.
6225
6233
  * The applied changes integrate with server DOM patching.
6226
6234
  *
6227
- * See [JavaScript interoperability](https://hexdocs.pm/phoenix_live_view/js-interop.html) for more information.
6235
+ * See [JavaScript interoperability](https://phoenix-live-view.hexdocs.pm/js-interop.html) for more information.
6228
6236
  */
6229
6237
  js() {
6230
6238
  return js_commands_default(this, "js");
@@ -6439,11 +6447,12 @@ var LiveSocket = class {
6439
6447
  `[${this.binding("remove")}]`
6440
6448
  ).filter((el) => !dom_default.isChildOfAny(el, stickies));
6441
6449
  const newMainEl = dom_default.cloneNode(this.outgoingMainEl, "");
6442
- this.main.showLoader(this.loaderTimeout);
6443
- this.main.destroy();
6450
+ const oldMainView = this.main;
6451
+ oldMainView.showLoader(this.loaderTimeout);
6452
+ oldMainView.destroy();
6444
6453
  this.main = this.newRootView(newMainEl, flash, liveReferer);
6445
6454
  this.main.setRedirect(href);
6446
- this.transitionRemoves(removeEls);
6455
+ this.transitionRemoves(removeEls, oldMainView);
6447
6456
  this.main.join((joinCount, onDone) => {
6448
6457
  if (joinCount === 1 && this.commitPendingLink(linkRef)) {
6449
6458
  this.requestDOMUpdate(() => {
@@ -6458,7 +6467,7 @@ var LiveSocket = class {
6458
6467
  });
6459
6468
  }
6460
6469
  /** @internal */
6461
- transitionRemoves(elements, callback) {
6470
+ transitionRemoves(elements, view, callback) {
6462
6471
  const removeAttr = this.binding("remove");
6463
6472
  const silenceEvents = (e) => {
6464
6473
  e.preventDefault();
@@ -6468,7 +6477,8 @@ var LiveSocket = class {
6468
6477
  for (const event of this.boundEventNames) {
6469
6478
  el.addEventListener(event, silenceEvents, true);
6470
6479
  }
6471
- this.execJS(el, el.getAttribute(removeAttr), "remove");
6480
+ const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
6481
+ js_default.exec(e, "remove", el.getAttribute(removeAttr), view, el);
6472
6482
  });
6473
6483
  this.requestDOMUpdate(() => {
6474
6484
  elements.forEach((el) => {
@@ -6494,7 +6504,7 @@ var LiveSocket = class {
6494
6504
  let view;
6495
6505
  const viewEl = dom_default.closestViewEl(childEl);
6496
6506
  if (viewEl) {
6497
- view = this.getViewByEl(viewEl);
6507
+ view = dom_default.private(viewEl, "view");
6498
6508
  } else {
6499
6509
  if (!childEl.isConnected) {
6500
6510
  return null;