glib-web 3.5.3 → 3.5.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.
@@ -3,6 +3,11 @@ import { vueApp } from "../../store";
3
3
  export default class {
4
4
  execute(properties, component) {
5
5
  vueApp.richTextValues = {};
6
+
7
+ if (!executeGlobalEvent('onbeforewindowsclose')) {
8
+ return;
9
+ }
10
+
6
11
  Utils.history.back();
7
12
 
8
13
  Utils.type.ifObject(properties["onClose"], it => {
@@ -1,8 +1,13 @@
1
- import { vueApp } from "../../store";
1
+ import { executeGlobalEvent, vueApp } from "../../store";
2
2
 
3
3
  export default class {
4
4
  execute(properties, component) {
5
5
  vueApp.richTextValues = {};
6
+
7
+ if (!executeGlobalEvent('onbeforewindowsopen')) {
8
+ return;
9
+ }
10
+
6
11
  Utils.http.load(properties, component);
7
12
  }
8
13
  }
@@ -1,9 +1,13 @@
1
- import { vueApp } from "../../store";
1
+ import { executeGlobalEvent, vueApp } from "../../store";
2
2
 
3
3
  export default class {
4
4
  execute(properties, component) {
5
5
  vueApp.richTextValues = {};
6
6
 
7
+ if (!executeGlobalEvent('onbeforewindowsreload')) {
8
+ return;
9
+ }
10
+
7
11
  if (Utils.settings.reactive) {
8
12
  Utils.http.reload(properties, component);
9
13
  } else {
package/app.vue CHANGED
@@ -33,6 +33,9 @@ import Utils from "./utils/helper";
33
33
  // import actionCableMixin from "./components/mixins/ws/actionCable.js";
34
34
  import FormPanel from "./components/panels/form.vue";
35
35
  import { vueApp } from "./store";
36
+ import { useDirtyState } from "./components/composable/dirtyState";
37
+
38
+ const { watchDirtyState } = useDirtyState();
36
39
 
37
40
  export default {
38
41
  components: {
@@ -88,7 +91,7 @@ export default {
88
91
  );
89
92
  Utils.history.saveInitialContent(this.page);
90
93
  Utils.history.restoreOnBackOrForward();
91
- Utils.http.promptIfDirtyOnUnload();
94
+ watchDirtyState();
92
95
  },
93
96
  methods: {
94
97
  $mounted() {
@@ -0,0 +1,39 @@
1
+ import { watchEffect } from "vue";
2
+ import { vueApp, glibevent, dialogStates } from "../../store";
3
+
4
+ export function useDirtyState() {
5
+ const ctx = dialogStates.slice(-1)[0] || vueApp;
6
+ const prompt = "Changes you made have not been saved. Are you sure?";
7
+
8
+ const isDirty = () => {
9
+ return ctx.isFormDirty && !ctx.isFormSubmitted && !confirm(prompt);
10
+ };
11
+
12
+ const clearDirtyState = () => {
13
+ ctx.isFormDirty = false;
14
+ };
15
+
16
+ const updateDirtyState = (val, oldVal, spec) => {
17
+ if (!spec.disableDirtyCheck && val != oldVal && val != spec.value) {
18
+ ctx.isFormDirty = true;
19
+ }
20
+ };
21
+
22
+ const watchDirtyState = () => {
23
+ return watchEffect(() => {
24
+ if (ctx.isFormDirty) {
25
+ window.onbeforeunload = () => !isDirty();
26
+ glibevent.onbeforewindowsopen = () => !isDirty();
27
+ glibevent.onbeforewindowsclose = () => !isDirty();
28
+ glibevent.onbeforewindowsreload = () => !isDirty();
29
+ } else {
30
+ window.onbeforeunload = null;
31
+ glibevent.onbeforewindowsopen = null;
32
+ glibevent.onbeforewindowsclose = null;
33
+ glibevent.onbeforewindowsreload = null;
34
+ }
35
+ });
36
+ };
37
+
38
+ return { clearDirtyState, updateDirtyState, watchDirtyState, isDirty };
39
+ }
@@ -1,33 +1,19 @@
1
1
  <template>
2
- <v-btn
3
- color="indigo"
4
- @click="$onClick()"
5
- :disabled="$isBusy"
6
- :style="genericStyles()"
7
- :class="$classes()"
8
- :href="$href()"
9
- :rel="$rel()"
10
- fab
11
- fixed
12
- bottom
13
- right
14
- >
15
- <!-- Using `white--text` as recommended in https://github.com/vuetifyjs/vuetify/issues/1030 -->
16
- <common-icon v-if="spec.icon" :spec="spec.icon" class="white--text" />
17
- </v-btn>
2
+ <div class="fab">
3
+ <common-button :spec="spec"></common-button>
4
+ </div>
18
5
  </template>
19
6
 
20
7
  <script>
21
8
  export default {
22
9
  props: ['spec']
23
- }
10
+ };
24
11
  </script>
25
12
 
26
13
  <style scoped>
27
-
28
- /* Attempts to fix icon alignment issue on production */
29
- .v-btn--floating .v-icon {
30
- display: inline-flex
14
+ .fab {
15
+ position: fixed;
16
+ right: 48px;
17
+ bottom: 24px;
31
18
  }
32
-
33
19
  </style>
@@ -4,7 +4,17 @@
4
4
  :multiple="spec.multiple" :disabled="spec.readOnly" :clearable="!spec.readOnly" :hint="spec.hint"
5
5
  :placeholder="spec.placeholder" :rules="rules" persistent-hint :append-icon="append.icon" validate-on="blur"
6
6
  item-title='text' :variant="variant" :closable-chips="spec.multiple" :density="density" persistent-placeholder
7
- @update:modelValue="onChange" />
7
+ @update:modelValue="onChange">
8
+
9
+ <template #item="data">
10
+ <v-list-subheader v-if="data.props.title.header">
11
+ {{ data.props.title.header }}
12
+ </v-list-subheader>
13
+ <v-divider v-else-if="data.props.title.divider"></v-divider>
14
+ <v-list-item v-else v-bind="data.props" />
15
+ </template>
16
+
17
+ </v-autocomplete>
8
18
 
9
19
  <input v-for="(item, index) in values" :key="index" type="hidden" :name="fieldName" :value="item" />
10
20
  </div>
@@ -6,9 +6,9 @@
6
6
  <div ref="map" class="map"></div>
7
7
 
8
8
  <div style="padding-top: 8px">
9
- <fields-text ref="latField" v-if="spec.latitudeField" :spec="spec.latitudeField"></fields-text>
10
- <fields-text ref="lngField" v-if="spec.longitudeField" :spec="spec.longitudeField"></fields-text>
11
- <fields-text ref="zoomField" v-if="spec.zoomField" :spec="spec.zoomField"></fields-text>
9
+ <fields-text ref="latField" v-if="spec.latitudeField" :spec="spec.latitudeField" type="text"></fields-text>
10
+ <fields-text ref="lngField" v-if="spec.longitudeField" :spec="spec.longitudeField" type="text"></fields-text>
11
+ <fields-text ref="zoomField" v-if="spec.zoomField" :spec="spec.zoomField" type="number"></fields-text>
12
12
  </div>
13
13
 
14
14
  </div>
@@ -1,7 +1,10 @@
1
1
  <template>
2
- <v-img :eager="true" :src="spec.url || spec.base64Data" @click="$onClick()" :style="styles" :class="$classes()"
3
- :max-width="width" :max-height="height" cover>
4
- </v-img>
2
+ <common-badge v-if="spec.badge" :spec="spec">
3
+ <img :src="spec.url || spec.base64Data" @click="$onClick()" :style="styles" :height="height" :width="width"
4
+ :class="$classes()" />
5
+ </common-badge>
6
+ <img v-else :src="spec.url || spec.base64Data" @click="$onClick()" :style="styles" :height="height" :width="width"
7
+ :class="$classes()" />
5
8
  </template>
6
9
 
7
10
  <script>
@@ -21,7 +24,7 @@ export default {
21
24
  $ready() {
22
25
  // this.$initAccessories();
23
26
 
24
- const styles = this.genericStyles(Object.assign({}, this.spec));
27
+ let styles = this.genericStyles(Object.assign({}, this.spec));
25
28
  this.styles = styles;
26
29
 
27
30
  if (this.spec.url) {
@@ -58,9 +61,9 @@ export default {
58
61
  }
59
62
  }
60
63
 
61
- Object.assign(this.styles, { width, height });
62
- this.width = width
63
- this.height = height
64
+ Object.assign(this.styles, { width, height, objectFit: 'contain' });
65
+ this.width = width;
66
+ this.height = height;
64
67
  },
65
68
  fitCrop(image) {
66
69
  let width, height;
@@ -73,10 +76,12 @@ export default {
73
76
  height = `${this.spec.height}px`;
74
77
  }
75
78
 
76
- Object.assign(this.styles, { width, height });
77
- this.width = width
78
- this.height = height
79
+ Object.assign(this.styles, { width, height, objectFit: 'cover' });
80
+ this.width = width;
81
+ this.height = height;
79
82
  }
80
83
  }
81
84
  };
82
85
  </script>
86
+
87
+ <style scoped></style>
@@ -1,6 +1,7 @@
1
- import { vueApp } from "../../store";
2
1
  import Hash from "../../utils/hash";
3
2
  import { fieldModels, watchFieldModels } from "../composable/conditional";
3
+ import { useDirtyState } from "../composable/dirtyState";
4
+ const { updateDirtyState } = useDirtyState();
4
5
 
5
6
  export default {
6
7
  data: function () {
@@ -21,7 +22,7 @@ export default {
21
22
 
22
23
  Object.assign(fieldModels, { [this.fieldName]: this.$internalizeValue(val) });
23
24
 
24
- this._checkDirtyState(val, oldVal);
25
+ updateDirtyState(val, oldVal, this.spec);
25
26
  },
26
27
  spec: {
27
28
  handler(spec, oldSpec) {
@@ -52,31 +53,36 @@ export default {
52
53
  });
53
54
  }
54
55
  },
56
+ unmounted() {
57
+ if (fieldModels[this.fieldName]) {
58
+ delete fieldModels[this.fieldName];
59
+ }
60
+ },
55
61
  methods: {
56
62
  // TODO: Deprecated
57
63
  genericStyles(spec) {
58
64
  return this.$styles(spec);
59
65
  },
60
- _checkDirtyState(val, oldVal) {
61
- const dirtyCheckEnabled = !this.spec.disableDirtyCheck;
62
- // Make sure value has changed and make sure that it is different from the original value.
63
- // Be strict with this so it doesn't execute when the component is just initializing (e.g value changing
64
- // from `null` to `this.spec.value`).
65
- if (dirtyCheckEnabled && val != oldVal && val != this.spec.value) {
66
- const topDialog = Utils.launch.dialog.topDialog();
67
- if (topDialog) {
68
- if (!topDialog.isFormDirty) {
69
- console.debug("Dialog form is now dirty");
70
- topDialog.isFormDirty = true;
71
- }
72
- } else {
73
- if (vueApp.isFormDirty) {
74
- console.log("Window form is now dirty");
75
- vueApp.isFormDirty = true;
76
- }
77
- }
78
- }
79
- },
66
+ // _checkDirtyState(val, oldVal) {
67
+ // const dirtyCheckEnabled = !this.spec.disableDirtyCheck;
68
+ // // Make sure value has changed and make sure that it is different from the original value.
69
+ // // Be strict with this so it doesn't execute when the component is just initializing (e.g value changing
70
+ // // from `null` to `this.spec.value`).
71
+ // if (dirtyCheckEnabled && val != oldVal && val != this.spec.value) {
72
+ // const topDialog = Utils.launch.dialog.topDialog();
73
+ // if (topDialog) {
74
+ // if (!topDialog.isFormDirty) {
75
+ // console.debug("Dialog form is now dirty");
76
+ // topDialog.isFormDirty = true;
77
+ // }
78
+ // } else {
79
+ // if (vueApp.isFormDirty) {
80
+ // console.log("Window form is now dirty");
81
+ // vueApp.isFormDirty = true;
82
+ // }
83
+ // }
84
+ // }
85
+ // },
80
86
  // NOTE: Styles are dynamic, do not save it in $ready().
81
87
  $styles(spec) {
82
88
  const properties = spec || this.spec;
package/nav/dialog.vue CHANGED
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <v-dialog :model-value="model" :width="spec.width || 600" :dark="false" :fullscreen="fullscreen" :sm-and-down="false"
3
- :persistent="!spec.closeOnBlur">
3
+ :persistent="true" @click:outside="clickOutside">
4
4
  <v-card :style="hamburgerStyles" class="hamburger">
5
5
 
6
6
  <panels-responsive v-if="header" :spec="header" />
@@ -43,10 +43,17 @@
43
43
  import Http from "../utils/http";
44
44
  import Action from "../action";
45
45
  import FormPanel from "../components/panels/form.vue";
46
- import { vueApp } from "../store";
47
-
46
+ import { useDirtyState } from "../components/composable/dirtyState";
47
+ import { dialogStates } from "../store";
48
+ import { reactive } from "vue";
48
49
 
49
50
  export default {
51
+ setup() {
52
+ const { isDirty } = useDirtyState();
53
+ const context = reactive({ isFormDirty: false });
54
+
55
+ return { isDirty, context };
56
+ },
50
57
  components: {
51
58
  "panels-form": FormPanel,
52
59
  },
@@ -66,8 +73,6 @@ export default {
66
73
  url: null,
67
74
  urlLoaded: false,
68
75
  disableCloseButton: false,
69
- isFormDirty: false,
70
- isFormSubmitted: false,
71
76
  isMobile: false,
72
77
  formSpec: null,
73
78
  };
@@ -98,10 +103,19 @@ export default {
98
103
  watch: {
99
104
  model: function (val, oldVal) {
100
105
  if (!val) {
101
- this.stack.remove(this);
106
+ if (!this.isDirty()) {
107
+ this.stack.remove(this);
108
+ dialogStates.remove(this.context);
109
+ } else {
110
+ this.model = true;
111
+ }
102
112
  }
103
113
  },
104
114
  },
115
+ mounted() {
116
+ this.stack.push(this);
117
+ dialogStates.push(this.context);
118
+ },
105
119
  methods: {
106
120
  $mounted() {
107
121
  window.addEventListener(
@@ -115,7 +129,6 @@ export default {
115
129
  $ready() {
116
130
  this.onResize();
117
131
  this.show(false);
118
- this.stack.push(this);
119
132
 
120
133
  this.$nextTick(() => {
121
134
  this.updateMainHeight();
@@ -127,7 +140,10 @@ export default {
127
140
  // this.stack.remove(this);
128
141
  // },
129
142
  close() {
130
- if (Utils.http.proceedEvenWhenDirty()) {
143
+ this.model = false;
144
+ },
145
+ clickOutside() {
146
+ if (this.spec.closeOnBlur) {
131
147
  this.model = false;
132
148
  }
133
149
  },
@@ -137,11 +153,9 @@ export default {
137
153
  Action.execute(onClick, this);
138
154
  },
139
155
  reload(newSpec) {
140
- if (Utils.http.proceedEvenWhenDirty()) {
141
- this.spec.url = newSpec.url;
142
- this.urlLoaded = false;
143
- this.show(true);
144
- }
156
+ this.spec.url = newSpec.url;
157
+ this.urlLoaded = false;
158
+ this.show(true);
145
159
  },
146
160
  onResize() {
147
161
  this.isMobile = window.innerWidth < 600;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glib-web",
3
- "version": "3.5.3",
3
+ "version": "3.5.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,33 +1,33 @@
1
1
  export default {
2
2
  install: (Vue, options) => {
3
3
  Vue.mixin({
4
- mounted: function () {
5
- let spec = this.spec;
6
- if (spec && spec.id && this.$registryEnabled()) {
7
- const id = spec.id
8
- const existingComponent = GLib.component.findById(id)
9
- // A component with the same ID in a different page shouldn't be considered a
10
- // duplicate. See `utils/components#deregister` for more details.
11
- if (existingComponent && existingComponent._mountedUrl == this._mountedUrl) {
12
- console.warn(
13
- "Duplicate component ID:",
14
- id,
15
- "Existing:",
16
- GLib.component.vueName(existingComponent),
17
- "New:",
18
- GLib.component.vueName(this)
19
- );
20
- }
21
- GLib.component.register(id, this);
22
- }
23
- },
24
- unmounted: function () {
25
- let spec = this.spec;
26
- if (spec && spec.id && this.$registryEnabled()) {
27
- GLib.component.deregister(spec.id, this);
28
- }
29
- },
30
4
  methods: {
5
+ $ready() {
6
+ let spec = this.spec;
7
+ if (spec && spec.id && this.$registryEnabled()) {
8
+ const id = spec.id;
9
+ const existingComponent = GLib.component.findById(id);
10
+ // A component with the same ID in a different page shouldn't be considered a
11
+ // duplicate. See `utils/components#deregister` for more details.
12
+ if (existingComponent) {
13
+ console.warn(
14
+ "Duplicate component ID:",
15
+ id,
16
+ "Existing:",
17
+ GLib.component.vueName(existingComponent),
18
+ "New:",
19
+ GLib.component.vueName(this)
20
+ );
21
+ }
22
+ GLib.component.register(id, this);
23
+ }
24
+ },
25
+ $tearDown() {
26
+ let spec = this.spec;
27
+ if (spec && spec.id && this.$registryEnabled()) {
28
+ GLib.component.deregister(spec.id, this);
29
+ }
30
+ },
31
31
  $registryEnabled() {
32
32
  // Common classes such as `_select` need to return false so that it doesn't override its parent (e.g. `select`).
33
33
  return true;
package/store.js CHANGED
@@ -15,4 +15,20 @@ export const vueApp = reactive({
15
15
  temp: {},
16
16
  richTextValues: {},
17
17
  draggedComponent: null
18
- });
18
+ });
19
+
20
+ export const dialogStates = reactive([]);
21
+
22
+ export const glibevent = reactive({
23
+ onbeforewindowsopen: null,
24
+ onbeforewindowsclose: null,
25
+ onbeforewindowsreload: null
26
+ });
27
+
28
+ export function executeGlobalEvent(name) {
29
+ if (!glibevent[name]) {
30
+ return true;
31
+ }
32
+
33
+ return glibevent[name]();
34
+ }
@@ -32,7 +32,7 @@ export default class {
32
32
  // component will get registered before the old component gets deregistered, in which case we
33
33
  // should skip the deregistering or else we'd accidentally deregister the new component.
34
34
  if (this._registry[id] == component) {
35
- console.debug("Deregistering component with ID", id)
35
+ console.debug("Deregistering component with ID", id);
36
36
  delete this._registry[id];
37
37
  }
38
38
  }
@@ -42,13 +42,13 @@ export default class {
42
42
  // return component.$options._componentTag;
43
43
  if (!component.name && component.spec) {
44
44
  if (component.spec.view == 'panels/fullPageForm') {
45
- component.name = 'panels-form'
45
+ component.name = 'panels-form';
46
46
  } else {
47
- component.name = (component.spec.view || '').replace('/', '-').replace('-v1', '')
47
+ component.name = (component.spec.view || '').replace('/', '-').replace('-v1', '');
48
48
  }
49
49
  }
50
50
 
51
- return component.name
51
+ return component.name;
52
52
  }
53
53
 
54
54
  static async preserveScroll(promise) {
package/utils/form.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { vueApp } from "../store";
2
+
1
3
  export default class {
2
4
  // Execution flows:
3
5
  // Button -> Button.onClick() -> Utils.http.execute() -- doesn't save autofill values
@@ -7,7 +9,8 @@ export default class {
7
9
  // Analogous to Rails' form_with's `local: true`
8
10
  if (form.dataset.local) {
9
11
  // Prevent onUnload dirty prompt.
10
- Utils.http.clearDirtyState();
12
+ // Utils.http.clearDirtyState();
13
+ vueApp.isFormDirty = false;
11
14
  form.submit();
12
15
  } else {
13
16
  const url = overrideUrl ? overrideUrl : form.getAttribute("action");
package/utils/http.js CHANGED
@@ -4,7 +4,6 @@ import { nextTick } from 'vue';
4
4
  import { vueApp } from "../store";
5
5
 
6
6
  let loading = false;
7
- const dirtyPrompt = "Changes you made have not been saved. Are you sure?";
8
7
 
9
8
  class HttpRequest {
10
9
  constructor() {
@@ -72,9 +71,9 @@ export default class {
72
71
  const domainMatched = window.location.hostname == url.hostname;
73
72
 
74
73
  // If this is an external domain, we rely on `onUnload()` instead.
75
- if (domainMatched && !this.proceedEvenWhenDirty()) {
76
- return;
77
- }
74
+ // if (domainMatched && !this.proceedEvenWhenDirty()) {
75
+ // return;
76
+ // }
78
77
 
79
78
  if (Utils.settings.reactive && domainMatched) {
80
79
  const currentUrl = window.location.href;
@@ -268,31 +267,25 @@ export default class {
268
267
  });
269
268
  }
270
269
 
271
- static promptIfDirtyOnUnload() {
272
- window.onbeforeunload = () => {
273
- vueApp.isFormDirty ? dirtyPrompt : null;
274
- };
275
- }
276
-
277
- static clearDirtyState() {
278
- vueApp.isFormDirty = false;
279
- }
270
+ // static clearDirtyState() {
271
+ // vueApp.isFormDirty = false;
272
+ // }
280
273
 
281
274
  static notifyFormSubmitted() {
282
275
  vueApp.isFormSubmitted = true;
283
276
  }
284
277
 
285
278
  // `context` can be either window or dialog.
286
- static proceedEvenWhenDirty() {
287
- const dirtyContext = vueApp;
288
- // Don't prompt if this is a result of form submission
289
- if (
290
- dirtyContext.isFormDirty &&
291
- !dirtyContext.isFormSubmitted &&
292
- !confirm(dirtyPrompt)
293
- ) {
294
- return false;
295
- }
296
- return true;
297
- }
279
+ // static proceedEvenWhenDirty() {
280
+ // const dirtyContext = vueApp;
281
+ // // Don't prompt if this is a result of form submission
282
+ // if (
283
+ // dirtyContext.isFormDirty &&
284
+ // !dirtyContext.isFormSubmitted &&
285
+ // !confirm(dirtyPrompt)
286
+ // ) {
287
+ // return false;
288
+ // }
289
+ // return true;
290
+ // }
298
291
  }