phoenix_live_view 1.0.16 → 1.0.18
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_patch.js +17 -1
- package/assets/js/phoenix_live_view/live_socket.js +9 -1
- package/assets/js/phoenix_live_view/view.js +26 -13
- package/package.json +2 -2
- package/priv/static/phoenix_live_view.cjs.js +36 -11
- package/priv/static/phoenix_live_view.cjs.js.map +2 -2
- package/priv/static/phoenix_live_view.esm.js +36 -11
- package/priv/static/phoenix_live_view.esm.js.map +2 -2
- package/priv/static/phoenix_live_view.js +36 -11
- package/priv/static/phoenix_live_view.min.js +5 -5
|
@@ -70,9 +70,25 @@ export default class DOMPatch {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
perform(isJoinPatch){
|
|
73
|
-
let {view, liveSocket, html, container
|
|
73
|
+
let {view, liveSocket, html, container} = this
|
|
74
|
+
let targetContainer = this.targetContainer
|
|
74
75
|
if(this.isCIDPatch() && !targetContainer){ return }
|
|
75
76
|
|
|
77
|
+
if(this.isCIDPatch()){
|
|
78
|
+
// https://github.com/phoenixframework/phoenix_live_view/pull/3942
|
|
79
|
+
// we need to ensure that no parent is locked
|
|
80
|
+
const closestLock = targetContainer.closest(`[${PHX_REF_LOCK}]`)
|
|
81
|
+
if(closestLock){
|
|
82
|
+
const clonedTree = DOM.private(closestLock, PHX_REF_LOCK)
|
|
83
|
+
if(clonedTree){
|
|
84
|
+
// if a parent is locked with a cloned tree, we need to patch the cloned tree instead
|
|
85
|
+
targetContainer = clonedTree.querySelector(
|
|
86
|
+
`[data-phx-component="${this.targetCID}"]`,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
let focused = liveSocket.getActiveElement()
|
|
77
93
|
let {selectionStart, selectionEnd} = focused && DOM.hasSelectionRange(focused) ? focused : {}
|
|
78
94
|
let phxUpdate = liveSocket.binding(PHX_UPDATE)
|
|
@@ -461,7 +461,15 @@ export default class LiveSocket {
|
|
|
461
461
|
}
|
|
462
462
|
|
|
463
463
|
owner(childEl, callback){
|
|
464
|
-
let view
|
|
464
|
+
let view
|
|
465
|
+
const closestViewEl = childEl.closest(PHX_VIEW_SELECTOR)
|
|
466
|
+
if(closestViewEl){
|
|
467
|
+
// it can happen that we find a view that is already destroyed;
|
|
468
|
+
// in that case we DO NOT want to fallback to the main element
|
|
469
|
+
view = this.getViewByEl(closestViewEl)
|
|
470
|
+
} else {
|
|
471
|
+
view = this.main
|
|
472
|
+
}
|
|
465
473
|
return view && callback ? callback(view) : view
|
|
466
474
|
}
|
|
467
475
|
|
|
@@ -57,6 +57,8 @@ import Rendered from "./rendered"
|
|
|
57
57
|
import ViewHook from "./view_hook"
|
|
58
58
|
import JS from "./js"
|
|
59
59
|
|
|
60
|
+
import morphdom from "morphdom"
|
|
61
|
+
|
|
60
62
|
export let prependFormDataKey = (key, prefix) => {
|
|
61
63
|
let isArray = key.endsWith("[]")
|
|
62
64
|
// Remove the "[]" if it's an array
|
|
@@ -663,9 +665,13 @@ export default class View {
|
|
|
663
665
|
})
|
|
664
666
|
}
|
|
665
667
|
|
|
666
|
-
update(diff, events){
|
|
668
|
+
update(diff, events, isPending=false){
|
|
667
669
|
if(this.isJoinPending() || (this.liveSocket.hasPendingLink() && this.root.isMain())){
|
|
668
|
-
|
|
670
|
+
// don't mutate if this is already a pending diff
|
|
671
|
+
if(!isPending){
|
|
672
|
+
this.pendingDiffs.push({diff, events})
|
|
673
|
+
}
|
|
674
|
+
return false
|
|
669
675
|
}
|
|
670
676
|
|
|
671
677
|
this.rendered.mergeDiff(diff)
|
|
@@ -691,6 +697,8 @@ export default class View {
|
|
|
691
697
|
|
|
692
698
|
this.liveSocket.dispatchEvents(events)
|
|
693
699
|
if(phxChildrenAdded){ this.joinNewChildren() }
|
|
700
|
+
|
|
701
|
+
return true
|
|
694
702
|
}
|
|
695
703
|
|
|
696
704
|
renderContainer(diff, kind){
|
|
@@ -756,15 +764,13 @@ export default class View {
|
|
|
756
764
|
}
|
|
757
765
|
|
|
758
766
|
applyPendingUpdates(){
|
|
759
|
-
// prevent race conditions where we might still be pending a new
|
|
760
|
-
// navigation
|
|
761
|
-
// if
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
this.
|
|
766
|
-
this.pendingDiffs = []
|
|
767
|
-
this.eachChild(child => child.applyPendingUpdates())
|
|
767
|
+
// To prevent race conditions where we might still be pending a new
|
|
768
|
+
// navigation or the join is still pending, `this.update` returns false
|
|
769
|
+
// if the diff was not applied.
|
|
770
|
+
this.pendingDiffs = this.pendingDiffs.filter(
|
|
771
|
+
({diff, events}) => !this.update(diff, events, true),
|
|
772
|
+
)
|
|
773
|
+
this.eachChild((child) => child.applyPendingUpdates())
|
|
768
774
|
}
|
|
769
775
|
|
|
770
776
|
eachChild(callback){
|
|
@@ -1533,13 +1539,20 @@ export default class View {
|
|
|
1533
1539
|
.filter(form => form.elements.length > 0)
|
|
1534
1540
|
.filter(form => form.getAttribute(this.binding(PHX_AUTO_RECOVER)) !== "ignore")
|
|
1535
1541
|
.map(form => {
|
|
1536
|
-
// we perform a shallow clone and manually copy all
|
|
1542
|
+
// we perform a shallow clone and manually copy all elements
|
|
1537
1543
|
const clonedForm = form.cloneNode(false)
|
|
1538
1544
|
// we need to copy the private data as it contains
|
|
1539
1545
|
// the information about touched fields
|
|
1540
1546
|
DOM.copyPrivates(clonedForm, form)
|
|
1541
1547
|
Array.from(form.elements).forEach((el) => {
|
|
1542
|
-
|
|
1548
|
+
// we need to clone all child nodes as well,
|
|
1549
|
+
// because those could also be selects
|
|
1550
|
+
const clonedEl = el.cloneNode(true)
|
|
1551
|
+
// we call morphdom to copy any special state
|
|
1552
|
+
// like the selected option of a <select> element;
|
|
1553
|
+
// this should be plenty fast as we call it on a small subset of the DOM,
|
|
1554
|
+
// single inputs or a select with children
|
|
1555
|
+
morphdom(clonedEl, el)
|
|
1543
1556
|
DOM.copyPrivates(clonedEl, el)
|
|
1544
1557
|
clonedForm.appendChild(clonedEl)
|
|
1545
1558
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phoenix_live_view",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "The Phoenix LiveView JavaScript client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "./priv/static/phoenix_live_view.esm.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"assets/js/phoenix_live_view/*"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"morphdom": "2.7.
|
|
27
|
+
"morphdom": "2.7.7"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@babel/cli": "7.27.0",
|
|
@@ -1675,6 +1675,10 @@ var specialElHandlers = {
|
|
|
1675
1675
|
if (nodeName === "OPTGROUP") {
|
|
1676
1676
|
optgroup = curChild;
|
|
1677
1677
|
curChild = optgroup.firstChild;
|
|
1678
|
+
if (!curChild) {
|
|
1679
|
+
curChild = optgroup.nextSibling;
|
|
1680
|
+
optgroup = null;
|
|
1681
|
+
}
|
|
1678
1682
|
} else {
|
|
1679
1683
|
if (nodeName === "OPTION") {
|
|
1680
1684
|
if (curChild.hasAttribute("selected")) {
|
|
@@ -2049,10 +2053,22 @@ var DOMPatch = class {
|
|
|
2049
2053
|
});
|
|
2050
2054
|
}
|
|
2051
2055
|
perform(isJoinPatch) {
|
|
2052
|
-
let { view, liveSocket, html, container
|
|
2056
|
+
let { view, liveSocket, html, container } = this;
|
|
2057
|
+
let targetContainer = this.targetContainer;
|
|
2053
2058
|
if (this.isCIDPatch() && !targetContainer) {
|
|
2054
2059
|
return;
|
|
2055
2060
|
}
|
|
2061
|
+
if (this.isCIDPatch()) {
|
|
2062
|
+
const closestLock = targetContainer.closest(`[${PHX_REF_LOCK}]`);
|
|
2063
|
+
if (closestLock) {
|
|
2064
|
+
const clonedTree = dom_default.private(closestLock, PHX_REF_LOCK);
|
|
2065
|
+
if (clonedTree) {
|
|
2066
|
+
targetContainer = clonedTree.querySelector(
|
|
2067
|
+
`[data-phx-component="${this.targetCID}"]`
|
|
2068
|
+
);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2056
2072
|
let focused = liveSocket.getActiveElement();
|
|
2057
2073
|
let { selectionStart, selectionEnd } = focused && dom_default.hasSelectionRange(focused) ? focused : {};
|
|
2058
2074
|
let phxUpdate = liveSocket.binding(PHX_UPDATE);
|
|
@@ -3925,9 +3941,12 @@ var View = class _View {
|
|
|
3925
3941
|
this.pendingJoinOps = [];
|
|
3926
3942
|
});
|
|
3927
3943
|
}
|
|
3928
|
-
update(diff, events) {
|
|
3944
|
+
update(diff, events, isPending = false) {
|
|
3929
3945
|
if (this.isJoinPending() || this.liveSocket.hasPendingLink() && this.root.isMain()) {
|
|
3930
|
-
|
|
3946
|
+
if (!isPending) {
|
|
3947
|
+
this.pendingDiffs.push({ diff, events });
|
|
3948
|
+
}
|
|
3949
|
+
return false;
|
|
3931
3950
|
}
|
|
3932
3951
|
this.rendered.mergeDiff(diff);
|
|
3933
3952
|
let phxChildrenAdded = false;
|
|
@@ -3951,6 +3970,7 @@ var View = class _View {
|
|
|
3951
3970
|
if (phxChildrenAdded) {
|
|
3952
3971
|
this.joinNewChildren();
|
|
3953
3972
|
}
|
|
3973
|
+
return true;
|
|
3954
3974
|
}
|
|
3955
3975
|
renderContainer(diff, kind) {
|
|
3956
3976
|
return this.liveSocket.time(`toString diff (${kind})`, () => {
|
|
@@ -4005,11 +4025,9 @@ var View = class _View {
|
|
|
4005
4025
|
delete this.viewHooks[hookId];
|
|
4006
4026
|
}
|
|
4007
4027
|
applyPendingUpdates() {
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
this.pendingDiffs.forEach(({ diff, events }) => this.update(diff, events));
|
|
4012
|
-
this.pendingDiffs = [];
|
|
4028
|
+
this.pendingDiffs = this.pendingDiffs.filter(
|
|
4029
|
+
({ diff, events }) => !this.update(diff, events, true)
|
|
4030
|
+
);
|
|
4013
4031
|
this.eachChild((child) => child.applyPendingUpdates());
|
|
4014
4032
|
}
|
|
4015
4033
|
eachChild(callback) {
|
|
@@ -4747,7 +4765,8 @@ var View = class _View {
|
|
|
4747
4765
|
const clonedForm = form.cloneNode(false);
|
|
4748
4766
|
dom_default.copyPrivates(clonedForm, form);
|
|
4749
4767
|
Array.from(form.elements).forEach((el) => {
|
|
4750
|
-
const clonedEl = el.cloneNode(
|
|
4768
|
+
const clonedEl = el.cloneNode(true);
|
|
4769
|
+
morphdom_esm_default(clonedEl, el);
|
|
4751
4770
|
dom_default.copyPrivates(clonedEl, el);
|
|
4752
4771
|
clonedForm.appendChild(clonedEl);
|
|
4753
4772
|
});
|
|
@@ -4864,7 +4883,7 @@ var LiveSocket = class {
|
|
|
4864
4883
|
}
|
|
4865
4884
|
// public
|
|
4866
4885
|
version() {
|
|
4867
|
-
return "1.0.
|
|
4886
|
+
return "1.0.18";
|
|
4868
4887
|
}
|
|
4869
4888
|
isProfileEnabled() {
|
|
4870
4889
|
return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
|
|
@@ -5134,7 +5153,13 @@ var LiveSocket = class {
|
|
|
5134
5153
|
return view;
|
|
5135
5154
|
}
|
|
5136
5155
|
owner(childEl, callback) {
|
|
5137
|
-
let view
|
|
5156
|
+
let view;
|
|
5157
|
+
const closestViewEl = childEl.closest(PHX_VIEW_SELECTOR);
|
|
5158
|
+
if (closestViewEl) {
|
|
5159
|
+
view = this.getViewByEl(closestViewEl);
|
|
5160
|
+
} else {
|
|
5161
|
+
view = this.main;
|
|
5162
|
+
}
|
|
5138
5163
|
return view && callback ? callback(view) : view;
|
|
5139
5164
|
}
|
|
5140
5165
|
withinOwners(childEl, callback) {
|