Files
plezy/lib/widgets/alpha_jump_helper.dart
T
2026-02-05 10:15:59 +01:00

109 lines
3.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import '../models/plex_first_character.dart';
/// Shared letter-index mapping logic used by both [AlphaJumpBar] (desktop/tablet/TV)
/// and [AlphaScrollHandle] (phone).
///
/// Builds a cumulative index map from [PlexFirstCharacter] data and provides
/// fraction-based mapping for proportional scroll handle positioning.
class AlphaJumpHelper {
static const List<String> allLetters = [
'#',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
];
/// Maps each letter to its cumulative start index in the full item list.
final Map<String, int> letterToIndex;
/// Letters that have at least one item in the data.
final Set<String> activeLetters;
/// Total number of items across all letters.
final int totalItemCount;
AlphaJumpHelper._(this.letterToIndex, this.activeLetters, this.totalItemCount);
factory AlphaJumpHelper(List<PlexFirstCharacter> firstCharacters) {
// Build a lookup from the API data. Note: the firstCharacters API may
// count by display title (e.g. "The Simpsons" → T) rather than titleSort
// ("Simpsons" → S), so cumulative indices are approximate. The browse tab
// corrects for this when jumping by searching loaded items' titleSort.
final sizeByLetter = <String, int>{};
for (final fc in firstCharacters) {
sizeByLetter[fc.title.toUpperCase()] = fc.size;
}
// Compute cumulative indices in allLetters order (#, A, B, …, Z).
final letterToIndex = <String, int>{};
final activeLetters = <String>{};
int cumulative = 0;
for (final letter in allLetters) {
final size = sizeByLetter[letter];
if (size != null && size > 0) {
activeLetters.add(letter);
letterToIndex[letter] = cumulative;
cumulative += size;
}
}
return AlphaJumpHelper._(letterToIndex, activeLetters, cumulative);
}
/// Returns the letter that the given item index falls within.
String currentLetter(int itemIndex) {
String current = allLetters.first;
for (final letter in allLetters) {
final startIndex = letterToIndex[letter];
if (startIndex != null && startIndex <= itemIndex) {
current = letter;
}
}
return current;
}
/// Returns the cumulative start index for a letter, or null if not present.
int? indexForLetter(String letter) => letterToIndex[letter];
/// Returns the fractional position (0.01.0) for a letter, proportional to
/// item count. Letters with more items occupy a larger segment.
double fractionForLetter(String letter) {
if (totalItemCount == 0) return 0.0;
final index = letterToIndex[letter];
if (index == null) return 0.0;
return index / totalItemCount;
}
/// Returns the letter at a given fractional position (0.01.0), proportional
/// to item count.
String letterAtFraction(double fraction) {
if (totalItemCount == 0) return allLetters.first;
final targetIndex = (fraction * totalItemCount).round().clamp(0, totalItemCount - 1);
return currentLetter(targetIndex);
}
}