lifecycle-timeline 1.2.7 → 1.2.9

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/dist/timeline.css CHANGED
@@ -1 +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;--accent-eol: #ef4444;--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;--accent-eol: #f87171}.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.lt-version-major{font-weight:800;border:2px solid var(--border-color)}.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(--accent-eol);color:#fff}.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:100%;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;height:100%}.lt-bar-eol{background:linear-gradient(to bottom,#fee2e2 0%,var(--accent-eol) 100%);z-index:0;border-radius:0;opacity:.8}.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(--accent-eol)}.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-checkbox-label{display:flex;align-items:center;gap:8px;font-size:.85rem;color:var(--text-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.lt-checkbox-label input{width:16px;height:16px;cursor:pointer}.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}.lt-mode-compact .lt-row{margin-bottom:.85rem;height:24px}.lt-mode-compact .lt-version-label{font-size:.75rem;padding:2px 4px}.lt-mode-compact .lt-bar-segment{font-size:.7rem}[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(--accent-eol);color:#fff}.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}
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;--accent-eol: #ef4444;--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;--accent-eol: #f87171}.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;min-height:36px;height:auto;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:90px;min-width:90px;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;white-space:normal;word-wrap:break-word;overflow:visible;display:flex;align-items:center;justify-content:center;line-height:1.2}.lt-version-label.lt-version-major{font-weight:800;border:2px solid var(--border-color)}.lt-version-label:hover{transform:scale(1.05);box-shadow:0 4px 12px #0000001a;filter:brightness(.95)}.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(--accent-eol);color:#fff}.lt-status-future{background-color:var(--bg-track);color:var(--text-secondary);border:1px solid var(--border-color)}.lt-track-container{flex-grow:1;position:relative;height:36px;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:100%;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;height:100%}.lt-bar-eol{background:linear-gradient(to bottom,#fee2e2 0%,var(--accent-eol) 100%);z-index:0;border-radius:0;opacity:.8}.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(--accent-eol)}.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-checkbox-label{display:flex;align-items:center;gap:8px;font-size:.85rem;color:var(--text-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.lt-checkbox-label input{width:16px;height:16px;cursor:pointer}.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}.lt-mode-compact .lt-row{margin-bottom:.85rem;height:24px}.lt-mode-compact .lt-version-label{font-size:.75rem;padding:2px 4px}.lt-mode-compact .lt-bar-segment{font-size:.7rem}[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(--accent-eol);color:#fff}.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}
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Represents a Lifecycle Timeline component.
3
+ */
4
+ declare class Timeline {
5
+ root: HTMLElement | null;
6
+ options: TimelineOptions;
7
+ visibleCount: number;
8
+ showTable: boolean;
9
+ showThemeToggle: boolean;
10
+ showLegend: boolean;
11
+ filterVersions: boolean;
12
+ splitSupport: boolean;
13
+ compactMode: boolean;
14
+ showMajorFilter: boolean;
15
+ isExpanded: boolean;
16
+ theme: 'light' | 'dark';
17
+ filterText: string;
18
+ majorFilterEnabled: boolean;
19
+ activeHighlight: string | null;
20
+ translations: Record<string, Record<string, string>>;
21
+ locale: string;
22
+ data: TimelineVersion[];
23
+ minYear: number;
24
+ maxYear: number;
25
+ currentDate: Date;
26
+ tableContainer: HTMLElement;
27
+ wrapper: HTMLElement;
28
+ axis: HTMLElement;
29
+ tracks: HTMLElement;
30
+ legendContainer: HTMLElement;
31
+ tableBody: HTMLElement;
32
+ tableRows: {
33
+ el: HTMLElement;
34
+ version: string;
35
+ versionOriginal: string;
36
+ }[];
37
+ tableToggleContainer: HTMLElement;
38
+ grid: HTMLElement;
39
+ indicators: HTMLElement;
40
+ rows: {
41
+ el: HTMLElement;
42
+ version: string;
43
+ versionOriginal: string;
44
+ isMajor?: boolean;
45
+ }[];
46
+ toggleContainer: HTMLElement;
47
+ tooltip: HTMLElement;
48
+ /**
49
+ * Creates an instance of Timeline.
50
+ * @param elementId - The ID of the root element.
51
+ * @param data - The data array containing version details.
52
+ * @param options - Configuration options.
53
+ */
54
+ constructor(elementId: string, data: TimelineVersion[], options?: TimelineOptions);
55
+ /**
56
+ * Detects the browser language.
57
+ * @returns The detected language code or 'en'.
58
+ */
59
+ detectLanguage(): string;
60
+ /**
61
+ * Translates a key based on the current locale.
62
+ * @param key - The translation key.
63
+ * @param params - Parameters to replace in the translation string.
64
+ * @returns The translated string.
65
+ */
66
+ t(key: string, params?: Record<string, any>): string;
67
+ /**
68
+ * Sets up the initial layout of the timeline.
69
+ */
70
+ setupBaseLayout(): void;
71
+ /**
72
+ * Updates the timeline data and re-renders.
73
+ * @param newData - The new data array.
74
+ */
75
+ updateData(newData: TimelineVersion[]): void;
76
+ /**
77
+ * Validates the input data.
78
+ * @param data - The data array to validate.
79
+ * @returns The validated and filtered data array.
80
+ */
81
+ validateData(data: TimelineVersion[]): TimelineVersion[];
82
+ /**
83
+ * Calculates the min and max years based on the data.
84
+ */
85
+ calculateTimeRange(): void;
86
+ /**
87
+ * Renders the data table.
88
+ */
89
+ renderTable(): void;
90
+ /**
91
+ * Creates a row for the data table.
92
+ * @param item - Version data.
93
+ */
94
+ createTableRow(item: TimelineVersion): {
95
+ el: HTMLElement;
96
+ version: string;
97
+ versionOriginal: string;
98
+ };
99
+ /**
100
+ * Updates table visibility based on filter and visibleCount.
101
+ */
102
+ updateTableVisibility(): void;
103
+ /**
104
+ * Renders the entire timeline.
105
+ */
106
+ render(): void;
107
+ /**
108
+ * Sets up the tooltip overlay.
109
+ */
110
+ setupTooltip(): void;
111
+ /**
112
+ * Shows the tooltip with provided content.
113
+ * @param e - The mouse event or position object.
114
+ * @param content - The HTML content for the tooltip.
115
+ */
116
+ showTooltip(e: MouseEvent | {
117
+ pageX: number;
118
+ pageY: number;
119
+ }, content: string): void;
120
+ /**
121
+ * Updates tooltip position relative to mouse.
122
+ * @param e - The mouse event or position object.
123
+ */
124
+ updateTooltipPos(e: MouseEvent | {
125
+ pageX: number;
126
+ pageY: number;
127
+ clientX?: number;
128
+ clientY?: number;
129
+ }): void;
130
+ /**
131
+ * Hides the tooltip.
132
+ */
133
+ hideTooltip(): void;
134
+ /**
135
+ * Highlights the search term in a given text.
136
+ * @param text - The text to process.
137
+ * @returns The HTML with highlighted segments.
138
+ */
139
+ highlight(text: string): string;
140
+ /**
141
+ * Helper to create DOM elements with styles and classes.
142
+ * @param tag - HTML tag.
143
+ * @param className - CSS class name.
144
+ * @param parent - Parent element.
145
+ * @param styles - Inline styles.
146
+ * @returns The created element.
147
+ */
148
+ el(tag: string, className: string, parent?: HTMLElement, styles?: Record<string, string>): HTMLElement;
149
+ /**
150
+ * Renders the toolbar with the filter input.
151
+ */
152
+ renderToolbar(): void;
153
+ /**
154
+ * Renders the theme toggle button.
155
+ */
156
+ renderThemeToggle(): void;
157
+ /**
158
+ * Renders the legend section.
159
+ */
160
+ renderLegend(): void;
161
+ /**
162
+ * Toggles highlighting for a specific segment type.
163
+ * @param type - 'oss', 'ent', or 'eol'.
164
+ */
165
+ highlightSegment(type: string): void;
166
+ /**
167
+ * Renders the year axis.
168
+ */
169
+ renderAxis(): void;
170
+ /**
171
+ * Renders the vertical grid lines for years.
172
+ */
173
+ renderGrid(): void;
174
+ /**
175
+ * Creates a row for a specific version.
176
+ * @param item - The version data item.
177
+ * @returns Metadata about the created row.
178
+ */
179
+ createRow(item: TimelineVersion, index: number): {
180
+ el: HTMLElement;
181
+ version: string;
182
+ versionOriginal: string;
183
+ isMajor?: boolean;
184
+ };
185
+ /**
186
+ * Creates a colored bar segment for the timeline.
187
+ * @param item - The version item.
188
+ * @param startStr - Start date string.
189
+ * @param endStr - End date string.
190
+ * @param className - CSS class.
191
+ * @param label - Support type label for tooltip.
192
+ * @returns The created bar element.
193
+ */
194
+ createBar(item: TimelineVersion, startStr: string, endStr: string, className: string, label: string): HTMLElement;
195
+ /**
196
+ * Updates visibility of rows based on filtering and expansion state.
197
+ */
198
+ updateVisibility(): void;
199
+ /**
200
+ * Renders the vertical line indicating current date.
201
+ */
202
+ renderCurrentDateLine(): void;
203
+ }
204
+ export default Timeline;
205
+
206
+ declare interface TimelineOptions {
207
+ /**
208
+ * Number of versions to show before "Show more" button appears.
209
+ * @default 3
210
+ */
211
+ visibleCount?: number;
212
+ /**
213
+ * Preferred locale for translations ('en' or 'fr').
214
+ * Defaults to browser language.
215
+ */
216
+ locale?: 'en' | 'fr' | string;
217
+ /**
218
+ * Custom translations to merge or override.
219
+ */
220
+ i18n?: Record<string, Record<string, string>>;
221
+ /**
222
+ * Whether to show the data table between filter and timeline.
223
+ * @default true
224
+ */
225
+ showTable?: boolean;
226
+ /**
227
+ * Whether to show the legend below the timeline.
228
+ * @default true
229
+ */
230
+ showLegend?: boolean;
231
+ /**
232
+ * Whether to show the version filter input.
233
+ * @default true
234
+ */
235
+ filterVersions?: boolean;
236
+ /**
237
+ * Whether to show the enterprise bar normally (false) or split after OSS (true).
238
+ * @default false
239
+ */
240
+ splitSupport?: boolean;
241
+ /**
242
+ * Whether to use compact mode (reduced height).
243
+ * @default false
244
+ */
245
+ compactMode?: boolean;
246
+ /**
247
+ * Whether to show the major version filter checkbox.
248
+ * @default false
249
+ */
250
+ showMajorFilter?: boolean;
251
+ /**
252
+ * Whether the major version filter runs by default.
253
+ * @default false
254
+ */
255
+ majorFilterDefault?: boolean;
256
+ }
257
+
258
+ declare interface TimelineVersion {
259
+ version: string;
260
+ ossStart: string;
261
+ ossEnd: string;
262
+ enterpriseEnd?: string;
263
+ releaseNotesUrl?: string;
264
+ isMajor?: boolean;
265
+ }
266
+
267
+ export { }
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",
@@ -38,22 +38,57 @@ const u = {
38
38
  majorOnly: "Versions majeures uniquement"
39
39
  }
40
40
  };
41
- class v {
41
+ class f {
42
+ root;
43
+ options;
44
+ visibleCount;
45
+ showTable;
46
+ showThemeToggle;
47
+ showLegend;
48
+ filterVersions;
49
+ splitSupport;
50
+ compactMode;
51
+ showMajorFilter;
52
+ isExpanded;
53
+ theme;
54
+ filterText;
55
+ majorFilterEnabled;
56
+ activeHighlight;
57
+ translations;
58
+ locale;
59
+ data = [];
60
+ minYear = 0;
61
+ maxYear = 0;
62
+ currentDate = /* @__PURE__ */ new Date();
63
+ tableContainer;
64
+ wrapper;
65
+ axis;
66
+ tracks;
67
+ legendContainer;
68
+ tableBody;
69
+ tableRows = [];
70
+ tableToggleContainer;
71
+ grid;
72
+ indicators;
73
+ rows = [];
74
+ toggleContainer;
75
+ tooltip;
42
76
  /**
43
77
  * Creates an instance of Timeline.
44
- * @param {string} elementId - The ID of the root element.
45
- * @param {Array<Object>} data - The data array containing version details.
46
- * @param {Object} [options={}] - Configuration options.
47
- * @param {number} [options.visibleCount=3] - Number of versions to show initially.
48
- * @param {string} [options.locale] - Language code (e.g., 'en', 'fr').
49
- * @param {Object} [options.i18n] - Custom translations to merge or override.
78
+ * @param elementId - The ID of the root element.
79
+ * @param data - The data array containing version details.
80
+ * @param options - Configuration options.
50
81
  */
51
82
  constructor(t, e, i = {}) {
52
- 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.showLegend = i.showLegend !== !1, this.filterVersions = i.filterVersions !== !1, this.splitSupport = i.splitSupport === !0, this.compactMode = i.compactMode === !0, this.showMajorFilter = i.showMajorFilter === !0, this.isExpanded = !1, this.theme = "light", this.filterText = "", this.majorFilterEnabled = !1, this.activeHighlight = null, this.root.classList.add("lt-root"), this.root.classList.toggle("lt-mode-split", this.splitSupport), this.root.classList.toggle("lt-mode-overlay", !this.splitSupport), this.root.classList.toggle("lt-mode-compact", this.compactMode), this.root.setAttribute("data-theme", this.theme), this.translations = { ...u, ...i.i18n || {} }, this.locale = i.locale || this.detectLanguage(), this.setupBaseLayout(), this.updateData(e));
83
+ if (this.root = document.getElementById(t), !this.root) {
84
+ this.options = i, this.visibleCount = 3, this.showTable = !0, this.showThemeToggle = !0, this.showLegend = !0, this.filterVersions = !0, this.splitSupport = !1, this.compactMode = !1, this.showMajorFilter = !1, this.isExpanded = !1, this.theme = "light", this.filterText = "", this.majorFilterEnabled = !1, this.activeHighlight = null, this.translations = {}, this.locale = "en";
85
+ return;
86
+ }
87
+ this.options = i, this.visibleCount = i.visibleCount || 3, this.showTable = i.showTable !== !1, this.showThemeToggle = i.showThemeToggle !== !1, this.showLegend = i.showLegend !== !1, this.filterVersions = i.filterVersions !== !1, this.splitSupport = i.splitSupport === !0, this.compactMode = i.compactMode === !0, this.showMajorFilter = i.showMajorFilter === !0, this.isExpanded = !1, this.theme = "light", this.filterText = "", this.majorFilterEnabled = i.majorFilterDefault === !0, this.activeHighlight = null, this.root.classList.add("lt-root"), this.root.classList.toggle("lt-mode-split", this.splitSupport), this.root.classList.toggle("lt-mode-overlay", !this.splitSupport), this.root.classList.toggle("lt-mode-compact", this.compactMode), this.root.setAttribute("data-theme", this.theme), this.translations = { ...p, ...i.i18n || {} }, this.locale = i.locale || this.detectLanguage(), this.setupBaseLayout(), this.updateData(e);
53
88
  }
54
89
  /**
55
90
  * Detects the browser language.
56
- * @returns {string} The detected language code or 'en'.
91
+ * @returns The detected language code or 'en'.
57
92
  */
58
93
  detectLanguage() {
59
94
  const t = (navigator.language || "en").split("-")[0];
@@ -61,9 +96,9 @@ class v {
61
96
  }
62
97
  /**
63
98
  * Translates a key based on the current locale.
64
- * @param {string} key - The translation key.
65
- * @param {Object} [params={}] - Parameters to replace in the translation string.
66
- * @returns {string} The translated string.
99
+ * @param key - The translation key.
100
+ * @param params - Parameters to replace in the translation string.
101
+ * @returns The translated string.
67
102
  */
68
103
  t(t, e = {}) {
69
104
  let i = (this.translations[this.locale] || this.translations.en)[t] || t;
@@ -73,26 +108,26 @@ class v {
73
108
  * Sets up the initial layout of the timeline.
74
109
  */
75
110
  setupBaseLayout() {
76
- 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.showLegend && (this.legendContainer = this.el("div", "lt-legend-container", this.wrapper));
111
+ this.root && (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.showLegend && (this.legendContainer = this.el("div", "lt-legend-container", this.wrapper)));
77
112
  }
78
113
  /**
79
114
  * Updates the timeline data and re-renders.
80
- * @param {Array<Object>} newData - The new data array.
115
+ * @param newData - The new data array.
81
116
  */
82
117
  updateData(t) {
83
118
  this.data = this.validateData(t || []), this.calculateTimeRange(), this.render();
84
119
  }
85
120
  /**
86
121
  * Validates the input data.
87
- * @param {Array<Object>} data - The data array to validate.
88
- * @returns {Array<Object>} The validated and filtered data array.
122
+ * @param data - The data array to validate.
123
+ * @returns The validated and filtered data array.
89
124
  */
90
125
  validateData(t) {
91
126
  return t.filter((e, i) => {
92
- const n = ["version", "ossStart", "ossEnd"].filter((r) => !e[r]);
93
- if (n.length > 0)
94
- return console.warn(`[Timeline] Missing fields for item at index ${i}: ${n.join(", ")}`), !1;
95
- const a = ["ossStart", "ossEnd", "enterpriseEnd"].filter((r) => e[r]).filter((r) => isNaN(new Date(e[r]).getTime()));
127
+ const o = ["version", "ossStart", "ossEnd"].filter((l) => !e[l]);
128
+ if (o.length > 0)
129
+ return console.warn(`[Timeline] Missing fields for item at index ${i}: ${o.join(", ")}`), !1;
130
+ const a = ["ossStart", "ossEnd", "enterpriseEnd"].filter((l) => e[l]).filter((l) => isNaN(new Date(e[l]).getTime()));
96
131
  return a.length > 0 ? (console.warn(`[Timeline] Invalid date format for item "${e.version}": ${a.join(", ")}`), !1) : !0;
97
132
  });
98
133
  }
@@ -105,8 +140,8 @@ class v {
105
140
  return;
106
141
  }
107
142
  const t = this.data.flatMap((e) => [
108
- new Date(e.ossStart),
109
- new Date(e.enterpriseEnd || e.ossEnd)
143
+ new Date(e.ossStart).getTime(),
144
+ new Date(e.enterpriseEnd || e.ossEnd).getTime()
110
145
  ]);
111
146
  this.minYear = new Date(Math.min(...t)).getFullYear(), this.maxYear = new Date(Math.max(...t)).getFullYear();
112
147
  }
@@ -123,18 +158,18 @@ class v {
123
158
  }
124
159
  /**
125
160
  * Creates a row for the data table.
126
- * @param {Object} item - Version data.
161
+ * @param item - Version data.
127
162
  */
128
163
  createTableRow(t) {
129
- const e = this.el("tr", "", this.tableBody), i = Date.now(), s = new Date(t.ossStart).getTime(), n = new Date(t.ossEnd).getTime(), l = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : n, a = i >= s && i <= n ? "lt-status-oss" : i > n && i <= l ? "lt-status-ent" : i > l ? "lt-status-expired" : "", r = this.el("td", "", e);
130
- let h = `<span class="lt-table-badge ${a}">${t.versionOriginal || t.version}</span>`;
131
- t.releaseNotesUrl ? r.innerHTML = `<a href="${t.releaseNotesUrl}" target="_blank" class="lt-table-version-link">${h}</a>` : r.innerHTML = h;
132
- const o = this.el("td", "", e);
133
- o.textContent = t.ossStart, i > s && (o.className = "lt-past-date");
164
+ const e = this.el("tr", "", this.tableBody), i = Date.now(), s = new Date(t.ossStart).getTime(), o = new Date(t.ossEnd).getTime(), n = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : o, a = i >= s && i <= o ? "lt-status-oss" : i > o && i <= n ? "lt-status-ent" : i > n ? "lt-status-expired" : "", l = this.el("td", "", e);
165
+ let h = `<span class="lt-table-badge ${a}">${t.version}</span>`;
166
+ t.releaseNotesUrl ? l.innerHTML = `<a href="${t.releaseNotesUrl}" target="_blank" class="lt-table-version-link">${h}</a>` : l.innerHTML = h;
167
+ const r = this.el("td", "", e);
168
+ r.textContent = t.ossStart, i > s && (r.className = "lt-past-date");
134
169
  const g = this.el("td", "", e);
135
- g.textContent = t.ossEnd, i > n && (g.className = "lt-past-date");
170
+ g.textContent = t.ossEnd, i > o && (g.className = "lt-past-date");
136
171
  const c = this.el("td", "", e);
137
- return c.textContent = t.enterpriseEnd || t.ossEnd, i > l && (c.className = "lt-past-date"), { el: e, version: t.version.toLowerCase(), versionOriginal: t.versionOriginal || t.version };
172
+ return c.textContent = t.enterpriseEnd || t.ossEnd, i > n && (c.className = "lt-past-date"), { el: e, version: t.version.toLowerCase(), versionOriginal: t.version };
138
173
  }
139
174
  /**
140
175
  * Updates table visibility based on filter and visibleCount.
@@ -142,15 +177,15 @@ class v {
142
177
  updateTableVisibility() {
143
178
  const t = this.tableRows.filter((s) => s.version.includes(this.filterText));
144
179
  this.tableRows.forEach((s) => {
145
- const n = s.el.querySelector(".lt-table-badge");
146
- n.innerHTML = this.highlight(s.versionOriginal || s.version), s.el.classList.remove("lt-row-visible"), s.el.classList.add("lt-row-hidden");
180
+ const o = s.el.querySelector(".lt-table-badge");
181
+ o && (o.innerHTML = this.highlight(s.versionOriginal || s.version)), s.el.classList.remove("lt-row-visible"), s.el.classList.add("lt-row-hidden");
147
182
  });
148
183
  const e = this.filterText === "" && t.length > this.visibleCount;
149
184
  if ((e && !this.isExpanded ? t.slice(0, this.visibleCount) : t).forEach((s) => {
150
185
  s.el.classList.remove("lt-row-hidden"), s.el.classList.add("lt-row-visible");
151
186
  }), this.tableToggleContainer.innerHTML = "", e) {
152
- 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>`;
153
- s.innerHTML = this.isExpanded ? `${this.t("less")} ${n}` : `${this.t("more", { n: t.length - this.visibleCount })} ${n}`, s.onclick = () => {
187
+ const s = this.el("button", "lt-toggle-btn lt-table-toggle", this.tableToggleContainer), o = `<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>`;
188
+ s.innerHTML = this.isExpanded ? `${this.t("less")} ${o}` : `${this.t("more", { n: t.length - this.visibleCount })} ${o}`, s.onclick = () => {
154
189
  this.isExpanded = !this.isExpanded, this.updateVisibility();
155
190
  };
156
191
  }
@@ -175,21 +210,22 @@ class v {
175
210
  }
176
211
  /**
177
212
  * Shows the tooltip with provided content.
178
- * @param {MouseEvent} e - The mouse event.
179
- * @param {string} content - The HTML content for the tooltip.
213
+ * @param e - The mouse event or position object.
214
+ * @param content - The HTML content for the tooltip.
180
215
  */
181
216
  showTooltip(t, e) {
182
217
  this.tooltip.innerHTML = e, this.tooltip.style.display = "block", this.updateTooltipPos(t);
183
218
  }
184
219
  /**
185
220
  * Updates tooltip position relative to mouse.
186
- * @param {MouseEvent} e - The mouse event.
221
+ * @param e - The mouse event or position object.
187
222
  */
188
223
  updateTooltipPos(t) {
189
- const e = this.root.getBoundingClientRect(), i = 12;
190
- let s = t.clientX - e.left + i, n = t.clientY - e.top + i;
191
- const l = this.tooltip.offsetWidth, a = this.tooltip.offsetHeight;
192
- s + l > this.root.offsetWidth && (s = t.clientX - e.left - l - 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`;
224
+ if (!this.root) return;
225
+ const e = this.root.getBoundingClientRect(), i = 12, s = "clientX" in t ? t.clientX : t.pageX - window.scrollX, o = "clientY" in t ? t.clientY : t.pageY - window.scrollY;
226
+ let n = (s || 0) - e.left + i, a = (o || 0) - e.top + i;
227
+ const l = this.tooltip.offsetWidth, h = this.tooltip.offsetHeight;
228
+ n + l > this.root.offsetWidth && (n = (s || 0) - e.left - l - i), a + h > this.root.offsetHeight && (a = (o || 0) - e.top - h - i), this.tooltip.style.left = `${n}px`, this.tooltip.style.top = `${a}px`;
193
229
  }
194
230
  /**
195
231
  * Hides the tooltip.
@@ -199,8 +235,8 @@ class v {
199
235
  }
200
236
  /**
201
237
  * Highlights the search term in a given text.
202
- * @param {string} text - The text to process.
203
- * @returns {string} The HTML with highlighted segments.
238
+ * @param text - The text to process.
239
+ * @returns The HTML with highlighted segments.
204
240
  */
205
241
  highlight(t) {
206
242
  if (!this.filterText) return t;
@@ -213,20 +249,21 @@ class v {
213
249
  }
214
250
  /**
215
251
  * Helper to create DOM elements with styles and classes.
216
- * @param {string} tag - HTML tag.
217
- * @param {string} className - CSS class name.
218
- * @param {HTMLElement} [parent] - Parent element.
219
- * @param {Object} [styles={}] - Inline styles.
220
- * @returns {HTMLElement} The created element.
252
+ * @param tag - HTML tag.
253
+ * @param className - CSS class name.
254
+ * @param parent - Parent element.
255
+ * @param styles - Inline styles.
256
+ * @returns The created element.
221
257
  */
222
258
  el(t, e, i, s = {}) {
223
- const n = document.createElement(t);
224
- return e && (n.className = e), Object.assign(n.style, s), i && i.appendChild(n), n;
259
+ const o = document.createElement(t);
260
+ return e && (o.className = e), Object.assign(o.style, s), i && i.appendChild(o), o;
225
261
  }
226
262
  /**
227
263
  * Renders the toolbar with the filter input.
228
264
  */
229
265
  renderToolbar() {
266
+ if (!this.root) return;
230
267
  const t = this.el("div", "lt-toolbar", this.root);
231
268
  if (this.filterVersions) {
232
269
  const e = this.el("div", "lt-filter-container", t);
@@ -238,8 +275,8 @@ class v {
238
275
  }
239
276
  if (this.showMajorFilter) {
240
277
  const e = this.el("label", "lt-checkbox-label", t), i = this.el("input", "", e);
241
- i.type = "checkbox", i.checked = this.majorFilterEnabled, i.onchange = (n) => {
242
- this.majorFilterEnabled = n.target.checked, this.updateVisibility();
278
+ i.type = "checkbox", i.checked = this.majorFilterEnabled, i.onchange = (o) => {
279
+ this.majorFilterEnabled = o.target.checked, this.updateVisibility();
243
280
  };
244
281
  const s = this.el("span", "", e);
245
282
  s.textContent = this.t("majorOnly");
@@ -249,6 +286,7 @@ class v {
249
286
  * Renders the theme toggle button.
250
287
  */
251
288
  renderThemeToggle() {
289
+ if (!this.root) return;
252
290
  const t = this.el("button", "lt-theme-toggle-btn", this.root);
253
291
  t.title = this.t("dark"), t.setAttribute("aria-label", this.t("dark"));
254
292
  const e = {
@@ -256,7 +294,7 @@ class v {
256
294
  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>'
257
295
  };
258
296
  t.innerHTML = this.theme === "dark" ? e.sun : e.moon, t.onclick = () => {
259
- 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");
297
+ 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").toString());
260
298
  };
261
299
  }
262
300
  /**
@@ -275,19 +313,19 @@ class v {
275
313
  }
276
314
  /**
277
315
  * Toggles highlighting for a specific segment type.
278
- * @param {string} type - 'oss', 'ent', or 'eol'.
316
+ * @param type - 'oss', 'ent', or 'eol'.
279
317
  */
280
318
  highlightSegment(t) {
281
- 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();
319
+ this.root && (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());
282
320
  }
283
321
  /**
284
322
  * Renders the year axis.
285
323
  */
286
324
  renderAxis() {
287
- this.el("div", "", this.axis, { width: "96px", flexShrink: "0" }).setAttribute("role", "presentation");
325
+ this.el("div", "", this.axis, { width: "106px", flexShrink: "0" }).setAttribute("role", "presentation");
288
326
  for (let e = this.minYear; e <= this.maxYear; e++) {
289
327
  const i = this.el("div", "lt-year", this.axis);
290
- i.textContent = e, i.setAttribute("role", "columnheader");
328
+ i.textContent = e.toString(), i.setAttribute("role", "columnheader");
291
329
  }
292
330
  }
293
331
  /**
@@ -302,65 +340,65 @@ class v {
302
340
  }
303
341
  /**
304
342
  * Creates a row for a specific version.
305
- * @param {Object} item - The version data item.
306
- * @returns {Object} Metadata about the created row.
343
+ * @param item - The version data item.
344
+ * @returns Metadata about the created row.
307
345
  */
308
346
  createRow(t, e) {
309
347
  const i = this.el("div", "lt-row row-entrance", this.tracks);
310
348
  i.setAttribute("role", "row"), i.style.transitionDelay = `${e * 0.05}s`;
311
349
  const s = this.el("div", "lt-version-label", i);
312
350
  s.setAttribute("role", "rowheader");
313
- const n = Date.now(), l = new Date(t.ossStart).getTime(), a = new Date(t.ossEnd).getTime(), r = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : a, h = n >= l && n <= a ? "lt-status-oss" : n > a && n <= r ? "lt-status-ent" : n > r ? "lt-status-expired" : "";
351
+ const o = Date.now(), n = new Date(t.ossStart).getTime(), a = new Date(t.ossEnd).getTime(), l = t.enterpriseEnd ? new Date(t.enterpriseEnd).getTime() : a, h = o < n ? "lt-status-future" : o >= n && o <= a ? "lt-status-oss" : o > a && o <= l ? "lt-status-ent" : o > l ? "lt-status-expired" : "";
314
352
  if (h && s.classList.add(h), t.isMajor && s.classList.add("lt-version-major"), t.releaseNotesUrl) {
315
- const d = this.el("a", "lt-version-link", s);
316
- d.href = t.releaseNotesUrl, d.target = "_blank", d.innerHTML = this.highlight(t.version), d.title = this.t("notes", { v: t.version }), d.setAttribute("aria-label", this.t("notes", { v: t.version }));
353
+ const u = this.el("a", "lt-version-link", s);
354
+ u.href = t.releaseNotesUrl, u.target = "_blank", u.innerHTML = this.highlight(t.version), u.title = this.t("notes", { v: t.version }), u.setAttribute("aria-label", this.t("notes", { v: t.version }));
317
355
  } else
318
- s.innerHTML = this.highlight(t.version);
319
- const o = this.el("div", "lt-track-container", i);
320
- o.setAttribute("role", "gridcell");
321
- const g = this.splitSupport ? t.ossEnd : t.ossStart, c = t.enterpriseEnd || t.ossEnd, p = new Date(this.maxYear, 11, 31).toISOString().split("T")[0];
322
- return o.appendChild(this.createBar(t, c, p, "lt-bar-eol", this.t("eol"))), o.appendChild(this.createBar(t, g, t.enterpriseEnd || t.ossEnd, "lt-bar-ent", this.t("ent"))), o.appendChild(this.createBar(t, t.ossStart, t.ossEnd, "lt-bar-oss", this.t("oss"))), { el: i, version: t.version.toLowerCase(), versionOriginal: t.version, isMajor: t.isMajor };
356
+ s.innerHTML = this.highlight(t.version), s.title = t.version;
357
+ const r = this.el("div", "lt-track-container", i);
358
+ r.setAttribute("role", "gridcell");
359
+ const g = this.splitSupport ? t.ossEnd : t.ossStart, c = t.enterpriseEnd || t.ossEnd, d = new Date(this.maxYear, 11, 31).toISOString().split("T")[0];
360
+ return r.appendChild(this.createBar(t, c, d, "lt-bar-eol", this.t("eol"))), r.appendChild(this.createBar(t, g, t.enterpriseEnd || t.ossEnd, "lt-bar-ent", this.t("ent"))), r.appendChild(this.createBar(t, t.ossStart, t.ossEnd, "lt-bar-oss", this.t("oss"))), { el: i, version: t.version.toLowerCase(), versionOriginal: t.version, isMajor: t.isMajor };
323
361
  }
324
362
  /**
325
363
  * Creates a colored bar segment for the timeline.
326
- * @param {Object} item - The version item.
327
- * @param {string} startStr - Start date string.
328
- * @param {string} endStr - End date string.
329
- * @param {string} className - CSS class.
330
- * @param {string} label - Support type label for tooltip.
331
- * @returns {HTMLElement} The created bar element.
364
+ * @param item - The version item.
365
+ * @param startStr - Start date string.
366
+ * @param endStr - End date string.
367
+ * @param className - CSS class.
368
+ * @param label - Support type label for tooltip.
369
+ * @returns The created bar element.
332
370
  */
333
- createBar(t, e, i, s, n) {
334
- const l = new Date(e).getTime(), a = new Date(i).getTime(), r = new Date(this.minYear, 0, 1).getTime(), h = new Date(this.maxYear, 11, 31).getTime() - r, o = this.el("div", `lt-bar-segment ${s}`), g = s === "lt-bar-oss" ? "lt-segment-oss" : s === "lt-bar-ent" ? "lt-segment-ent" : "lt-segment-eol";
335
- o.classList.add(g), o.style.left = `${(l - r) / h * 100}%`, o.style.width = `${(a - l) / h * 100}%`, o.setAttribute("role", "img"), o.setAttribute("aria-label", `${n}: ${t.version} (${e} to ${i})`), o.tabIndex = 0;
371
+ createBar(t, e, i, s, o) {
372
+ const n = new Date(e).getTime(), a = new Date(i).getTime(), l = new Date(this.minYear, 0, 1).getTime(), h = new Date(this.maxYear, 11, 31).getTime() - l, r = this.el("div", `lt-bar-segment ${s}`), g = s === "lt-bar-oss" ? "lt-segment-oss" : s === "lt-bar-ent" ? "lt-segment-ent" : "lt-segment-eol";
373
+ r.classList.add(g), r.style.left = `${(n - l) / h * 100}%`, r.style.width = `${(a - n) / h * 100}%`, r.setAttribute("role", "img"), r.setAttribute("aria-label", `${o}: ${t.version} (${e} to ${i})`), r.tabIndex = 0;
336
374
  const c = `
337
- <div class="lt-tooltip-header">${n} - ${t.version}</div>
375
+ <div class="lt-tooltip-header">${o} - ${t.version}</div>
338
376
  <div class="lt-tooltip-date"><strong>Du:</strong> ${e}</div>
339
377
  <div class="lt-tooltip-date"><strong>Au:</strong> ${i}</div>
340
378
  `;
341
- return o.onmouseenter = (p) => this.showTooltip(p, c), o.onmousemove = (p) => this.updateTooltipPos(p), o.onmouseleave = () => this.hideTooltip(), o.onfocus = (p) => {
342
- const d = o.getBoundingClientRect();
379
+ return r.onmouseenter = (d) => this.showTooltip(d, c), r.onmousemove = (d) => this.updateTooltipPos(d), r.onmouseleave = () => this.hideTooltip(), r.onfocus = () => {
380
+ const d = r.getBoundingClientRect();
343
381
  this.showTooltip({ pageX: d.left + window.scrollX, pageY: d.top + window.scrollY - 40 }, c);
344
- }, o.onblur = () => this.hideTooltip(), o;
382
+ }, r.onblur = () => this.hideTooltip(), r;
345
383
  }
346
384
  /**
347
385
  * Updates visibility of rows based on filtering and expansion state.
348
386
  */
349
387
  updateVisibility() {
350
388
  const t = this.rows.filter((s) => {
351
- const n = s.version.includes(this.filterText), l = this.majorFilterEnabled ? s.isMajor : !0;
352
- return n && l;
389
+ const o = s.version.includes(this.filterText), n = this.majorFilterEnabled ? s.isMajor : !0;
390
+ return o && n;
353
391
  });
354
392
  this.rows.forEach((s) => {
355
- const n = s.el.querySelector(".lt-version-label"), l = n.querySelector(".lt-version-link");
356
- l ? l.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");
393
+ const o = s.el.querySelector(".lt-version-label"), n = o.querySelector(".lt-version-link");
394
+ n ? n.innerHTML = this.highlight(s.versionOriginal || s.version) : o.innerHTML = this.highlight(s.versionOriginal || s.version), s.el.classList.remove("lt-row-visible"), s.el.classList.add("lt-row-hidden");
357
395
  });
358
396
  const e = this.filterText === "" && t.length > this.visibleCount;
359
397
  if ((e && !this.isExpanded ? t.slice(0, this.visibleCount) : t).forEach((s) => {
360
398
  s.el.classList.remove("lt-row-hidden"), s.el.classList.add("lt-row-visible");
361
399
  }), this.showTable && this.updateTableVisibility(), this.toggleContainer.innerHTML = "", e) {
362
- 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>`;
363
- 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 = () => {
400
+ const s = this.el("button", "lt-toggle-btn", this.toggleContainer), o = `<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>`;
401
+ s.innerHTML = this.isExpanded ? `${this.t("less")} ${o}` : `${this.t("more", { n: t.length - this.visibleCount })} ${o}`, s.setAttribute("aria-expanded", this.isExpanded.toString()), s.onclick = () => {
364
402
  this.isExpanded = !this.isExpanded, this.updateVisibility();
365
403
  };
366
404
  }
@@ -371,12 +409,12 @@ class v {
371
409
  renderCurrentDateLine() {
372
410
  const t = new Date(this.minYear, 0, 1).getTime(), e = new Date(this.maxYear, 11, 31).getTime() - t, i = Date.now() - t;
373
411
  if (i < 0 || i > e) return;
374
- const s = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], n = this.el("div", "lt-current-date-indicator", this.indicators);
375
- n.style.left = `${i / e * 100}%`, n.setAttribute("role", "separator"), n.setAttribute("aria-label", this.t("today", { date: s }));
376
- const l = this.el("div", "lt-current-date-badge", n);
377
- l.textContent = s, l.setAttribute("aria-hidden", "true");
412
+ const s = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], o = this.el("div", "lt-current-date-indicator", this.indicators);
413
+ o.style.left = `${i / e * 100}%`, o.setAttribute("role", "separator"), o.setAttribute("aria-label", this.t("today", { date: s }));
414
+ const n = this.el("div", "lt-current-date-badge", o);
415
+ n.textContent = s, n.setAttribute("aria-hidden", "true");
378
416
  }
379
417
  }
380
418
  export {
381
- v as default
419
+ f as default
382
420
  };
@@ -1,8 +1,8 @@
1
- (function(u,b){typeof exports=="object"&&typeof module<"u"?module.exports=b():typeof define=="function"&&define.amd?define(b):(u=typeof globalThis<"u"?globalThis:u||self,u.Timeline=b())})(this,(function(){"use strict";const u={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 *",majorOnly:"Major versions only"},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 *",majorOnly:"Versions majeures uniquement"}};class b{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.showLegend=i.showLegend!==!1,this.filterVersions=i.filterVersions!==!1,this.splitSupport=i.splitSupport===!0,this.compactMode=i.compactMode===!0,this.showMajorFilter=i.showMajorFilter===!0,this.isExpanded=!1,this.theme="light",this.filterText="",this.majorFilterEnabled=!1,this.activeHighlight=null,this.root.classList.add("lt-root"),this.root.classList.toggle("lt-mode-split",this.splitSupport),this.root.classList.toggle("lt-mode-overlay",!this.splitSupport),this.root.classList.toggle("lt-mode-compact",this.compactMode),this.root.setAttribute("data-theme",this.theme),this.translations={...u,...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.showLegend&&(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(),l=t.enterpriseEnd?new Date(t.enterpriseEnd).getTime():n,a=i>=s&&i<=n?"lt-status-oss":i>n&&i<=l?"lt-status-ent":i>l?"lt-status-expired":"",r=this.el("td","",e);let h=`<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">${h}</a>`:r.innerHTML=h;const o=this.el("td","",e);o.textContent=t.ossStart,i>s&&(o.className="lt-past-date");const g=this.el("td","",e);g.textContent=t.ossEnd,i>n&&(g.className="lt-past-date");const c=this.el("td","",e);return c.textContent=t.enterpriseEnd||t.ossEnd,i>l&&(c.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.showLegend&&(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.showLegend&&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 l=this.tooltip.offsetWidth,a=this.tooltip.offsetHeight;s+l>this.root.offsetWidth&&(s=t.clientX-e.left-l-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);if(this.filterVersions){const 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()}}if(this.showMajorFilter){const e=this.el("label","lt-checkbox-label",t),i=this.el("input","",e);i.type="checkbox",i.checked=this.majorFilterEnabled,i.onchange=n=>{this.majorFilterEnabled=n.target.checked,this.updateVisibility()};const s=this.el("span","",e);s.textContent=this.t("majorOnly")}}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=`
1
+ (function(u,p){typeof exports=="object"&&typeof module<"u"?module.exports=p():typeof define=="function"&&define.amd?define(p):(u=typeof globalThis<"u"?globalThis:u||self,u.Timeline=p())})(this,(function(){"use strict";const u={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 *",majorOnly:"Major versions only"},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 *",majorOnly:"Versions majeures uniquement"}};class p{root;options;visibleCount;showTable;showThemeToggle;showLegend;filterVersions;splitSupport;compactMode;showMajorFilter;isExpanded;theme;filterText;majorFilterEnabled;activeHighlight;translations;locale;data=[];minYear=0;maxYear=0;currentDate=new Date;tableContainer;wrapper;axis;tracks;legendContainer;tableBody;tableRows=[];tableToggleContainer;grid;indicators;rows=[];toggleContainer;tooltip;constructor(t,e,i={}){if(this.root=document.getElementById(t),!this.root){this.options=i,this.visibleCount=3,this.showTable=!0,this.showThemeToggle=!0,this.showLegend=!0,this.filterVersions=!0,this.splitSupport=!1,this.compactMode=!1,this.showMajorFilter=!1,this.isExpanded=!1,this.theme="light",this.filterText="",this.majorFilterEnabled=!1,this.activeHighlight=null,this.translations={},this.locale="en";return}this.options=i,this.visibleCount=i.visibleCount||3,this.showTable=i.showTable!==!1,this.showThemeToggle=i.showThemeToggle!==!1,this.showLegend=i.showLegend!==!1,this.filterVersions=i.filterVersions!==!1,this.splitSupport=i.splitSupport===!0,this.compactMode=i.compactMode===!0,this.showMajorFilter=i.showMajorFilter===!0,this.isExpanded=!1,this.theme="light",this.filterText="",this.majorFilterEnabled=i.majorFilterDefault===!0,this.activeHighlight=null,this.root.classList.add("lt-root"),this.root.classList.toggle("lt-mode-split",this.splitSupport),this.root.classList.toggle("lt-mode-overlay",!this.splitSupport),this.root.classList.toggle("lt-mode-compact",this.compactMode),this.root.setAttribute("data-theme",this.theme),this.translations={...u,...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&&(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.showLegend&&(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(l=>!e[l]);if(n.length>0)return console.warn(`[Timeline] Missing fields for item at index ${i}: ${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).getTime(),new Date(e.enterpriseEnd||e.ossEnd).getTime()]);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":"",l=this.el("td","",e);let h=`<span class="lt-table-badge ${a}">${t.version}</span>`;t.releaseNotesUrl?l.innerHTML=`<a href="${t.releaseNotesUrl}" target="_blank" class="lt-table-version-link">${h}</a>`:l.innerHTML=h;const r=this.el("td","",e);r.textContent=t.ossStart,i>s&&(r.className="lt-past-date");const g=this.el("td","",e);g.textContent=t.ossEnd,i>n&&(g.className="lt-past-date");const c=this.el("td","",e);return c.textContent=t.enterpriseEnd||t.ossEnd,i>o&&(c.className="lt-past-date"),{el:e,version:t.version.toLowerCase(),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&&(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.showLegend&&(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.showLegend&&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){if(!this.root)return;const e=this.root.getBoundingClientRect(),i=12,s="clientX"in t?t.clientX:t.pageX-window.scrollX,n="clientY"in t?t.clientY:t.pageY-window.scrollY;let o=(s||0)-e.left+i,a=(n||0)-e.top+i;const l=this.tooltip.offsetWidth,h=this.tooltip.offsetHeight;o+l>this.root.offsetWidth&&(o=(s||0)-e.left-l-i),a+h>this.root.offsetHeight&&(a=(n||0)-e.top-h-i),this.tooltip.style.left=`${o}px`,this.tooltip.style.top=`${a}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(){if(!this.root)return;const t=this.el("div","lt-toolbar",this.root);if(this.filterVersions){const 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()}}if(this.showMajorFilter){const e=this.el("label","lt-checkbox-label",t),i=this.el("input","",e);i.type="checkbox",i.checked=this.majorFilterEnabled,i.onchange=n=>{this.majorFilterEnabled=n.target.checked,this.updateVisibility()};const s=this.el("span","",e);s.textContent=this.t("majorOnly")}}renderThemeToggle(){if(!this.root)return;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").toString())}}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
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
- `,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(),l=new Date(t.ossStart).getTime(),a=new Date(t.ossEnd).getTime(),r=t.enterpriseEnd?new Date(t.enterpriseEnd).getTime():a,h=n>=l&&n<=a?"lt-status-oss":n>a&&n<=r?"lt-status-ent":n>r?"lt-status-expired":"";if(h&&s.classList.add(h),t.isMajor&&s.classList.add("lt-version-major"),t.releaseNotesUrl){const d=this.el("a","lt-version-link",s);d.href=t.releaseNotesUrl,d.target="_blank",d.innerHTML=this.highlight(t.version),d.title=this.t("notes",{v:t.version}),d.setAttribute("aria-label",this.t("notes",{v:t.version}))}else s.innerHTML=this.highlight(t.version);const o=this.el("div","lt-track-container",i);o.setAttribute("role","gridcell");const g=this.splitSupport?t.ossEnd:t.ossStart,c=t.enterpriseEnd||t.ossEnd,p=new Date(this.maxYear,11,31).toISOString().split("T")[0];return o.appendChild(this.createBar(t,c,p,"lt-bar-eol",this.t("eol"))),o.appendChild(this.createBar(t,g,t.enterpriseEnd||t.ossEnd,"lt-bar-ent",this.t("ent"))),o.appendChild(this.createBar(t,t.ossStart,t.ossEnd,"lt-bar-oss",this.t("oss"))),{el:i,version:t.version.toLowerCase(),versionOriginal:t.version,isMajor:t.isMajor}}createBar(t,e,i,s,n){const l=new Date(e).getTime(),a=new Date(i).getTime(),r=new Date(this.minYear,0,1).getTime(),h=new Date(this.maxYear,11,31).getTime()-r,o=this.el("div",`lt-bar-segment ${s}`),g=s==="lt-bar-oss"?"lt-segment-oss":s==="lt-bar-ent"?"lt-segment-ent":"lt-segment-eol";o.classList.add(g),o.style.left=`${(l-r)/h*100}%`,o.style.width=`${(a-l)/h*100}%`,o.setAttribute("role","img"),o.setAttribute("aria-label",`${n}: ${t.version} (${e} to ${i})`),o.tabIndex=0;const c=`
4
+ `,i.onclick=()=>this.highlightSegment(e)})}highlightSegment(t){this.root&&(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:"106px",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.toString(),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(),l=t.enterpriseEnd?new Date(t.enterpriseEnd).getTime():a,h=n<o?"lt-status-future":n>=o&&n<=a?"lt-status-oss":n>a&&n<=l?"lt-status-ent":n>l?"lt-status-expired":"";if(h&&s.classList.add(h),t.isMajor&&s.classList.add("lt-version-major"),t.releaseNotesUrl){const b=this.el("a","lt-version-link",s);b.href=t.releaseNotesUrl,b.target="_blank",b.innerHTML=this.highlight(t.version),b.title=this.t("notes",{v:t.version}),b.setAttribute("aria-label",this.t("notes",{v:t.version}))}else s.innerHTML=this.highlight(t.version),s.title=t.version;const r=this.el("div","lt-track-container",i);r.setAttribute("role","gridcell");const g=this.splitSupport?t.ossEnd:t.ossStart,c=t.enterpriseEnd||t.ossEnd,d=new Date(this.maxYear,11,31).toISOString().split("T")[0];return r.appendChild(this.createBar(t,c,d,"lt-bar-eol",this.t("eol"))),r.appendChild(this.createBar(t,g,t.enterpriseEnd||t.ossEnd,"lt-bar-ent",this.t("ent"))),r.appendChild(this.createBar(t,t.ossStart,t.ossEnd,"lt-bar-oss",this.t("oss"))),{el:i,version:t.version.toLowerCase(),versionOriginal:t.version,isMajor:t.isMajor}}createBar(t,e,i,s,n){const o=new Date(e).getTime(),a=new Date(i).getTime(),l=new Date(this.minYear,0,1).getTime(),h=new Date(this.maxYear,11,31).getTime()-l,r=this.el("div",`lt-bar-segment ${s}`),g=s==="lt-bar-oss"?"lt-segment-oss":s==="lt-bar-ent"?"lt-segment-ent":"lt-segment-eol";r.classList.add(g),r.style.left=`${(o-l)/h*100}%`,r.style.width=`${(a-o)/h*100}%`,r.setAttribute("role","img"),r.setAttribute("aria-label",`${n}: ${t.version} (${e} to ${i})`),r.tabIndex=0;const c=`
5
5
  <div class="lt-tooltip-header">${n} - ${t.version}</div>
6
6
  <div class="lt-tooltip-date"><strong>Du:</strong> ${e}</div>
7
7
  <div class="lt-tooltip-date"><strong>Au:</strong> ${i}</div>
8
- `;return o.onmouseenter=p=>this.showTooltip(p,c),o.onmousemove=p=>this.updateTooltipPos(p),o.onmouseleave=()=>this.hideTooltip(),o.onfocus=p=>{const d=o.getBoundingClientRect();this.showTooltip({pageX:d.left+window.scrollX,pageY:d.top+window.scrollY-40},c)},o.onblur=()=>this.hideTooltip(),o}updateVisibility(){const t=this.rows.filter(s=>{const n=s.version.includes(this.filterText),l=this.majorFilterEnabled?s.isMajor:!0;return n&&l});this.rows.forEach(s=>{const n=s.el.querySelector(".lt-version-label"),l=n.querySelector(".lt-version-link");l?l.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 l=this.el("div","lt-current-date-badge",n);l.textContent=s,l.setAttribute("aria-hidden","true")}}return b}));
8
+ `;return r.onmouseenter=d=>this.showTooltip(d,c),r.onmousemove=d=>this.updateTooltipPos(d),r.onmouseleave=()=>this.hideTooltip(),r.onfocus=()=>{const d=r.getBoundingClientRect();this.showTooltip({pageX:d.left+window.scrollX,pageY:d.top+window.scrollY-40},c)},r.onblur=()=>this.hideTooltip(),r}updateVisibility(){const t=this.rows.filter(s=>{const n=s.version.includes(this.filterText),o=this.majorFilterEnabled?s.isMajor:!0;return n&&o});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.toString()),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 p}));
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "lifecycle-timeline",
3
- "version": "1.2.7",
3
+ "version": "1.2.9",
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",
7
7
  "module": "./dist/timeline.js",
8
- "types": "./src/index.d.ts",
8
+ "types": "./dist/timeline.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./src/index.d.ts",
11
+ "types": "./dist/timeline.d.ts",
12
12
  "import": "./dist/timeline.js",
13
13
  "require": "./dist/timeline.umd.cjs"
14
14
  },
@@ -16,8 +16,7 @@
16
16
  "./dist/timeline.css": "./dist/timeline.css"
17
17
  },
18
18
  "files": [
19
- "dist",
20
- "src/index.d.ts"
19
+ "dist"
21
20
  ],
22
21
  "scripts": {
23
22
  "dev": "vite",
@@ -25,7 +24,6 @@
25
24
  "build:demo": "vite build --config vite.config.demo.js",
26
25
  "preview": "vite preview",
27
26
  "test": "vitest run",
28
- "prepublishOnly": "npm run build",
29
27
  "release": "npm run build && npm publish"
30
28
  },
31
29
  "keywords": [
@@ -61,10 +59,11 @@
61
59
  "devDependencies": {
62
60
  "@testing-library/dom": "^10.4.1",
63
61
  "jsdom": "^24.1.3",
62
+ "typescript": "^5.0.0",
63
+ "vite-plugin-dts": "^4.0.0",
64
+ "@types/node": "^20.0.0",
64
65
  "vite": "^7.3.0",
65
66
  "vitest": "^4.0.16"
66
67
  },
67
- "dependencies": {
68
- "html-to-image": "^1.11.13"
69
- }
68
+ "dependencies": {}
70
69
  }
package/src/index.d.ts DELETED
@@ -1,104 +0,0 @@
1
- export interface TimelineVersion {
2
- version: string;
3
- ossStart: string;
4
- ossEnd: string;
5
- enterpriseEnd: string;
6
- releaseNotesUrl?: string;
7
- isMajor?: boolean;
8
- }
9
-
10
- export interface TimelineOptions {
11
- /**
12
- * Number of versions to show before "Show more" button appears.
13
- * @default 3
14
- */
15
- visibleCount?: number;
16
-
17
- /**
18
- * Preferred locale for translations ('en' or 'fr').
19
- * Defaults to browser language.
20
- */
21
- locale?: 'en' | 'fr' | string;
22
-
23
- /**
24
- * Custom translations to merge or override.
25
- */
26
- i18n?: Record<string, Record<string, string>>;
27
-
28
- /**
29
- * Whether to show the data table between filter and timeline.
30
- * @default true
31
- */
32
- showTable?: boolean;
33
-
34
- /**
35
- * Whether to show the legend below the timeline.
36
- * @default true
37
- */
38
- showLegend?: boolean;
39
-
40
- /**
41
- * Whether to show the version filter input.
42
- * @default true
43
- */
44
- filterVersions?: boolean;
45
-
46
- /**
47
- * Whether to show the enterprise bar normally (false) or split after OSS (true).
48
- * @default false
49
- */
50
- splitSupport?: boolean;
51
-
52
- /**
53
- * Whether to use compact mode (reduced height).
54
- * @default false
55
- */
56
- compactMode?: boolean;
57
-
58
- /**
59
- * Whether to show the major version filter checkbox.
60
- * @default false
61
- */
62
- showMajorFilter?: boolean;
63
- }
64
-
65
- export default class Timeline {
66
- /**
67
- * Creates a new Lifecycle Timeline.
68
- * @param elementId The ID of the container element.
69
- * @param data Array of version data.
70
- * @param options Configuration options.
71
- */
72
- constructor(elementId: string, data: TimelineVersion[], options?: TimelineOptions);
73
-
74
- /**
75
- * Sets up the initial layout.
76
- */
77
- setupBaseLayout(): void;
78
-
79
- /**
80
- * Updates the timeline data and re-renders.
81
- * @param newData Array of version data.
82
- */
83
- updateData(newData: TimelineVersion[]): void;
84
-
85
- /**
86
- * Renders the entire timeline.
87
- */
88
- render(): void;
89
-
90
- /**
91
- * Renders the toolbar (search/filter).
92
- */
93
- renderToolbar(): void;
94
-
95
- /**
96
- * Renders the theme toggle button.
97
- */
98
- renderThemeToggle(): void;
99
-
100
- /**
101
- * Updates the visibility of rows based on filtering and expansion state.
102
- */
103
- updateVisibility(): void;
104
- }