glib-web 3.0.2 → 3.0.4

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.
Files changed (45) hide show
  1. package/action.js +15 -29
  2. package/actions/analytics/logEvent.js +2 -2
  3. package/actions/auth/saveCsrfToken.js +0 -6
  4. package/actions/cables/push.js +3 -1
  5. package/actions/http/get.js +28 -16
  6. package/actions/panels/scrollTo.js +32 -7
  7. package/actions/panels/scrollToBottom.js +1 -1
  8. package/actions/windows/closeWithReload.js +1 -1
  9. package/actions/ws/push.js +2 -2
  10. package/app.vue +8 -9
  11. package/components/_chip.vue +29 -12
  12. package/components/_responsive.vue +27 -10
  13. package/components/component.vue +10 -2
  14. package/components/datetime.vue +0 -2
  15. package/components/fields/check.vue +31 -23
  16. package/components/fields/hidden.vue +7 -2
  17. package/components/fields/newRichText.vue +1 -1
  18. package/components/fields/radioGroup.vue +2 -1
  19. package/components/fields/richText.vue +4 -4
  20. package/components/fields/submit.vue +5 -2
  21. package/components/fields/text.vue +22 -23
  22. package/components/fields/textarea.vue +2 -1
  23. package/components/fields/timer.vue +1 -2
  24. package/components/label.vue +8 -4
  25. package/components/mixins/events.js +14 -19
  26. package/components/mixins/generic.js +2 -2
  27. package/components/mixins/styles.js +1 -1
  28. package/components/mixins/ws/actionCable.js +2 -2
  29. package/components/mixins/ws/phoenixSocket.js +6 -7
  30. package/components/panels/horizontal.vue +8 -8
  31. package/components/panels/list.vue +22 -3
  32. package/components/panels/responsive.vue +2 -11
  33. package/components/panels/scroll.vue +0 -3
  34. package/components/panels/table.vue +0 -3
  35. package/components/panels/timeline.vue +3 -4
  36. package/components/switch.vue +5 -4
  37. package/nav/dialog.vue +18 -8
  38. package/package.json +2 -2
  39. package/utils/history.js +1 -1
  40. package/utils/http.js +56 -10
  41. package/utils/private/ws.js +2 -2
  42. package/utils/public.js +0 -6
  43. package/utils/settings.js +10 -2
  44. package/actions/commands/enqueue.js +0 -17
  45. package/utils/queue.js +0 -102
package/utils/http.js CHANGED
@@ -33,7 +33,8 @@ export default class {
33
33
  const formData = new FormData();
34
34
  var params = data || {};
35
35
  for (const key in params) {
36
- formData.append(key, params[key]);
36
+ // formData.append(key, params[key]);
37
+ this._populateFormData(formData, key, params[key]);
37
38
  }
38
39
 
39
40
  if (needCsrfToken && !Type.isString(params["authenticity_token"])) {
@@ -42,8 +43,33 @@ export default class {
42
43
  return formData;
43
44
  }
44
45
 
46
+ static _populateFormData(formData, key, value) {
47
+ if (GLib.type.isArray(value)) {
48
+ for (const item of value) {
49
+ this._populateFormData(formData, `${key}[]`, item);
50
+ }
51
+ } else if (GLib.type.isObject(value)) {
52
+ for (const innerKey in value) {
53
+ this._populateFormData(
54
+ formData,
55
+ `${key}[${innerKey}]`,
56
+ value[innerKey]
57
+ );
58
+ }
59
+ } else {
60
+ formData.append(key, value);
61
+ }
62
+ }
63
+
45
64
  static load(properties, component) {
46
- const url = new URL(properties["url"]);
65
+ const urlString = properties["url"];
66
+ let url;
67
+ try {
68
+ url = new URL(urlString);
69
+ } catch (e) {
70
+ console.warn("Invalid URL", urlString);
71
+ throw e;
72
+ }
47
73
  const domainMatched = window.location.hostname == url.hostname;
48
74
 
49
75
  // If this is an external domain, we rely on `onUnload()` instead.
@@ -120,14 +146,14 @@ export default class {
120
146
  Utils.http.forceComponentUpdate(() => {
121
147
  vueApp.page = page
122
148
  const redirectUrl = Utils.url.htmlUrl(response.url);
123
- Utils.history.updatePage(page, redirectUrl);
149
+ Utils.history.updatePage(vueApp.page, redirectUrl);
124
150
 
125
151
  GLib.action.execute(properties["onReload"], component);
126
152
  });
127
153
  });
128
154
  }
129
155
 
130
- static execute(properties, methodName, component, jsonHandler) {
156
+ static execute(properties, methodName, component, jsonHandler, errorHandler) {
131
157
  this.startIndicator(component);
132
158
 
133
159
  // `fetch()` only supports uppercase
@@ -138,18 +164,26 @@ export default class {
138
164
  url = Utils.url.appendParams(url, body);
139
165
  body = null;
140
166
  }
141
- console.log(`${method} ${url}`);
167
+ const silent = properties.silent;
168
+ if (!silent) {
169
+ console.debug(`${method} ${url}`);
170
+ }
142
171
 
143
172
  const request = new HttpRequest();
144
173
  let response = null;
145
174
  const vm = this;
146
175
 
147
176
  const analyticsHeaders = this.analyticsHeaders(component);
177
+ const headers = Object.assign(
178
+ { Accept: "text/html" },
179
+ analyticsHeaders,
180
+ GLib.settings.headerAugmenter(url, method)
181
+ );
148
182
 
149
183
  fetch(url, {
150
184
  method: method,
151
185
  body: body,
152
- headers: Object.assign({ Accept: "text/html" }, analyticsHeaders),
186
+ headers: headers,
153
187
  // Make sure to pass cookies for same origin URLs.
154
188
  // Needed for some browsers, e.g. Edge and Android's native.
155
189
  credentials: "same-origin",
@@ -158,24 +192,36 @@ export default class {
158
192
  .then(res => {
159
193
  vm.stopIndicator(component);
160
194
 
195
+ response = res;
161
196
  if (res.status >= 500) {
162
197
  throw "Server error";
163
198
  } else if (res.status >= 400) {
164
199
  throw "Not accessible";
165
200
  } else {
166
- response = res;
167
201
  return res.json();
168
202
  }
169
203
  })
170
204
  .then(data => {
171
- console.debug("Success", data);
205
+ if (!silent) {
206
+ console.debug("Success", data);
207
+ }
172
208
  jsonHandler(data, response);
173
209
  })
174
210
  .catch(error => {
175
211
  vm.stopIndicator(component);
212
+
176
213
  if (!request.canceled) {
177
- console.error("Error:", error);
178
- Utils.launch.snackbar.error(error, component);
214
+ const message = error.toString();
215
+
216
+ console.error("Error:", message);
217
+
218
+ if (errorHandler) {
219
+ errorHandler(error, response);
220
+ } else {
221
+ if (!silent) {
222
+ Utils.launch.snackbar.error(message, component);
223
+ }
224
+ }
179
225
  } else {
180
226
  console.info("Canceled");
181
227
  }
@@ -9,13 +9,13 @@ export default class {
9
9
  const ws = vueApp.webSocket;
10
10
  Utils.type.ifObject(payload.wsHeader, header => {
11
11
  Object.assign(ws.header, header);
12
- console.log("New header: ", ws.header);
12
+ console.debug("New header: ", ws.header);
13
13
  });
14
14
 
15
15
  const actionName = payload.action;
16
16
  if (actionName.startsWith("component/")) {
17
17
  const methodName = actionName.replace(/^component\//, "");
18
- console.log(`Executing component action: ${methodName}`);
18
+ console.debug(`Executing component action: ${methodName}`);
19
19
  component[`action_${methodName}`](payload);
20
20
  } else {
21
21
  GLib.action.execute(payload, component);
package/utils/public.js CHANGED
@@ -5,11 +5,8 @@ import Type from "./type";
5
5
  import Form from "./form";
6
6
  import Component from "./component";
7
7
  import Hash from "./hash";
8
- import Queue from "./queue";
9
8
  import Settings from "./settings";
10
9
 
11
- const _queue = new Queue();
12
-
13
10
  export default class {
14
11
  static get action() {
15
12
  return Action;
@@ -35,7 +32,4 @@ export default class {
35
32
  static get settings() {
36
33
  return Settings;
37
34
  }
38
- static get queue() {
39
- return _queue;
40
- }
41
35
  }
package/utils/settings.js CHANGED
@@ -5,8 +5,12 @@ class MutableSettings {
5
5
  this.reactive = true;
6
6
  this.themes = {};
7
7
  this.gtagId = null;
8
- this.errorHandler = err => {
9
- console.error(err.message);
8
+ this.errorHandler = (err, message) => {
9
+ console.error(message || err.message);
10
+ };
11
+ this.headerAugmenter = (_url, _method) => {
12
+ // Set a custom augmenter to add custom HTTP headers.
13
+ return {};
10
14
  };
11
15
  }
12
16
  }
@@ -26,6 +30,10 @@ export default class ImmutableSettings {
26
30
  return settings.errorHandler;
27
31
  }
28
32
 
33
+ static get headerAugmenter() {
34
+ return settings.headerAugmenter;
35
+ }
36
+
29
37
  static get env() {
30
38
  return process.env.NODE_ENV;
31
39
  }
@@ -1,17 +0,0 @@
1
- // import Hash from "../../utils/hash";
2
-
3
- export default class {
4
- // async executeJob(spec, component) {
5
- // console.log("Waiting for next job1...");
6
- // GLib.action.execute(spec.onExecute, component);
7
- // await this.sleep(2000);
8
- // console.log("Waiting for next job2...");
9
- // }
10
- execute(spec, component) {
11
- GLib.queue.enqueue(() => {
12
- GLib.action.execute(spec.onExecute, component);
13
- // console.log("Waiting for next job0...");
14
- // this.executeJob(spec, component);
15
- });
16
- }
17
- }
package/utils/queue.js DELETED
@@ -1,102 +0,0 @@
1
- // From https://stackoverflow.com/questions/47157428/how-to-implement-a-pseudo-blocking-async-queue-in-js-ts
2
-
3
- // const jobQueue = [];
4
- export default class {
5
- constructor() {
6
- // invariant: at least one of the arrays is empty
7
- this.resolvers = [];
8
- this.promises = [];
9
- }
10
- _add() {
11
- this.promises.push(
12
- new Promise(resolve => {
13
- this.resolvers.push(resolve);
14
- })
15
- );
16
- }
17
- enqueue(t) {
18
- // if (this.resolvers.length) this.resolvers.shift()(t);
19
- // else this.promises.push(Promise.resolve(t));
20
- if (!this.resolvers.length) this._add();
21
- this.resolvers.shift()(t);
22
- }
23
- dequeue() {
24
- if (!this.promises.length) this._add();
25
- return this.promises.shift();
26
- }
27
- // now some utilities:
28
- isEmpty() {
29
- // there are no values available
30
- return !this.promises.length; // this.length <= 0
31
- }
32
- isBlocked() {
33
- // it's waiting for values
34
- return !!this.resolvers.length; // this.length < 0
35
- }
36
- get length() {
37
- return this.promises.length - this.resolvers.length;
38
- }
39
- sleep(ms) {
40
- return new Promise(resolve => setTimeout(resolve, ms));
41
- }
42
- async start() {
43
- // TODO: This is experimental
44
- const delayBetweenJobs = 2000;
45
-
46
- for await (const job of this) {
47
- console.log("Executing job...");
48
- job();
49
- if (delayBetweenJobs) {
50
- console.log("Waiting for next job1...");
51
- await this.sleep(delayBetweenJobs);
52
- console.log("Waiting for next job2...");
53
- }
54
- }
55
- }
56
- [Symbol.asyncIterator]() {
57
- // Todo: Use AsyncIterator.from()
58
- return {
59
- next: () => this.dequeue().then(value => ({ done: false, value })),
60
- [Symbol.asyncIterator]() {
61
- return this;
62
- }
63
- };
64
- }
65
- }
66
-
67
- // export default class {
68
- // constructor() {
69
- // this.jobQueue = [];
70
- // this.timerHandle = null;
71
- // }
72
- // // static get csrfElement() {
73
- // // return document.querySelector(`meta[name="csrf-token"]`);
74
- // // }
75
-
76
- // // static getCsrf() {
77
- // // return this.csrfElement.getAttribute("content");
78
- // // }
79
-
80
- // // static setCsrf(value) {
81
- // // return this.csrfElement.setAttribute("content", value);
82
- // // }
83
-
84
- // start(interval) {
85
- // this.timerHandle = setInterval(() => {
86
- // console.log("DEQUQE1");
87
- // const oldestJob = this.jobQueue.shift();
88
- // if (oldestJob) {
89
- // console.log("DEQUQE2");
90
- // oldestJob();
91
- // }
92
- // }, interval);
93
- // }
94
-
95
- // stop() {
96
- // clearInterval(this.timerHandle);
97
- // }
98
-
99
- // enqueue(job) {
100
- // this.jobQueue.push(job);
101
- // }
102
- // }