168 lines
4.3 KiB
Vue
168 lines
4.3 KiB
Vue
<script setup>
|
|
|
|
import { ref, onMounted, useTemplateRef } from 'vue';
|
|
import { FormGroup, TextInput, Button, TagInput } from 'pankow';
|
|
import ImagePicker from '../ImagePicker.vue';
|
|
import AppsModel from '../../models/AppsModel.js';
|
|
|
|
const props = defineProps([ 'app' ]);
|
|
|
|
const appsModel = AppsModel.create();
|
|
|
|
const imagePicker = useTemplateRef('imagePicker');
|
|
const busy = ref(false);
|
|
const labelError = ref('');
|
|
const tagsError = ref('');
|
|
const iconError = ref('');
|
|
const label = ref('');
|
|
const tags = ref([]);
|
|
const iconUrl = ref('');
|
|
|
|
let newIcon = null;
|
|
|
|
function onIconChanged(file) {
|
|
newIcon = file;
|
|
}
|
|
|
|
function getDataURLFromFile(file) {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = function(event) {
|
|
resolve(event.target.result);
|
|
};
|
|
|
|
reader.onerror = function(event) {
|
|
reject(event.target.error);
|
|
};
|
|
|
|
reader.readAsDataURL(file);
|
|
});
|
|
}
|
|
|
|
async function onSubmit() {
|
|
busy.value = true;
|
|
labelError.value = '';
|
|
tagsError.value = '';
|
|
iconError.value = '';
|
|
|
|
if (label.value !== props.app.label) {
|
|
const [error] = await appsModel.configure(props.app.id, 'label', { label: label.value });
|
|
if (error) {
|
|
labelError.value = error.body ? error.body.message : 'Internal error';
|
|
busy.value = false;
|
|
return console.error(error);
|
|
}
|
|
}
|
|
|
|
if (tags.value.join() !== props.app.tags.join()) {
|
|
const [error] = await appsModel.configure(props.app.id, 'tags', { tags: tags.value });
|
|
if (error) {
|
|
tagsError.value = error.body ? error.body.message : 'Internal error';
|
|
busy.value = false;
|
|
return console.error(error);
|
|
}
|
|
}
|
|
|
|
if (newIcon) {
|
|
let tmp;
|
|
if (newIcon === 'clear') {
|
|
tmp = '';
|
|
} else {
|
|
tmp = (await getDataURLFromFile(newIcon)).replace(/^data:image\/[a-z]+;base64,/, '');
|
|
}
|
|
|
|
const [error] = await appsModel.configure(props.app.id, 'icon', { icon: tmp });
|
|
if (error) {
|
|
iconError.value = error.body ? error.body.message : 'Internal error';
|
|
busy.value = false;
|
|
return console.error(error);
|
|
}
|
|
}
|
|
|
|
busy.value = false;
|
|
}
|
|
|
|
function onClearIcon() {
|
|
imagePicker.value.clear(`${iconUrl.value}&original=true`);
|
|
newIcon = 'clear';
|
|
}
|
|
|
|
onMounted(() => {
|
|
label.value = props.app.label;
|
|
tags.value = props.app.tags;
|
|
iconUrl.value = props.app.iconUrl;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<form @submit.prevent="onSubmit()" autocomplete="off">
|
|
<fieldset :disabled="busy">
|
|
<input type="submit" style="display: none;"/>
|
|
|
|
<FormGroup>
|
|
<label for="labelInput">{{ $t('app.display.label') }}</label>
|
|
<TextInput id="labelInput" v-model="label"/>
|
|
<div class="text-error" v-if="labelError">{{ labelError }}</div>
|
|
</FormGroup>
|
|
|
|
<FormGroup>
|
|
<label for="tagsInput">{{ $t('app.display.tags') }}</label>
|
|
<TagInput id="tagsInput" :placeholder="$t('app.display.tagsPlaceholder')" v-model="tags" v-tooltip="$t('app.display.tagsTooltip')"/>
|
|
<div class="text-error" v-if="tagsError">{{ tagsError }}</div>
|
|
</FormGroup>
|
|
|
|
<FormGroup>
|
|
<label>{{ $t('app.display.icon') }}</label>
|
|
<ImagePicker ref="imagePicker" :src="iconUrl" fallback-src="/img/appicon_fallback.png" @changed="onIconChanged" :size="512" display-height="128px"/>
|
|
<div class="actionable" @click="onClearIcon()">{{ $t('app.display.iconResetAction') }}</div>
|
|
</FormGroup>
|
|
</fieldset>
|
|
</form>
|
|
<br/>
|
|
<Button @click="onSubmit()" :loading="busy" :disabled="busy">{{ $t('app.display.saveAction') }}</Button>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
.app-custom-icon {
|
|
position: relative;
|
|
cursor: pointer;
|
|
width: 64px;
|
|
height: 64px;
|
|
margin-bottom: 5px;
|
|
background-position: center;
|
|
background-size: 100% 100%;
|
|
background-repeat: no-repeat;
|
|
border: 1px solid gray;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.app-custom-icon > img {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.app-custom-icon-edit-indicator {
|
|
position: absolute;
|
|
bottom: -4px;
|
|
right: -4px;
|
|
border-radius: 20px;
|
|
padding: 5px;
|
|
color: var(--pankow-text-color);
|
|
background-color: var(--pankow-input-background-color);
|
|
transition: all 250ms;
|
|
}
|
|
|
|
.app-custom-icon:hover > .app-custom-icon-edit-indicator {
|
|
color: white;
|
|
background: var(--pankow-color-primary);
|
|
transform: scale(1.2);
|
|
}
|
|
|
|
</style>
|