import SwiftUI /// The reactions on an issue, a merge request or a comment: one chip per /// reaction given, tinted when it is the account's own, and a menu of the /// whole vocabulary. Tapping a chip toggles the account's reaction; the /// command decides, and the screen reloads from what it says. struct ReactionBar: View { let reactions: [ReactionCount] let disabled: Bool /// Names the bar's controls: `-` and `-add`. let idPrefix: String let react: (String) -> Void var body: some View { HStack(spacing: 6) { ForEach(reactions) { given in Button { react(given.reaction) } label: { HStack(spacing: 3) { Text(Reaction.emoji(for: given.reaction)) Text("\(given.count)") .monospacedDigit() .foregroundStyle(given.me ? Color.gbAccent : .secondary) } .font(.gbSans(.caption)) .padding(.horizontal, 7) .padding(.vertical, 3) .background((given.me ? Color.gbAccent : .secondary).opacity(0.07), in: gbChipShape) .overlay(gbChipShape.stroke((given.me ? Color.gbAccent : .secondary).opacity(0.35), lineWidth: 1)) .frame(minHeight: 44) .contentShape(Rectangle()) } .buttonStyle(.borderless) .disabled(disabled) .accessibilityLabel(label(for: given)) .accessibilityIdentifier("\(idPrefix)-\(given.reaction)") } Menu { ForEach(Reaction.all) { reaction in Button { react(reaction.name) } label: { // The account's own reaction carries the menu's checkmark; // picking it again takes it back. if Reaction.remove(reaction.name, given: reactions) { Label("\(reaction.emoji) \(reaction.name)", systemImage: "checkmark") } else { Text("\(reaction.emoji) \(reaction.name)") } } .accessibilityLabel(Reaction.spoken(for: reaction.name)) .accessibilityAddTraits(Reaction.remove(reaction.name, given: reactions) ? .isSelected : []) } } label: { Image(systemName: "face.smiling") .foregroundStyle(.secondary) .frame(minWidth: 44, minHeight: 44) .contentShape(Rectangle()) } .menuStyle(.borderlessButton) // A menu in a list row takes the accent; the icon is a quiet // affordance beside the chips, not a link. .tint(.secondary) .disabled(disabled) .accessibilityLabel("Add reaction") .accessibilityIdentifier("\(idPrefix)-add") Spacer(minLength: 0) } } private func label(for given: ReactionCount) -> String { let count = given.count == 1 ? "1 reaction" : "\(given.count) reactions" return "\(Reaction.spoken(for: given.reaction)), \(count)\(given.me ? ", including yours" : "")" } }