chromium/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_action_section.swift

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import SwiftUI

/// A SwiftUI view for the overflow menu displaying a subsection of the actions list.
struct OverflowMenuActionSection<FooterBackground: View>: View {

  // Default height if no other header or footer. This spaces the sections
  // out properly.
  static var headerFooterHeight: CGFloat { 20 }

  @ObservedObject var actionGroup: OverflowMenuActionGroup

  weak var metricsHandler: PopupMenuMetricsHandler?

  // A custom background for the footer if provided.
  let footerBackground: FooterBackground

  init(
    actionGroup: OverflowMenuActionGroup, metricsHandler: PopupMenuMetricsHandler? = nil,
    @ViewBuilder footerBackground: () -> FooterBackground = { EmptyView() }
  ) {
    self.actionGroup = actionGroup
    self.metricsHandler = metricsHandler
    self.footerBackground = footerBackground()
  }

  var body: some View {
    Section(
      content: {
        ForEach(actionGroup.actions) { action in
          OverflowMenuActionRow(action: action, metricsHandler: metricsHandler)
            .moveDisabled(!actionGroup.supportsReordering)
        }
        .onMove(perform: move)
      },
      header: {
        Spacer()
          .frame(height: Self.headerFooterHeight)
          .listRowInsets(EdgeInsets())
          .accessibilityHidden(true)
      },
      footer: {
        if let actionFooter = actionGroup.footer {
          OverflowMenuFooterRow(footer: actionFooter)
            .background {
              footerBackground
            }
        } else {
          Spacer()
            // Use `leastNonzeroMagnitude` to remove the footer. Otherwise,
            // it uses a default height.
            .frame(height: CGFloat.leastNonzeroMagnitude)
            .listRowInsets(EdgeInsets())
            .accessibilityHidden(true)
            .background {
              footerBackground
            }
        }
      })
  }

  func move(fromOffsets offsets: IndexSet, toOffset destination: Int) {
    actionGroup.actions.move(fromOffsets: offsets, toOffset: destination)
  }
}