Begin converting to composition api
This commit is contained in:
parent
b957d44aed
commit
a071e6b21e
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
# Ignore the default SQLite database.
|
# Ignore the default SQLite database.
|
||||||
/db/*.sqlite3
|
/db/*.sqlite3
|
||||||
/db/*.sqlite3-journal
|
/db/*.sqlite3*
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log/*
|
/log/*
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<component v-if="!hasError" :is="Component" />
|
<component v-if="!hasError" :is="Component" />
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h1>Error!</h1>
|
<h1>Error!</h1>
|
||||||
<p>{{ error }}</p>
|
<p>{{ appConfig.error }}</p>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
@ -18,70 +18,56 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
||||||
import { mapState } from "pinia";
|
|
||||||
import { useGlobalTweenGroup } from "../lib/useGlobalTweenGroup";
|
import { useGlobalTweenGroup } from "../lib/useGlobalTweenGroup";
|
||||||
import { useAppConfigStore } from "../stores/appConfig";
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
import { useCheckAuthentication } from "../lib/useCheckAuthentication";
|
||||||
|
|
||||||
const globalTweenGroup = useGlobalTweenGroup();
|
const globalTweenGroup = useGlobalTweenGroup();
|
||||||
|
let animationLoop = true;
|
||||||
|
|
||||||
export default {
|
const appConfig = useAppConfigStore();
|
||||||
data() {
|
const hasError = computed(() => appConfig.error !== null);
|
||||||
return {
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const { loadResource } = useLoadResource();
|
||||||
...mapState(useAppConfigStore, {
|
const { checkAuthentication } = useCheckAuthentication(loadResource);
|
||||||
hasError: store => store.error !== null,
|
|
||||||
error: store => store.error,
|
|
||||||
authChecked: store => store.authChecked,
|
|
||||||
initialLoad: store => store.initialLoad
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
watch(
|
||||||
isLoading(val) {
|
() => appConfig.initialLoad,
|
||||||
|
(val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
// this.$Progress.start();
|
nextTick(() => document.body.classList.remove("loading"));
|
||||||
} else {
|
|
||||||
// this.$Progress.finish();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
initialLoad(val) {
|
watch(
|
||||||
if (val) {
|
() => appConfig.isLoading,
|
||||||
this.$nextTick(() => {
|
(val) => {
|
||||||
document.body.classList.remove("loading");
|
// Update Progress
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
)
|
||||||
|
|
||||||
created() {
|
onMounted(() => {
|
||||||
// Setup global animation loop
|
// Setup global animation loop
|
||||||
function animate(time) {
|
function animate() {
|
||||||
globalTweenGroup.update(time);
|
if (animationLoop) {
|
||||||
|
globalTweenGroup.update();
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
}
|
}
|
||||||
animate();
|
|
||||||
|
|
||||||
if (this.user === null && this.authChecked === false) {
|
|
||||||
this.checkAuthentication();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
if (this.initialLoad) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
document.body.classList.remove("loading");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
}
|
}
|
||||||
}
|
animate();
|
||||||
|
|
||||||
|
if (appConfig.user === null && appConfig.authChecked === false) {
|
||||||
|
checkAuthentication();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
animationLoop = false;
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
@ -27,195 +27,179 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
export default {
|
const emit = defineEmits(["update:modelValue", "inputClick", "optionSelected"]);
|
||||||
props: {
|
|
||||||
modelValue: String,
|
|
||||||
id: String,
|
|
||||||
placeholder: String,
|
|
||||||
name: String,
|
|
||||||
inputClass: {
|
|
||||||
type: [String, Object, Array],
|
|
||||||
required: false,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
minLength: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
debounce: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default: 250
|
|
||||||
},
|
|
||||||
|
|
||||||
valueAttribute: String,
|
const props = defineProps({
|
||||||
labelAttribute: String,
|
modelValue: String,
|
||||||
keyAttribute: String,
|
id: String,
|
||||||
|
placeholder: String,
|
||||||
onGetOptions: Function,
|
name: String,
|
||||||
searchOptions: Array
|
inputClass: {
|
||||||
|
type: [String, Object, Array],
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
minLength: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
debounce: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 250
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
valueAttribute: String,
|
||||||
return {
|
labelAttribute: String,
|
||||||
options: [],
|
keyAttribute: String,
|
||||||
rawValue: "",
|
|
||||||
isListOpen: false,
|
|
||||||
activeListIndex: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
onGetOptions: Function,
|
||||||
this.rawValue = this.modelValue;
|
searchOptions: Array
|
||||||
|
});
|
||||||
|
|
||||||
},
|
const options = ref([]);
|
||||||
|
const rawValue = ref("");
|
||||||
|
const isListOpen = ref(false);
|
||||||
|
const activeListIndex = ref(0);
|
||||||
|
|
||||||
watch: {
|
const finalInputClass = computed(() => {
|
||||||
modelValue(newValue) {
|
let cls = ['input'];
|
||||||
this.rawValue = newValue;
|
if (props.inputClass === null) {
|
||||||
}
|
return cls;
|
||||||
},
|
} else if (Array.isArray(props.inputClass)) {
|
||||||
|
return cls.concat(props.inputClass);
|
||||||
|
} else {
|
||||||
|
cls.push(props.inputClass);
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
computed: {
|
watch(
|
||||||
finalInputClass() {
|
() => props.modelValue,
|
||||||
let cls = ['input'];
|
(newValue) => { rawValue.value = newValue; },
|
||||||
if (this.inputClass === null) {
|
{ immediate: true }
|
||||||
return cls;
|
);
|
||||||
} else if (Array.isArray(this.inputClass)) {
|
|
||||||
return cls.concat(this.inputClass);
|
|
||||||
} else {
|
|
||||||
cls.push(this.inputClass);
|
|
||||||
return cls;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
debouncedUpdateOptions() {
|
function optionClass(idx) {
|
||||||
return debounce(this.updateOptions, this.debounce);
|
return activeListIndex.value === idx ? 'option active' : 'option';
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
function optionClick(opt) {
|
||||||
optionClass(idx) {
|
selectOption(opt);
|
||||||
return this.activeListIndex === idx ? 'option active' : 'option';
|
}
|
||||||
},
|
|
||||||
|
|
||||||
optionClick(opt) {
|
function optionKey(opt) {
|
||||||
this.selectOption(opt);
|
if (props.keyAttribute) {
|
||||||
},
|
return opt[props.keyAttribute]
|
||||||
|
} else if (props.valueAttribute) {
|
||||||
|
return opt[props.valueAttribute];
|
||||||
|
} else {
|
||||||
|
return opt.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
optionKey(opt) {
|
function optionValue(opt) {
|
||||||
if (this.keyAttribute) {
|
if (props.valueAttribute) {
|
||||||
return opt[this.keyAttribute]
|
return opt[props.valueAttribute];
|
||||||
} else if (this.valueAttribute) {
|
} else {
|
||||||
return opt[this.valueAttribute];
|
return opt.toString();
|
||||||
} else {
|
}
|
||||||
return opt.toString();
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
optionValue(opt) {
|
function optionLabel(opt) {
|
||||||
if (this.valueAttribute) {
|
if (props.labelAttribute) {
|
||||||
return opt[this.valueAttribute];
|
return opt[props.labelAttribute];
|
||||||
} else {
|
} else {
|
||||||
return opt.toString();
|
return null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
optionLabel(opt) {
|
function optionMousemove(idx) {
|
||||||
if (this.labelAttribute) {
|
activeListIndex.value = idx;
|
||||||
return opt[this.labelAttribute];
|
}
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
optionMousemove(idx) {
|
function clickHandler(evt) {
|
||||||
this.activeListIndex = idx;
|
emit('inputClick', evt);
|
||||||
},
|
}
|
||||||
|
|
||||||
clickHandler(evt) {
|
function blurHandler(evt) {
|
||||||
this.$emit("inputClick", evt);
|
// blur fires before click. If the blur was fired because the user clicked a list item, immediately hiding the list here
|
||||||
},
|
// would prevent the click event from firing
|
||||||
|
setTimeout(() => {
|
||||||
|
isListOpen.value = false;
|
||||||
|
},250);
|
||||||
|
}
|
||||||
|
|
||||||
blurHandler(evt) {
|
function inputHandler(evt) {
|
||||||
// blur fires before click. If the blur was fired because the user clicked a list item, immediately hiding the list here
|
const newValue = evt.target.value;
|
||||||
// would prevent the click event from firing
|
|
||||||
setTimeout(() => {
|
|
||||||
this.isListOpen = false;
|
|
||||||
},250);
|
|
||||||
},
|
|
||||||
|
|
||||||
inputHandler(evt) {
|
if (rawValue.value !== newValue) {
|
||||||
const newValue = evt.target.value;
|
|
||||||
|
|
||||||
if (this.rawValue !== newValue) {
|
rawValue.value = newValue;
|
||||||
|
|
||||||
this.rawValue = newValue;
|
emit("update:modelValue", newValue);
|
||||||
|
|
||||||
this.$emit("update:modelValue", newValue);
|
if (newValue.length >= Math.max(1, props.minLength)) {
|
||||||
|
this.updateOptions(newValue);
|
||||||
if (newValue.length >= Math.max(1, this.minLength)) {
|
} else {
|
||||||
this.debouncedUpdateOptions(newValue);
|
isListOpen.value = false;
|
||||||
} else {
|
|
||||||
this.isListOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
keydownHandler(evt) {
|
|
||||||
if (this.isListOpen === false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (evt.key) {
|
|
||||||
case "ArrowUp":
|
|
||||||
evt.preventDefault();
|
|
||||||
this.activeListIndex = Math.max(0, this.activeListIndex - 1);
|
|
||||||
break;
|
|
||||||
case "ArrowDown":
|
|
||||||
evt.preventDefault();
|
|
||||||
this.activeListIndex = Math.min(this.options.length - 1, this.activeListIndex + 1);
|
|
||||||
break;
|
|
||||||
case "Enter":
|
|
||||||
evt.preventDefault();
|
|
||||||
this.selectOption(this.options[this.activeListIndex]);
|
|
||||||
break;
|
|
||||||
case "Escape":
|
|
||||||
evt.preventDefault();
|
|
||||||
this.isListOpen = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
selectOption(opt) {
|
|
||||||
this.rawValue = this.optionValue(opt);
|
|
||||||
this.$emit("update:modelValue", this.rawValue);
|
|
||||||
this.$emit("optionSelected", opt);
|
|
||||||
this.isListOpen = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateOptions(value) {
|
|
||||||
let p = null;
|
|
||||||
if (this.searchOptions) {
|
|
||||||
const reg = new RegExp("^" + value, "i");
|
|
||||||
const matcher = o => reg.test(this.optionValue(o));
|
|
||||||
p = Promise.resolve(this.searchOptions.filter(matcher));
|
|
||||||
} else {
|
|
||||||
p = this.onGetOptions(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.then(opts => {
|
|
||||||
this.options = opts;
|
|
||||||
this.isListOpen = opts.length > 0;
|
|
||||||
this.activeListIndex = 0;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function keydownHandler(evt) {
|
||||||
|
if (isListOpen.value === false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (evt.key) {
|
||||||
|
case "ArrowUp":
|
||||||
|
evt.preventDefault();
|
||||||
|
activeListIndex.value = Math.max(0, activeListIndex.value - 1);
|
||||||
|
break;
|
||||||
|
case "ArrowDown":
|
||||||
|
evt.preventDefault();
|
||||||
|
activeListIndex.value = Math.min(options.value.length - 1, activeListIndex.value + 1);
|
||||||
|
break;
|
||||||
|
case "Enter":
|
||||||
|
evt.preventDefault();
|
||||||
|
selectOption(options.value[activeListIndex.value]);
|
||||||
|
break;
|
||||||
|
case "Escape":
|
||||||
|
evt.preventDefault();
|
||||||
|
isListOpen.value = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectOption(opt) {
|
||||||
|
rawValue.value = optionValue(opt);
|
||||||
|
emit("update:modelValue", rawValue.value);
|
||||||
|
emit("optionSelected", opt);
|
||||||
|
isListOpen.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateOptions = debounce(function(value) {
|
||||||
|
let p = null;
|
||||||
|
if (props.searchOptions) {
|
||||||
|
const reg = new RegExp("^" + value, "i");
|
||||||
|
const matcher = o => reg.test(optionValue(o));
|
||||||
|
p = Promise.resolve(props.searchOptions.filter(matcher));
|
||||||
|
} else {
|
||||||
|
p = props.onGetOptions(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.then(opts => {
|
||||||
|
options.value = opts;
|
||||||
|
isListOpen.value = opts.length > 0;
|
||||||
|
activeListIndex.value = 0;
|
||||||
|
})
|
||||||
|
}, props.debounce);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -7,41 +7,37 @@
|
|||||||
</app-modal>
|
</app-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
cancel: {
|
||||||
cancel: {
|
type: Function,
|
||||||
type: Function,
|
required: true
|
||||||
required: true
|
},
|
||||||
},
|
|
||||||
|
|
||||||
confirm: {
|
confirm: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
|
||||||
message: {
|
message: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: 'Are you sure?'
|
default: 'Are you sure?'
|
||||||
},
|
},
|
||||||
|
|
||||||
open: {
|
open: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true
|
required: true
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
runConfirm() {
|
|
||||||
this.confirm();
|
|
||||||
},
|
|
||||||
|
|
||||||
runCancel() {
|
|
||||||
this.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function runConfirm() {
|
||||||
|
props.confirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCancel() {
|
||||||
|
props.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
@ -2,37 +2,33 @@
|
|||||||
<app-text-field :value="stringValue" @input="input" :label="label" type="date"></app-text-field>
|
<app-text-field :value="stringValue" @input="input" :label="label" type="date"></app-text-field>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed } from "vue";
|
||||||
import DateTimeUtils from "../lib/DateTimeUtils";
|
import DateTimeUtils from "../lib/DateTimeUtils";
|
||||||
|
|
||||||
export default {
|
const emit = defineEmits(["update:modelValue"]);
|
||||||
props: {
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
required: false,
|
required: false,
|
||||||
type: [Date, String]
|
type: [Date, String]
|
||||||
},
|
|
||||||
|
|
||||||
label: {
|
|
||||||
required: false,
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
label: {
|
||||||
stringValue() {
|
required: false,
|
||||||
const d = DateTimeUtils.toDate(this.modelValue);
|
type: String,
|
||||||
return DateTimeUtils.formatDateForEdit(d);
|
default: null
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
input(val) {
|
|
||||||
let d = DateTimeUtils.toDate(val + " 00:00");
|
|
||||||
this.$emit("update:modelValue", d);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const stringValue = computed(() => {
|
||||||
|
const d = DateTimeUtils.toDate(props.modelValue);
|
||||||
|
return DateTimeUtils.formatDateForEdit(d);
|
||||||
|
});
|
||||||
|
|
||||||
|
function input(val) {
|
||||||
|
let d = DateTimeUtils.toDate(val + " 00:00");
|
||||||
|
emit("update:modelValue", d);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -5,57 +5,49 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed } from "vue";
|
||||||
import DateTimeUtils from "../lib/DateTimeUtils";
|
import DateTimeUtils from "../lib/DateTimeUtils";
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
dateTime: {
|
||||||
dateTime: {
|
required: true,
|
||||||
required: true,
|
type: [Date, String]
|
||||||
type: [Date, String]
|
|
||||||
},
|
|
||||||
|
|
||||||
showDate: {
|
|
||||||
required: false,
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
|
|
||||||
showTime: {
|
|
||||||
required: false,
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
|
|
||||||
useInput: {
|
|
||||||
required: false,
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
showDate: {
|
||||||
dateObj() {
|
required: false,
|
||||||
return DateTimeUtils.toDate(this.dateTime);
|
type: Boolean,
|
||||||
},
|
default: true
|
||||||
|
},
|
||||||
|
|
||||||
friendlyString() {
|
showTime: {
|
||||||
const parts = [];
|
required: false,
|
||||||
if (this.showDate) {
|
type: Boolean,
|
||||||
parts.push(DateTimeUtils.formatDate(this.dateObj));
|
default: true
|
||||||
}
|
},
|
||||||
if (this.showTime) {
|
|
||||||
parts.push(DateTimeUtils.formatTime(this.dateObj, true));
|
|
||||||
}
|
|
||||||
return parts.join(" ");
|
|
||||||
},
|
|
||||||
|
|
||||||
fullString() {
|
useInput: {
|
||||||
return DateTimeUtils.formatTimestamp(this.dateObj);
|
required: false,
|
||||||
}
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
const dateObj = computed(() => DateTimeUtils.toDate(props.dateTime));
|
||||||
|
const fullString = computed(() => DateTimeUtils.formatTimestamp(dateObj.value));
|
||||||
|
|
||||||
|
const friendlyString = computed(() => {
|
||||||
|
const parts = [];
|
||||||
|
if (props.showDate) {
|
||||||
|
parts.push(DateTimeUtils.formatDate(dateObj.value));
|
||||||
|
}
|
||||||
|
if (props.showTime) {
|
||||||
|
parts.push(DateTimeUtils.formatTime(dateObj.value, true));
|
||||||
|
}
|
||||||
|
return parts.join(" ");
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dropdown" :class="{'is-active': open, 'is-hoverable': hover}">
|
<div ref="dropdown" class="dropdown" :class="{'is-active': open, 'is-hoverable': hover}">
|
||||||
<div class="dropdown-trigger">
|
<div class="dropdown-trigger">
|
||||||
<slot name="button">
|
<slot name="button">
|
||||||
<button type="button" class="button" :class="buttonClass" @click="toggle">
|
<button type="button" class="button" :class="buttonClass" @click="toggle">
|
||||||
@ -19,67 +19,63 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
import { useTemplateRef } from "vue";
|
||||||
props: {
|
import { onClickOutside } from '@vueuse/core'
|
||||||
open: {
|
|
||||||
required: false,
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
|
|
||||||
hover: {
|
const emit = defineEmits(["close", "open"]);
|
||||||
required: false,
|
const props = defineProps({
|
||||||
type: Boolean,
|
open: {
|
||||||
default: false
|
required: false,
|
||||||
},
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
|
||||||
label: {
|
hover: {
|
||||||
required: false,
|
required: false,
|
||||||
type: String,
|
type: Boolean,
|
||||||
default: 'Select'
|
default: false
|
||||||
},
|
},
|
||||||
|
|
||||||
buttonClass: {
|
label: {
|
||||||
required: false,
|
required: false,
|
||||||
default: ""
|
type: String,
|
||||||
}
|
default: 'Select'
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
buttonClass: {
|
||||||
toggle() {
|
required: false,
|
||||||
if (this.open) {
|
default: ""
|
||||||
this.triggerClose();
|
}
|
||||||
} else {
|
});
|
||||||
this.triggerOpen();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
triggerOpen() {
|
const dropdownElement = useTemplateRef("dropdown");
|
||||||
this.$emit("open");
|
|
||||||
},
|
|
||||||
|
|
||||||
triggerClose() {
|
onClickOutside(dropdownElement, event => handleOutsideClick(event))
|
||||||
this.$emit("close");
|
|
||||||
},
|
|
||||||
|
|
||||||
handleOutsideClick(evt) {
|
function toggle() {
|
||||||
if (this.open) {
|
if (props.open) {
|
||||||
if (!this.$el.contains(evt.target)) {
|
triggerClose();
|
||||||
this.triggerClose();
|
} else {
|
||||||
}
|
triggerOpen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
function triggerOpen() {
|
||||||
document.addEventListener("click", this.handleOutsideClick);
|
emit("open");
|
||||||
},
|
}
|
||||||
|
|
||||||
beforeDestroy() {
|
function triggerClose() {
|
||||||
document.removeEventListener("click", this.handleOutsideClick);
|
emit("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOutsideClick(evt) {
|
||||||
|
if (props.open) {
|
||||||
|
if (!dropdownElement.value.contains(evt.target)) {
|
||||||
|
triggerClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
@ -9,95 +9,87 @@
|
|||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
import TWEEN from '@tweenjs/tween.js';
|
import TWEEN from '@tweenjs/tween.js';
|
||||||
import { useGlobalTweenGroup } from "../lib/useGlobalTweenGroup";
|
import { useGlobalTweenGroup } from "../lib/useGlobalTweenGroup";
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
expandTime: {
|
||||||
expandTime: {
|
type: Number,
|
||||||
type: Number,
|
default: 250
|
||||||
default: 250
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
animation: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
cancel () {
|
|
||||||
if (this.animation) {
|
|
||||||
this.animation.stop();
|
|
||||||
this.animation = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
enter(element, done) {
|
|
||||||
const width = parseInt(getComputedStyle(element).width);
|
|
||||||
const paddingTop = parseInt(getComputedStyle(element).paddingTop);
|
|
||||||
const paddingBottom = parseInt(getComputedStyle(element).paddingBottom);
|
|
||||||
|
|
||||||
element.style.width = width;
|
|
||||||
element.style.position = 'absolute';
|
|
||||||
element.style.visibility = 'hidden';
|
|
||||||
element.style.height = 'auto';
|
|
||||||
|
|
||||||
const height = parseInt(getComputedStyle(element).height);
|
|
||||||
|
|
||||||
element.style.width = null;
|
|
||||||
element.style.position = null;
|
|
||||||
element.style.visibility = null;
|
|
||||||
element.style.overflow = 'hidden';
|
|
||||||
element.style.height = 0;
|
|
||||||
|
|
||||||
this.animation = new TWEEN.Tween({height: 0, paddingTop: 0, paddingBottom: 0})
|
|
||||||
.to({height: height, paddingTop: paddingTop, paddingBottom: paddingBottom}, this.expandTime)
|
|
||||||
.onUpdate(obj => {
|
|
||||||
element.style.height = obj.height + "px";
|
|
||||||
element.style.paddingBottom = obj.paddingBottom + "px";
|
|
||||||
element.style.paddingTop = obj.paddingTop + "px";
|
|
||||||
})
|
|
||||||
.onComplete(() => {
|
|
||||||
this.animation = null;
|
|
||||||
element.removeAttribute('style');
|
|
||||||
element.style.opacity = 0.99;
|
|
||||||
setTimeout(() => {
|
|
||||||
// Fixes odd drawing bug in Chrome
|
|
||||||
element.style.opacity = 1.0;
|
|
||||||
}, 1000);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.group(useGlobalTweenGroup())
|
|
||||||
.start();
|
|
||||||
},
|
|
||||||
|
|
||||||
leave(element, done) {
|
|
||||||
const height = parseInt(getComputedStyle(element).height);
|
|
||||||
const paddingTop = parseInt(getComputedStyle(element).paddingTop);
|
|
||||||
const paddingBottom = parseInt(getComputedStyle(element).paddingBottom);
|
|
||||||
|
|
||||||
element.style.overflow = 'hidden';
|
|
||||||
|
|
||||||
this.animation = new TWEEN.Tween({height: height, paddingTop: paddingTop, paddingBottom: paddingBottom})
|
|
||||||
.to({height: 0, paddingTop: 0, paddingBottom: 0}, this.expandTime)
|
|
||||||
.onUpdate(obj => {
|
|
||||||
element.style.height = obj.height + "px";
|
|
||||||
element.style.paddingBottom = obj.paddingBottom + "px";
|
|
||||||
element.style.paddingTop = obj.paddingTop + "px";
|
|
||||||
})
|
|
||||||
.onComplete(() => {
|
|
||||||
this.animation = null;
|
|
||||||
element.removeAttribute('style');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.group(useGlobalTweenGroup())
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let animation = null;
|
||||||
|
|
||||||
|
function cancel () {
|
||||||
|
if (animation) {
|
||||||
|
animation.stop();
|
||||||
|
animation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enter(element, done) {
|
||||||
|
const width = parseInt(getComputedStyle(element).width);
|
||||||
|
const paddingTop = parseInt(getComputedStyle(element).paddingTop);
|
||||||
|
const paddingBottom = parseInt(getComputedStyle(element).paddingBottom);
|
||||||
|
|
||||||
|
element.style.width = width;
|
||||||
|
element.style.position = 'absolute';
|
||||||
|
element.style.visibility = 'hidden';
|
||||||
|
element.style.height = 'auto';
|
||||||
|
|
||||||
|
const height = parseInt(getComputedStyle(element).height);
|
||||||
|
|
||||||
|
element.style.width = null;
|
||||||
|
element.style.position = null;
|
||||||
|
element.style.visibility = null;
|
||||||
|
element.style.overflow = 'hidden';
|
||||||
|
element.style.height = 0;
|
||||||
|
|
||||||
|
animation = new TWEEN.Tween({height: 0, paddingTop: 0, paddingBottom: 0})
|
||||||
|
.to({height: height, paddingTop: paddingTop, paddingBottom: paddingBottom}, props.expandTime)
|
||||||
|
.onUpdate(obj => {
|
||||||
|
element.style.height = obj.height + "px";
|
||||||
|
element.style.paddingBottom = obj.paddingBottom + "px";
|
||||||
|
element.style.paddingTop = obj.paddingTop + "px";
|
||||||
|
})
|
||||||
|
.onComplete(() => {
|
||||||
|
animation = null;
|
||||||
|
element.removeAttribute('style');
|
||||||
|
element.style.opacity = 0.99;
|
||||||
|
setTimeout(() => {
|
||||||
|
// Fixes odd drawing bug in Chrome
|
||||||
|
element.style.opacity = 1.0;
|
||||||
|
}, 1000);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.group(useGlobalTweenGroup())
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function leave(element, done) {
|
||||||
|
const height = parseInt(getComputedStyle(element).height);
|
||||||
|
const paddingTop = parseInt(getComputedStyle(element).paddingTop);
|
||||||
|
const paddingBottom = parseInt(getComputedStyle(element).paddingBottom);
|
||||||
|
|
||||||
|
element.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
animation = new TWEEN.Tween({height: height, paddingTop: paddingTop, paddingBottom: paddingBottom})
|
||||||
|
.to({height: 0, paddingTop: 0, paddingBottom: 0}, props.expandTime)
|
||||||
|
.onUpdate(obj => {
|
||||||
|
element.style.height = obj.height + "px";
|
||||||
|
element.style.paddingBottom = obj.paddingBottom + "px";
|
||||||
|
element.style.paddingTop = obj.paddingTop + "px";
|
||||||
|
})
|
||||||
|
.onComplete(() => {
|
||||||
|
animation = null;
|
||||||
|
element.removeAttribute('style');
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.group(useGlobalTweenGroup())
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
class IconData {
|
class IconData {
|
||||||
constructor(iconicIcon, dataAttributes) {
|
constructor(iconicIcon, dataAttributes) {
|
||||||
this.iconicIcon = iconicIcon;
|
this.iconicIcon = iconicIcon;
|
||||||
@ -49,6 +51,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
emits: ["click"],
|
||||||
props: {
|
props: {
|
||||||
icon: {
|
icon: {
|
||||||
validator: (i) => iconMap[i] !== undefined
|
validator: (i) => iconMap[i] !== undefined
|
||||||
@ -66,38 +69,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup(props) {
|
||||||
|
const iconData = computed(() => iconMap[props.icon]);
|
||||||
|
const sizeData = computed(() => sizeMap[props.size]);
|
||||||
|
const iconClasses = computed(() => [sizeData.value.bulmaIconClass, sizeData.value.customIconClass]);
|
||||||
|
const iconicSize = computed(() => sizeData.value.iconicSize);
|
||||||
|
const iconicIcon = computed(() => iconData.value.iconicIcon);
|
||||||
|
const iconicAttributes = computed(() => iconData.value.dataAttributes);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
injectedSvg: null
|
iconClasses,
|
||||||
}
|
iconData,
|
||||||
},
|
sizeData,
|
||||||
|
iconicAttributes,
|
||||||
computed: {
|
iconicIcon,
|
||||||
iconData() {
|
iconicSize
|
||||||
return iconMap[this.icon];
|
|
||||||
},
|
|
||||||
|
|
||||||
sizeData() {
|
|
||||||
return sizeMap[this.size];
|
|
||||||
},
|
|
||||||
|
|
||||||
iconClasses() {
|
|
||||||
return [
|
|
||||||
this.sizeData.bulmaIconClass,
|
|
||||||
this.sizeData.customIconClass
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
iconicSize() {
|
|
||||||
return this.sizeData.iconicSize;
|
|
||||||
},
|
|
||||||
|
|
||||||
iconicIcon() {
|
|
||||||
return this.iconData.iconicIcon;
|
|
||||||
},
|
|
||||||
|
|
||||||
iconicAttributes() {
|
|
||||||
return this.iconData.dataAttributes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg v-bind="svgAttributes" v-html="svgContent" :class="calculatedClasses"></svg>
|
<svg ref="svg" v-bind="svgAttributes" v-html="svgContent" :class="calculatedClasses"></svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import { computed, nextTick, useTemplateRef, watch } from "vue";
|
||||||
|
|
||||||
import Caret from "../iconic/svg/smart/caret";
|
import Caret from "../iconic/svg/smart/caret";
|
||||||
import Check from "../iconic/svg/smart/check";
|
import Check from "../iconic/svg/smart/check";
|
||||||
import CircleCheck from "../iconic/svg/smart/circle-check";
|
import CircleCheck from "../iconic/svg/smart/circle-check";
|
||||||
@ -68,29 +70,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup(props) {
|
||||||
return {
|
const svgElement = useTemplateRef("svg");
|
||||||
calculatedClasses: []
|
const svgData = computed(() => iconMap[props.icon]);
|
||||||
}
|
const svgAttributes = computed(() => svgData.value.attributes);
|
||||||
},
|
const svgName = computed(() => svgAttributes.value['data-icon']);
|
||||||
|
const svgContent = computed(() => {
|
||||||
|
let content = String(svgData.value.content);
|
||||||
|
|
||||||
computed: {
|
for (let idRep of svgData.value.idReplacements) {
|
||||||
svgData() {
|
|
||||||
return iconMap[this.icon];
|
|
||||||
},
|
|
||||||
|
|
||||||
svgAttributes() {
|
|
||||||
return this.svgData.attributes;
|
|
||||||
},
|
|
||||||
|
|
||||||
svgName() {
|
|
||||||
return this.svgAttributes['data-icon'];
|
|
||||||
},
|
|
||||||
|
|
||||||
svgContent() {
|
|
||||||
let content = String(this.svgData.content);
|
|
||||||
|
|
||||||
for (let idRep of this.svgData.idReplacements) {
|
|
||||||
let newId = `__new_id_${globalIdCounter}`;
|
let newId = `__new_id_${globalIdCounter}`;
|
||||||
globalIdCounter += 1;
|
globalIdCounter += 1;
|
||||||
|
|
||||||
@ -98,63 +86,66 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const calculatedClasses = computed(() => {
|
||||||
|
const classes = (svgAttributes.value.class || "").split(" ");
|
||||||
|
|
||||||
rebind() {
|
classes.push(`iconic-${props.size}`);
|
||||||
const apis = APIS;
|
|
||||||
|
|
||||||
if (apis && this.svgName && apis[this.svgName]) {
|
if (props.iconSizeOverride) {
|
||||||
const iconApi = apis[this.svgName](this.$el);
|
classes.push(`iconic-icon-${props.iconSizeOverride}`);
|
||||||
for (let func in iconApi) this.$el[func] = iconApi[func]
|
|
||||||
} else {
|
|
||||||
this.$el.update = function() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.calculatedClasses = (this.svgAttributes.class || "").split(" ");
|
if (props.displaySizeOverride) {
|
||||||
|
classes.push(`iconic-size-${props.displaySizeOverride}`);
|
||||||
this.calculatedClasses.push(`iconic-${this.size}`);
|
|
||||||
|
|
||||||
if (this.iconSizeOverride) {
|
|
||||||
this.calculatedClasses.push(`iconic-icon-${this.iconSizeOverride}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displaySizeOverride) {
|
return classes;
|
||||||
this.calculatedClasses.push(`iconic-size-${this.displaySizeOverride}`);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this.$el.update();
|
function ensureSvgApi(name, scripts) {
|
||||||
}
|
if (!name) { return; }
|
||||||
},
|
if (LOADED_APIS[name] !== true) {
|
||||||
|
for (let sb of scripts) {
|
||||||
created() {
|
try {
|
||||||
if (LOADED_APIS[this.svgName] !== true) {
|
new Function(sb)(window);
|
||||||
for (let sb of this.svgData.scriptBlocks) {
|
} catch (e) {
|
||||||
try {
|
console.log(sb);
|
||||||
new Function(sb)(window);
|
console.log(e);
|
||||||
} catch (e) {
|
}
|
||||||
console.log(sb);
|
|
||||||
console.log(e);
|
|
||||||
}
|
}
|
||||||
|
LOADED_APIS[name] = true;
|
||||||
}
|
}
|
||||||
LOADED_APIS[this.svgName] = true;
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
function setupSvgApi(name) {
|
||||||
|
const apis = APIS;
|
||||||
this.$watch(
|
if (apis && apis[name]) {
|
||||||
function() { return [this.$attrs, this.icon, this.fluid] },
|
const iconApi = apis[name](svgElement.value);
|
||||||
function() {
|
for (let func in iconApi) svgElement.value[func] = iconApi[func]
|
||||||
this.rebind()
|
} else {
|
||||||
},
|
svgElement.value.update = function() {}
|
||||||
{
|
|
||||||
immediate: true
|
|
||||||
}
|
}
|
||||||
|
svgElement.value.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.icon,
|
||||||
|
() => {
|
||||||
|
ensureSvgApi(svgName.value, svgData.value.scriptBlocks);
|
||||||
|
nextTick(() => setupSvgApi(svgName.value));
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
svgData,
|
||||||
|
svgAttributes,
|
||||||
|
svgName,
|
||||||
|
svgContent,
|
||||||
|
calculatedClasses
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -18,40 +18,30 @@
|
|||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
import { mapState } from "pinia";
|
import { computed } from "vue";
|
||||||
import { useAppConfigStore } from "../stores/appConfig";
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
export default {
|
const emit = defineEmits(["dismiss"]);
|
||||||
emits: ["dismiss"],
|
|
||||||
|
|
||||||
props: {
|
const props = defineProps({
|
||||||
open: {
|
open: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
|
||||||
title: String,
|
|
||||||
wide: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
title: String,
|
||||||
computed: {
|
wide: {
|
||||||
...mapState(useAppConfigStore, [
|
type: Boolean,
|
||||||
'error'
|
default: false
|
||||||
])
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
close() {
|
|
||||||
this.$emit("dismiss");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const appConfig = useAppConfigStore();
|
||||||
|
const error = computed(() => appConfig.error);
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
emit("dismiss");
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -51,47 +51,27 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { ref, watch } from "vue";
|
||||||
import UserLogin from "./UserLogin";
|
import UserLogin from "./UserLogin";
|
||||||
import { mapState } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { useAppConfigStore } from "../stores/appConfig";
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
import { swUpdate } from "../lib/ServiceWorker";
|
import { swUpdate } from "../lib/ServiceWorker";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
export default {
|
const appConfig = useAppConfigStore();
|
||||||
data() {
|
const menuActive = ref(false);
|
||||||
return {
|
const route = useRoute();
|
||||||
menuActive: false
|
const { isAdmin, isLoggedIn, updateAvailable, user } = storeToRefs(appConfig);
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
function updateApp() {
|
||||||
...mapState(useAppConfigStore, [
|
swUpdate();
|
||||||
'route',
|
|
||||||
'user',
|
|
||||||
'updateAvailable'
|
|
||||||
])
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateApp() {
|
|
||||||
swUpdate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
route() {
|
|
||||||
this.menuActive = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
user() {
|
|
||||||
this.menuActive = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
UserLogin
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [route, appConfig.user],
|
||||||
|
() => menuActive.value = false
|
||||||
|
);
|
||||||
|
|
||||||
</script>
|
</script>
|
12
app/javascript/lib/useCheckAuthentication.js
Normal file
12
app/javascript/lib/useCheckAuthentication.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
|
export function useCheckAuthentication(loadResource) {
|
||||||
|
const appConfig = useAppConfigStore();
|
||||||
|
const checkAuthentication = function() {
|
||||||
|
return loadResource(appConfig.updateCurrentUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
checkAuthentication
|
||||||
|
}
|
||||||
|
}
|
27
app/javascript/lib/useLoadResource.js
Normal file
27
app/javascript/lib/useLoadResource.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
|
export function useLoadResource() {
|
||||||
|
const appConfig = useAppConfigStore();
|
||||||
|
const localLoadingCount = ref(0);
|
||||||
|
const localLoading = computed(() => localLoadingCount.value > 0);
|
||||||
|
|
||||||
|
const loadResource = async (promise) => {
|
||||||
|
appConfig.setLoading(true);
|
||||||
|
localLoadingCount.value = localLoadingCount.value + 1;
|
||||||
|
try {
|
||||||
|
return await promise;
|
||||||
|
} catch (error) {
|
||||||
|
appConfig.setError(error);
|
||||||
|
} finally {
|
||||||
|
appConfig.setLoading(false);
|
||||||
|
localLoadingCount.value = localLoadingCount.value - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadResource,
|
||||||
|
localLoading,
|
||||||
|
localLoadingCount
|
||||||
|
};
|
||||||
|
}
|
@ -24,6 +24,7 @@
|
|||||||
"@tweenjs/tween.js": "^25.0.0",
|
"@tweenjs/tween.js": "^25.0.0",
|
||||||
"@types/babel__core": "7",
|
"@types/babel__core": "7",
|
||||||
"@types/webpack": "5",
|
"@types/webpack": "5",
|
||||||
|
"@vueuse/core": "^11.1.0",
|
||||||
"babel-loader": "8",
|
"babel-loader": "8",
|
||||||
"bulma": "^1.0.2",
|
"bulma": "^1.0.2",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
|
38
yarn.lock
38
yarn.lock
@ -1895,6 +1895,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/web-bluetooth@npm:^0.0.20":
|
||||||
|
version: 0.0.20
|
||||||
|
resolution: "@types/web-bluetooth@npm:0.0.20"
|
||||||
|
checksum: 10c0/3a49bd9396506af8f1b047db087aeeea9fe4301b7fad4fe06ae0f6e00d331138caae878fd09e6410658b70b4aaf10e4b191c41c1a5ff72211fe58da290c7d003
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/webpack@npm:5":
|
"@types/webpack@npm:5":
|
||||||
version: 5.28.5
|
version: 5.28.5
|
||||||
resolution: "@types/webpack@npm:5.28.5"
|
resolution: "@types/webpack@npm:5.28.5"
|
||||||
@ -2038,6 +2045,34 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@vueuse/core@npm:^11.1.0":
|
||||||
|
version: 11.1.0
|
||||||
|
resolution: "@vueuse/core@npm:11.1.0"
|
||||||
|
dependencies:
|
||||||
|
"@types/web-bluetooth": "npm:^0.0.20"
|
||||||
|
"@vueuse/metadata": "npm:11.1.0"
|
||||||
|
"@vueuse/shared": "npm:11.1.0"
|
||||||
|
vue-demi: "npm:>=0.14.10"
|
||||||
|
checksum: 10c0/ecbeb277de81608c78aa4ebc7e4cae8a6d5f0650e2ee5ed71bf387676df4725bd2e8bcfeadcc0798ab6850e6317ff79822ef32adef4646184086764fe7f684ac
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@vueuse/metadata@npm:11.1.0":
|
||||||
|
version: 11.1.0
|
||||||
|
resolution: "@vueuse/metadata@npm:11.1.0"
|
||||||
|
checksum: 10c0/5063d8b81c31e3c7ea24ff7fad0d0ec43a951540066329f36daf96fc0e8780ed902282d36c6df856288f07d0c5edb525f551cf9e8b84837782e2ac0a1988c498
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@vueuse/shared@npm:11.1.0":
|
||||||
|
version: 11.1.0
|
||||||
|
resolution: "@vueuse/shared@npm:11.1.0"
|
||||||
|
dependencies:
|
||||||
|
vue-demi: "npm:>=0.14.10"
|
||||||
|
checksum: 10c0/ed9a4625537825fe783c66823592560696604f5301e8dcef12dda3a816b85334e7c70ccbaaf1c0c62ea2b8efcce58893b780210162374d2e9bc1e4c5889e066b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1":
|
"@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1":
|
||||||
version: 1.12.1
|
version: 1.12.1
|
||||||
resolution: "@webassemblyjs/ast@npm:1.12.1"
|
resolution: "@webassemblyjs/ast@npm:1.12.1"
|
||||||
@ -2419,6 +2454,7 @@ __metadata:
|
|||||||
"@tweenjs/tween.js": "npm:^25.0.0"
|
"@tweenjs/tween.js": "npm:^25.0.0"
|
||||||
"@types/babel__core": "npm:7"
|
"@types/babel__core": "npm:7"
|
||||||
"@types/webpack": "npm:5"
|
"@types/webpack": "npm:5"
|
||||||
|
"@vueuse/core": "npm:^11.1.0"
|
||||||
babel-loader: "npm:8"
|
babel-loader: "npm:8"
|
||||||
bulma: "npm:^1.0.2"
|
bulma: "npm:^1.0.2"
|
||||||
cheerio: "npm:^1.0.0"
|
cheerio: "npm:^1.0.0"
|
||||||
@ -6606,7 +6642,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vue-demi@npm:^0.14.10":
|
"vue-demi@npm:>=0.14.10, vue-demi@npm:^0.14.10":
|
||||||
version: 0.14.10
|
version: 0.14.10
|
||||||
resolution: "vue-demi@npm:0.14.10"
|
resolution: "vue-demi@npm:0.14.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user