phoenix_live_view 1.0.2 → 1.0.3
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 +2 -10
- package/assets/js/phoenix_live_view/dom.js +6 -1
- package/assets/js/phoenix_live_view/dom_patch.js +8 -36
- package/assets/js/phoenix_live_view/element_ref.js +9 -0
- package/assets/js/phoenix_live_view/hooks.js +20 -2
- package/assets/js/phoenix_live_view/live_socket.js +7 -18
- package/assets/js/phoenix_live_view/utils.js +11 -0
- package/assets/js/phoenix_live_view/view.js +32 -17
- package/package.json +24 -6
- package/priv/static/phoenix_live_view.cjs.js +81 -76
- package/priv/static/phoenix_live_view.cjs.js.map +2 -2
- package/priv/static/phoenix_live_view.esm.js +81 -76
- package/priv/static/phoenix_live_view.esm.js.map +2 -2
- package/priv/static/phoenix_live_view.js +81 -76
- package/priv/static/phoenix_live_view.min.js +6 -6
- package/assets/package.json +0 -29
|
@@ -192,6 +192,16 @@ function detectDuplicateIds() {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
+
function detectInvalidStreamInserts(inserts) {
|
|
196
|
+
const errors = /* @__PURE__ */ new Set();
|
|
197
|
+
Object.keys(inserts).forEach((id) => {
|
|
198
|
+
const streamEl = document.getElementById(id);
|
|
199
|
+
if (streamEl && streamEl.parentElement && streamEl.parentElement.getAttribute("phx-update") !== "stream") {
|
|
200
|
+
errors.add(`The stream container with id "${streamEl.parentElement.id}" is missing the phx-update="stream" attribute. Ensure it is set for streams to work properly.`);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
errors.forEach((error) => console.error(error));
|
|
204
|
+
}
|
|
195
205
|
var debug = (view, kind, msg, obj) => {
|
|
196
206
|
if (view.liveSocket.isDebugEnabled()) {
|
|
197
207
|
console.log(`${view.id} ${kind}: ${msg} - `, obj);
|
|
@@ -418,7 +428,7 @@ var DOM = {
|
|
|
418
428
|
cids.forEach((cid) => {
|
|
419
429
|
this.filterWithinSameLiveView(this.all(node, `[${PHX_COMPONENT}="${cid}"]`), node).forEach((parent) => {
|
|
420
430
|
parentCids.add(cid);
|
|
421
|
-
this.all(parent, `[${PHX_COMPONENT}]`).map((el) => parseInt(el.getAttribute(PHX_COMPONENT))).forEach((childCID) => childrenCids.add(childCID));
|
|
431
|
+
this.filterWithinSameLiveView(this.all(parent, `[${PHX_COMPONENT}]`), parent).map((el) => parseInt(el.getAttribute(PHX_COMPONENT))).forEach((childCID) => childrenCids.add(childCID));
|
|
422
432
|
});
|
|
423
433
|
});
|
|
424
434
|
childrenCids.forEach((childCid) => parentCids.delete(childCid));
|
|
@@ -795,6 +805,9 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
795
805
|
return;
|
|
796
806
|
}
|
|
797
807
|
ops.forEach(([name, op, _stashed]) => this.putSticky(el, name, op));
|
|
808
|
+
},
|
|
809
|
+
isLocked(el) {
|
|
810
|
+
return el.hasAttribute && el.hasAttribute(PHX_REF_LOCK);
|
|
798
811
|
}
|
|
799
812
|
};
|
|
800
813
|
var dom_default = DOM;
|
|
@@ -1137,8 +1150,22 @@ var Hooks = {
|
|
|
1137
1150
|
mounted() {
|
|
1138
1151
|
this.focusStart = this.el.firstElementChild;
|
|
1139
1152
|
this.focusEnd = this.el.lastElementChild;
|
|
1140
|
-
this.focusStart.addEventListener("focus", () =>
|
|
1141
|
-
|
|
1153
|
+
this.focusStart.addEventListener("focus", (e) => {
|
|
1154
|
+
if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
|
|
1155
|
+
const nextFocus = e.target.nextElementSibling;
|
|
1156
|
+
aria_default.attemptFocus(nextFocus) || aria_default.focusFirst(nextFocus);
|
|
1157
|
+
} else {
|
|
1158
|
+
aria_default.focusLast(this.el);
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
this.focusEnd.addEventListener("focus", (e) => {
|
|
1162
|
+
if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
|
|
1163
|
+
const nextFocus = e.target.previousElementSibling;
|
|
1164
|
+
aria_default.attemptFocus(nextFocus) || aria_default.focusLast(nextFocus);
|
|
1165
|
+
} else {
|
|
1166
|
+
aria_default.focusFirst(this.el);
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1142
1169
|
this.el.addEventListener("phx:show-end", () => this.el.focus());
|
|
1143
1170
|
if (window.getComputedStyle(this.el).display !== "none") {
|
|
1144
1171
|
aria_default.focusFirst(this.el);
|
|
@@ -1287,6 +1314,16 @@ var hooks_default = Hooks;
|
|
|
1287
1314
|
|
|
1288
1315
|
// js/phoenix_live_view/element_ref.js
|
|
1289
1316
|
var ElementRef = class {
|
|
1317
|
+
static onUnlock(el, callback) {
|
|
1318
|
+
if (!dom_default.isLocked(el) && !el.closest(`[${PHX_REF_LOCK}]`)) {
|
|
1319
|
+
return callback();
|
|
1320
|
+
}
|
|
1321
|
+
const closestLock = el.closest(`[${PHX_REF_LOCK}]`);
|
|
1322
|
+
const ref = closestLock.closest(`[${PHX_REF_LOCK}]`).getAttribute(PHX_REF_LOCK);
|
|
1323
|
+
closestLock.addEventListener(`phx:undo-lock:${ref}`, () => {
|
|
1324
|
+
callback();
|
|
1325
|
+
}, { once: true });
|
|
1326
|
+
}
|
|
1290
1327
|
constructor(el) {
|
|
1291
1328
|
this.el = el;
|
|
1292
1329
|
this.loadingRef = el.hasAttribute(PHX_REF_LOADING) ? parseInt(el.getAttribute(PHX_REF_LOADING), 10) : null;
|
|
@@ -1938,37 +1975,7 @@ var morphdom_esm_default = morphdom;
|
|
|
1938
1975
|
|
|
1939
1976
|
// js/phoenix_live_view/dom_patch.js
|
|
1940
1977
|
var DOMPatch = class {
|
|
1941
|
-
|
|
1942
|
-
let focused = liveSocket.getActiveElement();
|
|
1943
|
-
let { selectionStart, selectionEnd } = focused && dom_default.hasSelectionRange(focused) ? focused : {};
|
|
1944
|
-
let phxUpdate = liveSocket.binding(PHX_UPDATE);
|
|
1945
|
-
let externalFormTriggered = null;
|
|
1946
|
-
morphdom_esm_default(container, clonedTree, {
|
|
1947
|
-
childrenOnly: false,
|
|
1948
|
-
onBeforeElUpdated: (fromEl, toEl) => {
|
|
1949
|
-
dom_default.syncPendingAttrs(fromEl, toEl);
|
|
1950
|
-
if (!container.isSameNode(fromEl) && fromEl.hasAttribute(PHX_REF_LOCK)) {
|
|
1951
|
-
return false;
|
|
1952
|
-
}
|
|
1953
|
-
if (dom_default.isIgnored(fromEl, phxUpdate)) {
|
|
1954
|
-
return false;
|
|
1955
|
-
}
|
|
1956
|
-
if (focused && focused.isSameNode(fromEl) && dom_default.isFormInput(fromEl)) {
|
|
1957
|
-
dom_default.mergeFocusedInput(fromEl, toEl);
|
|
1958
|
-
return false;
|
|
1959
|
-
}
|
|
1960
|
-
if (dom_default.isNowTriggerFormExternal(toEl, liveSocket.binding(PHX_TRIGGER_ACTION))) {
|
|
1961
|
-
externalFormTriggered = toEl;
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
});
|
|
1965
|
-
if (externalFormTriggered) {
|
|
1966
|
-
liveSocket.unload();
|
|
1967
|
-
Object.getPrototypeOf(externalFormTriggered).submit.call(externalFormTriggered);
|
|
1968
|
-
}
|
|
1969
|
-
liveSocket.silenceEvents(() => dom_default.restoreFocus(focused, selectionStart, selectionEnd));
|
|
1970
|
-
}
|
|
1971
|
-
constructor(view, container, id, html, streams, targetCID) {
|
|
1978
|
+
constructor(view, container, id, html, streams, targetCID, opts = {}) {
|
|
1972
1979
|
this.view = view;
|
|
1973
1980
|
this.liveSocket = view.liveSocket;
|
|
1974
1981
|
this.container = container;
|
|
@@ -1993,6 +2000,8 @@ var DOMPatch = class {
|
|
|
1993
2000
|
afterphxChildAdded: [],
|
|
1994
2001
|
aftertransitionsDiscarded: []
|
|
1995
2002
|
};
|
|
2003
|
+
this.withChildren = opts.withChildren || opts.undoRef || false;
|
|
2004
|
+
this.undoRef = opts.undoRef;
|
|
1996
2005
|
}
|
|
1997
2006
|
before(kind, callback) {
|
|
1998
2007
|
this.callbacks[`before${kind}`].push(callback);
|
|
@@ -2027,7 +2036,7 @@ var DOMPatch = class {
|
|
|
2027
2036
|
let updates = [];
|
|
2028
2037
|
let appendPrependUpdates = [];
|
|
2029
2038
|
let externalFormTriggered = null;
|
|
2030
|
-
function morph(targetContainer2, source, withChildren =
|
|
2039
|
+
function morph(targetContainer2, source, withChildren = this.withChildren) {
|
|
2031
2040
|
let morphCallbacks = {
|
|
2032
2041
|
// normally, we are running with childrenOnly, as the patch HTML for a LV
|
|
2033
2042
|
// does not include the LV attrs (data-phx-session, etc.)
|
|
@@ -2153,7 +2162,7 @@ var DOMPatch = class {
|
|
|
2153
2162
|
}
|
|
2154
2163
|
let isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl);
|
|
2155
2164
|
let focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
2156
|
-
if (fromEl.hasAttribute(PHX_REF_SRC)) {
|
|
2165
|
+
if (fromEl.hasAttribute(PHX_REF_SRC) && fromEl.getAttribute(PHX_REF_LOCK) != this.undoRef) {
|
|
2157
2166
|
if (dom_default.isUploadInput(fromEl)) {
|
|
2158
2167
|
dom_default.mergeAttrs(fromEl, toEl, { isIgnored: true });
|
|
2159
2168
|
this.trackBefore("updated", fromEl, toEl);
|
|
@@ -2237,6 +2246,7 @@ var DOMPatch = class {
|
|
|
2237
2246
|
});
|
|
2238
2247
|
if (liveSocket.isDebugEnabled()) {
|
|
2239
2248
|
detectDuplicateIds();
|
|
2249
|
+
detectInvalidStreamInserts(this.streamInserts);
|
|
2240
2250
|
Array.from(document.querySelectorAll("input[name=id]")).forEach((node) => {
|
|
2241
2251
|
if (node.form) {
|
|
2242
2252
|
console.error('Detected an input with name="id" inside a form! This will cause problems when patching the DOM.\n', node);
|
|
@@ -3571,7 +3581,11 @@ var View = class _View {
|
|
|
3571
3581
|
this.formsForRecovery = this.getFormsForRecovery();
|
|
3572
3582
|
}
|
|
3573
3583
|
if (this.isMain() && window.history.state === null) {
|
|
3574
|
-
|
|
3584
|
+
browser_default.pushState("replace", {
|
|
3585
|
+
type: "patch",
|
|
3586
|
+
id: this.id,
|
|
3587
|
+
position: this.liveSocket.currentHistoryPosition
|
|
3588
|
+
});
|
|
3575
3589
|
}
|
|
3576
3590
|
if (liveview_version !== this.liveSocket.version()) {
|
|
3577
3591
|
console.error(`LiveView asset version mismatch. JavaScript version ${this.liveSocket.version()} vs. server ${liveview_version}. To avoid issues, please ensure that your assets use the same version as the server.`);
|
|
@@ -3893,6 +3907,9 @@ var View = class _View {
|
|
|
3893
3907
|
}
|
|
3894
3908
|
addHook(el) {
|
|
3895
3909
|
let hookElId = ViewHook.elementID(el);
|
|
3910
|
+
if (el.getAttribute && !this.ownsElement(el)) {
|
|
3911
|
+
return;
|
|
3912
|
+
}
|
|
3896
3913
|
if (hookElId && !this.viewHooks[hookElId]) {
|
|
3897
3914
|
let hook = dom_default.getCustomElHook(el) || logError(`no hook found for custom element: ${el.id}`);
|
|
3898
3915
|
this.viewHooks[hookElId] = hook;
|
|
@@ -3902,9 +3919,6 @@ var View = class _View {
|
|
|
3902
3919
|
return;
|
|
3903
3920
|
} else {
|
|
3904
3921
|
let hookName = el.getAttribute(`data-phx-${PHX_HOOK}`) || el.getAttribute(this.binding(PHX_HOOK));
|
|
3905
|
-
if (hookName && !this.ownsElement(el)) {
|
|
3906
|
-
return;
|
|
3907
|
-
}
|
|
3908
3922
|
let callbacks = this.liveSocket.getHookCallbacks(hookName);
|
|
3909
3923
|
if (callbacks) {
|
|
3910
3924
|
if (!el.id) {
|
|
@@ -3919,9 +3933,10 @@ var View = class _View {
|
|
|
3919
3933
|
}
|
|
3920
3934
|
}
|
|
3921
3935
|
destroyHook(hook) {
|
|
3936
|
+
const hookId = ViewHook.elementID(hook.el);
|
|
3922
3937
|
hook.__destroyed();
|
|
3923
3938
|
hook.__cleanup__();
|
|
3924
|
-
delete this.viewHooks[
|
|
3939
|
+
delete this.viewHooks[hookId];
|
|
3925
3940
|
}
|
|
3926
3941
|
applyPendingUpdates() {
|
|
3927
3942
|
this.pendingDiffs.forEach(({ diff, events }) => this.update(diff, events));
|
|
@@ -4171,12 +4186,11 @@ var View = class _View {
|
|
|
4171
4186
|
undoElRef(el, ref, phxEvent) {
|
|
4172
4187
|
let elRef = new ElementRef(el);
|
|
4173
4188
|
elRef.maybeUndo(ref, phxEvent, (clonedTree) => {
|
|
4174
|
-
let
|
|
4175
|
-
|
|
4189
|
+
let patch = new DOMPatch(this, el, this.id, clonedTree, [], null, { undoRef: ref });
|
|
4190
|
+
const phxChildrenAdded = this.performPatch(patch, true);
|
|
4176
4191
|
dom_default.all(el, `[${PHX_REF_SRC}="${this.refSrc()}"]`, (child) => this.undoElRef(child, ref, phxEvent));
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
hook.__updated();
|
|
4192
|
+
if (phxChildrenAdded) {
|
|
4193
|
+
this.joinNewChildren();
|
|
4180
4194
|
}
|
|
4181
4195
|
});
|
|
4182
4196
|
}
|
|
@@ -4392,15 +4406,17 @@ var View = class _View {
|
|
|
4392
4406
|
};
|
|
4393
4407
|
this.pushWithReply(refGenerator, "event", event).then(({ resp }) => {
|
|
4394
4408
|
if (dom_default.isUploadInput(inputEl) && dom_default.isAutoUpload(inputEl)) {
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4409
|
+
ElementRef.onUnlock(inputEl, () => {
|
|
4410
|
+
if (LiveUploader.filesAwaitingPreflight(inputEl).length > 0) {
|
|
4411
|
+
let [ref, _els] = refGenerator();
|
|
4412
|
+
this.undoRefs(ref, phxEvent, [inputEl.form]);
|
|
4413
|
+
this.uploadFiles(inputEl.form, phxEvent, targetCtx, ref, cid, (_uploads) => {
|
|
4414
|
+
callback && callback(resp);
|
|
4415
|
+
this.triggerAwaitingSubmit(inputEl.form, phxEvent);
|
|
4416
|
+
this.undoRefs(ref, phxEvent);
|
|
4417
|
+
});
|
|
4418
|
+
}
|
|
4419
|
+
});
|
|
4404
4420
|
} else {
|
|
4405
4421
|
callback && callback(resp);
|
|
4406
4422
|
}
|
|
@@ -4751,7 +4767,7 @@ var LiveSocket = class {
|
|
|
4751
4767
|
}
|
|
4752
4768
|
// public
|
|
4753
4769
|
version() {
|
|
4754
|
-
return "1.0.
|
|
4770
|
+
return "1.0.3";
|
|
4755
4771
|
}
|
|
4756
4772
|
isProfileEnabled() {
|
|
4757
4773
|
return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
|
|
@@ -4948,7 +4964,9 @@ var LiveSocket = class {
|
|
|
4948
4964
|
dom_default.all(document, `${PHX_VIEW_SELECTOR}:not([${PHX_PARENT_ID}])`, (rootEl) => {
|
|
4949
4965
|
if (!this.getRootById(rootEl.id)) {
|
|
4950
4966
|
let view = this.newRootView(rootEl);
|
|
4951
|
-
|
|
4967
|
+
if (!dom_default.isPhxSticky(rootEl)) {
|
|
4968
|
+
view.setHref(this.getHref());
|
|
4969
|
+
}
|
|
4952
4970
|
view.join();
|
|
4953
4971
|
if (rootEl.hasAttribute(PHX_MAIN)) {
|
|
4954
4972
|
this.main = view;
|
|
@@ -5262,7 +5280,7 @@ var LiveSocket = class {
|
|
|
5262
5280
|
if (!this.registerNewLocation(window.location)) {
|
|
5263
5281
|
return;
|
|
5264
5282
|
}
|
|
5265
|
-
let { type, backType, id,
|
|
5283
|
+
let { type, backType, id, scroll, position } = event.state || {};
|
|
5266
5284
|
let href = window.location.href;
|
|
5267
5285
|
let isForward = position > this.currentHistoryPosition;
|
|
5268
5286
|
type = isForward ? type : backType || type;
|
|
@@ -5270,17 +5288,13 @@ var LiveSocket = class {
|
|
|
5270
5288
|
this.sessionStorage.setItem(PHX_LV_HISTORY_POSITION, this.currentHistoryPosition.toString());
|
|
5271
5289
|
dom_default.dispatchEvent(window, "phx:navigate", { detail: { href, patch: type === "patch", pop: true, direction: isForward ? "forward" : "backward" } });
|
|
5272
5290
|
this.requestDOMUpdate(() => {
|
|
5291
|
+
const callback = () => {
|
|
5292
|
+
this.maybeScroll(scroll);
|
|
5293
|
+
};
|
|
5273
5294
|
if (this.main.isConnected() && (type === "patch" && id === this.main.id)) {
|
|
5274
|
-
this.main.pushLinkPatch(event, href, null,
|
|
5275
|
-
this.maybeScroll(scroll);
|
|
5276
|
-
});
|
|
5295
|
+
this.main.pushLinkPatch(event, href, null, callback);
|
|
5277
5296
|
} else {
|
|
5278
|
-
this.replaceMain(href, null,
|
|
5279
|
-
if (root) {
|
|
5280
|
-
this.replaceRootHistory();
|
|
5281
|
-
}
|
|
5282
|
-
this.maybeScroll(scroll);
|
|
5283
|
-
});
|
|
5297
|
+
this.replaceMain(href, null, callback);
|
|
5284
5298
|
}
|
|
5285
5299
|
});
|
|
5286
5300
|
}, false);
|
|
@@ -5387,15 +5401,6 @@ var LiveSocket = class {
|
|
|
5387
5401
|
});
|
|
5388
5402
|
});
|
|
5389
5403
|
}
|
|
5390
|
-
replaceRootHistory() {
|
|
5391
|
-
browser_default.pushState("replace", {
|
|
5392
|
-
root: true,
|
|
5393
|
-
type: "patch",
|
|
5394
|
-
id: this.main.id,
|
|
5395
|
-
position: this.currentHistoryPosition
|
|
5396
|
-
// Preserve current position
|
|
5397
|
-
});
|
|
5398
|
-
}
|
|
5399
5404
|
registerNewLocation(newLocation) {
|
|
5400
5405
|
let { pathname, search } = this.currentLocation;
|
|
5401
5406
|
if (pathname + search === newLocation.pathname + newLocation.search) {
|