import Subscriber from "@supersoniks/concorde/core/mixins/Subscriber"; import TemplatesContainer from "@supersoniks/concorde/core/mixins/TemplatesContainer"; import Format from "@supersoniks/concorde/core/utils/Format"; import HTML, { detecHTMLLanguageChange, } from "@supersoniks/concorde/core/utils/HTML"; import { LitElement, nothing, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators.js"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; const tagName = "sonic-date"; type DateTimeFormatPartExtended = { hidden?: boolean; source?: string; type: string; value: string; }; type DateTimeFormatExtened = Intl.DateTimeFormat & { formatRangeToParts: ( startDate: Date, endDate: Date ) => DateTimeFormatPartExtended[]; }; detecHTMLLanguageChange(() => SonicDate.updateComponentsLanguage()); @customElement(tagName) export class SonicDate extends Subscriber(TemplatesContainer(LitElement)) { static dateCompnents: Set = new Set(); static updateComponentsLanguage() { SonicDate.dateCompnents.forEach( (component) => (component.pageLanguage = HTML.getLanguage()) ); } pageLanguage = "fr"; private duAu: string[] = []; private _wording_billet_periode_validite = ""; @property() get wording_billet_periode_validite(): string { return this._wording_billet_periode_validite; } set wording_billet_periode_validite(value: string) { if (!value) value = "Du %s au %s"; this._wording_billet_periode_validite = value; this.duAu = this.wording_billet_periode_validite ?.split("%s") .map((str) => str.trim()); this.duAu.pop(); this.requestUpdate(); } @property({ type: Boolean }) designMode: boolean | null = null; @property({ type: String }) time_zone: string | null = null; @property({ type: Number }) date: number | null = null; @property({ type: String }) date_string: string | null = null; @property({ type: String }) start_date_string: string | null = null; @property({ type: String }) end_date_string: string | null = null; @property({ type: Number }) start_date = 0; computedStartDate = 0; @property({ type: Number }) end_date = 0; computedEndDate = 0; @property({ type: Boolean }) hide_hours = false; @property({ type: String }) era: "narrow" | "short" | "long" | "" = ""; @property({ type: String }) year: "numeric" | "2-digit" = "numeric"; @property({ type: String }) month: | "numeric" | "2-digit" | "narrow" | "short" | "long" = "short"; @property({ type: String }) day: "numeric" | "2-digit" = "2-digit"; @property({ type: String }) weekday: "narrow" | "short" | "long" | "hidden" = "short"; @property({ type: String }) hour: "numeric" | "2-digit" | "hidden" = "2-digit"; @property({ type: Boolean }) hour12 = false; @property({ type: String }) minute: "numeric" | "2-digit" | "hidden" = "2-digit"; @property({ type: String }) language = ""; @property({ type: Boolean }) renderIf = true; @property({ type: Boolean }) now = false; // @property({type:String}) second: 'numeric' | '2-digit'= "2-digit" // @property({ type: String }) hour12: boolean = false; private startDateObject = new Date(); private endDateObject = new Date(); connectedCallback(): void { if (!this.hasAttribute("wording_billet_periode_validite")) this.wording_billet_periode_validite = "Du %s au %s"; this.pageLanguage = HTML.getLanguage(); SonicDate.dateCompnents.add(this); super.connectedCallback(); } disconnectedCallback(): void { super.disconnectedCallback(); SonicDate.dateCompnents.delete(this); } /** * Retourne un tableau des différentes parties de la date en fonction des options données, de la date de début et de fin. */ getDatesParts( start_date: number, end_date: number, options: Intl.DateTimeFormatOptions ) { const start = this.startDateObject; start.setTime(start_date * 1000); let parts: DateTimeFormatPartExtended[] = []; if (end_date > 0) { const end = this.endDateObject; end.setTime(end_date * 1000); // //on affiche pas l'heure si les dates sont les mêmes const isSameDay = start.toDateString() == end.toDateString(); if (!isSameDay || this.hide_hours) { delete options.hour; delete options.minute; } const format: DateTimeFormatExtened = new Intl.DateTimeFormat( this.language || this.pageLanguage, options ) as DateTimeFormatExtened; parts = format.formatRangeToParts(start, end); //on affiche du au uniquement si on a deux dates différentes if (!isSameDay) { const to = parts.find( (part) => part.type == "literal" && part.source == "shared" && part.value.trim().length > 0 ); if (to) { to.value = " " + this.duAu[1] + " "; to.type = "to"; } if (!this.designMode) parts.unshift({ type: "from", value: this.duAu[0] + " ", source: "shared", }); } } else { const format: Intl.DateTimeFormat = new Intl.DateTimeFormat( this.language || this.pageLanguage, options ); parts = format.formatToParts(start); } //En mode design on cache les "," if (this.designMode) { // parts.forEach((part) => (part.hidden = part.value.trim() == ",")); // replace all "," by " " parts.forEach((part) => (part.value = part.value.replace(/,/g, " "))); } parts[0].value = Format.ucFirst(parts[0].value); return parts.filter((p: DateTimeFormatPartExtended) => p.hidden !== true); } dateStringToSeconds(dateString: string) { return new Date(dateString).getTime() / 1000; } protected willUpdate(_changedProperties: PropertyValues): void { /* * * Normalisation en fonction des valeurs de dates passées * * */ this.computedStartDate = 0; this.computedEndDate = 0; if (this.date_string) this.date = this.dateStringToSeconds(this.date_string); if (this.date) this.computedStartDate = this.date; if (this.start_date_string) this.computedStartDate = this.dateStringToSeconds(this.start_date_string); if (this.end_date_string) this.end_date = this.dateStringToSeconds(this.end_date_string); if (this.start_date) this.computedStartDate = this.start_date; if (!this.computedStartDate && !this.now && !this.end_date) return; if (!this.computedStartDate) this.computedStartDate = Date.now() / 1000; if (this.end_date) this.computedEndDate = this.end_date; if ( this.computedEndDate > 0 && this.computedEndDate < this.computedStartDate ) { const copy = this.computedStartDate; this.computedStartDate = this.computedEndDate; this.computedEndDate = copy; } } render() { if (!this.renderIf) return nothing; if (!this.computedStartDate && !this.now && !this.computedEndDate) return nothing; /* * * gestion des options d'affichage des dates * */ const options: Intl.DateTimeFormatOptions = { year: this.year || "numeric", month: this.month || "short", day: this.day || "2-digit", hour12: this.hour12 || false, }; if (this.weekday !== "hidden") options.weekday = this.weekday || "short"; if (this.hour !== "hidden") options.hour = this.hour || "2-digit"; if (this.minute !== "hidden") options.minute = this.minute || "2-digit"; if (this.era) options.era = this.era; if (this.time_zone) options.timeZone = this.time_zone; /** * On récupère les différentes partie affichées en vu de l'injection dans le template */ const parts = this.getDatesParts( this.computedStartDate, this.computedEndDate, options ); return unsafeHTML( `${parts .map((part: DateTimeFormatPartExtended) => { const template = this.templateParts[part.type]; if (template) { const clone = document.importNode(template.content, true); const child: HTMLElement = clone.children[0] as HTMLElement; if (child.innerText.trim() == "") child.innerText = part.value; return child.outerHTML; } const span = document.createElement("span"); span.innerText = part.value; span.className = part.type; return `${part.value}`; }) .join("")}` ); } }