ldc-gen-game-web/resources/ts/components/SelectMultipleEnumComponent.vue

259 lines
6.5 KiB
Vue

<template>
<div class="select-multiple-enum user-select-none row w-100 mx-auto">
<div
class="col-12 p-0"
:class="intSpecificItem ? 'col-md-6' : ''"
>
<label
:for="intName"
class="col-form-label"
>
<b>{{ intLabel }} {{ intRequired ? "*" : "" }}</b>
</label>
<div class="d-inline-block w-100">
<VueTagsInputCls
v-model="intItem"
:tags="intSelectedItem"
:autocomplete-items="filteredItems"
:autocomplete-min-length="0"
@before-adding-tag="beforeTagSave"
@tags-changed="changedTags"
:max-tags="6"
:placeholder="intPlaceholder"
/>
</div>
<template
v-for="(sItem, index) in intSelectedItem"
:key="index"
>
<p class="text-danger m-0">
{{
parseLaravelErrorMessages.methods.parseValidationInput(
`${intName}[${String(index)}][name]`,
intAllErrors
)
}}
</p>
<input
:name="`${intName}[${String(index)}]`"
type="hidden"
:value="sItem.id"
>
</template>
<small class="form-text text-muted">{{ intHelper }}</small>
</div>
<div
v-if="intSpecificItem && intShowSpecificItem"
class="col-12 col-md-6 mb-3"
>
<label
for="shop"
class="col-form-label"
>
<b>{{ intSpecificItem.label }}</b>
</label>
<p class="text-danger m-0">
{{ parseLaravelErrorMessages.methods.parseValidationInput(`${intSpecificItem.name}`, intAllErrors) }}
</p>
<input
class="form-control"
:name="intSpecificItem.name"
type="text"
:placeholder="intSpecificItem.placeholder"
>
<small class="form-text text-muted">{{ intSpecificItem.helper }}</small>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, useAttrs } from "vue";
import type VueTagsInput from "@sipec/vue3-tags-input";
import { VueTagsInput as VueTagsInputCls } from "@sipec/vue3-tags-input";
import slugify from "slugify";
import parseLaravelErrorMessages from "./../modules/parseLaravelErrorMessages";
defineOptions({
name: "SelectMultipleEnumComponent"
});
// * ATTRIBUTES
const attrs = useAttrs();
// * DATA
const intName = ref<string>("");
const intLabel = ref<string>("");
const intHelper = ref<string>("");
const intItem = ref<string>("");
const intSelectedItem = ref<CustomTag[]>([]);
const intAllItems = ref<CustomTag[]>([]);
const intPlaceholder = ref<string>("");
const intItemLimit = ref<number[]>([0, 0]);
const intAllErrors = ref<Record<string, string[]>>({});
const intSpecificItem = ref<{
name: string;
label: string;
placeholder: string;
showId: number;
helper: string;
} | null>(null);
const intShowSpecificItem = ref<boolean>(false);
const intRequired = ref<boolean>(false);
// * LIFECYCLE
onMounted(() => {
const json = String(attrs.json ?? "{}"),
data = JSON.parse(json);
if (!isUsedWithProps.value) {
intName.value = data.name;
intLabel.value = data.label;
intHelper.value = data.helper;
intAllItems.value = parseTags(data.itemsList);
intSelectedItem.value = parseTags(data.itemsSelected);
intPlaceholder.value = data.placeholder;
intItemLimit.value = data.itemLimit;
intAllErrors.value = data.errors;
intSpecificItem.value = data.showSpecificItem;
intRequired.value = data.required;
}
checkSpecificItem();
});
// * COMPUTED
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
const filteredItems = computed(() => {
return intAllItems.value.filter((i) => {
const label = i.label as string;
return label.toLowerCase().indexOf(intItem.value.toLowerCase()) !== -1;
});
});
function parseTags(intSelectedItem: CustomTag[]): CustomTag[] {
return intSelectedItem.map((intItem: CustomTag) => {
if (!intItem.value)
intItem = intAllItems.value
.filter((intItemEnum) => {
return intItemEnum.value === intItem;
})
.shift() as CustomTag;
intItem.id = String(intItem.value);
intItem.text = String(intItem.label);
return intItem;
});
}
/**
* Add item
* @param obj
*/
function beforeTagSave(obj: VueTagsInput.IAddArgs): void {
if (intSelectedItem.value.length < intItemLimit.value[1]) {
const customTag = tagExists(intAllItems.value, obj.tag);
if (customTag) {
obj.tag = customTag as unknown as VueTagsInput.ITag;
intSelectedItem.value.push(customTag);
intItem.value = "";
checkSpecificItem();
return;
}
}
}
/**
* Check if the specific item must be shown.
* @param newItem
*/
function checkSpecificItem(): void {
if (intSpecificItem.value) {
intShowSpecificItem.value = intSelectedItem.value.some((item) => {
return item.value == String(intSpecificItem.value?.showId);
});
}
}
/**
* Delete item.
* @param newItem
*/
function changedTags(newItem: CustomTag[]): void {
intSelectedItem.value = newItem as CustomTag[];
checkSpecificItem();
}
/**
* Verify if the tag exist.
* @param newItem
*/
function tagExists(
items: CustomTag[],
tagToCheck: VueTagsInput.ITag
): CustomTag | null {
let customTag: CustomTag | null = null;
items.filter((intItem: CustomTag) => {
if (
intItem.value === (tagToCheck as unknown as CustomTag).value ||
intItem.label === slugify(tagToCheck.text)
) {
customTag = intItem;
}
});
return customTag;
}
</script>
<style lang="scss">
.select-multiple-enum {
max-width: 100% !important;
.vue-tags-input {
max-width: 100% !important;
}
.ti- {
&autocomplete {
max-height: 10rem !important;
border-radius: 0.375rem;
box-shadow: 0 0 4px var(--bs-gray-400);
overflow-y: auto !important;
ul {
padding: 0.25rem 0 !important;
}
}
&input {
padding: 0.375rem !important;
border-radius: 0.375rem;
}
&tags {
line-height: inherit !important;
}
&tag,
&new-tag-input-wrapper {
padding: 0.1rem !important;
margin: 0.1rem !important;
}
&new-tag-input-wrapper {
font-size: 1em !important;
input {
min-width: 17rem !important;
}
}
&tag {
padding: 0.1rem 0.3rem !important;
background-color: #0d6efd !important;
border-radius: 0.375rem !important;
}
&selected-item {
background-color: var(--bs-gray-300) !important;
color: var(--bs-gray-900) !important;
}
}
}
</style>