phoenix_live_view 0.18.15 → 0.18.17

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 CHANGED
@@ -134,6 +134,8 @@ $ npm install --save --prefix assets mdn-polyfills url-search-params-polyfill fo
134
134
 
135
135
  Note: The `shim-keyboard-event-key` polyfill is also required for [MS Edge 12-18](https://caniuse.com/#feat=keyboardevent-key).
136
136
 
137
+ Note: The `event-submitter-polyfill` package is also required for [MS Edge 12-80 & Safari < 15.4](https://caniuse.com/mdn-api_submitevent_submitter).
138
+
137
139
  ```
138
140
  // assets/js/app.js
139
141
  import "mdn-polyfills/Object.assign"
@@ -153,6 +155,7 @@ import "classlist-polyfill"
153
155
  import "new-event-polyfill"
154
156
  import "@webcomponents/template"
155
157
  import "shim-keyboard-event-key"
158
+ import "event-submitter-polyfill"
156
159
  import "core-js/features/set"
157
160
  import "core-js/features/url"
158
161
 
@@ -98,7 +98,6 @@ export default class DOMPatch {
98
98
  this.trackBefore("updated", container, container)
99
99
 
100
100
  liveSocket.time("morphdom", () => {
101
-
102
101
  this.streams.forEach(([inserts, deleteIds]) => {
103
102
  this.streamInserts = Object.assign(this.streamInserts, inserts)
104
103
  deleteIds.forEach(id => {
@@ -124,8 +123,7 @@ export default class DOMPatch {
124
123
  let streamAt = child.id ? this.streamInserts[child.id] : undefined
125
124
  if(streamAt === undefined) { return parent.appendChild(child) }
126
125
 
127
- //streaming
128
- DOM.putPrivate(child, PHX_STREAM, true)
126
+ // streaming
129
127
  if(streamAt === 0){
130
128
  parent.insertAdjacentElement("afterbegin", child)
131
129
  } else if(streamAt === -1){
@@ -160,10 +158,13 @@ export default class DOMPatch {
160
158
  onNodeDiscarded: (el) => this.onNodeDiscarded(el),
161
159
  onBeforeNodeDiscarded: (el) => {
162
160
  if(el.getAttribute && el.getAttribute(PHX_PRUNE) !== null){ return true }
163
- if(DOM.private(el, PHX_STREAM)){ return false }
164
- if(el.parentElement !== null && DOM.isPhxUpdate(el.parentElement, phxUpdate, ["append", "prepend"]) && el.id){ return false }
161
+ if(el.parentElement !== null && el.id &&
162
+ DOM.isPhxUpdate(el.parentElement, phxUpdate, [PHX_STREAM, "append", "prepend"])){
163
+ return false
164
+ }
165
165
  if(this.maybePendingRemove(el)){ return false }
166
166
  if(this.skipCIDSibling(el)){ return false }
167
+
167
168
  return true
168
169
  },
169
170
  onElUpdated: (el) => {
@@ -270,7 +271,6 @@ export default class DOMPatch {
270
271
  let streamAt = el.id ? this.streamInserts[el.id] : undefined
271
272
  if(streamAt === undefined){ return }
272
273
 
273
- DOM.putPrivate(el, PHX_STREAM, true)
274
274
  if(streamAt === 0){
275
275
  el.parentElement.insertBefore(el, el.parentElement.firstElementChild)
276
276
  } else if(streamAt > 0){
@@ -47,7 +47,8 @@ let JS = {
47
47
  if(_target){ pushOpts._target = _target }
48
48
  targetView.pushInput(sourceEl, targetCtx, newCid, event || phxEvent, pushOpts, callback)
49
49
  } else if(eventType === "submit"){
50
- targetView.submitForm(sourceEl, targetCtx, event || phxEvent, pushOpts)
50
+ let {submitter} = args
51
+ targetView.submitForm(sourceEl, targetCtx, event || phxEvent, submitter, pushOpts)
51
52
  } else {
52
53
  targetView.pushEvent(eventType, sourceEl, targetCtx, event || phxEvent, data, pushOpts)
53
54
  }
@@ -822,7 +822,7 @@ export default class LiveSocket {
822
822
  e.preventDefault()
823
823
  e.target.disabled = true
824
824
  this.withinOwners(e.target, view => {
825
- JS.exec("submit", phxEvent, view, e.target, ["push", {}])
825
+ JS.exec("submit", phxEvent, view, e.target, ["push", {submitter: e.submitter}])
826
826
  })
827
827
  }, false)
828
828
 
@@ -51,8 +51,18 @@ import Rendered from "./rendered"
51
51
  import ViewHook from "./view_hook"
52
52
  import JS from "./js"
53
53
 
54
- let serializeForm = (form, meta, onlyNames = []) => {
54
+ let serializeForm = (form, metadata, onlyNames = []) => {
55
+ let {submitter, ...meta} = metadata
56
+
57
+ // TODO: Replace with `new FormData(form, submitter)` when supported by latest browsers,
58
+ // and mention `formdata-submitter-polyfill` in the docs.
55
59
  let formData = new FormData(form)
60
+
61
+ // TODO: Remove when FormData constructor supports the submitter argument.
62
+ if (submitter && submitter.form && submitter.form === form){
63
+ formData.append(submitter.name, submitter.value)
64
+ }
65
+
56
66
  let toRemove = []
57
67
 
58
68
  formData.forEach((val, key, _index) => {
@@ -632,7 +642,10 @@ export default class View {
632
642
  }
633
643
 
634
644
  onJoinError(resp){
635
- if(resp.reason === "unauthorized" || resp.reason === "stale"){
645
+ if(resp.reason === "reload"){
646
+ this.log("error", () => [`failed mount with ${resp.status}. Falling back to page request`, resp])
647
+ return this.onRedirect({to: this.href})
648
+ } else if(resp.reason === "unauthorized" || resp.reason === "stale"){
636
649
  this.log("error", () => ["unauthorized live_redirect. Falling back to page request", resp])
637
650
  return this.onRedirect({to: this.href})
638
651
  }
@@ -951,18 +964,18 @@ export default class View {
951
964
  return this.putRef([formEl].concat(disables).concat(buttons).concat(inputs), "submit", opts)
952
965
  }
953
966
 
954
- pushFormSubmit(formEl, targetCtx, phxEvent, opts, onReply){
967
+ pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply){
955
968
  let refGenerator = () => this.disableForm(formEl, opts)
956
969
  let cid = this.targetComponentID(formEl, targetCtx)
957
970
  if(LiveUploader.hasUploadsInProgress(formEl)){
958
971
  let [ref, _els] = refGenerator()
959
- let push = () => this.pushFormSubmit(formEl, targetCtx, phxEvent, opts, onReply)
972
+ let push = () => this.pushFormSubmit(formEl, submitter, targetCtx, phxEvent, opts, onReply)
960
973
  return this.scheduleSubmit(formEl, ref, opts, push)
961
974
  } else if(LiveUploader.inputsAwaitingPreflight(formEl).length > 0){
962
975
  let [ref, els] = refGenerator()
963
976
  let proxyRefGen = () => [ref, els, opts]
964
977
  this.uploadFiles(formEl, targetCtx, ref, cid, (_uploads) => {
965
- let formData = serializeForm(formEl, {})
978
+ let formData = serializeForm(formEl, {submitter})
966
979
  this.pushWithReply(proxyRefGen, "event", {
967
980
  type: "form",
968
981
  event: phxEvent,
@@ -971,7 +984,7 @@ export default class View {
971
984
  }, onReply)
972
985
  })
973
986
  } else {
974
- let formData = serializeForm(formEl, {})
987
+ let formData = serializeForm(formEl, {submitter})
975
988
  this.pushWithReply(refGenerator, "event", {
976
989
  type: "form",
977
990
  event: phxEvent,
@@ -1124,13 +1137,13 @@ export default class View {
1124
1137
  (!parentViewEl && this.isDead)
1125
1138
  }
1126
1139
 
1127
- submitForm(form, targetCtx, phxEvent, opts = {}){
1140
+ submitForm(form, targetCtx, phxEvent, submitter, opts = {}){
1128
1141
  DOM.putPrivate(form, PHX_HAS_SUBMITTED, true)
1129
1142
  let phxFeedback = this.liveSocket.binding(PHX_FEEDBACK_FOR)
1130
1143
  let inputs = Array.from(form.elements)
1131
1144
  inputs.forEach(input => DOM.putPrivate(input, PHX_HAS_SUBMITTED, true))
1132
1145
  this.liveSocket.blurActiveElement(this)
1133
- this.pushFormSubmit(form, targetCtx, phxEvent, opts, () => {
1146
+ this.pushFormSubmit(form, targetCtx, phxEvent, submitter, opts, () => {
1134
1147
  inputs.forEach(input => DOM.showError(input, phxFeedback))
1135
1148
  this.liveSocket.restorePreviouslyActiveFocus()
1136
1149
  })
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phoenix_live_view",
3
- "version": "0.18.15",
3
+ "version": "0.18.17",
4
4
  "description": "The Phoenix LiveView JavaScript client.",
5
5
  "license": "MIT",
6
6
  "repository": {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phoenix_live_view",
3
- "version": "0.18.15",
3
+ "version": "0.18.17",
4
4
  "description": "The Phoenix LiveView JavaScript client.",
5
5
  "license": "MIT",
6
6
  "module": "./priv/static/phoenix_live_view.esm.js",
@@ -1662,7 +1662,6 @@ var DOMPatch = class {
1662
1662
  if (streamAt === void 0) {
1663
1663
  return parent.appendChild(child);
1664
1664
  }
1665
- dom_default.putPrivate(child, PHX_STREAM, true);
1666
1665
  if (streamAt === 0) {
1667
1666
  parent.insertAdjacentElement("afterbegin", child);
1668
1667
  } else if (streamAt === -1) {
@@ -1696,10 +1695,7 @@ var DOMPatch = class {
1696
1695
  if (el.getAttribute && el.getAttribute(PHX_PRUNE) !== null) {
1697
1696
  return true;
1698
1697
  }
1699
- if (dom_default.private(el, PHX_STREAM)) {
1700
- return false;
1701
- }
1702
- if (el.parentElement !== null && dom_default.isPhxUpdate(el.parentElement, phxUpdate, ["append", "prepend"]) && el.id) {
1698
+ if (el.parentElement !== null && el.id && dom_default.isPhxUpdate(el.parentElement, phxUpdate, [PHX_STREAM, "append", "prepend"])) {
1703
1699
  return false;
1704
1700
  }
1705
1701
  if (this.maybePendingRemove(el)) {
@@ -1813,7 +1809,6 @@ var DOMPatch = class {
1813
1809
  if (streamAt === void 0) {
1814
1810
  return;
1815
1811
  }
1816
- dom_default.putPrivate(el, PHX_STREAM, true);
1817
1812
  if (streamAt === 0) {
1818
1813
  el.parentElement.insertBefore(el, el.parentElement.firstElementChild);
1819
1814
  } else if (streamAt > 0) {
@@ -2232,7 +2227,8 @@ var JS = {
2232
2227
  }
2233
2228
  targetView.pushInput(sourceEl, targetCtx, newCid, event || phxEvent, pushOpts, callback);
2234
2229
  } else if (eventType === "submit") {
2235
- targetView.submitForm(sourceEl, targetCtx, event || phxEvent, pushOpts);
2230
+ let { submitter } = args;
2231
+ targetView.submitForm(sourceEl, targetCtx, event || phxEvent, submitter, pushOpts);
2236
2232
  } else {
2237
2233
  targetView.pushEvent(eventType, sourceEl, targetCtx, event || phxEvent, data, pushOpts);
2238
2234
  }
@@ -2396,8 +2392,12 @@ var JS = {
2396
2392
  var js_default = JS;
2397
2393
 
2398
2394
  // js/phoenix_live_view/view.js
2399
- var serializeForm = (form, meta, onlyNames = []) => {
2395
+ var serializeForm = (form, metadata, onlyNames = []) => {
2396
+ let { submitter, ...meta } = metadata;
2400
2397
  let formData = new FormData(form);
2398
+ if (submitter && submitter.form && submitter.form === form) {
2399
+ formData.append(submitter.name, submitter.value);
2400
+ }
2401
2401
  let toRemove = [];
2402
2402
  formData.forEach((val, key, _index) => {
2403
2403
  if (val instanceof File) {
@@ -2942,7 +2942,10 @@ var View = class {
2942
2942
  });
2943
2943
  }
2944
2944
  onJoinError(resp) {
2945
- if (resp.reason === "unauthorized" || resp.reason === "stale") {
2945
+ if (resp.reason === "reload") {
2946
+ this.log("error", () => [`failed mount with ${resp.status}. Falling back to page request`, resp]);
2947
+ return this.onRedirect({ to: this.href });
2948
+ } else if (resp.reason === "unauthorized" || resp.reason === "stale") {
2946
2949
  this.log("error", () => ["unauthorized live_redirect. Falling back to page request", resp]);
2947
2950
  return this.onRedirect({ to: this.href });
2948
2951
  }
@@ -3277,18 +3280,18 @@ var View = class {
3277
3280
  formEl.setAttribute(this.binding(PHX_PAGE_LOADING), "");
3278
3281
  return this.putRef([formEl].concat(disables).concat(buttons).concat(inputs), "submit", opts);
3279
3282
  }
3280
- pushFormSubmit(formEl, targetCtx, phxEvent, opts, onReply) {
3283
+ pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply) {
3281
3284
  let refGenerator = () => this.disableForm(formEl, opts);
3282
3285
  let cid = this.targetComponentID(formEl, targetCtx);
3283
3286
  if (LiveUploader.hasUploadsInProgress(formEl)) {
3284
3287
  let [ref, _els] = refGenerator();
3285
- let push = () => this.pushFormSubmit(formEl, targetCtx, phxEvent, opts, onReply);
3288
+ let push = () => this.pushFormSubmit(formEl, submitter, targetCtx, phxEvent, opts, onReply);
3286
3289
  return this.scheduleSubmit(formEl, ref, opts, push);
3287
3290
  } else if (LiveUploader.inputsAwaitingPreflight(formEl).length > 0) {
3288
3291
  let [ref, els] = refGenerator();
3289
3292
  let proxyRefGen = () => [ref, els, opts];
3290
3293
  this.uploadFiles(formEl, targetCtx, ref, cid, (_uploads) => {
3291
- let formData = serializeForm(formEl, {});
3294
+ let formData = serializeForm(formEl, { submitter });
3292
3295
  this.pushWithReply(proxyRefGen, "event", {
3293
3296
  type: "form",
3294
3297
  event: phxEvent,
@@ -3297,7 +3300,7 @@ var View = class {
3297
3300
  }, onReply);
3298
3301
  });
3299
3302
  } else {
3300
- let formData = serializeForm(formEl, {});
3303
+ let formData = serializeForm(formEl, { submitter });
3301
3304
  this.pushWithReply(refGenerator, "event", {
3302
3305
  type: "form",
3303
3306
  event: phxEvent,
@@ -3425,13 +3428,13 @@ var View = class {
3425
3428
  let parentViewEl = el.closest(PHX_VIEW_SELECTOR);
3426
3429
  return el.getAttribute(PHX_PARENT_ID) === this.id || parentViewEl && parentViewEl.id === this.id || !parentViewEl && this.isDead;
3427
3430
  }
3428
- submitForm(form, targetCtx, phxEvent, opts = {}) {
3431
+ submitForm(form, targetCtx, phxEvent, submitter, opts = {}) {
3429
3432
  dom_default.putPrivate(form, PHX_HAS_SUBMITTED, true);
3430
3433
  let phxFeedback = this.liveSocket.binding(PHX_FEEDBACK_FOR);
3431
3434
  let inputs = Array.from(form.elements);
3432
3435
  inputs.forEach((input) => dom_default.putPrivate(input, PHX_HAS_SUBMITTED, true));
3433
3436
  this.liveSocket.blurActiveElement(this);
3434
- this.pushFormSubmit(form, targetCtx, phxEvent, opts, () => {
3437
+ this.pushFormSubmit(form, targetCtx, phxEvent, submitter, opts, () => {
3435
3438
  inputs.forEach((input) => dom_default.showError(input, phxFeedback));
3436
3439
  this.liveSocket.restorePreviouslyActiveFocus();
3437
3440
  });
@@ -4156,7 +4159,7 @@ var LiveSocket = class {
4156
4159
  e.preventDefault();
4157
4160
  e.target.disabled = true;
4158
4161
  this.withinOwners(e.target, (view) => {
4159
- js_default.exec("submit", phxEvent, view, e.target, ["push", {}]);
4162
+ js_default.exec("submit", phxEvent, view, e.target, ["push", { submitter: e.submitter }]);
4160
4163
  });
4161
4164
  }, false);
4162
4165
  for (let type of ["change", "input"]) {