From d457091b0d3b111029c7cdd63f6365ba8ad8bbd8 Mon Sep 17 00:00:00 2001 From: zzchao-1999 <149659707+zzchao-1999@users.noreply.github.com> Date: Thu, 18 Jul 2024 08:33:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20[JIRA:HCPSDKFIORIUIKIT-1?= =?UTF-8?q?933]SwiftUI=20Timeline=20(#734)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 [JIRA:HCPSDKFIORIUIKIT-1933]SwiftUI Timeline * feat: 🎸 Fixed swift scan issues * feat: 🎸 [JIRA:HCPSDKFIORIUIKIT-1933]SwiftUI Timeline * feat: 🎸 address review comments * feat: 🎸 Address comments * feat: 🎸 address hound comments * feat: 🎸 addressed new coomments --------- Co-authored-by: Bill Zhou --- .../Examples.xcodeproj/project.pbxproj | 25 + .../FioriSwiftUICore/CoreContentView.swift | 9 +- .../Timeline/CustomTimelineExample.swift | 80 +++ .../Timeline/SimpleTimelineExample.swift | 44 ++ .../Timeline/TimelineExample.swift | 15 + .../Timeline/TimelineItemsExample.swift | 72 +++ .../Views/TimelineNodeView.swift | 46 ++ .../BaseComponentProtocols.swift | 37 ++ .../CompositeComponentProtocols.swift | 63 +++ .../_FioriStyles/AttributeStyle.fiori.swift | 19 + .../NowIndicatorNodeStyle.fiori.swift | 19 + .../SecondaryTimestampStyle.fiori.swift | 19 + .../SubAttributeStyle.fiori.swift | 19 + .../TimelineMarkerStyle.fiori.swift | 152 ++++++ .../TimelineNodeStyle.fiori.swift | 19 + .../TimelineNowIndicatorStyle.fiori.swift | 37 ++ .../_FioriStyles/TimelineStyle.fiori.swift | 244 +++++++++ .../_FioriStyles/TimestampStyle.fiori.swift | 19 + .../Attribute/Attribute.generated.swift | 63 +++ .../Attribute/AttributeStyle.generated.swift | 28 ++ .../NowIndicatorNode.generated.swift | 57 +++ .../NowIndicatorNodeStyle.generated.swift | 28 ++ .../SecondaryTimestamp.generated.swift | 63 +++ .../SecondaryTimestampStyle.generated.swift | 28 ++ .../SubAttribute/SubAttribute.generated.swift | 63 +++ .../SubAttributeStyle.generated.swift | 28 ++ .../Timeline/Timeline.generated.swift | 134 +++++ .../Timeline/TimelineStyle.generated.swift | 64 +++ .../TimelineMarker.generated.swift | 121 +++++ .../TimelineMarkerStyle.generated.swift | 51 ++ .../TimelineNode/TimelineNode.generated.swift | 63 +++ .../TimelineNodeStyle.generated.swift | 28 ++ .../TimelineNowIndicator.generated.swift | 63 +++ .../TimelineNowIndicatorStyle.generated.swift | 35 ++ .../Timestamp/Timestamp.generated.swift | 63 +++ .../Timestamp/TimestampStyle.generated.swift | 28 ++ ...entStyleProtocol+Extension.generated.swift | 462 ++++++++++++++++++ .../EnvironmentVariables.generated.swift | 189 +++++++ .../ModifiedStyle.generated.swift | 252 ++++++++++ .../ResolvedStyle.generated.swift | 144 ++++++ .../View+Extension_.generated.swift | 153 ++++++ ...iewEmptyChecking+Extension.generated.swift | 67 +++ 42 files changed, 3212 insertions(+), 1 deletion(-) create mode 100644 Apps/Examples/Examples/FioriSwiftUICore/Timeline/CustomTimelineExample.swift create mode 100644 Apps/Examples/Examples/FioriSwiftUICore/Timeline/SimpleTimelineExample.swift create mode 100644 Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineExample.swift create mode 100644 Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineItemsExample.swift create mode 100644 Sources/FioriSwiftUICore/Views/TimelineNodeView.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/AttributeStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/NowIndicatorNodeStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/SecondaryTimestampStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/SubAttributeStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/TimelineMarkerStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/TimelineNodeStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/TimelineNowIndicatorStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/TimelineStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/TimestampStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/Attribute.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/AttributeStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNode.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNodeStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestamp.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestampStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttribute.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttributeStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/Timeline.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/TimelineStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarker.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarkerStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNode.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNodeStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicator.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicatorStyle.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/Timestamp.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/TimestampStyle.generated.swift diff --git a/Apps/Examples/Examples.xcodeproj/project.pbxproj b/Apps/Examples/Examples.xcodeproj/project.pbxproj index 9bc707b18..8078f4a3e 100644 --- a/Apps/Examples/Examples.xcodeproj/project.pbxproj +++ b/Apps/Examples/Examples.xcodeproj/project.pbxproj @@ -29,6 +29,10 @@ 691DE21925F2A30B00094D4A /* KPIViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691DE21825F2A30B00094D4A /* KPIViewExample.swift */; }; 692F338B26556A6A009B98DA /* SideBarExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692F338A26556A6A009B98DA /* SideBarExample.swift */; }; 69B2B5D9268A333C009AC6B3 /* KPIProgressViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69B2B5D8268A333C009AC6B3 /* KPIProgressViewExample.swift */; }; + 8732C2C52C350957002110E9 /* TimelineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8732C2C42C350957002110E9 /* TimelineExample.swift */; }; + 8732C2C72C3524B6002110E9 /* TimelineItemsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8732C2C62C3524B6002110E9 /* TimelineItemsExample.swift */; }; + 8732C2C92C3524C9002110E9 /* SimpleTimelineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8732C2C82C3524C9002110E9 /* SimpleTimelineExample.swift */; }; + 8732C2CB2C3524D9002110E9 /* CustomTimelineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8732C2CA2C3524D9002110E9 /* CustomTimelineExample.swift */; }; 878219C42BEE128E002FDFBC /* StepperViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 878219C32BEE128E002FDFBC /* StepperViewExample.swift */; }; 8A55795724C1286E0098003A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A55795624C1286E0098003A /* AppDelegate.swift */; }; 8A55795924C1286E0098003A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A55795824C1286E0098003A /* SceneDelegate.swift */; }; @@ -212,6 +216,10 @@ 691DE21825F2A30B00094D4A /* KPIViewExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KPIViewExample.swift; sourceTree = ""; }; 692F338A26556A6A009B98DA /* SideBarExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideBarExample.swift; sourceTree = ""; }; 69B2B5D8268A333C009AC6B3 /* KPIProgressViewExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KPIProgressViewExample.swift; sourceTree = ""; }; + 8732C2C42C350957002110E9 /* TimelineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineExample.swift; sourceTree = ""; }; + 8732C2C62C3524B6002110E9 /* TimelineItemsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemsExample.swift; sourceTree = ""; }; + 8732C2C82C3524C9002110E9 /* SimpleTimelineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleTimelineExample.swift; sourceTree = ""; }; + 8732C2CA2C3524D9002110E9 /* CustomTimelineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimelineExample.swift; sourceTree = ""; }; 878219C32BEE128E002FDFBC /* StepperViewExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StepperViewExample.swift; sourceTree = ""; }; 8A1E99AD24D59C8000ED8A39 /* cloud-sdk-ios-fiori */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "cloud-sdk-ios-fiori"; path = ../..; sourceTree = ""; }; 8A55795324C1286E0098003A /* Examples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -460,6 +468,17 @@ path = SideBar; sourceTree = ""; }; + 8732C2C32C35092D002110E9 /* Timeline */ = { + isa = PBXGroup; + children = ( + 8732C2C42C350957002110E9 /* TimelineExample.swift */, + 8732C2C62C3524B6002110E9 /* TimelineItemsExample.swift */, + 8732C2C82C3524C9002110E9 /* SimpleTimelineExample.swift */, + 8732C2CA2C3524D9002110E9 /* CustomTimelineExample.swift */, + ); + path = Timeline; + sourceTree = ""; + }; 8A55794A24C1286E0098003A = { isa = PBXGroup; children = ( @@ -531,6 +550,7 @@ 8A5579C824C1293C0098003A /* FioriSwiftUICore */ = { isa = PBXGroup; children = ( + 8732C2C32C35092D002110E9 /* Timeline */, B19006582C201BAC000C8B10 /* ProfileHeader */, 1F1A1FF82C0BDA42007109D8 /* MenuSelection */, B1A98FF02C11591B00FC9998 /* BannerMessage */, @@ -966,6 +986,7 @@ 1FC30414270541BF004BEE00 /* FioriThemeManagerContentView.swift in Sources */, C106AD422B336EA400FE8B35 /* SearchWithSuggestion.swift in Sources */, 99942D59261698FC001912C5 /* InfoViewSample.swift in Sources */, + 8732C2C72C3524B6002110E9 /* TimelineItemsExample.swift in Sources */, 993B55BE29DF7EC70002B065 /* IconLibraryExample.swift in Sources */, B80DA9BE260C1CC200C0B2E9 /* ListDataProtocol.swift in Sources */, B1DD86532B0758F000D7EDFD /* NavigationBarPopover.swift in Sources */, @@ -993,6 +1014,8 @@ 8A5579CC24C1293C0098003A /* SettingsColorForCategory.swift in Sources */, C18868D12B32535100F865F7 /* SearchFontAndColor.swift in Sources */, 9D0B26092B9BA5C0004278A5 /* KeyValueFormViewExample.swift in Sources */, + 8732C2C52C350957002110E9 /* TimelineExample.swift in Sources */, + 9D057DAB2C2F260200F5331C /* RatingControlExample.swift in Sources */, 8A557A1A24C12C820098003A /* ChartsContentView.swift in Sources */, 8A5579CE24C1293C0098003A /* SettingColor.swift in Sources */, 1F55FEF32AC941FF00D7A1BE /* View+Extensions.swift in Sources */, @@ -1010,6 +1033,7 @@ B8D4376F25F980340024EE7D /* ObjectCell_Spec_Jan2018.swift in Sources */, 8A5579CF24C1293C0098003A /* SettingsAxis.swift in Sources */, B80DA9BC260BED9400C0B2E9 /* SingleActionCollectionView.swift in Sources */, + 8732C2CB2C3524D9002110E9 /* CustomTimelineExample.swift in Sources */, 8A5579D924C1293C0098003A /* SettingsSelection.swift in Sources */, B1A98FF22C11592B00FC9998 /* BannerMessageExample.swift in Sources */, B80DA9C62612A54E00C0B2E9 /* ActivationScreenSample.swift in Sources */, @@ -1049,6 +1073,7 @@ 975CB76B256C5A7400DB7A15 /* SignatureCaptureViewExample.swift in Sources */, 8AD9DFB125D49967007448EC /* ContactItemStateAndDataBindingExample.swift in Sources */, C150CCCC2B86B78E00118DF7 /* ChromeEffect.swift in Sources */, + 8732C2C92C3524C9002110E9 /* SimpleTimelineExample.swift in Sources */, B190065A2C201BBE000C8B10 /* ProfileHeaderExample.swift in Sources */, B86F02A82679835F0049DDA7 /* ObjectItemInitExamples.swift in Sources */, 692F338B26556A6A009B98DA /* SideBarExample.swift in Sources */, diff --git a/Apps/Examples/Examples/FioriSwiftUICore/CoreContentView.swift b/Apps/Examples/Examples/FioriSwiftUICore/CoreContentView.swift index c2b189b7d..6e663396e 100644 --- a/Apps/Examples/Examples/FioriSwiftUICore/CoreContentView.swift +++ b/Apps/Examples/Examples/FioriSwiftUICore/CoreContentView.swift @@ -218,7 +218,14 @@ struct CoreContentView: View { { Text("Menu Selection") } - + + NavigationLink( + destination: TimelineExample(), + label: { + Text("Timeline") + } + ) + NavigationLink( destination: OtherViewExamples()) { diff --git a/Apps/Examples/Examples/FioriSwiftUICore/Timeline/CustomTimelineExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/CustomTimelineExample.swift new file mode 100644 index 000000000..03e1d4e66 --- /dev/null +++ b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/CustomTimelineExample.swift @@ -0,0 +1,80 @@ +import FioriSwiftUICore +import SwiftUI + +struct CustomTimelineExample: View { + var body: some View { + List { + Section(header: Text("Custom Timeline Example")) { + TimelineMarker(timestamp: "06/03/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .beforeStart, title: "POC", isPast: true, showUpperVerticalLine: false) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + TimelineMarker(timestamp: "06/05/24", secondaryTimestamp: .text("Sunny"), timelineNode: .start, title: "Project Start", isPast: true) + .modifier(CustomListRowModifier()) + .timestampStyle(content: { config in + config.timestamp.foregroundColor(.red) + }) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.red) + }) + .timelineNodeStyle(content: { config in + config.timelineNode.foregroundColor(.yellow) + }) + .titleStyle(content: { config in + config.title.foregroundColor(.red) + }) + Timeline(timestamp: "06/12/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .complete, title: "Project Phase 1", attribute: "xx features implementation done", status: .text("Done"), isPast: true) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + Timeline(timestamp: "06/20/24", timelineNode: .inProgress, title: "Project Phase 2", subtitle: "Integration test", status: .text("ongoing"), isPresent: true) + .modifier(CustomListRowModifier()) + TimelineNowIndicator() + .modifier(CustomListRowModifier()) + Timeline(timestamp: "06/25/24", timelineNode: .open, title: "Project Phase 3", attribute: "feature list: xx, xx, xx", status: .text("pending")) + .modifier(CustomListRowModifier()) + .timestampStyle(content: { config in + config.timestamp.foregroundColor(.red) + }) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.red) + }) + .timelineNodeStyle(content: { config in + config.timelineNode.foregroundColor(.yellow) + }) + .titleStyle(content: { config in + config.title.foregroundColor(.red) + }) + .statusStyle(content: { config in + config.status.foregroundColor(.red) + }) + .attributeStyle(content: { config in + config.attribute.foregroundColor(.red) + }) + Timeline(timestamp: "06/28/24", timelineNode: .open, icon: Image(systemName: "p.circle"), title: "Project Phase 4", attribute: "feature list: xx, xx, xx", status: .text("pending")) + .modifier(CustomListRowModifier()) + .iconStyle(content: { config in + config.icon.foregroundColor(.cyan) + }) + TimelineMarker(timestamp: "07/06/24", timelineNode: .open, icon: Image(systemName: "a.circle"), title: "Accept Test") + .modifier(CustomListRowModifier()) + .iconStyle(content: { config in + config.icon.foregroundColor(.red) + }) + TimelineMarker(timestamp: "07/09/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .end, title: "Project End", showLowerVerticalLine: false) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + } + } + .listStyle(.plain) + .environment(\.defaultMinListRowHeight, 7) + } +} + +#Preview { + CustomTimelineExample() +} diff --git a/Apps/Examples/Examples/FioriSwiftUICore/Timeline/SimpleTimelineExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/SimpleTimelineExample.swift new file mode 100644 index 000000000..f60f9730f --- /dev/null +++ b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/SimpleTimelineExample.swift @@ -0,0 +1,44 @@ +import FioriSwiftUICore +import SwiftUI + +struct SimpleTimelineExample: View { + var body: some View { + List { + Section(header: Text("Simple Timeline Example")) { + TimelineMarker(timestamp: "06/03/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .beforeStart, title: "POC", isPast: true, showUpperVerticalLine: false) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + TimelineMarker(timestamp: "06/05/24", secondaryTimestamp: .text("Sunny"), timelineNode: .start, title: "Project Start", isPast: true) + .modifier(CustomListRowModifier()) + Timeline(timestamp: "06/12/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .complete, title: "Project Phase 1", attribute: "xx features implementation done", status: .text("Done"), isPast: true) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + Timeline(timestamp: "06/20/24", timelineNode: .inProgress, title: "Project Phase 2", subtitle: "Integration test", status: .text("ongoing"), isPresent: true) + .modifier(CustomListRowModifier()) + TimelineNowIndicator() + .modifier(CustomListRowModifier()) + Timeline(timestamp: "06/25/24", timelineNode: .open, title: "Project Phase 3", attribute: "feature list: xx, xx, xx", status: .text("pending")) + .modifier(CustomListRowModifier()) + Timeline(timestamp: "06/28/24", timelineNode: .open, title: "Project Phase 4", attribute: "feature list: xx, xx, xx", status: .text("pending")) + .modifier(CustomListRowModifier()) + TimelineMarker(timestamp: "07/05/24", timelineNode: .beforeEnd, title: "Accept Test") + .modifier(CustomListRowModifier()) + TimelineMarker(timestamp: "07/09/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .end, title: "Project End", showLowerVerticalLine: false) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + } + } + .listStyle(.plain) + .environment(\.defaultMinListRowHeight, 7) + } +} + +#Preview { + SimpleTimelineExample() +} diff --git a/Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineExample.swift new file mode 100644 index 000000000..4b1bac39b --- /dev/null +++ b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineExample.swift @@ -0,0 +1,15 @@ +import SwiftUI + +struct TimelineExample: View { + var body: some View { + List { + NavigationLink("TimelineItems", destination: TimelineItemsExample()) + NavigationLink("SimpleTimelineExample", destination: SimpleTimelineExample()) + NavigationLink("CustomTimelineExample", destination: CustomTimelineExample()) + } + } +} + +#Preview { + TimelineExample() +} diff --git a/Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineItemsExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineItemsExample.swift new file mode 100644 index 000000000..ddb1f35da --- /dev/null +++ b/Apps/Examples/Examples/FioriSwiftUICore/Timeline/TimelineItemsExample.swift @@ -0,0 +1,72 @@ +import FioriSwiftUICore +import SwiftUI + +struct TimelineItemsExample: View { + var body: some View { + List { + Section(header: Text("TimelineMarker")) { + TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .beforeStart, title: "Before Start", isPast: true, showUpperVerticalLine: false) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .text("Sunny"), timelineNode: .start, title: "Start", isPresent: true) + .modifier(CustomListRowModifier()) + TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .text("Sunny"), timelineNode: .beforeEnd, title: "Before End", isPresent: true) + .modifier(CustomListRowModifier()) + TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .end, title: "End", showLowerVerticalLine: true) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + } + Section(header: Text("Timeline")) { + Timeline(timestamp: "06/21/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .complete, title: "Complete", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .icon(Image(systemName: "exclamationmark.circle")), subAttribute: "subAttr", isPast: true) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + .substatusStyle(content: { config in + config.substatus.foregroundColor(.yellow) + }) + Timeline(timestamp: "06/21/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .complete, title: "Complete(Disabled)", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .icon(Image(systemName: "exclamationmark.circle")), subAttribute: "subAttr", isPast: true) + .modifier(CustomListRowModifier()) + .disabled(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/) + Timeline(timestamp: "06/21/24", secondaryTimestamp: .text("Sunny"), timelineNode: .inProgress, title: "Inprogress", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .text("Warning"), subAttribute: "subAttr", isPresent: true) + .modifier(CustomListRowModifier()) + .secondaryTimestampStyle(content: { config in + config.secondaryTimestamp.foregroundColor(.yellow) + }) + .substatusStyle(content: { config in + config.substatus.foregroundColor(.yellow) + }) + Timeline(timestamp: "06/21/24", secondaryTimestamp: .text("Sunny"), timelineNode: .inProgress, title: "Inprogress(Disabled)", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .text("Warning"), subAttribute: "subAttr", isPresent: true) + .modifier(CustomListRowModifier()) + .disabled(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/) + Timeline(timestamp: "06/21/24", secondaryTimestamp: .text("Sunny"), timelineNode: .open, title: "Open", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .text("Warning"), subAttribute: "subAttr") + .modifier(CustomListRowModifier()) + Timeline(timestamp: "06/21/24", secondaryTimestamp: .text("Sunny"), timelineNode: .open, title: "Open(Disabled)", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .text("Warning"), subAttribute: "subAttr") + .modifier(CustomListRowModifier()) + .disabled(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/) + } + Section(header: Text("TimelineNowIndicator")) { + TimelineNowIndicator() + .modifier(CustomListRowModifier()) + } + } + .listStyle(.plain) + .environment(\.defaultMinListRowHeight, 7) + } +} + +struct CustomListRowModifier: ViewModifier { + func body(content: Content) -> some View { + content + .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) + .listRowSeparator(.hidden) + } +} + +#Preview { + TimelineItemsExample() +} diff --git a/Sources/FioriSwiftUICore/Views/TimelineNodeView.swift b/Sources/FioriSwiftUICore/Views/TimelineNodeView.swift new file mode 100644 index 000000000..520a19253 --- /dev/null +++ b/Sources/FioriSwiftUICore/Views/TimelineNodeView.swift @@ -0,0 +1,46 @@ +import SwiftUI + +/// 'TImelineNodeView' displays an image representing a timeline node. +public struct TimelineNodeView: View { + /// The type of timeline node content to display + public var nodeType: TimelineNodeType + + /// init with node image + public init(_ nodeType: TimelineNodeType) { + self.nodeType = nodeType + } + + @ViewBuilder + public var body: some View { + switch self.nodeType { + case .beforeStart, .beforeEnd: + Image(systemName: "diamond") + case .start, .end: + Image(systemName: "diamond.fill") + case .open: + Image(systemName: "circle") + case .inProgress: + Image(systemName: "ellipsis.circle.fill") + case .complete: + Image(systemName: "checkmark.circle.fill") + } + } +} + +/// The status of a timeline node +public enum TimelineNodeType { + /// timeline event before start + case beforeStart + /// timeline event start + case start + /// timeline event open + case open + /// timeline event in progress + case inProgress + /// timeline event complete + case complete + /// timeline event before end + case beforeEnd + /// timeline event end + case end +} diff --git a/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift b/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift index 979cbc986..266b860da 100755 --- a/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift +++ b/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift @@ -270,3 +270,40 @@ protocol _CloseActionComponent { // sourcery: resultBuilder.defaultValue = "{ FioriButton { _ in Image(systemName: "xmark") } }" var closeAction: FioriButton? { get } } + +// sourcery: BaseComponent +protocol _TimestampComponent { + // sourcery: @ViewBuilder + var timestamp: AttributedString? { get } +} + +// sourcery: BaseComponent +protocol _SecondaryTimestampComponent { + // sourcery: resultBuilder.name = @ViewBuilder, resultBuilder.backingComponent = TextOrIconView + var secondaryTimestamp: TextOrIcon? { get } +} + +// sourcery: BaseComponent +protocol _TimelineNodeComponent { + // sourcery: resultBuilder.name = @ViewBuilder, resultBuilder.backingComponent = TimelineNodeView + var timelineNode: TimelineNodeType { get } +} + +// sourcery: BaseComponent +protocol _AttributeComponent { + // sourcery: @ViewBuilder + var attribute: AttributedString? { get } +} + +// sourcery: BaseComponent +protocol _SubAttributeComponent { + // sourcery: @ViewBuilder + var subAttribute: AttributedString? { get } +} + +// sourcery: BaseComponent +protocol _NowIndicatorNodeComponent { + // sourcery: defaultValue = "{ Image(systemName: "circle.fill") }" + @ViewBuilder + var nowIndicatorNode: (() -> any View)? { get } +} diff --git a/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift b/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift index 69d23f2d6..de13ed638 100755 --- a/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift +++ b/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift @@ -318,6 +318,69 @@ protocol _RatingControlComponent { var interItemSpacing: CGFloat? { get } } +/// `TimelineMarker` is a non-selectable view intended for timelineMarkers that require beforeStart, start, beforeEnd and end status that displays timelineMarker details. +/// It uses a vertical line and `timelineNode` as a separator. To the left of the vertical line is the timeline timestamp stack view that contains `timestamp` and `secondaryTimestamp`. To the right of the vertical line is the main stack view that contains `title`. There is a divider line as separator under main stack. +/// +/// ## Notes +/// ### Separator between TimelineMarker Items in the List +/// All list styles in SwiftUI include separators by default. This is why there is a separator between two timelineMarker items in the list. To get rid of the separator, set ‘listRowSeparator’ modifier to hidden. +/// +/// ## Usage +/// ```swift +/// TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .beforeStart, title: "Before Start", isPast: true, showUpperVerticalLine: false) +/// TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .text("Sunny"), timelineNode: .beforeEnd, title: "Before End", isPresent: true) +/// ``` +// sourcery: CompositeComponent +protocol _TimelineMarkerComponent: _TimestampComponent, _SecondaryTimestampComponent, _TimelineNodeComponent, _IconComponent, _TitleComponent { + // sourcery: defaultValue = false + /// check if event is past + var isPast: Bool { get } + + // sourcery: defaultValue = false + /// check if event is present + var isPresent: Bool { get } + + // sourcery: defaultValue = true + /// Show upper vertical line or not. Default is to show. + var showUpperVerticalLine: Bool { get } + + // sourcery: defaultValue = true + /// Show lower vertical line or not. Default is to show. + var showLowerVerticalLine: Bool { get } +} + +/// `Timeline` is a selectable view intended for timelines that require open, inProgress and complete status that displays timeline details. +/// It uses a vertical line and `timelineNode` as a separator. To the left of the vertical line is the timeline timestamp stack view that contains `timestamp` and `secondaryTimestamp`. To the right of the vertical line is the main stack view that contains title view and attribute view. Title view contains `title`, ‘subtitle’, ‘status’, ‘substatus’, below the title view is an attribute view with ‘ attribute’ and ‘subAttribute’. There is a divider line as separator under main stack. +/// +/// ## Notes +/// ### Separator between Timeline Items in the List +/// All list styles in SwiftUI include separators by default. That is why there is a separator between two timeline items in the list. To get rid of the separator, set ‘listRowSeparator’ modifier to hidden. +/// +/// ## Usage +/// ```swift +/// Timeline(timestamp: "06/21/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .complete, title: "Complete(Disabled)", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .icon(Image(systemName: "exclamationmark.circle")), subAttribute: "subAttr", isPast: true) +/// Timeline(timestamp: "06/21/24", secondaryTimestamp: .text("Sunny"), timelineNode: .open, title: "Open", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .icon(Image(systemName: "exclamationmark.circle")), subAttribute: "subAttr") +/// ``` +// sourcery: CompositeComponent +protocol _TimelineComponent: _TimestampComponent, _SecondaryTimestampComponent, _TimelineNodeComponent, _IconComponent, _TitleComponent, _SubtitleComponent, _AttributeComponent, _StatusComponent, _SubstatusComponent, _SubAttributeComponent { + // sourcery: defaultValue = false + /// check if event is past + var isPast: Bool { get } + + // sourcery: defaultValue = false + /// check if event is present + var isPresent: Bool { get } +} + +/// `TimelineNowIndicator` is used to present now indicator in a Timeline view. +/// It uses a node view and horizontal line to present now indicator. +/// +/// ## Notes +/// ### Minimum list row height between Timeline Items in the List +/// Since the default size of node image on the TimelineNowIndicator is 7 pixels, in order to display TimelineNowIndicator correctly in the List, set the minimum height for all row in a List using the .environment(\.defaultMinListRowHeight, value) modifier on the List, the value should be less than or equal to 7. +// sourcery: CompositeComponent +protocol _TimelineNowIndicatorComponent: _NowIndicatorNodeComponent {} + /// The form view which contains a title, rating control, and a subtitle // sourcery: CompositeComponent protocol _RatingControlFormViewComponent: _TitleComponent, _RatingControlComponent, _SubtitleComponent, _FormViewComponent { diff --git a/Sources/FioriSwiftUICore/_FioriStyles/AttributeStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/AttributeStyle.fiori.swift new file mode 100644 index 000000000..d43e992b8 --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/AttributeStyle.fiori.swift @@ -0,0 +1,19 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct AttributeBaseStyle: AttributeStyle { + @ViewBuilder + public func makeBody(_ configuration: AttributeConfiguration) -> some View { + configuration.attribute + } +} + +// Default fiori styles +public struct AttributeFioriStyle: AttributeStyle { + @ViewBuilder + public func makeBody(_ configuration: AttributeConfiguration) -> some View { + Attribute(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/NowIndicatorNodeStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/NowIndicatorNodeStyle.fiori.swift new file mode 100644 index 000000000..c03da53ff --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/NowIndicatorNodeStyle.fiori.swift @@ -0,0 +1,19 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct NowIndicatorNodeBaseStyle: NowIndicatorNodeStyle { + @ViewBuilder + public func makeBody(_ configuration: NowIndicatorNodeConfiguration) -> some View { + configuration.nowIndicatorNode + } +} + +// Default fiori styles +public struct NowIndicatorNodeFioriStyle: NowIndicatorNodeStyle { + @ViewBuilder + public func makeBody(_ configuration: NowIndicatorNodeConfiguration) -> some View { + NowIndicatorNode(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/SecondaryTimestampStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/SecondaryTimestampStyle.fiori.swift new file mode 100644 index 000000000..ad4ddb956 --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/SecondaryTimestampStyle.fiori.swift @@ -0,0 +1,19 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct SecondaryTimestampBaseStyle: SecondaryTimestampStyle { + @ViewBuilder + public func makeBody(_ configuration: SecondaryTimestampConfiguration) -> some View { + configuration.secondaryTimestamp + } +} + +// Default fiori styles +public struct SecondaryTimestampFioriStyle: SecondaryTimestampStyle { + @ViewBuilder + public func makeBody(_ configuration: SecondaryTimestampConfiguration) -> some View { + SecondaryTimestamp(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/SubAttributeStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/SubAttributeStyle.fiori.swift new file mode 100644 index 000000000..417dca7e3 --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/SubAttributeStyle.fiori.swift @@ -0,0 +1,19 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct SubAttributeBaseStyle: SubAttributeStyle { + @ViewBuilder + public func makeBody(_ configuration: SubAttributeConfiguration) -> some View { + configuration.subAttribute + } +} + +// Default fiori styles +public struct SubAttributeFioriStyle: SubAttributeStyle { + @ViewBuilder + public func makeBody(_ configuration: SubAttributeConfiguration) -> some View { + SubAttribute(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/TimelineMarkerStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/TimelineMarkerStyle.fiori.swift new file mode 100644 index 000000000..71e80d086 --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/TimelineMarkerStyle.fiori.swift @@ -0,0 +1,152 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct TimelineMarkerBaseStyle: TimelineMarkerStyle { + @State var timelineMarkerWidth: CGFloat = 100 + + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + ZStack { + HStack { + Spacer() + Rectangle() + .foregroundColor(configuration.isPresent ? Color.preferredColor(.blue1) : Color.preferredColor(.secondaryGroupedBackground)) + .frame(alignment: .trailing) + .frame(width: self.timelineMarkerWidth - 92.5) + } + HStack(alignment: .top, spacing: 0) { + VStack(alignment: .trailing) { + HStack { + Spacer() + configuration.timestamp + } + HStack { + Spacer() + configuration.secondaryTimestamp + } + } + .frame(width: 60) + .background(configuration.isPresent ? Color.preferredColor(.secondaryGroupedBackground) : Color.clear) + .padding(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 0)) + .alignmentGuide(.top) { _ in + 16 + } + VStack(alignment: .center, spacing: 0) { + Rectangle() + .frame(width: 2, height: 16) + .foregroundColor(configuration.isPast || configuration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.grey3)) + .opacity(configuration.showUpperVerticalLine ? 1 : 0) + if configuration.icon.isEmpty { + configuration.timelineNode + } else { + configuration.icon + } + Rectangle() + .frame(width: 2) + .foregroundColor(configuration.isPast || configuration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.grey3)) + .opacity(configuration.showLowerVerticalLine ? 1 : 0) + } + .frame(width: 15) + .padding(EdgeInsets(top: 0, leading: 9, bottom: 0, trailing: 8)) + .alignmentGuide(.top) { _ in + 16 + } + VStack(alignment: .leading) { + HStack { + configuration.title + Spacer() + } + Divider() + .foregroundColor(Color.preferredColor(.separator).opacity(0.41)) + .frame(height: 0.33) + .padding(EdgeInsets(top: 0, leading: 9, bottom: 0, trailing: -16)) + .offset(y: 16) + } + .padding(EdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 16)) + .alignmentGuide(.top) { _ in + 16 + } + } + .fixedSize(horizontal: false, vertical: true) + .overlay { + GeometryReader { proxy in + Color.clear + .preference(key: TimelineMarkerWidthKey.self, value: proxy.size.width) + } + } + } + .onPreferenceChange(TimelineMarkerWidthKey.self) { value in + DispatchQueue.main.async { + self.timelineMarkerWidth = value + } + } + } +} + +struct TimelineMarkerWidthKey: PreferenceKey { + static var defaultValue: CGFloat { 0 } + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} + +// Default fiori styles +extension TimelineMarkerFioriStyle { + struct ContentFioriStyle: TimelineMarkerStyle { + func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .allowsHitTesting(/*@START_MENU_TOKEN@*/false/*@END_MENU_TOKEN@*/) + } + } + + struct TimestampFioriStyle: TimestampStyle { + let timelineMarkerConfiguration: TimelineMarkerConfiguration + + func makeBody(_ configuration: TimestampConfiguration) -> some View { + Timestamp(configuration) + .font(.fiori(forTextStyle: .caption1)) + .foregroundColor(Color.preferredColor(.primaryLabel)) + } + } + + struct SecondaryTimestampFioriStyle: SecondaryTimestampStyle { + let timelineMarkerConfiguration: TimelineMarkerConfiguration + + func makeBody(_ configuration: SecondaryTimestampConfiguration) -> some View { + SecondaryTimestamp(configuration) + .font(.fiori(forTextStyle: .caption1)) + .foregroundColor(Color.preferredColor(.primaryLabel)) + } + } + + struct TimelineNodeFioriStyle: TimelineNodeStyle { + let timelineMarkerConfiguration: TimelineMarkerConfiguration + + func makeBody(_ configuration: TimelineNodeConfiguration) -> some View { + TimelineNode(configuration) + .foregroundColor(self.timelineMarkerConfiguration.isPast || self.timelineMarkerConfiguration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.separatorOpaque)) + .font(.system(size: 9, weight: .bold)) + } + } + + struct IconFioriStyle: IconStyle { + let timelineMarkerConfiguration: TimelineMarkerConfiguration + + func makeBody(_ configuration: IconConfiguration) -> some View { + Icon(configuration) + .foregroundColor(self.timelineMarkerConfiguration.isPast || self.timelineMarkerConfiguration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.separatorOpaque)) + .font(.system(size: 9, weight: .bold)) + } + } + + struct TitleFioriStyle: TitleStyle { + let timelineMarkerConfiguration: TimelineMarkerConfiguration + + func makeBody(_ configuration: TitleConfiguration) -> some View { + Title(configuration) + .font(.fiori(forTextStyle: .subheadline)) + .foregroundColor(Color.preferredColor(.primaryLabel)) + } + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/TimelineNodeStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/TimelineNodeStyle.fiori.swift new file mode 100644 index 000000000..69f763846 --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/TimelineNodeStyle.fiori.swift @@ -0,0 +1,19 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct TimelineNodeBaseStyle: TimelineNodeStyle { + @ViewBuilder + public func makeBody(_ configuration: TimelineNodeConfiguration) -> some View { + configuration.timelineNode + } +} + +// Default fiori styles +public struct TimelineNodeFioriStyle: TimelineNodeStyle { + @ViewBuilder + public func makeBody(_ configuration: TimelineNodeConfiguration) -> some View { + TimelineNode(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/TimelineNowIndicatorStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/TimelineNowIndicatorStyle.fiori.swift new file mode 100644 index 000000000..10ddce151 --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/TimelineNowIndicatorStyle.fiori.swift @@ -0,0 +1,37 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct TimelineNowIndicatorBaseStyle: TimelineNowIndicatorStyle { + public func makeBody(_ configuration: TimelineNowIndicatorConfiguration) -> some View { + HStack(alignment: .center, spacing: 0) { + configuration.nowIndicatorNode + Rectangle() + .frame(height: 2) + .foregroundColor(Color.preferredColor(.tintColor)) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + } + } +} + +// Default fiori styles +extension TimelineNowIndicatorFioriStyle { + struct ContentFioriStyle: TimelineNowIndicatorStyle { + func makeBody(_ configuration: TimelineNowIndicatorConfiguration) -> some View { + TimelineNowIndicator(configuration) + .padding(EdgeInsets(top: 0, leading: 88, bottom: 0, trailing: 0)) + } + } + + struct NowIndicatorNodeFioriStyle: NowIndicatorNodeStyle { + let timelineNowIndicatorConfiguration: TimelineNowIndicatorConfiguration + + func makeBody(_ configuration: NowIndicatorNodeConfiguration) -> some View { + NowIndicatorNode(configuration) + .font(.system(size: 7)) + .foregroundColor(.preferredColor(.tintColor)) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + } + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/TimelineStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/TimelineStyle.fiori.swift new file mode 100644 index 000000000..2bba6d137 --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/TimelineStyle.fiori.swift @@ -0,0 +1,244 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct TimelineBaseStyle: TimelineStyle { + @State var timelineMainStackWidth: CGFloat = 100 + @Environment(\.isEnabled) var isEnabled: Bool + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + VStack { + ZStack { + HStack { + Spacer() + Rectangle() + .foregroundColor(configuration.isPresent ? Color.preferredColor(.blue1) : Color.preferredColor(.secondaryGroupedBackground)) + .frame(alignment: .trailing) + .frame(width: self.timelineMainStackWidth - 92.5) + } + HStack(alignment: .top, spacing: 0) { + TimelineTimestampStack(configuration: configuration) + TimelineNodeStack(configuration: configuration) + TimelineMainStack(configuration: configuration) + } + .fixedSize(horizontal: false, vertical: true) + .overlay { + GeometryReader { proxy in + Color.clear.preference(key: TMSSizePreferenceKey.self, value: proxy.size) + } + } + } + .onPreferenceChange(TMSSizePreferenceKey.self) { newValue in + self.timelineMainStackWidth = newValue.width + } + } + } +} + +struct TimelineTimestampStack: View { + let configuration: TimelineConfiguration + + var body: some View { + VStack(alignment: .trailing) { + HStack { + Spacer() + self.configuration.timestamp + } + HStack { + Spacer() + self.configuration.secondaryTimestamp + } + } + .frame(width: 60) + .background(self.configuration.isPresent ? Color.preferredColor(.secondaryGroupedBackground) : Color.clear) + .padding(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 0)) + .alignmentGuide(.top) { _ in + 16 + } + } +} + +struct TimelineNodeStack: View { + let configuration: TimelineConfiguration + + var body: some View { + VStack(alignment: .center, spacing: 0) { + Rectangle() + .frame(width: 2, height: 16) + .foregroundColor(self.configuration.isPast || self.configuration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.grey3)) + if self.configuration.icon.isEmpty { + self.configuration.timelineNode + } else { + self.configuration.icon + } + Rectangle() + .frame(width: 2) + .foregroundColor(self.configuration.isPast || self.configuration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.grey3)) + } + .frame(width: 15) + .padding(EdgeInsets(top: 0, leading: 9, bottom: 0, trailing: 8)) + .alignmentGuide(.top) { _ in + 16 + } + } +} + +struct TimelineMainStack: View { + let configuration: TimelineConfiguration + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .top) { + VStack(alignment: .leading) { + self.configuration.title + self.configuration.subtitle + } + Spacer() + VStack(alignment: .trailing) { + self.configuration.status + self.configuration.substatus + } + } + HStack(alignment: .top) { + self.configuration.attribute + Spacer() + self.configuration.subAttribute + } + Divider() + .foregroundColor(Color.preferredColor(.separator).opacity(0.41)) + .frame(height: 0.33) + .padding(EdgeInsets(top: 0, leading: 9, bottom: 0, trailing: -16)) + .offset(y: 16) + } + .padding(EdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 16)) + .alignmentGuide(.top) { _ in + 16 + } + } +} + +struct TMSSizePreferenceKey: PreferenceKey { + static var defaultValue: CGSize = .zero + static func reduce(value: inout CGSize, nextValue: () -> CGSize) { + value = nextValue() + } +} + +// Default fiori styles +extension TimelineFioriStyle { + struct ContentFioriStyle: TimelineStyle { + func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .contentShape(.rect) + } + } + + struct TimestampFioriStyle: TimestampStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: TimestampConfiguration) -> some View { + Timestamp(configuration) + .font(.fiori(forTextStyle: .caption1)) + .foregroundColor(self.isEnabled ? Color.preferredColor(.primaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } + + struct SecondaryTimestampFioriStyle: SecondaryTimestampStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: SecondaryTimestampConfiguration) -> some View { + SecondaryTimestamp(configuration) + .font(.fiori(forTextStyle: .caption1)) + .foregroundColor(self.isEnabled ? Color.preferredColor(.primaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } + + struct TimelineNodeFioriStyle: TimelineNodeStyle { + let timelineConfiguration: TimelineConfiguration + + func makeBody(_ configuration: TimelineNodeConfiguration) -> some View { + TimelineNode(configuration) + .foregroundColor(self.timelineConfiguration.isPast || self.timelineConfiguration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.separatorOpaque)) + .font(.system(size: 15, weight: .bold)) + } + } + + struct IconFioriStyle: IconStyle { + let timelineConfiguration: TimelineConfiguration + + func makeBody(_ configuration: IconConfiguration) -> some View { + Icon(configuration) + .foregroundColor(self.timelineConfiguration.isPast || self.timelineConfiguration.isPresent ? Color.preferredColor(.tintColor) : Color.preferredColor(.separatorOpaque)) + .font(.system(size: 15, weight: .bold)) + } + } + + struct TitleFioriStyle: TitleStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: TitleConfiguration) -> some View { + Title(configuration) + .font(.fiori(forTextStyle: .headline)) + .fontWeight(.semibold) + .foregroundColor(self.isEnabled ? Color.preferredColor(.primaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } + + struct SubtitleFioriStyle: SubtitleStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: SubtitleConfiguration) -> some View { + Subtitle(configuration) + .font(.fiori(forTextStyle: .subheadline)) + .foregroundColor(self.isEnabled ? Color.preferredColor(.secondaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } + + struct AttributeFioriStyle: AttributeStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: AttributeConfiguration) -> some View { + Attribute(configuration) + .font(.fiori(forTextStyle: .footnote)) + .foregroundColor(self.isEnabled ? Color.preferredColor(.secondaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } + + struct StatusFioriStyle: StatusStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: StatusConfiguration) -> some View { + Status(configuration) + .font(.fiori(forTextStyle: .footnote)) + .foregroundColor(self.isEnabled ? Color.preferredColor(.secondaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } + + struct SubstatusFioriStyle: SubstatusStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: SubstatusConfiguration) -> some View { + Substatus(configuration) + .font(.fiori(forTextStyle: .footnote)) + .foregroundColor(self.isEnabled ? Color.preferredColor(.secondaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } + + struct SubAttributeFioriStyle: SubAttributeStyle { + let timelineConfiguration: TimelineConfiguration + @Environment(\.isEnabled) var isEnabled: Bool + + func makeBody(_ configuration: SubAttributeConfiguration) -> some View { + SubAttribute(configuration) + .font(.fiori(forTextStyle: .footnote)) + .foregroundColor(self.isEnabled ? Color.preferredColor(.secondaryLabel) : Color.preferredColor(.quaternaryLabel)) + } + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/TimestampStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/TimestampStyle.fiori.swift new file mode 100644 index 000000000..a811fb6bd --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/TimestampStyle.fiori.swift @@ -0,0 +1,19 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct TimestampBaseStyle: TimestampStyle { + @ViewBuilder + public func makeBody(_ configuration: TimestampConfiguration) -> some View { + configuration.timestamp + } +} + +// Default fiori styles +public struct TimestampFioriStyle: TimestampStyle { + @ViewBuilder + public func makeBody(_ configuration: TimestampConfiguration) -> some View { + Timestamp(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/Attribute.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/Attribute.generated.swift new file mode 100644 index 000000000..f6032d910 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/Attribute.generated.swift @@ -0,0 +1,63 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public struct Attribute { + let attribute: any View + + @Environment(\.attributeStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder attribute: () -> any View = { EmptyView() }) { + self.attribute = attribute() + } +} + +public extension Attribute { + init(attribute: AttributedString? = nil) { + self.init(attribute: { OptionalText(attribute) }) + } +} + +public extension Attribute { + init(_ configuration: AttributeConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: AttributeConfiguration, shouldApplyDefaultStyle: Bool) { + self.attribute = configuration.attribute + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension Attribute: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(attribute: .init(self.attribute))).typeErased + .transformEnvironment(\.attributeStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension Attribute { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + Attribute(attribute: { self.attribute }) + .shouldApplyDefaultStyle(false) + .attributeStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/AttributeStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/AttributeStyle.generated.swift new file mode 100644 index 000000000..30bb8d1dc --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Attribute/AttributeStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol AttributeStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: AttributeConfiguration) -> Body +} + +struct AnyAttributeStyle: AttributeStyle { + let content: (AttributeConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (AttributeConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: AttributeConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct AttributeConfiguration { + public let attribute: Attribute + + public typealias Attribute = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNode.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNode.generated.swift new file mode 100644 index 000000000..8cf37e8fe --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNode.generated.swift @@ -0,0 +1,57 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public struct NowIndicatorNode { + let nowIndicatorNode: any View + + @Environment(\.nowIndicatorNodeStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder nowIndicatorNode: () -> any View = { Image(systemName: "circle.fill") }) { + self.nowIndicatorNode = nowIndicatorNode() + } +} + +public extension NowIndicatorNode { + init(_ configuration: NowIndicatorNodeConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: NowIndicatorNodeConfiguration, shouldApplyDefaultStyle: Bool) { + self.nowIndicatorNode = configuration.nowIndicatorNode + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension NowIndicatorNode: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(nowIndicatorNode: .init(self.nowIndicatorNode))).typeErased + .transformEnvironment(\.nowIndicatorNodeStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension NowIndicatorNode { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + NowIndicatorNode(nowIndicatorNode: { self.nowIndicatorNode }) + .shouldApplyDefaultStyle(false) + .nowIndicatorNodeStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNodeStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNodeStyle.generated.swift new file mode 100644 index 000000000..07f690d68 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/NowIndicatorNode/NowIndicatorNodeStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol NowIndicatorNodeStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: NowIndicatorNodeConfiguration) -> Body +} + +struct AnyNowIndicatorNodeStyle: NowIndicatorNodeStyle { + let content: (NowIndicatorNodeConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (NowIndicatorNodeConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: NowIndicatorNodeConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct NowIndicatorNodeConfiguration { + public let nowIndicatorNode: NowIndicatorNode + + public typealias NowIndicatorNode = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestamp.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestamp.generated.swift new file mode 100644 index 000000000..c5d59840d --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestamp.generated.swift @@ -0,0 +1,63 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public struct SecondaryTimestamp { + let secondaryTimestamp: any View + + @Environment(\.secondaryTimestampStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder secondaryTimestamp: () -> any View = { EmptyView() }) { + self.secondaryTimestamp = secondaryTimestamp() + } +} + +public extension SecondaryTimestamp { + init(secondaryTimestamp: TextOrIcon? = nil) { + self.init(secondaryTimestamp: { TextOrIconView(secondaryTimestamp) }) + } +} + +public extension SecondaryTimestamp { + init(_ configuration: SecondaryTimestampConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: SecondaryTimestampConfiguration, shouldApplyDefaultStyle: Bool) { + self.secondaryTimestamp = configuration.secondaryTimestamp + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension SecondaryTimestamp: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(secondaryTimestamp: .init(self.secondaryTimestamp))).typeErased + .transformEnvironment(\.secondaryTimestampStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension SecondaryTimestamp { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + SecondaryTimestamp(secondaryTimestamp: { self.secondaryTimestamp }) + .shouldApplyDefaultStyle(false) + .secondaryTimestampStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestampStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestampStyle.generated.swift new file mode 100644 index 000000000..be8137f37 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SecondaryTimestamp/SecondaryTimestampStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol SecondaryTimestampStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: SecondaryTimestampConfiguration) -> Body +} + +struct AnySecondaryTimestampStyle: SecondaryTimestampStyle { + let content: (SecondaryTimestampConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (SecondaryTimestampConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: SecondaryTimestampConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct SecondaryTimestampConfiguration { + public let secondaryTimestamp: SecondaryTimestamp + + public typealias SecondaryTimestamp = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttribute.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttribute.generated.swift new file mode 100644 index 000000000..e114148ab --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttribute.generated.swift @@ -0,0 +1,63 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public struct SubAttribute { + let subAttribute: any View + + @Environment(\.subAttributeStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder subAttribute: () -> any View = { EmptyView() }) { + self.subAttribute = subAttribute() + } +} + +public extension SubAttribute { + init(subAttribute: AttributedString? = nil) { + self.init(subAttribute: { OptionalText(subAttribute) }) + } +} + +public extension SubAttribute { + init(_ configuration: SubAttributeConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: SubAttributeConfiguration, shouldApplyDefaultStyle: Bool) { + self.subAttribute = configuration.subAttribute + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension SubAttribute: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(subAttribute: .init(self.subAttribute))).typeErased + .transformEnvironment(\.subAttributeStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension SubAttribute { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + SubAttribute(subAttribute: { self.subAttribute }) + .shouldApplyDefaultStyle(false) + .subAttributeStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttributeStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttributeStyle.generated.swift new file mode 100644 index 000000000..a7430aca8 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/SubAttribute/SubAttributeStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol SubAttributeStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: SubAttributeConfiguration) -> Body +} + +struct AnySubAttributeStyle: SubAttributeStyle { + let content: (SubAttributeConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (SubAttributeConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: SubAttributeConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct SubAttributeConfiguration { + public let subAttribute: SubAttribute + + public typealias SubAttribute = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/Timeline.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/Timeline.generated.swift new file mode 100644 index 000000000..611f769ec --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/Timeline.generated.swift @@ -0,0 +1,134 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +/// `Timeline` is a selectable view intended for timelines that require open, inProgress and complete status that displays timeline details. +/// It uses a vertical line and `timelineNode` as a separator. To the left of the vertical line is the timeline timestamp stack view that contains `timestamp` and `secondaryTimestamp`. To the right of the vertical line is the main stack view that contains title view and attribute view. Title view contains `title`, ‘subtitle’, ‘status’, ‘substatus’, below the title view is an attribute view with ‘ attribute’ and ‘subAttribute’. There is a divider line as separator under main stack. +/// +/// ## Notes +/// ### Separator between Timeline Items in the List +/// All list styles in SwiftUI include separators by default. That is why there is a separator between two timeline items in the list. To get rid of the separator, set ‘listRowSeparator’ modifier to hidden. +/// +/// ## Usage +/// ```swift +/// Timeline(timestamp: "06/21/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .complete, title: "Complete(Disabled)", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .icon(Image(systemName: "exclamationmark.circle")), subAttribute: "subAttr", isPast: true) +/// Timeline(timestamp: "06/21/24", secondaryTimestamp: .text("Sunny"), timelineNode: .open, title: "Open", subtitle: "abc", attribute: "attr", status: .text("Info"), substatus: .icon(Image(systemName: "exclamationmark.circle")), subAttribute: "subAttr") +/// ``` +public struct Timeline { + let timestamp: any View + let secondaryTimestamp: any View + let timelineNode: any View + let icon: any View + let title: any View + let subtitle: any View + let attribute: any View + let status: any View + let substatus: any View + let subAttribute: any View + /// check if event is past + let isPast: Bool + /// check if event is present + let isPresent: Bool + + @Environment(\.timelineStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder timestamp: () -> any View = { EmptyView() }, + @ViewBuilder secondaryTimestamp: () -> any View = { EmptyView() }, + @ViewBuilder timelineNode: () -> any View, + @ViewBuilder icon: () -> any View = { EmptyView() }, + @ViewBuilder title: () -> any View, + @ViewBuilder subtitle: () -> any View = { EmptyView() }, + @ViewBuilder attribute: () -> any View = { EmptyView() }, + @ViewBuilder status: () -> any View = { EmptyView() }, + @ViewBuilder substatus: () -> any View = { EmptyView() }, + @ViewBuilder subAttribute: () -> any View = { EmptyView() }, + isPast: Bool = false, + isPresent: Bool = false) + { + self.timestamp = Timestamp { timestamp() } + self.secondaryTimestamp = SecondaryTimestamp { secondaryTimestamp() } + self.timelineNode = TimelineNode { timelineNode() } + self.icon = Icon { icon() } + self.title = Title { title() } + self.subtitle = Subtitle { subtitle() } + self.attribute = Attribute { attribute() } + self.status = Status { status() } + self.substatus = Substatus { substatus() } + self.subAttribute = SubAttribute { subAttribute() } + self.isPast = isPast + self.isPresent = isPresent + } +} + +public extension Timeline { + init(timestamp: AttributedString? = nil, + secondaryTimestamp: TextOrIcon? = nil, + timelineNode: TimelineNodeType, + icon: Image? = nil, + title: AttributedString, + subtitle: AttributedString? = nil, + attribute: AttributedString? = nil, + status: TextOrIcon? = nil, + substatus: TextOrIcon? = nil, + subAttribute: AttributedString? = nil, + isPast: Bool = false, + isPresent: Bool = false) + { + self.init(timestamp: { OptionalText(timestamp) }, secondaryTimestamp: { TextOrIconView(secondaryTimestamp) }, timelineNode: { TimelineNodeView(timelineNode) }, icon: { icon }, title: { Text(title) }, subtitle: { OptionalText(subtitle) }, attribute: { OptionalText(attribute) }, status: { TextOrIconView(status) }, substatus: { TextOrIconView(substatus) }, subAttribute: { OptionalText(subAttribute) }, isPast: isPast, isPresent: isPresent) + } +} + +public extension Timeline { + init(_ configuration: TimelineConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: TimelineConfiguration, shouldApplyDefaultStyle: Bool) { + self.timestamp = configuration.timestamp + self.secondaryTimestamp = configuration.secondaryTimestamp + self.timelineNode = configuration.timelineNode + self.icon = configuration.icon + self.title = configuration.title + self.subtitle = configuration.subtitle + self.attribute = configuration.attribute + self.status = configuration.status + self.substatus = configuration.substatus + self.subAttribute = configuration.subAttribute + self.isPast = configuration.isPast + self.isPresent = configuration.isPresent + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension Timeline: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(timestamp: .init(self.timestamp), secondaryTimestamp: .init(self.secondaryTimestamp), timelineNode: .init(self.timelineNode), icon: .init(self.icon), title: .init(self.title), subtitle: .init(self.subtitle), attribute: .init(self.attribute), status: .init(self.status), substatus: .init(self.substatus), subAttribute: .init(self.subAttribute), isPast: self.isPast, isPresent: self.isPresent)).typeErased + .transformEnvironment(\.timelineStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension Timeline { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + Timeline(.init(timestamp: .init(self.timestamp), secondaryTimestamp: .init(self.secondaryTimestamp), timelineNode: .init(self.timelineNode), icon: .init(self.icon), title: .init(self.title), subtitle: .init(self.subtitle), attribute: .init(self.attribute), status: .init(self.status), substatus: .init(self.substatus), subAttribute: .init(self.subAttribute), isPast: self.isPast, isPresent: self.isPresent)) + .shouldApplyDefaultStyle(false) + .timelineStyle(TimelineFioriStyle.ContentFioriStyle()) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/TimelineStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/TimelineStyle.generated.swift new file mode 100644 index 000000000..63d80fba6 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timeline/TimelineStyle.generated.swift @@ -0,0 +1,64 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol TimelineStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: TimelineConfiguration) -> Body +} + +struct AnyTimelineStyle: TimelineStyle { + let content: (TimelineConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (TimelineConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct TimelineConfiguration { + public let timestamp: Timestamp + public let secondaryTimestamp: SecondaryTimestamp + public let timelineNode: TimelineNode + public let icon: Icon + public let title: Title + public let subtitle: Subtitle + public let attribute: Attribute + public let status: Status + public let substatus: Substatus + public let subAttribute: SubAttribute + public let isPast: Bool + public let isPresent: Bool + + public typealias Timestamp = ConfigurationViewWrapper + public typealias SecondaryTimestamp = ConfigurationViewWrapper + public typealias TimelineNode = ConfigurationViewWrapper + public typealias Icon = ConfigurationViewWrapper + public typealias Title = ConfigurationViewWrapper + public typealias Subtitle = ConfigurationViewWrapper + public typealias Attribute = ConfigurationViewWrapper + public typealias Status = ConfigurationViewWrapper + public typealias Substatus = ConfigurationViewWrapper + public typealias SubAttribute = ConfigurationViewWrapper +} + +public struct TimelineFioriStyle: TimelineStyle { + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .timestampStyle(TimestampFioriStyle(timelineConfiguration: configuration)) + .secondaryTimestampStyle(SecondaryTimestampFioriStyle(timelineConfiguration: configuration)) + .timelineNodeStyle(TimelineNodeFioriStyle(timelineConfiguration: configuration)) + .iconStyle(IconFioriStyle(timelineConfiguration: configuration)) + .titleStyle(TitleFioriStyle(timelineConfiguration: configuration)) + .subtitleStyle(SubtitleFioriStyle(timelineConfiguration: configuration)) + .attributeStyle(AttributeFioriStyle(timelineConfiguration: configuration)) + .statusStyle(StatusFioriStyle(timelineConfiguration: configuration)) + .substatusStyle(SubstatusFioriStyle(timelineConfiguration: configuration)) + .subAttributeStyle(SubAttributeFioriStyle(timelineConfiguration: configuration)) + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarker.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarker.generated.swift new file mode 100644 index 000000000..dbd1886c8 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarker.generated.swift @@ -0,0 +1,121 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +/// `TimelineMarker` is a non-selectable view intended for timelineMarkers that require beforeStart, start, beforeEnd and end status that displays timelineMarker details. +/// It uses a vertical line and `timelineNode` as a separator. To the left of the vertical line is the timeline timestamp stack view that contains `timestamp` and `secondaryTimestamp`. To the right of the vertical line is the main stack view that contains `title`. There is a divider line as separator under main stack. +/// +/// ## Notes +/// ### Separator between TimelineMarker Items in the List +/// All list styles in SwiftUI include separators by default. This is why there is a separator between two timelineMarker items in the list. To get rid of the separator, set ‘listRowSeparator’ modifier to hidden. +/// +/// ## Usage +/// ```swift +/// TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .icon(Image(systemName: "sun.max")), timelineNode: .beforeStart, title: "Before Start", isPast: true, showUpperVerticalLine: false) +/// TimelineMarker(timestamp: "06/20/24", secondaryTimestamp: .text("Sunny"), timelineNode: .beforeEnd, title: "Before End", isPresent: true) +/// ``` +public struct TimelineMarker { + let timestamp: any View + let secondaryTimestamp: any View + let timelineNode: any View + let icon: any View + let title: any View + /// check if event is past + let isPast: Bool + /// check if event is present + let isPresent: Bool + /// Show upper vertical line or not. Default is to show. + let showUpperVerticalLine: Bool + /// Show lower vertical line or not. Default is to show. + let showLowerVerticalLine: Bool + + @Environment(\.timelineMarkerStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder timestamp: () -> any View = { EmptyView() }, + @ViewBuilder secondaryTimestamp: () -> any View = { EmptyView() }, + @ViewBuilder timelineNode: () -> any View, + @ViewBuilder icon: () -> any View = { EmptyView() }, + @ViewBuilder title: () -> any View, + isPast: Bool = false, + isPresent: Bool = false, + showUpperVerticalLine: Bool = true, + showLowerVerticalLine: Bool = true) + { + self.timestamp = Timestamp { timestamp() } + self.secondaryTimestamp = SecondaryTimestamp { secondaryTimestamp() } + self.timelineNode = TimelineNode { timelineNode() } + self.icon = Icon { icon() } + self.title = Title { title() } + self.isPast = isPast + self.isPresent = isPresent + self.showUpperVerticalLine = showUpperVerticalLine + self.showLowerVerticalLine = showLowerVerticalLine + } +} + +public extension TimelineMarker { + init(timestamp: AttributedString? = nil, + secondaryTimestamp: TextOrIcon? = nil, + timelineNode: TimelineNodeType, + icon: Image? = nil, + title: AttributedString, + isPast: Bool = false, + isPresent: Bool = false, + showUpperVerticalLine: Bool = true, + showLowerVerticalLine: Bool = true) + { + self.init(timestamp: { OptionalText(timestamp) }, secondaryTimestamp: { TextOrIconView(secondaryTimestamp) }, timelineNode: { TimelineNodeView(timelineNode) }, icon: { icon }, title: { Text(title) }, isPast: isPast, isPresent: isPresent, showUpperVerticalLine: showUpperVerticalLine, showLowerVerticalLine: showLowerVerticalLine) + } +} + +public extension TimelineMarker { + init(_ configuration: TimelineMarkerConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: TimelineMarkerConfiguration, shouldApplyDefaultStyle: Bool) { + self.timestamp = configuration.timestamp + self.secondaryTimestamp = configuration.secondaryTimestamp + self.timelineNode = configuration.timelineNode + self.icon = configuration.icon + self.title = configuration.title + self.isPast = configuration.isPast + self.isPresent = configuration.isPresent + self.showUpperVerticalLine = configuration.showUpperVerticalLine + self.showLowerVerticalLine = configuration.showLowerVerticalLine + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension TimelineMarker: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(timestamp: .init(self.timestamp), secondaryTimestamp: .init(self.secondaryTimestamp), timelineNode: .init(self.timelineNode), icon: .init(self.icon), title: .init(self.title), isPast: self.isPast, isPresent: self.isPresent, showUpperVerticalLine: self.showUpperVerticalLine, showLowerVerticalLine: self.showLowerVerticalLine)).typeErased + .transformEnvironment(\.timelineMarkerStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension TimelineMarker { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + TimelineMarker(.init(timestamp: .init(self.timestamp), secondaryTimestamp: .init(self.secondaryTimestamp), timelineNode: .init(self.timelineNode), icon: .init(self.icon), title: .init(self.title), isPast: self.isPast, isPresent: self.isPresent, showUpperVerticalLine: self.showUpperVerticalLine, showLowerVerticalLine: self.showLowerVerticalLine)) + .shouldApplyDefaultStyle(false) + .timelineMarkerStyle(TimelineMarkerFioriStyle.ContentFioriStyle()) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarkerStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarkerStyle.generated.swift new file mode 100644 index 000000000..92b296b16 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineMarker/TimelineMarkerStyle.generated.swift @@ -0,0 +1,51 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol TimelineMarkerStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: TimelineMarkerConfiguration) -> Body +} + +struct AnyTimelineMarkerStyle: TimelineMarkerStyle { + let content: (TimelineMarkerConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (TimelineMarkerConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct TimelineMarkerConfiguration { + public let timestamp: Timestamp + public let secondaryTimestamp: SecondaryTimestamp + public let timelineNode: TimelineNode + public let icon: Icon + public let title: Title + public let isPast: Bool + public let isPresent: Bool + public let showUpperVerticalLine: Bool + public let showLowerVerticalLine: Bool + + public typealias Timestamp = ConfigurationViewWrapper + public typealias SecondaryTimestamp = ConfigurationViewWrapper + public typealias TimelineNode = ConfigurationViewWrapper + public typealias Icon = ConfigurationViewWrapper + public typealias Title = ConfigurationViewWrapper +} + +public struct TimelineMarkerFioriStyle: TimelineMarkerStyle { + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .timestampStyle(TimestampFioriStyle(timelineMarkerConfiguration: configuration)) + .secondaryTimestampStyle(SecondaryTimestampFioriStyle(timelineMarkerConfiguration: configuration)) + .timelineNodeStyle(TimelineNodeFioriStyle(timelineMarkerConfiguration: configuration)) + .iconStyle(IconFioriStyle(timelineMarkerConfiguration: configuration)) + .titleStyle(TitleFioriStyle(timelineMarkerConfiguration: configuration)) + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNode.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNode.generated.swift new file mode 100644 index 000000000..73294ec30 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNode.generated.swift @@ -0,0 +1,63 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public struct TimelineNode { + let timelineNode: any View + + @Environment(\.timelineNodeStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder timelineNode: () -> any View) { + self.timelineNode = timelineNode() + } +} + +public extension TimelineNode { + init(timelineNode: TimelineNodeType) { + self.init(timelineNode: { TimelineNodeView(timelineNode) }) + } +} + +public extension TimelineNode { + init(_ configuration: TimelineNodeConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: TimelineNodeConfiguration, shouldApplyDefaultStyle: Bool) { + self.timelineNode = configuration.timelineNode + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension TimelineNode: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(timelineNode: .init(self.timelineNode))).typeErased + .transformEnvironment(\.timelineNodeStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension TimelineNode { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + TimelineNode(timelineNode: { self.timelineNode }) + .shouldApplyDefaultStyle(false) + .timelineNodeStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNodeStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNodeStyle.generated.swift new file mode 100644 index 000000000..dd743fe94 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNode/TimelineNodeStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol TimelineNodeStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: TimelineNodeConfiguration) -> Body +} + +struct AnyTimelineNodeStyle: TimelineNodeStyle { + let content: (TimelineNodeConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (TimelineNodeConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: TimelineNodeConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct TimelineNodeConfiguration { + public let timelineNode: TimelineNode + + public typealias TimelineNode = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicator.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicator.generated.swift new file mode 100644 index 000000000..031ae9e23 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicator.generated.swift @@ -0,0 +1,63 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +/// `TimelineNowIndicator` is used to present now indicator in a Timeline view. +/// It uses a node view and horizontal line to present now indicator. +/// +/// ## Notes +/// ### Minimum list row height between Timeline Items in the List +/// Since the default size of node image on the TimelineNowIndicator is 7 pixels, in order to display TimelineNowIndicator correctly in the List, set the minimum height for all row in a List using the .environment(\.defaultMinListRowHeight, value) modifier on the List, the value should be less than or equal to 7. +public struct TimelineNowIndicator { + let nowIndicatorNode: any View + + @Environment(\.timelineNowIndicatorStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder nowIndicatorNode: () -> any View = { Image(systemName: "circle.fill") }) { + self.nowIndicatorNode = NowIndicatorNode { nowIndicatorNode() } + } +} + +public extension TimelineNowIndicator { + init(_ configuration: TimelineNowIndicatorConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: TimelineNowIndicatorConfiguration, shouldApplyDefaultStyle: Bool) { + self.nowIndicatorNode = configuration.nowIndicatorNode + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension TimelineNowIndicator: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(nowIndicatorNode: .init(self.nowIndicatorNode))).typeErased + .transformEnvironment(\.timelineNowIndicatorStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension TimelineNowIndicator { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + TimelineNowIndicator(.init(nowIndicatorNode: .init(self.nowIndicatorNode))) + .shouldApplyDefaultStyle(false) + .timelineNowIndicatorStyle(TimelineNowIndicatorFioriStyle.ContentFioriStyle()) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicatorStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicatorStyle.generated.swift new file mode 100644 index 000000000..6b86d3860 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TimelineNowIndicator/TimelineNowIndicatorStyle.generated.swift @@ -0,0 +1,35 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol TimelineNowIndicatorStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: TimelineNowIndicatorConfiguration) -> Body +} + +struct AnyTimelineNowIndicatorStyle: TimelineNowIndicatorStyle { + let content: (TimelineNowIndicatorConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (TimelineNowIndicatorConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: TimelineNowIndicatorConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct TimelineNowIndicatorConfiguration { + public let nowIndicatorNode: NowIndicatorNode + + public typealias NowIndicatorNode = ConfigurationViewWrapper +} + +public struct TimelineNowIndicatorFioriStyle: TimelineNowIndicatorStyle { + public func makeBody(_ configuration: TimelineNowIndicatorConfiguration) -> some View { + TimelineNowIndicator(configuration) + .nowIndicatorNodeStyle(NowIndicatorNodeFioriStyle(timelineNowIndicatorConfiguration: configuration)) + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/Timestamp.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/Timestamp.generated.swift new file mode 100644 index 000000000..6157dd6fe --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/Timestamp.generated.swift @@ -0,0 +1,63 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public struct Timestamp { + let timestamp: any View + + @Environment(\.timestampStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder timestamp: () -> any View = { EmptyView() }) { + self.timestamp = timestamp() + } +} + +public extension Timestamp { + init(timestamp: AttributedString? = nil) { + self.init(timestamp: { OptionalText(timestamp) }) + } +} + +public extension Timestamp { + init(_ configuration: TimestampConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: TimestampConfiguration, shouldApplyDefaultStyle: Bool) { + self.timestamp = configuration.timestamp + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension Timestamp: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(timestamp: .init(self.timestamp))).typeErased + .transformEnvironment(\.timestampStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension Timestamp { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + Timestamp(timestamp: { self.timestamp }) + .shouldApplyDefaultStyle(false) + .timestampStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/TimestampStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/TimestampStyle.generated.swift new file mode 100644 index 000000000..22fc748ba --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/Timestamp/TimestampStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol TimestampStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: TimestampConfiguration) -> Body +} + +struct AnyTimestampStyle: TimestampStyle { + let content: (TimestampConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (TimestampConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: TimestampConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct TimestampConfiguration { + public let timestamp: Timestamp + + public typealias Timestamp = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift index a5923fc83..7b9078f06 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift @@ -31,6 +31,20 @@ public extension ActionStyle where Self == ActionFioriStyle { } } +// MARK: AttributeStyle + +public extension AttributeStyle where Self == AttributeBaseStyle { + static var base: AttributeBaseStyle { + AttributeBaseStyle() + } +} + +public extension AttributeStyle where Self == AttributeFioriStyle { + static var fiori: AttributeFioriStyle { + AttributeFioriStyle() + } +} + // MARK: AvatarsStyle public extension AvatarsStyle where Self == AvatarsBaseStyle { @@ -2474,6 +2488,20 @@ public extension NoteFormViewStyle where Self == NoteFormViewFormViewStyle { } } +// MARK: NowIndicatorNodeStyle + +public extension NowIndicatorNodeStyle where Self == NowIndicatorNodeBaseStyle { + static var base: NowIndicatorNodeBaseStyle { + NowIndicatorNodeBaseStyle() + } +} + +public extension NowIndicatorNodeStyle where Self == NowIndicatorNodeFioriStyle { + static var fiori: NowIndicatorNodeFioriStyle { + NowIndicatorNodeFioriStyle() + } +} + // MARK: ObjectItemStyle public extension ObjectItemStyle where Self == ObjectItemBaseStyle { @@ -3146,6 +3174,20 @@ public extension SecondaryActionStyle where Self == SecondaryActionFioriStyle { } } +// MARK: SecondaryTimestampStyle + +public extension SecondaryTimestampStyle where Self == SecondaryTimestampBaseStyle { + static var base: SecondaryTimestampBaseStyle { + SecondaryTimestampBaseStyle() + } +} + +public extension SecondaryTimestampStyle where Self == SecondaryTimestampFioriStyle { + static var fiori: SecondaryTimestampFioriStyle { + SecondaryTimestampFioriStyle() + } +} + // MARK: SideBarStyle public extension SideBarStyle where Self == SideBarBaseStyle { @@ -3573,6 +3615,20 @@ public extension StepperViewStyle where Self == StepperViewInformationViewStyle } } +// MARK: SubAttributeStyle + +public extension SubAttributeStyle where Self == SubAttributeBaseStyle { + static var base: SubAttributeBaseStyle { + SubAttributeBaseStyle() + } +} + +public extension SubAttributeStyle where Self == SubAttributeFioriStyle { + static var fiori: SubAttributeFioriStyle { + SubAttributeFioriStyle() + } +} + // MARK: SubstatusStyle public extension SubstatusStyle where Self == SubstatusBaseStyle { @@ -3888,6 +3944,412 @@ public extension TextViewStyle where Self == TextViewFioriStyle { } } +// MARK: TimelineStyle + +public extension TimelineStyle where Self == TimelineBaseStyle { + static var base: TimelineBaseStyle { + TimelineBaseStyle() + } +} + +public extension TimelineStyle where Self == TimelineFioriStyle { + static var fiori: TimelineFioriStyle { + TimelineFioriStyle() + } +} + +public struct TimelineTimestampStyle: TimelineStyle { + let style: any TimestampStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .timestampStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineTimestampStyle { + static func timestampStyle(_ style: some TimestampStyle) -> TimelineTimestampStyle { + TimelineTimestampStyle(style: style) + } + + static func timestampStyle(@ViewBuilder content: @escaping (TimestampConfiguration) -> some View) -> TimelineTimestampStyle { + let style = AnyTimestampStyle(content) + return TimelineTimestampStyle(style: style) + } +} + +public struct TimelineSecondaryTimestampStyle: TimelineStyle { + let style: any SecondaryTimestampStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .secondaryTimestampStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineSecondaryTimestampStyle { + static func secondaryTimestampStyle(_ style: some SecondaryTimestampStyle) -> TimelineSecondaryTimestampStyle { + TimelineSecondaryTimestampStyle(style: style) + } + + static func secondaryTimestampStyle(@ViewBuilder content: @escaping (SecondaryTimestampConfiguration) -> some View) -> TimelineSecondaryTimestampStyle { + let style = AnySecondaryTimestampStyle(content) + return TimelineSecondaryTimestampStyle(style: style) + } +} + +public struct TimelineTimelineNodeStyle: TimelineStyle { + let style: any TimelineNodeStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .timelineNodeStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineTimelineNodeStyle { + static func timelineNodeStyle(_ style: some TimelineNodeStyle) -> TimelineTimelineNodeStyle { + TimelineTimelineNodeStyle(style: style) + } + + static func timelineNodeStyle(@ViewBuilder content: @escaping (TimelineNodeConfiguration) -> some View) -> TimelineTimelineNodeStyle { + let style = AnyTimelineNodeStyle(content) + return TimelineTimelineNodeStyle(style: style) + } +} + +public struct TimelineIconStyle: TimelineStyle { + let style: any IconStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .iconStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineIconStyle { + static func iconStyle(_ style: some IconStyle) -> TimelineIconStyle { + TimelineIconStyle(style: style) + } + + static func iconStyle(@ViewBuilder content: @escaping (IconConfiguration) -> some View) -> TimelineIconStyle { + let style = AnyIconStyle(content) + return TimelineIconStyle(style: style) + } +} + +public struct TimelineTitleStyle: TimelineStyle { + let style: any TitleStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .titleStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineTitleStyle { + static func titleStyle(_ style: some TitleStyle) -> TimelineTitleStyle { + TimelineTitleStyle(style: style) + } + + static func titleStyle(@ViewBuilder content: @escaping (TitleConfiguration) -> some View) -> TimelineTitleStyle { + let style = AnyTitleStyle(content) + return TimelineTitleStyle(style: style) + } +} + +public struct TimelineSubtitleStyle: TimelineStyle { + let style: any SubtitleStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .subtitleStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineSubtitleStyle { + static func subtitleStyle(_ style: some SubtitleStyle) -> TimelineSubtitleStyle { + TimelineSubtitleStyle(style: style) + } + + static func subtitleStyle(@ViewBuilder content: @escaping (SubtitleConfiguration) -> some View) -> TimelineSubtitleStyle { + let style = AnySubtitleStyle(content) + return TimelineSubtitleStyle(style: style) + } +} + +public struct TimelineAttributeStyle: TimelineStyle { + let style: any AttributeStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .attributeStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineAttributeStyle { + static func attributeStyle(_ style: some AttributeStyle) -> TimelineAttributeStyle { + TimelineAttributeStyle(style: style) + } + + static func attributeStyle(@ViewBuilder content: @escaping (AttributeConfiguration) -> some View) -> TimelineAttributeStyle { + let style = AnyAttributeStyle(content) + return TimelineAttributeStyle(style: style) + } +} + +public struct TimelineStatusStyle: TimelineStyle { + let style: any StatusStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .statusStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineStatusStyle { + static func statusStyle(_ style: some StatusStyle) -> TimelineStatusStyle { + TimelineStatusStyle(style: style) + } + + static func statusStyle(@ViewBuilder content: @escaping (StatusConfiguration) -> some View) -> TimelineStatusStyle { + let style = AnyStatusStyle(content) + return TimelineStatusStyle(style: style) + } +} + +public struct TimelineSubstatusStyle: TimelineStyle { + let style: any SubstatusStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .substatusStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineSubstatusStyle { + static func substatusStyle(_ style: some SubstatusStyle) -> TimelineSubstatusStyle { + TimelineSubstatusStyle(style: style) + } + + static func substatusStyle(@ViewBuilder content: @escaping (SubstatusConfiguration) -> some View) -> TimelineSubstatusStyle { + let style = AnySubstatusStyle(content) + return TimelineSubstatusStyle(style: style) + } +} + +public struct TimelineSubAttributeStyle: TimelineStyle { + let style: any SubAttributeStyle + + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .subAttributeStyle(self.style) + .typeErased + } +} + +public extension TimelineStyle where Self == TimelineSubAttributeStyle { + static func subAttributeStyle(_ style: some SubAttributeStyle) -> TimelineSubAttributeStyle { + TimelineSubAttributeStyle(style: style) + } + + static func subAttributeStyle(@ViewBuilder content: @escaping (SubAttributeConfiguration) -> some View) -> TimelineSubAttributeStyle { + let style = AnySubAttributeStyle(content) + return TimelineSubAttributeStyle(style: style) + } +} + +// MARK: TimelineMarkerStyle + +public extension TimelineMarkerStyle where Self == TimelineMarkerBaseStyle { + static var base: TimelineMarkerBaseStyle { + TimelineMarkerBaseStyle() + } +} + +public extension TimelineMarkerStyle where Self == TimelineMarkerFioriStyle { + static var fiori: TimelineMarkerFioriStyle { + TimelineMarkerFioriStyle() + } +} + +public struct TimelineMarkerTimestampStyle: TimelineMarkerStyle { + let style: any TimestampStyle + + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .timestampStyle(self.style) + .typeErased + } +} + +public extension TimelineMarkerStyle where Self == TimelineMarkerTimestampStyle { + static func timestampStyle(_ style: some TimestampStyle) -> TimelineMarkerTimestampStyle { + TimelineMarkerTimestampStyle(style: style) + } + + static func timestampStyle(@ViewBuilder content: @escaping (TimestampConfiguration) -> some View) -> TimelineMarkerTimestampStyle { + let style = AnyTimestampStyle(content) + return TimelineMarkerTimestampStyle(style: style) + } +} + +public struct TimelineMarkerSecondaryTimestampStyle: TimelineMarkerStyle { + let style: any SecondaryTimestampStyle + + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .secondaryTimestampStyle(self.style) + .typeErased + } +} + +public extension TimelineMarkerStyle where Self == TimelineMarkerSecondaryTimestampStyle { + static func secondaryTimestampStyle(_ style: some SecondaryTimestampStyle) -> TimelineMarkerSecondaryTimestampStyle { + TimelineMarkerSecondaryTimestampStyle(style: style) + } + + static func secondaryTimestampStyle(@ViewBuilder content: @escaping (SecondaryTimestampConfiguration) -> some View) -> TimelineMarkerSecondaryTimestampStyle { + let style = AnySecondaryTimestampStyle(content) + return TimelineMarkerSecondaryTimestampStyle(style: style) + } +} + +public struct TimelineMarkerTimelineNodeStyle: TimelineMarkerStyle { + let style: any TimelineNodeStyle + + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .timelineNodeStyle(self.style) + .typeErased + } +} + +public extension TimelineMarkerStyle where Self == TimelineMarkerTimelineNodeStyle { + static func timelineNodeStyle(_ style: some TimelineNodeStyle) -> TimelineMarkerTimelineNodeStyle { + TimelineMarkerTimelineNodeStyle(style: style) + } + + static func timelineNodeStyle(@ViewBuilder content: @escaping (TimelineNodeConfiguration) -> some View) -> TimelineMarkerTimelineNodeStyle { + let style = AnyTimelineNodeStyle(content) + return TimelineMarkerTimelineNodeStyle(style: style) + } +} + +public struct TimelineMarkerIconStyle: TimelineMarkerStyle { + let style: any IconStyle + + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .iconStyle(self.style) + .typeErased + } +} + +public extension TimelineMarkerStyle where Self == TimelineMarkerIconStyle { + static func iconStyle(_ style: some IconStyle) -> TimelineMarkerIconStyle { + TimelineMarkerIconStyle(style: style) + } + + static func iconStyle(@ViewBuilder content: @escaping (IconConfiguration) -> some View) -> TimelineMarkerIconStyle { + let style = AnyIconStyle(content) + return TimelineMarkerIconStyle(style: style) + } +} + +public struct TimelineMarkerTitleStyle: TimelineMarkerStyle { + let style: any TitleStyle + + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .titleStyle(self.style) + .typeErased + } +} + +public extension TimelineMarkerStyle where Self == TimelineMarkerTitleStyle { + static func titleStyle(_ style: some TitleStyle) -> TimelineMarkerTitleStyle { + TimelineMarkerTitleStyle(style: style) + } + + static func titleStyle(@ViewBuilder content: @escaping (TitleConfiguration) -> some View) -> TimelineMarkerTitleStyle { + let style = AnyTitleStyle(content) + return TimelineMarkerTitleStyle(style: style) + } +} + +// MARK: TimelineNodeStyle + +public extension TimelineNodeStyle where Self == TimelineNodeBaseStyle { + static var base: TimelineNodeBaseStyle { + TimelineNodeBaseStyle() + } +} + +public extension TimelineNodeStyle where Self == TimelineNodeFioriStyle { + static var fiori: TimelineNodeFioriStyle { + TimelineNodeFioriStyle() + } +} + +// MARK: TimelineNowIndicatorStyle + +public extension TimelineNowIndicatorStyle where Self == TimelineNowIndicatorBaseStyle { + static var base: TimelineNowIndicatorBaseStyle { + TimelineNowIndicatorBaseStyle() + } +} + +public extension TimelineNowIndicatorStyle where Self == TimelineNowIndicatorFioriStyle { + static var fiori: TimelineNowIndicatorFioriStyle { + TimelineNowIndicatorFioriStyle() + } +} + +public struct TimelineNowIndicatorNowIndicatorNodeStyle: TimelineNowIndicatorStyle { + let style: any NowIndicatorNodeStyle + + public func makeBody(_ configuration: TimelineNowIndicatorConfiguration) -> some View { + TimelineNowIndicator(configuration) + .nowIndicatorNodeStyle(self.style) + .typeErased + } +} + +public extension TimelineNowIndicatorStyle where Self == TimelineNowIndicatorNowIndicatorNodeStyle { + static func nowIndicatorNodeStyle(_ style: some NowIndicatorNodeStyle) -> TimelineNowIndicatorNowIndicatorNodeStyle { + TimelineNowIndicatorNowIndicatorNodeStyle(style: style) + } + + static func nowIndicatorNodeStyle(@ViewBuilder content: @escaping (NowIndicatorNodeConfiguration) -> some View) -> TimelineNowIndicatorNowIndicatorNodeStyle { + let style = AnyNowIndicatorNodeStyle(content) + return TimelineNowIndicatorNowIndicatorNodeStyle(style: style) + } +} + +// MARK: TimestampStyle + +public extension TimestampStyle where Self == TimestampBaseStyle { + static var base: TimestampBaseStyle { + TimestampBaseStyle() + } +} + +public extension TimestampStyle where Self == TimestampFioriStyle { + static var fiori: TimestampFioriStyle { + TimestampFioriStyle() + } +} + // MARK: TitleStyle public extension TitleStyle where Self == TitleBaseStyle { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift index 2cb6602c1..7956617c5 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift @@ -45,6 +45,27 @@ extension EnvironmentValues { } } +// MARK: AttributeStyle + +struct AttributeStyleStackKey: EnvironmentKey { + static let defaultValue: [any AttributeStyle] = [] +} + +extension EnvironmentValues { + var attributeStyle: any AttributeStyle { + self.attributeStyleStack.last ?? .base + } + + var attributeStyleStack: [any AttributeStyle] { + get { + self[AttributeStyleStackKey.self] + } + set { + self[AttributeStyleStackKey.self] = newValue + } + } +} + // MARK: AvatarsStyle struct AvatarsStyleStackKey: EnvironmentKey { @@ -906,6 +927,27 @@ extension EnvironmentValues { } } +// MARK: NowIndicatorNodeStyle + +struct NowIndicatorNodeStyleStackKey: EnvironmentKey { + static let defaultValue: [any NowIndicatorNodeStyle] = [] +} + +extension EnvironmentValues { + var nowIndicatorNodeStyle: any NowIndicatorNodeStyle { + self.nowIndicatorNodeStyleStack.last ?? .base + } + + var nowIndicatorNodeStyleStack: [any NowIndicatorNodeStyle] { + get { + self[NowIndicatorNodeStyleStackKey.self] + } + set { + self[NowIndicatorNodeStyleStackKey.self] = newValue + } + } +} + // MARK: ObjectItemStyle struct ObjectItemStyleStackKey: EnvironmentKey { @@ -1158,6 +1200,27 @@ extension EnvironmentValues { } } +// MARK: SecondaryTimestampStyle + +struct SecondaryTimestampStyleStackKey: EnvironmentKey { + static let defaultValue: [any SecondaryTimestampStyle] = [] +} + +extension EnvironmentValues { + var secondaryTimestampStyle: any SecondaryTimestampStyle { + self.secondaryTimestampStyleStack.last ?? .base + } + + var secondaryTimestampStyleStack: [any SecondaryTimestampStyle] { + get { + self[SecondaryTimestampStyleStackKey.self] + } + set { + self[SecondaryTimestampStyleStackKey.self] = newValue + } + } +} + // MARK: SideBarStyle struct SideBarStyleStackKey: EnvironmentKey { @@ -1263,6 +1326,27 @@ extension EnvironmentValues { } } +// MARK: SubAttributeStyle + +struct SubAttributeStyleStackKey: EnvironmentKey { + static let defaultValue: [any SubAttributeStyle] = [] +} + +extension EnvironmentValues { + var subAttributeStyle: any SubAttributeStyle { + self.subAttributeStyleStack.last ?? .base + } + + var subAttributeStyleStack: [any SubAttributeStyle] { + get { + self[SubAttributeStyleStackKey.self] + } + set { + self[SubAttributeStyleStackKey.self] = newValue + } + } +} + // MARK: SubstatusStyle struct SubstatusStyleStackKey: EnvironmentKey { @@ -1452,6 +1536,111 @@ extension EnvironmentValues { } } +// MARK: TimelineStyle + +struct TimelineStyleStackKey: EnvironmentKey { + static let defaultValue: [any TimelineStyle] = [] +} + +extension EnvironmentValues { + var timelineStyle: any TimelineStyle { + self.timelineStyleStack.last ?? .base.concat(.fiori) + } + + var timelineStyleStack: [any TimelineStyle] { + get { + self[TimelineStyleStackKey.self] + } + set { + self[TimelineStyleStackKey.self] = newValue + } + } +} + +// MARK: TimelineMarkerStyle + +struct TimelineMarkerStyleStackKey: EnvironmentKey { + static let defaultValue: [any TimelineMarkerStyle] = [] +} + +extension EnvironmentValues { + var timelineMarkerStyle: any TimelineMarkerStyle { + self.timelineMarkerStyleStack.last ?? .base.concat(.fiori) + } + + var timelineMarkerStyleStack: [any TimelineMarkerStyle] { + get { + self[TimelineMarkerStyleStackKey.self] + } + set { + self[TimelineMarkerStyleStackKey.self] = newValue + } + } +} + +// MARK: TimelineNodeStyle + +struct TimelineNodeStyleStackKey: EnvironmentKey { + static let defaultValue: [any TimelineNodeStyle] = [] +} + +extension EnvironmentValues { + var timelineNodeStyle: any TimelineNodeStyle { + self.timelineNodeStyleStack.last ?? .base + } + + var timelineNodeStyleStack: [any TimelineNodeStyle] { + get { + self[TimelineNodeStyleStackKey.self] + } + set { + self[TimelineNodeStyleStackKey.self] = newValue + } + } +} + +// MARK: TimelineNowIndicatorStyle + +struct TimelineNowIndicatorStyleStackKey: EnvironmentKey { + static let defaultValue: [any TimelineNowIndicatorStyle] = [] +} + +extension EnvironmentValues { + var timelineNowIndicatorStyle: any TimelineNowIndicatorStyle { + self.timelineNowIndicatorStyleStack.last ?? .base.concat(.fiori) + } + + var timelineNowIndicatorStyleStack: [any TimelineNowIndicatorStyle] { + get { + self[TimelineNowIndicatorStyleStackKey.self] + } + set { + self[TimelineNowIndicatorStyleStackKey.self] = newValue + } + } +} + +// MARK: TimestampStyle + +struct TimestampStyleStackKey: EnvironmentKey { + static let defaultValue: [any TimestampStyle] = [] +} + +extension EnvironmentValues { + var timestampStyle: any TimestampStyle { + self.timestampStyleStack.last ?? .base + } + + var timestampStyleStack: [any TimestampStyle] { + get { + self[TimestampStyleStackKey.self] + } + set { + self[TimestampStyleStackKey.self] = newValue + } + } +} + // MARK: TitleStyle struct TitleStyleStackKey: EnvironmentKey { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift index 10a499961..f674beef1 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift @@ -64,6 +64,34 @@ public extension ActionStyle { } } +// MARK: AttributeStyle + +extension ModifiedStyle: AttributeStyle where Style: AttributeStyle { + public func makeBody(_ configuration: AttributeConfiguration) -> some View { + Attribute(configuration) + .attributeStyle(self.style) + .modifier(self.modifier) + } +} + +public struct AttributeStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.attributeStyle(self.style) + } +} + +public extension AttributeStyle { + func modifier(_ modifier: some ViewModifier) -> some AttributeStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some AttributeStyle) -> some AttributeStyle { + style.modifier(AttributeStyleModifier(style: self)) + } +} + // MARK: AvatarsStyle extension ModifiedStyle: AvatarsStyle where Style: AvatarsStyle { @@ -1212,6 +1240,34 @@ public extension NoteFormViewStyle { } } +// MARK: NowIndicatorNodeStyle + +extension ModifiedStyle: NowIndicatorNodeStyle where Style: NowIndicatorNodeStyle { + public func makeBody(_ configuration: NowIndicatorNodeConfiguration) -> some View { + NowIndicatorNode(configuration) + .nowIndicatorNodeStyle(self.style) + .modifier(self.modifier) + } +} + +public struct NowIndicatorNodeStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.nowIndicatorNodeStyle(self.style) + } +} + +public extension NowIndicatorNodeStyle { + func modifier(_ modifier: some ViewModifier) -> some NowIndicatorNodeStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some NowIndicatorNodeStyle) -> some NowIndicatorNodeStyle { + style.modifier(NowIndicatorNodeStyleModifier(style: self)) + } +} + // MARK: ObjectItemStyle extension ModifiedStyle: ObjectItemStyle where Style: ObjectItemStyle { @@ -1548,6 +1604,34 @@ public extension SecondaryActionStyle { } } +// MARK: SecondaryTimestampStyle + +extension ModifiedStyle: SecondaryTimestampStyle where Style: SecondaryTimestampStyle { + public func makeBody(_ configuration: SecondaryTimestampConfiguration) -> some View { + SecondaryTimestamp(configuration) + .secondaryTimestampStyle(self.style) + .modifier(self.modifier) + } +} + +public struct SecondaryTimestampStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.secondaryTimestampStyle(self.style) + } +} + +public extension SecondaryTimestampStyle { + func modifier(_ modifier: some ViewModifier) -> some SecondaryTimestampStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some SecondaryTimestampStyle) -> some SecondaryTimestampStyle { + style.modifier(SecondaryTimestampStyleModifier(style: self)) + } +} + // MARK: SideBarStyle extension ModifiedStyle: SideBarStyle where Style: SideBarStyle { @@ -1688,6 +1772,34 @@ public extension StepperViewStyle { } } +// MARK: SubAttributeStyle + +extension ModifiedStyle: SubAttributeStyle where Style: SubAttributeStyle { + public func makeBody(_ configuration: SubAttributeConfiguration) -> some View { + SubAttribute(configuration) + .subAttributeStyle(self.style) + .modifier(self.modifier) + } +} + +public struct SubAttributeStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.subAttributeStyle(self.style) + } +} + +public extension SubAttributeStyle { + func modifier(_ modifier: some ViewModifier) -> some SubAttributeStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some SubAttributeStyle) -> some SubAttributeStyle { + style.modifier(SubAttributeStyleModifier(style: self)) + } +} + // MARK: SubstatusStyle extension ModifiedStyle: SubstatusStyle where Style: SubstatusStyle { @@ -1940,6 +2052,146 @@ public extension TextViewStyle { } } +// MARK: TimelineStyle + +extension ModifiedStyle: TimelineStyle where Style: TimelineStyle { + public func makeBody(_ configuration: TimelineConfiguration) -> some View { + Timeline(configuration) + .timelineStyle(self.style) + .modifier(self.modifier) + } +} + +public struct TimelineStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.timelineStyle(self.style) + } +} + +public extension TimelineStyle { + func modifier(_ modifier: some ViewModifier) -> some TimelineStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some TimelineStyle) -> some TimelineStyle { + style.modifier(TimelineStyleModifier(style: self)) + } +} + +// MARK: TimelineMarkerStyle + +extension ModifiedStyle: TimelineMarkerStyle where Style: TimelineMarkerStyle { + public func makeBody(_ configuration: TimelineMarkerConfiguration) -> some View { + TimelineMarker(configuration) + .timelineMarkerStyle(self.style) + .modifier(self.modifier) + } +} + +public struct TimelineMarkerStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.timelineMarkerStyle(self.style) + } +} + +public extension TimelineMarkerStyle { + func modifier(_ modifier: some ViewModifier) -> some TimelineMarkerStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some TimelineMarkerStyle) -> some TimelineMarkerStyle { + style.modifier(TimelineMarkerStyleModifier(style: self)) + } +} + +// MARK: TimelineNodeStyle + +extension ModifiedStyle: TimelineNodeStyle where Style: TimelineNodeStyle { + public func makeBody(_ configuration: TimelineNodeConfiguration) -> some View { + TimelineNode(configuration) + .timelineNodeStyle(self.style) + .modifier(self.modifier) + } +} + +public struct TimelineNodeStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.timelineNodeStyle(self.style) + } +} + +public extension TimelineNodeStyle { + func modifier(_ modifier: some ViewModifier) -> some TimelineNodeStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some TimelineNodeStyle) -> some TimelineNodeStyle { + style.modifier(TimelineNodeStyleModifier(style: self)) + } +} + +// MARK: TimelineNowIndicatorStyle + +extension ModifiedStyle: TimelineNowIndicatorStyle where Style: TimelineNowIndicatorStyle { + public func makeBody(_ configuration: TimelineNowIndicatorConfiguration) -> some View { + TimelineNowIndicator(configuration) + .timelineNowIndicatorStyle(self.style) + .modifier(self.modifier) + } +} + +public struct TimelineNowIndicatorStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.timelineNowIndicatorStyle(self.style) + } +} + +public extension TimelineNowIndicatorStyle { + func modifier(_ modifier: some ViewModifier) -> some TimelineNowIndicatorStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some TimelineNowIndicatorStyle) -> some TimelineNowIndicatorStyle { + style.modifier(TimelineNowIndicatorStyleModifier(style: self)) + } +} + +// MARK: TimestampStyle + +extension ModifiedStyle: TimestampStyle where Style: TimestampStyle { + public func makeBody(_ configuration: TimestampConfiguration) -> some View { + Timestamp(configuration) + .timestampStyle(self.style) + .modifier(self.modifier) + } +} + +public struct TimestampStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.timestampStyle(self.style) + } +} + +public extension TimestampStyle { + func modifier(_ modifier: some ViewModifier) -> some TimestampStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some TimestampStyle) -> some TimestampStyle { + style.modifier(TimestampStyleModifier(style: self)) + } +} + // MARK: TitleStyle extension ModifiedStyle: TitleStyle where Style: TitleStyle { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift index 5ee4b309c..b6ad5de7b 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift @@ -35,6 +35,22 @@ extension ActionStyle { } } +// MARK: AttributeStyle + +struct ResolvedAttributeStyle: View { + let style: Style + let configuration: AttributeConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension AttributeStyle { + func resolve(configuration: AttributeConfiguration) -> some View { + ResolvedAttributeStyle(style: self, configuration: configuration) + } +} + // MARK: AvatarsStyle struct ResolvedAvatarsStyle: View { @@ -691,6 +707,22 @@ extension NoteFormViewStyle { } } +// MARK: NowIndicatorNodeStyle + +struct ResolvedNowIndicatorNodeStyle: View { + let style: Style + let configuration: NowIndicatorNodeConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension NowIndicatorNodeStyle { + func resolve(configuration: NowIndicatorNodeConfiguration) -> some View { + ResolvedNowIndicatorNodeStyle(style: self, configuration: configuration) + } +} + // MARK: ObjectItemStyle struct ResolvedObjectItemStyle: View { @@ -883,6 +915,22 @@ extension SecondaryActionStyle { } } +// MARK: SecondaryTimestampStyle + +struct ResolvedSecondaryTimestampStyle: View { + let style: Style + let configuration: SecondaryTimestampConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension SecondaryTimestampStyle { + func resolve(configuration: SecondaryTimestampConfiguration) -> some View { + ResolvedSecondaryTimestampStyle(style: self, configuration: configuration) + } +} + // MARK: SideBarStyle struct ResolvedSideBarStyle: View { @@ -963,6 +1011,22 @@ extension StepperViewStyle { } } +// MARK: SubAttributeStyle + +struct ResolvedSubAttributeStyle: View { + let style: Style + let configuration: SubAttributeConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension SubAttributeStyle { + func resolve(configuration: SubAttributeConfiguration) -> some View { + ResolvedSubAttributeStyle(style: self, configuration: configuration) + } +} + // MARK: SubstatusStyle struct ResolvedSubstatusStyle: View { @@ -1107,6 +1171,86 @@ extension TextViewStyle { } } +// MARK: TimelineStyle + +struct ResolvedTimelineStyle: View { + let style: Style + let configuration: TimelineConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension TimelineStyle { + func resolve(configuration: TimelineConfiguration) -> some View { + ResolvedTimelineStyle(style: self, configuration: configuration) + } +} + +// MARK: TimelineMarkerStyle + +struct ResolvedTimelineMarkerStyle: View { + let style: Style + let configuration: TimelineMarkerConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension TimelineMarkerStyle { + func resolve(configuration: TimelineMarkerConfiguration) -> some View { + ResolvedTimelineMarkerStyle(style: self, configuration: configuration) + } +} + +// MARK: TimelineNodeStyle + +struct ResolvedTimelineNodeStyle: View { + let style: Style + let configuration: TimelineNodeConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension TimelineNodeStyle { + func resolve(configuration: TimelineNodeConfiguration) -> some View { + ResolvedTimelineNodeStyle(style: self, configuration: configuration) + } +} + +// MARK: TimelineNowIndicatorStyle + +struct ResolvedTimelineNowIndicatorStyle: View { + let style: Style + let configuration: TimelineNowIndicatorConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension TimelineNowIndicatorStyle { + func resolve(configuration: TimelineNowIndicatorConfiguration) -> some View { + ResolvedTimelineNowIndicatorStyle(style: self, configuration: configuration) + } +} + +// MARK: TimestampStyle + +struct ResolvedTimestampStyle: View { + let style: Style + let configuration: TimestampConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension TimestampStyle { + func resolve(configuration: TimestampConfiguration) -> some View { + ResolvedTimestampStyle(style: self, configuration: configuration) + } +} + // MARK: TitleStyle struct ResolvedTitleStyle: View { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift index 8323cb032..d4cec10f9 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift @@ -37,6 +37,23 @@ public extension View { } } +// MARK: AttributeStyle + +public extension View { + func attributeStyle(_ style: some AttributeStyle) -> some View { + self.transformEnvironment(\.attributeStyleStack) { stack in + stack.append(style) + } + } + + func attributeStyle(@ViewBuilder content: @escaping (AttributeConfiguration) -> some View) -> some View { + self.transformEnvironment(\.attributeStyleStack) { stack in + let style = AnyAttributeStyle(content) + stack.append(style) + } + } +} + // MARK: AvatarsStyle public extension View { @@ -734,6 +751,23 @@ public extension View { } } +// MARK: NowIndicatorNodeStyle + +public extension View { + func nowIndicatorNodeStyle(_ style: some NowIndicatorNodeStyle) -> some View { + self.transformEnvironment(\.nowIndicatorNodeStyleStack) { stack in + stack.append(style) + } + } + + func nowIndicatorNodeStyle(@ViewBuilder content: @escaping (NowIndicatorNodeConfiguration) -> some View) -> some View { + self.transformEnvironment(\.nowIndicatorNodeStyleStack) { stack in + let style = AnyNowIndicatorNodeStyle(content) + stack.append(style) + } + } +} + // MARK: ObjectItemStyle public extension View { @@ -938,6 +972,23 @@ public extension View { } } +// MARK: SecondaryTimestampStyle + +public extension View { + func secondaryTimestampStyle(_ style: some SecondaryTimestampStyle) -> some View { + self.transformEnvironment(\.secondaryTimestampStyleStack) { stack in + stack.append(style) + } + } + + func secondaryTimestampStyle(@ViewBuilder content: @escaping (SecondaryTimestampConfiguration) -> some View) -> some View { + self.transformEnvironment(\.secondaryTimestampStyleStack) { stack in + let style = AnySecondaryTimestampStyle(content) + stack.append(style) + } + } +} + // MARK: SideBarStyle public extension View { @@ -1023,6 +1074,23 @@ public extension View { } } +// MARK: SubAttributeStyle + +public extension View { + func subAttributeStyle(_ style: some SubAttributeStyle) -> some View { + self.transformEnvironment(\.subAttributeStyleStack) { stack in + stack.append(style) + } + } + + func subAttributeStyle(@ViewBuilder content: @escaping (SubAttributeConfiguration) -> some View) -> some View { + self.transformEnvironment(\.subAttributeStyleStack) { stack in + let style = AnySubAttributeStyle(content) + stack.append(style) + } + } +} + // MARK: SubstatusStyle public extension View { @@ -1176,6 +1244,91 @@ public extension View { } } +// MARK: TimelineStyle + +public extension View { + func timelineStyle(_ style: some TimelineStyle) -> some View { + self.transformEnvironment(\.timelineStyleStack) { stack in + stack.append(style) + } + } + + func timelineStyle(@ViewBuilder content: @escaping (TimelineConfiguration) -> some View) -> some View { + self.transformEnvironment(\.timelineStyleStack) { stack in + let style = AnyTimelineStyle(content) + stack.append(style) + } + } +} + +// MARK: TimelineMarkerStyle + +public extension View { + func timelineMarkerStyle(_ style: some TimelineMarkerStyle) -> some View { + self.transformEnvironment(\.timelineMarkerStyleStack) { stack in + stack.append(style) + } + } + + func timelineMarkerStyle(@ViewBuilder content: @escaping (TimelineMarkerConfiguration) -> some View) -> some View { + self.transformEnvironment(\.timelineMarkerStyleStack) { stack in + let style = AnyTimelineMarkerStyle(content) + stack.append(style) + } + } +} + +// MARK: TimelineNodeStyle + +public extension View { + func timelineNodeStyle(_ style: some TimelineNodeStyle) -> some View { + self.transformEnvironment(\.timelineNodeStyleStack) { stack in + stack.append(style) + } + } + + func timelineNodeStyle(@ViewBuilder content: @escaping (TimelineNodeConfiguration) -> some View) -> some View { + self.transformEnvironment(\.timelineNodeStyleStack) { stack in + let style = AnyTimelineNodeStyle(content) + stack.append(style) + } + } +} + +// MARK: TimelineNowIndicatorStyle + +public extension View { + func timelineNowIndicatorStyle(_ style: some TimelineNowIndicatorStyle) -> some View { + self.transformEnvironment(\.timelineNowIndicatorStyleStack) { stack in + stack.append(style) + } + } + + func timelineNowIndicatorStyle(@ViewBuilder content: @escaping (TimelineNowIndicatorConfiguration) -> some View) -> some View { + self.transformEnvironment(\.timelineNowIndicatorStyleStack) { stack in + let style = AnyTimelineNowIndicatorStyle(content) + stack.append(style) + } + } +} + +// MARK: TimestampStyle + +public extension View { + func timestampStyle(_ style: some TimestampStyle) -> some View { + self.transformEnvironment(\.timestampStyleStack) { stack in + stack.append(style) + } + } + + func timestampStyle(@ViewBuilder content: @escaping (TimestampConfiguration) -> some View) -> some View { + self.transformEnvironment(\.timestampStyleStack) { stack in + let style = AnyTimestampStyle(content) + stack.append(style) + } + } +} + // MARK: TitleStyle public extension View { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift index b2aa03e1e..7a21cbc0a 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift @@ -15,6 +15,12 @@ extension Action: _ViewEmptyChecking { } } +extension Attribute: _ViewEmptyChecking { + public var isEmpty: Bool { + attribute.isEmpty + } +} + extension Avatars: _ViewEmptyChecking { public var isEmpty: Bool { avatars.isEmpty @@ -322,6 +328,12 @@ extension NoteFormView: _ViewEmptyChecking { } } +extension NowIndicatorNode: _ViewEmptyChecking { + public var isEmpty: Bool { + nowIndicatorNode.isEmpty + } +} + extension ObjectItem: _ViewEmptyChecking { public var isEmpty: Bool { title.isEmpty && @@ -410,6 +422,12 @@ extension SecondaryAction: _ViewEmptyChecking { } } +extension SecondaryTimestamp: _ViewEmptyChecking { + public var isEmpty: Bool { + secondaryTimestamp.isEmpty + } +} + extension SideBar: _ViewEmptyChecking { public var isEmpty: Bool { footer.isEmpty && @@ -450,6 +468,12 @@ extension StepperView: _ViewEmptyChecking { } } +extension SubAttribute: _ViewEmptyChecking { + public var isEmpty: Bool { + subAttribute.isEmpty + } +} + extension Substatus: _ViewEmptyChecking { public var isEmpty: Bool { substatus.isEmpty @@ -508,6 +532,49 @@ extension TextView: _ViewEmptyChecking { } } +extension Timeline: _ViewEmptyChecking { + public var isEmpty: Bool { + timestamp.isEmpty && + secondaryTimestamp.isEmpty && + timelineNode.isEmpty && + icon.isEmpty && + title.isEmpty && + subtitle.isEmpty && + attribute.isEmpty && + status.isEmpty && + substatus.isEmpty && + subAttribute.isEmpty + } +} + +extension TimelineMarker: _ViewEmptyChecking { + public var isEmpty: Bool { + timestamp.isEmpty && + secondaryTimestamp.isEmpty && + timelineNode.isEmpty && + icon.isEmpty && + title.isEmpty + } +} + +extension TimelineNode: _ViewEmptyChecking { + public var isEmpty: Bool { + timelineNode.isEmpty + } +} + +extension TimelineNowIndicator: _ViewEmptyChecking { + public var isEmpty: Bool { + nowIndicatorNode.isEmpty + } +} + +extension Timestamp: _ViewEmptyChecking { + public var isEmpty: Bool { + timestamp.isEmpty + } +} + extension Title: _ViewEmptyChecking { public var isEmpty: Bool { title.isEmpty