Files
ios-mail/Modules/App/Sources/UI/Views/OneLineLabelsView/OneLineLabelsListView.swift
T

106 lines
3.8 KiB
Swift

// Copyright (c) 2024 Proton Technologies AG
//
// This file is part of Proton Mail.
//
// Proton Mail is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail. If not, see https://www.gnu.org/licenses/.
import InboxCore
import InboxDesignSystem
import SwiftUI
struct OneLineLabelsListView: View {
let labels: [LabelUIModel]
private let labelsSpacing: CGFloat = DS.Spacing.small
private let horizontalPadding: CGFloat = DS.Spacing.compact
private let minimalLabelWidth: CGFloat = DS.Spacing.extraLarge
private let height: CGFloat = 20
private let font = UIFont.font(textStyle: .caption2, weight: .semibold)
var body: some View {
GeometryReader { geometry in
HStack(spacing: labelsSpacing) {
ForEach(labelsFittingSpace(labels: labels, availableWidth: geometry.size.width), id: \.self) { label in
switch label {
case .regular(let viewModel):
Text(viewModel.text)
.font(.caption2)
.fontWeight(.semibold)
.foregroundColor(DS.Color.Global.white)
.lineLimit(1)
.frame(height: height)
.frame(minWidth: minimalLabelWidth)
.padding(.horizontal, horizontalPadding)
.background(viewModel.color)
.clipShape(RoundedRectangle(cornerRadius: DS.Radius.medium))
case .count(let count):
Text(Strings.plus(count: count))
.foregroundStyle(DS.Color.Text.weak)
.fontWeight(.semibold)
.font(.caption)
}
}
}
}.frame(height: height)
}
// MARK: - Private
private enum Label: Hashable {
case regular(model: LabelUIModel)
case count(Int)
}
@MainActor
private func labelsFittingSpace(labels: [LabelUIModel], availableWidth: CGFloat) -> [Label] {
let padding = 2 * horizontalPadding
var availableWidth: CGFloat = availableWidth
var labelsToDisplay: [Label] = []
for label in labels {
let labelWidth = calculateLabel(label.text, font: font) + padding
let requiredSpace = labelWidth + labelsSpacing
if requiredSpace < availableWidth || labelsToDisplay.isEmpty {
availableWidth -= requiredSpace
labelsToDisplay.append(.regular(model: label))
} else {
labelsToDisplay.append(.count(labels.count - labelsToDisplay.count))
break
}
}
return labelsToDisplay
}
@MainActor
private func calculateLabel(_ text: String, font: UIFont) -> CGFloat {
let label = UILabel()
label.text = text
label.font = font
label.numberOfLines = 1
label.sizeToFit()
return max(label.frame.width, minimalLabelWidth)
}
}
#Preview {
VStack {
ForEach(OneLineLabelsListViewPreviewDataProvider.labels, id: \.self) { labels in
OneLineLabelsListView(labels: labels)
}
Spacer()
}.padding()
}