lifecycle-timeline 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,9 @@ A premium, interactive Vanilla JS component for visualizing product lifecycles,
27
27
  npm install lifecycle-timeline
28
28
  ```
29
29
 
30
+ > [!TIP]
31
+ > Using Angular? Check out our [Angular Integration Guide](ANGULAR_INTEGRATION.md).
32
+
30
33
  ### Manual Installation
31
34
  Download the files from the `dist` folder: `timeline.js` (ESM), `timeline.umd.cjs` (UMD), and `timeline.css`.
32
35
 
@@ -0,0 +1 @@
1
+ .lt-root{--bg-page: #f9fafb;--bg-card: #ffffff;--bg-track: #e5e7eb;--text-primary: #1f2937;--text-secondary: #6b7280;--text-inverse: #ffffff;--border-color: #e5e7eb;--line-color: #e5e7eb;--grid-line: #9ca3af;--accent-oss: #99e67d;--accent-ent: #ffe88e;--current-date: #086dc3;--font-family: "Inter", system-ui, -apple-system, sans-serif;--tooltip-bg: #ffffff;--tooltip-shadow: rgba(0, 0, 0, .1);transition:background-color .3s ease,color .3s ease;background-color:var(--bg-card);color:var(--text-primary);font-family:var(--font-family);padding:24px;border-radius:16px;overflow:hidden;border:1px solid var(--border-color)}.lt-root[data-theme=dark]{--bg-page: #111827;--bg-card: #1f2937;--bg-track: #374151;--text-primary: #f9fafb;--text-secondary: #9ca3af;--border-color: #374151;--line-color: #374151;--grid-line: #6b7280;--tooltip-bg: #1f2937;--tooltip-shadow: rgba(0, 0, 0, .5);--accent-oss: #78bd5d;--accent-ent: #e3c456}.lt-wrapper{position:relative;padding-top:40px;padding-bottom:60px;overflow-x:auto;scrollbar-width:thin}.lt-header{display:flex;position:sticky;left:0;margin-bottom:20px}.lt-tracks{position:relative;padding-top:20px}.lt-axis{display:flex;border-bottom:1px solid var(--border-color);margin-bottom:10px;padding-bottom:8px;position:relative}.lt-year{flex:1;font-size:.875rem;font-weight:600;color:var(--text-secondary);border-left:1px dashed var(--border-color);padding-left:0;text-align:center;box-sizing:border-box}.lt-year:first-child{border-left:none}.lt-row{display:flex;align-items:center;margin-bottom:1.5rem;position:relative;height:36px;z-index:1;opacity:0;transform:translateY(10px);transition:opacity .4s ease,transform .4s ease}.lt-row.lt-row-visible{opacity:1;transform:translateY(0)}.lt-version-label{width:80px;min-width:80px;box-sizing:border-box;padding:4px;border-radius:12px;flex-shrink:0;font-weight:600;font-size:.85rem;color:var(--text-primary);margin-right:16px;text-align:center;background-color:transparent;cursor:pointer;transition:all .3s ease;position:sticky;left:0;z-index:10}.lt-version-label:hover{background-color:var(--bg-track);transform:scale(1.05);box-shadow:0 4px 12px #0000001a}.lt-version-label a.lt-version-link{text-decoration:none;color:inherit;display:block;width:100%;height:100%}.lt-version-label a.lt-version-link:hover{text-decoration:underline;opacity:.8}.lt-status-oss{background-color:var(--accent-oss);color:#000}.lt-status-ent{background-color:var(--accent-ent);color:#000}.lt-status-expired{background-color:var(--bg-track);color:var(--text-secondary)}.lt-track-container{flex-grow:1;position:relative;height:100%;background:var(--bg-track);border-radius:0;overflow:hidden;transition:background-color .3s ease}.lt-bar-segment{position:absolute;height:100%;top:0;transition:opacity .3s ease;display:flex;align-items:center;justify-content:center;color:#fff;font-size:.75rem;font-weight:500;overflow:hidden;white-space:nowrap;box-shadow:0 1px 2px #0000001a}.lt-bar-oss{background:linear-gradient(to bottom,#dcfce7 0%,var(--accent-oss) 100%);z-index:2;border-radius:0;height:66%;top:auto;bottom:0}[data-theme=dark] .lt-bar-oss{background:linear-gradient(to bottom,#a7f3d0 0%,var(--accent-oss) 100%)}.lt-bar-ent{background:linear-gradient(to bottom,#fef9c3 0%,var(--accent-ent) 100%);z-index:1;border-radius:0;opacity:.9}.lt-grid-lines-container{position:absolute;inset:0 0 0 96px;pointer-events:none;z-index:5}.lt-indicators-container{position:absolute;inset:0 0 0 96px;pointer-events:none;z-index:20}.lt-current-date-indicator{position:absolute;top:-30px;bottom:0;width:3px;background-image:linear-gradient(to bottom,var(--current-date) 60%,transparent 60%);background-size:3px 20px;background-repeat:repeat-y;border-left:none;z-index:20;pointer-events:none}@keyframes pulse-blue{0%{box-shadow:0 0 #086dc3b3}70%{box-shadow:0 0 0 6px #086dc300}to{box-shadow:0 0 #086dc300}}.lt-current-date-badge{position:absolute;top:100%;margin-top:8px;left:50%;transform:translate(-50%);background-color:var(--current-date);color:#fff;padding:4px 12px;border-radius:12px;font-size:.75rem;font-weight:600;white-space:nowrap;box-shadow:0 2px 4px #086dc333;z-index:25;animation:pulse-blue 2s infinite}.lt-year-grid-line{position:absolute;top:0;bottom:0;border-left:2px solid var(--grid-line);opacity:1;z-index:5;pointer-events:none}.lt-tooltip{position:absolute;background:var(--tooltip-bg);color:var(--text-primary);padding:10px 14px;border-radius:8px;font-size:.85rem;box-shadow:0 10px 15px -3px var(--tooltip-shadow),0 4px 6px -4px var(--tooltip-shadow);border:1px solid var(--border-color);pointer-events:none;z-index:10000;transition:opacity .15s ease-out;line-height:1.4}.lt-tooltip-header{font-weight:700;margin-bottom:4px;color:var(--current-date);text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}.lt-tooltip-date strong{color:var(--text-secondary)}.lt-row{transition:opacity .3s ease,transform .3s ease,height .3s ease}.lt-row[style*="display: none"]{opacity:0;transform:translateY(10px)}.lt-bar-segment{transition:transform .2s ease,box-shadow .2s ease}.lt-bar-segment:hover{box-shadow:0 4px 12px #0000001a}.lt-bar-segment:focus-visible{outline:2px solid var(--text-primary);outline-offset:-2px;z-index:100}.lt-track-container{transition:background-color .3s ease}.lt-legend{margin:40px auto 0;padding:24px;background-color:var(--bg-card);border:1px solid var(--border-color);border-radius:12px;display:flex;flex-direction:column;gap:16px;max-width:800px}.lt-legend-block{display:flex;align-items:flex-start;gap:12px}.lt-legend-icon{width:20px;height:20px;border-radius:4px;flex-shrink:0;margin-top:2px}.lt-legend-block h3{margin:0 0 4px;font-size:1rem;font-weight:700;color:var(--text-primary)}.lt-legend-block p{margin:0;font-size:.9rem;color:var(--text-secondary);line-height:1.5}.lt-legend-block.oss .lt-legend-icon{background-color:var(--accent-oss)}.lt-legend-block.commercial .lt-legend-icon{background-color:var(--accent-ent)}.lt-legend-block.eol .lt-legend-icon{background-color:var(--bg-track)}.lt-toolbar{display:flex;justify-content:flex-start;align-items:center;margin-bottom:24px;gap:16px}.lt-filter-container{display:flex;align-items:center;gap:12px;background:var(--bg-card);border:1px solid var(--border-color);padding:8px 16px;border-radius:24px;max-width:400px;flex-grow:1;transition:all .3s ease;box-shadow:0 2px 4px #0000000d}.lt-filter-container:focus-within{border-color:var(--current-date);box-shadow:0 8px 20px #086dc31a}.lt-filter-input{border:none;outline:none;font-size:.95rem;color:var(--text-primary);background:transparent;width:100%}.lt-highlight-match{background-color:#fde68a;color:#92400e;padding:0 2px;border-radius:2px;font-weight:700}.lt-legend{display:flex;flex-wrap:wrap;gap:1.5rem;margin-top:2rem;padding-top:1.5rem;border-top:1px solid var(--border-color)}.lt-legend-item-reactive{cursor:pointer;transition:all .2s ease;padding:8px;border-radius:8px;border:1px solid transparent}.lt-legend-item-reactive:hover{background-color:var(--bg-track);transform:translateY(-2px)}.lt-legend-item-reactive.lt-active-highlight{background-color:var(--bg-track);border-color:var(--grid-line);box-shadow:0 2px 4px #0000001a}.lt-bar-segment,.lt-version-label{transition:opacity .3s ease,filter .3s ease}[class*=lt-highlight-oss] .lt-bar-segment:not(.lt-segment-oss),[class*=lt-highlight-oss] .lt-version-label:not(.lt-status-oss){opacity:.3;filter:grayscale(.5)}[class*=lt-highlight-ent] .lt-bar-segment:not(.lt-segment-ent),[class*=lt-highlight-ent] .lt-version-label:not(.lt-status-ent){opacity:.3;filter:grayscale(.5)}[class*=lt-highlight-eol] .lt-bar-segment,[class*=lt-highlight-eol] .lt-version-label:not(.lt-status-expired){opacity:.3;filter:grayscale(.5)}.lt-toggle-btn{display:flex;align-items:center;gap:8px;background:var(--bg-card);border:1px solid var(--border-color);padding:8px 16px;border-radius:20px;cursor:pointer;font-size:.875rem;font-weight:500;color:var(--text-primary);box-shadow:0 1px 2px #0000000d;transition:all .2s ease}.lt-toggle-btn:hover{background:var(--bg-page);border-color:var(--text-secondary);transform:translateY(-1px)}.lt-toggle-btn:focus-visible{outline:2px solid var(--current-date);outline-offset:2px}.lt-theme-toggle-btn{position:absolute;top:24px;right:24px;background:var(--bg-card);border:1px solid var(--border-color);color:var(--text-primary);width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s ease;z-index:1000;box-shadow:0 4px 6px -1px #0000001a}.lt-theme-toggle-btn:focus-visible{outline:2px solid var(--current-date);outline-offset:2px}.lt-root{position:relative}.lt-theme-toggle-btn:hover{background-color:var(--bg-track);transform:scale(1.1)}@media(max-width:1024px){.lt-wrapper{padding-left:0}}@media(max-width:768px){.page-container{padding:1rem}.lt-toolbar{flex-direction:column;align-items:stretch}.lt-filter-container{max-width:none}}.lt-row,.lt-table tr{transition:opacity .4s ease,transform .4s cubic-bezier(.4,0,.2,1)}.lt-row-hidden{display:none!important;opacity:0;transform:translateY(-10px)}.lt-row-visible{display:flex!important;opacity:1;transform:translateY(0)}.lt-table tr.lt-row-visible{display:table-row!important}.lt-tracks,.lt-table tbody{transition:max-height .5s ease-in-out}.lt-table-container{margin-top:1rem;margin-bottom:2rem;overflow-x:auto;border-radius:12px;border:1px solid var(--border-color);background-color:var(--bg-card);transition:all .3s ease}.lt-table{width:100%;border-collapse:collapse;font-size:.85rem;text-align:left}.lt-table th{background-color:var(--bg-page);color:var(--text-secondary);font-weight:700;padding:14px 16px;border-bottom:1px solid var(--border-color);text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}.lt-table td{padding:12px 16px;border-bottom:1px solid var(--border-color);color:var(--text-primary);vertical-align:middle}.lt-table tr:hover td{background-color:var(--bg-page)}.lt-table tr:last-child td{border-bottom:none}.lt-table-badge{display:inline-block;padding:4px 12px;border-radius:12px;font-size:.85rem;font-weight:600;font-family:var(--font-family);text-align:center;min-width:64px;transition:all .3s ease}.lt-table-version-link{text-decoration:none;color:inherit;display:inline-block}.lt-table-version-link:hover .lt-table-badge{transform:translateY(-1px);box-shadow:0 4px 8px #0000001a}.lt-table-badge.status-oss{background-color:var(--accent-oss);color:#000}.lt-table-badge.status-ent{background-color:var(--accent-ent);color:#000}.lt-table-badge.status-expired{background-color:var(--bg-track);color:var(--text-secondary)}.lt-past-date{color:var(--text-secondary);opacity:.7;font-style:italic}.lt-table-toggle{display:flex;justify-content:center;padding:10px;background-color:var(--bg-card);border-top:1px solid var(--border-color);border-bottom-left-radius:12px;margin-top:40px}.risk-high{color:#ef4444}.risk-medium{color:#f59e0b}.risk-low{color:#10b981}[data-theme=dark] .details-header{background-color:#0f172a}
package/dist/timeline.js CHANGED
@@ -1,4 +1,4 @@
1
- const u = {
1
+ const p = {
2
2
  en: {
3
3
  filter: "Filter versions...",
4
4
  oss: "OSS Support",
@@ -46,8 +46,8 @@ class v {
46
46
  * @param {string} [options.locale] - Language code (e.g., 'en', 'fr').
47
47
  * @param {Object} [options.i18n] - Custom translations to merge or override.
48
48
  */
49
- constructor(t, e, s = {}) {
50
- this.root = document.getElementById(t), this.root && (this.options = s, this.visibleCount = s.visibleCount || 3, this.showTable = s.showTable !== !1, this.isExpanded = !1, this.theme = "light", this.filterText = "", this.activeHighlight = null, this.translations = { ...u, ...s.i18n || {} }, this.locale = s.locale || this.detectLanguage(), this.setupBaseLayout(), this.updateData(e));
49
+ constructor(t, e, i = {}) {
50
+ this.root = document.getElementById(t), this.root && (this.options = i, this.visibleCount = i.visibleCount || 3, this.showTable = i.showTable !== !1, this.showThemeToggle = i.showThemeToggle !== !1, this.isExpanded = !1, this.theme = "light", this.filterText = "", this.activeHighlight = null, this.root.classList.add("lt-root"), this.root.setAttribute("data-theme", this.theme), this.translations = { ...p, ...i.i18n || {} }, this.locale = i.locale || this.detectLanguage(), this.setupBaseLayout(), this.updateData(e));
51
51
  }
52
52
  /**
53
53
  * Detects the browser language.
@@ -64,14 +64,14 @@ class v {
64
64
  * @returns {string} The translated string.
65
65
  */
66
66
  t(t, e = {}) {
67
- let s = (this.translations[this.locale] || this.translations.en)[t] || t;
68
- return Object.keys(e).forEach((i) => s = s.replace(`{${i}}`, e[i])), s;
67
+ let i = (this.translations[this.locale] || this.translations.en)[t] || t;
68
+ return Object.keys(e).forEach((s) => i = i.replace(`{${s}}`, e[s])), i;
69
69
  }
70
70
  /**
71
71
  * Sets up the initial layout of the timeline.
72
72
  */
73
73
  setupBaseLayout() {
74
- this.root.innerHTML = "", this.root.setAttribute("role", "application"), this.root.setAttribute("aria-label", "Product Lifecycle Timeline"), this.renderToolbar(), this.renderThemeToggle(), this.showTable && (this.tableContainer = this.el("div", "timeline-table-container", this.root)), this.wrapper = this.el("div", "timeline-wrapper", this.root), this.wrapper.setAttribute("role", "grid"), this.wrapper.setAttribute("aria-readonly", "true"), this.axis = this.el("div", "timeline-axis", this.wrapper), this.axis.setAttribute("role", "row"), this.tracks = this.el("div", "timeline-tracks", this.wrapper), this.legendContainer = this.el("div", "timeline-legend-container", this.wrapper);
74
+ this.root.innerHTML = "", this.root.setAttribute("role", "application"), this.root.setAttribute("aria-label", "Product Lifecycle Timeline"), this.renderToolbar(), this.showThemeToggle && this.renderThemeToggle(), this.showTable && (this.tableContainer = this.el("div", "lt-table-container", this.root)), this.wrapper = this.el("div", "lt-wrapper", this.root), this.wrapper.setAttribute("role", "grid"), this.wrapper.setAttribute("aria-readonly", "true"), this.axis = this.el("div", "lt-axis", this.wrapper), this.axis.setAttribute("role", "row"), this.tracks = this.el("div", "lt-tracks", this.wrapper), this.legendContainer = this.el("div", "lt-legend-container", this.wrapper);
75
75
  }
76
76
  /**
77
77
  * Updates the timeline data and re-renders.
@@ -86,11 +86,11 @@ class v {
86
86
  * @returns {Array<Object>} The validated and filtered data array.
87
87
  */
88
88
  validateData(t) {
89
- return t.filter((e, s) => {
90
- const n = ["version", "ossStart", "ossEnd"].filter((l) => !e[l]);
89
+ return t.filter((e, i) => {
90
+ const n = ["version", "ossStart", "ossEnd"].filter((o) => !e[o]);
91
91
  if (n.length > 0)
92
- return console.warn(`[Timeline] Missing fields for item at index ${s}: ${n.join(", ")}`), !1;
93
- const a = ["ossStart", "ossEnd", "enterpriseEnd"].filter((l) => e[l]).filter((l) => isNaN(new Date(e[l]).getTime()));
92
+ return console.warn(`[Timeline] Missing fields for item at index ${i}: ${n.join(", ")}`), !1;
93
+ const a = ["ossStart", "ossEnd", "enterpriseEnd"].filter((o) => e[o]).filter((o) => isNaN(new Date(e[o]).getTime()));
94
94
  return a.length > 0 ? (console.warn(`[Timeline] Invalid date format for item "${e.version}": ${a.join(", ")}`), !1) : !0;
95
95
  });
96
96
  }
@@ -112,43 +112,43 @@ class v {
112
112
  * Renders the data table.
113
113
  */
114
114
  renderTable() {
115
- const t = this.el("table", "timeline-table", this.tableContainer);
115
+ const t = this.el("table", "lt-table", this.tableContainer);
116
116
  t.setAttribute("aria-label", "Project support");
117
- const e = this.el("thead", "", t), s = this.el("tr", "", e);
118
- [this.t("branch"), this.t("initial"), this.t("ossEnd"), this.t("entEnd")].forEach((i) => {
119
- this.el("th", "", s).textContent = i;
120
- }), this.tableBody = this.el("tbody", "", t), this.tableRows = this.data.map((i) => this.createTableRow(i)), this.tableToggleContainer = this.el("div", "timeline-table-toggle", this.tableContainer), this.updateTableVisibility();
117
+ const e = this.el("thead", "", t), i = this.el("tr", "", e);
118
+ [this.t("branch"), this.t("initial"), this.t("ossEnd"), this.t("entEnd")].forEach((s) => {
119
+ this.el("th", "", i).textContent = s;
120
+ }), this.tableBody = this.el("tbody", "", t), this.tableRows = this.data.map((s) => this.createTableRow(s)), this.tableToggleContainer = this.el("div", "lt-table-toggle", this.tableContainer), this.updateTableVisibility();
121
121
  }
122
122
  /**
123
123
  * Creates a row for the data table.
124
124
  * @param {Object} item - Version data.
125
125
  */
126
126
  createTableRow(t) {
127
- const e = this.el("tr", "", this.tableBody), s = Date.now(), i = new Date(t.ossStart).getTime(), n = new Date(t.ossEnd).getTime(), o = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : n, a = s >= i && s <= n ? "status-oss" : s > n && s <= o ? "status-ent" : s > o ? "status-expired" : "", l = this.el("td", "", e);
128
- let d = `<span class="table-badge ${a}">${t.versionOriginal || t.version}</span>`;
129
- t.releaseNotesUrl ? l.innerHTML = `<a href="${t.releaseNotesUrl}" target="_blank" class="table-version-link">${d}</a>` : l.innerHTML = d;
130
- const r = this.el("td", "", e);
131
- r.textContent = t.ossStart, s > i && (r.className = "past-date");
127
+ const e = this.el("tr", "", this.tableBody), i = Date.now(), s = new Date(t.ossStart).getTime(), n = new Date(t.ossEnd).getTime(), r = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : n, a = i >= s && i <= n ? "lt-status-oss" : i > n && i <= r ? "lt-status-ent" : i > r ? "lt-status-expired" : "", o = this.el("td", "", e);
128
+ let d = `<span class="lt-table-badge ${a}">${t.versionOriginal || t.version}</span>`;
129
+ t.releaseNotesUrl ? o.innerHTML = `<a href="${t.releaseNotesUrl}" target="_blank" class="lt-table-version-link">${d}</a>` : o.innerHTML = d;
130
+ const l = this.el("td", "", e);
131
+ l.textContent = t.ossStart, i > s && (l.className = "lt-past-date");
132
132
  const h = this.el("td", "", e);
133
- h.textContent = t.ossEnd, s > n && (h.className = "past-date");
133
+ h.textContent = t.ossEnd, i > n && (h.className = "lt-past-date");
134
134
  const c = this.el("td", "", e);
135
- return c.textContent = t.enterpriseEnd || t.ossEnd, s > o && (c.className = "past-date"), { el: e, version: t.version.toLowerCase(), versionOriginal: t.versionOriginal || t.version };
135
+ return c.textContent = t.enterpriseEnd || t.ossEnd, i > r && (c.className = "lt-past-date"), { el: e, version: t.version.toLowerCase(), versionOriginal: t.versionOriginal || t.version };
136
136
  }
137
137
  /**
138
138
  * Updates table visibility based on filter and visibleCount.
139
139
  */
140
140
  updateTableVisibility() {
141
- const t = this.tableRows.filter((i) => i.version.includes(this.filterText));
142
- this.tableRows.forEach((i) => {
143
- const n = i.el.querySelector(".table-badge");
144
- n.innerHTML = this.highlight(i.versionOriginal || i.version), i.el.classList.remove("row-visible"), i.el.classList.add("row-hidden");
141
+ const t = this.tableRows.filter((s) => s.version.includes(this.filterText));
142
+ this.tableRows.forEach((s) => {
143
+ const n = s.el.querySelector(".lt-table-badge");
144
+ n.innerHTML = this.highlight(s.versionOriginal || s.version), s.el.classList.remove("lt-row-visible"), s.el.classList.add("lt-row-hidden");
145
145
  });
146
146
  const e = this.filterText === "" && t.length > this.visibleCount;
147
- if ((e && !this.isExpanded ? t.slice(0, this.visibleCount) : t).forEach((i) => {
148
- i.el.classList.remove("row-hidden"), i.el.classList.add("row-visible");
147
+ if ((e && !this.isExpanded ? t.slice(0, this.visibleCount) : t).forEach((s) => {
148
+ s.el.classList.remove("lt-row-hidden"), s.el.classList.add("lt-row-visible");
149
149
  }), this.tableToggleContainer.innerHTML = "", e) {
150
- const i = this.el("button", "timeline-toggle-btn table-toggle", this.tableToggleContainer), n = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="${this.isExpanded ? "18 15 12 9 6 15" : "6 9 12 15 18 9"}"></polyline></svg>`;
151
- i.innerHTML = this.isExpanded ? `${this.t("less")} ${n}` : `${this.t("more", { n: t.length - this.visibleCount })} ${n}`, i.onclick = () => {
150
+ const s = this.el("button", "lt-toggle-btn lt-table-toggle", this.tableToggleContainer), n = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="${this.isExpanded ? "18 15 12 9 6 15" : "6 9 12 15 18 9"}"></polyline></svg>`;
151
+ s.innerHTML = this.isExpanded ? `${this.t("less")} ${n}` : `${this.t("more", { n: t.length - this.visibleCount })} ${n}`, s.onclick = () => {
152
152
  this.isExpanded = !this.isExpanded, this.updateVisibility();
153
153
  };
154
154
  }
@@ -157,7 +157,7 @@ class v {
157
157
  * Renders the entire timeline.
158
158
  */
159
159
  render() {
160
- this.showTable && (this.tableContainer.innerHTML = ""), this.axis.innerHTML = "", this.tracks.innerHTML = "", this.legendContainer.innerHTML = "", this.showTable && this.renderTable(), this.renderAxis(), this.grid = this.el("div", "grid-lines-container", this.tracks), this.indicators = this.el("div", "indicators-container", this.tracks), this.renderGrid(), this.renderCurrentDateLine(), this.rows = this.data.map((t, e) => this.createRow(t, e)), this.toggleContainer = this.el("div", "timeline-more-toggle", this.tracks, {
160
+ this.showTable && (this.tableContainer.innerHTML = ""), this.axis.innerHTML = "", this.tracks.innerHTML = "", this.legendContainer.innerHTML = "", this.showTable && this.renderTable(), this.renderAxis(), this.grid = this.el("div", "lt-grid-lines-container", this.tracks), this.indicators = this.el("div", "lt-indicators-container", this.tracks), this.renderGrid(), this.renderCurrentDateLine(), this.rows = this.data.map((t, e) => this.createRow(t, e)), this.toggleContainer = this.el("div", "lt-more-toggle", this.tracks, {
161
161
  paddingTop: "10px",
162
162
  display: "flex",
163
163
  justifyContent: "center",
@@ -169,7 +169,7 @@ class v {
169
169
  * Sets up the tooltip overlay.
170
170
  */
171
171
  setupTooltip() {
172
- this.tooltip && this.tooltip.remove(), this.tooltip = this.el("div", "timeline-tooltip-overlay", document.body), this.tooltip.style.display = "none", this.tooltip.setAttribute("role", "tooltip");
172
+ this.tooltip && this.tooltip.remove(), this.tooltip = this.el("div", "lt-tooltip", this.root), this.tooltip.style.display = "none", this.tooltip.setAttribute("role", "tooltip");
173
173
  }
174
174
  /**
175
175
  * Shows the tooltip with provided content.
@@ -184,9 +184,10 @@ class v {
184
184
  * @param {MouseEvent} e - The mouse event.
185
185
  */
186
186
  updateTooltipPos(t) {
187
- let s = t.pageX + 12, i = t.pageY + 12;
188
- const n = this.tooltip.offsetWidth, o = this.tooltip.offsetHeight;
189
- s + n > window.innerWidth && (s = t.pageX - n - 12), i + o > window.innerHeight && (i = t.pageY - o - 12), this.tooltip.style.left = `${s}px`, this.tooltip.style.top = `${i}px`;
187
+ const e = this.root.getBoundingClientRect(), i = 12;
188
+ let s = t.clientX - e.left + i, n = t.clientY - e.top + i;
189
+ const r = this.tooltip.offsetWidth, a = this.tooltip.offsetHeight;
190
+ s + r > this.root.offsetWidth && (s = t.clientX - e.left - r - i), n + a > this.root.offsetHeight && (n = t.clientY - e.top - a - i), this.tooltip.style.left = `${s}px`, this.tooltip.style.top = `${n}px`;
190
191
  }
191
192
  /**
192
193
  * Hides the tooltip.
@@ -203,7 +204,7 @@ class v {
203
204
  if (!this.filterText) return t;
204
205
  try {
205
206
  const e = new RegExp(`(${this.filterText})`, "gi");
206
- return t.replace(e, '<mark class="highlight-match">$1</mark>');
207
+ return t.replace(e, '<mark class="lt-highlight-match">$1</mark>');
207
208
  } catch {
208
209
  return t;
209
210
  }
@@ -216,33 +217,33 @@ class v {
216
217
  * @param {Object} [styles={}] - Inline styles.
217
218
  * @returns {HTMLElement} The created element.
218
219
  */
219
- el(t, e, s, i = {}) {
220
+ el(t, e, i, s = {}) {
220
221
  const n = document.createElement(t);
221
- return e && (n.className = e), Object.assign(n.style, i), s && s.appendChild(n), n;
222
+ return e && (n.className = e), Object.assign(n.style, s), i && i.appendChild(n), n;
222
223
  }
223
224
  /**
224
225
  * Renders the toolbar with the filter input.
225
226
  */
226
227
  renderToolbar() {
227
- const t = this.el("div", "timeline-toolbar", this.root), e = this.el("div", "timeline-filter-container", t);
228
+ const t = this.el("div", "lt-toolbar", this.root), e = this.el("div", "lt-filter-container", t);
228
229
  e.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';
229
- const s = this.el("input", "timeline-filter-input", e);
230
- s.placeholder = this.t("filter"), s.value = this.filterText, s.setAttribute("aria-label", this.t("filter")), s.oninput = (i) => {
231
- this.filterText = i.target.value.toLowerCase().trim(), this.updateVisibility();
230
+ const i = this.el("input", "lt-filter-input", e);
231
+ i.placeholder = this.t("filter"), i.value = this.filterText, i.setAttribute("aria-label", this.t("filter")), i.oninput = (s) => {
232
+ this.filterText = s.target.value.toLowerCase().trim(), this.updateVisibility();
232
233
  };
233
234
  }
234
235
  /**
235
236
  * Renders the theme toggle button.
236
237
  */
237
238
  renderThemeToggle() {
238
- const t = this.el("button", "theme-toggle-btn", this.root);
239
+ const t = this.el("button", "lt-theme-toggle-btn", this.root);
239
240
  t.title = this.t("dark"), t.setAttribute("aria-label", this.t("dark"));
240
241
  const e = {
241
242
  moon: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>',
242
243
  sun: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>'
243
244
  };
244
245
  t.innerHTML = this.theme === "dark" ? e.sun : e.moon, t.onclick = () => {
245
- this.theme = this.theme === "light" ? "dark" : "light", document.documentElement.setAttribute("data-theme", this.theme), t.innerHTML = this.theme === "dark" ? e.sun : e.moon, t.setAttribute("aria-pressed", this.theme === "dark");
246
+ this.theme = this.theme === "light" ? "dark" : "light", this.root.setAttribute("data-theme", this.theme), t.innerHTML = this.theme === "dark" ? e.sun : e.moon, t.setAttribute("aria-pressed", this.theme === "dark");
246
247
  };
247
248
  }
248
249
  /**
@@ -250,13 +251,13 @@ class v {
250
251
  */
251
252
  renderLegend() {
252
253
  this.legendContainer.innerHTML = "";
253
- const t = this.el("div", "release-legend", this.legendContainer);
254
+ const t = this.el("div", "lt-legend", this.legendContainer);
254
255
  t.setAttribute("role", "complementary"), t.setAttribute("aria-label", "Support Legend"), ["oss", "ent", "eol"].forEach((e) => {
255
- const s = this.el("div", `legend-block ${e === "ent" ? "commercial" : e}`, t);
256
- s.classList.add("legend-item-reactive"), this.activeHighlight === e && s.classList.add("active-highlight"), s.innerHTML = `
257
- <div class="legend-icon" aria-hidden="true"></div>
256
+ const i = this.el("div", `lt-legend-block ${e === "ent" ? "commercial" : e}`, t);
257
+ i.classList.add("lt-legend-item-reactive"), this.activeHighlight === e && i.classList.add("lt-active-highlight"), i.innerHTML = `
258
+ <div class="lt-legend-icon" aria-hidden="true"></div>
258
259
  <div><h3>${this.t(e)}</h3><p>${this.t(e + "Desc")}</p></div>
259
- `, s.onclick = () => this.highlightSegment(e);
260
+ `, i.onclick = () => this.highlightSegment(e);
260
261
  });
261
262
  }
262
263
  /**
@@ -264,7 +265,7 @@ class v {
264
265
  * @param {string} type - 'oss', 'ent', or 'eol'.
265
266
  */
266
267
  highlightSegment(t) {
267
- this.activeHighlight = this.activeHighlight === t ? null : t, this.root.classList.remove("highlight-oss", "highlight-ent", "highlight-eol"), this.activeHighlight && this.root.classList.add(`highlight-${this.activeHighlight}`), this.renderLegend();
268
+ this.activeHighlight = this.activeHighlight === t ? null : t, this.root.classList.remove("lt-highlight-oss", "lt-highlight-ent", "lt-highlight-eol"), this.activeHighlight && this.root.classList.add(`lt-highlight-${this.activeHighlight}`), this.renderLegend();
268
269
  }
269
270
  /**
270
271
  * Renders the year axis.
@@ -272,8 +273,8 @@ class v {
272
273
  renderAxis() {
273
274
  this.el("div", "", this.axis, { width: "96px", flexShrink: "0" }).setAttribute("role", "presentation");
274
275
  for (let e = this.minYear; e <= this.maxYear; e++) {
275
- const s = this.el("div", "timeline-year", this.axis);
276
- s.textContent = e, s.setAttribute("role", "columnheader");
276
+ const i = this.el("div", "lt-year", this.axis);
277
+ i.textContent = e, i.setAttribute("role", "columnheader");
277
278
  }
278
279
  }
279
280
  /**
@@ -281,9 +282,9 @@ class v {
281
282
  */
282
283
  renderGrid() {
283
284
  const t = new Date(this.minYear, 0, 1).getTime(), e = new Date(this.maxYear, 11, 31).getTime() - t;
284
- for (let s = this.minYear; s <= this.maxYear; s++) {
285
- const i = this.el("div", "year-grid-line", this.grid);
286
- i.style.left = `${(new Date(s, 0, 1).getTime() - t) / e * 100}%`, i.setAttribute("role", "presentation");
285
+ for (let i = this.minYear; i <= this.maxYear; i++) {
286
+ const s = this.el("div", "lt-year-grid-line", this.grid);
287
+ s.style.left = `${(new Date(i, 0, 1).getTime() - t) / e * 100}%`, s.setAttribute("role", "presentation");
287
288
  }
288
289
  }
289
290
  /**
@@ -292,18 +293,18 @@ class v {
292
293
  * @returns {Object} Metadata about the created row.
293
294
  */
294
295
  createRow(t, e) {
295
- const s = this.el("div", "timeline-row row-entrance", this.tracks);
296
- s.setAttribute("role", "row"), s.style.transitionDelay = `${e * 0.05}s`;
297
- const i = this.el("div", "version-label", s);
298
- i.setAttribute("role", "rowheader");
299
- const n = Date.now(), o = new Date(t.ossStart).getTime(), a = new Date(t.ossEnd).getTime(), l = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : a, d = n >= o && n <= a ? "status-oss" : n > a && n <= l ? "status-ent" : n > l ? "status-expired" : "";
300
- if (d && i.classList.add(d), t.releaseNotesUrl) {
301
- const h = this.el("a", "version-link", i);
296
+ const i = this.el("div", "lt-row row-entrance", this.tracks);
297
+ i.setAttribute("role", "row"), i.style.transitionDelay = `${e * 0.05}s`;
298
+ const s = this.el("div", "lt-version-label", i);
299
+ s.setAttribute("role", "rowheader");
300
+ const n = Date.now(), r = new Date(t.ossStart).getTime(), a = new Date(t.ossEnd).getTime(), o = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : a, d = n >= r && n <= a ? "lt-status-oss" : n > a && n <= o ? "lt-status-ent" : n > o ? "lt-status-expired" : "";
301
+ if (d && s.classList.add(d), t.releaseNotesUrl) {
302
+ const h = this.el("a", "lt-version-link", s);
302
303
  h.href = t.releaseNotesUrl, h.target = "_blank", h.innerHTML = this.highlight(t.version), h.title = this.t("notes", { v: t.version }), h.setAttribute("aria-label", this.t("notes", { v: t.version }));
303
304
  } else
304
- i.innerHTML = this.highlight(t.version);
305
- const r = this.el("div", "track-container", s);
306
- return r.setAttribute("role", "gridcell"), r.appendChild(this.createBar(t, t.ossStart, t.enterpriseEnd || t.ossEnd, "bar-ent", this.t("ent"))), r.appendChild(this.createBar(t, t.ossStart, t.ossEnd, "bar-oss", this.t("oss"))), { el: s, version: t.version.toLowerCase(), versionOriginal: t.version };
305
+ s.innerHTML = this.highlight(t.version);
306
+ const l = this.el("div", "lt-track-container", i);
307
+ return l.setAttribute("role", "gridcell"), l.appendChild(this.createBar(t, t.ossStart, t.enterpriseEnd || t.ossEnd, "lt-bar-ent", this.t("ent"))), l.appendChild(this.createBar(t, t.ossStart, t.ossEnd, "lt-bar-oss", this.t("oss"))), { el: i, version: t.version.toLowerCase(), versionOriginal: t.version };
307
308
  }
308
309
  /**
309
310
  * Creates a colored bar segment for the timeline.
@@ -314,34 +315,34 @@ class v {
314
315
  * @param {string} label - Support type label for tooltip.
315
316
  * @returns {HTMLElement} The created bar element.
316
317
  */
317
- createBar(t, e, s, i, n) {
318
- const o = new Date(e).getTime(), a = new Date(s).getTime(), l = new Date(this.minYear, 0, 1).getTime(), d = new Date(this.maxYear, 11, 31).getTime() - l, r = this.el("div", `bar-segment ${i}`), h = i === "bar-oss" ? "segment-oss" : "segment-ent";
319
- r.classList.add(h), r.style.left = `${(o - l) / d * 100}%`, r.style.width = `${(a - o) / d * 100}%`, r.setAttribute("role", "img"), r.setAttribute("aria-label", `${n}: ${t.version} (${e} to ${s})`), r.tabIndex = 0;
318
+ createBar(t, e, i, s, n) {
319
+ const r = new Date(e).getTime(), a = new Date(i).getTime(), o = new Date(this.minYear, 0, 1).getTime(), d = new Date(this.maxYear, 11, 31).getTime() - o, l = this.el("div", `lt-bar-segment ${s}`), h = s === "lt-bar-oss" ? "lt-segment-oss" : "lt-segment-ent";
320
+ l.classList.add(h), l.style.left = `${(r - o) / d * 100}%`, l.style.width = `${(a - r) / d * 100}%`, l.setAttribute("role", "img"), l.setAttribute("aria-label", `${n}: ${t.version} (${e} to ${i})`), l.tabIndex = 0;
320
321
  const c = `
321
- <div class="tooltip-header">${n} - ${t.version}</div>
322
- <div class="tooltip-date"><strong>Du:</strong> ${e}</div>
323
- <div class="tooltip-date"><strong>Au:</strong> ${s}</div>
322
+ <div class="lt-tooltip-header">${n} - ${t.version}</div>
323
+ <div class="lt-tooltip-date"><strong>Du:</strong> ${e}</div>
324
+ <div class="lt-tooltip-date"><strong>Au:</strong> ${i}</div>
324
325
  `;
325
- return r.onmouseenter = (g) => this.showTooltip(g, c), r.onmousemove = (g) => this.updateTooltipPos(g), r.onmouseleave = () => this.hideTooltip(), r.onfocus = (g) => {
326
- const p = r.getBoundingClientRect();
327
- this.showTooltip({ pageX: p.left + window.scrollX, pageY: p.top + window.scrollY - 40 }, c);
328
- }, r.onblur = () => this.hideTooltip(), r;
326
+ return l.onmouseenter = (g) => this.showTooltip(g, c), l.onmousemove = (g) => this.updateTooltipPos(g), l.onmouseleave = () => this.hideTooltip(), l.onfocus = (g) => {
327
+ const u = l.getBoundingClientRect();
328
+ this.showTooltip({ pageX: u.left + window.scrollX, pageY: u.top + window.scrollY - 40 }, c);
329
+ }, l.onblur = () => this.hideTooltip(), l;
329
330
  }
330
331
  /**
331
332
  * Updates visibility of rows based on filtering and expansion state.
332
333
  */
333
334
  updateVisibility() {
334
- const t = this.rows.filter((i) => i.version.includes(this.filterText));
335
- this.rows.forEach((i) => {
336
- const n = i.el.querySelector(".version-label"), o = n.querySelector(".version-link");
337
- o ? o.innerHTML = this.highlight(i.versionOriginal || i.version) : n.innerHTML = this.highlight(i.versionOriginal || i.version), i.el.classList.remove("row-visible"), i.el.classList.add("row-hidden");
335
+ const t = this.rows.filter((s) => s.version.includes(this.filterText));
336
+ this.rows.forEach((s) => {
337
+ const n = s.el.querySelector(".lt-version-label"), r = n.querySelector(".lt-version-link");
338
+ r ? r.innerHTML = this.highlight(s.versionOriginal || s.version) : n.innerHTML = this.highlight(s.versionOriginal || s.version), s.el.classList.remove("lt-row-visible"), s.el.classList.add("lt-row-hidden");
338
339
  });
339
340
  const e = this.filterText === "" && t.length > this.visibleCount;
340
- if ((e && !this.isExpanded ? t.slice(0, this.visibleCount) : t).forEach((i) => {
341
- i.el.classList.remove("row-hidden"), i.el.classList.add("row-visible");
341
+ if ((e && !this.isExpanded ? t.slice(0, this.visibleCount) : t).forEach((s) => {
342
+ s.el.classList.remove("lt-row-hidden"), s.el.classList.add("lt-row-visible");
342
343
  }), this.showTable && this.updateTableVisibility(), this.toggleContainer.innerHTML = "", e) {
343
- const i = this.el("button", "timeline-toggle-btn", this.toggleContainer), n = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="${this.isExpanded ? "18 15 12 9 6 15" : "6 9 12 15 18 9"}"></polyline></svg>`;
344
- i.innerHTML = this.isExpanded ? `${this.t("less")} ${n}` : `${this.t("more", { n: t.length - this.visibleCount })} ${n}`, i.setAttribute("aria-expanded", this.isExpanded), i.onclick = () => {
344
+ const s = this.el("button", "lt-toggle-btn", this.toggleContainer), n = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="${this.isExpanded ? "18 15 12 9 6 15" : "6 9 12 15 18 9"}"></polyline></svg>`;
345
+ s.innerHTML = this.isExpanded ? `${this.t("less")} ${n}` : `${this.t("more", { n: t.length - this.visibleCount })} ${n}`, s.setAttribute("aria-expanded", this.isExpanded), s.onclick = () => {
345
346
  this.isExpanded = !this.isExpanded, this.updateVisibility();
346
347
  };
347
348
  }
@@ -350,12 +351,12 @@ class v {
350
351
  * Renders the vertical line indicating current date.
351
352
  */
352
353
  renderCurrentDateLine() {
353
- const t = new Date(this.minYear, 0, 1).getTime(), e = new Date(this.maxYear, 11, 31).getTime() - t, s = Date.now() - t;
354
- if (s < 0 || s > e) return;
355
- const i = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], n = this.el("div", "current-date-indicator", this.indicators);
356
- n.style.left = `${s / e * 100}%`, n.setAttribute("role", "separator"), n.setAttribute("aria-label", this.t("today", { date: i }));
357
- const o = this.el("div", "current-date-badge", n);
358
- o.textContent = i, o.setAttribute("aria-hidden", "true");
354
+ const t = new Date(this.minYear, 0, 1).getTime(), e = new Date(this.maxYear, 11, 31).getTime() - t, i = Date.now() - t;
355
+ if (i < 0 || i > e) return;
356
+ const s = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], n = this.el("div", "lt-current-date-indicator", this.indicators);
357
+ n.style.left = `${i / e * 100}%`, n.setAttribute("role", "separator"), n.setAttribute("aria-label", this.t("today", { date: s }));
358
+ const r = this.el("div", "lt-current-date-badge", n);
359
+ r.textContent = s, r.setAttribute("aria-hidden", "true");
359
360
  }
360
361
  }
361
362
  export {
@@ -1,8 +1,8 @@
1
- (function(c,g){typeof exports=="object"&&typeof module<"u"?module.exports=g():typeof define=="function"&&define.amd?define(g):(c=typeof globalThis<"u"?globalThis:c||self,c.Timeline=g())})(this,(function(){"use strict";const c={en:{filter:"Filter versions...",oss:"OSS Support",ent:"Enterprise Support",eol:"Out of Support",less:"Show Less",more:"Show {n} more versions",notes:"View Release Notes for {v}",dark:"Toggle Dark Mode",ossDesc:"Free security updates and bugfixes.",entDesc:"Expert support during OSS plus extended support after EOL.",eolDesc:"End of life. No further updates are provided.",today:"Today's date: {date}",branch:"Branch",initial:"Initial Release",ossEnd:"End OSS",entEnd:"End Enterprise *"},fr:{filter:"Filtrer les versions...",oss:"Support OSS",ent:"Support Entreprise",eol:"Fin de support",less:"Voir moins",more:"Voir {n} versions supplémentaires",notes:"Voir les notes de version pour {v}",dark:"Changer le mode sombre",ossDesc:"Mises à jour de sécurité et corrections de bugs gratuites.",entDesc:"Support pendant la période OSS plus support étendu après.",eolDesc:"Version en fin de vie. Plus de mises à jour.",today:"Date du jour : {date}",branch:"Version",initial:"Sortie initiale",ossEnd:"Fin OSS",entEnd:"Fin Entreprise *"}};class g{constructor(t,e,s={}){this.root=document.getElementById(t),this.root&&(this.options=s,this.visibleCount=s.visibleCount||3,this.showTable=s.showTable!==!1,this.isExpanded=!1,this.theme="light",this.filterText="",this.activeHighlight=null,this.translations={...c,...s.i18n||{}},this.locale=s.locale||this.detectLanguage(),this.setupBaseLayout(),this.updateData(e))}detectLanguage(){const t=(navigator.language||"en").split("-")[0];return this.translations[t]?t:"en"}t(t,e={}){let s=(this.translations[this.locale]||this.translations.en)[t]||t;return Object.keys(e).forEach(i=>s=s.replace(`{${i}}`,e[i])),s}setupBaseLayout(){this.root.innerHTML="",this.root.setAttribute("role","application"),this.root.setAttribute("aria-label","Product Lifecycle Timeline"),this.renderToolbar(),this.renderThemeToggle(),this.showTable&&(this.tableContainer=this.el("div","timeline-table-container",this.root)),this.wrapper=this.el("div","timeline-wrapper",this.root),this.wrapper.setAttribute("role","grid"),this.wrapper.setAttribute("aria-readonly","true"),this.axis=this.el("div","timeline-axis",this.wrapper),this.axis.setAttribute("role","row"),this.tracks=this.el("div","timeline-tracks",this.wrapper),this.legendContainer=this.el("div","timeline-legend-container",this.wrapper)}updateData(t){this.data=this.validateData(t||[]),this.calculateTimeRange(),this.render()}validateData(t){return t.filter((e,s)=>{const n=["version","ossStart","ossEnd"].filter(l=>!e[l]);if(n.length>0)return console.warn(`[Timeline] Missing fields for item at index ${s}: ${n.join(", ")}`),!1;const a=["ossStart","ossEnd","enterpriseEnd"].filter(l=>e[l]).filter(l=>isNaN(new Date(e[l]).getTime()));return a.length>0?(console.warn(`[Timeline] Invalid date format for item "${e.version}": ${a.join(", ")}`),!1):!0})}calculateTimeRange(){if(this.currentDate=new Date,!this.data.length){this.minYear=this.currentDate.getFullYear()-1,this.maxYear=this.currentDate.getFullYear()+3;return}const t=this.data.flatMap(e=>[new Date(e.ossStart),new Date(e.enterpriseEnd||e.ossEnd)]);this.minYear=new Date(Math.min(...t)).getFullYear(),this.maxYear=new Date(Math.max(...t)).getFullYear()}renderTable(){const t=this.el("table","timeline-table",this.tableContainer);t.setAttribute("aria-label","Project support");const e=this.el("thead","",t),s=this.el("tr","",e);[this.t("branch"),this.t("initial"),this.t("ossEnd"),this.t("entEnd")].forEach(i=>{this.el("th","",s).textContent=i}),this.tableBody=this.el("tbody","",t),this.tableRows=this.data.map(i=>this.createTableRow(i)),this.tableToggleContainer=this.el("div","timeline-table-toggle",this.tableContainer),this.updateTableVisibility()}createTableRow(t){const e=this.el("tr","",this.tableBody),s=Date.now(),i=new Date(t.ossStart).getTime(),n=new Date(t.ossEnd).getTime(),o=t.enterpriseEnd?new Date(t.enterpriseEnd).getTime():n,a=s>=i&&s<=n?"status-oss":s>n&&s<=o?"status-ent":s>o?"status-expired":"",l=this.el("td","",e);let d=`<span class="table-badge ${a}">${t.versionOriginal||t.version}</span>`;t.releaseNotesUrl?l.innerHTML=`<a href="${t.releaseNotesUrl}" target="_blank" class="table-version-link">${d}</a>`:l.innerHTML=d;const r=this.el("td","",e);r.textContent=t.ossStart,s>i&&(r.className="past-date");const h=this.el("td","",e);h.textContent=t.ossEnd,s>n&&(h.className="past-date");const u=this.el("td","",e);return u.textContent=t.enterpriseEnd||t.ossEnd,s>o&&(u.className="past-date"),{el:e,version:t.version.toLowerCase(),versionOriginal:t.versionOriginal||t.version}}updateTableVisibility(){const t=this.tableRows.filter(i=>i.version.includes(this.filterText));this.tableRows.forEach(i=>{const n=i.el.querySelector(".table-badge");n.innerHTML=this.highlight(i.versionOriginal||i.version),i.el.classList.remove("row-visible"),i.el.classList.add("row-hidden")});const e=this.filterText===""&&t.length>this.visibleCount;if((e&&!this.isExpanded?t.slice(0,this.visibleCount):t).forEach(i=>{i.el.classList.remove("row-hidden"),i.el.classList.add("row-visible")}),this.tableToggleContainer.innerHTML="",e){const i=this.el("button","timeline-toggle-btn table-toggle",this.tableToggleContainer),n=`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="${this.isExpanded?"18 15 12 9 6 15":"6 9 12 15 18 9"}"></polyline></svg>`;i.innerHTML=this.isExpanded?`${this.t("less")} ${n}`:`${this.t("more",{n:t.length-this.visibleCount})} ${n}`,i.onclick=()=>{this.isExpanded=!this.isExpanded,this.updateVisibility()}}}render(){this.showTable&&(this.tableContainer.innerHTML=""),this.axis.innerHTML="",this.tracks.innerHTML="",this.legendContainer.innerHTML="",this.showTable&&this.renderTable(),this.renderAxis(),this.grid=this.el("div","grid-lines-container",this.tracks),this.indicators=this.el("div","indicators-container",this.tracks),this.renderGrid(),this.renderCurrentDateLine(),this.rows=this.data.map((t,e)=>this.createRow(t,e)),this.toggleContainer=this.el("div","timeline-more-toggle",this.tracks,{paddingTop:"10px",display:"flex",justifyContent:"center",position:"relative",zIndex:"10"}),this.updateVisibility(),this.renderLegend(),this.setupTooltip()}setupTooltip(){this.tooltip&&this.tooltip.remove(),this.tooltip=this.el("div","timeline-tooltip-overlay",document.body),this.tooltip.style.display="none",this.tooltip.setAttribute("role","tooltip")}showTooltip(t,e){this.tooltip.innerHTML=e,this.tooltip.style.display="block",this.updateTooltipPos(t)}updateTooltipPos(t){let s=t.pageX+12,i=t.pageY+12;const n=this.tooltip.offsetWidth,o=this.tooltip.offsetHeight;s+n>window.innerWidth&&(s=t.pageX-n-12),i+o>window.innerHeight&&(i=t.pageY-o-12),this.tooltip.style.left=`${s}px`,this.tooltip.style.top=`${i}px`}hideTooltip(){this.tooltip&&(this.tooltip.style.display="none")}highlight(t){if(!this.filterText)return t;try{const e=new RegExp(`(${this.filterText})`,"gi");return t.replace(e,'<mark class="highlight-match">$1</mark>')}catch{return t}}el(t,e,s,i={}){const n=document.createElement(t);return e&&(n.className=e),Object.assign(n.style,i),s&&s.appendChild(n),n}renderToolbar(){const t=this.el("div","timeline-toolbar",this.root),e=this.el("div","timeline-filter-container",t);e.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';const s=this.el("input","timeline-filter-input",e);s.placeholder=this.t("filter"),s.value=this.filterText,s.setAttribute("aria-label",this.t("filter")),s.oninput=i=>{this.filterText=i.target.value.toLowerCase().trim(),this.updateVisibility()}}renderThemeToggle(){const t=this.el("button","theme-toggle-btn",this.root);t.title=this.t("dark"),t.setAttribute("aria-label",this.t("dark"));const e={moon:'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>',sun:'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>'};t.innerHTML=this.theme==="dark"?e.sun:e.moon,t.onclick=()=>{this.theme=this.theme==="light"?"dark":"light",document.documentElement.setAttribute("data-theme",this.theme),t.innerHTML=this.theme==="dark"?e.sun:e.moon,t.setAttribute("aria-pressed",this.theme==="dark")}}renderLegend(){this.legendContainer.innerHTML="";const t=this.el("div","release-legend",this.legendContainer);t.setAttribute("role","complementary"),t.setAttribute("aria-label","Support Legend"),["oss","ent","eol"].forEach(e=>{const s=this.el("div",`legend-block ${e==="ent"?"commercial":e}`,t);s.classList.add("legend-item-reactive"),this.activeHighlight===e&&s.classList.add("active-highlight"),s.innerHTML=`
2
- <div class="legend-icon" aria-hidden="true"></div>
1
+ (function(c,g){typeof exports=="object"&&typeof module<"u"?module.exports=g():typeof define=="function"&&define.amd?define(g):(c=typeof globalThis<"u"?globalThis:c||self,c.Timeline=g())})(this,(function(){"use strict";const c={en:{filter:"Filter versions...",oss:"OSS Support",ent:"Enterprise Support",eol:"Out of Support",less:"Show Less",more:"Show {n} more versions",notes:"View Release Notes for {v}",dark:"Toggle Dark Mode",ossDesc:"Free security updates and bugfixes.",entDesc:"Expert support during OSS plus extended support after EOL.",eolDesc:"End of life. No further updates are provided.",today:"Today's date: {date}",branch:"Branch",initial:"Initial Release",ossEnd:"End OSS",entEnd:"End Enterprise *"},fr:{filter:"Filtrer les versions...",oss:"Support OSS",ent:"Support Entreprise",eol:"Fin de support",less:"Voir moins",more:"Voir {n} versions supplémentaires",notes:"Voir les notes de version pour {v}",dark:"Changer le mode sombre",ossDesc:"Mises à jour de sécurité et corrections de bugs gratuites.",entDesc:"Support pendant la période OSS plus support étendu après.",eolDesc:"Version en fin de vie. Plus de mises à jour.",today:"Date du jour : {date}",branch:"Version",initial:"Sortie initiale",ossEnd:"Fin OSS",entEnd:"Fin Entreprise *"}};class g{constructor(t,e,i={}){this.root=document.getElementById(t),this.root&&(this.options=i,this.visibleCount=i.visibleCount||3,this.showTable=i.showTable!==!1,this.showThemeToggle=i.showThemeToggle!==!1,this.isExpanded=!1,this.theme="light",this.filterText="",this.activeHighlight=null,this.root.classList.add("lt-root"),this.root.setAttribute("data-theme",this.theme),this.translations={...c,...i.i18n||{}},this.locale=i.locale||this.detectLanguage(),this.setupBaseLayout(),this.updateData(e))}detectLanguage(){const t=(navigator.language||"en").split("-")[0];return this.translations[t]?t:"en"}t(t,e={}){let i=(this.translations[this.locale]||this.translations.en)[t]||t;return Object.keys(e).forEach(s=>i=i.replace(`{${s}}`,e[s])),i}setupBaseLayout(){this.root.innerHTML="",this.root.setAttribute("role","application"),this.root.setAttribute("aria-label","Product Lifecycle Timeline"),this.renderToolbar(),this.showThemeToggle&&this.renderThemeToggle(),this.showTable&&(this.tableContainer=this.el("div","lt-table-container",this.root)),this.wrapper=this.el("div","lt-wrapper",this.root),this.wrapper.setAttribute("role","grid"),this.wrapper.setAttribute("aria-readonly","true"),this.axis=this.el("div","lt-axis",this.wrapper),this.axis.setAttribute("role","row"),this.tracks=this.el("div","lt-tracks",this.wrapper),this.legendContainer=this.el("div","lt-legend-container",this.wrapper)}updateData(t){this.data=this.validateData(t||[]),this.calculateTimeRange(),this.render()}validateData(t){return t.filter((e,i)=>{const n=["version","ossStart","ossEnd"].filter(r=>!e[r]);if(n.length>0)return console.warn(`[Timeline] Missing fields for item at index ${i}: ${n.join(", ")}`),!1;const a=["ossStart","ossEnd","enterpriseEnd"].filter(r=>e[r]).filter(r=>isNaN(new Date(e[r]).getTime()));return a.length>0?(console.warn(`[Timeline] Invalid date format for item "${e.version}": ${a.join(", ")}`),!1):!0})}calculateTimeRange(){if(this.currentDate=new Date,!this.data.length){this.minYear=this.currentDate.getFullYear()-1,this.maxYear=this.currentDate.getFullYear()+3;return}const t=this.data.flatMap(e=>[new Date(e.ossStart),new Date(e.enterpriseEnd||e.ossEnd)]);this.minYear=new Date(Math.min(...t)).getFullYear(),this.maxYear=new Date(Math.max(...t)).getFullYear()}renderTable(){const t=this.el("table","lt-table",this.tableContainer);t.setAttribute("aria-label","Project support");const e=this.el("thead","",t),i=this.el("tr","",e);[this.t("branch"),this.t("initial"),this.t("ossEnd"),this.t("entEnd")].forEach(s=>{this.el("th","",i).textContent=s}),this.tableBody=this.el("tbody","",t),this.tableRows=this.data.map(s=>this.createTableRow(s)),this.tableToggleContainer=this.el("div","lt-table-toggle",this.tableContainer),this.updateTableVisibility()}createTableRow(t){const e=this.el("tr","",this.tableBody),i=Date.now(),s=new Date(t.ossStart).getTime(),n=new Date(t.ossEnd).getTime(),o=t.enterpriseEnd?new Date(t.enterpriseEnd).getTime():n,a=i>=s&&i<=n?"lt-status-oss":i>n&&i<=o?"lt-status-ent":i>o?"lt-status-expired":"",r=this.el("td","",e);let d=`<span class="lt-table-badge ${a}">${t.versionOriginal||t.version}</span>`;t.releaseNotesUrl?r.innerHTML=`<a href="${t.releaseNotesUrl}" target="_blank" class="lt-table-version-link">${d}</a>`:r.innerHTML=d;const l=this.el("td","",e);l.textContent=t.ossStart,i>s&&(l.className="lt-past-date");const h=this.el("td","",e);h.textContent=t.ossEnd,i>n&&(h.className="lt-past-date");const u=this.el("td","",e);return u.textContent=t.enterpriseEnd||t.ossEnd,i>o&&(u.className="lt-past-date"),{el:e,version:t.version.toLowerCase(),versionOriginal:t.versionOriginal||t.version}}updateTableVisibility(){const t=this.tableRows.filter(s=>s.version.includes(this.filterText));this.tableRows.forEach(s=>{const n=s.el.querySelector(".lt-table-badge");n.innerHTML=this.highlight(s.versionOriginal||s.version),s.el.classList.remove("lt-row-visible"),s.el.classList.add("lt-row-hidden")});const e=this.filterText===""&&t.length>this.visibleCount;if((e&&!this.isExpanded?t.slice(0,this.visibleCount):t).forEach(s=>{s.el.classList.remove("lt-row-hidden"),s.el.classList.add("lt-row-visible")}),this.tableToggleContainer.innerHTML="",e){const s=this.el("button","lt-toggle-btn lt-table-toggle",this.tableToggleContainer),n=`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="${this.isExpanded?"18 15 12 9 6 15":"6 9 12 15 18 9"}"></polyline></svg>`;s.innerHTML=this.isExpanded?`${this.t("less")} ${n}`:`${this.t("more",{n:t.length-this.visibleCount})} ${n}`,s.onclick=()=>{this.isExpanded=!this.isExpanded,this.updateVisibility()}}}render(){this.showTable&&(this.tableContainer.innerHTML=""),this.axis.innerHTML="",this.tracks.innerHTML="",this.legendContainer.innerHTML="",this.showTable&&this.renderTable(),this.renderAxis(),this.grid=this.el("div","lt-grid-lines-container",this.tracks),this.indicators=this.el("div","lt-indicators-container",this.tracks),this.renderGrid(),this.renderCurrentDateLine(),this.rows=this.data.map((t,e)=>this.createRow(t,e)),this.toggleContainer=this.el("div","lt-more-toggle",this.tracks,{paddingTop:"10px",display:"flex",justifyContent:"center",position:"relative",zIndex:"10"}),this.updateVisibility(),this.renderLegend(),this.setupTooltip()}setupTooltip(){this.tooltip&&this.tooltip.remove(),this.tooltip=this.el("div","lt-tooltip",this.root),this.tooltip.style.display="none",this.tooltip.setAttribute("role","tooltip")}showTooltip(t,e){this.tooltip.innerHTML=e,this.tooltip.style.display="block",this.updateTooltipPos(t)}updateTooltipPos(t){const e=this.root.getBoundingClientRect(),i=12;let s=t.clientX-e.left+i,n=t.clientY-e.top+i;const o=this.tooltip.offsetWidth,a=this.tooltip.offsetHeight;s+o>this.root.offsetWidth&&(s=t.clientX-e.left-o-i),n+a>this.root.offsetHeight&&(n=t.clientY-e.top-a-i),this.tooltip.style.left=`${s}px`,this.tooltip.style.top=`${n}px`}hideTooltip(){this.tooltip&&(this.tooltip.style.display="none")}highlight(t){if(!this.filterText)return t;try{const e=new RegExp(`(${this.filterText})`,"gi");return t.replace(e,'<mark class="lt-highlight-match">$1</mark>')}catch{return t}}el(t,e,i,s={}){const n=document.createElement(t);return e&&(n.className=e),Object.assign(n.style,s),i&&i.appendChild(n),n}renderToolbar(){const t=this.el("div","lt-toolbar",this.root),e=this.el("div","lt-filter-container",t);e.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';const i=this.el("input","lt-filter-input",e);i.placeholder=this.t("filter"),i.value=this.filterText,i.setAttribute("aria-label",this.t("filter")),i.oninput=s=>{this.filterText=s.target.value.toLowerCase().trim(),this.updateVisibility()}}renderThemeToggle(){const t=this.el("button","lt-theme-toggle-btn",this.root);t.title=this.t("dark"),t.setAttribute("aria-label",this.t("dark"));const e={moon:'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>',sun:'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>'};t.innerHTML=this.theme==="dark"?e.sun:e.moon,t.onclick=()=>{this.theme=this.theme==="light"?"dark":"light",this.root.setAttribute("data-theme",this.theme),t.innerHTML=this.theme==="dark"?e.sun:e.moon,t.setAttribute("aria-pressed",this.theme==="dark")}}renderLegend(){this.legendContainer.innerHTML="";const t=this.el("div","lt-legend",this.legendContainer);t.setAttribute("role","complementary"),t.setAttribute("aria-label","Support Legend"),["oss","ent","eol"].forEach(e=>{const i=this.el("div",`lt-legend-block ${e==="ent"?"commercial":e}`,t);i.classList.add("lt-legend-item-reactive"),this.activeHighlight===e&&i.classList.add("lt-active-highlight"),i.innerHTML=`
2
+ <div class="lt-legend-icon" aria-hidden="true"></div>
3
3
  <div><h3>${this.t(e)}</h3><p>${this.t(e+"Desc")}</p></div>
4
- `,s.onclick=()=>this.highlightSegment(e)})}highlightSegment(t){this.activeHighlight=this.activeHighlight===t?null:t,this.root.classList.remove("highlight-oss","highlight-ent","highlight-eol"),this.activeHighlight&&this.root.classList.add(`highlight-${this.activeHighlight}`),this.renderLegend()}renderAxis(){this.el("div","",this.axis,{width:"96px",flexShrink:"0"}).setAttribute("role","presentation");for(let e=this.minYear;e<=this.maxYear;e++){const s=this.el("div","timeline-year",this.axis);s.textContent=e,s.setAttribute("role","columnheader")}}renderGrid(){const t=new Date(this.minYear,0,1).getTime(),e=new Date(this.maxYear,11,31).getTime()-t;for(let s=this.minYear;s<=this.maxYear;s++){const i=this.el("div","year-grid-line",this.grid);i.style.left=`${(new Date(s,0,1).getTime()-t)/e*100}%`,i.setAttribute("role","presentation")}}createRow(t,e){const s=this.el("div","timeline-row row-entrance",this.tracks);s.setAttribute("role","row"),s.style.transitionDelay=`${e*.05}s`;const i=this.el("div","version-label",s);i.setAttribute("role","rowheader");const n=Date.now(),o=new Date(t.ossStart).getTime(),a=new Date(t.ossEnd).getTime(),l=t.enterpriseEnd?new Date(t.enterpriseEnd).getTime():a,d=n>=o&&n<=a?"status-oss":n>a&&n<=l?"status-ent":n>l?"status-expired":"";if(d&&i.classList.add(d),t.releaseNotesUrl){const h=this.el("a","version-link",i);h.href=t.releaseNotesUrl,h.target="_blank",h.innerHTML=this.highlight(t.version),h.title=this.t("notes",{v:t.version}),h.setAttribute("aria-label",this.t("notes",{v:t.version}))}else i.innerHTML=this.highlight(t.version);const r=this.el("div","track-container",s);return r.setAttribute("role","gridcell"),r.appendChild(this.createBar(t,t.ossStart,t.enterpriseEnd||t.ossEnd,"bar-ent",this.t("ent"))),r.appendChild(this.createBar(t,t.ossStart,t.ossEnd,"bar-oss",this.t("oss"))),{el:s,version:t.version.toLowerCase(),versionOriginal:t.version}}createBar(t,e,s,i,n){const o=new Date(e).getTime(),a=new Date(s).getTime(),l=new Date(this.minYear,0,1).getTime(),d=new Date(this.maxYear,11,31).getTime()-l,r=this.el("div",`bar-segment ${i}`),h=i==="bar-oss"?"segment-oss":"segment-ent";r.classList.add(h),r.style.left=`${(o-l)/d*100}%`,r.style.width=`${(a-o)/d*100}%`,r.setAttribute("role","img"),r.setAttribute("aria-label",`${n}: ${t.version} (${e} to ${s})`),r.tabIndex=0;const u=`
5
- <div class="tooltip-header">${n} - ${t.version}</div>
6
- <div class="tooltip-date"><strong>Du:</strong> ${e}</div>
7
- <div class="tooltip-date"><strong>Au:</strong> ${s}</div>
8
- `;return r.onmouseenter=p=>this.showTooltip(p,u),r.onmousemove=p=>this.updateTooltipPos(p),r.onmouseleave=()=>this.hideTooltip(),r.onfocus=p=>{const b=r.getBoundingClientRect();this.showTooltip({pageX:b.left+window.scrollX,pageY:b.top+window.scrollY-40},u)},r.onblur=()=>this.hideTooltip(),r}updateVisibility(){const t=this.rows.filter(i=>i.version.includes(this.filterText));this.rows.forEach(i=>{const n=i.el.querySelector(".version-label"),o=n.querySelector(".version-link");o?o.innerHTML=this.highlight(i.versionOriginal||i.version):n.innerHTML=this.highlight(i.versionOriginal||i.version),i.el.classList.remove("row-visible"),i.el.classList.add("row-hidden")});const e=this.filterText===""&&t.length>this.visibleCount;if((e&&!this.isExpanded?t.slice(0,this.visibleCount):t).forEach(i=>{i.el.classList.remove("row-hidden"),i.el.classList.add("row-visible")}),this.showTable&&this.updateTableVisibility(),this.toggleContainer.innerHTML="",e){const i=this.el("button","timeline-toggle-btn",this.toggleContainer),n=`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="${this.isExpanded?"18 15 12 9 6 15":"6 9 12 15 18 9"}"></polyline></svg>`;i.innerHTML=this.isExpanded?`${this.t("less")} ${n}`:`${this.t("more",{n:t.length-this.visibleCount})} ${n}`,i.setAttribute("aria-expanded",this.isExpanded),i.onclick=()=>{this.isExpanded=!this.isExpanded,this.updateVisibility()}}}renderCurrentDateLine(){const t=new Date(this.minYear,0,1).getTime(),e=new Date(this.maxYear,11,31).getTime()-t,s=Date.now()-t;if(s<0||s>e)return;const i=new Date().toISOString().split("T")[0],n=this.el("div","current-date-indicator",this.indicators);n.style.left=`${s/e*100}%`,n.setAttribute("role","separator"),n.setAttribute("aria-label",this.t("today",{date:i}));const o=this.el("div","current-date-badge",n);o.textContent=i,o.setAttribute("aria-hidden","true")}}return g}));
4
+ `,i.onclick=()=>this.highlightSegment(e)})}highlightSegment(t){this.activeHighlight=this.activeHighlight===t?null:t,this.root.classList.remove("lt-highlight-oss","lt-highlight-ent","lt-highlight-eol"),this.activeHighlight&&this.root.classList.add(`lt-highlight-${this.activeHighlight}`),this.renderLegend()}renderAxis(){this.el("div","",this.axis,{width:"96px",flexShrink:"0"}).setAttribute("role","presentation");for(let e=this.minYear;e<=this.maxYear;e++){const i=this.el("div","lt-year",this.axis);i.textContent=e,i.setAttribute("role","columnheader")}}renderGrid(){const t=new Date(this.minYear,0,1).getTime(),e=new Date(this.maxYear,11,31).getTime()-t;for(let i=this.minYear;i<=this.maxYear;i++){const s=this.el("div","lt-year-grid-line",this.grid);s.style.left=`${(new Date(i,0,1).getTime()-t)/e*100}%`,s.setAttribute("role","presentation")}}createRow(t,e){const i=this.el("div","lt-row row-entrance",this.tracks);i.setAttribute("role","row"),i.style.transitionDelay=`${e*.05}s`;const s=this.el("div","lt-version-label",i);s.setAttribute("role","rowheader");const n=Date.now(),o=new Date(t.ossStart).getTime(),a=new Date(t.ossEnd).getTime(),r=t.enterpriseEnd?new Date(t.enterpriseEnd).getTime():a,d=n>=o&&n<=a?"lt-status-oss":n>a&&n<=r?"lt-status-ent":n>r?"lt-status-expired":"";if(d&&s.classList.add(d),t.releaseNotesUrl){const h=this.el("a","lt-version-link",s);h.href=t.releaseNotesUrl,h.target="_blank",h.innerHTML=this.highlight(t.version),h.title=this.t("notes",{v:t.version}),h.setAttribute("aria-label",this.t("notes",{v:t.version}))}else s.innerHTML=this.highlight(t.version);const l=this.el("div","lt-track-container",i);return l.setAttribute("role","gridcell"),l.appendChild(this.createBar(t,t.ossStart,t.enterpriseEnd||t.ossEnd,"lt-bar-ent",this.t("ent"))),l.appendChild(this.createBar(t,t.ossStart,t.ossEnd,"lt-bar-oss",this.t("oss"))),{el:i,version:t.version.toLowerCase(),versionOriginal:t.version}}createBar(t,e,i,s,n){const o=new Date(e).getTime(),a=new Date(i).getTime(),r=new Date(this.minYear,0,1).getTime(),d=new Date(this.maxYear,11,31).getTime()-r,l=this.el("div",`lt-bar-segment ${s}`),h=s==="lt-bar-oss"?"lt-segment-oss":"lt-segment-ent";l.classList.add(h),l.style.left=`${(o-r)/d*100}%`,l.style.width=`${(a-o)/d*100}%`,l.setAttribute("role","img"),l.setAttribute("aria-label",`${n}: ${t.version} (${e} to ${i})`),l.tabIndex=0;const u=`
5
+ <div class="lt-tooltip-header">${n} - ${t.version}</div>
6
+ <div class="lt-tooltip-date"><strong>Du:</strong> ${e}</div>
7
+ <div class="lt-tooltip-date"><strong>Au:</strong> ${i}</div>
8
+ `;return l.onmouseenter=p=>this.showTooltip(p,u),l.onmousemove=p=>this.updateTooltipPos(p),l.onmouseleave=()=>this.hideTooltip(),l.onfocus=p=>{const b=l.getBoundingClientRect();this.showTooltip({pageX:b.left+window.scrollX,pageY:b.top+window.scrollY-40},u)},l.onblur=()=>this.hideTooltip(),l}updateVisibility(){const t=this.rows.filter(s=>s.version.includes(this.filterText));this.rows.forEach(s=>{const n=s.el.querySelector(".lt-version-label"),o=n.querySelector(".lt-version-link");o?o.innerHTML=this.highlight(s.versionOriginal||s.version):n.innerHTML=this.highlight(s.versionOriginal||s.version),s.el.classList.remove("lt-row-visible"),s.el.classList.add("lt-row-hidden")});const e=this.filterText===""&&t.length>this.visibleCount;if((e&&!this.isExpanded?t.slice(0,this.visibleCount):t).forEach(s=>{s.el.classList.remove("lt-row-hidden"),s.el.classList.add("lt-row-visible")}),this.showTable&&this.updateTableVisibility(),this.toggleContainer.innerHTML="",e){const s=this.el("button","lt-toggle-btn",this.toggleContainer),n=`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="${this.isExpanded?"18 15 12 9 6 15":"6 9 12 15 18 9"}"></polyline></svg>`;s.innerHTML=this.isExpanded?`${this.t("less")} ${n}`:`${this.t("more",{n:t.length-this.visibleCount})} ${n}`,s.setAttribute("aria-expanded",this.isExpanded),s.onclick=()=>{this.isExpanded=!this.isExpanded,this.updateVisibility()}}}renderCurrentDateLine(){const t=new Date(this.minYear,0,1).getTime(),e=new Date(this.maxYear,11,31).getTime()-t,i=Date.now()-t;if(i<0||i>e)return;const s=new Date().toISOString().split("T")[0],n=this.el("div","lt-current-date-indicator",this.indicators);n.style.left=`${i/e*100}%`,n.setAttribute("role","separator"),n.setAttribute("aria-label",this.t("today",{date:s}));const o=this.el("div","lt-current-date-badge",n);o.textContent=s,o.setAttribute("aria-hidden","true")}}return g}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lifecycle-timeline",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "A beautiful, premium JS timeline component for visualizing product lifecycles, OSS support, and enterprise support dates.",
5
5
  "type": "module",
6
6
  "main": "./dist/timeline.umd.cjs",
@@ -12,7 +12,8 @@
12
12
  "import": "./dist/timeline.js",
13
13
  "require": "./dist/timeline.umd.cjs"
14
14
  },
15
- "./style.css": "./dist/timeline.css"
15
+ "./style.css": "./dist/timeline.css",
16
+ "./dist/timeline.css": "./dist/timeline.css"
16
17
  },
17
18
  "files": [
18
19
  "dist",