glib-web 0.6.17 → 0.8.2
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/actions/windows/open.js +1 -1
- package/actions/windows/reload.js +0 -15
- package/app.vue +1 -0
- package/components/component.vue +2 -2
- package/components/mixins/styles.js +12 -2
- package/components/mixins/table/import.js +0 -1
- package/components/panels/horizontal.vue +25 -4
- package/components/{socialSharing.vue → shareButton.vue} +20 -1
- package/index.js +5 -1
- package/nav/dialog.vue +1 -1
- package/nav/drawer.vue +83 -2
- package/package.json +1 -1
- package/utils/form.js +2 -1
- package/utils/history.js +9 -0
- package/utils/http.js +33 -1
- package/utils/uploader.js +0 -7
package/actions/windows/open.js
CHANGED
|
@@ -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
package/components/component.vue
CHANGED
|
@@ -113,7 +113,7 @@ import SelectBanner from "./banners/select";
|
|
|
113
113
|
import LineChart from "./charts/line";
|
|
114
114
|
import ColumnChart from "./charts/column";
|
|
115
115
|
|
|
116
|
-
import
|
|
116
|
+
import ShareButton from "./shareButton";
|
|
117
117
|
|
|
118
118
|
export default {
|
|
119
119
|
components: {
|
|
@@ -145,7 +145,7 @@ export default {
|
|
|
145
145
|
"views-map": Map,
|
|
146
146
|
"views-tabBar": TabBar,
|
|
147
147
|
"views-calendar": Calendar,
|
|
148
|
-
"views-
|
|
148
|
+
"views-shareButton": ShareButton,
|
|
149
149
|
|
|
150
150
|
"fields-hidden": HiddenField,
|
|
151
151
|
"fields-text": TextField,
|
|
@@ -46,8 +46,18 @@ export default {
|
|
|
46
46
|
this.$internalizeValue(val)
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
-
//
|
|
50
|
-
//
|
|
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: {
|
|
@@ -14,13 +14,22 @@ export default {
|
|
|
14
14
|
computed: {
|
|
15
15
|
cssClasses: function() {
|
|
16
16
|
const classes = this.$classes().concat("layouts-horizontal");
|
|
17
|
-
|
|
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
|
-
|
|
71
|
-
|
|
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>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
:url="spec.url"
|
|
6
6
|
:title="spec.title"
|
|
7
7
|
:description="spec.description"
|
|
8
|
-
:quote="spec.
|
|
8
|
+
:quote="spec.facebookQuote"
|
|
9
9
|
:hashtags="spec.hashtags"
|
|
10
10
|
:twitter-user="spec.twitterUser"
|
|
11
11
|
>
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
</template>
|
|
17
17
|
|
|
18
18
|
<script>
|
|
19
|
+
import Action from "../action";
|
|
20
|
+
|
|
19
21
|
export default {
|
|
20
22
|
props: {
|
|
21
23
|
spec: { type: Object, required: true }
|
|
@@ -23,6 +25,10 @@ export default {
|
|
|
23
25
|
data() {
|
|
24
26
|
return {
|
|
25
27
|
config: {
|
|
28
|
+
copy: {
|
|
29
|
+
icon: "far fah fa-lg fa-copy",
|
|
30
|
+
color: "#183153"
|
|
31
|
+
},
|
|
26
32
|
email: {
|
|
27
33
|
icon: "far fah fa-lg fa-envelope",
|
|
28
34
|
color: "#333333"
|
|
@@ -85,6 +91,19 @@ export default {
|
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
};
|
|
94
|
+
},
|
|
95
|
+
mounted() {
|
|
96
|
+
if (this.spec.onClick) {
|
|
97
|
+
const parentElement = this.$el.parentElement;
|
|
98
|
+
// remove default event listener
|
|
99
|
+
this.$el.replaceWith(this.$el.cloneNode(true));
|
|
100
|
+
// manually attach event listener because @click not working
|
|
101
|
+
parentElement
|
|
102
|
+
.querySelector(`a.${this.$el.classList[0]}`)
|
|
103
|
+
.addEventListener("click", () => {
|
|
104
|
+
Action.execute(this.spec.onClick, this);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
88
107
|
}
|
|
89
108
|
};
|
|
90
109
|
</script>
|
package/index.js
CHANGED
|
@@ -39,7 +39,7 @@ Vue.use(VueYoutube);
|
|
|
39
39
|
// })
|
|
40
40
|
|
|
41
41
|
import VueSocialSharing from "vue-social-sharing";
|
|
42
|
-
Vue.use(VueSocialSharing);
|
|
42
|
+
Vue.use(VueSocialSharing, { networks: { copy: "" } });
|
|
43
43
|
|
|
44
44
|
import "./extensions/string.js";
|
|
45
45
|
import "./extensions/array.js";
|
|
@@ -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/nav/dialog.vue
CHANGED
package/nav/drawer.vue
CHANGED
|
@@ -50,6 +50,75 @@
|
|
|
50
50
|
import NavDrawerButton from "./drawerButton";
|
|
51
51
|
import NavDrawerLabel from "./drawerLabel";
|
|
52
52
|
|
|
53
|
+
const activeClass = (element, action) => {
|
|
54
|
+
let indicator = element.querySelector(".indicator");
|
|
55
|
+
if (indicator) {
|
|
56
|
+
indicator.classList[action]("active");
|
|
57
|
+
}
|
|
58
|
+
let icon = element.querySelector(".v-list-item__action");
|
|
59
|
+
if (icon) {
|
|
60
|
+
icon.classList[action]("active");
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const comparePathname = (hrefPathname, locationPathname) => {
|
|
65
|
+
let value = 0;
|
|
66
|
+
const loc = locationPathname.split("/").filter(v => v);
|
|
67
|
+
const hr = hrefPathname.split("/").filter(v => v);
|
|
68
|
+
|
|
69
|
+
if (loc.length == hr.length) value = value + 1;
|
|
70
|
+
|
|
71
|
+
loc.forEach((route, index) => {
|
|
72
|
+
if (!hr[index]) {
|
|
73
|
+
value = value + 0;
|
|
74
|
+
} else if (hr[index] == route) {
|
|
75
|
+
value = value + 1;
|
|
76
|
+
} else if (hr[index] != route) {
|
|
77
|
+
value = value - 2;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return value;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const navigationMap = (element, locationPathname) => {
|
|
85
|
+
return Array.from(element.querySelectorAll(".nav-item")).reduce(
|
|
86
|
+
(prev, curr) => {
|
|
87
|
+
let a = curr.querySelector("a");
|
|
88
|
+
if (a) {
|
|
89
|
+
let obj = {};
|
|
90
|
+
obj[new URL(a.href).pathname] = {
|
|
91
|
+
element: curr,
|
|
92
|
+
matchValue: comparePathname(
|
|
93
|
+
new URL(a.href).pathname,
|
|
94
|
+
locationPathname
|
|
95
|
+
)
|
|
96
|
+
};
|
|
97
|
+
return Object.assign(prev, obj);
|
|
98
|
+
} else {
|
|
99
|
+
return prev;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{}
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const getActiveNav = navigationMap => {
|
|
107
|
+
let currentRoute = "";
|
|
108
|
+
let maxMatchValue = null;
|
|
109
|
+
|
|
110
|
+
for (let route in navigationMap) {
|
|
111
|
+
if (!maxMatchValue) {
|
|
112
|
+
maxMatchValue = navigationMap[route].matchValue;
|
|
113
|
+
currentRoute = route;
|
|
114
|
+
} else if (maxMatchValue < navigationMap[route].matchValue) {
|
|
115
|
+
maxMatchValue = navigationMap[route].matchValue;
|
|
116
|
+
currentRoute = route;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return navigationMap[currentRoute];
|
|
120
|
+
};
|
|
121
|
+
|
|
53
122
|
export default {
|
|
54
123
|
components: {
|
|
55
124
|
"nav-drawerButton": NavDrawerButton,
|
|
@@ -98,6 +167,8 @@ export default {
|
|
|
98
167
|
},
|
|
99
168
|
methods: {
|
|
100
169
|
$ready() {
|
|
170
|
+
this.updateNavigationStyle(window.location);
|
|
171
|
+
|
|
101
172
|
// this.$el.addEventListener('drawers/clickButton', () => { this.updateState(false) }, false)
|
|
102
173
|
this.$el.addEventListener("drawers/clickButton", () => {
|
|
103
174
|
if (!this.permanent) {
|
|
@@ -113,6 +184,15 @@ export default {
|
|
|
113
184
|
this.cssClasses.includes("mini") ||
|
|
114
185
|
(this.cssClasses.includes("mini-lg-and-above") && this.lgAndAbove)
|
|
115
186
|
);
|
|
187
|
+
},
|
|
188
|
+
updateNavigationStyle(urlObject) {
|
|
189
|
+
const navMap = navigationMap(this.$el, urlObject.pathname);
|
|
190
|
+
// remove all active class
|
|
191
|
+
for (let route in navMap) {
|
|
192
|
+
activeClass(navMap[route].element, "remove");
|
|
193
|
+
}
|
|
194
|
+
// add active class to nav
|
|
195
|
+
activeClass(getActiveNav(navMap).element, "add");
|
|
116
196
|
}
|
|
117
197
|
}
|
|
118
198
|
};
|
|
@@ -122,12 +202,13 @@ export default {
|
|
|
122
202
|
.nav-item {
|
|
123
203
|
position: relative;
|
|
124
204
|
}
|
|
125
|
-
.nav-item:hover .indicator
|
|
205
|
+
.nav-item:hover .indicator,
|
|
206
|
+
.nav-item .indicator.active {
|
|
126
207
|
display: flex;
|
|
127
208
|
position: absolute;
|
|
128
209
|
width: 3px;
|
|
129
210
|
height: 65%;
|
|
130
|
-
margin:
|
|
211
|
+
margin: 8px 0;
|
|
131
212
|
justify-content: center;
|
|
132
213
|
align-items: center;
|
|
133
214
|
background-color: white;
|
package/package.json
CHANGED
package/utils/form.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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"]);
|
|
@@ -60,6 +67,7 @@ export default class {
|
|
|
60
67
|
} else {
|
|
61
68
|
window.location = Utils.url.htmlUrl(properties["url"]);
|
|
62
69
|
}
|
|
70
|
+
// bus.$emit("glibWebHttpLoad", new URL(Utils.url.htmlUrl(properties["url"])));
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
static analyticsHeaders(component) {
|
|
@@ -202,8 +210,32 @@ export default class {
|
|
|
202
210
|
|
|
203
211
|
// Queue the execution so the first isStale has time to resets state before handler gets executed
|
|
204
212
|
setTimeout(() => {
|
|
213
|
+
window.vueApp.isFormSubmitted = false;
|
|
214
|
+
window.vueApp.isFormDirty = false;
|
|
205
215
|
handler();
|
|
206
216
|
window.vueApp.isStale = false;
|
|
207
217
|
}, 0);
|
|
208
218
|
}
|
|
219
|
+
|
|
220
|
+
static promptIfDirtyOnUnload() {
|
|
221
|
+
window.onbeforeunload = function() {
|
|
222
|
+
return window.vueApp.isFormDirty ? dirtyPrompt : null;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static notifyFormSubmitted() {
|
|
227
|
+
window.vueApp.isFormSubmitted = true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static proceedEvenWhenDirty() {
|
|
231
|
+
// Don't prompt if this is a result of form submission
|
|
232
|
+
if (
|
|
233
|
+
window.vueApp.isFormDirty &&
|
|
234
|
+
!window.vueApp.isFormSubmitted &&
|
|
235
|
+
!confirm(dirtyPrompt)
|
|
236
|
+
) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
209
241
|
}
|
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;
|