phoenix_live_view 1.0.9 → 1.0.10

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.
@@ -21,7 +21,7 @@ import {
21
21
  detectInvalidStreamInserts,
22
22
  isCid
23
23
  } from "./utils"
24
-
24
+ import ElementRef from "./element_ref"
25
25
  import DOM from "./dom"
26
26
  import DOMPostMorphRestorer from "./dom_post_morph_restorer"
27
27
  import morphdom from "morphdom"
@@ -217,20 +217,23 @@ export default class DOMPatch {
217
217
  // apply any changes that happened while the element was locked.
218
218
  let isFocusedFormEl = focused && fromEl.isSameNode(focused) && DOM.isFormInput(fromEl)
219
219
  let focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl)
220
- // only perform the clone step if this is not a patch that unlocks
221
- if(fromEl.hasAttribute(PHX_REF_SRC) && fromEl.getAttribute(PHX_REF_LOCK) != this.undoRef){
222
- if(DOM.isUploadInput(fromEl)){
223
- DOM.mergeAttrs(fromEl, toEl, {isIgnored: true})
224
- this.trackBefore("updated", fromEl, toEl)
225
- updates.push(fromEl)
226
- }
227
- DOM.applyStickyOperations(fromEl)
228
- let isLocked = fromEl.hasAttribute(PHX_REF_LOCK)
229
- let clone = isLocked ? DOM.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null
230
- if(clone){
231
- DOM.putPrivate(fromEl, PHX_REF_LOCK, clone)
232
- if(!isFocusedFormEl){
233
- fromEl = clone
220
+ if(fromEl.hasAttribute(PHX_REF_SRC)){
221
+ const ref = new ElementRef(fromEl)
222
+ // only perform the clone step if this is not a patch that unlocks
223
+ if(ref.lockRef && (!this.undoRef || !ref.isLockUndoneBy(this.undoRef))){
224
+ if(DOM.isUploadInput(fromEl)){
225
+ DOM.mergeAttrs(fromEl, toEl, {isIgnored: true})
226
+ this.trackBefore("updated", fromEl, toEl)
227
+ updates.push(fromEl)
228
+ }
229
+ DOM.applyStickyOperations(fromEl)
230
+ let isLocked = fromEl.hasAttribute(PHX_REF_LOCK)
231
+ let clone = isLocked ? DOM.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null
232
+ if(clone){
233
+ DOM.putPrivate(fromEl, PHX_REF_LOCK, clone)
234
+ if(!isFocusedFormEl){
235
+ fromEl = clone
236
+ }
234
237
  }
235
238
  }
236
239
  }
@@ -24,6 +24,7 @@ import {
24
24
  PHX_ROOT_ID,
25
25
  PHX_SESSION,
26
26
  PHX_STATIC,
27
+ PHX_STICKY,
27
28
  PHX_TRACK_STATIC,
28
29
  PHX_TRACK_UPLOADS,
29
30
  PHX_UPDATE,
@@ -181,6 +182,7 @@ export default class View {
181
182
  session: this.getSession(),
182
183
  static: this.getStatic(),
183
184
  flash: this.flash,
185
+ sticky: this.el.hasAttribute(PHX_STICKY)
184
186
  }
185
187
  })
186
188
  }
@@ -850,7 +852,7 @@ export default class View {
850
852
  return
851
853
  } else if(resp.reason === "unauthorized" || resp.reason === "stale"){
852
854
  this.log("error", () => ["unauthorized live_redirect. Falling back to page request", resp])
853
- this.onRedirect({to: this.root.href})
855
+ this.onRedirect({to: this.root.href, flash: this.flash})
854
856
  return
855
857
  }
856
858
  if(resp.redirect || resp.live_redirect){
@@ -1168,7 +1170,9 @@ export default class View {
1168
1170
  event: phxEvent,
1169
1171
  value: this.extractMeta(el, meta, opts.value),
1170
1172
  cid: this.targetComponentID(el, targetCtx, opts)
1171
- }).then(({reply}) => onReply && onReply(reply))
1173
+ })
1174
+ .then(({reply}) => onReply && onReply(reply))
1175
+ .catch((error) => logError("Failed to push event", error))
1172
1176
  }
1173
1177
 
1174
1178
  pushFileProgress(fileEl, entryRef, progress, onReply = function (){ }){
@@ -1179,7 +1183,9 @@ export default class View {
1179
1183
  entry_ref: entryRef,
1180
1184
  progress: progress,
1181
1185
  cid: view.targetComponentID(fileEl.form, targetCtx)
1182
- }).then(({resp}) => onReply(resp))
1186
+ })
1187
+ .then(({resp}) => onReply(resp))
1188
+ .catch((error) => logError("Failed to push file progress", error))
1183
1189
  })
1184
1190
  }
1185
1191
 
@@ -1244,7 +1250,7 @@ export default class View {
1244
1250
  } else {
1245
1251
  callback && callback(resp)
1246
1252
  }
1247
- })
1253
+ }).catch((error) => logError("Failed to push input event", error))
1248
1254
  }
1249
1255
 
1250
1256
  triggerAwaitingSubmit(formEl, phxEvent){
@@ -1343,7 +1349,9 @@ export default class View {
1343
1349
  value: formData,
1344
1350
  meta: meta,
1345
1351
  cid: cid
1346
- }).then(({resp}) => onReply(resp))
1352
+ })
1353
+ .then(({resp}) => onReply(resp))
1354
+ .catch((error) => logError("Failed to push form submit", error))
1347
1355
  })
1348
1356
  } else if(!(formEl.hasAttribute(PHX_REF_SRC) && formEl.classList.contains("phx-submit-loading"))){
1349
1357
  let meta = this.extractMeta(formEl, {}, opts.value)
@@ -1354,7 +1362,9 @@ export default class View {
1354
1362
  value: formData,
1355
1363
  meta: meta,
1356
1364
  cid: cid
1357
- }).then(({resp}) => onReply(resp))
1365
+ })
1366
+ .then(({resp}) => onReply(resp))
1367
+ .catch((error) => logError("Failed to push form submit", error))
1358
1368
  }
1359
1369
  }
1360
1370
 
@@ -1410,7 +1420,7 @@ export default class View {
1410
1420
  }
1411
1421
  uploader.initAdapterUpload(resp, onError, this.liveSocket)
1412
1422
  }
1413
- })
1423
+ }).catch((error) => logError("Failed to push upload", error))
1414
1424
  })
1415
1425
  }
1416
1426
 
@@ -1546,10 +1556,10 @@ export default class View {
1546
1556
  if(completelyDestroyCIDs.length > 0){
1547
1557
  this.pushWithReply(null, "cids_destroyed", {cids: completelyDestroyCIDs}).then(({resp}) => {
1548
1558
  this.rendered.pruneCIDs(resp.cids)
1549
- })
1559
+ }).catch((error) => logError("Failed to push components destroyed", error))
1550
1560
  }
1551
1561
  })
1552
- })
1562
+ }).catch((error) => logError("Failed to push components destroyed", error))
1553
1563
  }
1554
1564
  }
1555
1565
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phoenix_live_view",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "The Phoenix LiveView JavaScript client.",
5
5
  "license": "MIT",
6
6
  "module": "./priv/static/phoenix_live_view.esm.js",
@@ -24,25 +24,25 @@
24
24
  "assets/js/phoenix_live_view/*"
25
25
  ],
26
26
  "dependencies": {
27
- "morphdom": "2.7.4"
27
+ "morphdom": "2.7.5"
28
28
  },
29
29
  "devDependencies": {
30
- "@babel/cli": "7.26.4",
31
- "@babel/core": "7.26.0",
32
- "@babel/preset-env": "7.26.0",
33
- "@eslint/js": "^9.18.0",
34
- "@playwright/test": "^1.49.1",
35
- "@stylistic/eslint-plugin-js": "^2.12.1",
30
+ "@babel/cli": "7.27.0",
31
+ "@babel/core": "7.26.10",
32
+ "@babel/preset-env": "7.26.9",
33
+ "@eslint/js": "^9.24.0",
34
+ "@playwright/test": "^1.51.1",
35
+ "@stylistic/eslint-plugin-js": "^4.2.0",
36
36
  "css.escape": "^1.5.1",
37
- "eslint": "9.18.0",
38
- "eslint-plugin-jest": "28.10.0",
39
- "eslint-plugin-playwright": "^2.1.0",
40
- "globals": "^15.14.0",
37
+ "eslint": "9.24.0",
38
+ "eslint-plugin-jest": "28.11.0",
39
+ "eslint-plugin-playwright": "^2.2.0",
40
+ "globals": "^16.0.0",
41
41
  "jest": "^29.7.0",
42
42
  "jest-environment-jsdom": "^29.7.0",
43
43
  "jest-monocart-coverage": "^1.1.1",
44
- "monocart-reporter": "^2.9.13",
45
- "phoenix": "1.7.18"
44
+ "monocart-reporter": "^2.9.17",
45
+ "phoenix": "1.7.21"
46
46
  },
47
47
  "scripts": {
48
48
  "setup": "mix deps.get && npm install",
@@ -2164,19 +2164,22 @@ var DOMPatch = class {
2164
2164
  }
2165
2165
  let isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl);
2166
2166
  let focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
2167
- if (fromEl.hasAttribute(PHX_REF_SRC) && fromEl.getAttribute(PHX_REF_LOCK) != this.undoRef) {
2168
- if (dom_default.isUploadInput(fromEl)) {
2169
- dom_default.mergeAttrs(fromEl, toEl, { isIgnored: true });
2170
- this.trackBefore("updated", fromEl, toEl);
2171
- updates.push(fromEl);
2172
- }
2173
- dom_default.applyStickyOperations(fromEl);
2174
- let isLocked = fromEl.hasAttribute(PHX_REF_LOCK);
2175
- let clone2 = isLocked ? dom_default.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null;
2176
- if (clone2) {
2177
- dom_default.putPrivate(fromEl, PHX_REF_LOCK, clone2);
2178
- if (!isFocusedFormEl) {
2179
- fromEl = clone2;
2167
+ if (fromEl.hasAttribute(PHX_REF_SRC)) {
2168
+ const ref = new ElementRef(fromEl);
2169
+ if (ref.lockRef && (!this.undoRef || !ref.isLockUndoneBy(this.undoRef))) {
2170
+ if (dom_default.isUploadInput(fromEl)) {
2171
+ dom_default.mergeAttrs(fromEl, toEl, { isIgnored: true });
2172
+ this.trackBefore("updated", fromEl, toEl);
2173
+ updates.push(fromEl);
2174
+ }
2175
+ dom_default.applyStickyOperations(fromEl);
2176
+ let isLocked = fromEl.hasAttribute(PHX_REF_LOCK);
2177
+ let clone2 = isLocked ? dom_default.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null;
2178
+ if (clone2) {
2179
+ dom_default.putPrivate(fromEl, PHX_REF_LOCK, clone2);
2180
+ if (!isFocusedFormEl) {
2181
+ fromEl = clone2;
2182
+ }
2180
2183
  }
2181
2184
  }
2182
2185
  }
@@ -3463,7 +3466,8 @@ var View = class _View {
3463
3466
  params: this.connectParams(liveReferer),
3464
3467
  session: this.getSession(),
3465
3468
  static: this.getStatic(),
3466
- flash: this.flash
3469
+ flash: this.flash,
3470
+ sticky: this.el.hasAttribute(PHX_STICKY)
3467
3471
  };
3468
3472
  });
3469
3473
  }
@@ -4052,7 +4056,7 @@ var View = class _View {
4052
4056
  return;
4053
4057
  } else if (resp.reason === "unauthorized" || resp.reason === "stale") {
4054
4058
  this.log("error", () => ["unauthorized live_redirect. Falling back to page request", resp]);
4055
- this.onRedirect({ to: this.root.href });
4059
+ this.onRedirect({ to: this.root.href, flash: this.flash });
4056
4060
  return;
4057
4061
  }
4058
4062
  if (resp.redirect || resp.live_redirect) {
@@ -4390,7 +4394,7 @@ var View = class _View {
4390
4394
  event: phxEvent,
4391
4395
  value: this.extractMeta(el, meta, opts.value),
4392
4396
  cid: this.targetComponentID(el, targetCtx, opts)
4393
- }).then(({ reply }) => onReply && onReply(reply));
4397
+ }).then(({ reply }) => onReply && onReply(reply)).catch((error) => logError("Failed to push event", error));
4394
4398
  }
4395
4399
  pushFileProgress(fileEl, entryRef, progress, onReply = function() {
4396
4400
  }) {
@@ -4401,7 +4405,7 @@ var View = class _View {
4401
4405
  entry_ref: entryRef,
4402
4406
  progress,
4403
4407
  cid: view.targetComponentID(fileEl.form, targetCtx)
4404
- }).then(({ resp }) => onReply(resp));
4408
+ }).then(({ resp }) => onReply(resp)).catch((error) => logError("Failed to push file progress", error));
4405
4409
  });
4406
4410
  }
4407
4411
  pushInput(inputEl, targetCtx, forceCid, phxEvent, opts, callback) {
@@ -4462,7 +4466,7 @@ var View = class _View {
4462
4466
  } else {
4463
4467
  callback && callback(resp);
4464
4468
  }
4465
- });
4469
+ }).catch((error) => logError("Failed to push input event", error));
4466
4470
  }
4467
4471
  triggerAwaitingSubmit(formEl, phxEvent) {
4468
4472
  let awaitingSubmit = this.getScheduledSubmit(formEl);
@@ -4549,7 +4553,7 @@ var View = class _View {
4549
4553
  value: formData,
4550
4554
  meta,
4551
4555
  cid
4552
- }).then(({ resp }) => onReply(resp));
4556
+ }).then(({ resp }) => onReply(resp)).catch((error) => logError("Failed to push form submit", error));
4553
4557
  });
4554
4558
  } else if (!(formEl.hasAttribute(PHX_REF_SRC) && formEl.classList.contains("phx-submit-loading"))) {
4555
4559
  let meta = this.extractMeta(formEl, {}, opts.value);
@@ -4560,7 +4564,7 @@ var View = class _View {
4560
4564
  value: formData,
4561
4565
  meta,
4562
4566
  cid
4563
- }).then(({ resp }) => onReply(resp));
4567
+ }).then(({ resp }) => onReply(resp)).catch((error) => logError("Failed to push form submit", error));
4564
4568
  }
4565
4569
  }
4566
4570
  uploadFiles(formEl, phxEvent, targetCtx, ref, cid, onComplete) {
@@ -4608,7 +4612,7 @@ var View = class _View {
4608
4612
  };
4609
4613
  uploader.initAdapterUpload(resp, onError, this.liveSocket);
4610
4614
  }
4611
- });
4615
+ }).catch((error) => logError("Failed to push upload", error));
4612
4616
  });
4613
4617
  }
4614
4618
  handleFailedEntryPreflight(uploadRef, reason, uploader) {
@@ -4719,10 +4723,10 @@ var View = class _View {
4719
4723
  if (completelyDestroyCIDs.length > 0) {
4720
4724
  this.pushWithReply(null, "cids_destroyed", { cids: completelyDestroyCIDs }).then(({ resp }) => {
4721
4725
  this.rendered.pruneCIDs(resp.cids);
4722
- });
4726
+ }).catch((error) => logError("Failed to push components destroyed", error));
4723
4727
  }
4724
4728
  });
4725
- });
4729
+ }).catch((error) => logError("Failed to push components destroyed", error));
4726
4730
  }
4727
4731
  }
4728
4732
  ownsElement(el) {
@@ -4812,7 +4816,7 @@ var LiveSocket = class {
4812
4816
  }
4813
4817
  // public
4814
4818
  version() {
4815
- return "1.0.9";
4819
+ return "1.0.10";
4816
4820
  }
4817
4821
  isProfileEnabled() {
4818
4822
  return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";