ldc-legaulois/resources/ts/bo/components/MainMenuNodeEditor.vue

246 lines
6.6 KiB
Vue

<template>
<!-- NODE PROPS EDITOR -->
<fieldset
ref="mainMenuNodeEditor"
class="main-menu-node-editor-component container-fluid mb-3"
>
<!-- LEGEND -->
<legend>
<span class="fs-5">{{ __("Modification d'un noeud") }}</span>
<button
class="btn btn-sm btn-primary float-end mt-2"
:title="__('Fermer la modification')"
data-bs-toggle="tooltip"
@click.prevent="closeEditor"
>
<FontAwesomeIcon icon="fa fa-close" />
</button>
</legend>
<!-- MENU NAME -->
<div class="mb-3 row">
<label
for="title-node"
class="col-sm-2 col-form-label"
>
{{ __("Titre du noeud principal") }}
</label>
<div class="col-sm-10 p-2">
<WordCounter
id="title-node"
class="ck-word-count"
/>
<input
id="title-node"
type="text"
class="form-control"
:value="menuNode.title"
@blur="menuNode.title = ($event.target as HTMLInputElement).value"
>
<small class="form-text text-body-secondary">
{{ __('validation.between.string', {
attribute: "titre du noeud principal",
min: "3",
max: "255",
}) }}
</small>
</div>
</div>
<!-- MENU PUBLISHED -->
<div class="mb-3 row">
<!-- LINK TARGET -->
<label
for="published-checkbox"
class="col-sm-2 col-form-label"
>
{{ __("Visibilité du noeud") }}
</label>
<div class="col p-2">
<div class="form-check form-switch py-0">
<input
name="published"
class="form-check-input pt-3"
type="checkbox"
role="switch"
id="published-checkbox"
:checked="menuNode.published"
@blur="menuNode.published = ($event.target as HTMLInputElement).checked"
>
</div>
<small class="form-text text-body-secondary">{{ __('Deux réponses possibles : oui ou non (maximum 8 liens publiés).') }}</small>
</div>
</div>
<!-- ROUTE NAME -->
<div
v-if="props.enum.value !== MenuNodeTypeEnum.link"
class="mb-3 row"
>
<label
for="type"
class="col-sm-2 col-form-label"
>
{{ __("Lien du noeud") }}
</label>
<div class="col p-2">
<div class="input-group">
<input
type="text"
class="form-control"
:value="getUrlFromRoute(String(menuNode.link.routeName))"
disabled
>
<a
class="btn btn-info"
target="_blank"
:href="getUrlFromRoute(String(menuNode.link.routeName))"
:title="__('Accéder à la page liée')"
data-bs-toggle="tooltip"
>
<FontAwesomeIcon icon="fa fa-arrow-up-right-from-square" />
</a>
</div>
</div>
</div>
<!-- URL -->
<div
v-else
class="mb-3 row"
>
<label
for="type"
class="col-sm-2 col-form-label"
>
{{ __("Url") }}
</label>
<div class="col p-2">
<div>
<WordCounter
id="node-url"
class="ck-word-count"
/>
<div class="input-group">
<input
ref="urlInput"
id="node-url"
type="text"
class="form-control"
:value="String(menuNode.link.url)"
@blur="saveUrl(($event.target as HTMLInputElement | null)!.value)"
>
<a
class="btn btn-info rounded-top-0"
target="_blank"
:href="String(menuNode.link.url)"
:title="__('Accéder à la page liée')"
data-bs-toggle="tooltip"
>
<FontAwesomeIcon icon="fa fa-arrow-up-right-from-square" />
</a>
</div>
</div>
<small class="form-text text-body-secondary">
{{ __('Doît être une url valide.') }}
</small>
</div>
<small
v-if="!urlIsValid"
class="d-block text-danger"
>{{ __("L'url saisie n'est pas valide") }}</small>
</div>
</fieldset>
</template>
<script lang="ts" setup>
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { __ } from "@modules/trans";
import { onMounted, ref, watch, type PropType } from "vue";
import route from "../../modules/route";
import type { MenuNodeInterface } from "../../types/models/MenuNode";
import { MenuNodeTypeEnum } from "../enums";
import { Tooltips } from "./../../modules/bsTooltips";
import MainMenuNodeDataEditor from "./MainMenuNodeDataEditor.vue";
import WordCounter from "./WordCounterComponent.vue";
defineOptions({
name: "MainMenuNodeEditor",
components: {MainMenuNodeDataEditor}
});
// * EMITS
const emits = defineEmits<{
closeEditor: [],
updatedNode: [menuNode: MenuNodeInterface],
}>();
// * PROPS
const props = defineProps({
menuNode: {
type: Object as PropType<MenuNodeInterface>,
required: true
},
enum: {
type: Object as PropType<Enum>,
required: true
}
});
// * REFS
const mainMenuNodeEditor = ref<HTMLInputElement|null>(null);
const urlInput = ref<HTMLInputElement|null>(null);
// * DATA
const menuNode = ref<MenuNodeInterface>(Object.assign({}, props.menuNode));
const urlIsValid = ref<boolean>(true);
const tooltips = ref<Tooltips|null>(null);
// * LIFECYCLE
onMounted(() => {
urlIsValid.value = isUrlValid(String(menuNode.value.link.url));
initTooltips();
});
// * WATCHERS
watch(menuNode, (newValue) => {
emits("updatedNode", newValue);
}, { deep: true });
// * METHODS
function saveUrl(newUrl: string): void {
urlIsValid.value = isUrlValid(newUrl);
menuNode.value.link.url = urlInput.value!.value;
}
function closeEditor(): void {
tooltips.value?.closeBootstrapTooltip();
emits("closeEditor");
}
function isUrlValid(url: string):boolean {
const regex = RegExp("(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+@]*)*(\\?[;&a-z\\d%_.~+=-@]*)?(\\#[-a-z\\d_@]*)?$", "i");
return url.match(regex)?.[0] !== undefined;
}
function getUrlFromRoute(url: string) {
const routeUrl = route(url);
if (!routeUrl) {
throw new Error("Undefined route " + url);
}
return routeUrl;
}
/** Initialise all tooltips in the component */
function initTooltips(): void {
setTimeout(() => {
tooltips.value = Tooltips.make({
type: "dom",
elements: mainMenuNodeEditor.value!.querySelectorAll("[data-bs-toggle=\"tooltip\"]")
});
}, 500);
}
</script>
<style lang="scss" scoped>
.main-menu-node-editor-component {
max-height: 30rem;
}
</style>