phoenix_live_view 1.1.28 → 1.1.30
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 +96 -37
- package/assets/js/phoenix_live_view/hooks.js +9 -3
- package/assets/js/phoenix_live_view/js.js +5 -0
- package/assets/js/phoenix_live_view/view.js +2 -1
- package/assets/js/types/dom_patch.d.ts +3 -0
- package/assets/js/types/hooks.d.ts +1 -0
- package/package.json +1 -1
- package/priv/static/phoenix_live_view.cjs.js +74 -33
- package/priv/static/phoenix_live_view.cjs.js.map +3 -3
- package/priv/static/phoenix_live_view.esm.js +74 -33
- package/priv/static/phoenix_live_view.esm.js.map +3 -3
- package/priv/static/phoenix_live_view.js +74 -33
- package/priv/static/phoenix_live_view.min.js +6 -6
|
@@ -54,7 +54,11 @@ export default class DOMPatch {
|
|
|
54
54
|
afterphxChildAdded: [],
|
|
55
55
|
aftertransitionsDiscarded: [],
|
|
56
56
|
};
|
|
57
|
-
|
|
57
|
+
// unlock patches pass undoRef and must morph the locked element itself, not
|
|
58
|
+
// only its children. The first client ref is 0, so this must check for the
|
|
59
|
+
// option's presence rather than truthiness.
|
|
60
|
+
this.withChildren =
|
|
61
|
+
opts.withChildren || opts.undoRef !== undefined || false;
|
|
58
62
|
this.undoRef = opts.undoRef;
|
|
59
63
|
}
|
|
60
64
|
|
|
@@ -105,6 +109,12 @@ export default class DOMPatch {
|
|
|
105
109
|
targetContainer = clonedTree.querySelector(
|
|
106
110
|
`[data-phx-component="${this.targetCID}"]`,
|
|
107
111
|
);
|
|
112
|
+
// The visible DOM can still contain the target CID while the locked
|
|
113
|
+
// clone has gone stale and no longer does. In that case there is no
|
|
114
|
+
// safe clone target for this component diff, so leave the visible DOM
|
|
115
|
+
// locked and wait for a later patch instead of throwing or patching
|
|
116
|
+
// outside the locked tree.
|
|
117
|
+
if (!targetContainer) return;
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
120
|
}
|
|
@@ -148,6 +158,13 @@ export default class DOMPatch {
|
|
|
148
158
|
if (isJoinPatch) {
|
|
149
159
|
return node.id;
|
|
150
160
|
}
|
|
161
|
+
|
|
162
|
+
// If ID was touched by JavaScript hook, use PHX_MAGIC_ID for matching.
|
|
163
|
+
// This ensures morphdom can match elements even when JS modifies their IDs.
|
|
164
|
+
if (DOM.private(node, "clientsideIdAttribute")) {
|
|
165
|
+
return node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
|
|
166
|
+
}
|
|
167
|
+
|
|
151
168
|
return (
|
|
152
169
|
node.id || (node.getAttribute && node.getAttribute(PHX_MAGIC_ID))
|
|
153
170
|
);
|
|
@@ -309,7 +326,16 @@ export default class DOMPatch {
|
|
|
309
326
|
phxViewportBottom,
|
|
310
327
|
);
|
|
311
328
|
DOM.cleanChildNodes(toEl, phxUpdate);
|
|
329
|
+
const isFocusedFormEl =
|
|
330
|
+
focused && fromEl.isSameNode(focused) && DOM.isFormInput(fromEl);
|
|
331
|
+
const focusedSelectChanged =
|
|
332
|
+
isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
312
333
|
if (this.skipCIDSibling(toEl)) {
|
|
334
|
+
// A skipped update returns before the normal update path below, so
|
|
335
|
+
// it must still perform the lock bookkeeping that keeps private
|
|
336
|
+
// cloned trees in sync.
|
|
337
|
+
this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
|
|
338
|
+
this.copyNestedPrivateLock(fromEl, toEl);
|
|
313
339
|
// if this is a live component used in a stream, we may need to reorder it
|
|
314
340
|
this.maybeReOrderStream(fromEl);
|
|
315
341
|
return false;
|
|
@@ -354,30 +380,7 @@ export default class DOMPatch {
|
|
|
354
380
|
// We keep a reference to the cloned tree in the element's private data, and
|
|
355
381
|
// on ack (view.undoRefs), we morph the cloned tree with the true fromEl in the DOM to
|
|
356
382
|
// apply any changes that happened while the element was locked.
|
|
357
|
-
|
|
358
|
-
focused && fromEl.isSameNode(focused) && DOM.isFormInput(fromEl);
|
|
359
|
-
const focusedSelectChanged =
|
|
360
|
-
isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
361
|
-
if (fromEl.hasAttribute(PHX_REF_SRC)) {
|
|
362
|
-
const ref = new ElementRef(fromEl);
|
|
363
|
-
// only perform the clone step if this is not a patch that unlocks
|
|
364
|
-
if (
|
|
365
|
-
ref.lockRef &&
|
|
366
|
-
(!this.undoRef || !ref.isLockUndoneBy(this.undoRef))
|
|
367
|
-
) {
|
|
368
|
-
DOM.applyStickyOperations(fromEl);
|
|
369
|
-
const isLocked = fromEl.hasAttribute(PHX_REF_LOCK);
|
|
370
|
-
const clone = isLocked
|
|
371
|
-
? DOM.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true)
|
|
372
|
-
: null;
|
|
373
|
-
if (clone) {
|
|
374
|
-
DOM.putPrivate(fromEl, PHX_REF_LOCK, clone);
|
|
375
|
-
if (!isFocusedFormEl) {
|
|
376
|
-
fromEl = clone;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
383
|
+
fromEl = this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
|
|
381
384
|
|
|
382
385
|
// nested view handling
|
|
383
386
|
if (DOM.isPhxChild(toEl)) {
|
|
@@ -391,14 +394,10 @@ export default class DOMPatch {
|
|
|
391
394
|
return false;
|
|
392
395
|
}
|
|
393
396
|
|
|
394
|
-
//
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
PHX_REF_LOCK,
|
|
399
|
-
DOM.private(toEl, PHX_REF_LOCK),
|
|
400
|
-
);
|
|
401
|
-
}
|
|
397
|
+
// If we are undoing a lock, copy potentially nested clones over.
|
|
398
|
+
// This keeps an inner locked subtree's private clone alive while an
|
|
399
|
+
// ancestor lock is being reconciled.
|
|
400
|
+
this.copyNestedPrivateLock(fromEl, toEl);
|
|
402
401
|
// now copy regular DOM.private data
|
|
403
402
|
DOM.copyPrivates(toEl, fromEl);
|
|
404
403
|
|
|
@@ -649,18 +648,26 @@ export default class DOMPatch {
|
|
|
649
648
|
}
|
|
650
649
|
|
|
651
650
|
if (streamAt === 0) {
|
|
652
|
-
|
|
651
|
+
this.moveOrInsertBefore(
|
|
652
|
+
el.parentElement,
|
|
653
|
+
el,
|
|
654
|
+
el.parentElement.firstElementChild,
|
|
655
|
+
);
|
|
653
656
|
} else if (streamAt > 0) {
|
|
654
657
|
const children = Array.from(el.parentElement.children);
|
|
655
658
|
const oldIndex = children.indexOf(el);
|
|
656
659
|
if (streamAt >= children.length - 1) {
|
|
657
|
-
el.parentElement
|
|
660
|
+
this.moveOrInsertBefore(el.parentElement, el, null);
|
|
658
661
|
} else {
|
|
659
662
|
const sibling = children[streamAt];
|
|
660
663
|
if (oldIndex > streamAt) {
|
|
661
|
-
el.parentElement
|
|
664
|
+
this.moveOrInsertBefore(el.parentElement, el, sibling);
|
|
662
665
|
} else {
|
|
663
|
-
|
|
666
|
+
this.moveOrInsertBefore(
|
|
667
|
+
el.parentElement,
|
|
668
|
+
el,
|
|
669
|
+
sibling.nextElementSibling,
|
|
670
|
+
);
|
|
664
671
|
}
|
|
665
672
|
}
|
|
666
673
|
}
|
|
@@ -668,6 +675,25 @@ export default class DOMPatch {
|
|
|
668
675
|
this.maybeLimitStream(el);
|
|
669
676
|
}
|
|
670
677
|
|
|
678
|
+
// Reorder a child within its parent. When supported, use the atomic
|
|
679
|
+
// moveBefore (https://developer.mozilla.org/en-US/docs/Web/API/Node/moveBefore)
|
|
680
|
+
// so connected custom elements (and other state-bearing nodes like iframes)
|
|
681
|
+
// are not disconnected and reconnected by the move. Falls back to
|
|
682
|
+
// insertBefore otherwise. Passing `ref === null` moves to the end.
|
|
683
|
+
// See also https://github.com/phoenixframework/phoenix_live_view/issues/4212.
|
|
684
|
+
moveOrInsertBefore(parent, child, ref) {
|
|
685
|
+
if (typeof parent.moveBefore === "function") {
|
|
686
|
+
try {
|
|
687
|
+
parent.moveBefore(child, ref);
|
|
688
|
+
return;
|
|
689
|
+
} catch {
|
|
690
|
+
// moveBefore can throw (e.g. HierarchyRequestError) in cases where
|
|
691
|
+
// an atomic move is not possible; fall back to insertBefore.
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
parent.insertBefore(child, ref);
|
|
695
|
+
}
|
|
696
|
+
|
|
671
697
|
maybeLimitStream(el) {
|
|
672
698
|
const { limit } = this.getStreamInsert(el);
|
|
673
699
|
const children = limit !== null && Array.from(el.parentElement.children);
|
|
@@ -722,6 +748,39 @@ export default class DOMPatch {
|
|
|
722
748
|
return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
|
|
723
749
|
}
|
|
724
750
|
|
|
751
|
+
maybeCloneLockedElement(fromEl, isFocusedFormEl) {
|
|
752
|
+
if (!fromEl.hasAttribute(PHX_REF_SRC)) return fromEl;
|
|
753
|
+
|
|
754
|
+
const ref = new ElementRef(fromEl);
|
|
755
|
+
// Only perform the clone step while the element remains locked. lockRef can
|
|
756
|
+
// be 0 for the first event, so compare against null/undefined explicitly.
|
|
757
|
+
if (
|
|
758
|
+
ref.lockRef === null ||
|
|
759
|
+
(this.undoRef !== undefined && ref.isLockUndoneBy(this.undoRef))
|
|
760
|
+
) {
|
|
761
|
+
return fromEl;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
DOM.applyStickyOperations(fromEl);
|
|
765
|
+
const clone = fromEl.hasAttribute(PHX_REF_LOCK)
|
|
766
|
+
? DOM.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true)
|
|
767
|
+
: null;
|
|
768
|
+
if (!clone) return fromEl;
|
|
769
|
+
|
|
770
|
+
DOM.putPrivate(fromEl, PHX_REF_LOCK, clone);
|
|
771
|
+
return isFocusedFormEl ? fromEl : clone;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
copyNestedPrivateLock(fromEl, toEl) {
|
|
775
|
+
// During unlock morphs, toEl may be the private clone that accumulated a
|
|
776
|
+
// nested locked subtree. Copy that private clone back to fromEl before the
|
|
777
|
+
// outer unlock finishes so the nested element can apply its own ack later.
|
|
778
|
+
// undoRef can be 0, so presence is checked with undefined.
|
|
779
|
+
if (this.undoRef === undefined || !DOM.private(toEl, PHX_REF_LOCK)) return;
|
|
780
|
+
|
|
781
|
+
DOM.putPrivate(fromEl, PHX_REF_LOCK, DOM.private(toEl, PHX_REF_LOCK));
|
|
782
|
+
}
|
|
783
|
+
|
|
725
784
|
targetCIDContainer(html) {
|
|
726
785
|
if (!this.isCIDPatch()) {
|
|
727
786
|
return;
|
|
@@ -131,7 +131,6 @@ const isAtViewportTop = (el, scrollContainer) => {
|
|
|
131
131
|
const rect = el.getBoundingClientRect();
|
|
132
132
|
return (
|
|
133
133
|
Math.ceil(rect.top) >= top(scrollContainer) &&
|
|
134
|
-
Math.ceil(rect.left) >= 0 &&
|
|
135
134
|
Math.floor(rect.top) <= bottom(scrollContainer)
|
|
136
135
|
);
|
|
137
136
|
};
|
|
@@ -140,7 +139,6 @@ const isAtViewportBottom = (el, scrollContainer) => {
|
|
|
140
139
|
const rect = el.getBoundingClientRect();
|
|
141
140
|
return (
|
|
142
141
|
Math.ceil(rect.bottom) >= top(scrollContainer) &&
|
|
143
|
-
Math.ceil(rect.left) >= 0 &&
|
|
144
142
|
Math.floor(rect.bottom) <= bottom(scrollContainer)
|
|
145
143
|
);
|
|
146
144
|
};
|
|
@@ -149,7 +147,6 @@ const isWithinViewport = (el, scrollContainer) => {
|
|
|
149
147
|
const rect = el.getBoundingClientRect();
|
|
150
148
|
return (
|
|
151
149
|
Math.ceil(rect.top) >= top(scrollContainer) &&
|
|
152
|
-
Math.ceil(rect.left) >= 0 &&
|
|
153
150
|
Math.floor(rect.top) <= bottom(scrollContainer)
|
|
154
151
|
);
|
|
155
152
|
};
|
|
@@ -264,6 +261,15 @@ Hooks.InfiniteScroll = {
|
|
|
264
261
|
}
|
|
265
262
|
},
|
|
266
263
|
|
|
264
|
+
updated() {
|
|
265
|
+
// Check if the scroll container still exists
|
|
266
|
+
// https://github.com/phoenixframework/phoenix_live_view/issues/4169.
|
|
267
|
+
if (this.scrollContainer && !this.scrollContainer.isConnected) {
|
|
268
|
+
this.destroyed();
|
|
269
|
+
this.mounted();
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
|
|
267
273
|
destroyed() {
|
|
268
274
|
if (this.scrollContainer) {
|
|
269
275
|
this.scrollContainer.removeEventListener("scroll", this.onScroll);
|
|
@@ -599,6 +599,11 @@ const JS = {
|
|
|
599
599
|
.filter((attr) => !alteredAttrs.includes(attr))
|
|
600
600
|
.concat(removes);
|
|
601
601
|
|
|
602
|
+
// If element ID is touched via JavaScript, mark it for cheap lookup during morphdom
|
|
603
|
+
if (sets.some(([attr, _val]) => attr === "id")) {
|
|
604
|
+
DOM.putPrivate(el, "clientsideIdAttribute", true);
|
|
605
|
+
}
|
|
606
|
+
|
|
602
607
|
DOM.putSticky(el, "attrs", (currentEl) => {
|
|
603
608
|
newRemoves.forEach((attr) => currentEl.removeAttribute(attr));
|
|
604
609
|
newSets.forEach(([attr, val]) => currentEl.setAttribute(attr, val));
|
|
@@ -38,11 +38,14 @@ export default class DOMPatch {
|
|
|
38
38
|
getStreamInsert(el: any): any;
|
|
39
39
|
setStreamRef(el: any, ref: any): void;
|
|
40
40
|
maybeReOrderStream(el: any, isNew: any): void;
|
|
41
|
+
moveOrInsertBefore(parent: any, child: any, ref: any): void;
|
|
41
42
|
maybeLimitStream(el: any): void;
|
|
42
43
|
transitionPendingRemoves(): void;
|
|
43
44
|
isChangedSelect(fromEl: any, toEl: any): boolean;
|
|
44
45
|
isCIDPatch(): boolean;
|
|
45
46
|
skipCIDSibling(el: any): any;
|
|
47
|
+
maybeCloneLockedElement(fromEl: any, isFocusedFormEl: any): any;
|
|
48
|
+
copyNestedPrivateLock(fromEl: any, toEl: any): void;
|
|
46
49
|
targetCIDContainer(html: any): any;
|
|
47
50
|
indexOf(parent: any, child: any): number;
|
|
48
51
|
teleport(el: any, morph: any): void;
|
|
@@ -2,6 +2,7 @@ export default Hooks;
|
|
|
2
2
|
declare namespace Hooks {
|
|
3
3
|
namespace InfiniteScroll {
|
|
4
4
|
function mounted(): void;
|
|
5
|
+
function updated(): void;
|
|
5
6
|
function destroyed(): void;
|
|
6
7
|
function throttle(interval: any, callback: any): (...args: any[]) => void;
|
|
7
8
|
function findOverrunTarget(): any;
|
package/package.json
CHANGED
|
@@ -1359,15 +1359,15 @@ var top = (scrollContainer) => {
|
|
|
1359
1359
|
};
|
|
1360
1360
|
var isAtViewportTop = (el, scrollContainer) => {
|
|
1361
1361
|
const rect = el.getBoundingClientRect();
|
|
1362
|
-
return Math.ceil(rect.top) >= top(scrollContainer) && Math.
|
|
1362
|
+
return Math.ceil(rect.top) >= top(scrollContainer) && Math.floor(rect.top) <= bottom(scrollContainer);
|
|
1363
1363
|
};
|
|
1364
1364
|
var isAtViewportBottom = (el, scrollContainer) => {
|
|
1365
1365
|
const rect = el.getBoundingClientRect();
|
|
1366
|
-
return Math.ceil(rect.bottom) >= top(scrollContainer) && Math.
|
|
1366
|
+
return Math.ceil(rect.bottom) >= top(scrollContainer) && Math.floor(rect.bottom) <= bottom(scrollContainer);
|
|
1367
1367
|
};
|
|
1368
1368
|
var isWithinViewport = (el, scrollContainer) => {
|
|
1369
1369
|
const rect = el.getBoundingClientRect();
|
|
1370
|
-
return Math.ceil(rect.top) >= top(scrollContainer) && Math.
|
|
1370
|
+
return Math.ceil(rect.top) >= top(scrollContainer) && Math.floor(rect.top) <= bottom(scrollContainer);
|
|
1371
1371
|
};
|
|
1372
1372
|
Hooks.InfiniteScroll = {
|
|
1373
1373
|
mounted() {
|
|
@@ -1458,6 +1458,12 @@ Hooks.InfiniteScroll = {
|
|
|
1458
1458
|
window.addEventListener("scroll", this.onScroll);
|
|
1459
1459
|
}
|
|
1460
1460
|
},
|
|
1461
|
+
updated() {
|
|
1462
|
+
if (this.scrollContainer && !this.scrollContainer.isConnected) {
|
|
1463
|
+
this.destroyed();
|
|
1464
|
+
this.mounted();
|
|
1465
|
+
}
|
|
1466
|
+
},
|
|
1461
1467
|
destroyed() {
|
|
1462
1468
|
if (this.scrollContainer) {
|
|
1463
1469
|
this.scrollContainer.removeEventListener("scroll", this.onScroll);
|
|
@@ -2264,7 +2270,7 @@ var DOMPatch = class {
|
|
|
2264
2270
|
afterphxChildAdded: [],
|
|
2265
2271
|
aftertransitionsDiscarded: []
|
|
2266
2272
|
};
|
|
2267
|
-
this.withChildren = opts.withChildren || opts.undoRef || false;
|
|
2273
|
+
this.withChildren = opts.withChildren || opts.undoRef !== void 0 || false;
|
|
2268
2274
|
this.undoRef = opts.undoRef;
|
|
2269
2275
|
}
|
|
2270
2276
|
before(kind, callback) {
|
|
@@ -2303,6 +2309,8 @@ var DOMPatch = class {
|
|
|
2303
2309
|
targetContainer = clonedTree.querySelector(
|
|
2304
2310
|
`[data-phx-component="${this.targetCID}"]`
|
|
2305
2311
|
);
|
|
2312
|
+
if (!targetContainer)
|
|
2313
|
+
return;
|
|
2306
2314
|
}
|
|
2307
2315
|
}
|
|
2308
2316
|
}
|
|
@@ -2331,6 +2339,9 @@ var DOMPatch = class {
|
|
|
2331
2339
|
if (isJoinPatch) {
|
|
2332
2340
|
return node.id;
|
|
2333
2341
|
}
|
|
2342
|
+
if (dom_default.private(node, "clientsideIdAttribute")) {
|
|
2343
|
+
return node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
|
|
2344
|
+
}
|
|
2334
2345
|
return node.id || node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
|
|
2335
2346
|
},
|
|
2336
2347
|
// skip indexing from children when container is stream
|
|
@@ -2452,7 +2463,11 @@ var DOMPatch = class {
|
|
|
2452
2463
|
phxViewportBottom
|
|
2453
2464
|
);
|
|
2454
2465
|
dom_default.cleanChildNodes(toEl, phxUpdate);
|
|
2466
|
+
const isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl);
|
|
2467
|
+
const focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
2455
2468
|
if (this.skipCIDSibling(toEl)) {
|
|
2469
|
+
this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
|
|
2470
|
+
this.copyNestedPrivateLock(fromEl, toEl);
|
|
2456
2471
|
this.maybeReOrderStream(fromEl);
|
|
2457
2472
|
return false;
|
|
2458
2473
|
}
|
|
@@ -2480,22 +2495,7 @@ var DOMPatch = class {
|
|
|
2480
2495
|
if (fromEl.type === "number" && fromEl.validity && fromEl.validity.badInput) {
|
|
2481
2496
|
return false;
|
|
2482
2497
|
}
|
|
2483
|
-
|
|
2484
|
-
const focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
2485
|
-
if (fromEl.hasAttribute(PHX_REF_SRC)) {
|
|
2486
|
-
const ref = new ElementRef(fromEl);
|
|
2487
|
-
if (ref.lockRef && (!this.undoRef || !ref.isLockUndoneBy(this.undoRef))) {
|
|
2488
|
-
dom_default.applyStickyOperations(fromEl);
|
|
2489
|
-
const isLocked = fromEl.hasAttribute(PHX_REF_LOCK);
|
|
2490
|
-
const clone2 = isLocked ? dom_default.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null;
|
|
2491
|
-
if (clone2) {
|
|
2492
|
-
dom_default.putPrivate(fromEl, PHX_REF_LOCK, clone2);
|
|
2493
|
-
if (!isFocusedFormEl) {
|
|
2494
|
-
fromEl = clone2;
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2498
|
+
fromEl = this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
|
|
2499
2499
|
if (dom_default.isPhxChild(toEl)) {
|
|
2500
2500
|
const prevSession = fromEl.getAttribute(PHX_SESSION);
|
|
2501
2501
|
dom_default.mergeAttrs(fromEl, toEl, { exclude: [PHX_STATIC] });
|
|
@@ -2506,13 +2506,7 @@ var DOMPatch = class {
|
|
|
2506
2506
|
dom_default.applyStickyOperations(fromEl);
|
|
2507
2507
|
return false;
|
|
2508
2508
|
}
|
|
2509
|
-
|
|
2510
|
-
dom_default.putPrivate(
|
|
2511
|
-
fromEl,
|
|
2512
|
-
PHX_REF_LOCK,
|
|
2513
|
-
dom_default.private(toEl, PHX_REF_LOCK)
|
|
2514
|
-
);
|
|
2515
|
-
}
|
|
2509
|
+
this.copyNestedPrivateLock(fromEl, toEl);
|
|
2516
2510
|
dom_default.copyPrivates(toEl, fromEl);
|
|
2517
2511
|
if (dom_default.isPortalTemplate(toEl)) {
|
|
2518
2512
|
portalCallbacks.push(() => this.teleport(toEl, morph));
|
|
@@ -2694,23 +2688,47 @@ var DOMPatch = class {
|
|
|
2694
2688
|
return;
|
|
2695
2689
|
}
|
|
2696
2690
|
if (streamAt === 0) {
|
|
2697
|
-
|
|
2691
|
+
this.moveOrInsertBefore(
|
|
2692
|
+
el.parentElement,
|
|
2693
|
+
el,
|
|
2694
|
+
el.parentElement.firstElementChild
|
|
2695
|
+
);
|
|
2698
2696
|
} else if (streamAt > 0) {
|
|
2699
2697
|
const children = Array.from(el.parentElement.children);
|
|
2700
2698
|
const oldIndex = children.indexOf(el);
|
|
2701
2699
|
if (streamAt >= children.length - 1) {
|
|
2702
|
-
el.parentElement
|
|
2700
|
+
this.moveOrInsertBefore(el.parentElement, el, null);
|
|
2703
2701
|
} else {
|
|
2704
2702
|
const sibling = children[streamAt];
|
|
2705
2703
|
if (oldIndex > streamAt) {
|
|
2706
|
-
el.parentElement
|
|
2704
|
+
this.moveOrInsertBefore(el.parentElement, el, sibling);
|
|
2707
2705
|
} else {
|
|
2708
|
-
|
|
2706
|
+
this.moveOrInsertBefore(
|
|
2707
|
+
el.parentElement,
|
|
2708
|
+
el,
|
|
2709
|
+
sibling.nextElementSibling
|
|
2710
|
+
);
|
|
2709
2711
|
}
|
|
2710
2712
|
}
|
|
2711
2713
|
}
|
|
2712
2714
|
this.maybeLimitStream(el);
|
|
2713
2715
|
}
|
|
2716
|
+
// Reorder a child within its parent. When supported, use the atomic
|
|
2717
|
+
// moveBefore (https://developer.mozilla.org/en-US/docs/Web/API/Node/moveBefore)
|
|
2718
|
+
// so connected custom elements (and other state-bearing nodes like iframes)
|
|
2719
|
+
// are not disconnected and reconnected by the move. Falls back to
|
|
2720
|
+
// insertBefore otherwise. Passing `ref === null` moves to the end.
|
|
2721
|
+
// See also https://github.com/phoenixframework/phoenix_live_view/issues/4212.
|
|
2722
|
+
moveOrInsertBefore(parent, child, ref) {
|
|
2723
|
+
if (typeof parent.moveBefore === "function") {
|
|
2724
|
+
try {
|
|
2725
|
+
parent.moveBefore(child, ref);
|
|
2726
|
+
return;
|
|
2727
|
+
} catch {
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
parent.insertBefore(child, ref);
|
|
2731
|
+
}
|
|
2714
2732
|
maybeLimitStream(el) {
|
|
2715
2733
|
const { limit } = this.getStreamInsert(el);
|
|
2716
2734
|
const children = limit !== null && Array.from(el.parentElement.children);
|
|
@@ -2751,6 +2769,25 @@ var DOMPatch = class {
|
|
|
2751
2769
|
skipCIDSibling(el) {
|
|
2752
2770
|
return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
|
|
2753
2771
|
}
|
|
2772
|
+
maybeCloneLockedElement(fromEl, isFocusedFormEl) {
|
|
2773
|
+
if (!fromEl.hasAttribute(PHX_REF_SRC))
|
|
2774
|
+
return fromEl;
|
|
2775
|
+
const ref = new ElementRef(fromEl);
|
|
2776
|
+
if (ref.lockRef === null || this.undoRef !== void 0 && ref.isLockUndoneBy(this.undoRef)) {
|
|
2777
|
+
return fromEl;
|
|
2778
|
+
}
|
|
2779
|
+
dom_default.applyStickyOperations(fromEl);
|
|
2780
|
+
const clone2 = fromEl.hasAttribute(PHX_REF_LOCK) ? dom_default.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null;
|
|
2781
|
+
if (!clone2)
|
|
2782
|
+
return fromEl;
|
|
2783
|
+
dom_default.putPrivate(fromEl, PHX_REF_LOCK, clone2);
|
|
2784
|
+
return isFocusedFormEl ? fromEl : clone2;
|
|
2785
|
+
}
|
|
2786
|
+
copyNestedPrivateLock(fromEl, toEl) {
|
|
2787
|
+
if (this.undoRef === void 0 || !dom_default.private(toEl, PHX_REF_LOCK))
|
|
2788
|
+
return;
|
|
2789
|
+
dom_default.putPrivate(fromEl, PHX_REF_LOCK, dom_default.private(toEl, PHX_REF_LOCK));
|
|
2790
|
+
}
|
|
2754
2791
|
targetCIDContainer(html) {
|
|
2755
2792
|
if (!this.isCIDPatch()) {
|
|
2756
2793
|
return;
|
|
@@ -3688,6 +3725,9 @@ var JS = {
|
|
|
3688
3725
|
const alteredAttrs = sets.map(([attr, _val]) => attr).concat(removes);
|
|
3689
3726
|
const newSets = prevSets.filter(([attr, _val]) => !alteredAttrs.includes(attr)).concat(sets);
|
|
3690
3727
|
const newRemoves = prevRemoves.filter((attr) => !alteredAttrs.includes(attr)).concat(removes);
|
|
3728
|
+
if (sets.some(([attr, _val]) => attr === "id")) {
|
|
3729
|
+
dom_default.putPrivate(el, "clientsideIdAttribute", true);
|
|
3730
|
+
}
|
|
3691
3731
|
dom_default.putSticky(el, "attrs", (currentEl) => {
|
|
3692
3732
|
newRemoves.forEach((attr) => currentEl.removeAttribute(attr));
|
|
3693
3733
|
newSets.forEach(([attr, val]) => currentEl.setAttribute(attr, val));
|
|
@@ -4593,7 +4633,8 @@ var View = class _View {
|
|
|
4593
4633
|
});
|
|
4594
4634
|
patch.after("updated", (el) => {
|
|
4595
4635
|
if (updatedHookIds.has(el.id)) {
|
|
4596
|
-
this.getHook(el)
|
|
4636
|
+
const hook = this.getHook(el);
|
|
4637
|
+
hook && hook.__updated();
|
|
4597
4638
|
}
|
|
4598
4639
|
});
|
|
4599
4640
|
patch.after("discarded", (el) => {
|
|
@@ -5935,7 +5976,7 @@ var LiveSocket = class {
|
|
|
5935
5976
|
}
|
|
5936
5977
|
// public
|
|
5937
5978
|
version() {
|
|
5938
|
-
return "1.1.
|
|
5979
|
+
return "1.1.30";
|
|
5939
5980
|
}
|
|
5940
5981
|
isProfileEnabled() {
|
|
5941
5982
|
return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
|