Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Implementation of FluentList and FluentListSection #2019

Merged
merged 7 commits into from
May 16, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ struct ListActionItemDemoView: View {
@State var topSeparatorType: ListActionItemSeparatorType = .none
@State var bottomSeparatorType: ListActionItemSeparatorType = .inset
@State var backgroundStyleType: ListItemBackgroundStyleType = .grouped
@State var listStyle: FluentListStyle = .plain

public var body: some View {

@ViewBuilder
var textFields: some View {
TextField("Primary Action Title", text: $primaryActionTitle)
Expand Down Expand Up @@ -80,58 +80,72 @@ struct ListActionItemDemoView: View {
Picker("Bottom Separator Type", selection: $bottomSeparatorType) {
separatorTypePickerOptions
}

let listStylePickerOptions = Group {
Text(".plain").tag(FluentListStyle.plain)
Text(".insetGrouped").tag(FluentListStyle.insetGrouped)
Text(".inset").tag(FluentListStyle.inset)
}

Picker("List Style Type", selection: $listStyle) {
listStylePickerOptions
}
}

@ViewBuilder
var content: some View {
List {
Section {
if showSecondaryAction {
ListActionItem(primaryActionTitle: primaryActionTitle,
onPrimaryActionTapped: {
showingAlert.toggle()
},
primaryActionType: primaryActionType,
secondaryActionTitle: secondaryActionTitle,
onSecondaryActionTapped: {
showingAlert.toggle()
},
secondaryActionType: secondaryActionType)
.topSeparatorType(topSeparatorType)
.bottomSeparatorType(bottomSeparatorType)
.backgroundStyleType(backgroundStyleType)
} else {
ListActionItem(title: primaryActionTitle,
onTapped: {
showingAlert.toggle()
},
actionType: primaryActionType)
.topSeparatorType(topSeparatorType)
.bottomSeparatorType(bottomSeparatorType)
.backgroundStyleType(backgroundStyleType)
}

} header: {
Text("ListActionItem")
}
.alert("Action tapped", isPresented: $showingAlert) {
Button("OK", role: .cancel) { }
.accessibilityIdentifier("DismissAlertButton")
FluentListSection {
if showSecondaryAction {
ListActionItem(primaryActionTitle: primaryActionTitle,
onPrimaryActionTapped: {
showingAlert.toggle()
},
primaryActionType: primaryActionType,
secondaryActionTitle: secondaryActionTitle,
onSecondaryActionTapped: {
showingAlert.toggle()
},
secondaryActionType: secondaryActionType)
.topSeparatorType(topSeparatorType)
.bottomSeparatorType(bottomSeparatorType)
.backgroundStyleType(backgroundStyleType)
} else {
ListActionItem(title: primaryActionTitle,
onTapped: {
showingAlert.toggle()
},
actionType: primaryActionType)
.topSeparatorType(topSeparatorType)
.bottomSeparatorType(bottomSeparatorType)
.backgroundStyleType(backgroundStyleType)
}
} header: {
Text("ListActionItem")
}
.alert("Action tapped", isPresented: $showingAlert) {
Button("OK", role: .cancel) { }
.accessibilityIdentifier("DismissAlertButton")
}

Section {
FluentUIDemoToggle(titleKey: "Show secondary action", isOn: $showSecondaryAction)
.accessibilityIdentifier("showSecondaryActionSwitch")
textFields
pickers
} header: {
Text("Settings")
}
FluentListSection {
FluentUIDemoToggle(titleKey: "Show secondary action", isOn: $showSecondaryAction)
.accessibilityIdentifier("showSecondaryActionSwitch")
textFields
pickers
} header: {
Text("Settings")
}
}

@ViewBuilder
var list: some View {
FluentList {
content
}
.fluentListStyle(listStyle)
.fluentTheme(fluentTheme)
.listStyle(.insetGrouped)
}

return content
return list
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct ListItemDemoView: View {
@State var trailingContentFocusableElementCount: Int = 0
@State var trailingContentToggleEnabled: Bool = true
@State var renderStandalone: Bool = false
@State var listStyle: FluentListStyle = .plain

public var body: some View {

Expand Down Expand Up @@ -103,6 +104,11 @@ struct ListItemDemoView: View {
Text(".clear").tag(ListItemBackgroundStyleType.clear)
Text(".custom").tag(ListItemBackgroundStyleType.custom)
}
Picker("List Style Type", selection: $listStyle) {
Text(".plain").tag(FluentListStyle.plain)
Text(".insetGrouped").tag(FluentListStyle.insetGrouped)
Text(".inset").tag(FluentListStyle.inset)
}
}

@ViewBuilder
Expand Down Expand Up @@ -202,19 +208,19 @@ struct ListItemDemoView: View {
if renderStandalone {
listItem
}
List {
FluentList {
if !renderStandalone {
Section {
FluentListSection {
listItem
} header: {
Text("ListItem")
}
}
controls
}
.fluentListStyle(listStyle)
.background(ListItem.listBackgroundColor(for: .grouped))
.fluentTheme(fluentTheme)
.listStyle(.insetGrouped)
}
}

Expand Down
12 changes: 12 additions & 0 deletions ios/FluentUI.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@
92ECB2DF2BE06BCB00404D79 /* FluentTheme+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ECB2DE2BE06BCB00404D79 /* FluentTheme+UIKit.swift */; };
92EE82AE27025A94009D52B5 /* TokenSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92EE82AC27025A94009D52B5 /* TokenSet.swift */; };
92F8054E272B2DF3000EAFDB /* CardNudgeModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92079C8E26B66E5100D688DA /* CardNudgeModifiers.swift */; };
94679F2C2BF55059004A1560 /* FluentListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94679F2A2BF55059004A1560 /* FluentListSection.swift */; };
94679F2D2BF55059004A1560 /* FluentList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94679F2B2BF55059004A1560 /* FluentList.swift */; };
94679F2F2BF57F86004A1560 /* FluentListModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94679F2E2BF57F86004A1560 /* FluentListModifiers.swift */; };
94A7EC1A2836DCB200BFFBAE /* CommandBarCommandGroupsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94A7EC192836DCB100BFFBAE /* CommandBarCommandGroupsView.swift */; };
A257F82C251D98F3002CAA6E /* FluentUI-ios.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A257F82B251D98F3002CAA6E /* FluentUI-ios.xcassets */; };
A542A9D7226FC01100204A52 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A559BB81212B6FA40055E107 /* Localizable.strings */; };
Expand Down Expand Up @@ -383,6 +386,9 @@
92ECB2DC2BE069D100404D79 /* Color+DynamicColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+DynamicColor.swift"; sourceTree = "<group>"; };
92ECB2DE2BE06BCB00404D79 /* FluentTheme+UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FluentTheme+UIKit.swift"; sourceTree = "<group>"; };
92EE82AC27025A94009D52B5 /* TokenSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenSet.swift; sourceTree = "<group>"; };
94679F2A2BF55059004A1560 /* FluentListSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FluentListSection.swift; sourceTree = "<group>"; };
94679F2B2BF55059004A1560 /* FluentList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FluentList.swift; sourceTree = "<group>"; };
94679F2E2BF57F86004A1560 /* FluentListModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FluentListModifiers.swift; sourceTree = "<group>"; };
94A7EC192836DCB100BFFBAE /* CommandBarCommandGroupsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandBarCommandGroupsView.swift; sourceTree = "<group>"; };
A257F82B251D98F3002CAA6E /* FluentUI-ios.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = "FluentUI-ios.xcassets"; path = "FluentUI/Resources/FluentUI-ios.xcassets"; sourceTree = "<group>"; };
A5237ACA21DED7030040BF27 /* ResizingHandleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizingHandleView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1190,6 +1196,9 @@
F3F113872A705AC300DA852A /* List */ = {
isa = PBXGroup;
children = (
94679F2B2BF55059004A1560 /* FluentList.swift */,
94679F2E2BF57F86004A1560 /* FluentListModifiers.swift */,
94679F2A2BF55059004A1560 /* FluentListSection.swift */,
F3A87D5F2BF57421000D6A64 /* FluentListSectionFooter.swift */,
F3A87D5D2BF5606E000D6A64 /* FluentListSectionHeader.swift */,
F32E6E8A2A7997F3003F9AE7 /* ListActionItem.swift */,
Expand Down Expand Up @@ -1607,9 +1616,11 @@
5314E14325F016860099271A /* CardTransitionAnimator.swift in Sources */,
5314E0F825F012CB0099271A /* AvatarTitleView.swift in Sources */,
8035CAB62633A4DB007B3FD1 /* BottomCommandingController.swift in Sources */,
94679F2F2BF57F86004A1560 /* FluentListModifiers.swift in Sources */,
5314E13725F016370099271A /* PopupMenuProtocols.swift in Sources */,
5314E19725F019650099271A /* TabBarItem.swift in Sources */,
EC98E2B4298D989100B9DF91 /* FluentTextInputError.swift in Sources */,
94679F2C2BF55059004A1560 /* FluentListSection.swift in Sources */,
6F2F218F2A12BFD900C50EAB /* BadgeLabelTokenSet.swift in Sources */,
6FC3705E29E7707F0096B239 /* BadgeViewTokenSet.swift in Sources */,
92B7E6A326864AE900EFC15E /* MSFPersonaButton.swift in Sources */,
Expand Down Expand Up @@ -1758,6 +1769,7 @@
5314E03B25F00E3D0099271A /* BadgeStringExtractor.swift in Sources */,
EC1C31732923022E00CF052C /* SegmentedControlTokenSet.swift in Sources */,
5314E19825F019650099271A /* SideTabBar.swift in Sources */,
94679F2D2BF55059004A1560 /* FluentList.swift in Sources */,
5314E10A25F014600099271A /* Obscurable.swift in Sources */,
F3F1138D2A705B6900DA852A /* ListItemModifiers.swift in Sources */,
5314E07D25F00F1A0099271A /* DateTimePickerViewComponentTableView.swift in Sources */,
Expand Down
61 changes: 61 additions & 0 deletions ios/FluentUI/List/FluentList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//

import SwiftUI

/// Fluent specific list style enum
public enum FluentListStyle {
case plain
nodes11 marked this conversation as resolved.
Show resolved Hide resolved
case insetGrouped
case inset
}

/// This a wrapper around `SwiftUI.List` that has fluent style applied. It is intended to be used in conjunction with `FluentUI.FluentListSection` and `FluentUI.ListItem`
nodes11 marked this conversation as resolved.
Show resolved Hide resolved
/// to provide a completely fluentized list, however, it can be used on it's own if desired.
///
/// This component is a work in progress. Expect changes to be made to it on a somewhat regular basis.
public struct FluentList<ListContent: View>: View {

// MARK: Initializer

/// Creates a `FluentList`
/// - Parameters:
/// - content: content to show inside of the list.
public init(@ViewBuilder content: @escaping () -> ListContent) {
self.content = content
}

public var body: some View {
@ViewBuilder
var list: some View {
List {
content()
}
}

@ViewBuilder
var styledList: some View {
switch listStyle {
case .inset:
list.listStyle(.inset)
case .insetGrouped:
list.listStyle(.insetGrouped)
case .plain:
list.listStyle(.plain)
}
}

return styledList
}

/// Style to be used by the list
var listStyle: FluentListStyle = .plain

// MARK: Private variables

/// Content to render inside the list
private var content: () -> ListContent

}
17 changes: 17 additions & 0 deletions ios/FluentUI/List/FluentListModifiers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//

import SwiftUI

public extension FluentList {
/// The list style type for the `FluentList`.
/// - Parameter listStyle: Type of style to display the list with.
/// - Returns: The modified `FluentList` with the style property set.
func fluentListStyle(_ listStyle: FluentListStyle) -> FluentList {
var list = self
list.listStyle = listStyle
return list
}
}
78 changes: 78 additions & 0 deletions ios/FluentUI/List/FluentListSection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//

import SwiftUI

/// This a wrapper around `SwiftUI.Section` that has fluent style applied. It is intended to be used in conjunction with `FluentUI.FluentList` and `FluentUI.ListItem`
/// to provide a completely fluentized list, however, it can be used on it's own if desired.
///
/// This component is a work in progress. Expect changes to be made to it on a somewhat regular basis.
public struct FluentListSection<SectionContent: View, SectionHeaderContent: View, SectionFooterContent: View>: View {

// MARK: Initializer

/// Creates a `FluentListSection`
/// - Parameters:
/// - content: content to show inside of the section.
/// - header: content to show inside of the header.
/// - footer: content to show inside of the footer.
public init(@ViewBuilder content: @escaping () -> SectionContent,
@ViewBuilder header: @escaping () -> SectionHeaderContent,
@ViewBuilder footer: @escaping () -> SectionFooterContent) {
self.content = content
self.header = header
self.footer = footer
}

public var body: some View {
@ViewBuilder
var sectionView: some View {
Section {
content()
} header: {
if let header = header {
header()
}
} footer: {
if let footer = footer {
footer()
}
}
}
return sectionView
}

// MARK: Private variables

/// Content to display in the body of the section
private var content: () -> SectionContent

/// Content to display in the footer of the section
private var footer: (() -> SectionFooterContent)?

/// Content to display in the header of the section
private var header: (() -> SectionHeaderContent)?

}

public extension FluentListSection where SectionHeaderContent == EmptyView, SectionFooterContent == EmptyView {
init(@ViewBuilder content: @escaping () -> SectionContent) {
self.content = content
}
}

public extension FluentListSection where SectionFooterContent == EmptyView {
init(@ViewBuilder content: @escaping () -> SectionContent, @ViewBuilder header: @escaping () -> SectionHeaderContent) {
self.content = content
self.header = header
}
}

public extension FluentListSection where SectionHeaderContent == EmptyView {
init(@ViewBuilder content: @escaping () -> SectionContent, @ViewBuilder footer: @escaping () -> SectionFooterContent) {
self.content = content
self.footer = footer
}
}
Loading