glib-web 4.8.3 → 4.9.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 +0 -2
- package/actions/logics/set.js +25 -0
- package/components/component.vue +3 -1
- package/components/composable/parser.js +6 -1
- package/components/fields/radio/_featured.vue +7 -14
- package/components/fields/radio.vue +7 -28
- package/components/h1.vue +1 -9
- package/components/h2.vue +1 -9
- package/components/h3.vue +1 -9
- package/components/h4.vue +1 -3
- package/components/h5.vue +1 -9
- package/components/h6.vue +1 -10
- package/components/label.vue +1 -4
- package/components/mixins/updatableComponent.js +54 -0
- package/components/panels/bulkEdit2.vue +80 -35
- package/components/panels/pagination.vue +47 -0
- package/components/panels/table2.vue +113 -0
- package/components/panels/tree/standard.vue +7 -1
- package/components/{_responsive.vue → responsive.vue} +2 -0
- package/index.js +7 -6
- package/package.json +1 -1
- package/templates/thumbnail.vue +3 -3
- package/utils/hash.js +5 -0
- package/utils/http.js +17 -3
- package/actions/ws/push.js +0 -38
- package/components/_tooltip.vue +0 -73
- package/components/charts/line-old.vue +0 -62
- package/components/mixins/text.js +0 -20
- package/components/mixins/ws/actionCable.js +0 -49
- package/components/panels/responsive.vue +0 -23
- package/plugins/updatableComponent.js +0 -58
package/action.js
CHANGED
|
@@ -36,7 +36,6 @@ import ActionsWindowsPrint from "./actions/windows/print";
|
|
|
36
36
|
import ActionsPanelsScrollToBottom from "./actions/panels/scrollToBottom";
|
|
37
37
|
import ActionsPanelsScrollTo from "./actions/panels/scrollTo";
|
|
38
38
|
|
|
39
|
-
import ActionsWsPush from "./actions/ws/push";
|
|
40
39
|
import ActionsCablesPush from "./actions/cables/push";
|
|
41
40
|
|
|
42
41
|
import ActionsTimeoutsSet from "./actions/timeouts/set";
|
|
@@ -138,7 +137,6 @@ const actions = {
|
|
|
138
137
|
"timeouts/set": ActionsTimeoutsSet,
|
|
139
138
|
"timeouts/clear": ActionsTimeoutsClear,
|
|
140
139
|
|
|
141
|
-
"ws/push": ActionsWsPush,
|
|
142
140
|
"cables/push": ActionsCablesPush,
|
|
143
141
|
|
|
144
142
|
"auth/saveCsrfToken": ActionsAuthSaveCsrfToken,
|
package/actions/logics/set.js
CHANGED
|
@@ -22,6 +22,31 @@ const nullishCoalescing = function (a, b) {
|
|
|
22
22
|
};
|
|
23
23
|
jsonLogic.add_operation("??", nullishCoalescing);
|
|
24
24
|
|
|
25
|
+
const sum = function (...args) {
|
|
26
|
+
return args.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
|
|
27
|
+
};
|
|
28
|
+
jsonLogic.add_operation("sum", sum);
|
|
29
|
+
|
|
30
|
+
const countNonNull = function (...args) {
|
|
31
|
+
return args.reduce((accumulator, currentValue) => {
|
|
32
|
+
if (currentValue) {
|
|
33
|
+
accumulator++;
|
|
34
|
+
}
|
|
35
|
+
}, 0);
|
|
36
|
+
};
|
|
37
|
+
jsonLogic.add_operation("countNonNull", countNonNull);
|
|
38
|
+
|
|
39
|
+
const printf = function (template, ...args) {
|
|
40
|
+
// See https://www.geeksforgeeks.org/what-are-the-equivalent-of-printf-string-format-in-javascript/
|
|
41
|
+
// const args = arguments;
|
|
42
|
+
return template.replace(/{(\d+)}/g, function (match, number) {
|
|
43
|
+
return typeof args[number] != 'undefined'
|
|
44
|
+
? args[number]
|
|
45
|
+
: match;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
jsonLogic.add_operation("printf", printf);
|
|
49
|
+
|
|
25
50
|
export default class {
|
|
26
51
|
execute(spec, component) {
|
|
27
52
|
let targetComponent;
|
package/components/component.vue
CHANGED
|
@@ -107,13 +107,14 @@ import BulkEditPanel from "./panels/bulkEdit.vue";
|
|
|
107
107
|
import BulkEditPanel2 from "./panels/bulkEdit2.vue";
|
|
108
108
|
import CustomPanel from "./panels/custom.vue";
|
|
109
109
|
import ColumnPanel from "./panels/column.vue";
|
|
110
|
-
import ResponsivePanel from "./
|
|
110
|
+
import ResponsivePanel from "./responsive.vue";
|
|
111
111
|
import UlPanel from "./panels/ul.vue";
|
|
112
112
|
import WebPanel from "./panels/web.vue";
|
|
113
113
|
import GridPanel from "./panels/grid.vue";
|
|
114
114
|
import TimelinePanel from "./panels/timeline.vue";
|
|
115
115
|
import AssociationPanel from "./panels/association.vue";
|
|
116
116
|
import TreePanel from "./panels/tree.vue";
|
|
117
|
+
import PaginationPanel from "./panels/pagination.vue";
|
|
117
118
|
|
|
118
119
|
import MultimediaVideo from "./multimedia/video.vue";
|
|
119
120
|
|
|
@@ -214,6 +215,7 @@ export default {
|
|
|
214
215
|
"panels-timeline": TimelinePanel,
|
|
215
216
|
"panels-association": AssociationPanel,
|
|
216
217
|
"panels-tree": TreePanel,
|
|
218
|
+
"panels-pagination": PaginationPanel,
|
|
217
219
|
|
|
218
220
|
"multimedia-video": MultimediaVideo,
|
|
219
221
|
|
|
@@ -37,5 +37,10 @@ export function parseCsv(csvString) {
|
|
|
37
37
|
// return r[0] !== "undefined"
|
|
38
38
|
// });
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
const columns = result.shift();
|
|
41
|
+
return result.map(row => {
|
|
42
|
+
return columns.reduce((prev, curr, index) => {
|
|
43
|
+
return Object.assign({}, prev, { [curr]: row[index] });
|
|
44
|
+
}, {});
|
|
45
|
+
});
|
|
41
46
|
}
|
|
@@ -28,7 +28,8 @@ export default {
|
|
|
28
28
|
display: flex;
|
|
29
29
|
align-items: center;
|
|
30
30
|
justify-content: center;
|
|
31
|
-
width:
|
|
31
|
+
width: 100%;
|
|
32
|
+
min-height: 230px;
|
|
32
33
|
height: 100%;
|
|
33
34
|
transition: border-color 0.3s, box-shadow 0.3s, color 0.3s;
|
|
34
35
|
text-align: center;
|
|
@@ -45,7 +46,6 @@ export default {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
.custom-radio:hover .custom-radio-label {
|
|
48
|
-
font-size: 22px;
|
|
49
49
|
color: #0A2A9E;
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -55,27 +55,21 @@ export default {
|
|
|
55
55
|
align-items: center;
|
|
56
56
|
justify-content: center;
|
|
57
57
|
width: 100%;
|
|
58
|
-
|
|
58
|
+
min-height: 226px;
|
|
59
|
+
padding: 32px;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
.custom-radio .custom-radio-label {
|
|
62
|
-
font-size:
|
|
63
|
+
font-size: 18px;
|
|
63
64
|
color: inherit;
|
|
64
65
|
margin-top: 24px;
|
|
65
66
|
word-break: break-word;
|
|
66
|
-
min-width: 180px;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
.custom-radio ::v-deep .v-selection-control__input {
|
|
70
|
-
width:
|
|
71
|
-
height:
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 100%;
|
|
72
72
|
background-color: transparent;
|
|
73
|
-
left: 50%;
|
|
74
|
-
|
|
75
|
-
>.v-icon {
|
|
76
|
-
opacity: 0;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
73
|
}
|
|
80
74
|
|
|
81
75
|
.custom-radio ::v-deep .v-selection-control__input::before {
|
|
@@ -103,7 +97,6 @@ export default {
|
|
|
103
97
|
.radio--active .custom-radio-label {
|
|
104
98
|
color: #0A2A9E;
|
|
105
99
|
font-weight: 700;
|
|
106
|
-
font-size: 22px;
|
|
107
100
|
}
|
|
108
101
|
|
|
109
102
|
.radio--active .v-icon {
|
|
@@ -37,8 +37,9 @@ export default {
|
|
|
37
37
|
display: flex;
|
|
38
38
|
align-items: center;
|
|
39
39
|
justify-content: center;
|
|
40
|
-
width:
|
|
41
|
-
min-height:
|
|
40
|
+
width: 100%;
|
|
41
|
+
min-height: 230px;
|
|
42
|
+
height: 100%;
|
|
42
43
|
transition: border-color 0.3s, box-shadow 0.3s, color 0.3s;
|
|
43
44
|
text-align: center;
|
|
44
45
|
cursor: pointer;
|
|
@@ -53,36 +54,20 @@ export default {
|
|
|
53
54
|
border-color: #0A2A9E;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
.custom-radio:hover .custom-radio-label {
|
|
57
|
-
font-size: 22px;
|
|
58
|
-
color: #0A2A9E;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
57
|
.custom-radio .custom-radio-content {
|
|
62
58
|
display: flex;
|
|
63
59
|
flex-direction: column;
|
|
64
60
|
align-items: center;
|
|
65
61
|
justify-content: center;
|
|
66
62
|
width: 100%;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.custom-radio .custom-radio-label {
|
|
71
|
-
font-size: 22px;
|
|
72
|
-
color: inherit;
|
|
73
|
-
margin-top: 24px;
|
|
63
|
+
min-height: 226px;
|
|
64
|
+
padding: 32px;
|
|
74
65
|
}
|
|
75
66
|
|
|
76
67
|
.custom-radio ::v-deep .v-selection-control__input {
|
|
77
|
-
width:
|
|
78
|
-
height:
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: 100%;
|
|
79
70
|
background-color: transparent;
|
|
80
|
-
left: 50%;
|
|
81
|
-
|
|
82
|
-
>.v-icon {
|
|
83
|
-
opacity: 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
71
|
}
|
|
87
72
|
|
|
88
73
|
.custom-radio ::v-deep .v-selection-control__input::before {
|
|
@@ -107,12 +92,6 @@ export default {
|
|
|
107
92
|
border-color: #0A2A9E;
|
|
108
93
|
}
|
|
109
94
|
|
|
110
|
-
.radio--active .custom-radio-label {
|
|
111
|
-
color: #0A2A9E;
|
|
112
|
-
font-weight: 700;
|
|
113
|
-
font-size: 22px;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
95
|
.radio--active .v-icon {
|
|
117
96
|
color: #0A2A9E;
|
|
118
97
|
}
|
package/components/h1.vue
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<h1
|
|
3
|
-
:style="textStyles()"
|
|
4
|
-
:class="$classes()"
|
|
5
|
-
:href="$href()"
|
|
6
|
-
:rel="$rel()"
|
|
7
|
-
@click="$onClick()"
|
|
8
|
-
>
|
|
2
|
+
<h1 :style="$styles()" :class="$classes()" :href="$href()" :rel="$rel()" @click="$onClick()">
|
|
9
3
|
{{ spec.text }}
|
|
10
4
|
</h1>
|
|
11
5
|
</template>
|
|
12
6
|
|
|
13
7
|
<script>
|
|
14
|
-
import textMixin from "./mixins/text.js";
|
|
15
8
|
|
|
16
9
|
export default {
|
|
17
|
-
mixins: [textMixin],
|
|
18
10
|
props: {
|
|
19
11
|
spec: { type: Object, required: true }
|
|
20
12
|
}
|
package/components/h2.vue
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<h2
|
|
3
|
-
:style="textStyles()"
|
|
4
|
-
:class="$classes()"
|
|
5
|
-
:href="$href()"
|
|
6
|
-
:rel="$rel()"
|
|
7
|
-
@click="$onClick()"
|
|
8
|
-
>
|
|
2
|
+
<h2 :style="$styles()" :class="$classes()" :href="$href()" :rel="$rel()" @click="$onClick()">
|
|
9
3
|
{{ spec.text }}
|
|
10
4
|
</h2>
|
|
11
5
|
</template>
|
|
12
6
|
|
|
13
7
|
<script>
|
|
14
|
-
import textMixin from "./mixins/text.js";
|
|
15
8
|
|
|
16
9
|
export default {
|
|
17
|
-
mixins: [textMixin],
|
|
18
10
|
props: {
|
|
19
11
|
spec: { type: Object, required: true }
|
|
20
12
|
}
|
package/components/h3.vue
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<h3
|
|
3
|
-
:style="textStyles()"
|
|
4
|
-
:class="$classes()"
|
|
5
|
-
:href="$href()"
|
|
6
|
-
:rel="$rel()"
|
|
7
|
-
@click="$onClick()"
|
|
8
|
-
>
|
|
2
|
+
<h3 :style="$styles()" :class="$classes()" :href="$href()" :rel="$rel()" @click="$onClick()">
|
|
9
3
|
{{ spec.text }}
|
|
10
4
|
</h3>
|
|
11
5
|
</template>
|
|
12
6
|
|
|
13
7
|
<script>
|
|
14
|
-
import textMixin from "./mixins/text.js";
|
|
15
8
|
|
|
16
9
|
export default {
|
|
17
|
-
mixins: [textMixin],
|
|
18
10
|
props: {
|
|
19
11
|
spec: { type: Object, required: true }
|
|
20
12
|
}
|
package/components/h4.vue
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<h4 :style="
|
|
2
|
+
<h4 :style="$styles()" :class="$classes()" :href="$href()" :rel="$rel()" @click="$onClick()">
|
|
3
3
|
{{ spec.text }}
|
|
4
4
|
</h4>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script>
|
|
8
|
-
import textMixin from "./mixins/text.js";
|
|
9
8
|
|
|
10
9
|
export default {
|
|
11
|
-
mixins: [textMixin],
|
|
12
10
|
props: {
|
|
13
11
|
spec: { type: Object, required: true }
|
|
14
12
|
}
|
package/components/h5.vue
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<h5
|
|
3
|
-
:style="textStyles()"
|
|
4
|
-
:class="$classes()"
|
|
5
|
-
:href="$href()"
|
|
6
|
-
:rel="$rel()"
|
|
7
|
-
@click="$onClick()"
|
|
8
|
-
>
|
|
2
|
+
<h5 :style="$styles()" :class="$classes()" :href="$href()" :rel="$rel()" @click="$onClick()">
|
|
9
3
|
{{ spec.text }}
|
|
10
4
|
</h5>
|
|
11
5
|
</template>
|
|
12
6
|
|
|
13
7
|
<script>
|
|
14
|
-
import textMixin from "./mixins/text.js";
|
|
15
8
|
|
|
16
9
|
export default {
|
|
17
|
-
mixins: [textMixin],
|
|
18
10
|
props: {
|
|
19
11
|
spec: { type: Object, required: true }
|
|
20
12
|
}
|
package/components/h6.vue
CHANGED
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<h4
|
|
3
|
-
:style="textStyles()"
|
|
4
|
-
:class="$classes()"
|
|
5
|
-
:href="$href()"
|
|
6
|
-
:rel="$rel()"
|
|
7
|
-
@click="$onClick()"
|
|
8
|
-
>
|
|
2
|
+
<h4 :style="$styles()" :class="$classes()" :href="$href()" :rel="$rel()" @click="$onClick()">
|
|
9
3
|
{{ spec.text }}
|
|
10
4
|
</h4>
|
|
11
5
|
</template>
|
|
12
6
|
|
|
13
7
|
<script>
|
|
14
|
-
import textMixin from "./mixins/text.js";
|
|
15
|
-
|
|
16
8
|
export default {
|
|
17
|
-
mixins: [textMixin],
|
|
18
9
|
props: {
|
|
19
10
|
spec: { type: Object, required: true }
|
|
20
11
|
}
|
package/components/label.vue
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<a v-if="spec.onClick" :href="$href()" :rel="$rel()" :style="
|
|
2
|
+
<a v-if="spec.onClick" :href="$href()" :rel="$rel()" :style="$styles()" :class="$classes()" @click="$onClick()">{{
|
|
3
3
|
spec.text }}</a>
|
|
4
4
|
<span v-else :style="$styles()" :class="$classes()">{{ spec.text }}</span>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script>
|
|
8
|
-
// import actionCableMixin from "./mixins/ws/actionCable";
|
|
9
|
-
import textMixin from "./mixins/text.js";
|
|
10
8
|
|
|
11
9
|
export default {
|
|
12
|
-
mixins: [textMixin],
|
|
13
10
|
props: {
|
|
14
11
|
spec: { type: Object, required: true }
|
|
15
12
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
computed: {
|
|
3
|
+
viewId() {
|
|
4
|
+
if (this.spec && this.spec.id) {
|
|
5
|
+
const id = this.spec.id;
|
|
6
|
+
|
|
7
|
+
if (id.includes('{{entry_index}}')) {
|
|
8
|
+
const dynamicGroupEntry = this.$closest("fields/internalDynamicGroupEntry");
|
|
9
|
+
if (dynamicGroupEntry) {
|
|
10
|
+
return dynamicGroupEntry.$populateIndexes(id);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return id;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
methods: {
|
|
19
|
+
$ready() {
|
|
20
|
+
let spec = this.spec;
|
|
21
|
+
if (spec && spec.id && this.$registryEnabled()) {
|
|
22
|
+
|
|
23
|
+
const id = this.viewId;
|
|
24
|
+
|
|
25
|
+
const existingComponent = GLib.component.findById(id);
|
|
26
|
+
// A component with the same ID in a different page shouldn't be considered a
|
|
27
|
+
// duplicate. See `utils/components#deregister` for more details.
|
|
28
|
+
if (existingComponent) {
|
|
29
|
+
console.warn(
|
|
30
|
+
"Duplicate component ID:",
|
|
31
|
+
id,
|
|
32
|
+
"Existing:",
|
|
33
|
+
GLib.component.vueName(existingComponent),
|
|
34
|
+
"New:",
|
|
35
|
+
GLib.component.vueName(this)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const newComponent = this;
|
|
39
|
+
GLib.component.register(id, newComponent);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
$tearDown() {
|
|
43
|
+
let spec = this.spec;
|
|
44
|
+
|
|
45
|
+
if (spec && spec.id && this.$registryEnabled()) {
|
|
46
|
+
GLib.component.deregister(spec.id, this);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
$registryEnabled() {
|
|
50
|
+
// Common classes such as `_select` need to return false so that it doesn't override its parent (e.g. `select`).
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
<div :style="$styles()" :class="$classes()" v-if="loadIf">
|
|
3
3
|
<div>Rows: {{ loadedRowCount }} Selected: {{ selectedRowCount }}</div>
|
|
4
4
|
<input ref="fileInput" style="display: none;" type="file" accept=".csv" @change="handleClick" />
|
|
5
|
+
|
|
5
6
|
<div class="scrollable">
|
|
6
|
-
<table :class="rowLoaded ? 'loaded' : 'nonLoaded'">
|
|
7
|
+
<table :class="rowLoaded ? 'loaded' : 'nonLoaded'" width="100%">
|
|
7
8
|
<thead v-if="props.spec.viewHeaders">
|
|
8
9
|
<tr>
|
|
9
|
-
<th class="cell-selection" v-if="rowLoaded"><v-checkbox v-model="checkbox" color="primary"
|
|
10
|
+
<th class="cell-selection" v-if="rowLoaded"><v-checkbox v-model="checkbox" color="primary"
|
|
11
|
+
@change="checkAll"></v-checkbox></th>
|
|
12
|
+
<th></th>
|
|
10
13
|
<th :class="`cell-column${cellIndex}`" v-for="(cell, cellIndex) in props.spec.viewHeaders" :key="cell.id"
|
|
11
14
|
:style="{ minWidth: `${cell.minWidth || 100}px` }">
|
|
12
15
|
<span>{{ cell.text }}</span>
|
|
@@ -18,6 +21,7 @@
|
|
|
18
21
|
<template v-if="rowLoaded">
|
|
19
22
|
<tr v-for="(row, rowIndex) in rows" :key="`row_${rowIndex}`">
|
|
20
23
|
<td class="cell-selection"><v-checkbox v-model="row.selected" color="primary"></v-checkbox></td>
|
|
24
|
+
<td class="cell-status"><glib-component :spec="row.iconSpec" /></td>
|
|
21
25
|
<td :class="`cell-column${cellIndex}`" v-for="(cell, cellIndex) in row.columns" :key="`cell_${cellIndex}`"
|
|
22
26
|
@change="(e) => handleCellChange(e, cell)">
|
|
23
27
|
<glib-component :spec="cell.view"></glib-component>
|
|
@@ -25,19 +29,21 @@
|
|
|
25
29
|
</tr>
|
|
26
30
|
</template>
|
|
27
31
|
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
@
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
<template v-else>
|
|
33
|
+
<tr>
|
|
34
|
+
<td colspan="100%" style="padding-top: 8px">
|
|
35
|
+
<div class="gdrop-file border-[2px]" @dragover="(e) => e.preventDefault()" @drop="handleDrop"
|
|
36
|
+
@click="fileInput.click()">
|
|
37
|
+
|
|
38
|
+
<div class="cloud" style="pointer-events: none;">
|
|
39
|
+
<v-icon ref="icon" size="48" class="icon">cloud_upload</v-icon>
|
|
40
|
+
<h4 class="title">Drag your CSV file here</h4>
|
|
41
|
+
<p class="subtitle">or click to browse</p>
|
|
42
|
+
</div>
|
|
37
43
|
</div>
|
|
38
|
-
</
|
|
39
|
-
</
|
|
40
|
-
</
|
|
44
|
+
</td>
|
|
45
|
+
</tr>
|
|
46
|
+
</template>
|
|
41
47
|
</tbody>
|
|
42
48
|
</table>
|
|
43
49
|
</div>
|
|
@@ -51,10 +57,12 @@ table thead th {
|
|
|
51
57
|
z-index: 1005;
|
|
52
58
|
background-color: white;
|
|
53
59
|
}
|
|
60
|
+
|
|
54
61
|
.scrollable {
|
|
55
62
|
width: 100%;
|
|
56
63
|
overflow: auto;
|
|
57
64
|
}
|
|
65
|
+
|
|
58
66
|
.cell-selection {
|
|
59
67
|
min-width: 40px;
|
|
60
68
|
}
|
|
@@ -65,6 +73,28 @@ import { computed, getCurrentInstance, onMounted, ref, watch } from "vue";
|
|
|
65
73
|
import { parseCsv } from "../composable/parser";
|
|
66
74
|
import Action from "../../action";
|
|
67
75
|
|
|
76
|
+
class Row {
|
|
77
|
+
constructor({ id, columns, selected, index }) {
|
|
78
|
+
this.id = id;
|
|
79
|
+
this.selected = selected;
|
|
80
|
+
this.columns = columns;
|
|
81
|
+
|
|
82
|
+
this.statusCompId = statusCompId(index);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get iconSpec() {
|
|
86
|
+
return {
|
|
87
|
+
view: 'icon',
|
|
88
|
+
id: this.statusCompId,
|
|
89
|
+
material: { name: 'preview' },
|
|
90
|
+
styleClasses: ['warning'],
|
|
91
|
+
tooltip: {
|
|
92
|
+
text: "Review"
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
class Cell {
|
|
69
99
|
constructor({ viewHeaders, cellIndex, rowId, view, value }) {
|
|
70
100
|
this.viewHeaders = viewHeaders;
|
|
@@ -87,19 +117,23 @@ function handleDrop(e) {
|
|
|
87
117
|
loadFile(e.dataTransfer.files);
|
|
88
118
|
}
|
|
89
119
|
|
|
120
|
+
function statusCompId(rowIndex) {
|
|
121
|
+
return (props.spec.statusViewIdPrefix || 'status') + `-${rowIndex}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
90
124
|
function makeRows(dataRows) {
|
|
91
125
|
if (!dataRows || dataRows.length <= 0) return [];
|
|
92
|
-
const rows = dataRows.map((dataRow) => {
|
|
126
|
+
const rows = dataRows.map((dataRow, index) => {
|
|
93
127
|
const selected = false;
|
|
94
|
-
const
|
|
128
|
+
const id = dataRow.rowId;
|
|
95
129
|
const columns = props.spec.viewCells.map((viewCells, i) => {
|
|
96
130
|
const view = Object.assign({}, viewCells, dataRow.columns[i]);
|
|
97
131
|
const cellIndex = i;
|
|
98
132
|
const viewHeaders = props.spec.viewHeaders;
|
|
99
133
|
const value = dataRow.columns[i].value;
|
|
100
|
-
return new Cell({ rowId, view, cellIndex, viewHeaders, value });
|
|
134
|
+
return new Cell({ rowId: id, view, cellIndex, viewHeaders, value });
|
|
101
135
|
});
|
|
102
|
-
return { selected, columns,
|
|
136
|
+
return new Row({ selected, columns, id, index });
|
|
103
137
|
});
|
|
104
138
|
return rows;
|
|
105
139
|
}
|
|
@@ -109,15 +143,12 @@ const fileInput = ref(null);
|
|
|
109
143
|
const checkbox = ref(false);
|
|
110
144
|
const instance = getCurrentInstance();
|
|
111
145
|
|
|
112
|
-
const fillableColumnIndexes = props.spec.viewHeaders.reduce((prev, curr, index) => {
|
|
113
|
-
if (curr.importable) prev.push(index);
|
|
114
|
-
return prev;
|
|
115
|
-
}, []);
|
|
116
|
-
|
|
117
146
|
const rows = ref(makeRows(props.spec.dataRows));
|
|
118
147
|
|
|
119
148
|
watch(props, (value) => {
|
|
120
|
-
|
|
149
|
+
if (value.spec.dataRows && value.spec.dataRows.length > 0) {
|
|
150
|
+
rows.value = makeRows(value.spec.dataRows);
|
|
151
|
+
}
|
|
121
152
|
});
|
|
122
153
|
|
|
123
154
|
const selectedRows = computed(() => {
|
|
@@ -173,22 +204,23 @@ function handleCellChange(e, cell) {
|
|
|
173
204
|
Action.execute(data, instance.ctx);
|
|
174
205
|
}
|
|
175
206
|
|
|
207
|
+
function getColumnIndexById(id) {
|
|
208
|
+
return props.spec.viewHeaders.map(v => v.id).indexOf(id);
|
|
209
|
+
}
|
|
210
|
+
|
|
176
211
|
function loadFile(files) {
|
|
177
212
|
const reader = new FileReader();
|
|
178
213
|
reader.readAsText(files[0]);
|
|
179
214
|
|
|
180
215
|
reader.onload = (ev) => {
|
|
181
216
|
const csvData = parseCsv(ev.target.result);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const cols =
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
188
|
-
cols.
|
|
189
|
-
i++;
|
|
190
|
-
} else {
|
|
191
|
-
cols.push(Object.assign(obj, viewCell, { id: `cell-${rowIndex}-${i}` }));
|
|
217
|
+
|
|
218
|
+
const dataRows = csvData.map((row) => {
|
|
219
|
+
const cols = JSON.parse(JSON.stringify(props.spec.viewCells));
|
|
220
|
+
Object.entries(row).forEach((v) => {
|
|
221
|
+
const index = getColumnIndexById(v[0]);
|
|
222
|
+
if (cols[index]) {
|
|
223
|
+
cols[index].value = v[1];
|
|
192
224
|
}
|
|
193
225
|
});
|
|
194
226
|
return { rowId: null, columns: cols };
|
|
@@ -204,8 +236,21 @@ function submitRows(rows) {
|
|
|
204
236
|
if (!rows || rows.length <= 0) return;
|
|
205
237
|
|
|
206
238
|
const row = rows.shift();
|
|
207
|
-
row.columns = row.columns.map((v) => ({ rowId: v.rowId, cellId: v.cellId, value: v.value, compId: v.view.id }));
|
|
208
239
|
|
|
240
|
+
// change row status to pending
|
|
241
|
+
Action.execute({
|
|
242
|
+
action: 'components/set',
|
|
243
|
+
targetId: row.statusCompId,
|
|
244
|
+
data: {
|
|
245
|
+
material: { name: 'pending' },
|
|
246
|
+
styleClasses: ['info']
|
|
247
|
+
},
|
|
248
|
+
tooltip: {
|
|
249
|
+
text: 'pending'
|
|
250
|
+
},
|
|
251
|
+
}, instance.ctx);
|
|
252
|
+
|
|
253
|
+
row.columns = row.columns.map((v) => ({ rowId: v.rowId, cellId: v.cellId, value: v.value }));
|
|
209
254
|
const { submitUrl, paramName } = props.spec.import;
|
|
210
255
|
|
|
211
256
|
const data = {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-pagination :style="$styles()" :class="$classes()"
|
|
3
|
+
:density="density" :variant="variant"
|
|
4
|
+
:length="spec.length" v-model="spec.value" @update:model-value="updatePage">
|
|
5
|
+
</v-pagination>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import { determineDensity, determineVariant } from "../../utils/constant";
|
|
10
|
+
|
|
11
|
+
// import { computed, getCurrentInstance, onMounted, ref, watch } from "vue";
|
|
12
|
+
// import { getCurrentInstance } from "vue";
|
|
13
|
+
|
|
14
|
+
// const props = defineProps(['spec']);
|
|
15
|
+
// const instance = getCurrentInstance();
|
|
16
|
+
|
|
17
|
+
// function updatePage(pageIndex) {
|
|
18
|
+
// this.$executeOnChange(pageIndex)
|
|
19
|
+
|
|
20
|
+
// // const url = `${props.spec.baseUrl}
|
|
21
|
+
// // alert(pageIndex)
|
|
22
|
+
// // console.log("P1", pageIndex)
|
|
23
|
+
// // Action.execute(props.spec.onChange, instance.ctx);
|
|
24
|
+
// }
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
props: {
|
|
28
|
+
spec: { type: Object, required: true }
|
|
29
|
+
},
|
|
30
|
+
computed: {
|
|
31
|
+
density() {
|
|
32
|
+
return determineDensity(this.spec.styleClasses);
|
|
33
|
+
},
|
|
34
|
+
variant() {
|
|
35
|
+
return determineVariant(this.spec.styleClasses);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
methods: {
|
|
39
|
+
updatePage(pageIndex) {
|
|
40
|
+
this.$executeOnChange(pageIndex)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<style lang="scss" scoped>
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="scrollable">
|
|
3
|
+
<table :style="$styles()" :class="$classes()" v-if="loadIf">
|
|
4
|
+
<thead>
|
|
5
|
+
<tr v-if="section.header" :style="$styles(section.header)">
|
|
6
|
+
<th v-for="(cell, index) in section.header.cellViews" :key="index" :colSpan="colSpan(section.header, index)">
|
|
7
|
+
<glib-component :spec="cell" />
|
|
8
|
+
</th>
|
|
9
|
+
</tr>
|
|
10
|
+
</thead>
|
|
11
|
+
|
|
12
|
+
<tbody>
|
|
13
|
+
<template v-for="(row, rowIndex) in rows" :key="`row_${rowIndex}`">
|
|
14
|
+
<tr :class="row.onClick ? 'clickable' : ''" @[dstart(row.dragData)]="(e) => handleDragStart(e, row.dragData)"
|
|
15
|
+
:draggable="!!row.dragData">
|
|
16
|
+
<td v-for="(cell, cellIndex) in row.cellViews" :key="`cell_${cellIndex}`" :colSpan="colSpan(row, cellIndex)"
|
|
17
|
+
:style="colStyles(row, cellIndex)">
|
|
18
|
+
<span>
|
|
19
|
+
<!-- Prevent double links -->
|
|
20
|
+
<glib-component v-if="$href(cell)" :spec="cell" />
|
|
21
|
+
<!-- without "|| null" the browser will reload strangely -->
|
|
22
|
+
<a v-else :href="$href(row) || null" @click="$onClick($event, row)">
|
|
23
|
+
<glib-component :spec="cell" />
|
|
24
|
+
</a>
|
|
25
|
+
</span>
|
|
26
|
+
</td>
|
|
27
|
+
</tr>
|
|
28
|
+
</template>
|
|
29
|
+
</tbody>
|
|
30
|
+
</table>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script>
|
|
35
|
+
|
|
36
|
+
export default {
|
|
37
|
+
props: {
|
|
38
|
+
spec: { type: Object, required: true }
|
|
39
|
+
},
|
|
40
|
+
methods: {
|
|
41
|
+
colSpan(row, index) {
|
|
42
|
+
const spans = row.colSpans || [];
|
|
43
|
+
return spans[index] || 1;
|
|
44
|
+
},
|
|
45
|
+
colStyles(row, index) {
|
|
46
|
+
const colStyles = row.colStyles || [];
|
|
47
|
+
return this.$styles(colStyles[index] || {});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<style lang="scss" scoped>
|
|
54
|
+
table {
|
|
55
|
+
border-spacing: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
tbody {
|
|
59
|
+
tr.clickable {
|
|
60
|
+
td>a {
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&:hover {
|
|
65
|
+
background: #eee;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
td {
|
|
70
|
+
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
|
71
|
+
|
|
72
|
+
span {
|
|
73
|
+
display: block;
|
|
74
|
+
color: inherit;
|
|
75
|
+
cursor: default;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.scrollable {
|
|
81
|
+
width: 100%;
|
|
82
|
+
overflow: auto;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.data-cell {
|
|
86
|
+
white-space: pre-line;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
table.table--grid {
|
|
90
|
+
tbody {
|
|
91
|
+
td {
|
|
92
|
+
border-right: 1px solid rgba(0, 0, 0, 0.12);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
97
|
+
|
|
98
|
+
<!-- Overridable -->
|
|
99
|
+
<style lang="scss">
|
|
100
|
+
.panels-table {
|
|
101
|
+
th {
|
|
102
|
+
padding: 10px 4px;
|
|
103
|
+
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
|
104
|
+
// border-left: 1px solid rgba(0, 0, 0, 0.12);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
td {
|
|
108
|
+
>span {
|
|
109
|
+
padding: 10px 24px;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div class="content">
|
|
6
6
|
<v-btn style="z-index: 11;" size="24" :icon="node.expand ? 'arrow_drop_down' : 'arrow_right'"
|
|
7
7
|
v-if="props.node.rows" @click="node.expand = !node.expand" variant="plain" @click.stop></v-btn>
|
|
8
|
-
<div
|
|
8
|
+
<div class="expand-none" v-else></div>
|
|
9
9
|
<div class="text">
|
|
10
10
|
<v-icon v-if="node.icon" :size="node.icon.size || 24" :icon="node.icon.name"
|
|
11
11
|
:color="node.icon.color"></v-icon>
|
|
@@ -27,6 +27,12 @@
|
|
|
27
27
|
</template>
|
|
28
28
|
|
|
29
29
|
<style lang="scss">
|
|
30
|
+
.panels-tree {
|
|
31
|
+
.expand-none {
|
|
32
|
+
width: 24px;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
.gtree-row .node {
|
|
31
37
|
position: relative;
|
|
32
38
|
width: 100%;
|
package/index.js
CHANGED
|
@@ -26,7 +26,8 @@ Vue.use(vuetify);
|
|
|
26
26
|
import { gmapPlugin } from "./plugins/gmap";
|
|
27
27
|
Vue.use(gmapPlugin, {
|
|
28
28
|
key: import.meta.env.VITE_GMAPS_API_KEY,
|
|
29
|
-
libraries: 'places'
|
|
29
|
+
libraries: 'places',
|
|
30
|
+
loading: 'async'
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
// import VueAnalytics from 'vue-analytics'
|
|
@@ -40,7 +41,7 @@ import "./extensions/array.js";
|
|
|
40
41
|
|
|
41
42
|
// Recursive components must be global
|
|
42
43
|
import VerticalPanel from "./components/panels/vertical.vue";
|
|
43
|
-
import ResponsivePanel from "./components/
|
|
44
|
+
import ResponsivePanel from "./components/responsive.vue";
|
|
44
45
|
import Component from "./components/component.vue";
|
|
45
46
|
import CommonIcon from "./components/_icon.vue";
|
|
46
47
|
import CommonBadge from "./components/_badge.vue";
|
|
@@ -49,7 +50,7 @@ import CommonButton from "./components/_button.vue";
|
|
|
49
50
|
import CommonChip from "./components/_chip.vue";
|
|
50
51
|
import CommonMessage from "./components/_message.vue";
|
|
51
52
|
import CommonDropdownMenu from "./components/_dropdownMenu.vue";
|
|
52
|
-
import CommonResponsive from "./components/
|
|
53
|
+
import CommonResponsive from "./components/responsive.vue";
|
|
53
54
|
import CommonTemplateMenu from "./templates/_menu.vue";
|
|
54
55
|
import RichButton from "./components/button.vue";
|
|
55
56
|
Vue.component("panels-vertical", VerticalPanel);
|
|
@@ -82,6 +83,9 @@ Vue.mixin(stylesMixin);
|
|
|
82
83
|
import scrollingMixin from "./components/mixins/scrolling.js";
|
|
83
84
|
Vue.mixin(scrollingMixin);
|
|
84
85
|
|
|
86
|
+
import updatableComponent from "./components/mixins/updatableComponent";
|
|
87
|
+
Vue.mixin(updatableComponent);
|
|
88
|
+
|
|
85
89
|
Vue.config.globalProperties.extension = {};
|
|
86
90
|
import extension from "./components/mixins/extension.js";
|
|
87
91
|
Vue.mixin(extension);
|
|
@@ -117,9 +121,6 @@ window.GLib = Framework;
|
|
|
117
121
|
import driverCustomBehavior from "./plugins/driverCustomBehavior";
|
|
118
122
|
Vue.use(driverCustomBehavior);
|
|
119
123
|
|
|
120
|
-
import updatableComponent from "./plugins/updatableComponent";
|
|
121
|
-
Vue.use(updatableComponent);
|
|
122
|
-
|
|
123
124
|
import 'flag-icons/css/flag-icons.min.css';
|
|
124
125
|
import 'v-phone-input/dist/v-phone-input.css';
|
|
125
126
|
import { createVPhoneInput } from 'v-phone-input';
|
package/package.json
CHANGED
package/templates/thumbnail.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component :is="componentName" :href="$href()" class="thumbnail" :class="cssClasses" @[clickCondition]="$onClick()">
|
|
3
|
-
<panels-responsive :spec="spec.header" />
|
|
3
|
+
<panels-responsive v-if="spec.header" :spec="spec.header" />
|
|
4
4
|
<div style="display:flex;">
|
|
5
5
|
<!-- <div v-if="spec.leftOuterButtons" style="display:flex; margin-top:10px;">
|
|
6
6
|
<template v-for="(item, index) in spec.leftOuterButtons" :key="index">
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
</template>
|
|
71
71
|
|
|
72
72
|
</v-list-item>
|
|
73
|
-
<panels-responsive :spec="spec.right" />
|
|
73
|
+
<panels-responsive v-if="spec.right" :spec="spec.right" />
|
|
74
74
|
</div>
|
|
75
|
-
<panels-responsive :spec="spec.footer" />
|
|
75
|
+
<panels-responsive v-if="spec.footer" :spec="spec.footer" />
|
|
76
76
|
</component>
|
|
77
77
|
</template>
|
|
78
78
|
|
package/utils/hash.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import merge from 'lodash.merge';
|
|
1
2
|
export default class Hash {
|
|
2
3
|
constructor(object) {
|
|
3
4
|
for (const key in object) {
|
|
@@ -51,4 +52,8 @@ export default class Hash {
|
|
|
51
52
|
handler(key, this[key]);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
|
|
56
|
+
deepMerge(hash) {
|
|
57
|
+
return merge(this, hash)
|
|
58
|
+
}
|
|
54
59
|
}
|
package/utils/http.js
CHANGED
|
@@ -2,6 +2,7 @@ import Type from "./type";
|
|
|
2
2
|
import Action from "../action";
|
|
3
3
|
import { nextTick } from 'vue';
|
|
4
4
|
import { ctx, dialogs, jsonView, vueApp } from "../store";
|
|
5
|
+
import Hash from "./hash";
|
|
5
6
|
|
|
6
7
|
let loading = false;
|
|
7
8
|
|
|
@@ -59,9 +60,21 @@ export default class {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
// Merge params in `url` and `formData` that have the same name.
|
|
64
|
+
static mergeDuplicateParamsIntoUrl(url, properties) {
|
|
65
|
+
const baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
|
|
66
|
+
const hash = new Hash()
|
|
67
|
+
url.searchParams.forEach((value, name) => {
|
|
68
|
+
hash[name] = value;
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
properties.url = Utils.url.appendParams(baseUrl, hash.deepMerge(properties.formData || {}))
|
|
72
|
+
properties.formData = {}
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
// Set `forcePushHistory` when it is important to clear the forward history.
|
|
63
76
|
static load(properties, component, windowMode, forcePushHistory) {
|
|
64
|
-
const urlString = properties
|
|
77
|
+
const urlString = properties.url;
|
|
65
78
|
let url;
|
|
66
79
|
try {
|
|
67
80
|
url = new URL(urlString);
|
|
@@ -70,6 +83,7 @@ export default class {
|
|
|
70
83
|
throw e;
|
|
71
84
|
}
|
|
72
85
|
const domainMatched = window.location.hostname == url.hostname;
|
|
86
|
+
this.mergeDuplicateParamsIntoUrl(url, properties)
|
|
73
87
|
|
|
74
88
|
// If this is an external domain, we rely on `onUnload()` instead.
|
|
75
89
|
// if (domainMatched && !this.proceedEvenWhenDirty()) {
|
|
@@ -78,12 +92,12 @@ export default class {
|
|
|
78
92
|
|
|
79
93
|
if (Utils.settings.reactive && domainMatched) {
|
|
80
94
|
const currentUrl = window.location.href;
|
|
81
|
-
const htmlUrl = Utils.url.htmlUrl(properties
|
|
95
|
+
const htmlUrl = Utils.url.htmlUrl(properties.url);
|
|
82
96
|
const sameUrl = htmlUrl === currentUrl;
|
|
83
97
|
const topOfDialog = Utils.type.isObject(dialogs.value.last());
|
|
84
98
|
const windowOrDialog = windowMode ? true : !topOfDialog;
|
|
85
99
|
|
|
86
|
-
|
|
100
|
+
this.execute(properties, "GET", component, (data, response) => {
|
|
87
101
|
// TODO: Check if it is okay to remove this `if` statement so we always push even if it's the same URL.
|
|
88
102
|
if (forcePushHistory || (windowOrDialog && !sameUrl && !properties.updateExisting)) {
|
|
89
103
|
const redirectUrl = Utils.url.htmlUrl(response.url);
|
package/actions/ws/push.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { vueApp } from "../../store";
|
|
2
|
-
|
|
3
|
-
export default class {
|
|
4
|
-
execute(properties, component, params) {
|
|
5
|
-
Utils.type.ifString(properties.topic, topicName => {
|
|
6
|
-
const ws = vueApp.webSocket;
|
|
7
|
-
const channel = ws.channels[topicName];
|
|
8
|
-
|
|
9
|
-
Utils.type.ifString(properties.event, eventName => {
|
|
10
|
-
if (channel) {
|
|
11
|
-
const payload = Object.assign({}, properties.payload, ws.header, {
|
|
12
|
-
formData: properties.formData
|
|
13
|
-
});
|
|
14
|
-
console.debug(`Pushing to '${topicName}/${eventName}'`, payload);
|
|
15
|
-
channel
|
|
16
|
-
.push(eventName, payload)
|
|
17
|
-
.receive("ok", resp => {
|
|
18
|
-
console.debug(
|
|
19
|
-
`Push to '${topicName}/${eventName}' succeeded`,
|
|
20
|
-
resp
|
|
21
|
-
);
|
|
22
|
-
Utils.ws.handleResponse(resp.onResponse, component);
|
|
23
|
-
})
|
|
24
|
-
.receive("error", resp => {
|
|
25
|
-
console.debug(`Push to '${topicName}/${eventName}' failed`, resp);
|
|
26
|
-
Utils.ws.handleResponse(resp.onResponse, component);
|
|
27
|
-
});
|
|
28
|
-
} else {
|
|
29
|
-
console.error(`Topic not joined: '${topicName}'`);
|
|
30
|
-
Utils.launch.snackbar.error(
|
|
31
|
-
"Something went wrong and we have been notified",
|
|
32
|
-
component
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
package/components/_tooltip.vue
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-menu v-if="spec.childButtons" left bottom>
|
|
3
|
-
<template v-slot:activator="{ on: onMenu }">
|
|
4
|
-
<v-tooltip :disabled="tooltip.disabled" :top="tooltipPositionMatches('top')"
|
|
5
|
-
:right="tooltipPositionMatches('right')" :bottom="tooltipPositionMatches('bottom')"
|
|
6
|
-
:left="tooltipPositionMatches('left')">
|
|
7
|
-
<template v-slot:activator="{ on: onTooltip }">
|
|
8
|
-
<slot name="activator" :on="{ ...onMenu, ...onTooltip }" />
|
|
9
|
-
</template>
|
|
10
|
-
<span> {{ tooltip.text }} </span>
|
|
11
|
-
</v-tooltip>
|
|
12
|
-
</template>
|
|
13
|
-
|
|
14
|
-
<v-list>
|
|
15
|
-
<v-list-item v-for="(childItem, childIndex) in spec.childButtons" :key="childIndex">
|
|
16
|
-
<common-button :spec="buttonSpec(childItem)" :disabled="childItem.disabled" />
|
|
17
|
-
</v-list-item>
|
|
18
|
-
</v-list>
|
|
19
|
-
</v-menu>
|
|
20
|
-
<v-tooltip v-else :disabled="tooltip.disabled" :top="tooltipPositionMatches('top')"
|
|
21
|
-
:right="tooltipPositionMatches('right')" :bottom="tooltipPositionMatches('bottom')"
|
|
22
|
-
:left="tooltipPositionMatches('left')">
|
|
23
|
-
<template v-slot:activator="{ on: onTooltip }">
|
|
24
|
-
<slot name="activator" :on="{ ...onTooltip }" />
|
|
25
|
-
</template>
|
|
26
|
-
<span> {{ tooltip.text }} </span>
|
|
27
|
-
</v-tooltip>
|
|
28
|
-
</template>
|
|
29
|
-
|
|
30
|
-
<script>
|
|
31
|
-
export default {
|
|
32
|
-
props: {
|
|
33
|
-
spec: { type: Object, required: true }
|
|
34
|
-
},
|
|
35
|
-
data() {
|
|
36
|
-
return {
|
|
37
|
-
tooltip: {}
|
|
38
|
-
// childSpec: Object.assign({}, this.spec, { id: undefined })
|
|
39
|
-
};
|
|
40
|
-
},
|
|
41
|
-
methods: {
|
|
42
|
-
$ready() {
|
|
43
|
-
this.updateData();
|
|
44
|
-
// this.tooltip = this.spec.tooltip || { disabled: true };
|
|
45
|
-
// this.childSpec = Object.assign({}, this.spec, { id: undefined });
|
|
46
|
-
},
|
|
47
|
-
// $initAccessories() {
|
|
48
|
-
// this.tooltip = this.spec.tooltip || { disabled: true };
|
|
49
|
-
// },
|
|
50
|
-
tooltipPositionMatches(position) {
|
|
51
|
-
if (this.spec.tooltip && this.spec.tooltip.position) {
|
|
52
|
-
return position == this.spec.tooltip.position;
|
|
53
|
-
} else {
|
|
54
|
-
return position == "bottom";
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
buttonSpec(item) {
|
|
58
|
-
return Object.assign({}, item, {
|
|
59
|
-
view: "button-v1",
|
|
60
|
-
styleClasses: ["text"]
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
$registryEnabled() {
|
|
64
|
-
return false;
|
|
65
|
-
},
|
|
66
|
-
updateData() {
|
|
67
|
-
this.tooltip = this.spec.tooltip || { disabled: true };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
</script>
|
|
72
|
-
|
|
73
|
-
<style lang="scss" scoped></style>
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<line-chart :style="$styles()" :class="$classes()" :data="series"></line-chart>
|
|
3
|
-
</template>
|
|
4
|
-
|
|
5
|
-
<script>
|
|
6
|
-
// import annotation from "../mixins/chart/annotation.js";
|
|
7
|
-
// import tooltip from "../mixins/chart/tooltip.js";
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
// mixins: [annotation],
|
|
11
|
-
props: {
|
|
12
|
-
spec: { type: Object, required: true }
|
|
13
|
-
},
|
|
14
|
-
data: function () {
|
|
15
|
-
return {
|
|
16
|
-
series: [],
|
|
17
|
-
dataName: "dataSeries"
|
|
18
|
-
};
|
|
19
|
-
},
|
|
20
|
-
methods: {
|
|
21
|
-
$ready() {
|
|
22
|
-
this.series.clear();
|
|
23
|
-
this.renderData(this.spec.dataSeries);
|
|
24
|
-
this.fetchNext(this.spec.nextPage);
|
|
25
|
-
},
|
|
26
|
-
fetchData(url) {
|
|
27
|
-
const vm = this;
|
|
28
|
-
Utils.type.ifString(url, val => {
|
|
29
|
-
Utils.http.execute({ url: val }, "GET", this, response => {
|
|
30
|
-
vm.renderData(response.dataSeries);
|
|
31
|
-
vm.fetchNext(response.nextPage);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
},
|
|
35
|
-
fetchNext(nextPage) {
|
|
36
|
-
if (!Utils.type.isObject(nextPage)) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (nextPage.autoload != "all") {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
this.fetchData(nextPage.url);
|
|
43
|
-
},
|
|
44
|
-
renderData(series) {
|
|
45
|
-
if (!Utils.type.isArray(series)) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const data = series.map(element => {
|
|
50
|
-
const dataPoints = {};
|
|
51
|
-
element.points.forEach(point => {
|
|
52
|
-
dataPoints[point.x] = point.y;
|
|
53
|
-
});
|
|
54
|
-
return { name: element.title, data: dataPoints };
|
|
55
|
-
});
|
|
56
|
-
this.series = this.series.concat(data);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
</script>
|
|
61
|
-
|
|
62
|
-
<style scoped></style>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
methods: {
|
|
3
|
-
textStyles(spec) {
|
|
4
|
-
const styles = this.$styles(spec);
|
|
5
|
-
|
|
6
|
-
switch (this.spec.textAlign) {
|
|
7
|
-
case "center":
|
|
8
|
-
styles["text-align"] = "center";
|
|
9
|
-
break;
|
|
10
|
-
case "right":
|
|
11
|
-
styles["text-align"] = "right";
|
|
12
|
-
break;
|
|
13
|
-
default:
|
|
14
|
-
styles["text-align"] = "left";
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return styles;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { createConsumer } from "@rails/actioncable";
|
|
2
|
-
import { vueApp } from "../../../store";
|
|
3
|
-
|
|
4
|
-
const consumer = createConsumer();
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
data: function () {
|
|
8
|
-
return {
|
|
9
|
-
_wsSocket: null
|
|
10
|
-
};
|
|
11
|
-
},
|
|
12
|
-
methods: {
|
|
13
|
-
$wsInitActionCable(spec) {
|
|
14
|
-
const component = this;
|
|
15
|
-
Utils.type.ifObject(
|
|
16
|
-
spec,
|
|
17
|
-
ws => {
|
|
18
|
-
const channelName = ws.channel;
|
|
19
|
-
const subscription = Object.assign({}, ws.params, {
|
|
20
|
-
channel: channelName
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
console.debug("Connecting to channel", subscription);
|
|
24
|
-
|
|
25
|
-
consumer.subscriptions.create(subscription, {
|
|
26
|
-
connected() {
|
|
27
|
-
const ws = vueApp.actionCable;
|
|
28
|
-
ws.channels[channelName] = this;
|
|
29
|
-
console.debug("Connected to channel", channelName);
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
disconnected() { },
|
|
33
|
-
|
|
34
|
-
received(data) {
|
|
35
|
-
const action = data.action;
|
|
36
|
-
const payload = { ...action, filterKey: ws.filterKey };
|
|
37
|
-
if (ws.filterKey === data.filterKey) {
|
|
38
|
-
GLib.action.execute(payload, component);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
},
|
|
43
|
-
() => {
|
|
44
|
-
// this._wsDisconnectSocket();
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<common-responsive ref="delegate" :spec="spec" />
|
|
3
|
-
</template>
|
|
4
|
-
|
|
5
|
-
<script>
|
|
6
|
-
export default {
|
|
7
|
-
props: {
|
|
8
|
-
spec: {
|
|
9
|
-
type: Object,
|
|
10
|
-
required: true,
|
|
11
|
-
default: function () {
|
|
12
|
-
return {};
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
</script>
|
|
18
|
-
|
|
19
|
-
<style scoped>
|
|
20
|
-
.hover {
|
|
21
|
-
width: 8rem;
|
|
22
|
-
}
|
|
23
|
-
</style>
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
install: (Vue, options) => {
|
|
3
|
-
Vue.mixin({
|
|
4
|
-
computed: {
|
|
5
|
-
viewId() {
|
|
6
|
-
if (this.spec && this.spec.id) {
|
|
7
|
-
const id = this.spec.id;
|
|
8
|
-
|
|
9
|
-
if (id.includes('{{entry_index}}')) {
|
|
10
|
-
const dynamicGroupEntry = this.$closest("fields/internalDynamicGroupEntry");
|
|
11
|
-
if (dynamicGroupEntry) {
|
|
12
|
-
return dynamicGroupEntry.$populateIndexes(id);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return id;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
methods: {
|
|
21
|
-
$ready() {
|
|
22
|
-
let spec = this.spec;
|
|
23
|
-
if (spec && spec.id && this.$registryEnabled()) {
|
|
24
|
-
|
|
25
|
-
const id = this.viewId;
|
|
26
|
-
|
|
27
|
-
const existingComponent = GLib.component.findById(id);
|
|
28
|
-
// A component with the same ID in a different page shouldn't be considered a
|
|
29
|
-
// duplicate. See `utils/components#deregister` for more details.
|
|
30
|
-
if (existingComponent) {
|
|
31
|
-
console.warn(
|
|
32
|
-
"Duplicate component ID:",
|
|
33
|
-
id,
|
|
34
|
-
"Existing:",
|
|
35
|
-
GLib.component.vueName(existingComponent),
|
|
36
|
-
"New:",
|
|
37
|
-
GLib.component.vueName(this)
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
const newComponent = this;
|
|
41
|
-
GLib.component.register(id, newComponent);
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
$tearDown() {
|
|
45
|
-
let spec = this.spec;
|
|
46
|
-
|
|
47
|
-
if (spec && spec.id && this.$registryEnabled()) {
|
|
48
|
-
GLib.component.deregister(spec.id, this);
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
$registryEnabled() {
|
|
52
|
-
// Common classes such as `_select` need to return false so that it doesn't override its parent (e.g. `select`).
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
};
|