Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BrowserKit/Sources/Common/Theming/DarkTheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ private struct DarkColourPalette: ThemeColourPalette {
])
var layerSurfaceLow = FXColors.DarkGrey60
var layerSurfaceMedium = FXColors.DarkGrey80
var layerSurfaceMediumAlpha = FXColors.DarkGrey80.withAlphaComponent(0.8)
var layerSurfaceMediumAlt = FXColors.DarkGrey40
var layerGradientSummary = Gradient(colors: [
FXColors.Red70,
Expand Down
1 change: 1 addition & 0 deletions BrowserKit/Sources/Common/Theming/LightTheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ private struct LightColourPalette: ThemeColourPalette {
])
var layerSurfaceLow = FXColors.LightGrey20
var layerSurfaceMedium = FXColors.White
var layerSurfaceMediumAlpha = FXColors.White.withAlphaComponent(0.8)
var layerSurfaceMediumAlt = FXColors.LightGrey40
var layerGradientSummary = Gradient(colors: [
FXColors.Red70,
Expand Down
1 change: 1 addition & 0 deletions BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ private struct PrivateModeColorPalette: ThemeColourPalette {
])
var layerSurfaceLow = UIColor(rgb: 0x342B4A)
var layerSurfaceMedium = UIColor(rgb: 0x24183A)
var layerSurfaceMediumAlpha = UIColor(rgb: 0x24183A).withAlphaComponent(0.8)
var layerSurfaceMediumAlt = UIColor(rgb: 0x24183A)
var layerGradientSummary = Gradient(colors: [
FXColors.Red70,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public protocol ThemeColourPalette {
var layerGradientURL: Gradient { get }
var layerSurfaceLow: UIColor { get }
var layerSurfaceMedium: UIColor { get }
var layerSurfaceMediumAlpha: UIColor { get }
var layerSurfaceMediumAlt: UIColor { get }
var layerGradientSummary: Gradient { get }

Expand Down
126 changes: 126 additions & 0 deletions BrowserKit/Sources/ComponentLibrary/Buttons/ChipButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Common
import UIKit

/// `ChipButton` is a capsule-style button used in chip pickers
public final class ChipButton: UIButton, ThemeApplicable {
private struct UX {
static let verticalInset: CGFloat = 8
static let horizontalInset: CGFloat = 12
}

private var viewModel: ChipButtonViewModel?

private var normalBackgroundColor: UIColor = .clear
private var normalForegroundColor: UIColor = .clear
private var selectedBackgroundColor: UIColor = .clear
private var selectedForegroundColor: UIColor = .clear
private var disabledBackgroundColor: UIColor = .clear
private var disabledForegroundColor: UIColor = .clear

override public init(frame: CGRect) {
super.init(frame: frame)

configuration = UIButton.Configuration.filled()
configuration?.cornerStyle = .capsule
configuration?.background.backgroundColorTransformer = nil
configuration?.titleLineBreakMode = .byTruncatingTail

addTarget(self, action: #selector(tapped), for: .touchUpInside)
}

public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override public var intrinsicContentSize: CGSize {
guard let title = configuration?.title, !title.isEmpty else {
return super.intrinsicContentSize
}

// Reserve space for the wider selected/unselected title so the chip
// does not resize when its font weight changes with selection.
let regularFont = FXFontStyles.Regular.body.scaledFont()
let boldFont = FXFontStyles.Bold.body.scaledFont()
let regularWidth = title.size(withAttributes: [.font: regularFont]).width
let boldWidth = title.size(withAttributes: [.font: boldFont]).width
let reservedTextWidth = ceil(max(regularWidth, boldWidth))
let reservedTextHeight = ceil(max(regularFont.lineHeight, boldFont.lineHeight))

return CGSize(
width: reservedTextWidth + UX.horizontalInset * 2,
height: reservedTextHeight + UX.verticalInset * 2
)
}

override public func updateConfiguration() {
guard var updatedConfiguration = configuration else { return }

let foregroundColor: UIColor
let backgroundColor: UIColor

if !isEnabled {
foregroundColor = disabledForegroundColor
backgroundColor = disabledBackgroundColor
} else if isSelected {
foregroundColor = selectedForegroundColor
backgroundColor = selectedBackgroundColor
} else {
foregroundColor = normalForegroundColor
backgroundColor = normalBackgroundColor
}

updatedConfiguration.baseForegroundColor = foregroundColor
updatedConfiguration.background.backgroundColor = backgroundColor
updatedConfiguration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
var outgoing = incoming
outgoing.font = self.isSelected ? FXFontStyles.Bold.body.scaledFont() : FXFontStyles.Regular.body.scaledFont()
outgoing.foregroundColor = foregroundColor
return outgoing
}

configuration = updatedConfiguration

if !isEnabled {
accessibilityTraits = [.button, .notEnabled]
} else if isSelected {
accessibilityTraits = [.button, .selected]
} else {
accessibilityTraits = [.button]
}
}

public func configure(viewModel: ChipButtonViewModel) {
self.viewModel = viewModel
accessibilityIdentifier = viewModel.a11yIdentifier
isSelected = viewModel.isSelected

guard var updatedConfiguration = configuration else { return }
updatedConfiguration.title = viewModel.title
updatedConfiguration.contentInsets = NSDirectionalEdgeInsets(
top: UX.verticalInset,
leading: UX.horizontalInset,
bottom: UX.verticalInset,
trailing: UX.horizontalInset,
)
configuration = updatedConfiguration
}

public func applyTheme(theme: Theme) {
normalBackgroundColor = theme.colors.layerSurfaceMediumAlpha
normalForegroundColor = theme.colors.textPrimary
selectedBackgroundColor = theme.colors.actionPrimary
selectedForegroundColor = theme.colors.textInverted
disabledBackgroundColor = theme.colors.layer2
disabledForegroundColor = theme.colors.textDisabled
setNeedsUpdateConfiguration()
}

@objc
private func tapped(sender: UIButton) {
viewModel?.tappedAction?(sender)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Foundation
import UIKit

public struct ChipButtonViewModel {
public let title: String
public let a11yIdentifier: String?
public let isSelected: Bool
public var tappedAction: (@MainActor (UIButton) -> Void)?

public init(
title: String,
a11yIdentifier: String,
isSelected: Bool,
touchUpAction: (@MainActor (UIButton) -> Void)?
) {
self.title = title
self.a11yIdentifier = a11yIdentifier
self.isSelected = isSelected
self.tappedAction = touchUpAction
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct TabTrayPanelSwipePalette: ThemeColourPalette {
var layerGradientURL: Gradient { base.layerGradientURL }
var layerSurfaceLow: UIColor { base.layerSurfaceLow }
var layerSurfaceMedium: UIColor { base.layerSurfaceMedium }
var layerSurfaceMediumAlpha: UIColor { base.layerSurfaceMediumAlpha }
var layerSurfaceMediumAlt: UIColor { base.layerSurfaceMediumAlt }
var layerGradientSummary: Gradient { base.layerGradientSummary }

Expand Down
Loading