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
|
@@ -165,6 +165,16 @@ function detectDuplicateIds() {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
+
function detectInvalidStreamInserts(inserts) {
|
|
169
|
+
const errors = /* @__PURE__ */ new Set();
|
|
170
|
+
Object.keys(inserts).forEach((id) => {
|
|
171
|
+
const streamEl = document.getElementById(id);
|
|
172
|
+
if (streamEl && streamEl.parentElement && streamEl.parentElement.getAttribute("phx-update") !== "stream") {
|
|
173
|
+
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.`);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
errors.forEach((error) => console.error(error));
|
|
177
|
+
}
|
|
168
178
|
var debug = (view, kind, msg, obj) => {
|
|
169
179
|
if (view.liveSocket.isDebugEnabled()) {
|
|
170
180
|
console.log(`${view.id} ${kind}: ${msg} - `, obj);
|
|
@@ -391,7 +401,7 @@ var DOM = {
|
|
|
391
401
|
cids.forEach((cid) => {
|
|
392
402
|
this.filterWithinSameLiveView(this.all(node, `[${PHX_COMPONENT}="${cid}"]`), node).forEach((parent) => {
|
|
393
403
|
parentCids.add(cid);
|
|
394
|
-
this.all(parent, `[${PHX_COMPONENT}]`).map((el) => parseInt(el.getAttribute(PHX_COMPONENT))).forEach((childCID) => childrenCids.add(childCID));
|
|
404
|
+
this.filterWithinSameLiveView(this.all(parent, `[${PHX_COMPONENT}]`), parent).map((el) => parseInt(el.getAttribute(PHX_COMPONENT))).forEach((childCID) => childrenCids.add(childCID));
|
|
395
405
|
});
|
|
396
406
|
});
|
|
397
407
|
childrenCids.forEach((childCid) => parentCids.delete(childCid));
|
|
@@ -768,6 +778,9 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
768
778
|
return;
|
|
769
779
|
}
|
|
770
780
|
ops.forEach(([name, op, _stashed]) => this.putSticky(el, name, op));
|
|
781
|
+
},
|
|
782
|
+
isLocked(el) {
|
|
783
|
+
return el.hasAttribute && el.hasAttribute(PHX_REF_LOCK);
|
|
771
784
|
}
|
|
772
785
|
};
|
|
773
786
|
var dom_default = DOM;
|
|
@@ -1110,8 +1123,22 @@ var Hooks = {
|
|
|
1110
1123
|
mounted() {
|
|
1111
1124
|
this.focusStart = this.el.firstElementChild;
|
|
1112
1125
|
this.focusEnd = this.el.lastElementChild;
|
|
1113
|
-
this.focusStart.addEventListener("focus", () =>
|
|
1114
|
-
|
|
1126
|
+
this.focusStart.addEventListener("focus", (e) => {
|
|
1127
|
+
if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
|
|
1128
|
+
const nextFocus = e.target.nextElementSibling;
|
|
1129
|
+
aria_default.attemptFocus(nextFocus) || aria_default.focusFirst(nextFocus);
|
|
1130
|
+
} else {
|
|
1131
|
+
aria_default.focusLast(this.el);
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
this.focusEnd.addEventListener("focus", (e) => {
|
|
1135
|
+
if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
|
|
1136
|
+
const nextFocus = e.target.previousElementSibling;
|
|
1137
|
+
aria_default.attemptFocus(nextFocus) || aria_default.focusLast(nextFocus);
|
|
1138
|
+
} else {
|
|
1139
|
+
aria_default.focusFirst(this.el);
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1115
1142
|
this.el.addEventListener("phx:show-end", () => this.el.focus());
|
|
1116
1143
|
if (window.getComputedStyle(this.el).display !== "none") {
|
|
1117
1144
|
aria_default.focusFirst(this.el);
|
|
@@ -1260,6 +1287,16 @@ var hooks_default = Hooks;
|
|
|
1260
1287
|
|
|
1261
1288
|
// js/phoenix_live_view/element_ref.js
|
|
1262
1289
|
var ElementRef = class {
|
|
1290
|
+
static onUnlock(el, callback) {
|
|
1291
|
+
if (!dom_default.isLocked(el) && !el.closest(`[${PHX_REF_LOCK}]`)) {
|
|
1292
|
+
return callback();
|
|
1293
|
+
}
|
|
1294
|
+
const closestLock = el.closest(`[${PHX_REF_LOCK}]`);
|
|
1295
|
+
const ref = closestLock.closest(`[${PHX_REF_LOCK}]`).getAttribute(PHX_REF_LOCK);
|
|
1296
|
+
closestLock.addEventListener(`phx:undo-lock:${ref}`, () => {
|
|
1297
|
+
callback();
|
|
1298
|
+
}, { once: true });
|
|
1299
|
+
}
|
|
1263
1300
|
constructor(el) {
|
|
1264
1301
|
this.el = el;
|
|
1265
1302
|
this.loadingRef = el.hasAttribute(PHX_REF_LOADING) ? parseInt(el.getAttribute(PHX_REF_LOADING), 10) : null;
|
|
@@ -1911,37 +1948,7 @@ var morphdom_esm_default = morphdom;
|
|
|
1911
1948
|
|
|
1912
1949
|
// js/phoenix_live_view/dom_patch.js
|
|
1913
1950
|
var DOMPatch = class {
|
|
1914
|
-
|
|
1915
|
-
let focused = liveSocket.getActiveElement();
|
|
1916
|
-
let { selectionStart, selectionEnd } = focused && dom_default.hasSelectionRange(focused) ? focused : {};
|
|
1917
|
-
let phxUpdate = liveSocket.binding(PHX_UPDATE);
|
|
1918
|
-
let externalFormTriggered = null;
|
|
1919
|
-
morphdom_esm_default(container, clonedTree, {
|
|
1920
|
-
childrenOnly: false,
|
|
1921
|
-
onBeforeElUpdated: (fromEl, toEl) => {
|
|
1922
|
-
dom_default.syncPendingAttrs(fromEl, toEl);
|
|
1923
|
-
if (!container.isSameNode(fromEl) && fromEl.hasAttribute(PHX_REF_LOCK)) {
|
|
1924
|
-
return false;
|
|
1925
|
-
}
|
|
1926
|
-
if (dom_default.isIgnored(fromEl, phxUpdate)) {
|
|
1927
|
-
return false;
|
|
1928
|
-
}
|
|
1929
|
-
if (focused && focused.isSameNode(fromEl) && dom_default.isFormInput(fromEl)) {
|
|
1930
|
-
dom_default.mergeFocusedInput(fromEl, toEl);
|
|
1931
|
-
return false;
|
|
1932
|
-
}
|
|
1933
|
-
if (dom_default.isNowTriggerFormExternal(toEl, liveSocket.binding(PHX_TRIGGER_ACTION))) {
|
|
1934
|
-
externalFormTriggered = toEl;
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
});
|
|
1938
|
-
if (externalFormTriggered) {
|
|
1939
|
-
liveSocket.unload();
|
|
1940
|
-
Object.getPrototypeOf(externalFormTriggered).submit.call(externalFormTriggered);
|
|
1941
|
-
}
|
|
1942
|
-
liveSocket.silenceEvents(() => dom_default.restoreFocus(focused, selectionStart, selectionEnd));
|
|
1943
|
-
}
|
|
1944
|
-
constructor(view, container, id, html, streams, targetCID) {
|
|
1951
|
+
constructor(view, container, id, html, streams, targetCID, opts = {}) {
|
|
1945
1952
|
this.view = view;
|
|
1946
1953
|
this.liveSocket = view.liveSocket;
|
|
1947
1954
|
this.container = container;
|
|
@@ -1966,6 +1973,8 @@ var DOMPatch = class {
|
|
|
1966
1973
|
afterphxChildAdded: [],
|
|
1967
1974
|
aftertransitionsDiscarded: []
|
|
1968
1975
|
};
|
|
1976
|
+
this.withChildren = opts.withChildren || opts.undoRef || false;
|
|
1977
|
+
this.undoRef = opts.undoRef;
|
|
1969
1978
|
}
|
|
1970
1979
|
before(kind, callback) {
|
|
1971
1980
|
this.callbacks[`before${kind}`].push(callback);
|
|
@@ -2000,7 +2009,7 @@ var DOMPatch = class {
|
|
|
2000
2009
|
let updates = [];
|
|
2001
2010
|
let appendPrependUpdates = [];
|
|
2002
2011
|
let externalFormTriggered = null;
|
|
2003
|
-
function morph(targetContainer2, source, withChildren =
|
|
2012
|
+
function morph(targetContainer2, source, withChildren = this.withChildren) {
|
|
2004
2013
|
let morphCallbacks = {
|
|
2005
2014
|
// normally, we are running with childrenOnly, as the patch HTML for a LV
|
|
2006
2015
|
// does not include the LV attrs (data-phx-session, etc.)
|
|
@@ -2126,7 +2135,7 @@ var DOMPatch = class {
|
|
|
2126
2135
|
}
|
|
2127
2136
|
let isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl);
|
|
2128
2137
|
let focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
|
|
2129
|
-
if (fromEl.hasAttribute(PHX_REF_SRC)) {
|
|
2138
|
+
if (fromEl.hasAttribute(PHX_REF_SRC) && fromEl.getAttribute(PHX_REF_LOCK) != this.undoRef) {
|
|
2130
2139
|
if (dom_default.isUploadInput(fromEl)) {
|
|
2131
2140
|
dom_default.mergeAttrs(fromEl, toEl, { isIgnored: true });
|
|
2132
2141
|
this.trackBefore("updated", fromEl, toEl);
|
|
@@ -2210,6 +2219,7 @@ var DOMPatch = class {
|
|
|
2210
2219
|
});
|
|
2211
2220
|
if (liveSocket.isDebugEnabled()) {
|
|
2212
2221
|
detectDuplicateIds();
|
|
2222
|
+
detectInvalidStreamInserts(this.streamInserts);
|
|
2213
2223
|
Array.from(document.querySelectorAll("input[name=id]")).forEach((node) => {
|
|
2214
2224
|
if (node.form) {
|
|
2215
2225
|
console.error('Detected an input with name="id" inside a form! This will cause problems when patching the DOM.\n', node);
|
|
@@ -3544,7 +3554,11 @@ var View = class _View {
|
|
|
3544
3554
|
this.formsForRecovery = this.getFormsForRecovery();
|
|
3545
3555
|
}
|
|
3546
3556
|
if (this.isMain() && window.history.state === null) {
|
|
3547
|
-
|
|
3557
|
+
browser_default.pushState("replace", {
|
|
3558
|
+
type: "patch",
|
|
3559
|
+
id: this.id,
|
|
3560
|
+
position: this.liveSocket.currentHistoryPosition
|
|
3561
|
+
});
|
|
3548
3562
|
}
|
|
3549
3563
|
if (liveview_version !== this.liveSocket.version()) {
|
|
3550
3564
|
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.`);
|
|
@@ -3866,6 +3880,9 @@ var View = class _View {
|
|
|
3866
3880
|
}
|
|
3867
3881
|
addHook(el) {
|
|
3868
3882
|
let hookElId = ViewHook.elementID(el);
|
|
3883
|
+
if (el.getAttribute && !this.ownsElement(el)) {
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3869
3886
|
if (hookElId && !this.viewHooks[hookElId]) {
|
|
3870
3887
|
let hook = dom_default.getCustomElHook(el) || logError(`no hook found for custom element: ${el.id}`);
|
|
3871
3888
|
this.viewHooks[hookElId] = hook;
|
|
@@ -3875,9 +3892,6 @@ var View = class _View {
|
|
|
3875
3892
|
return;
|
|
3876
3893
|
} else {
|
|
3877
3894
|
let hookName = el.getAttribute(`data-phx-${PHX_HOOK}`) || el.getAttribute(this.binding(PHX_HOOK));
|
|
3878
|
-
if (hookName && !this.ownsElement(el)) {
|
|
3879
|
-
return;
|
|
3880
|
-
}
|
|
3881
3895
|
let callbacks = this.liveSocket.getHookCallbacks(hookName);
|
|
3882
3896
|
if (callbacks) {
|
|
3883
3897
|
if (!el.id) {
|
|
@@ -3892,9 +3906,10 @@ var View = class _View {
|
|
|
3892
3906
|
}
|
|
3893
3907
|
}
|
|
3894
3908
|
destroyHook(hook) {
|
|
3909
|
+
const hookId = ViewHook.elementID(hook.el);
|
|
3895
3910
|
hook.__destroyed();
|
|
3896
3911
|
hook.__cleanup__();
|
|
3897
|
-
delete this.viewHooks[
|
|
3912
|
+
delete this.viewHooks[hookId];
|
|
3898
3913
|
}
|
|
3899
3914
|
applyPendingUpdates() {
|
|
3900
3915
|
this.pendingDiffs.forEach(({ diff, events }) => this.update(diff, events));
|
|
@@ -4144,12 +4159,11 @@ var View = class _View {
|
|
|
4144
4159
|
undoElRef(el, ref, phxEvent) {
|
|
4145
4160
|
let elRef = new ElementRef(el);
|
|
4146
4161
|
elRef.maybeUndo(ref, phxEvent, (clonedTree) => {
|
|
4147
|
-
let
|
|
4148
|
-
|
|
4162
|
+
let patch = new DOMPatch(this, el, this.id, clonedTree, [], null, { undoRef: ref });
|
|
4163
|
+
const phxChildrenAdded = this.performPatch(patch, true);
|
|
4149
4164
|
dom_default.all(el, `[${PHX_REF_SRC}="${this.refSrc()}"]`, (child) => this.undoElRef(child, ref, phxEvent));
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
hook.__updated();
|
|
4165
|
+
if (phxChildrenAdded) {
|
|
4166
|
+
this.joinNewChildren();
|
|
4153
4167
|
}
|
|
4154
4168
|
});
|
|
4155
4169
|
}
|
|
@@ -4365,15 +4379,17 @@ var View = class _View {
|
|
|
4365
4379
|
};
|
|
4366
4380
|
this.pushWithReply(refGenerator, "event", event).then(({ resp }) => {
|
|
4367
4381
|
if (dom_default.isUploadInput(inputEl) && dom_default.isAutoUpload(inputEl)) {
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4382
|
+
ElementRef.onUnlock(inputEl, () => {
|
|
4383
|
+
if (LiveUploader.filesAwaitingPreflight(inputEl).length > 0) {
|
|
4384
|
+
let [ref, _els] = refGenerator();
|
|
4385
|
+
this.undoRefs(ref, phxEvent, [inputEl.form]);
|
|
4386
|
+
this.uploadFiles(inputEl.form, phxEvent, targetCtx, ref, cid, (_uploads) => {
|
|
4387
|
+
callback && callback(resp);
|
|
4388
|
+
this.triggerAwaitingSubmit(inputEl.form, phxEvent);
|
|
4389
|
+
this.undoRefs(ref, phxEvent);
|
|
4390
|
+
});
|
|
4391
|
+
}
|
|
4392
|
+
});
|
|
4377
4393
|
} else {
|
|
4378
4394
|
callback && callback(resp);
|
|
4379
4395
|
}
|
|
@@ -4724,7 +4740,7 @@ var LiveSocket = class {
|
|
|
4724
4740
|
}
|
|
4725
4741
|
// public
|
|
4726
4742
|
version() {
|
|
4727
|
-
return "1.0.
|
|
4743
|
+
return "1.0.3";
|
|
4728
4744
|
}
|
|
4729
4745
|
isProfileEnabled() {
|
|
4730
4746
|
return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
|
|
@@ -4921,7 +4937,9 @@ var LiveSocket = class {
|
|
|
4921
4937
|
dom_default.all(document, `${PHX_VIEW_SELECTOR}:not([${PHX_PARENT_ID}])`, (rootEl) => {
|
|
4922
4938
|
if (!this.getRootById(rootEl.id)) {
|
|
4923
4939
|
let view = this.newRootView(rootEl);
|
|
4924
|
-
|
|
4940
|
+
if (!dom_default.isPhxSticky(rootEl)) {
|
|
4941
|
+
view.setHref(this.getHref());
|
|
4942
|
+
}
|
|
4925
4943
|
view.join();
|
|
4926
4944
|
if (rootEl.hasAttribute(PHX_MAIN)) {
|
|
4927
4945
|
this.main = view;
|
|
@@ -5235,7 +5253,7 @@ var LiveSocket = class {
|
|
|
5235
5253
|
if (!this.registerNewLocation(window.location)) {
|
|
5236
5254
|
return;
|
|
5237
5255
|
}
|
|
5238
|
-
let { type, backType, id,
|
|
5256
|
+
let { type, backType, id, scroll, position } = event.state || {};
|
|
5239
5257
|
let href = window.location.href;
|
|
5240
5258
|
let isForward = position > this.currentHistoryPosition;
|
|
5241
5259
|
type = isForward ? type : backType || type;
|
|
@@ -5243,17 +5261,13 @@ var LiveSocket = class {
|
|
|
5243
5261
|
this.sessionStorage.setItem(PHX_LV_HISTORY_POSITION, this.currentHistoryPosition.toString());
|
|
5244
5262
|
dom_default.dispatchEvent(window, "phx:navigate", { detail: { href, patch: type === "patch", pop: true, direction: isForward ? "forward" : "backward" } });
|
|
5245
5263
|
this.requestDOMUpdate(() => {
|
|
5264
|
+
const callback = () => {
|
|
5265
|
+
this.maybeScroll(scroll);
|
|
5266
|
+
};
|
|
5246
5267
|
if (this.main.isConnected() && (type === "patch" && id === this.main.id)) {
|
|
5247
|
-
this.main.pushLinkPatch(event, href, null,
|
|
5248
|
-
this.maybeScroll(scroll);
|
|
5249
|
-
});
|
|
5268
|
+
this.main.pushLinkPatch(event, href, null, callback);
|
|
5250
5269
|
} else {
|
|
5251
|
-
this.replaceMain(href, null,
|
|
5252
|
-
if (root) {
|
|
5253
|
-
this.replaceRootHistory();
|
|
5254
|
-
}
|
|
5255
|
-
this.maybeScroll(scroll);
|
|
5256
|
-
});
|
|
5270
|
+
this.replaceMain(href, null, callback);
|
|
5257
5271
|
}
|
|
5258
5272
|
});
|
|
5259
5273
|
}, false);
|
|
@@ -5360,15 +5374,6 @@ var LiveSocket = class {
|
|
|
5360
5374
|
});
|
|
5361
5375
|
});
|
|
5362
5376
|
}
|
|
5363
|
-
replaceRootHistory() {
|
|
5364
|
-
browser_default.pushState("replace", {
|
|
5365
|
-
root: true,
|
|
5366
|
-
type: "patch",
|
|
5367
|
-
id: this.main.id,
|
|
5368
|
-
position: this.currentHistoryPosition
|
|
5369
|
-
// Preserve current position
|
|
5370
|
-
});
|
|
5371
|
-
}
|
|
5372
5377
|
registerNewLocation(newLocation) {
|
|
5373
5378
|
let { pathname, search } = this.currentLocation;
|
|
5374
5379
|
if (pathname + search === newLocation.pathname + newLocation.search) {
|