| 147 | }; |
| 148 | |
| 149 | const calculateTabValues = <T extends TabValue>({ |
| 150 | tabs, |
| 151 | availableWidth, |
| 152 | tabWidthByValue, |
| 153 | overflowTriggerWidth, |
| 154 | tabGap, |
| 155 | }: { |
| 156 | tabs: readonly T[]; |
| 157 | availableWidth: number; |
| 158 | tabWidthByValue: Readonly<Record<string, number>>; |
| 159 | overflowTriggerWidth: number; |
| 160 | tabGap: number; |
| 161 | }): string[] => { |
| 162 | if (tabs.length <= ALWAYS_VISIBLE_TABS_COUNT) { |
| 163 | return []; |
| 164 | } |
| 165 | |
| 166 | let usedWidth = 0; |
| 167 | let visibleCount = 0; |
| 168 | |
| 169 | for (const [index, tab] of tabs.entries()) { |
| 170 | const tabWidth = tabWidthByValue[tab.value] ?? 0; |
| 171 | const gapBeforeTab = visibleCount > 0 ? tabGap : 0; |
| 172 | const usedWidthWithTab = usedWidth + gapBeforeTab + tabWidth; |
| 173 | const hasMoreTabs = index < tabs.length - 1; |
| 174 | // Reserve kebab trigger width whenever additional tabs remain. |
| 175 | const widthNeeded = |
| 176 | usedWidthWithTab + (hasMoreTabs ? tabGap + overflowTriggerWidth : 0); |
| 177 | |
| 178 | if (index < ALWAYS_VISIBLE_TABS_COUNT || widthNeeded <= availableWidth) { |
| 179 | usedWidth = usedWidthWithTab; |
| 180 | visibleCount += 1; |
| 181 | continue; |
| 182 | } |
| 183 | |
| 184 | return tabs.slice(index).map((overflowTab) => overflowTab.value); |
| 185 | } |
| 186 | |
| 187 | return []; |
| 188 | }; |
| 189 | |
| 190 | const measureTabWidths = <T extends TabValue>({ |
| 191 | tabs, |