ldc-showroom/components/ProductDescriptionModal.vue

185 lines
4.9 KiB
Vue

<template>
<div ref="modal" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-xxl modal-fullscreen-md-down">
<div class="modal-content">
<div class="modal-header position-relative">
<h5
v-if="product"
:class="{ ...{
'product-title': true,
'fw-bold text-capitalize text-center rounded mt-4 py-3 px-4 flex-grow-1 mx-5 px-5': true,
'fs-1 mt-2 mt-md-5': true,
}, ... rangeFont(range.font_id)}"
>
{{ transField(product, 'name') }}
</h5>
<button
type="button"
class="btn-close me-4 position-absolute end-0 top-0"
:aria-label="t('close_products_modal')"
:title="t('close_products_modal')"
data-bs-toggle="tooltip"
data-bs-dismiss="modal"
/>
</div>
<div class="modal-body">
<div class="row">
<div class="col text-center">
<img
v-if="product"
class="img-fluid"
:src="`/data/${product.picture}`"
:alt="t('products_logo', {'product_name': transField(product, 'name')})"
:title="t('products_logo', {'product_name': transField(product, 'name')})"
>
</div>
</div>
<div class="row">
<div class="col pt-5 ps-md-4 description">
<div v-if="product" v-sane-html="transField(product, 'description')" class="p-md-3 my-md-5" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Modal } from "bootstrap";
import DOMPurify from "dompurify";
import type { DirectiveBinding, PropType } from "vue";
import { defineComponent } from "vue";
import ToolboxHelper from "~/classes/ToolboxHelper";
const saneHtml = {
mounted: (el: HTMLElement, binding: DirectiveBinding) => {
el.innerHTML = DOMPurify.sanitize(binding.value);
},
};
export default defineComponent({
name: "ProductDescriptionModal",
directives: {
saneHtml,
},
props: {
show: {
type: Boolean,
required: true,
},
range: {
type: Object as PropType<TypeRange>,
required: true,
},
product: {
type: Object as PropType<TypeProduct>|null,
nullable: true,
default: undefined,
required: false,
}
},
emits: [
"closed", "openned"
],
data(): {
modal: Modal;
} {
return {
modal: {} as Modal
};
},
watch: {
show() {
this.$nextTick(() => {
if(this.show) {
this.modal.show();
} else if(this.modal.hide) {
this.modal.hide();
}
});
}
},
mounted() {
this.$nextTick(() => {
const modal = this.$refs.modal as HTMLElement;
modal.addEventListener("shown.bs.modal", () => {
this.$emit("openned");
});
modal.addEventListener("hidden.bs.modal", () => {
this.$emit("closed");
});
this.modal = Modal.getOrCreateInstance(modal);
});
},
beforeUnmount() {
if(this.modal.hide) {
this.modal.hide();
}
},
methods: {
t(text: string, params: Record<string,string> = {}, localeToUse: string|null = null): string {
return ToolboxHelper.trans(text, params, localeToUse);
},
transField(model: Typei18nModel, field: Typei18nFieldList) {
return ToolboxHelper.getTranslatableField(model, field);
},
rangeFont(fontId: number) {
const font = useAppStore().getFont(fontId);
const out: Map<string, boolean> = new Map<string, boolean>();
if(font) {
out.set(`f-${font.font_name}`, true);
}
return Object.fromEntries(out.entries());
},
},
});
</script>
<style lang="scss" scoped>
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/vendor/rfs";
.btn-close {
@include media-breakpoint-up(md) {
@include rfs(1.5em, padding-bottom);
@include rfs(1.3em, font-size);
}
@include media-breakpoint-up(xxl) {
@include rfs(2em, padding-bottom);
@include rfs(1.5em, font-size);
}
}
.product-title {
@include rfs(1.4rem, font-size);
@include rfs(1rem, padding-bottom);
@include media-breakpoint-up(md) {
@include rfs(1.8rem, font-size);
@include rfs(3rem, padding-bottom);
}
@include media-breakpoint-up(xxl) {
@include rfs(2.8rem, font-size);
@include rfs(6rem, padding-bottom);
}
}
.description {
@include rfs(1em, font-size);
@include media-breakpoint-up(md) {
@include rfs(1.1em, font-size);
@include rfs(1.1em, padding-bottom);
}
@include media-breakpoint-up(xxl) {
@include rfs(1.15em, font-size);
@include rfs(1.35em, padding-bottom);
}
}
</style>