glib-web 3.27.2 → 3.28.0
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/action.js +4 -0
- package/actions/dialogs/closeAll.js +18 -0
- package/actions/logics/set.js +8 -7
- package/actions/windows/closeAllWithOpen.js +18 -0
- package/actions/windows/closeWithOpen.js +2 -2
- package/components/component.vue +2 -0
- package/components/composable/alert.js +15 -0
- package/components/composable/dropable.js +8 -1
- package/components/composable/validation.js +21 -0
- package/components/fields/file.vue +3 -1
- package/components/fields/sign.vue +132 -0
- package/components/tabBar.vue +21 -8
- package/components/treeView.vue +3 -6
- package/package.json +1 -1
- package/utils/history.js +6 -3
- package/utils/http.js +7 -2
- package/utils/uploader.js +67 -3
package/action.js
CHANGED
|
@@ -12,6 +12,7 @@ import ActionsDialogsAlert from "./actions/dialogs/alert";
|
|
|
12
12
|
import ActionsDialogsShow from "./actions/dialogs/show";
|
|
13
13
|
import ActionsDialogsOpen from "./actions/dialogs/open";
|
|
14
14
|
import ActionsDialogsClose from "./actions/dialogs/close";
|
|
15
|
+
import ActionsDialogsCloseAll from './actions/dialogs/closeAll';
|
|
15
16
|
import ActionsDialogsReload from "./actions/dialogs/reload";
|
|
16
17
|
import ActionsDialogsOptions from "./actions/dialogs/options";
|
|
17
18
|
import ActionsDialogsNotification from "./actions/dialogs/notification";
|
|
@@ -25,6 +26,7 @@ import ActionsSheetsSelect from "./actions/sheets/select";
|
|
|
25
26
|
|
|
26
27
|
import ActionsWindowsClose from "./actions/windows/close";
|
|
27
28
|
import ActionsWindowsCloseAll from "./actions/windows/closeAll";
|
|
29
|
+
import ActionsWindowsCloseAllWithOpen from "./actions/windows/closeAllWithOpen";
|
|
28
30
|
import ActionsWindowsOpen from "./actions/windows/open";
|
|
29
31
|
import ActionsWindowsOpenWeb from "./actions/windows/openWeb";
|
|
30
32
|
import ActionsWindowsReload from "./actions/windows/reload";
|
|
@@ -98,6 +100,7 @@ const actions = {
|
|
|
98
100
|
"dialogs/show": ActionsDialogsShow,
|
|
99
101
|
"dialogs/open": ActionsDialogsOpen,
|
|
100
102
|
"dialogs/close": ActionsDialogsClose,
|
|
103
|
+
"dialogs/closeAll": ActionsDialogsCloseAll,
|
|
101
104
|
"dialogs/reload": ActionsDialogsReload,
|
|
102
105
|
"dialogs/options": ActionsDialogsOptions,
|
|
103
106
|
"dialogs/notification": ActionsDialogsNotification,
|
|
@@ -111,6 +114,7 @@ const actions = {
|
|
|
111
114
|
|
|
112
115
|
"windows/close": ActionsWindowsClose,
|
|
113
116
|
"windows/closeAll": ActionsWindowsCloseAll,
|
|
117
|
+
"windows/closeAllWithOpen": ActionsWindowsCloseAllWithOpen,
|
|
114
118
|
"windows/open": ActionsWindowsOpen,
|
|
115
119
|
"windows/openWeb": ActionsWindowsOpenWeb,
|
|
116
120
|
"windows/reload": ActionsWindowsReload,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { nextTick } from "vue";
|
|
2
|
+
import { closeAllDialog } from "../../store";
|
|
3
|
+
import Action from "../../action";
|
|
4
|
+
|
|
5
|
+
export default class {
|
|
6
|
+
execute(spec, component) {
|
|
7
|
+
closeAllDialog();
|
|
8
|
+
|
|
9
|
+
const { onClose } = spec;
|
|
10
|
+
|
|
11
|
+
if (onClose) {
|
|
12
|
+
nextTick(() => {
|
|
13
|
+
Action.execute(onClose, component);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
}
|
package/actions/logics/set.js
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import jsonLogic from 'json-logic-js';
|
|
2
|
+
import { fieldModels } from "../../components/composable/conditional";
|
|
2
3
|
|
|
3
|
-
const subscript = function(a, b) {
|
|
4
|
+
const subscript = function (a, b) {
|
|
4
5
|
if (a) {
|
|
5
6
|
if (b) { // This is expected when the dependent field doesn't have a value
|
|
6
7
|
return a[b];
|
|
7
8
|
} else {
|
|
8
|
-
console.info('Null subscript')
|
|
9
|
+
console.info('Null subscript');
|
|
9
10
|
}
|
|
10
11
|
} else {
|
|
11
|
-
console.error('Left hand operator not found')
|
|
12
|
+
console.error('Left hand operator not found');
|
|
12
13
|
}
|
|
13
14
|
};
|
|
14
15
|
jsonLogic.add_operation("[]", subscript);
|
|
15
16
|
|
|
16
17
|
// Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing
|
|
17
|
-
const nullishCoalescing = function(a, b) {
|
|
18
|
+
const nullishCoalescing = function (a, b) {
|
|
18
19
|
return a ?? b;
|
|
19
|
-
}
|
|
20
|
+
};
|
|
20
21
|
jsonLogic.add_operation("??", nullishCoalescing);
|
|
21
22
|
|
|
22
23
|
export default class {
|
|
@@ -28,9 +29,9 @@ export default class {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
Utils.type.ifObject(spec.conditionalData, (properties) => {
|
|
31
|
-
const data = {}
|
|
32
|
+
const data = {};
|
|
32
33
|
for (const key in properties) {
|
|
33
|
-
data[key] = jsonLogic.apply(properties[key], spec.variables);
|
|
34
|
+
data[key] = jsonLogic.apply(properties[key], spec.variables || fieldModels);
|
|
34
35
|
}
|
|
35
36
|
targetComponent.action_merge(data);
|
|
36
37
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import Action from "../../action";
|
|
2
|
+
|
|
3
|
+
export default class {
|
|
4
|
+
execute(properties, component) {
|
|
5
|
+
Utils.history.destroy();
|
|
6
|
+
|
|
7
|
+
// Don't use nextTick so that there is no visible flicker.
|
|
8
|
+
Utils.http.load(properties, component, true, true);
|
|
9
|
+
|
|
10
|
+
// const { url, onOpen } = properties;
|
|
11
|
+
// Action.execute({ action: 'windows/open', url }, component);
|
|
12
|
+
|
|
13
|
+
const { onOpen } = properties;
|
|
14
|
+
if (onOpen) {
|
|
15
|
+
Action.execute(onOpen, component);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -2,7 +2,7 @@ export default class {
|
|
|
2
2
|
execute(properties, component) {
|
|
3
3
|
Utils.history.backWithoutRender()
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
Utils.http.load(properties, component, true);
|
|
5
|
+
// Don't use nextTick so that there is no visible flicker.
|
|
6
|
+
Utils.http.load(properties, component, true, true);
|
|
7
7
|
}
|
|
8
8
|
}
|
package/components/component.vue
CHANGED
|
@@ -73,6 +73,7 @@ const RichTextField = defineAsyncComponent(() => import("./fields/richText.vue")
|
|
|
73
73
|
// import NewRichTextField from "./fields/newRichText.vue";
|
|
74
74
|
const FileField = defineAsyncComponent(() => import("./fields/file.vue"));
|
|
75
75
|
const MultiUploadField = defineAsyncComponent(() => import("./fields/multiUpload.vue"));
|
|
76
|
+
import SignField from "./fields/sign.vue";
|
|
76
77
|
import AutocompleteField from "./fields/autocomplete.vue";
|
|
77
78
|
import SelectField from "./fields/select.vue";
|
|
78
79
|
import TimeZoneField from "./fields/timeZone.vue";
|
|
@@ -168,6 +169,7 @@ export default {
|
|
|
168
169
|
// "fields-newRichText": NewRichTextField,
|
|
169
170
|
"fields-file": FileField,
|
|
170
171
|
"fields-multiUpload": MultiUploadField,
|
|
172
|
+
"fields-sign": SignField,
|
|
171
173
|
"fields-autocomplete": AutocompleteField,
|
|
172
174
|
"fields-select": SelectField,
|
|
173
175
|
"fields-timeZone": TimeZoneField,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Action from '../../action';
|
|
2
|
+
|
|
3
|
+
export function showError(options) {
|
|
4
|
+
let { body, button, styleClasses } = options;
|
|
5
|
+
styleClasses ||= ['error', 'outlined'];
|
|
6
|
+
Action.execute(
|
|
7
|
+
{
|
|
8
|
+
action: 'snackbars/select',
|
|
9
|
+
message: body,
|
|
10
|
+
styleClasses,
|
|
11
|
+
buttons: [{ text: button }]
|
|
12
|
+
},
|
|
13
|
+
{}
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -103,6 +103,7 @@ function setBusyWhenUploading({ files }) {
|
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
// Deprecated, use alert.js
|
|
106
107
|
const showError = (options) => {
|
|
107
108
|
const { body, button } = options;
|
|
108
109
|
Action.execute(
|
|
@@ -139,7 +140,13 @@ function uploadFiles({ droppedFiles, files, spec, container }) {
|
|
|
139
140
|
function uploadOneFile({ files, key, spec, container, onAfterUploaded }) {
|
|
140
141
|
const { directUploadUrl, accepts } = spec;
|
|
141
142
|
let responseMessages = spec.responseMessages || {};
|
|
142
|
-
const uploader = new Uploader(
|
|
143
|
+
const uploader = new Uploader(
|
|
144
|
+
files.value[key].el,
|
|
145
|
+
directUploadUrl,
|
|
146
|
+
files.value[key].progress,
|
|
147
|
+
spec.storagePrefix,
|
|
148
|
+
spec.metadata
|
|
149
|
+
);
|
|
143
150
|
|
|
144
151
|
// track progress
|
|
145
152
|
uploader.directUploadWillStoreFileWithXHR = (request) => {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function validationRules(validation) {
|
|
2
|
+
var augmentedRules = [];
|
|
3
|
+
Utils.type.ifObject(validation, val => {
|
|
4
|
+
Utils.type.ifObject(val.required, spec => {
|
|
5
|
+
augmentedRules = augmentedRules.concat([
|
|
6
|
+
v => (v ? true : spec.message)
|
|
7
|
+
]);
|
|
8
|
+
});
|
|
9
|
+
Utils.type.ifObject(val.format, spec => {
|
|
10
|
+
augmentedRules = augmentedRules.concat([
|
|
11
|
+
v => {
|
|
12
|
+
if (v && !v.toString().match(spec.regex)) {
|
|
13
|
+
return spec.message;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
return augmentedRules;
|
|
21
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$classes()" :style="$styles()">
|
|
3
|
+
<div class="glib-canvas-container">
|
|
4
|
+
<canvas style="width: 100%; height: 100%" @mousedown="handleDrawStart" @mousemove="handleDrawing"
|
|
5
|
+
@mouseup="handleDrawEnd" @touchstart="handleDrawStart" @touchmove="handleDrawing" @touchend="handleDrawEnd"
|
|
6
|
+
ref="canvas"></canvas>
|
|
7
|
+
<div class="action" v-show="!pen.sign && touched">
|
|
8
|
+
<v-icon color="success" class="bg-white" icon="check_circle" @click="upload()"></v-icon>
|
|
9
|
+
<v-icon color="error" class="bg-white" icon="cancel" @click="clear()"></v-icon>
|
|
10
|
+
</div>
|
|
11
|
+
<v-chip v-show="signedId" class="status" color="success" size="x-small">Saved</v-chip>
|
|
12
|
+
<input type="hidden" :name="fieldName" :value="signedId" :disabled="inputDisabled" />
|
|
13
|
+
<v-input :model-value="signedId" :rules="rules"></v-input>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<style>
|
|
20
|
+
.glib-canvas-container {
|
|
21
|
+
position: relative;
|
|
22
|
+
height: 100%;
|
|
23
|
+
width: 100%;
|
|
24
|
+
border: 2px solid #9ca3af;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.glib-canvas-container .action {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
position: absolute;
|
|
31
|
+
top: 0;
|
|
32
|
+
right: 0;
|
|
33
|
+
gap: 2px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.glib-canvas-container .status {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
position: absolute;
|
|
40
|
+
top: 0;
|
|
41
|
+
left: 0;
|
|
42
|
+
margin: 4px;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
45
|
+
|
|
46
|
+
<script setup>
|
|
47
|
+
import { ref, computed, defineProps } from "vue";
|
|
48
|
+
import * as uploader from "../composable/dropable";
|
|
49
|
+
import { validationRules } from "../composable/validation";
|
|
50
|
+
|
|
51
|
+
const { makeKey, Item } = uploader.useDropableUtils();
|
|
52
|
+
const props = defineProps(['spec']);
|
|
53
|
+
|
|
54
|
+
const rules = validationRules(props.spec.validation);
|
|
55
|
+
|
|
56
|
+
const canvas = ref(null);
|
|
57
|
+
const context = computed(() => {
|
|
58
|
+
if (!canvas.value) return;
|
|
59
|
+
|
|
60
|
+
return canvas.value.getContext('2d');
|
|
61
|
+
});
|
|
62
|
+
const pen = ref({ sign: false, prevX: null, prevY: null });
|
|
63
|
+
const touched = computed(() => pen.value.prevX && pen.value.prevY);
|
|
64
|
+
|
|
65
|
+
const files = ref({});
|
|
66
|
+
const signedId = ref(undefined);
|
|
67
|
+
|
|
68
|
+
uploader.setBusyWhenUploading({ files });
|
|
69
|
+
|
|
70
|
+
function clear() {
|
|
71
|
+
context.value.clearRect(0, 0, canvas.value.width, canvas.value.height);
|
|
72
|
+
signedId.value = null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function upload() {
|
|
76
|
+
files.value = {};
|
|
77
|
+
canvas.value.toBlob((blob) => {
|
|
78
|
+
const key = makeKey();
|
|
79
|
+
const file = new File([blob], 'sign.png');
|
|
80
|
+
files.value[key] = new Item({ el: file, status: 'pending' });
|
|
81
|
+
|
|
82
|
+
uploader.uploadOneFile({ files, key, spec: props.spec, onAfterUploaded: (file) => signedId.value = file.signedId });
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function penSource(e) {
|
|
87
|
+
let source = {};
|
|
88
|
+
const touch = e.touches ? e.touches[0] : null;
|
|
89
|
+
if (touch) {
|
|
90
|
+
const canvasPos = e.target.getBoundingClientRect();
|
|
91
|
+
const posX = touch.clientX - canvasPos.x;
|
|
92
|
+
const posY = touch.clientY - canvasPos.y;
|
|
93
|
+
source = { posX, posY };
|
|
94
|
+
} else {
|
|
95
|
+
source = { posX: e.offsetX, posY: e.offsetY };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return source;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function handleDrawing(e) {
|
|
102
|
+
if (!pen.value.sign) return;
|
|
103
|
+
|
|
104
|
+
const source = penSource(e);
|
|
105
|
+
const { prevX, prevY } = pen.value;
|
|
106
|
+
|
|
107
|
+
context.value.beginPath();
|
|
108
|
+
context.value.moveTo(prevX, prevY);
|
|
109
|
+
context.value.lineTo(source.posX, source.posY);
|
|
110
|
+
context.value.stroke();
|
|
111
|
+
|
|
112
|
+
pen.value.prevX = source.posX;
|
|
113
|
+
pen.value.prevY = source.posY;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function handleDrawEnd(e) {
|
|
117
|
+
pen.value.sign = false;
|
|
118
|
+
context.value.closePath();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function handleDrawStart(e) {
|
|
122
|
+
const source = penSource(e);
|
|
123
|
+
pen.value = {
|
|
124
|
+
sign: true,
|
|
125
|
+
prevX: source.posX,
|
|
126
|
+
prevY: source.posY
|
|
127
|
+
};
|
|
128
|
+
context.value.lineWidth = 2;
|
|
129
|
+
context.value.fillRect(source.posX, source.posY, 2, 2);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
</script>
|
package/components/tabBar.vue
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :style="'width: 100%'" :class="$classes()">
|
|
3
|
-
<v-tabs v-model="tab" :bg-color="spec.backgroundColor || 'white'"
|
|
3
|
+
<v-tabs class="bordered-tab" v-model="tab" :bg-color="spec.backgroundColor || 'white'" color="primary" show-arrows
|
|
4
4
|
:height="spec.height || 60" :grow="$classes().includes('no-grow') ? false : true">
|
|
5
|
-
<v-tab v-for="(item, index) in spec.buttons" @click="handleClick(index)" :key="index" height="100%"
|
|
6
|
-
|
|
5
|
+
<v-tab class="bordered-tab" v-for="(item, index) in spec.buttons" @click="handleClick(index)" :key="index" height="100%"
|
|
6
|
+
:disabled="item.disabled" :value="index">
|
|
7
7
|
<common-badge :spec="item">
|
|
8
8
|
<div class="tab-content">
|
|
9
|
-
<common-icon :class="index == spec.activeIndex ? 'text-primary' : null" :spec="item.icon"
|
|
10
|
-
|
|
11
|
-
<span :style="{ marginLeft: '2px' }" :class="index == spec.activeIndex ? 'text-primary' : null">{{ item.text
|
|
12
|
-
}}</span>
|
|
9
|
+
<common-icon :class="index == spec.activeIndex ? 'text-primary' : null" :spec="item.icon" v-if="item.icon" />
|
|
10
|
+
<span :style="{ marginLeft: '2px' }" :class="index == spec.activeIndex ? 'text-primary' : null">{{ item.text }}</span>
|
|
13
11
|
</div>
|
|
14
12
|
</common-badge>
|
|
15
13
|
</v-tab>
|
|
@@ -50,7 +48,6 @@ export default {
|
|
|
50
48
|
this.$nextTick(function () {
|
|
51
49
|
const activeTab = this.buttons[index];
|
|
52
50
|
const onClick = activeTab?.onClick;
|
|
53
|
-
|
|
54
51
|
if (onClick) {
|
|
55
52
|
Action.execute(onClick, this);
|
|
56
53
|
}
|
|
@@ -71,6 +68,7 @@ export default {
|
|
|
71
68
|
}
|
|
72
69
|
};
|
|
73
70
|
</script>
|
|
71
|
+
|
|
74
72
|
<style lang="scss" scoped>
|
|
75
73
|
.v-tab .v-badge span {
|
|
76
74
|
// Make sure tab label is middle aligned.
|
|
@@ -89,4 +87,19 @@ export default {
|
|
|
89
87
|
display: flex;
|
|
90
88
|
padding: 5px;
|
|
91
89
|
}
|
|
90
|
+
|
|
91
|
+
//Need to use :deep in order to make the slider work
|
|
92
|
+
.bordered-tab {
|
|
93
|
+
:deep(.v-tab__slider) {
|
|
94
|
+
opacity: 1;
|
|
95
|
+
color: #E6E6E6;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.bordered-tab {
|
|
100
|
+
:deep(.v-tab--selected .v-tab__slider) {
|
|
101
|
+
opacity: 1;
|
|
102
|
+
color: #0A2A9E;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
92
105
|
</style>
|
package/components/treeView.vue
CHANGED
|
@@ -43,13 +43,14 @@
|
|
|
43
43
|
align-items: center;
|
|
44
44
|
flex-wrap: nowrap;
|
|
45
45
|
padding: 12px 24px;
|
|
46
|
-
margin-bottom: 8px;
|
|
47
46
|
cursor: pointer;
|
|
48
47
|
|
|
49
48
|
.label {
|
|
50
49
|
display: inline-flex;
|
|
51
50
|
align-items: center;
|
|
52
51
|
gap: 12px;
|
|
52
|
+
font-weight: 700;
|
|
53
|
+
color: #47495F
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -60,10 +61,6 @@
|
|
|
60
61
|
background-color: #47495F0D;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
.gtreeview .parent.active label {
|
|
64
|
-
font-weight: 700;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
64
|
.gtreeview .child.active,
|
|
68
65
|
.gtreeview .child:hover,
|
|
69
66
|
.gtreeview .child.hover {
|
|
@@ -77,7 +74,7 @@
|
|
|
77
74
|
flex-wrap: nowrap;
|
|
78
75
|
gap: 12px;
|
|
79
76
|
align-items: center;
|
|
80
|
-
margin-
|
|
77
|
+
margin-top: 4px;
|
|
81
78
|
cursor: pointer;
|
|
82
79
|
}
|
|
83
80
|
|
package/package.json
CHANGED
package/utils/history.js
CHANGED
|
@@ -51,7 +51,6 @@ export default class {
|
|
|
51
51
|
let skipOnce = false;
|
|
52
52
|
window.onpopstate = event => {
|
|
53
53
|
if (skipRendering) {
|
|
54
|
-
skipRendering = false
|
|
55
54
|
return false;
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -110,14 +109,18 @@ export default class {
|
|
|
110
109
|
static backWithoutRender() {
|
|
111
110
|
skipRendering = true
|
|
112
111
|
this.back()
|
|
112
|
+
skipRendering = false
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
static destroy() {
|
|
116
116
|
if (this.navigationCount <= 0) return false;
|
|
117
117
|
|
|
118
|
-
window.onpopstate = () => { };
|
|
118
|
+
// window.onpopstate = () => { };
|
|
119
|
+
skipRendering = true
|
|
119
120
|
window.history.go(-this.navigationCount);
|
|
120
|
-
|
|
121
|
+
skipRendering = false
|
|
122
|
+
// this.restoreOnBackOrForward();
|
|
123
|
+
this.navigationCount = 0
|
|
121
124
|
|
|
122
125
|
return true;
|
|
123
126
|
}
|
package/utils/http.js
CHANGED
|
@@ -59,7 +59,8 @@ export default class {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
// Set `forcePushHistory` when it is important to clear the forward history.
|
|
63
|
+
static load(properties, component, windowMode, forcePushHistory) {
|
|
63
64
|
const urlString = properties["url"];
|
|
64
65
|
let url;
|
|
65
66
|
try {
|
|
@@ -81,7 +82,8 @@ export default class {
|
|
|
81
82
|
|
|
82
83
|
Utils.http.execute(properties, "GET", component, (data, response) => {
|
|
83
84
|
const pushHistory = windowMode ? true : !Utils.type.isObject(dialogs.last());
|
|
84
|
-
if
|
|
85
|
+
// TODO: Check if it is okay to remove this `if` statement so we always push even if it's the same URL.
|
|
86
|
+
if (forcePushHistory || (htmlUrl !== currentUrl && pushHistory)) {
|
|
85
87
|
const redirectUrl = Utils.url.htmlUrl(response.url);
|
|
86
88
|
Utils.history.pushPage(data, redirectUrl);
|
|
87
89
|
}
|
|
@@ -143,6 +145,9 @@ export default class {
|
|
|
143
145
|
};
|
|
144
146
|
|
|
145
147
|
Utils.http.execute(data, "GET", component, (page, response) => {
|
|
148
|
+
// The execution goes in the following order: onResponse, page render, onLoad
|
|
149
|
+
GLib.action.execute(page.onResponse, component);
|
|
150
|
+
|
|
146
151
|
Utils.http.forceComponentUpdate(() => {
|
|
147
152
|
vueApp.page = page;
|
|
148
153
|
const redirectUrl = Utils.url.htmlUrl(response.url);
|
package/utils/uploader.js
CHANGED
|
@@ -1,15 +1,79 @@
|
|
|
1
|
-
import { DirectUpload } from "@rails/activestorage";
|
|
1
|
+
import { DirectUpload } from "@rails/activestorage/src/direct_upload";
|
|
2
|
+
import { BlobRecord } from "@rails/activestorage/src/blob_record";
|
|
3
|
+
import { FileChecksum } from "@rails/activestorage/src/file_checksum";
|
|
4
|
+
import { BlobUpload } from "@rails/activestorage/src/blob_upload";
|
|
2
5
|
import Type from "../utils/type";
|
|
3
6
|
import mimeType from "../utils/mime_type";
|
|
7
|
+
import { showError } from "../components/composable/alert";
|
|
4
8
|
|
|
5
9
|
const MB_SIZE = 1000;
|
|
6
10
|
|
|
11
|
+
function notify(object, methodName, ...messages) {
|
|
12
|
+
if (object && typeof object[methodName] == "function") {
|
|
13
|
+
return object[methodName](...messages);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class GlibBlobRecord extends BlobRecord {
|
|
18
|
+
constructor(file, checksum, url, customHeaders = {}, prefix = '', metadata = {}) {
|
|
19
|
+
super(file, checksum, url, customHeaders);
|
|
20
|
+
|
|
21
|
+
this.attributes = Object.assign(this.attributes, { prefix }, { metadata });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class GlibDirectUpload extends DirectUpload {
|
|
26
|
+
constructor(file, url, delegate, customHeaders = {}, prefix = '', metadata = {}) {
|
|
27
|
+
super(file, url, delegate, customHeaders);
|
|
28
|
+
this.prefix = prefix;
|
|
29
|
+
this.metadata = { custom: metadata };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
create(callback) {
|
|
33
|
+
FileChecksum.create(this.file, (error, checksum) => {
|
|
34
|
+
if (error) {
|
|
35
|
+
callback(error);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const blob = new GlibBlobRecord(this.file, checksum, this.url, this.customHeaders, this.prefix, this.metadata);
|
|
40
|
+
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
|
41
|
+
|
|
42
|
+
blob.create(error => {
|
|
43
|
+
if (error) {
|
|
44
|
+
callback(error);
|
|
45
|
+
} else {
|
|
46
|
+
const upload = new BlobUpload(blob);
|
|
47
|
+
|
|
48
|
+
// check if there is prefix
|
|
49
|
+
|
|
50
|
+
const { url } = blob.directUploadData;
|
|
51
|
+
if (this.prefix && !url.includes(this.prefix)) {
|
|
52
|
+
showError({ body: 'Something went wrong', button: 'Dismiss' });
|
|
53
|
+
console.error('prefix not match');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
|
|
58
|
+
upload.create(error => {
|
|
59
|
+
if (error) {
|
|
60
|
+
callback(error);
|
|
61
|
+
} else {
|
|
62
|
+
callback(null, blob.toJSON());
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
7
71
|
export default class Uploader {
|
|
8
|
-
constructor(file, url, progress) {
|
|
72
|
+
constructor(file, url, progress, prefix, metadata) {
|
|
9
73
|
this.file = file;
|
|
10
74
|
this.url = url;
|
|
11
75
|
this.progress = progress;
|
|
12
|
-
this.upload = new
|
|
76
|
+
this.upload = new GlibDirectUpload(this.file, this.url, this, {}, prefix, metadata);
|
|
13
77
|
}
|
|
14
78
|
|
|
15
79
|
start(callback) {
|