Tooltips fix

This commit is contained in:
Jérémy Munsch 2024-02-02 08:47:23 +01:00
parent a1aa2252ba
commit 7720230013
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
8 changed files with 161 additions and 54 deletions

View file

@ -1,5 +1,5 @@
<template>
<div ref="modal" class="modal fade" tabindex="-1">
<div id="product-modal" 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">
@ -32,6 +32,7 @@
:src="`/data/${product.picture}`"
:alt="t('products_logo', {'product_name': transField(product, 'name')})"
:title="t('products_logo', {'product_name': transField(product, 'name')})"
data-bs-toggle="tooltip"
>
</div>
</div>
@ -53,6 +54,7 @@ import DOMPurify from "dompurify";
import type { DirectiveBinding, PropType } from "vue";
import { defineComponent } from "vue";
import ToolboxHelper from "~/classes/ToolboxHelper";
import { Tooltips } from "~/vueModules/bsTooltips";
const saneHtml = {
mounted: (el: HTMLElement, binding: DirectiveBinding) => {
@ -85,19 +87,21 @@ export default defineComponent({
"closed", "openned"
],
data(): {
modal: Modal;
modal: Modal|null;
tooltips: Tooltips|null;
} {
return {
modal: {} as Modal
modal: null,
tooltips: null
};
},
watch: {
show() {
this.$nextTick(() => {
if(this.show) {
this.modal.show();
} else if(this.modal.hide) {
this.modal.hide();
this.modal?.show();
} else {
this.modal?.hide();
}
});
}
@ -106,18 +110,19 @@ export default defineComponent({
this.$nextTick(() => {
const modal = this.$refs.modal as HTMLElement;
modal.addEventListener("shown.bs.modal", () => {
this.tooltips = Tooltips.make({type:"parent", parentSelector: "#product-modal"});
this.$emit("openned");
});
modal.addEventListener("hidden.bs.modal", () => {
this.tooltips?.closeBootstrapTooltip();
this.$emit("closed");
});
this.modal = Modal.getOrCreateInstance(modal);
});
},
beforeUnmount() {
if(this.modal.hide) {
this.modal.hide();
}
this.tooltips?.closeBootstrapTooltip();
this.modal?.hide();
},
methods: {
t(text: string, params: Record<string,string> = {}, localeToUse: string|null = null): string {

View file

@ -1,5 +1,5 @@
<template>
<div class="container justify-content-center d-flex flex-column">
<div id="product-list-component" class="container justify-content-center d-flex flex-column">
<div v-if="range.products" ref="rangeList" class="row justify-content-around">
<div
v-for="(productPair, productIndex) of chunkInefficient(range.products, 2)"
@ -17,6 +17,7 @@
<button
class="d-inline-block btn bg-body position-relative"
:title="t('more_about_this_product')"
data-bs-toggle="tooltip"
@click.stop="showProduct(product, $event)"
>
<div class="container-fluid">
@ -26,7 +27,6 @@
class="img-thumbnail"
:src="`/data/${product.picture}`"
:alt="t('products_logo', {'product_name': transField(product, 'name')})"
:title="t('products_logo', {'product_name': transField(product, 'name')})"
>
</div>
</div>
@ -69,7 +69,7 @@ import type { DirectiveBinding } from "vue";
import { defineComponent } from "vue";
import { useRoute, type RouteLocationNormalizedLoaded } from "vue-router";
import ToolboxHelper from "~/classes/ToolboxHelper";
import bsTooltips from "~/vueModules/bsTooltips";
import { Tooltips } from "~/vueModules/bsTooltips";
const saneHtml = {
mounted: (el: HTMLElement, binding: DirectiveBinding) => {
@ -97,10 +97,12 @@ export default defineComponent({
data(): {
route: RouteLocationNormalizedLoaded;
displayShelves: boolean;
tooltips: Tooltips|null;
} {
return {
route: useRoute(),
displayShelves: this.range.shelf
displayShelves: this.range.shelf,
tooltips: null
};
},
watch: {
@ -109,9 +111,12 @@ export default defineComponent({
}
},
mounted() {
this.$nextTick(bsTooltips.methods.initTooltips);
this.$nextTick(() => {this.tooltips = Tooltips.make({type:"parent", parentSelector: "#product-list-component"});});
this.$nextTick(this.applySameHeight);
},
beforeUnmount() {
this.tooltips?.closeBootstrapTooltip();
},
methods: {
chunkInefficient(products: Array<TypeProduct>, chunkSize: number):Array<Array<TypeProduct>> {
const out:Array<Array<TypeProduct>> = [];
@ -144,6 +149,7 @@ export default defineComponent({
return Object.fromEntries(out.entries());
},
showProduct(product: TypeProduct, $event: Event) {
this.tooltips?.refreshTooltips();
const el = $event.target as HTMLButtonElement;
el.scrollIntoView({
behavior: "smooth",

View file

@ -81,7 +81,7 @@ import { useRoute, type RouteLocationNormalizedLoaded } from "vue-router";
import ToolboxHelper from "~/classes/ToolboxHelper";
import type { appInfo } from "~/electron/main";
import { useAppStore } from "~/stores/useAppStore";
import bsTooltips from "~/vueModules/bsTooltips";
import { Tooltips } from "~/vueModules/bsTooltips";
export default defineComponent({
name: "AppFooter",
@ -108,6 +108,7 @@ export default defineComponent({
modules: Array<{
name: string;
}>;
tooltips: Tooltips|null;
} {
return {
locale: "",
@ -121,6 +122,7 @@ export default defineComponent({
settings: {} as TypeSettings,
brands: [],
modules: [Scrollbar],
tooltips: null
};
},
computed: {
@ -147,7 +149,10 @@ export default defineComponent({
this.locale = locale;
},
mounted() {
this.$nextTick(() => bsTooltips.methods.initTooltips());
this.$nextTick(() => {this.tooltips = Tooltips.make({type:"parent", parentSelector: "#footer"});});
},
beforeUnmount() {
this.tooltips?.closeBootstrapTooltip();
},
methods: {
t(text: string, params: Record<string,string> = {}, localeToUse: string|null = null): string {

View file

@ -97,7 +97,7 @@ import { defineComponent } from "vue";
import type { RouteLocationNormalizedLoaded } from "vue-router";
import ToolboxHelper from "~/classes/ToolboxHelper";
import { useAppStore } from "~/stores/useAppStore";
import bsTooltips from "~/vueModules/bsTooltips";
import { Tooltips } from "~/vueModules/bsTooltips";
type LocaleParams = {
iso: string,
@ -125,6 +125,7 @@ export default defineComponent({
locales: Array<LocaleParams>,
locale: string;
route: RouteLocationNormalizedLoaded;
tooltips: Tooltips|null;
} {
return {
versions: window.versions,
@ -132,6 +133,7 @@ export default defineComponent({
locales: [],
locale: "",
route: useRoute(),
tooltips: null
};
},
computed: {
@ -167,7 +169,10 @@ export default defineComponent({
});
},
mounted() {
this.$nextTick(() => bsTooltips.methods.initTooltips());
this.$nextTick(() => {this.tooltips = Tooltips.make({type:"parent", parentSelector: "#top-navbar"});});
},
beforeUnmount() {
this.tooltips?.closeBootstrapTooltip();
},
methods: {
t(text: string, params: Record<string,string> = {}, localeToUse: string|null = null): string {

View file

@ -70,7 +70,7 @@ import type AppState from "~/classes/AppState";
import Toast, { ToastTypeEnum } from "~/classes/Toast";
import ToolboxHelper from "~/classes/ToolboxHelper";
import { useAppStore } from "~/stores/useAppStore";
import bsTooltips from "~/vueModules/bsTooltips";
import { Tooltips } from "~/vueModules/bsTooltips";
export default defineComponent({
name: "BrandShow",
@ -82,6 +82,7 @@ export default defineComponent({
animateIndex: number|null;
animations: Array<string>;
displayShelves: boolean;
tooltips: Tooltips|null;
} {
return {
appState: {} as AppState,
@ -90,7 +91,8 @@ export default defineComponent({
animateTimeout: undefined,
animateIndex: null,
animations: ["heartBeat", "tada", "shakeX", "rubberBand"],
displayShelves: false
displayShelves: false,
tooltips: null
};
},
beforeMount() {
@ -113,7 +115,7 @@ export default defineComponent({
}
},
mounted() {
this.$nextTick(() => bsTooltips.methods.initTooltips());
this.$nextTick(() => {this.tooltips = Tooltips.make({type:"parent", parentSelector: ".page-brands-show"});});
// * ANIMATIONS
clearTimeout(this.animateTimeout);
@ -130,6 +132,7 @@ export default defineComponent({
});
},
beforeUnmount() {
this.tooltips?.closeBootstrapTooltip();
clearTimeout(this.animateTimeout);
},
methods: {

View file

@ -38,10 +38,6 @@
'color': getContrastYIQ(brand.color),
'background-color': brand.color
}"
role="button"
:title="t('view_brand_ranges', {'brand_name': transField(brand, 'name')})"
data-bs-toggle="tooltip"
@click="gotoBrand(brand)"
>
{{ transField(brand, 'name') }}
</span>
@ -62,7 +58,7 @@ import { defineComponent } from "vue";
import type AppState from "~/classes/AppState";
import ToolboxHelper from "~/classes/ToolboxHelper";
import { useAppStore } from "~/stores/useAppStore";
import bsTooltips from "~/vueModules/bsTooltips";
import { Tooltips } from "~/vueModules/bsTooltips";
export default defineComponent({
name: "HomePage",
@ -71,16 +67,18 @@ export default defineComponent({
animateTimeout: NodeJS.Timeout|undefined;
animateIndex: number|null;
animations: Array<string>;
tooltips: Tooltips|null;
} {
return {
appState: {} as AppState,
animateTimeout: undefined,
animateIndex: null,
animations: ["heartBeat", "tada", "shakeX", "rubberBand"]
animations: ["heartBeat", "tada", "shakeX", "rubberBand"],
tooltips: null
};
},
mounted() {
this.$nextTick(() => bsTooltips.methods.initTooltips());
this.$nextTick(() => {this.tooltips = Tooltips.make({type:"parent", parentSelector: ".page-home"});});
this.appState = useAppStore().data;
// * ANIMATIONS
@ -98,6 +96,7 @@ export default defineComponent({
});
},
beforeUnmount() {
this.tooltips?.closeBootstrapTooltip();
clearTimeout(this.animateTimeout);
},
methods: {

View file

@ -21,7 +21,6 @@ import Toast, { ToastTypeEnum } from "~/classes/Toast";
import ToolboxHelper from "~/classes/ToolboxHelper";
import ProductDescriptionModalVue from "~/components/ProductDescriptionModal.vue";
import ProductListVue from "~/components/ProductList.vue";
import bsTooltips from "~/vueModules/bsTooltips";
const saneHtml = {
mounted: (el: HTMLElement, binding: DirectiveBinding) => {
@ -56,9 +55,6 @@ export default defineComponent({
}
this.range = range;
},
mounted() {
this.$nextTick(() => bsTooltips.methods.initTooltips());
},
methods: {
t(text: string, params: Record<string, string> = {}, localeToUse: string | null = null): string {
return ToolboxHelper.trans(text, params, localeToUse);

View file

@ -1,26 +1,114 @@
import { Tooltip } from "bootstrap";
let tooltips: Array<Tooltip> = [];
export default {
methods: {
initTooltips() {
// Disable tooltips on reboot.
tooltips.forEach((tooltip) => {
tooltip.disable();
tooltip.hide();
});
tooltips = [].slice
.call(document.querySelectorAll("[data-bs-toggle=\"tooltip\"]"))
.map(function (tooltipTriggerEl) {
return new Tooltip(tooltipTriggerEl, {
customClass: "large-tooltip",
delay: {
show: 1250,
hide: 0,
},
});
});
type ConstructorTypeParent = "parent";
type ConstructorTypeDom = "dom";
interface ParentSelectorTooltips {
type: ConstructorTypeParent
/** The parent element that contains Dom elements for which tooltips shall be created. */
parentSelector: string;
}
interface DomProvidedTooltips {
type: ConstructorTypeDom;
/** Dom elements for which tooltips shall be created. */
elements: HTMLElement | NodeListOf<HTMLElement>;
}
interface TooltipsOptions {
/** Tooltip options. */
options?: Partial<Tooltip.Options>|null|undefined;
}
export type TooltipsConstructor = TooltipsOptions & (ParentSelectorTooltips|DomProvidedTooltips);
export class Tooltips {
/** BS tooltips instances */
private tooltips: Array<Tooltip> = [];
/** The parent element that contains Dom elements for which tooltips shall be created. */
private parentSelector: string | null = null;
/** Dom elements for which tooltips shall be created. */
private elements: Array<HTMLElement> = [];
/** Tooltip default options. */
private static defaultOptions: Partial<Tooltip.Options> = {
delay: {
show: 1250,
hide: 0,
},
},
};
};
/** Tooltip options. */
private options: Partial<Tooltip.Options> = {};
/**
* @param parentSelector
* @param options Tooltip options.
*/
constructor(params: TooltipsConstructor) {
if (params.type === "parent") {
this.parentSelectorConstructor(params);
}
if (params.type === "dom") {
this.domPrividedTooltipsContructor(params);
}
}
public static make(params: TooltipsConstructor = { type: "parent", parentSelector: "body"}
) {
const fullParams = { ...{ type: "parent", options: {} }, ...params };
return new Tooltips(fullParams);
}
private parentSelectorConstructor({ parentSelector, options }: TooltipsOptions & ParentSelectorTooltips) {
this.parentSelector = parentSelector;
this.options = { ...Tooltips.defaultOptions, ...options };
this.initTooltips();
}
private domPrividedTooltipsContructor({ elements, options }: TooltipsOptions & DomProvidedTooltips) {
if (elements instanceof HTMLElement) {
this.elements = [elements];
} else {
this.elements = Array.from(elements);
}
this.options = { ...Tooltips.defaultOptions, ...options };
this.initTooltips();
}
/**
* Close all Bootstrap tooltips and reinstanciate them.
*/
public refreshTooltips(): void {
this.closeBootstrapTooltip();
this.initTooltips();
}
/**
* Close all Bootstrap tooltips for ever.
*/
public closeBootstrapTooltip(): void {
this.tooltips.forEach((tooltip) => {
tooltip.hide();
// * Prevent pending tooltips beeing shown after we asked to hide them
tooltip.disable();
});
}
/**
* Initialize tooltips
*/
private initTooltips() {
this.closeBootstrapTooltip();
/** data-bs-toggle tooltips elements */
const elements = this.parentSelector ? [].slice
.call(document.querySelectorAll(`${this.parentSelector} [data-bs-toggle="tooltip"]`)) :
this.elements;
this.tooltips = elements.map((tooltipTriggerEl) => {
return new Tooltip(tooltipTriggerEl, this.options);
});
}
}