phoenix_live_view 1.2.0-rc.0 → 1.2.0-rc.2
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 +3 -5
- package/assets/js/phoenix_live_view/index.ts +30 -1
- package/assets/js/phoenix_live_view/js.js +5 -0
- package/assets/js/phoenix_live_view/live_uploader.js +3 -2
- package/package.json +1 -1
- package/priv/static/phoenix_live_view.cjs.js +74 -36
- package/priv/static/phoenix_live_view.cjs.js.map +3 -3
- package/priv/static/phoenix_live_view.esm.js +74 -36
- package/priv/static/phoenix_live_view.esm.js.map +3 -3
- package/priv/static/phoenix_live_view.js +74 -36
- 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;
|
|
@@ -46,10 +46,8 @@ const Hooks = {
|
|
|
46
46
|
this.inputEl = document.getElementById(
|
|
47
47
|
this.el.getAttribute(PHX_UPLOAD_REF),
|
|
48
48
|
);
|
|
49
|
-
LiveUploader.getEntryDataURL(this.inputEl, this.ref
|
|
50
|
-
|
|
51
|
-
this.el.src = url;
|
|
52
|
-
});
|
|
49
|
+
this.url = LiveUploader.getEntryDataURL(this.inputEl, this.ref);
|
|
50
|
+
this.el.src = this.url;
|
|
53
51
|
},
|
|
54
52
|
destroyed() {
|
|
55
53
|
URL.revokeObjectURL(this.url);
|
|
@@ -264,7 +262,7 @@ Hooks.InfiniteScroll = {
|
|
|
264
262
|
updated() {
|
|
265
263
|
// Check if the scroll container still exists
|
|
266
264
|
// https://github.com/phoenixframework/phoenix_live_view/issues/4169.
|
|
267
|
-
if (!this.scrollContainer.isConnected) {
|
|
265
|
+
if (this.scrollContainer && !this.scrollContainer.isConnected) {
|
|
268
266
|
this.destroyed();
|
|
269
267
|
this.mounted();
|
|
270
268
|
}
|
|
@@ -15,6 +15,7 @@ import { logError } from "./utils";
|
|
|
15
15
|
import type { EncodedJS, LiveSocketJSCommands } from "./js_commands";
|
|
16
16
|
import type { Hook, HooksOptions } from "./view_hook";
|
|
17
17
|
import type { Socket as PhoenixSocket } from "phoenix";
|
|
18
|
+
import LiveUploader from "./live_uploader";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Options for configuring the LiveSocket instance.
|
|
@@ -350,4 +351,32 @@ function createHook(el: HTMLElement, callbacks: Hook): ViewHook {
|
|
|
350
351
|
return hook;
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
|
|
354
|
+
/** Returns an object URL for the file matching the given upload ref,
|
|
355
|
+
* or `null` if no matching file is found.
|
|
356
|
+
*
|
|
357
|
+
* @param input - The file input element associated with the upload.
|
|
358
|
+
* @param uploadRef - The upload ref identifying the file entry.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
*
|
|
362
|
+
* import { getFileURLForUpload } from "phoenix_live_view"
|
|
363
|
+
*
|
|
364
|
+
* let url = getFileURLForUpload(inputEl, uploadRef)
|
|
365
|
+
* if (url) { imgEl.src = url }
|
|
366
|
+
*/
|
|
367
|
+
function getFileURLForUpload(
|
|
368
|
+
input: HTMLElement,
|
|
369
|
+
uploadRef: string,
|
|
370
|
+
): string | null {
|
|
371
|
+
return LiveUploader.getEntryDataURL(input, uploadRef);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export {
|
|
375
|
+
LiveSocket,
|
|
376
|
+
isUsedInput,
|
|
377
|
+
createHook,
|
|
378
|
+
ViewHook,
|
|
379
|
+
Hook,
|
|
380
|
+
HooksOptions,
|
|
381
|
+
getFileURLForUpload,
|
|
382
|
+
};
|
|
@@ -600,6 +600,11 @@ const JS = {
|
|
|
600
600
|
.filter((attr) => !alteredAttrs.includes(attr))
|
|
601
601
|
.concat(removes);
|
|
602
602
|
|
|
603
|
+
// If element ID is touched via JavaScript, mark it for cheap lookup during morphdom
|
|
604
|
+
if (sets.some(([attr, _val]) => attr === "id")) {
|
|
605
|
+
DOM.putPrivate(el, "clientsideIdAttribute", true);
|
|
606
|
+
}
|
|
607
|
+
|
|
603
608
|
DOM.putSticky(el, "attrs", (currentEl) => {
|
|
604
609
|
newRemoves.forEach((attr) => currentEl.removeAttribute(attr));
|
|
605
610
|
newSets.forEach(([attr, val]) => currentEl.setAttribute(attr, val));
|
|
@@ -22,11 +22,12 @@ export default class LiveUploader {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
static getEntryDataURL(inputEl, ref
|
|
25
|
+
static getEntryDataURL(inputEl, ref) {
|
|
26
26
|
const file = this.activeFiles(inputEl).find(
|
|
27
27
|
(file) => this.genFileRef(file) === ref,
|
|
28
28
|
);
|
|
29
|
-
|
|
29
|
+
if (!file) return null;
|
|
30
|
+
return URL.createObjectURL(file);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
static hasUploadsInProgress(formEl) {
|
package/package.json
CHANGED
|
@@ -22,6 +22,7 @@ __export(phoenix_live_view_exports, {
|
|
|
22
22
|
LiveSocket: () => LiveSocket2,
|
|
23
23
|
ViewHook: () => ViewHook,
|
|
24
24
|
createHook: () => createHook,
|
|
25
|
+
getFileURLForUpload: () => getFileURLForUpload,
|
|
25
26
|
isUsedInput: () => isUsedInput
|
|
26
27
|
});
|
|
27
28
|
module.exports = __toCommonJS(phoenix_live_view_exports);
|
|
@@ -1061,11 +1062,13 @@ var LiveUploader = class _LiveUploader {
|
|
|
1061
1062
|
return file._phxRef;
|
|
1062
1063
|
}
|
|
1063
1064
|
}
|
|
1064
|
-
static getEntryDataURL(inputEl, ref
|
|
1065
|
+
static getEntryDataURL(inputEl, ref) {
|
|
1065
1066
|
const file = this.activeFiles(inputEl).find(
|
|
1066
1067
|
(file2) => this.genFileRef(file2) === ref
|
|
1067
1068
|
);
|
|
1068
|
-
|
|
1069
|
+
if (!file)
|
|
1070
|
+
return null;
|
|
1071
|
+
return URL.createObjectURL(file);
|
|
1069
1072
|
}
|
|
1070
1073
|
static hasUploadsInProgress(formEl) {
|
|
1071
1074
|
let active = 0;
|
|
@@ -1292,10 +1295,8 @@ var Hooks = {
|
|
|
1292
1295
|
this.inputEl = document.getElementById(
|
|
1293
1296
|
this.el.getAttribute(PHX_UPLOAD_REF)
|
|
1294
1297
|
);
|
|
1295
|
-
LiveUploader.getEntryDataURL(this.inputEl, this.ref
|
|
1296
|
-
|
|
1297
|
-
this.el.src = url;
|
|
1298
|
-
});
|
|
1298
|
+
this.url = LiveUploader.getEntryDataURL(this.inputEl, this.ref);
|
|
1299
|
+
this.el.src = this.url;
|
|
1299
1300
|
},
|
|
1300
1301
|
destroyed() {
|
|
1301
1302
|
URL.revokeObjectURL(this.url);
|
|
@@ -1460,7 +1461,7 @@ Hooks.InfiniteScroll = {
|
|
|
1460
1461
|
}
|
|
1461
1462
|
},
|
|
1462
1463
|
updated() {
|
|
1463
|
-
if (!this.scrollContainer.isConnected) {
|
|
1464
|
+
if (this.scrollContainer && !this.scrollContainer.isConnected) {
|
|
1464
1465
|
this.destroyed();
|
|
1465
1466
|
this.mounted();
|
|
1466
1467
|
}
|
|
@@ -2271,7 +2272,7 @@ var DOMPatch = class {
|
|
|
2271
2272
|
afterphxChildAdded: [],
|
|
2272
2273
|
aftertransitionsDiscarded: []
|
|
2273
2274
|
};
|
|
2274
|
-
this.withChildren = opts.withChildren || opts.undoRef || false;
|
|
2275
|
+
this.withChildren = opts.withChildren || opts.undoRef !== void 0 || false;
|
|
2275
2276
|
this.undoRef = opts.undoRef;
|
|
2276
2277
|
}
|
|
2277
2278
|
before(kind, callback) {
|
|
@@ -2310,6 +2311,8 @@ var DOMPatch = class {
|
|
|
2310
2311
|
targetContainer = clonedTree.querySelector(
|
|
2311
2312
|
`[data-phx-component="${this.targetCID}"]`
|
|
2312
2313
|
);
|
|
2314
|
+
if (!targetContainer)
|
|
2315
|
+
return;
|
|
2313
2316
|
}
|
|
2314
2317
|
}
|
|
2315
2318
|
}
|
|
@@ -2338,6 +2341,9 @@ var DOMPatch = class {
|
|
|
2338
2341
|
if (isJoinPatch) {
|
|
2339
2342
|
return node.id;
|
|
2340
2343
|
}
|
|
2344
|
+
if (dom_default.private(node, "clientsideIdAttribute")) {
|
|
2345
|
+
return node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
|
|
2346
|
+
}
|
|
2341
2347
|
return node.id || node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
|
|
2342
2348
|
},
|
|
2343
2349
|
// skip indexing from children when container is stream
|
|
@@ -2459,7 +2465,11 @@ var DOMPatch = class {
|
|
|
2459
2465
|
phxViewportBottom
|
|
2460
2466
|
);
|
|
2461
2467
|
dom_default.cleanChildNodes(toEl, phxUpdate);
|
|
2468
|
+
const isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl);
|
|
2469
|
+
const focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
2462
2470
|
if (this.skipCIDSibling(toEl)) {
|
|
2471
|
+
this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
|
|
2472
|
+
this.copyNestedPrivateLock(fromEl, toEl);
|
|
2463
2473
|
this.maybeReOrderStream(fromEl);
|
|
2464
2474
|
return false;
|
|
2465
2475
|
}
|
|
@@ -2487,22 +2497,7 @@ var DOMPatch = class {
|
|
|
2487
2497
|
if (fromEl.type === "number" && fromEl.validity && fromEl.validity.badInput) {
|
|
2488
2498
|
return false;
|
|
2489
2499
|
}
|
|
2490
|
-
|
|
2491
|
-
const focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
2492
|
-
if (fromEl.hasAttribute(PHX_REF_SRC)) {
|
|
2493
|
-
const ref = new ElementRef(fromEl);
|
|
2494
|
-
if (ref.lockRef && (!this.undoRef || !ref.isLockUndoneBy(this.undoRef))) {
|
|
2495
|
-
dom_default.applyStickyOperations(fromEl);
|
|
2496
|
-
const isLocked = fromEl.hasAttribute(PHX_REF_LOCK);
|
|
2497
|
-
const clone2 = isLocked ? dom_default.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null;
|
|
2498
|
-
if (clone2) {
|
|
2499
|
-
dom_default.putPrivate(fromEl, PHX_REF_LOCK, clone2);
|
|
2500
|
-
if (!isFocusedFormEl) {
|
|
2501
|
-
fromEl = clone2;
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2500
|
+
fromEl = this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
|
|
2506
2501
|
if (dom_default.isPhxChild(toEl)) {
|
|
2507
2502
|
const prevSession = fromEl.getAttribute(PHX_SESSION);
|
|
2508
2503
|
dom_default.mergeAttrs(fromEl, toEl, { exclude: [PHX_STATIC] });
|
|
@@ -2513,13 +2508,7 @@ var DOMPatch = class {
|
|
|
2513
2508
|
dom_default.applyStickyOperations(fromEl);
|
|
2514
2509
|
return false;
|
|
2515
2510
|
}
|
|
2516
|
-
|
|
2517
|
-
dom_default.putPrivate(
|
|
2518
|
-
fromEl,
|
|
2519
|
-
PHX_REF_LOCK,
|
|
2520
|
-
dom_default.private(toEl, PHX_REF_LOCK)
|
|
2521
|
-
);
|
|
2522
|
-
}
|
|
2511
|
+
this.copyNestedPrivateLock(fromEl, toEl);
|
|
2523
2512
|
dom_default.copyPrivates(toEl, fromEl);
|
|
2524
2513
|
if (dom_default.isPortalTemplate(toEl)) {
|
|
2525
2514
|
portalCallbacks.push(() => this.teleport(toEl, morph));
|
|
@@ -2701,23 +2690,47 @@ var DOMPatch = class {
|
|
|
2701
2690
|
return;
|
|
2702
2691
|
}
|
|
2703
2692
|
if (streamAt === 0) {
|
|
2704
|
-
|
|
2693
|
+
this.moveOrInsertBefore(
|
|
2694
|
+
el.parentElement,
|
|
2695
|
+
el,
|
|
2696
|
+
el.parentElement.firstElementChild
|
|
2697
|
+
);
|
|
2705
2698
|
} else if (streamAt > 0) {
|
|
2706
2699
|
const children = Array.from(el.parentElement.children);
|
|
2707
2700
|
const oldIndex = children.indexOf(el);
|
|
2708
2701
|
if (streamAt >= children.length - 1) {
|
|
2709
|
-
el.parentElement
|
|
2702
|
+
this.moveOrInsertBefore(el.parentElement, el, null);
|
|
2710
2703
|
} else {
|
|
2711
2704
|
const sibling = children[streamAt];
|
|
2712
2705
|
if (oldIndex > streamAt) {
|
|
2713
|
-
el.parentElement
|
|
2706
|
+
this.moveOrInsertBefore(el.parentElement, el, sibling);
|
|
2714
2707
|
} else {
|
|
2715
|
-
|
|
2708
|
+
this.moveOrInsertBefore(
|
|
2709
|
+
el.parentElement,
|
|
2710
|
+
el,
|
|
2711
|
+
sibling.nextElementSibling
|
|
2712
|
+
);
|
|
2716
2713
|
}
|
|
2717
2714
|
}
|
|
2718
2715
|
}
|
|
2719
2716
|
this.maybeLimitStream(el);
|
|
2720
2717
|
}
|
|
2718
|
+
// Reorder a child within its parent. When supported, use the atomic
|
|
2719
|
+
// moveBefore (https://developer.mozilla.org/en-US/docs/Web/API/Node/moveBefore)
|
|
2720
|
+
// so connected custom elements (and other state-bearing nodes like iframes)
|
|
2721
|
+
// are not disconnected and reconnected by the move. Falls back to
|
|
2722
|
+
// insertBefore otherwise. Passing `ref === null` moves to the end.
|
|
2723
|
+
// See also https://github.com/phoenixframework/phoenix_live_view/issues/4212.
|
|
2724
|
+
moveOrInsertBefore(parent, child, ref) {
|
|
2725
|
+
if (typeof parent.moveBefore === "function") {
|
|
2726
|
+
try {
|
|
2727
|
+
parent.moveBefore(child, ref);
|
|
2728
|
+
return;
|
|
2729
|
+
} catch {
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
parent.insertBefore(child, ref);
|
|
2733
|
+
}
|
|
2721
2734
|
maybeLimitStream(el) {
|
|
2722
2735
|
const { limit } = this.getStreamInsert(el);
|
|
2723
2736
|
const children = limit !== null && Array.from(el.parentElement.children);
|
|
@@ -2758,6 +2771,25 @@ var DOMPatch = class {
|
|
|
2758
2771
|
skipCIDSibling(el) {
|
|
2759
2772
|
return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
|
|
2760
2773
|
}
|
|
2774
|
+
maybeCloneLockedElement(fromEl, isFocusedFormEl) {
|
|
2775
|
+
if (!fromEl.hasAttribute(PHX_REF_SRC))
|
|
2776
|
+
return fromEl;
|
|
2777
|
+
const ref = new ElementRef(fromEl);
|
|
2778
|
+
if (ref.lockRef === null || this.undoRef !== void 0 && ref.isLockUndoneBy(this.undoRef)) {
|
|
2779
|
+
return fromEl;
|
|
2780
|
+
}
|
|
2781
|
+
dom_default.applyStickyOperations(fromEl);
|
|
2782
|
+
const clone2 = fromEl.hasAttribute(PHX_REF_LOCK) ? dom_default.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null;
|
|
2783
|
+
if (!clone2)
|
|
2784
|
+
return fromEl;
|
|
2785
|
+
dom_default.putPrivate(fromEl, PHX_REF_LOCK, clone2);
|
|
2786
|
+
return isFocusedFormEl ? fromEl : clone2;
|
|
2787
|
+
}
|
|
2788
|
+
copyNestedPrivateLock(fromEl, toEl) {
|
|
2789
|
+
if (this.undoRef === void 0 || !dom_default.private(toEl, PHX_REF_LOCK))
|
|
2790
|
+
return;
|
|
2791
|
+
dom_default.putPrivate(fromEl, PHX_REF_LOCK, dom_default.private(toEl, PHX_REF_LOCK));
|
|
2792
|
+
}
|
|
2761
2793
|
targetCIDContainer(html) {
|
|
2762
2794
|
if (!this.isCIDPatch()) {
|
|
2763
2795
|
return;
|
|
@@ -3695,6 +3727,9 @@ var JS = {
|
|
|
3695
3727
|
const alteredAttrs = sets.map(([attr, _val]) => attr).concat(removes);
|
|
3696
3728
|
const newSets = prevSets.filter(([attr, _val]) => !alteredAttrs.includes(attr)).concat(sets);
|
|
3697
3729
|
const newRemoves = prevRemoves.filter((attr) => !alteredAttrs.includes(attr)).concat(removes);
|
|
3730
|
+
if (sets.some(([attr, _val]) => attr === "id")) {
|
|
3731
|
+
dom_default.putPrivate(el, "clientsideIdAttribute", true);
|
|
3732
|
+
}
|
|
3698
3733
|
dom_default.putSticky(el, "attrs", (currentEl) => {
|
|
3699
3734
|
newRemoves.forEach((attr) => currentEl.removeAttribute(attr));
|
|
3700
3735
|
newSets.forEach(([attr, val]) => currentEl.setAttribute(attr, val));
|
|
@@ -5952,7 +5987,7 @@ var LiveSocket = class {
|
|
|
5952
5987
|
}
|
|
5953
5988
|
// public
|
|
5954
5989
|
version() {
|
|
5955
|
-
return "1.2.0-rc.
|
|
5990
|
+
return "1.2.0-rc.2";
|
|
5956
5991
|
}
|
|
5957
5992
|
isProfileEnabled() {
|
|
5958
5993
|
return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
|
|
@@ -7003,4 +7038,7 @@ function createHook(el, callbacks) {
|
|
|
7003
7038
|
dom_default.putCustomElHook(el, hook);
|
|
7004
7039
|
return hook;
|
|
7005
7040
|
}
|
|
7041
|
+
function getFileURLForUpload(input, uploadRef) {
|
|
7042
|
+
return LiveUploader.getEntryDataURL(input, uploadRef);
|
|
7043
|
+
}
|
|
7006
7044
|
//# sourceMappingURL=phoenix_live_view.cjs.js.map
|