556 lines
17 KiB
Vue
556 lines
17 KiB
Vue
<template>
|
|
<hr>
|
|
<fieldset class="main-menu-node-data-editor-component container-fluid">
|
|
<legend>
|
|
<span class="fs-5">{{ __("Modification de données d'un noeud") }}</span>
|
|
</legend>
|
|
<span class="d-block fw-bold">{{ __('Partie gauche') }}</span>
|
|
<!-- SUB LABEL -->
|
|
<div class="mb-3 row">
|
|
<label
|
|
for="title-sub-label"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Label du sous-menu") }}
|
|
</label>
|
|
<div class="col">
|
|
<WordCounter
|
|
id="title-sub-label"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="title-sub-label"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.sub_label"
|
|
@blur="($event) => saveNodeDataInput('sub_label', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">
|
|
{{ __('validation.between.string', {
|
|
attribute: "label du sous-menu",
|
|
min: "3",
|
|
max: "255",
|
|
}) }}
|
|
</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.sub_label"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.sub_label"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- SEPARATION -->
|
|
<hr>
|
|
<span class="d-block fw-bold">{{ __('Partie droite') }}</span>
|
|
<!-- TITLE -->
|
|
<div class="mb-3 row">
|
|
<label
|
|
for="title-text"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Titre") }}
|
|
</label>
|
|
<div class="col">
|
|
<WordCounter
|
|
id="title-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="title-text"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.title"
|
|
@blur="($event) => saveNodeDataInput('title', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">
|
|
{{ __('validation.between.string', {
|
|
attribute: "titre",
|
|
min: "3",
|
|
max: "255",
|
|
}) }}
|
|
</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.title"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.title"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- TEXT -->
|
|
<div class="mb-3 row">
|
|
<label
|
|
for="text-text"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Texte") }}
|
|
</label>
|
|
<div class="col">
|
|
<WordCounter
|
|
id="text-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<textarea
|
|
id="text-text"
|
|
class="form-control"
|
|
@blur="($event) => $nextTick(() => saveNodeDataInput('text', ($event.target as HTMLTextAreaElement).value))"
|
|
:value="menuNodeData.text"
|
|
/>
|
|
<small class="form-text text-body-secondary">
|
|
{{ __('validation.between.string', {
|
|
attribute: "texte",
|
|
min: "3",
|
|
max: "255",
|
|
}) + " " + __('crud.minicrud.text_warning') }}
|
|
</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.text"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.text"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- COLOR -->
|
|
<div class="mb-3 row">
|
|
<label
|
|
for="text_color"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Couleur du texte") }}
|
|
</label>
|
|
<div class="col">
|
|
<ColorPickerComponent
|
|
name="text_color"
|
|
:value="menuNodeData.text_color"
|
|
@update-color="($event) => $nextTick(() => saveNodeDataInput('text_color', ($event as HTMLInputElement).value))"
|
|
/>
|
|
<small class="form-text text-body-secondary">
|
|
{{ __('Choisissez une couleur qui se distingue bien de l\'image.') }}
|
|
</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.text_color"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.text_color"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- LINK -->
|
|
<div class="mb-3 row">
|
|
<!-- LINK URL -->
|
|
<label
|
|
for="link-text"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("URL du lien") }}
|
|
</label>
|
|
<div class="col-sm-10 p-2">
|
|
<WordCounter
|
|
id="link-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="link-text"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.link_url"
|
|
@blur="($event) => saveNodeDataInput('link_url', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">
|
|
{{ __('validation.between.string', {
|
|
attribute: "titre du lien",
|
|
min: "3",
|
|
max: "255",
|
|
}) + " " + __('Doît être une url valide.') }}
|
|
</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.link_url"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.link_url"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<!-- LINK TITLE -->
|
|
<label
|
|
for="link-title-text"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Titre du lien") }}
|
|
</label>
|
|
<div class="col-sm-10 p-2">
|
|
<WordCounter
|
|
id="link-title-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="link-title-text"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.link_title"
|
|
@blur="($event) => saveNodeDataInput('link_title', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">
|
|
{{ __('validation.between.string', {
|
|
attribute: "titre du lien",
|
|
min: "3",
|
|
max: "255",
|
|
}) }}
|
|
</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.link_title"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.link_title"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<!-- LINK TARGET -->
|
|
<label
|
|
for="link-target-checkbox"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Cible du lien") }}
|
|
</label>
|
|
<div class="col-sm-4 p-2">
|
|
<div class="form-check form-switch py-0">
|
|
<input
|
|
name="link_target"
|
|
class="form-check-input pt-3"
|
|
type="checkbox"
|
|
role="switch"
|
|
id="link-target-checkbox"
|
|
:checked="menuNodeData.link_target"
|
|
@change="($event) => saveNodeDataInput('link_target', ($event.target as HTMLInputElement).checked)"
|
|
>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.link_target"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.link_target"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<small class="form-text text-body-secondary">{{ __('Ouverture dans un nouvel onglet. Deux réponses possibles : oui ou non.') }}</small>
|
|
</div>
|
|
<!-- LINK STATUS -->
|
|
<label
|
|
class="col-sm-2 col-form-label"
|
|
for="link-status-checkbox"
|
|
>
|
|
{{ __("Statut du lien") }}
|
|
</label>
|
|
<div class="col-sm-4 p-2">
|
|
<div class="form-check form-switch py-0">
|
|
<input
|
|
name="link_status"
|
|
class="form-check-input pt-3"
|
|
type="checkbox"
|
|
role="switch"
|
|
id="link-status-checkbox"
|
|
:checked="menuNodeData.link_status"
|
|
@change="($event) => saveNodeDataInput('link_status', ($event.target as HTMLInputElement).checked)"
|
|
>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.link_status"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.link_status"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<small class="form-text text-body-secondary">{{ __('Visibilité du lien. Deux réponses possibles : visible ou caché.') }}</small>
|
|
</div>
|
|
</div>
|
|
<!-- PICTURE -->
|
|
<div class="mb-3 row">
|
|
<!-- PICTURE -->
|
|
<label
|
|
for="picture-input"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Image") }}
|
|
</label>
|
|
<div class="col-sm-10 p-2">
|
|
<ImageInputComponent
|
|
id="picture-input"
|
|
:required="true"
|
|
:show-labels="true"
|
|
name="picture"
|
|
:helper="__('crud.minicrud.dimensions_must_be', {
|
|
widthpx: '180px',
|
|
heightpx: '228px',
|
|
})"
|
|
:width="180"
|
|
:height="228"
|
|
:value="menuNodeData.picture"
|
|
:preview="false"
|
|
:browse-event="false"
|
|
@image-file="(file) => file ? saveNodeDataInput('picture', file) : null"
|
|
/>
|
|
</div>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.picture"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.picture"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
<!-- PICTURE ALT -->
|
|
<label
|
|
for="picture-alt-text"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Text alternatif de l'image") }}
|
|
</label>
|
|
<div class="col-sm-10 p-2">
|
|
<WordCounter
|
|
id="picture-alt-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="picture-alt-text"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.picture_alt"
|
|
@blur="($event) => saveNodeDataInput('picture_alt', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">{{ __('crud.minicrud.picture_alt_helper') }}</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.picture_alt"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.picture_alt"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<!-- PICTURE TITLE -->
|
|
<label
|
|
for="picture-title-text"
|
|
class="col-sm-2 col-form-label"
|
|
>
|
|
{{ __("Titre de l'image") }}
|
|
</label>
|
|
<div class="col-sm-10 p-2">
|
|
<WordCounter
|
|
id="picture-title-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="picture-title-text"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.picture_title"
|
|
@blur="($event) => saveNodeDataInput('picture_title', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">{{ __('crud.minicrud.picture_title_helper') }}</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.picture_title"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.picture_title"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<!-- PICTURE STATUS -->
|
|
<label
|
|
class="col-sm-2 col-form-label"
|
|
for="picture-status-checkbox"
|
|
>
|
|
{{ __("Statut de l'image") }}
|
|
</label>
|
|
<div class="col-sm-4 p-2">
|
|
<div class="form-check form-switch py-0">
|
|
<input
|
|
name="picture_status"
|
|
class="form-check-input pt-3"
|
|
type="checkbox"
|
|
role="switch"
|
|
id="picture-status-checkbox"
|
|
:checked="menuNodeData.picture_status"
|
|
@change="($event) => saveNodeDataInput('picture_status', ($event.target as HTMLInputElement).checked)"
|
|
>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.picture_status"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.picture_status"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<small class="form-text text-body-secondary">{{ __('Visibilité de l\'image. Deux réponses possibles : visible ou cachée.') }}</small>
|
|
</div>
|
|
</div>
|
|
<!-- BACKGROUND PICTURE-->
|
|
<div class="mb-3 row">
|
|
<label
|
|
for="background-picture-input"
|
|
class="col-sm-2 col-form-label p-2"
|
|
>
|
|
{{ __("Image de fond") }}
|
|
</label>
|
|
<!-- BACKGROUND PICTURE -->
|
|
<div class="col-sm-10 p-2">
|
|
<ImageInputComponent
|
|
id="background-picture-input"
|
|
:required="true"
|
|
:show-labels="true"
|
|
name="background_picture"
|
|
:helper="__('crud.minicrud.dimensions_must_be', {
|
|
widthpx: '500px',
|
|
heightpx: '231px',
|
|
})"
|
|
:width="500"
|
|
:height="231"
|
|
:value="menuNodeData.background_picture"
|
|
:preview="false"
|
|
:browse-event="false"
|
|
@image-file="(file) => file ? saveNodeDataInput('background_picture', file) : null"
|
|
/>
|
|
</div>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.background_picture"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.background_picture"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
<!-- BACKGROUND PICTURE ALT -->
|
|
<label
|
|
for="background-picture-alt-text"
|
|
class="col-sm-2 col-form-label p-2"
|
|
>
|
|
{{ __("Text alternatif de l'image de fond") }}
|
|
</label>
|
|
<div class="col-sm-10 p-2">
|
|
<WordCounter
|
|
id="background-picture-alt-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="background-picture-alt-text"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.background_picture_alt"
|
|
@blur="($event) => saveNodeDataInput('background_picture_alt', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">{{ __('crud.minicrud.picture_alt_helper') }}</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.background_picture_alt"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.background_picture_alt"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
<!-- BACKGROUND PICTURE TITLE -->
|
|
<label
|
|
for="background-picture-title-text"
|
|
class="col-sm-2 col-form-label p-2"
|
|
>
|
|
{{ __("Titre de l'image de fond") }}
|
|
</label>
|
|
<div class="col-sm-10 p-2">
|
|
<WordCounter
|
|
id="background-picture-title-text"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="background-picture-title-text"
|
|
type="text"
|
|
class="form-control"
|
|
:value="menuNodeData.background_picture_title"
|
|
@blur="($event) => saveNodeDataInput('background_picture_title', ($event.target as HTMLInputElement).value)"
|
|
>
|
|
<small class="form-text text-body-secondary">{{ __('crud.minicrud.picture_title_helper') }}</small>
|
|
<template
|
|
v-for="(message, messageIndex) of errors.background_picture_title"
|
|
:key="messageIndex"
|
|
>
|
|
<small
|
|
v-if="errors.background_picture_title"
|
|
class="text-danger"
|
|
>"{{ message }}"</small>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { __ } from "@modules/trans";
|
|
import { reactive, watch, type PropType } from "vue";
|
|
import type { IMenuNodeDataInputInterface } from "../../interfaces/IMenuNodeDataInputInterface";
|
|
import type { MenuNodeDataInterface } from "../../types/models/MenuNodeData";
|
|
import ColorPickerComponent from "./ColorPickerComponent.vue";
|
|
import ImageInputComponent from "./ImageInputComponent.vue";
|
|
import WordCounter from "./WordCounterComponent.vue";
|
|
|
|
defineOptions({
|
|
name: "MainMenuNodeDataEditor"
|
|
});
|
|
|
|
// * EMITS
|
|
const emits = defineEmits<{
|
|
updatedNodeData: [prop: string, value: ValueOf<IMenuNodeDataInputInterface> ],
|
|
}>();
|
|
|
|
// * PROPS
|
|
const props = defineProps({
|
|
menuNodeData: {
|
|
type: Object as PropType<MenuNodeDataInterface>,
|
|
required: true
|
|
},
|
|
errors: {
|
|
type: Object as PropType<LaravelValidationErrors>,
|
|
required: true
|
|
}
|
|
});
|
|
|
|
// * DATA
|
|
let menuNodeData = reactive<MenuNodeDataInterface>(Object.assign({}, props.menuNodeData));
|
|
|
|
// * WATCHERS
|
|
watch(props, () => {
|
|
menuNodeData = Object.assign({}, props.menuNodeData);
|
|
});
|
|
|
|
// * METHODS
|
|
function saveNodeDataInput(inputName: string, inputValue: ValueOf<IMenuNodeDataInputInterface>): void {
|
|
emits("updatedNodeData", inputName, inputValue);
|
|
}
|
|
</script>
|