glib-web 0.6.16 → 0.7.1

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 (58) hide show
  1. package/LICENSE +0 -0
  2. package/actions/auth/restart.js +0 -0
  3. package/actions/dialogs/oauth.js +0 -0
  4. package/actions/dialogs/options.js +0 -0
  5. package/actions/sheets/select.js +0 -0
  6. package/actions/snackbars/alert.js +0 -0
  7. package/actions/snackbars/select.js +0 -0
  8. package/actions/windows/open.js +1 -1
  9. package/actions/windows/openWeb.js +0 -0
  10. package/actions/windows/reload.js +0 -15
  11. package/app.vue +1 -0
  12. package/components/_message.vue +0 -0
  13. package/components/avatar.vue +33 -8
  14. package/components/datetime.vue +0 -0
  15. package/components/fab.vue +0 -0
  16. package/components/fields/_select.vue +0 -0
  17. package/components/fields/country/countries.js +0 -0
  18. package/components/fields/country/field.vue +0 -0
  19. package/components/fields/country/regions.js +0 -0
  20. package/components/fields/datetime.vue +0 -0
  21. package/components/fields/dynamicSelect.vue +0 -0
  22. package/components/fields/select.vue +0 -0
  23. package/components/fields/timeZone.vue +0 -0
  24. package/components/hr.vue +0 -0
  25. package/components/html.vue +0 -0
  26. package/components/image.vue +26 -15
  27. package/components/mixins/longClick.js +0 -0
  28. package/components/mixins/scrolling.js +0 -0
  29. package/components/mixins/styles.js +12 -2
  30. package/components/mixins/table/export.js +0 -0
  31. package/components/mixins/table/import.js +0 -1
  32. package/components/p.vue +0 -0
  33. package/components/panels/column.vue +0 -0
  34. package/components/panels/custom.vue +0 -0
  35. package/components/panels/horizontal.vue +25 -4
  36. package/components/panels/list.vue +13 -1
  37. package/components/panels/split.vue +0 -0
  38. package/components/panels/ul.vue +0 -0
  39. package/index.js +4 -0
  40. package/keys.js +0 -0
  41. package/nav/drawerButton.vue +0 -0
  42. package/package.json +1 -1
  43. package/settings.json.example +0 -0
  44. package/styles/test.sass +0 -0
  45. package/styles/test.scss +0 -0
  46. package/templates/comment.vue +41 -21
  47. package/templates/featured.vue +0 -0
  48. package/templates/thumbnail.vue +13 -1
  49. package/templates/unsupported.vue +0 -0
  50. package/utils/dom.js +0 -0
  51. package/utils/form.js +3 -2
  52. package/utils/history.js +9 -0
  53. package/utils/http.js +32 -1
  54. package/utils/public.js +0 -0
  55. package/utils/settings.js +0 -0
  56. package/utils/storage.js +0 -0
  57. package/utils/uploader.js +0 -7
  58. package/utils/url.js +0 -0
package/LICENSE CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,5 +1,5 @@
1
1
  export default class {
2
2
  execute(properties, component) {
3
- Utils.http.load(properties, null, component);
3
+ Utils.http.load(properties, component);
4
4
  }
5
5
  }
File without changes
@@ -1,21 +1,6 @@
1
1
  export default class {
2
2
  execute(properties, component) {
3
3
  if (Utils.settings.reactive) {
4
- // const currentUrl = window.location.href;
5
- // const data = {
6
- // url: properties.url || currentUrl
7
- // };
8
-
9
- // Utils.http.execute(data, "GET", component, (page, response) => {
10
- // Utils.http.forceComponentUpdate(() => {
11
- // window.vueApp.page = page;
12
- // const redirectUrl = Utils.url.htmlUrl(response.url);
13
- // Utils.history.updatePage(redirectUrl);
14
-
15
- // GLib.action.execute(properties["onReload"], null, component);
16
- // });
17
- // });
18
-
19
4
  Utils.http.reload(properties, component);
20
5
  } else {
21
6
  window.location.reload();
package/app.vue CHANGED
@@ -80,6 +80,7 @@ export default {
80
80
  );
81
81
  Utils.history.saveInitialContent(this.page);
82
82
  Utils.history.restoreOnBackOrForward();
83
+ Utils.http.promptIfDirtyOnUnload();
83
84
  },
84
85
  methods: {
85
86
  $ready: function() {
File without changes
@@ -1,18 +1,43 @@
1
1
  <template>
2
- <v-avatar :style="genericStyles()" :class="$classes()" :size="spec.size">
3
- <v-img :src="spec.url || spec.base64Data" @click="$onClick()"> </v-img>
4
- </v-avatar>
5
-
6
- <!-- <v-avatar :width="genericStyles().remove('width')" :height="genericStyles().remove('height')">
7
- <v-img :src="spec.url || spec.base64Data" :style="genericStyles()" @click='$onClick()'>
8
- </v-img>
9
- </v-avatar> -->
2
+ <v-tooltip
3
+ :disabled="tooltip.disabled"
4
+ :top="tooltipPositionMatches('top')"
5
+ :right="tooltipPositionMatches('right')"
6
+ :bottom="tooltipPositionMatches('bottom')"
7
+ :left="tooltipPositionMatches('left')"
8
+ >
9
+ <template v-slot:activator="{ on }">
10
+ <v-avatar
11
+ :style="genericStyles()"
12
+ :class="$classes()"
13
+ :size="spec.size"
14
+ v-on="on"
15
+ >
16
+ <v-img :src="spec.url || spec.base64Data" @click="$onClick()"> </v-img>
17
+ </v-avatar>
18
+ </template>
19
+ <span>{{ tooltip.text }}</span>
20
+ </v-tooltip>
10
21
  </template>
11
22
 
12
23
  <script>
24
+ import TooltipMixins from "./mixins/tooltip";
25
+
13
26
  export default {
27
+ mixins: [TooltipMixins],
14
28
  props: {
15
29
  spec: { type: Object, required: true }
30
+ },
31
+ data() {
32
+ return {
33
+ styles: {},
34
+ tooltip: {}
35
+ };
36
+ },
37
+ methods: {
38
+ $ready() {
39
+ this.tooltip = this.spec.tooltip || { disabled: true };
40
+ }
16
41
  }
17
42
  };
18
43
  </script>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/components/hr.vue CHANGED
File without changes
File without changes
@@ -1,34 +1,45 @@
1
1
  <template>
2
- <!-- TODO: Add support for href and :rel="$rel()" -->
3
- <v-img :src="spec.url || spec.base64Data" :style="styles" @click="$onClick()">
4
- <!-- <v-progress-circular v-if="$isBusy" indeterminate /> -->
5
- </v-img>
2
+ <v-tooltip
3
+ :disabled="tooltip.disabled"
4
+ :top="tooltipPositionMatches('top')"
5
+ :right="tooltipPositionMatches('right')"
6
+ :bottom="tooltipPositionMatches('bottom')"
7
+ :left="tooltipPositionMatches('left')"
8
+ >
9
+ <template v-slot:activator="{ on }">
10
+ <!-- TODO: Add support for href and :rel="$rel()" -->
11
+ <v-img
12
+ :src="spec.url || spec.base64Data"
13
+ :style="styles"
14
+ v-on="on"
15
+ @click="$onClick()"
16
+ >
17
+ <!-- <v-progress-circular v-if="$isBusy" indeterminate /> -->
18
+ </v-img>
19
+ </template>
20
+ <span>{{ tooltip.text }}</span>
21
+ </v-tooltip>
6
22
  </template>
7
23
 
8
24
  <script>
9
25
  import Vue from "vue";
26
+ import TooltipMixins from "./mixins/tooltip";
10
27
 
11
28
  export default {
29
+ mixins: [TooltipMixins],
12
30
  props: {
13
31
  spec: { type: Object, required: true }
14
32
  },
15
33
  data() {
16
34
  return {
17
- styles: {}
35
+ styles: {},
36
+ tooltip: {}
18
37
  };
19
38
  },
20
- // computed: {
21
- // isOnClickPresent() {
22
- // return this.spec.hasOwnProperty('onClick')
23
- // }
24
- // },
25
39
  methods: {
26
- // styles: function() {
27
- // // const styles = this.genericStyles(Object.assign({ height: 210 }, this.spec))
28
- // const styles = this.genericStyles(Object.assign({}, this.spec));
29
- // return styles;
30
- // }
31
40
  $ready() {
41
+ this.tooltip = this.spec.tooltip || { disabled: true };
42
+
32
43
  const styles = this.genericStyles(Object.assign({}, this.spec));
33
44
  this.styles = styles;
34
45
 
File without changes
File without changes
@@ -46,8 +46,18 @@ export default {
46
46
  this.$internalizeValue(val)
47
47
  );
48
48
 
49
- // this.$saveValue(val);
50
- // Vue.set(this.$data._fieldModels, this.fieldName, val);
49
+ // Make sure value has changed and make sure that it is different from the original value.
50
+ // Be strict with this so it doesn't execute when the component is just initializing (e.g value changing
51
+ // from `null` to `this.spec.value`).
52
+ if (
53
+ !window.vueApp.page.disableDirtyPrompt &&
54
+ !window.vueApp.isFormDirty &&
55
+ val != oldVal &&
56
+ val != this.spec.value
57
+ ) {
58
+ console.log("Form is now dirty");
59
+ window.vueApp.isFormDirty = true;
60
+ }
51
61
  }
52
62
  },
53
63
  methods: {
File without changes
@@ -42,7 +42,6 @@ export default {
42
42
  };
43
43
  Utils.http.execute(data, "POST", vm, response => {
44
44
  GLib.action.handleResponse(response, vm);
45
- // Action.execute(response["onResponse"], target, vm);
46
45
  vm._submitEachRow(rows, target);
47
46
  });
48
47
  }
package/components/p.vue CHANGED
File without changes
File without changes
File without changes
@@ -14,13 +14,22 @@ export default {
14
14
  computed: {
15
15
  cssClasses: function() {
16
16
  const classes = this.$classes().concat("layouts-horizontal");
17
- switch (this.spec.distribution) {
17
+ const distribution = this.spec.distribution;
18
+ switch (distribution) {
18
19
  case "fillEqually":
19
20
  classes.push("layouts-horizontal--fill-equally");
20
21
  break;
21
22
  case "spaceEqually":
22
23
  classes.push("layouts-horizontal--space-equally");
23
24
  break;
25
+ default:
26
+ Utils.type.ifString(distribution, distribution => {
27
+ // Uses Material Design spacings: https://vuetifyjs.com/en/styles/spacing/#how-it-works
28
+ if (distribution.startsWith("overlap")) {
29
+ classes.push(`layouts-horizontal--${distribution}`);
30
+ }
31
+ });
32
+ break;
24
33
  }
25
34
  return classes;
26
35
  },
@@ -67,7 +76,19 @@ export default {
67
76
  .layouts-horizontal--space-equally > * {
68
77
  flex: initial;
69
78
  }
70
- /* .layouts-horizontal > div {
71
- display: inline-block;
72
- } */
79
+ .layouts-horizontal--overlap-1 > *:not(:first-child) {
80
+ margin-left: -4px;
81
+ }
82
+ .layouts-horizontal--overlap-2 > *:not(:first-child) {
83
+ margin-left: -8px;
84
+ }
85
+ .layouts-horizontal--overlap-3 > *:not(:first-child) {
86
+ margin-left: -12px;
87
+ }
88
+ .layouts-horizontal--overlap-4 > *:not(:first-child) {
89
+ margin-left: -16px;
90
+ }
91
+ .layouts-horizontal--overlap-5 > *:not(:first-child) {
92
+ margin-left: -20px;
93
+ }
73
94
  </style>
@@ -28,7 +28,7 @@
28
28
 
29
29
  <component
30
30
  :is="template(row)"
31
- :spec="row"
31
+ :spec="rowSpec(row)"
32
32
  :responsive-cols="spec.responsiveCols"
33
33
  :index="rowIndex"
34
34
  />
@@ -113,6 +113,18 @@ export default {
113
113
  section.rows = section.rows || [];
114
114
  }
115
115
  },
116
+ rowSpec(row) {
117
+ if (this.spec.fieldPrefix) {
118
+ const prefix = `${this.spec.fieldPrefix}[${row.recordId}]`;
119
+ return Object.assign({}, row, {
120
+ fieldCheckName: `${prefix}[checked]`,
121
+ fieldTitleName: this.spec.fieldTitleName
122
+ ? `${prefix}[${this.spec.fieldTitleName}]`
123
+ : null
124
+ });
125
+ }
126
+ return row;
127
+ },
116
128
  action_loadNext(spec) {
117
129
  console.log("TODO: load new comments and append to this list", spec);
118
130
  // TODO
File without changes
File without changes
package/index.js CHANGED
@@ -160,6 +160,10 @@ document.addEventListener("DOMContentLoaded", () => {
160
160
  indicator: false,
161
161
  // Rename to isPageStale
162
162
  isStale: false,
163
+ /// Dirty form handling
164
+ isFormSubmitted: false,
165
+ isFormDirty: false,
166
+ ///
163
167
  stateUpdatedAt: null,
164
168
  webSocket: { channels: {}, header: {} },
165
169
  actionCable: { channels: {} },
package/keys.js CHANGED
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glib-web",
3
- "version": "0.6.16",
3
+ "version": "0.7.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
File without changes
package/styles/test.sass CHANGED
File without changes
package/styles/test.scss CHANGED
File without changes
@@ -5,11 +5,7 @@
5
5
  <v-card-title v-if="mode === 'system'" class="caption">{{
6
6
  spec.subtitle
7
7
  }}</v-card-title>
8
- <v-avatar
9
- v-if="spec.imageUrl && mode !== 'outgoing'"
10
- class="avatar"
11
- size="30px"
12
- >
8
+ <v-avatar v-if="spec.imageUrl" class="avatar" size="30px">
13
9
  <img :src="spec.imageUrl" alt="avatar" />
14
10
  </v-avatar>
15
11
  <v-card-title
@@ -156,47 +152,71 @@ export default {
156
152
  padding-top: 4px;
157
153
  align-self: start;
158
154
  }
155
+
156
+ > .avatar {
157
+ grid-row: 1/2;
158
+ grid-column: 1/2;
159
+ padding: 0;
160
+ justify-self: center;
161
+ }
162
+
163
+ > .subtitle {
164
+ grid-row: 1/2;
165
+ grid-column: 2/3;
166
+ padding: 0;
167
+ }
159
168
  }
160
- .avatar {
161
- grid-row: 1/2;
162
- grid-column: 1/2;
163
- padding: 0;
164
- justify-self: center;
165
- }
166
- .subtitle {
167
- grid-row: 1/2;
168
- grid-column: 2/3;
169
- padding: 0;
170
- }
169
+
171
170
  .outgoing {
172
171
  display: grid;
173
- grid-template-rows: auto 1fr;
174
- grid-column: 2/5;
172
+ grid-template-columns: 36px auto 36px;
173
+ grid-template-rows: 1fr auto 0.5fr;
174
+ grid-column: 4/4;
175
175
 
176
176
  > * {
177
177
  justify-self: end;
178
+ grid-column: 2/3;
179
+ }
180
+
181
+ > .chips {
182
+ grid-row: 4/4;
178
183
  }
179
184
 
180
185
  > .comment-bubble {
181
186
  background-color: #0084ff;
182
187
  color: #fff;
183
188
  border-radius: 10px 10px 0px 10px !important;
184
- grid-row: 1/2;
189
+ grid-row: 2/2;
185
190
  }
186
191
 
187
192
  > .comment-image {
188
193
  max-width: 100%;
189
194
  height: 300px;
190
195
  border-radius: 10px !important;
191
- grid-row: 1/2;
196
+ grid-row: 2/2;
192
197
  padding: 0px !important;
193
198
  cursor: pointer;
194
199
  }
195
200
 
196
201
  > .subsubtitle {
197
- grid-row: 2/3;
202
+ grid-row: 3/4;
198
203
  padding: 0;
199
204
  color: #9e9e9e;
205
+ padding-top: 4px;
206
+ align-self: start;
207
+ }
208
+
209
+ > .avatar {
210
+ grid-row: 1/2;
211
+ grid-column: 3/4;
212
+ padding: 0;
213
+ justify-self: end;
214
+ }
215
+
216
+ > .subtitle {
217
+ grid-row: 1/2;
218
+ grid-column: 2/3;
219
+ padding: 0;
200
220
  }
201
221
  }
202
222
  </style>
File without changes
@@ -23,6 +23,12 @@
23
23
  >
24
24
  <v-icon v-if="spec.onReorder" class="handle">drag_indicator</v-icon>
25
25
 
26
+ <v-checkbox
27
+ v-if="spec.fieldCheckName"
28
+ :name="spec.fieldCheckName"
29
+ :value="true"
30
+ ></v-checkbox>
31
+
26
32
  <div v-if="spec.leftButtons" class="left-universal">
27
33
  <template v-for="(item, index) in spec.leftButtons">
28
34
  <common-button
@@ -52,7 +58,13 @@
52
58
  </div>
53
59
 
54
60
  <v-list-item-content style="display:flex;">
55
- <v-list-item-title>{{ spec.title }}</v-list-item-title>
61
+ <v-text-field
62
+ v-if="spec.fieldTitleName"
63
+ :name="spec.fieldTitleName"
64
+ :value="spec.title"
65
+ />
66
+ <v-list-item-title v-else>{{ spec.title }}</v-list-item-title>
67
+
56
68
  <v-list-item-subtitle v-if="spec.subtitle">{{
57
69
  spec.subtitle
58
70
  }}</v-list-item-subtitle>
File without changes
package/utils/dom.js CHANGED
File without changes
package/utils/form.js CHANGED
@@ -4,7 +4,7 @@ export default class {
4
4
  // Button -> Button.onClick() -> form.submit() -- doesn't execute Form.onSubmit(), so cannot call Utils.http.execute() there
5
5
  // Submit -> Form.onSubmit() -> Utils.http.execute() -- save autofill values and auto-submit when ENTER is pressed
6
6
  static submitData(form, component) {
7
- // Analogous to Rails' form_with's local
7
+ // Analogous to Rails' form_with's `local: true`
8
8
  if (form.dataset.local) {
9
9
  form.submit();
10
10
  } else {
@@ -15,13 +15,14 @@ export default class {
15
15
  const data = {
16
16
  url: Utils.url.appendParams(url, formData)
17
17
  };
18
- Utils.http.load(data, null, component);
18
+ Utils.http.load(data, component);
19
19
  } else {
20
20
  const data = {
21
21
  url: url,
22
22
  formData: formData
23
23
  };
24
24
  Utils.http.execute(data, method, component, response => {
25
+ Utils.http.notifyFormSubmitted();
25
26
  GLib.action.handleResponse(response, component);
26
27
  });
27
28
  }
package/utils/history.js CHANGED
@@ -35,6 +35,15 @@ export default class {
35
35
  static restoreOnBackOrForward() {
36
36
  const vm = this;
37
37
  window.onpopstate = event => {
38
+ // TODO: Ideally display a prompt when dirty
39
+ // if (Utils.http.proceedEvenWhenDirty()) {
40
+ // event.preventDefault();
41
+ // history.go(1);
42
+ // return false;
43
+ // }
44
+
45
+ window.vueApp.isFormDirty = false;
46
+
38
47
  // Save scroll position of the current page when navigating through back/forward button
39
48
  this.bodyScrollTops[this.navigationCount] = this._pageBody.scrollTop;
40
49
 
package/utils/http.js CHANGED
@@ -2,6 +2,7 @@ import Type from "./type";
2
2
  import Action from "../action";
3
3
 
4
4
  let loading = false;
5
+ const dirtyPrompt = "Changes you made have not been saved. Are you sure?";
5
6
 
6
7
  class HttpRequest {
7
8
  constructor() {
@@ -38,9 +39,15 @@ export default class {
38
39
  return formData;
39
40
  }
40
41
 
41
- static load(properties, target, component) {
42
+ static load(properties, component) {
42
43
  const url = new URL(properties["url"]);
43
44
  const domainMatched = window.location.hostname == url.hostname;
45
+
46
+ // If this is an external domain, we rely on `onUnload()` instead.
47
+ if (domainMatched && !this.proceedEvenWhenDirty()) {
48
+ return;
49
+ }
50
+
44
51
  if (Utils.settings.reactive && domainMatched) {
45
52
  const currentUrl = window.location.href;
46
53
  const htmlUrl = Utils.url.htmlUrl(properties["url"]);
@@ -202,8 +209,32 @@ export default class {
202
209
 
203
210
  // Queue the execution so the first isStale has time to resets state before handler gets executed
204
211
  setTimeout(() => {
212
+ window.vueApp.isFormSubmitted = false;
213
+ window.vueApp.isFormDirty = false;
205
214
  handler();
206
215
  window.vueApp.isStale = false;
207
216
  }, 0);
208
217
  }
218
+
219
+ static promptIfDirtyOnUnload() {
220
+ window.onbeforeunload = function() {
221
+ return window.vueApp.isFormDirty ? dirtyPrompt : null;
222
+ };
223
+ }
224
+
225
+ static notifyFormSubmitted() {
226
+ window.vueApp.isFormSubmitted = true;
227
+ }
228
+
229
+ static proceedEvenWhenDirty() {
230
+ // Don't prompt if this is a result of form submission
231
+ if (
232
+ window.vueApp.isFormDirty &&
233
+ !window.vueApp.isFormSubmitted &&
234
+ !confirm(dirtyPrompt)
235
+ ) {
236
+ return false;
237
+ }
238
+ return true;
239
+ }
209
240
  }
package/utils/public.js CHANGED
File without changes
package/utils/settings.js CHANGED
File without changes
package/utils/storage.js CHANGED
File without changes
package/utils/uploader.js CHANGED
@@ -5,13 +5,6 @@ import mimeType from "../utils/mime_type";
5
5
  const MB_SIZE = 1000;
6
6
 
7
7
  export default class Uploader {
8
- // constructor(input, file, url) {
9
- // this.input = input
10
- // this.file = file
11
- // this.url = url
12
- // this.upload = new DirectUpload(this.file, this.url, this)
13
- // }
14
-
15
8
  constructor(file, url, progress) {
16
9
  this.file = file;
17
10
  this.url = url;
package/utils/url.js CHANGED
File without changes