ldc-legaulois/resources/ts/bo/components/SliderComponent.vue
2026-05-05 18:06:46 +02:00

285 lines
8.5 KiB
Vue

<template>
<div
:class="`slider-component slider-${inputParameters.name}`"
ref="slider"
>
<div
v-if="itemParameters.list.length > 0"
>
<div
class="border rounded-3 p-3 mb-3"
v-for="(item, itemIndex) of itemParameters.list"
:key="itemIndex"
>
<div
class="d-flex justify-content-between align-items-center"
>
<legend class="w-fit me-2 mb-0">
Slide n°{{ itemIndex + 1 }}
</legend>
<div
v-if="buttonsDisplayParameters.remove"
class="align-bottom"
>
<button
@click.stop.prevent="removeLine(itemIndex)"
class="btn btn-sm btn-danger"
:title="trans.methods.__('Supprimer la ligne')"
data-bs-toggle="tooltip"
>
<i class="fa-solid fa-trash" />
</button>
</div>
</div>
<div
class="row"
>
<div
class="col-6 mb-3"
v-for="(column, columnIndex) of itemParameters.columns"
:key="columnIndex"
>
<label class="py-2">
<b>{{ column.placeholder + (column.required ? '*' : '') }}</b>
</label>
<div
v-if="itemParameters.columns[columnIndex].type == 'wysiwyg'"
:class="(getErrorMessage(itemIndex, column.id) !== null) ? 'is-invalid' : ''"
>
<CkEditorComponent
:name="`data[${inputParameters.name}][value][${String(itemIndex)}][${column.id}]`"
:placeholder="itemParameters.columns[columnIndex].placeholder"
:dbtext="String(item[column.id as keyof typeof item] ?? '')"
:required="true"
:errors="allErrors"
:editor-config="itemParameters.wysiwygConfig"
/>
</div>
<div
v-else-if="itemParameters.columns[columnIndex].type == 'picture'"
:class="(getErrorMessage(itemIndex, column.id) !== null) ? 'is-invalid' : ''"
>
<ImageInputComponent
:id="'picture-input-' + inputParameters.name + '-' + column.id + '-' + itemIndex"
:required="itemParameters.columns[columnIndex].required"
:show-labels="true"
:name="`data[${inputParameters.name}][value][${String(itemIndex)}][${column.id}]`"
:helper="trans.methods.__('crud.minicrud.dimensions_must_be', {
widthpx: (itemParameters.imageSizes[0] ?? String(500)) + 'px',
heightpx: (itemParameters.imageSizes[1] ?? String(500)) + 'px',
})"
:width="itemParameters.imageSizes[0] ?? 500"
:height="itemParameters.imageSizes[1] ?? 500"
:value="item[column.id as keyof typeof item] ? String(item[column.id as keyof typeof item]) : undefined"
:preview="false"
:browse-event="false"
/>
</div>
<div
v-else
>
<WordCounter
:id="'text-input-' + inputParameters.name + '-' + column.id + '-' + itemIndex"
class="ck-word-count"
/>
<input
:id="'text-input-' + inputParameters.name + '-' + column.id + '-' + itemIndex"
:class="['form-control', (getErrorMessage(itemIndex, column.id) !== null) ? 'is-invalid' : '']"
:name="`data[${inputParameters.name}][value][${String(itemIndex)}][${column.id}]`"
:type="itemParameters.columns[columnIndex].type"
v-model="item[column.id as keyof typeof item]"
:placeholder="itemParameters.columns[columnIndex].placeholder"
:required="itemParameters.columns[columnIndex].required"
>
</div>
<small class="text-danger m-0">
{{ getErrorMessage(itemIndex, column.id) }}
</small>
</div>
</div>
</div>
</div>
<div v-else>
<p>{{ trans.methods.__("Aucun élément renseigné(s).") }}</p>
</div>
<div
v-if="buttonsDisplayParameters.add"
class="text-center mt-1"
>
<button
@click.prevent="addLine"
class="btn btn-sm btn-primary"
type="button"
:title="trans.methods.__('Ajouter une ligne')"
data-bs-toggle="tooltip"
>
<i class="fa-solid fa-plus" />
</button>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, defineOptions, onMounted, reactive, ref, useAttrs } from "vue";
import parseLaravelErrorMessages from "./../../modules/parseLaravelErrorMessages";
import trans from "./../../modules/trans";
import { Tooltips } from "./../../modules/bsTooltips";
import CkEditorComponent from "./CkEditorComponent.vue";
import ImageInputComponent from "./ImageInputComponent.vue";
import WordCounter from "./WordCounterComponent.vue";
defineOptions({
name: "SliderComponent"
});
// * ATTRIBUTES
const attrs = useAttrs();
// * REFS
const slider = ref<HTMLDivElement|null>(null);
// * DATA
const tooltips = ref<Tooltips | null>(null);
const allErrors = ref<LaravelValidationErrors>({});
interface Column {
id: string;
type: "picture" | "text" | "wysiwyg";
placeholder: string;
validation: string;
required: boolean;
}
/** Input parameters. */
const inputParameters = reactive<{
id: string,
name: string,
}>({
id: "string",
name: "",
});
/** Item parameters. */
const itemParameters = reactive<{
list: Array<Object>,
columns: Array<Column>,
limits: Array<number>,
wysiwygConfig: string,
imageSizes: Array<number>,
}>({
list: [{}],
columns: [],
limits: [0, 0],
wysiwygConfig: "",
imageSizes: [500, 500],
});
/** Buttons parameters. */
const buttonsDisplayParameters = reactive<{
add: boolean,
remove: boolean,
}>({
add: true,
remove: true,
});
// * COMPUTED
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
// * LIFECYCLE
onMounted(() => {
const json = String(attrs.json ?? "{}"),
data = JSON.parse(json);
if (!isUsedWithProps.value) {
/** Input parameters. */
inputParameters.name = data.inputName;
/** Item parameters. */
itemParameters.list = Array.isArray(data.itemList) ? data.itemList : [];
itemParameters.columns = data.columns;
itemParameters.limits = data.itemLimit;
itemParameters.wysiwygConfig = data.wysiwygConfig;
itemParameters.imageSizes = data.imageSizes;
/** Global parameters */
allErrors.value = data.errors;
}
setDefaultLines();
checkLengthList();
initTooltips();
});
// * METHODS
/**
* Set default line(s)
* according to the minimum limit defined.
*/
function setDefaultLines(): void {
while (itemParameters.list.length < itemParameters.limits[0]) {
addLine();
}
}
/**
* Add a new line to the <table>,
* add a new element to the array,
* show remove line button.
*/
function addLine(): void {
itemParameters.list.push({});
if (buttonsDisplayParameters.remove == false) {
buttonsDisplayParameters.remove = true;
}
checkLengthList();
tooltips.value?.closeBootstrapTooltip();
initTooltips();
}
/**
* Remove a specific line from the <table>,
* remove a specific element to the array,
* show adding line button.
*/
function removeLine(itemIndex: number): void {
itemParameters.list.splice(itemIndex, 1);
if (buttonsDisplayParameters.add == false) {
buttonsDisplayParameters.add = true;
}
checkLengthList();
tooltips.value?.closeBootstrapTooltip();
initTooltips();
}
/**
* Check the length of the list,
* update variables accordingly.
*/
function checkLengthList(): void {
/** Length minimum */
if (itemParameters.list.length <= itemParameters.limits[0]) {
buttonsDisplayParameters.remove = false;
}
/** Length maximum */
if (itemParameters.list.length >= itemParameters.limits[1]) {
buttonsDisplayParameters.add = false;
}
}
/** Return error message or null. */
function getErrorMessage(itemIndex: number, column: string): string|null {
return parseLaravelErrorMessages.methods.parseValidationInput(
`${inputParameters.name}.${String(itemIndex)}.${column}`,
allErrors.value
);
}
/** Initialise all tooltips in the component. */
function initTooltips(): void {
setTimeout(() => {
tooltips.value = Tooltips.make({
type: "dom",
elements: slider.value!.querySelectorAll("[data-bs-toggle=\"tooltip\"]")
});
}, 500);
}
</script>