phoenix_live_view 1.2.0 → 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/assets/js/phoenix_live_view/dom.ts +3 -1
- package/assets/js/phoenix_live_view/dom_patch.ts +1 -1
- package/assets/js/phoenix_live_view/live_socket.ts +16 -9
- package/assets/js/phoenix_live_view/view.ts +6 -0
- package/package.json +2 -2
- package/priv/static/phoenix_live_view.cjs.js +16 -9
- package/priv/static/phoenix_live_view.cjs.js.map +2 -2
- package/priv/static/phoenix_live_view.esm.js +16 -9
- package/priv/static/phoenix_live_view.esm.js.map +2 -2
- package/priv/static/phoenix_live_view.js +16 -9
- package/priv/static/phoenix_live_view.min.js +4 -4
|
@@ -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
|
-
|
|
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) {
|
|
@@ -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
|
|
827
|
-
this.
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
892
|
-
//
|
|
893
|
-
|
|
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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phoenix_live_view",
|
|
3
|
-
"version": "1.2.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
@@ -6089,7 +6094,7 @@ var LiveSocket = class {
|
|
|
6089
6094
|
* Returns the version of the LiveView client.
|
|
6090
6095
|
*/
|
|
6091
6096
|
version() {
|
|
6092
|
-
return "1.2.
|
|
6097
|
+
return "1.2.1";
|
|
6093
6098
|
}
|
|
6094
6099
|
/**
|
|
6095
6100
|
* Returns true if profiling is enabled. See {@link enableProfiling} and {@link disableProfiling}.
|
|
@@ -6442,11 +6447,12 @@ var LiveSocket = class {
|
|
|
6442
6447
|
`[${this.binding("remove")}]`
|
|
6443
6448
|
).filter((el) => !dom_default.isChildOfAny(el, stickies));
|
|
6444
6449
|
const newMainEl = dom_default.cloneNode(this.outgoingMainEl, "");
|
|
6445
|
-
this.main
|
|
6446
|
-
this.
|
|
6450
|
+
const oldMainView = this.main;
|
|
6451
|
+
oldMainView.showLoader(this.loaderTimeout);
|
|
6452
|
+
oldMainView.destroy();
|
|
6447
6453
|
this.main = this.newRootView(newMainEl, flash, liveReferer);
|
|
6448
6454
|
this.main.setRedirect(href);
|
|
6449
|
-
this.transitionRemoves(removeEls);
|
|
6455
|
+
this.transitionRemoves(removeEls, oldMainView);
|
|
6450
6456
|
this.main.join((joinCount, onDone) => {
|
|
6451
6457
|
if (joinCount === 1 && this.commitPendingLink(linkRef)) {
|
|
6452
6458
|
this.requestDOMUpdate(() => {
|
|
@@ -6461,7 +6467,7 @@ var LiveSocket = class {
|
|
|
6461
6467
|
});
|
|
6462
6468
|
}
|
|
6463
6469
|
/** @internal */
|
|
6464
|
-
transitionRemoves(elements, callback) {
|
|
6470
|
+
transitionRemoves(elements, view, callback) {
|
|
6465
6471
|
const removeAttr = this.binding("remove");
|
|
6466
6472
|
const silenceEvents = (e) => {
|
|
6467
6473
|
e.preventDefault();
|
|
@@ -6471,7 +6477,8 @@ var LiveSocket = class {
|
|
|
6471
6477
|
for (const event of this.boundEventNames) {
|
|
6472
6478
|
el.addEventListener(event, silenceEvents, true);
|
|
6473
6479
|
}
|
|
6474
|
-
|
|
6480
|
+
const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
|
|
6481
|
+
js_default.exec(e, "remove", el.getAttribute(removeAttr), view, el);
|
|
6475
6482
|
});
|
|
6476
6483
|
this.requestDOMUpdate(() => {
|
|
6477
6484
|
elements.forEach((el) => {
|
|
@@ -6497,7 +6504,7 @@ var LiveSocket = class {
|
|
|
6497
6504
|
let view;
|
|
6498
6505
|
const viewEl = dom_default.closestViewEl(childEl);
|
|
6499
6506
|
if (viewEl) {
|
|
6500
|
-
view =
|
|
6507
|
+
view = dom_default.private(viewEl, "view");
|
|
6501
6508
|
} else {
|
|
6502
6509
|
if (!childEl.isConnected) {
|
|
6503
6510
|
return null;
|