glib-web 3.2.0 → 3.4.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/actions/dialogs/alert.js +1 -0
- package/actions/http/get.js +1 -1
- package/components/charts/area.vue +4 -2
- package/components/charts/column.vue +2 -1
- package/components/charts/line.vue +2 -1
- package/components/charts/pie.vue +2 -1
- package/components/component.vue +4 -4
- package/components/composable/conditional.js +11 -0
- package/components/fields/_otp.vue +161 -0
- package/components/fields/location.vue +20 -52
- package/components/fields/otpField.vue +10 -116
- package/components/mixins/events.js +10 -2
- package/components/mixins/styles.js +26 -51
- package/index.js +24 -17
- package/nav/dialog.vue +18 -18
- package/nav/drawer.vue +8 -2
- package/nav/snackbar.vue +16 -29
- package/package.json +3 -4
- package/utils/http.js +12 -12
- package/utils/launch.js +30 -29
package/actions/dialogs/alert.js
CHANGED
package/actions/http/get.js
CHANGED
|
@@ -22,7 +22,7 @@ export default class {
|
|
|
22
22
|
Utils.type.ifString(spec.historyUrl, historyUrl => {
|
|
23
23
|
const cleanUrl = Utils.url.htmlUrl(historyUrl);
|
|
24
24
|
if (cleanUrl !== currentUrl) {
|
|
25
|
-
const data = Object.assign({},
|
|
25
|
+
const data = Object.assign({}, page, {
|
|
26
26
|
replayGetResponse: page.onResponse
|
|
27
27
|
});
|
|
28
28
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<div>
|
|
3
|
+
<area-chart :style="genericStyles()" :class="$classes()" :data="series" :colors="color">></area-chart>
|
|
4
|
+
</div>
|
|
3
5
|
</template>
|
|
4
6
|
|
|
5
7
|
<script setup>
|
|
@@ -7,6 +9,6 @@ import { computed } from 'vue';
|
|
|
7
9
|
import { singleDataSeries } from './series';
|
|
8
10
|
|
|
9
11
|
const { spec } = defineProps({ spec: Object })
|
|
10
|
-
|
|
12
|
+
const color = spec.colors == null ? '' : [spec.colors]
|
|
11
13
|
const series = computed(() => singleDataSeries(spec.dataSeries))
|
|
12
14
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<column-chart :data="series" :stacked="spec.stacked"></column-chart>
|
|
2
|
+
<column-chart :data="series" :stacked="spec.stacked" :colors="color"></column-chart>
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script setup>
|
|
@@ -8,6 +8,7 @@ import { computed } from 'vue';
|
|
|
8
8
|
import { multipleDataSeries } from './series';
|
|
9
9
|
|
|
10
10
|
const { spec } = defineProps({ spec: Object })
|
|
11
|
+
const color = spec.colors == null ? '' : spec.colors
|
|
11
12
|
const series = computed(() => multipleDataSeries(spec.dataGroups))
|
|
12
13
|
|
|
13
14
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<line-chart :style="genericStyles()" :class="$classes()" :data="series"></line-chart>
|
|
2
|
+
<line-chart :style="genericStyles()" :class="$classes()" :data="series" :colors="color"></line-chart>
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script setup>
|
|
@@ -7,6 +7,7 @@ import { computed } from 'vue';
|
|
|
7
7
|
import { multipleDataSeries } from './series';
|
|
8
8
|
|
|
9
9
|
const { spec } = defineProps({ spec: Object })
|
|
10
|
+
const color = spec.colors == null ? '' : spec.colors
|
|
10
11
|
|
|
11
12
|
const series = computed(() => multipleDataSeries(spec.dataSeries))
|
|
12
13
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<pie-chart :style="genericStyles()" :class="$classes()" :data="series"></pie-chart>
|
|
2
|
+
<pie-chart :style="genericStyles()" :class="$classes()" :data="series" :colors="color"></pie-chart>
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script setup>
|
|
@@ -7,6 +7,7 @@ import { computed } from 'vue';
|
|
|
7
7
|
import { singleDataSeries } from './series';
|
|
8
8
|
|
|
9
9
|
const { spec } = defineProps({ spec: Object })
|
|
10
|
+
const color = spec.colors == null ? '' : spec.colors
|
|
10
11
|
const series = computed(() => singleDataSeries(spec.dataSeries))
|
|
11
12
|
|
|
12
13
|
</script>
|
package/components/component.vue
CHANGED
|
@@ -148,7 +148,7 @@ export default {
|
|
|
148
148
|
"views-hr": Hr,
|
|
149
149
|
"views-spacer": Spacer,
|
|
150
150
|
|
|
151
|
-
"views-map": Map,
|
|
151
|
+
// "views-map": Map,
|
|
152
152
|
"views-tabBar": TabBar,
|
|
153
153
|
"views-calendar": Calendar,
|
|
154
154
|
"views-shareButton": ShareButton,
|
|
@@ -221,10 +221,10 @@ export default {
|
|
|
221
221
|
},
|
|
222
222
|
computed: {
|
|
223
223
|
menter() {
|
|
224
|
-
return this.spec.onMouseEnter ? 'mouseenter' : null
|
|
224
|
+
return this.spec.onMouseEnter ? 'mouseenter' : null;
|
|
225
225
|
},
|
|
226
226
|
mleave() {
|
|
227
|
-
return this.spec.onMouseLeave ? 'mouseleave' : null
|
|
227
|
+
return this.spec.onMouseLeave ? 'mouseleave' : null;
|
|
228
228
|
}
|
|
229
229
|
},
|
|
230
230
|
watch: {
|
|
@@ -275,7 +275,7 @@ export default {
|
|
|
275
275
|
return false;
|
|
276
276
|
},
|
|
277
277
|
handlePopover(spec) {
|
|
278
|
-
GLib.action.execute(spec, this)
|
|
278
|
+
GLib.action.execute(spec, this);
|
|
279
279
|
}
|
|
280
280
|
},
|
|
281
281
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { reactive, watchEffect } from "vue";
|
|
2
|
+
import jsonLogic from 'json-logic-js';
|
|
3
|
+
|
|
4
|
+
const fieldModels = reactive({});
|
|
5
|
+
|
|
6
|
+
const watchFieldModels = (logic, cb) => watchEffect(() => {
|
|
7
|
+
const value = jsonLogic.apply(logic, fieldModels);
|
|
8
|
+
cb(value);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export { fieldModels, watchFieldModels };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :style="$styles()" :class="$classes()">
|
|
3
|
+
<div v-if="spec.type === 'text'">
|
|
4
|
+
<input v-for="(digit, index) in digits" ref="input" :key="index" v-model="val[index]" type="text" maxlength="1"
|
|
5
|
+
class="otp-input" @input="handleInput($event, index)" @keydown="handleKeydown($event, index)"
|
|
6
|
+
@paste="handlePaste($event, index)" @focus="handleFocus($event, index)" @blur="handleBlur($event, index)"
|
|
7
|
+
@click="handleClick($event)" />
|
|
8
|
+
<label class="hint">{{ spec.hint }}</label>
|
|
9
|
+
</div>
|
|
10
|
+
<div v-else>
|
|
11
|
+
<input v-for="(digit, index) in digits" :key="index" ref="input" :value="val[index]" type="number"
|
|
12
|
+
oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);"
|
|
13
|
+
maxlength="1" class="otp-input" @input="handleInput($event, index)" @keydown="handleKeydown($event, index)"
|
|
14
|
+
@paste="handlePaste($event, index)" @focus="handleFocus($event, index)" @blur="handleBlur($event, index)"
|
|
15
|
+
@click="handleClick($event)" />
|
|
16
|
+
<label class="hint">{{ spec.hint }}</label>
|
|
17
|
+
</div>
|
|
18
|
+
<input type="hidden" :name="fieldName" v-model="fieldModel" />
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
import bus from '../../utils/eventBus'
|
|
24
|
+
|
|
25
|
+
export default {
|
|
26
|
+
props: {
|
|
27
|
+
spec: { type: Object, required: true }
|
|
28
|
+
},
|
|
29
|
+
data() {
|
|
30
|
+
return {
|
|
31
|
+
otp: (this.spec.value || '').toString().split(""),
|
|
32
|
+
alignment: this.spec.align === null ? 'left' : this.spec.align
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
computed: {
|
|
36
|
+
otpValue() {
|
|
37
|
+
if (!this.otp) return ''
|
|
38
|
+
|
|
39
|
+
// concatenate the digits into a single string
|
|
40
|
+
return this.otp.join("");
|
|
41
|
+
},
|
|
42
|
+
digits() {
|
|
43
|
+
return this.spec.length
|
|
44
|
+
},
|
|
45
|
+
val() {
|
|
46
|
+
const field = this.fieldModel == null ? [] : this.fieldModel.toString().split("")
|
|
47
|
+
|
|
48
|
+
if (this.alignment === 'right') {
|
|
49
|
+
if (this.spec.valueIf == null) {
|
|
50
|
+
if (this.digits > this.otp.length && this.otp.length != 0) {
|
|
51
|
+
const results = []
|
|
52
|
+
for (let i = 0; i < (this.digits - this.otp.length); i++) {
|
|
53
|
+
results.push('')
|
|
54
|
+
}
|
|
55
|
+
return Reflect.apply(Array.prototype.concat, results, this.otp)
|
|
56
|
+
} else {
|
|
57
|
+
return field
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
return field
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
if (this.spec.valueIf == null) {
|
|
64
|
+
return this.otp
|
|
65
|
+
} else {
|
|
66
|
+
return field
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
methods: {
|
|
72
|
+
created() {
|
|
73
|
+
this.fieldModel = this.otpValue;
|
|
74
|
+
},
|
|
75
|
+
handleInput(event, index) {
|
|
76
|
+
if (event.target.value.length === 1) {
|
|
77
|
+
this.focusNext(index);
|
|
78
|
+
}
|
|
79
|
+
this.val[index] = event.target.value;
|
|
80
|
+
this.fieldModel = this.otp.join("");
|
|
81
|
+
},
|
|
82
|
+
handleKeydown(event, index) {
|
|
83
|
+
if (event.key === "Backspace") {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
if (this.otp[index] !== "") {
|
|
86
|
+
this.otp[index] = ""
|
|
87
|
+
} else {
|
|
88
|
+
this.focusPrevious(index);
|
|
89
|
+
this.otp[index - 1] = ""
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
handlePaste(event, index) {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
const pasted = event.clipboardData
|
|
96
|
+
.getData("text")
|
|
97
|
+
.slice(0, this.digits - index);
|
|
98
|
+
for (let i = 0; i < pasted.length; i++) {
|
|
99
|
+
this.$set(this.otp, index + i, pasted[i]);
|
|
100
|
+
}
|
|
101
|
+
this.focusNext(index + pasted.length - 1);
|
|
102
|
+
},
|
|
103
|
+
handleFocus(event, index) {
|
|
104
|
+
if (index > 0 && this.otp[index - 1] === "") {
|
|
105
|
+
this.focusPrevious(index);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
handleBlur(event, index) {
|
|
109
|
+
if (this.otp.filter(digit => digit === "").length === 0) {
|
|
110
|
+
bus.$emit("complete", this.otp.join(""));
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
focusNext(index) {
|
|
114
|
+
if (index < this.digits - 1) {
|
|
115
|
+
this.$refs.input[index + 1].focus();
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
focusPrevious(index) {
|
|
119
|
+
if (index > 0) {
|
|
120
|
+
this.$refs.input[index - 1].focus();
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
handleClick(event) {
|
|
124
|
+
event.target.select();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
</script>
|
|
129
|
+
<style scoped>
|
|
130
|
+
.otp-input {
|
|
131
|
+
border: 1px solid #47495f;
|
|
132
|
+
text-align: center;
|
|
133
|
+
width: 50px;
|
|
134
|
+
height: 50px;
|
|
135
|
+
border-radius: 4px;
|
|
136
|
+
font-size: 24px;
|
|
137
|
+
margin: 8px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Chrome, Safari, Edge, Opera */
|
|
141
|
+
input::-webkit-outer-spin-button,
|
|
142
|
+
input::-webkit-inner-spin-button {
|
|
143
|
+
-webkit-appearance: none;
|
|
144
|
+
margin: 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Firefox */
|
|
148
|
+
input[type="number"] {
|
|
149
|
+
-moz-appearance: textfield;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.hint {
|
|
153
|
+
display: none;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
input:focus~.hint,
|
|
157
|
+
input:focus-visible~.hint {
|
|
158
|
+
display: block;
|
|
159
|
+
padding: 8px 16px;
|
|
160
|
+
}
|
|
161
|
+
</style>
|
|
@@ -4,62 +4,29 @@
|
|
|
4
4
|
It's safer to manually implement styling than adding another dependency such as vuetify-google-autocomplete,
|
|
5
5
|
which behaves differently and needs to be actively maintained.
|
|
6
6
|
-->
|
|
7
|
-
<div
|
|
8
|
-
class="v-input theme--light v-text-field v-text-field--enclosed v-text-field--outlined"
|
|
9
|
-
>
|
|
7
|
+
<div class="v-input theme--light v-text-field v-text-field--enclosed v-text-field--outlined">
|
|
10
8
|
<div class="v-input__control">
|
|
11
9
|
<div class="v-input__slot">
|
|
12
10
|
<fieldset aria-hidden="true">
|
|
13
11
|
<legend :style="`width: ${labelWidth}px;`"><span></span></legend>
|
|
14
12
|
</fieldset>
|
|
15
13
|
<div class="v-text-field__slot">
|
|
16
|
-
<label
|
|
17
|
-
|
|
18
|
-
class="v-label v-label--active theme--light"
|
|
19
|
-
style="left: 0px; right: auto; position: absolute; font-size: 16px;"
|
|
20
|
-
>{{ spec.label }}</label
|
|
21
|
-
>
|
|
14
|
+
<label ref="label" class="v-label v-label--active theme--light"
|
|
15
|
+
style="left: 0px; right: auto; position: absolute; font-size: 16px;">{{ spec.label }}</label>
|
|
22
16
|
<!-- See https://developers.google.com/places/web-service/autocomplete#place_types for autocompleteOptions -->
|
|
23
|
-
<gmap-autocomplete
|
|
24
|
-
:
|
|
25
|
-
:placeholder="spec.placeholder"
|
|
26
|
-
:value="address"
|
|
27
|
-
:options="spec.autocompleteOptions"
|
|
28
|
-
@input="onAddressChanged"
|
|
29
|
-
@place_changed="onPlaceChanged"
|
|
30
|
-
/>
|
|
17
|
+
<gmap-autocomplete :name="spec.name" :placeholder="spec.placeholder" :value="address"
|
|
18
|
+
:options="spec.autocompleteOptions" @input="onAddressChanged" @place_changed="onPlaceChanged" />
|
|
31
19
|
</div>
|
|
32
20
|
</div>
|
|
33
21
|
</div>
|
|
34
22
|
</div>
|
|
35
23
|
|
|
36
|
-
<gmap-map
|
|
37
|
-
|
|
38
|
-
:
|
|
39
|
-
:zoom="13"
|
|
40
|
-
class="mt-3 mb-3 map"
|
|
41
|
-
map-type-id="roadmap"
|
|
42
|
-
:style="mapStyles"
|
|
43
|
-
@idle="onActionCompleted"
|
|
44
|
-
>
|
|
45
|
-
<gmap-marker
|
|
46
|
-
:position="markerPos"
|
|
47
|
-
:clickable="true"
|
|
48
|
-
:draggable="true"
|
|
49
|
-
@drag="onDrag"
|
|
50
|
-
@dragend="onDrag"
|
|
51
|
-
/>
|
|
24
|
+
<gmap-map ref="map" :center="{ lat: 0, lng: 0 }" :zoom="13" class="mt-3 mb-3 map" map-type-id="roadmap"
|
|
25
|
+
:style="mapStyles" @idle="onActionCompleted">
|
|
26
|
+
<gmap-marker :position="markerPos" :clickable="true" :draggable="true" @drag="onDrag" @dragend="onDrag" />
|
|
52
27
|
</gmap-map>
|
|
53
|
-
<glib-component
|
|
54
|
-
|
|
55
|
-
ref="latitudeView"
|
|
56
|
-
:spec="latitudeField"
|
|
57
|
-
/>
|
|
58
|
-
<glib-component
|
|
59
|
-
v-if="longitudeField"
|
|
60
|
-
ref="longitudeView"
|
|
61
|
-
:spec="longitudeField"
|
|
62
|
-
/>
|
|
28
|
+
<glib-component v-if="latitudeField" ref="latitudeView" :spec="latitudeField" />
|
|
29
|
+
<glib-component v-if="longitudeField" ref="longitudeView" :spec="longitudeField" />
|
|
63
30
|
<glib-component v-if="zoomField" ref="zoomView" :spec="zoomField" />
|
|
64
31
|
</v-container>
|
|
65
32
|
</template>
|
|
@@ -97,8 +64,8 @@ export default {
|
|
|
97
64
|
mapStyles() {
|
|
98
65
|
return this.longitude != null && this.latitude != null
|
|
99
66
|
? {
|
|
100
|
-
|
|
101
|
-
|
|
67
|
+
display: "block"
|
|
68
|
+
}
|
|
102
69
|
: { display: "none" };
|
|
103
70
|
}
|
|
104
71
|
},
|
|
@@ -112,11 +79,11 @@ export default {
|
|
|
112
79
|
this.centerInitial();
|
|
113
80
|
|
|
114
81
|
setTimeout(() => {
|
|
115
|
-
this.latitudeView = this.$refs.latitudeView
|
|
116
|
-
this.longitudeView = this.$refs.longitudeView
|
|
82
|
+
this.latitudeView = this.$refs.latitudeView;
|
|
83
|
+
this.longitudeView = this.$refs.longitudeView;
|
|
117
84
|
|
|
118
85
|
if (this.$refs.zoomView) {
|
|
119
|
-
this.zoomView = this.$refs.zoomView
|
|
86
|
+
this.zoomView = this.$refs.zoomView;
|
|
120
87
|
}
|
|
121
88
|
|
|
122
89
|
const labelView = this.$refs.label;
|
|
@@ -139,10 +106,10 @@ export default {
|
|
|
139
106
|
}
|
|
140
107
|
},
|
|
141
108
|
setLocation(latitudeValue, longitudeValue, dropPin) {
|
|
142
|
-
this.$
|
|
109
|
+
this.$gmapApiPromiseLazy().then(() => {
|
|
143
110
|
// eslint-disable-next-line no-undef
|
|
144
111
|
const location = new google.maps.LatLng(latitudeValue, longitudeValue);
|
|
145
|
-
this.$refs.map
|
|
112
|
+
this.$refs.map.mapInstance.setCenter(location);
|
|
146
113
|
|
|
147
114
|
if (dropPin) {
|
|
148
115
|
this.setMarkerPosition(location);
|
|
@@ -161,14 +128,15 @@ export default {
|
|
|
161
128
|
|
|
162
129
|
const location = place.geometry.location;
|
|
163
130
|
this.setMarkerPosition(location);
|
|
164
|
-
|
|
131
|
+
|
|
132
|
+
this.$refs.map.mapInstance.setCenter(location);
|
|
165
133
|
},
|
|
166
134
|
onDrag(e) {
|
|
167
135
|
this.setMarkerPosition(e.latLng);
|
|
168
136
|
},
|
|
169
137
|
onActionCompleted() {
|
|
170
138
|
if (this.zoomView) {
|
|
171
|
-
this.zoomView.fieldModel = this.$refs.map
|
|
139
|
+
this.zoomView.fieldModel = this.$refs.map.mapInstance.zoom;
|
|
172
140
|
}
|
|
173
141
|
},
|
|
174
142
|
onAddressChanged(e) {
|
|
@@ -1,130 +1,24 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<div v-if="spec.type === 'text'">
|
|
4
|
-
<input v-for="(digit, index) in digits" ref="input" :key="index" v-model="otp[index]" type="text" maxlength="1"
|
|
5
|
-
class="otp-input" @input="handleInput($event, index)" @keydown="handleKeydown($event, index)"
|
|
6
|
-
@paste="handlePaste($event, index)" @focus="handleFocus($event, index)" @blur="handleBlur($event, index)"
|
|
7
|
-
@click="handleClick($event)" />
|
|
8
|
-
<label class="hint">{{ spec.hint }}</label>
|
|
9
|
-
</div>
|
|
10
|
-
<div v-else>
|
|
11
|
-
<input v-for="(digit, index) in digits" ref="input" :key="index" v-model="otp[index]" type="number"
|
|
12
|
-
oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);"
|
|
13
|
-
maxlength="1" class="otp-input" @input="handleInput($event, index)" @keydown="handleKeydown($event, index)"
|
|
14
|
-
@paste="handlePaste($event, index)" @focus="handleFocus($event, index)" @blur="handleBlur($event, index)"
|
|
15
|
-
@click="handleClick($event)" />
|
|
16
|
-
<label class="hint">{{ spec.hint }}</label>
|
|
17
|
-
</div>
|
|
18
|
-
<input type="hidden" :name="spec.name" :value="otpValue" />
|
|
19
|
-
</div>
|
|
2
|
+
<fields-otp :spec="spec" />
|
|
20
3
|
</template>
|
|
21
4
|
|
|
22
5
|
<script>
|
|
23
|
-
import
|
|
6
|
+
import OtpField from "./_otp.vue";
|
|
24
7
|
|
|
25
8
|
export default {
|
|
9
|
+
components: {
|
|
10
|
+
// Need to start with `fields-` to enable jsonlogic in `styles.js#_linkFieldModels`
|
|
11
|
+
"fields-otp": OtpField,
|
|
12
|
+
},
|
|
26
13
|
props: {
|
|
27
|
-
spec: { type: Object, required: true }
|
|
14
|
+
spec: { type: Object, required: true },
|
|
28
15
|
},
|
|
29
16
|
data() {
|
|
30
17
|
return {
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
computed: {
|
|
35
|
-
otpValue() {
|
|
36
|
-
if (!this.otp) return ''
|
|
37
|
-
|
|
38
|
-
// concatenate the digits into a single string
|
|
39
|
-
return this.otp.join("");
|
|
40
|
-
},
|
|
41
|
-
digits() {
|
|
42
|
-
return this.spec.length
|
|
43
|
-
},
|
|
18
|
+
value: null,
|
|
19
|
+
};
|
|
44
20
|
},
|
|
45
|
-
methods: {
|
|
46
|
-
handleInput(event, index) {
|
|
47
|
-
if (event.target.value.length === 1) {
|
|
48
|
-
this.focusNext(index);
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
handleKeydown(event, index) {
|
|
52
|
-
if (event.key === "Backspace") {
|
|
53
|
-
event.preventDefault();
|
|
54
|
-
if (this.otp[index] !== "") {
|
|
55
|
-
this.otp[index] = ""
|
|
56
|
-
} else {
|
|
57
|
-
this.focusPrevious(index);
|
|
58
|
-
this.otp[index - 1] = ""
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
handlePaste(event, index) {
|
|
63
|
-
event.preventDefault();
|
|
64
|
-
const pasted = event.clipboardData
|
|
65
|
-
.getData("text")
|
|
66
|
-
.slice(0, this.digits - index);
|
|
67
|
-
for (let i = 0; i < pasted.length; i++) {
|
|
68
|
-
this.$set(this.otp, index + i, pasted[i]);
|
|
69
|
-
}
|
|
70
|
-
this.focusNext(index + pasted.length - 1);
|
|
71
|
-
},
|
|
72
|
-
handleFocus(event, index) {
|
|
73
|
-
if (index > 0 && this.otp[index - 1] === "") {
|
|
74
|
-
this.focusPrevious(index);
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
handleBlur(event, index) {
|
|
78
|
-
if (this.otp.filter(digit => digit === "").length === 0) {
|
|
79
|
-
bus.$emit("complete", this.otp.join(""));
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
focusNext(index) {
|
|
83
|
-
if (index < this.digits - 1) {
|
|
84
|
-
this.$refs.input[index + 1].focus();
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
focusPrevious(index) {
|
|
88
|
-
if (index > 0) {
|
|
89
|
-
this.$refs.input[index - 1].focus();
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
handleClick(event) {
|
|
93
|
-
event.target.select();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
21
|
};
|
|
97
22
|
</script>
|
|
98
|
-
<style scoped>
|
|
99
|
-
.otp-input {
|
|
100
|
-
border: 1px solid #47495f;
|
|
101
|
-
text-align: center;
|
|
102
|
-
width: 50px;
|
|
103
|
-
height: 50px;
|
|
104
|
-
border-radius: 4px;
|
|
105
|
-
font-size: 24px;
|
|
106
|
-
margin: 8px;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/* Chrome, Safari, Edge, Opera */
|
|
110
|
-
input::-webkit-outer-spin-button,
|
|
111
|
-
input::-webkit-inner-spin-button {
|
|
112
|
-
-webkit-appearance: none;
|
|
113
|
-
margin: 0;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/* Firefox */
|
|
117
|
-
input[type="number"] {
|
|
118
|
-
-moz-appearance: textfield;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.hint {
|
|
122
|
-
display: none;
|
|
123
|
-
}
|
|
124
23
|
|
|
125
|
-
|
|
126
|
-
input:focus-visible~.hint {
|
|
127
|
-
display: block;
|
|
128
|
-
padding: 8px 16px;
|
|
129
|
-
}
|
|
130
|
-
</style>
|
|
24
|
+
<style scoped></style>
|
|
@@ -28,6 +28,14 @@ export default {
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
created() {
|
|
31
|
+
// watch components/update
|
|
32
|
+
if (this.spec) {
|
|
33
|
+
this.$watch(
|
|
34
|
+
() => Object.keys(this.spec).map((specKey) => this.spec[specKey]),
|
|
35
|
+
() => this._ready()
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
this.$created();
|
|
32
40
|
},
|
|
33
41
|
mounted() {
|
|
@@ -37,7 +45,7 @@ export default {
|
|
|
37
45
|
},
|
|
38
46
|
beforeUpdate() {
|
|
39
47
|
if (vueApp.isStale) {
|
|
40
|
-
this._mountedUrl = null
|
|
48
|
+
this._mountedUrl = null;
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
if (!this._renderingTheSamePage()) {
|
|
@@ -69,7 +77,7 @@ export default {
|
|
|
69
77
|
return UrlUtils.htmlUrl(onClick.url);
|
|
70
78
|
}
|
|
71
79
|
}
|
|
72
|
-
return
|
|
80
|
+
return '';
|
|
73
81
|
},
|
|
74
82
|
$onClick: function (explicitEvent, spec) {
|
|
75
83
|
const properties = spec || this.spec;
|
|
@@ -1,56 +1,34 @@
|
|
|
1
1
|
import { vueApp } from "../../store";
|
|
2
2
|
import Hash from "../../utils/hash";
|
|
3
|
+
import { fieldModels, watchFieldModels } from "../composable/conditional";
|
|
3
4
|
|
|
4
|
-
import jsonLogic from 'json-logic-js';
|
|
5
|
-
|
|
6
|
-
// jsonLogic.add_operation("add_ms", function(a, b) {
|
|
7
|
-
// if (Utils.type.isString(a)) {
|
|
8
|
-
// const newValue = new Date(new Date(a).getTime() + b * 1000);
|
|
9
|
-
// return newValue.toISOString();
|
|
10
|
-
// }
|
|
11
|
-
// return null;
|
|
12
|
-
// });
|
|
13
5
|
export default {
|
|
14
6
|
data: function () {
|
|
15
7
|
return {
|
|
16
|
-
_fieldModels: {},
|
|
17
8
|
fieldName: null,
|
|
18
9
|
fieldModel: null,
|
|
19
|
-
|
|
10
|
+
_show: true,
|
|
20
11
|
// Some components do not support null or empty string value, so we need to use an intermediary value.
|
|
21
12
|
// See https://github.com/vuetifyjs/vuetify/issues/8876
|
|
22
13
|
vuetifyEmptyString: "<EMPTY_STRING>"
|
|
23
14
|
};
|
|
24
15
|
},
|
|
25
|
-
computed: {
|
|
26
|
-
display() {
|
|
27
|
-
const conditions = this.spec.showIf;
|
|
28
|
-
if (conditions) {
|
|
29
|
-
return jsonLogic.apply(conditions, this.$data._fieldModels)
|
|
30
|
-
? this.$displayValue()
|
|
31
|
-
: "none";
|
|
32
|
-
} else {
|
|
33
|
-
return this.$displayValue();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
16
|
watch: {
|
|
38
17
|
fieldModel: function (val, oldVal) {
|
|
39
18
|
if (val === this.vuetifyEmptyString) {
|
|
40
19
|
val = "";
|
|
41
20
|
}
|
|
42
21
|
|
|
43
|
-
|
|
44
|
-
Object.assign(this.$data._fieldModels, { [this.fieldName]: this.$internalizeValue(val) })
|
|
22
|
+
Object.assign(fieldModels, { [this.fieldName]: this.$internalizeValue(val) });
|
|
45
23
|
|
|
46
24
|
this._checkDirtyState(val, oldVal);
|
|
47
25
|
},
|
|
48
26
|
spec: {
|
|
49
27
|
handler(spec, oldSpec) {
|
|
50
28
|
if (spec) {
|
|
51
|
-
let valueChanged = true
|
|
29
|
+
let valueChanged = true;
|
|
52
30
|
if (oldSpec && oldSpec.value === spec.value) {
|
|
53
|
-
valueChanged = false
|
|
31
|
+
valueChanged = false;
|
|
54
32
|
}
|
|
55
33
|
this._linkFieldModels(valueChanged);
|
|
56
34
|
}
|
|
@@ -58,6 +36,22 @@ export default {
|
|
|
58
36
|
immediate: true
|
|
59
37
|
}
|
|
60
38
|
},
|
|
39
|
+
// watch showIf and valueIf
|
|
40
|
+
mounted() {
|
|
41
|
+
if (this.spec && this.spec.valueIf) {
|
|
42
|
+
watchFieldModels(this.spec.valueIf, (value) => this.fieldModel = this.$sanitizeValue(this.$externalizeValue(value)));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.spec && this.spec.showIf) {
|
|
46
|
+
watchFieldModels(this.spec.showIf, (value) => {
|
|
47
|
+
if (value) {
|
|
48
|
+
this._show = true;
|
|
49
|
+
} else {
|
|
50
|
+
this._show = false;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
61
55
|
methods: {
|
|
62
56
|
// TODO: Deprecated
|
|
63
57
|
genericStyles(spec) {
|
|
@@ -132,13 +126,11 @@ export default {
|
|
|
132
126
|
val => (styles["height"] = val)
|
|
133
127
|
);
|
|
134
128
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// This is the main reason styles have to be used in a dynamic way.
|
|
141
|
-
this._updateDisplay(styles, properties);
|
|
129
|
+
if (this._show) {
|
|
130
|
+
styles['display'] = this.$displayValue();
|
|
131
|
+
} else {
|
|
132
|
+
styles['display'] = 'none';
|
|
133
|
+
}
|
|
142
134
|
|
|
143
135
|
return new Hash(styles);
|
|
144
136
|
},
|
|
@@ -166,18 +158,6 @@ export default {
|
|
|
166
158
|
// });
|
|
167
159
|
// return color;
|
|
168
160
|
// },
|
|
169
|
-
_updateDisplay(styles, properties) {
|
|
170
|
-
Utils.type.ifObject(
|
|
171
|
-
properties.showIf,
|
|
172
|
-
() => {
|
|
173
|
-
styles["display"] = this.display
|
|
174
|
-
}
|
|
175
|
-
);
|
|
176
|
-
Utils.type.ifObject(properties.valueIf, logic => {
|
|
177
|
-
const newValue = jsonLogic.apply(logic, this.$data._fieldModels);
|
|
178
|
-
this.fieldModel = this._sanitizeValue(this.$externalizeValue(newValue));
|
|
179
|
-
});
|
|
180
|
-
},
|
|
181
161
|
$displayValue() {
|
|
182
162
|
// return "block"; Deprecated because it causes problems for multiple rows components
|
|
183
163
|
// return "inherit"; // To be overridden
|
|
@@ -204,11 +184,6 @@ export default {
|
|
|
204
184
|
|
|
205
185
|
const isField = name && name.startsWith("fields-");
|
|
206
186
|
if (hasCondition || isField) {
|
|
207
|
-
const form = this.$closest("panels-form");
|
|
208
|
-
if (form != null) {
|
|
209
|
-
this.$data._fieldModels = form.$data._fieldModels;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
187
|
// Has to be executed before $ready(). This executes regardless of whether a form is found because fields
|
|
213
188
|
// may be used without a form.
|
|
214
189
|
this.fieldName = this.spec.name;
|
package/index.js
CHANGED
|
@@ -7,42 +7,49 @@ import { useTheme } from "vuetify";
|
|
|
7
7
|
import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
|
8
8
|
|
|
9
9
|
// lib for deep merge
|
|
10
|
-
import merge from 'lodash.merge'
|
|
10
|
+
import merge from 'lodash.merge';
|
|
11
11
|
|
|
12
12
|
const Vue = createApp({
|
|
13
13
|
data() {
|
|
14
14
|
return {
|
|
15
15
|
vueApp,
|
|
16
16
|
themeConfig: useTheme()
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
},
|
|
19
19
|
created() {
|
|
20
|
-
this.themeConfig.themes = merge(this.themeConfig.themes, settings.themes)
|
|
20
|
+
this.themeConfig.themes = merge(this.themeConfig.themes, settings.themes);
|
|
21
21
|
},
|
|
22
22
|
render: function () {
|
|
23
23
|
return h(App, { page: this.$data.vueApp.page });
|
|
24
24
|
}
|
|
25
|
-
})
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Vue.use(vuetify);
|
|
29
|
+
|
|
30
|
+
import { GmapVuePlugin, components } from "@gmap-vue/v3";
|
|
31
|
+
Vue.use(GmapVuePlugin, {
|
|
32
|
+
load: {
|
|
33
|
+
key: import.meta.env.GMAPS_API_KEY,
|
|
34
|
+
libraries: "places"
|
|
35
|
+
}
|
|
36
|
+
});
|
|
26
37
|
|
|
38
|
+
const { MapLayer, Autocomplete, Marker } = components;
|
|
39
|
+
Vue.component('gmap-map', MapLayer);
|
|
40
|
+
Vue.component('gmap-marker', Marker);
|
|
41
|
+
Vue.component('gmap-autocomplete', Autocomplete);
|
|
27
42
|
|
|
28
|
-
Vue.use(vuetify)
|
|
29
43
|
|
|
30
|
-
// import * as VueGoogleMaps from "vue2-google-maps";
|
|
31
|
-
// Vue.use(VueGoogleMaps, {
|
|
32
|
-
// load: {
|
|
33
|
-
// key: process.env.GMAPS_API_KEY,
|
|
34
|
-
// libraries: "places"
|
|
35
|
-
// }
|
|
36
|
-
// });
|
|
37
44
|
|
|
38
45
|
import "./styles/test.scss";
|
|
39
46
|
import "./styles/test.sass";
|
|
40
47
|
|
|
41
48
|
|
|
42
|
-
import VueChartkick from 'vue-chartkick'
|
|
43
|
-
import 'chartkick/chart.js'
|
|
49
|
+
import VueChartkick from 'vue-chartkick';
|
|
50
|
+
import 'chartkick/chart.js';
|
|
44
51
|
|
|
45
|
-
Vue.use(VueChartkick)
|
|
52
|
+
Vue.use(VueChartkick);
|
|
46
53
|
|
|
47
54
|
// import VueAnalytics from 'vue-analytics'
|
|
48
55
|
// // TODO: Avoid hardcoding
|
|
@@ -153,10 +160,10 @@ Vue.use(updatableComponent);
|
|
|
153
160
|
import { vueApp } from "./store";
|
|
154
161
|
|
|
155
162
|
document.addEventListener("DOMContentLoaded", () => {
|
|
156
|
-
Vue.mount("#app")
|
|
163
|
+
Vue.mount("#app");
|
|
157
164
|
});
|
|
158
165
|
|
|
159
|
-
export { Vue, settings, vueApp }
|
|
166
|
+
export { Vue, settings, vueApp };
|
|
160
167
|
|
|
161
168
|
// The above code uses Vue without the compiler, which means you cannot
|
|
162
169
|
// use Vue to target elements in your existing html templates. You would
|
package/nav/dialog.vue
CHANGED
|
@@ -4,17 +4,18 @@
|
|
|
4
4
|
<v-card :style="hamburgerStyles" class="hamburger">
|
|
5
5
|
|
|
6
6
|
<panels-responsive v-if="header" :spec="header" />
|
|
7
|
-
<div class="dialog-title flex flex-wrap justify-between"
|
|
8
|
-
<
|
|
7
|
+
<div class="dialog-title d-flex flex-wrap justify-space-between align-center" style="width: 100%; height: 100%;">
|
|
8
|
+
<h3 v-if="title">
|
|
9
9
|
{{ title }}
|
|
10
|
-
</
|
|
11
|
-
</div>
|
|
10
|
+
</h3>
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
<v-btn size="small" v-if="!disableCloseButton" text icon @click="close" class="mr-3 mt-2 close-btn"
|
|
12
|
+
<v-btn size="small" v-if="!disableCloseButton" text icon @click="close" class="mr-4 mt-4 close-btn"
|
|
15
13
|
style="z-index: 999;" variant="flat">
|
|
16
14
|
<v-icon>close</v-icon>
|
|
17
15
|
</v-btn>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="dialogs-body relative">
|
|
18
19
|
<component :is="containerComponent" :spec="formSpec">
|
|
19
20
|
<div v-if="message" class="dialog-message">
|
|
20
21
|
<common-message :spec="{ message: message }" />
|
|
@@ -94,14 +95,13 @@ export default {
|
|
|
94
95
|
return null;
|
|
95
96
|
}
|
|
96
97
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// },
|
|
98
|
+
watch: {
|
|
99
|
+
model: function (val, oldVal) {
|
|
100
|
+
if (!val) {
|
|
101
|
+
this.stack.remove(this);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
105
|
methods: {
|
|
106
106
|
$mounted() {
|
|
107
107
|
window.addEventListener(
|
|
@@ -122,10 +122,10 @@ export default {
|
|
|
122
122
|
});
|
|
123
123
|
},
|
|
124
124
|
// TODO: This doesn't get called anymore after Vue3 upgrade.
|
|
125
|
-
$tearDown() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
},
|
|
125
|
+
// $tearDown() {
|
|
126
|
+
// console.debug("Dialog destroyed");
|
|
127
|
+
// this.stack.remove(this);
|
|
128
|
+
// },
|
|
129
129
|
close() {
|
|
130
130
|
if (Utils.http.proceedEvenWhenDirty()) {
|
|
131
131
|
this.model = false;
|
package/nav/drawer.vue
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
<div :class="cssClasses" :style="style">
|
|
6
6
|
<v-navigation-drawer v-model="state" app :dark="cssClasses.includes('dark') || null"
|
|
7
|
-
:permanent="cssClasses.includes('permanent')" :expand-on-hover="expandOnHover()"
|
|
8
|
-
@input="updateState">
|
|
7
|
+
:permanent="mdAndUp || cssClasses.includes('permanent')" :expand-on-hover="expandOnHover()"
|
|
8
|
+
v-model:mini-variant="shrunk" @input="updateState">
|
|
9
9
|
|
|
10
10
|
<template v-if="spec.header">
|
|
11
11
|
<panels-responsive :spec="spec.header" />
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
import NavDrawerButton from "./drawerButton.vue";
|
|
40
40
|
import NavDrawerLabel from "./drawerLabel.vue";
|
|
41
41
|
import bus from "../utils/eventBus";
|
|
42
|
+
import { useDisplay } from "vuetify";
|
|
42
43
|
|
|
43
44
|
export default {
|
|
44
45
|
components: {
|
|
@@ -51,6 +52,11 @@ export default {
|
|
|
51
52
|
trigger: { type: [Object, Date], default: null },
|
|
52
53
|
permanent: { type: Boolean, required: true },
|
|
53
54
|
},
|
|
55
|
+
setup() {
|
|
56
|
+
const { mdAndUp } = useDisplay();
|
|
57
|
+
|
|
58
|
+
return { mdAndUp };
|
|
59
|
+
},
|
|
54
60
|
data: function () {
|
|
55
61
|
return {
|
|
56
62
|
// Using null as the starting value for its v-model will initialize the drawer as closed on mobile and as open on desktop.
|
package/nav/snackbar.vue
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-snackbar v-model="show" :location="spec.location" :color="color" :timeout="spec.timeout"
|
|
2
|
+
<v-snackbar :vertical="vertical" v-model="show" :location="spec.location" :color="color" :timeout="spec.timeout"
|
|
3
|
+
:variant="variant">
|
|
3
4
|
<common-message :spec="spec" />
|
|
4
5
|
|
|
5
|
-
<v-
|
|
6
|
-
<
|
|
7
|
-
|
|
6
|
+
<template v-slot:actions v-if="spec.buttons">
|
|
7
|
+
<v-btn v-for="(button, index) in spec.buttons" :key="index" :color="color" :variant="variant"
|
|
8
|
+
@click="click(button, $event)">
|
|
9
|
+
<span :class="`text-${color}`">{{ button.text }}</span>
|
|
10
|
+
</v-btn>
|
|
11
|
+
</template>
|
|
8
12
|
|
|
9
|
-
<!-- <v-btn
|
|
10
|
-
flat
|
|
11
|
-
@click="close"
|
|
12
|
-
>
|
|
13
|
-
Close
|
|
14
|
-
</v-btn> -->
|
|
15
13
|
</v-snackbar>
|
|
16
14
|
</template>
|
|
17
15
|
|
|
18
16
|
<script>
|
|
19
|
-
import {
|
|
17
|
+
import { determineColor, determineVariant } from '../utils/constant';
|
|
20
18
|
|
|
21
19
|
export default {
|
|
22
20
|
props: {
|
|
@@ -25,28 +23,17 @@ export default {
|
|
|
25
23
|
data: function () {
|
|
26
24
|
return {
|
|
27
25
|
show: true,
|
|
28
|
-
color:
|
|
29
|
-
|
|
26
|
+
color: 'primary',
|
|
27
|
+
variant: 'outlined',
|
|
28
|
+
indicator: false,
|
|
29
|
+
vertical: false
|
|
30
30
|
};
|
|
31
31
|
},
|
|
32
|
-
// watch: {
|
|
33
|
-
// model: function (val, oldVal) {
|
|
34
|
-
// if (!val) {
|
|
35
|
-
// // this.$destroy();
|
|
36
|
-
// this.$.appContext.app.unmount()
|
|
37
|
-
// }
|
|
38
|
-
// },
|
|
39
|
-
// },
|
|
40
32
|
methods: {
|
|
41
33
|
$ready() {
|
|
42
|
-
|
|
43
|
-
this
|
|
44
|
-
|
|
45
|
-
if (val.includes(color)) {
|
|
46
|
-
vm.color = color;
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
});
|
|
34
|
+
this.variant = determineVariant(this.spec.styleClasses, 'outlined');
|
|
35
|
+
this.color = determineColor(this.spec.styleClasses, 'primary');
|
|
36
|
+
this.vertical = [this.spec.styleClasses].flat().includes('vertical');
|
|
50
37
|
},
|
|
51
38
|
click: function (spec) {
|
|
52
39
|
const onClick = spec["onClick"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glib-web",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@floating-ui/dom": "^1.2.8",
|
|
13
|
+
"@gmap-vue/v3": "^1.0.3",
|
|
13
14
|
"@rails/actioncable": "^6.0.0",
|
|
14
15
|
"@rails/activestorage": "^6.0.0-alpha",
|
|
15
16
|
"@vueup/vue-quill": "^1.2.0",
|
|
@@ -30,8 +31,6 @@
|
|
|
30
31
|
"vue": "3.2.47",
|
|
31
32
|
"vue-chartkick": "^1.1.0",
|
|
32
33
|
"vue-social-sharing": "^4.0.0-alpha4",
|
|
33
|
-
"vue2-gmap-custom-marker": "^6.1.1",
|
|
34
|
-
"vue2-google-maps": "^0.10.6",
|
|
35
34
|
"vuedraggable": "^4.1.0",
|
|
36
35
|
"vuetify": "^3.3.2"
|
|
37
36
|
},
|
|
@@ -42,4 +41,4 @@
|
|
|
42
41
|
"prettier": "^1.18.2",
|
|
43
42
|
"typescript": "^4.9.5"
|
|
44
43
|
}
|
|
45
|
-
}
|
|
44
|
+
}
|
package/utils/http.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Type from "./type";
|
|
2
2
|
import Action from "../action";
|
|
3
|
-
import { nextTick } from 'vue'
|
|
3
|
+
import { nextTick } from 'vue';
|
|
4
4
|
import { vueApp } from "../store";
|
|
5
5
|
|
|
6
6
|
let loading = false;
|
|
@@ -89,7 +89,7 @@ export default class {
|
|
|
89
89
|
this.forceComponentUpdate(() => {
|
|
90
90
|
Utils.history.resetScroll();
|
|
91
91
|
|
|
92
|
-
vueApp.page = data
|
|
92
|
+
vueApp.page = data;
|
|
93
93
|
|
|
94
94
|
Action.execute(properties["onOpen"], component);
|
|
95
95
|
});
|
|
@@ -143,7 +143,7 @@ export default class {
|
|
|
143
143
|
|
|
144
144
|
Utils.http.execute(data, "GET", component, (page, response) => {
|
|
145
145
|
Utils.http.forceComponentUpdate(() => {
|
|
146
|
-
vueApp.page = page
|
|
146
|
+
vueApp.page = page;
|
|
147
147
|
const redirectUrl = Utils.url.htmlUrl(response.url);
|
|
148
148
|
Utils.history.updatePage(vueApp.page, redirectUrl);
|
|
149
149
|
|
|
@@ -244,27 +244,27 @@ export default class {
|
|
|
244
244
|
setTimeout(() => {
|
|
245
245
|
if (loading) {
|
|
246
246
|
loading = false;
|
|
247
|
-
vueApp.indicator = true
|
|
247
|
+
vueApp.indicator = true;
|
|
248
248
|
}
|
|
249
249
|
}, 200);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
static _hideIndicator() {
|
|
253
253
|
loading = false;
|
|
254
|
-
vueApp.indicator = false
|
|
254
|
+
vueApp.indicator = false;
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
// See generic.js
|
|
258
258
|
static forceComponentUpdate(handler) {
|
|
259
259
|
// GLib.component.clearRegistry();
|
|
260
|
-
vueApp.isStale = true
|
|
261
|
-
vueApp.isFormSubmitted = false
|
|
262
|
-
vueApp.isFormDirty = false
|
|
260
|
+
vueApp.isStale = true;
|
|
261
|
+
vueApp.isFormSubmitted = false;
|
|
262
|
+
vueApp.isFormDirty = false;
|
|
263
263
|
handler();
|
|
264
264
|
|
|
265
265
|
// Queue the execution so the first isStale has time to resets state before handler gets executed
|
|
266
266
|
nextTick(() => {
|
|
267
|
-
vueApp.isStale = false
|
|
267
|
+
vueApp.isStale = false;
|
|
268
268
|
});
|
|
269
269
|
}
|
|
270
270
|
|
|
@@ -275,16 +275,16 @@ export default class {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
static clearDirtyState() {
|
|
278
|
-
vueApp.isFormDirty = false
|
|
278
|
+
vueApp.isFormDirty = false;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
static notifyFormSubmitted() {
|
|
282
|
-
vueApp.isFormSubmitted = true
|
|
282
|
+
vueApp.isFormSubmitted = true;
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
// `context` can be either window or dialog.
|
|
286
286
|
static proceedEvenWhenDirty() {
|
|
287
|
-
const dirtyContext = vueApp
|
|
287
|
+
const dirtyContext = vueApp;
|
|
288
288
|
// Don't prompt if this is a result of form submission
|
|
289
289
|
if (
|
|
290
290
|
dirtyContext.isFormDirty &&
|
package/utils/launch.js
CHANGED
|
@@ -3,7 +3,7 @@ import Dialog from "../nav/dialog.vue";
|
|
|
3
3
|
import Sheet from "../nav/sheet.vue";
|
|
4
4
|
import Snackbar from "../nav/snackbar.vue";
|
|
5
5
|
import Popover from "../components/popover.vue";
|
|
6
|
-
import { computePosition, flip, offset } from '@floating-ui/dom'
|
|
6
|
+
import { computePosition, flip, offset } from '@floating-ui/dom';
|
|
7
7
|
import bus from "./eventBus";
|
|
8
8
|
|
|
9
9
|
import { createApp, h } from "vue";
|
|
@@ -31,17 +31,17 @@ class LaunchPopover {
|
|
|
31
31
|
const instance = createApp(
|
|
32
32
|
{
|
|
33
33
|
render: function () {
|
|
34
|
-
return h(Popover, { spec: properties })
|
|
34
|
+
return h(Popover, { spec: properties });
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
)
|
|
38
|
-
Object.assign(instance._context, Vue._context)
|
|
37
|
+
);
|
|
38
|
+
Object.assign(instance._context, Vue._context);
|
|
39
39
|
|
|
40
40
|
if (component) {
|
|
41
|
-
const placeholder = document.createElement('div')
|
|
41
|
+
const placeholder = document.createElement('div');
|
|
42
42
|
|
|
43
43
|
// const pageBody = Utils.launch.dialog.closestBody(component) || Utils.history._pageBody;
|
|
44
|
-
const dialogBody = Utils.launch.dialog.closestBody(component)
|
|
44
|
+
const dialogBody = Utils.launch.dialog.closestBody(component);
|
|
45
45
|
if (dialogBody) {
|
|
46
46
|
// Put the popover on the dialog so that clicking it doesn't close a non-persistent dialog.
|
|
47
47
|
dialogBody.appendChild(placeholder);
|
|
@@ -53,11 +53,11 @@ class LaunchPopover {
|
|
|
53
53
|
|
|
54
54
|
instance.mount(placeholder);
|
|
55
55
|
|
|
56
|
-
const reference = component.$el instanceof HTMLElement ? component.$el : component.$el.nextElementSibling
|
|
56
|
+
const reference = component.$el instanceof HTMLElement ? component.$el : component.$el.nextElementSibling;
|
|
57
57
|
|
|
58
|
-
const floating = placeholder
|
|
59
|
-
const placement = properties.placement || 'right'
|
|
60
|
-
const offsetSize = properties.offset || 8
|
|
58
|
+
const floating = placeholder;
|
|
59
|
+
const placement = properties.placement || 'right';
|
|
60
|
+
const offsetSize = properties.offset || 8;
|
|
61
61
|
|
|
62
62
|
// These properties need to be set to get an accurate calculation. See https://floating-ui.com/docs/computeposition
|
|
63
63
|
Object.assign(placeholder.style, { position: 'absolute', display: 'block', top: 0, left: 0 });
|
|
@@ -65,7 +65,7 @@ class LaunchPopover {
|
|
|
65
65
|
computePosition(reference, floating, { placement: placement, middleware: [flip(), offset(offsetSize)] })
|
|
66
66
|
.then(({ x, y }) => {
|
|
67
67
|
Object.assign(placeholder.style, { top: `${y}px`, left: `${x}px`, zIndex: 1200 });
|
|
68
|
-
})
|
|
68
|
+
});
|
|
69
69
|
} else {
|
|
70
70
|
console.error("A popover has to be displayed in a component");
|
|
71
71
|
}
|
|
@@ -74,13 +74,13 @@ class LaunchPopover {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
static close(properties) {
|
|
77
|
-
bus.$emit(`popover/close-${properties.key}`, properties)
|
|
77
|
+
bus.$emit(`popover/close-${properties.key}`, properties);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
class LaunchDialog {
|
|
82
82
|
static closestBody(component) {
|
|
83
|
-
const element = component.$el instanceof HTMLElement ? component.$el : component.$el.nextElementSibling
|
|
83
|
+
const element = component.$el instanceof HTMLElement ? component.$el : component.$el.nextElementSibling;
|
|
84
84
|
const dialog = element.closest(".v-dialog");
|
|
85
85
|
if (dialog) {
|
|
86
86
|
return dialog.querySelector(".dialogs-body");
|
|
@@ -110,20 +110,20 @@ class LaunchDialog {
|
|
|
110
110
|
const props = {
|
|
111
111
|
spec: properties,
|
|
112
112
|
stack: this.stack
|
|
113
|
-
}
|
|
113
|
+
};
|
|
114
114
|
|
|
115
115
|
const instance = createApp(
|
|
116
116
|
{
|
|
117
117
|
render: function () {
|
|
118
|
-
return h(Dialog, props)
|
|
118
|
+
return h(Dialog, props);
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
)
|
|
122
|
-
Object.assign(instance._context, Vue._context)
|
|
121
|
+
);
|
|
122
|
+
Object.assign(instance._context, Vue._context);
|
|
123
123
|
|
|
124
124
|
if (component) {
|
|
125
|
-
const placeholder = document.createElement('div')
|
|
126
|
-
document.body.appendChild(placeholder)
|
|
125
|
+
const placeholder = document.createElement('div');
|
|
126
|
+
document.body.appendChild(placeholder);
|
|
127
127
|
instance.mount(placeholder);
|
|
128
128
|
} else {
|
|
129
129
|
console.error("A dialog has to be displayed in a component");
|
|
@@ -160,6 +160,7 @@ class LaunchDialog {
|
|
|
160
160
|
message: message
|
|
161
161
|
};
|
|
162
162
|
const spec = Object.assign({}, properties, {
|
|
163
|
+
disableCloseButton: true,
|
|
163
164
|
buttons: [
|
|
164
165
|
{
|
|
165
166
|
text: "OK",
|
|
@@ -176,15 +177,15 @@ class LaunchSheet {
|
|
|
176
177
|
const instance = createApp(
|
|
177
178
|
{
|
|
178
179
|
render: function () {
|
|
179
|
-
return h(Sheet, { spec: properties })
|
|
180
|
+
return h(Sheet, { spec: properties });
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
|
-
)
|
|
183
|
-
Object.assign(instance._context, Vue._context)
|
|
183
|
+
);
|
|
184
|
+
Object.assign(instance._context, Vue._context);
|
|
184
185
|
|
|
185
186
|
if (component) {
|
|
186
|
-
const placeholder = document.createElement('div')
|
|
187
|
-
document.body.appendChild(placeholder)
|
|
187
|
+
const placeholder = document.createElement('div');
|
|
188
|
+
document.body.appendChild(placeholder);
|
|
188
189
|
instance.mount(placeholder);
|
|
189
190
|
} else {
|
|
190
191
|
console.error("A sheet has to be displayed in a component");
|
|
@@ -216,15 +217,15 @@ class LaunchSnackbar {
|
|
|
216
217
|
const instance = createApp(
|
|
217
218
|
{
|
|
218
219
|
render: function () {
|
|
219
|
-
return h(Snackbar, { spec: properties })
|
|
220
|
+
return h(Snackbar, { spec: properties });
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
|
-
)
|
|
223
|
-
Object.assign(instance._context, Vue._context)
|
|
223
|
+
);
|
|
224
|
+
Object.assign(instance._context, Vue._context);
|
|
224
225
|
|
|
225
226
|
if (component) {
|
|
226
|
-
const placeholder = document.createElement('div')
|
|
227
|
-
document.body.appendChild(placeholder)
|
|
227
|
+
const placeholder = document.createElement('div');
|
|
228
|
+
document.body.appendChild(placeholder);
|
|
228
229
|
instance.mount(placeholder);
|
|
229
230
|
instance.show = true;
|
|
230
231
|
} else {
|