From 309785b96918a1ad955efd1528c7dda833e75155 Mon Sep 17 00:00:00 2001 From: Carson Katri Date: Mon, 13 Jul 2020 18:49:30 -0400 Subject: [PATCH 1/2] Add LazyHGrid and LazyVGrid --- Sources/TokamakCore/Tokens/GridItem.swift | 36 +++++++++++ Sources/TokamakCore/Views/LazyHGrid.swift | 50 +++++++++++++++ Sources/TokamakCore/Views/LazyVGrid.swift | 50 +++++++++++++++ Sources/TokamakCore/Views/ScrollView.swift | 10 +++ Sources/TokamakDOM/Tokens/Tokens.swift | 19 ++++++ Sources/TokamakDOM/Views/LazyHGrid.swift | 63 +++++++++++++++++++ Sources/TokamakDOM/Views/LazyVGrid.swift | 63 +++++++++++++++++++ Sources/TokamakDOM/Views/SecureField.swift | 8 +-- Sources/TokamakDOM/Views/Spacer.swift | 5 +- Sources/TokamakDOM/Views/TextField.swift | 8 +-- Sources/TokamakDemo/GridDemo.swift | 56 +++++++++++++++++ Sources/TokamakDemo/TokamakDemo.swift | 39 +++++++----- .../project.pbxproj | 6 ++ 13 files changed, 387 insertions(+), 26 deletions(-) create mode 100644 Sources/TokamakCore/Tokens/GridItem.swift create mode 100644 Sources/TokamakCore/Views/LazyHGrid.swift create mode 100644 Sources/TokamakCore/Views/LazyVGrid.swift create mode 100644 Sources/TokamakDOM/Views/LazyHGrid.swift create mode 100644 Sources/TokamakDOM/Views/LazyVGrid.swift create mode 100644 Sources/TokamakDemo/GridDemo.swift diff --git a/Sources/TokamakCore/Tokens/GridItem.swift b/Sources/TokamakCore/Tokens/GridItem.swift new file mode 100644 index 000000000..e3b3eea62 --- /dev/null +++ b/Sources/TokamakCore/Tokens/GridItem.swift @@ -0,0 +1,36 @@ +// Copyright 2019-2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Created by Carson Katri on 7/13/20. +// + +public struct GridItem { + public enum Size { + case fixed(CGFloat) + case flexible(minimum: CGFloat = 10, maximum: CGFloat = .infinity) + case adaptive(minimum: CGFloat, maximum: CGFloat = .infinity) + } + + public var size: GridItem.Size + public var spacing: CGFloat + public var alignment: Alignment + + public init(_ size: GridItem.Size = .flexible(), + spacing: CGFloat? = nil, + alignment: Alignment? = nil) { + self.size = size + self.spacing = spacing ?? 4 + self.alignment = alignment ?? .center + } +} diff --git a/Sources/TokamakCore/Views/LazyHGrid.swift b/Sources/TokamakCore/Views/LazyHGrid.swift new file mode 100644 index 000000000..e954bc42b --- /dev/null +++ b/Sources/TokamakCore/Views/LazyHGrid.swift @@ -0,0 +1,50 @@ +// Copyright 2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Created by Carson Katri on 7/13/20. +// + +public struct LazyHGrid: View where Content: View { + let rows: [GridItem] + let alignment: VerticalAlignment + let spacing: CGFloat + let pinnedViews: PinnedScrollableViews + let content: Content + + public init(rows: [GridItem], + alignment: VerticalAlignment = .center, + spacing: CGFloat? = nil, + pinnedViews: PinnedScrollableViews = .init(), + @ViewBuilder content: () -> Content) { + self.rows = rows + self.alignment = alignment + self.spacing = spacing ?? 8 + self.pinnedViews = pinnedViews + self.content = content() + } + + public var body: Never { + neverBody("LazyVGrid") + } +} + +public struct _LazyHGridProxy where Content: View { + public let subject: LazyHGrid + + public init(_ subject: LazyHGrid) { self.subject = subject } + + public var rows: [GridItem] { subject.rows } + public var content: Content { subject.content } + public var spacing: CGFloat { subject.spacing } +} diff --git a/Sources/TokamakCore/Views/LazyVGrid.swift b/Sources/TokamakCore/Views/LazyVGrid.swift new file mode 100644 index 000000000..b1534f6a5 --- /dev/null +++ b/Sources/TokamakCore/Views/LazyVGrid.swift @@ -0,0 +1,50 @@ +// Copyright 2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Created by Carson Katri on 7/13/20. +// + +public struct LazyVGrid: View where Content: View { + let columns: [GridItem] + let alignment: HorizontalAlignment + let spacing: CGFloat + let pinnedViews: PinnedScrollableViews + let content: Content + + public init(columns: [GridItem], + alignment: HorizontalAlignment = .center, + spacing: CGFloat? = nil, + pinnedViews: PinnedScrollableViews = .init(), + @ViewBuilder content: () -> Content) { + self.columns = columns + self.alignment = alignment + self.spacing = spacing ?? 8 + self.pinnedViews = pinnedViews + self.content = content() + } + + public var body: Never { + neverBody("LazyVGrid") + } +} + +public struct _LazyVGridProxy where Content: View { + public let subject: LazyVGrid + + public init(_ subject: LazyVGrid) { self.subject = subject } + + public var columns: [GridItem] { subject.columns } + public var content: Content { subject.content } + public var spacing: CGFloat { subject.spacing } +} diff --git a/Sources/TokamakCore/Views/ScrollView.swift b/Sources/TokamakCore/Views/ScrollView.swift index 0551b8314..98348e2c9 100644 --- a/Sources/TokamakCore/Views/ScrollView.swift +++ b/Sources/TokamakCore/Views/ScrollView.swift @@ -58,3 +58,13 @@ extension ScrollView: ParentView { (content as? GroupView)?.children ?? [AnyView(content)] } } + +public struct PinnedScrollableViews: OptionSet { + public let rawValue: UInt32 + public init(rawValue: UInt32) { + self.rawValue = rawValue + } + + public static let sectionHeaders: Self = .init(rawValue: 1 << 0) + public static let sectionFooters: Self = .init(rawValue: 1 << 1) +} diff --git a/Sources/TokamakDOM/Tokens/Tokens.swift b/Sources/TokamakDOM/Tokens/Tokens.swift index 69ca84423..e605fc510 100644 --- a/Sources/TokamakDOM/Tokens/Tokens.swift +++ b/Sources/TokamakDOM/Tokens/Tokens.swift @@ -27,3 +27,22 @@ public typealias CGRect = TokamakCore.CGRect public typealias CGPoint = TokamakCore.CGPoint public typealias CGSize = TokamakCore.CGSize public typealias CGAffineTransform = TokamakCore.CGAffineTransform + +public typealias GridItem = TokamakCore.GridItem + +extension GridItem: CustomStringConvertible { + public var description: String { + switch size { + case let .adaptive(minimum: min, maximum: max): + let min = min == .infinity ? "1fr" : "\(min)px" + let max = max == .infinity ? "1fr" : "\(max)px" + return "repeat(auto-fill, minmax(\(min), \(max)))" + case let .fixed(size): + return "\(size)px" + case let .flexible(minimum: min, maximum: max): + let min = min == .infinity ? "1fr" : min.description + let max = max == .infinity ? "1fr" : max.description + return "minmax(\(min), \(max))" + } + } +} diff --git a/Sources/TokamakDOM/Views/LazyHGrid.swift b/Sources/TokamakDOM/Views/LazyHGrid.swift new file mode 100644 index 000000000..ebd749595 --- /dev/null +++ b/Sources/TokamakDOM/Views/LazyHGrid.swift @@ -0,0 +1,63 @@ +// Copyright 2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Created by Carson Katri on 7/13/20. +// + +import TokamakCore + +public typealias LazyHGrid = TokamakCore.LazyHGrid + +extension LazyHGrid: SpacerContainer { + var axis: SpacerContainerAxis { .horizontal } + var hasSpacer: Bool { false } + var fillCrossAxis: Bool { + _LazyHGridProxy(self).rows.contains { + if case .adaptive(minimum: _, maximum: _) = $0.size { + return true + } else { + return false + } + } + } +} + +extension LazyHGrid: ViewDeferredToRenderer { + var lastRow: GridItem? { + _LazyHGridProxy(self).rows.last + } + + public var deferredBody: AnyView { + var styles = """ + display: grid; + grid-template-rows: \(_LazyHGridProxy(self) + .rows + .map(\.description) + .joined(separator: " ")); + grid-auto-flow: column; + """ + if fillCrossAxis { + styles += "height: 100%;" + } + // CSS Grid doesn't let these be specified for specific rows + if let lastRow = lastRow { + styles += "justify-items: \(lastRow.alignment.horizontal.cssValue);" + styles += "align-items: \(lastRow.alignment.vertical.cssValue);" + } + styles += "grid-gap: \(_LazyHGridProxy(self).spacing)px;" + return AnyView(HTML("div", ["style": styles]) { + _LazyHGridProxy(self).content + }) + } +} diff --git a/Sources/TokamakDOM/Views/LazyVGrid.swift b/Sources/TokamakDOM/Views/LazyVGrid.swift new file mode 100644 index 000000000..b9be6c568 --- /dev/null +++ b/Sources/TokamakDOM/Views/LazyVGrid.swift @@ -0,0 +1,63 @@ +// Copyright 2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Created by Carson Katri on 7/13/20. +// + +import TokamakCore + +public typealias LazyVGrid = TokamakCore.LazyVGrid + +extension LazyVGrid: SpacerContainer { + var axis: SpacerContainerAxis { .vertical } + var hasSpacer: Bool { false } + var fillCrossAxis: Bool { + _LazyVGridProxy(self).columns.contains { + if case .adaptive(minimum: _, maximum: _) = $0.size { + return true + } else { + return false + } + } + } +} + +extension LazyVGrid: ViewDeferredToRenderer { + var lastColumn: GridItem? { + _LazyVGridProxy(self).columns.last + } + + public var deferredBody: AnyView { + var styles = """ + display: grid; + grid-template-columns: \(_LazyVGridProxy(self) + .columns + .map(\.description) + .joined(separator: " ")); + grid-auto-flow: row; + """ + if fillCrossAxis { + styles += "width: 100%;" + } + // CSS Grid doesn't let these be specified for specific columns + if let lastCol = lastColumn { + styles += "justify-items: \(lastCol.alignment.horizontal.cssValue);" + styles += "align-items: \(lastCol.alignment.vertical.cssValue);" + } + styles += "grid-gap: \(_LazyVGridProxy(self).spacing)px;" + return AnyView(HTML("div", ["style": styles]) { + _LazyVGridProxy(self).content + }) + } +} diff --git a/Sources/TokamakDOM/Views/SecureField.swift b/Sources/TokamakDOM/Views/SecureField.swift index d804f179a..084b39da0 100644 --- a/Sources/TokamakDOM/Views/SecureField.swift +++ b/Sources/TokamakDOM/Views/SecureField.swift @@ -29,10 +29,10 @@ extension SecureField: ViewDeferredToRenderer where Label == Text { ], listeners: [ "keypress": { event in if event.key == "Enter" { proxy.onCommit() } }, "input": { event in - if let newValue = event.target.object?.value.string { - proxy.textBinding.wrappedValue = newValue - } - }, + if let newValue = event.target.object?.value.string { + proxy.textBinding.wrappedValue = newValue + } + }, ])) } } diff --git a/Sources/TokamakDOM/Views/Spacer.swift b/Sources/TokamakDOM/Views/Spacer.swift index 87cb7a44e..c2397127a 100644 --- a/Sources/TokamakDOM/Views/Spacer.swift +++ b/Sources/TokamakDOM/Views/Spacer.swift @@ -20,12 +20,13 @@ enum SpacerContainerAxis { case horizontal, vertical } -protocol SpacerContainer: ParentView { +protocol SpacerContainer { var hasSpacer: Bool { get } var axis: SpacerContainerAxis { get } + var fillCrossAxis: Bool { get } } -extension SpacerContainer { +extension SpacerContainer where Self: ParentView { var hasSpacer: Bool { children .compactMap { diff --git a/Sources/TokamakDOM/Views/TextField.swift b/Sources/TokamakDOM/Views/TextField.swift index 199e55962..870461902 100644 --- a/Sources/TokamakDOM/Views/TextField.swift +++ b/Sources/TokamakDOM/Views/TextField.swift @@ -44,10 +44,10 @@ extension TextField: ViewDeferredToRenderer where Label == Text { "blur": { _ in proxy.onEditingChanged(false) }, "keypress": { event in if event.key == "Enter" { proxy.onCommit() } }, "input": { event in - if let newValue = event.target.object?.value.string { - proxy.textBinding.wrappedValue = newValue - } - }, + if let newValue = event.target.object?.value.string { + proxy.textBinding.wrappedValue = newValue + } + }, ])) } } diff --git a/Sources/TokamakDemo/GridDemo.swift b/Sources/TokamakDemo/GridDemo.swift new file mode 100644 index 000000000..b15e595f7 --- /dev/null +++ b/Sources/TokamakDemo/GridDemo.swift @@ -0,0 +1,56 @@ +// Copyright 2019-2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Created by Carson Katri on 7/13/20. +// + +#if canImport(SwiftUI) +import SwiftUI +#else +import TokamakDOM +#endif + +@available(OSX 10.16, *) +public struct GridDemo: View { + public var body: some View { + Group { + VStack { + Text("Adaptive LazyVGrid") + LazyVGrid(columns: [ + GridItem(.adaptive(minimum: 50)), + ]) { + ForEach(0..<50) { + Text("\($0 + 1)") + .padding() + .background(Color.red) + } + } + } + VStack { + Text("Simple LazyHGrid") + LazyHGrid(rows: [ + GridItem(.fixed(50)), + GridItem(.fixed(50)), + GridItem(.fixed(50)), + ]) { + ForEach(0..<45) { + Text("\($0 + 1)") + .padding() + .background(Color.blue) + } + } + } + } + } +} diff --git a/Sources/TokamakDemo/TokamakDemo.swift b/Sources/TokamakDemo/TokamakDemo.swift index 6f4ab657a..954a7ff4a 100644 --- a/Sources/TokamakDemo/TokamakDemo.swift +++ b/Sources/TokamakDemo/TokamakDemo.swift @@ -41,23 +41,30 @@ struct TokamakDemoView: View { } .padding(20) } - ForEachDemo() - TextDemo() - PathDemo() - TextFieldDemo() - SpacerDemo() - EnvironmentDemo() - .font(.system(size: 8)) - #if canImport(TokamakDOM) - ListDemo().listStyle(InsetGroupedListStyle()) - #else - ListDemo() - #endif - if #available(OSX 10.16, *) { - OutlineGroupDemo() + Group { + ForEachDemo() + TextDemo() + PathDemo() + TextFieldDemo() + SpacerDemo() + EnvironmentDemo() + .font(.system(size: 8)) + } + Group { + #if canImport(TokamakDOM) + ListDemo().listStyle(InsetGroupedListStyle()) + #else + ListDemo() + #endif + if #available(OSX 10.16, *) { + OutlineGroupDemo() + } + ColorDemo() + .padding() + if #available(OSX 10.16, *) { + GridDemo() + } } - ColorDemo() - .padding() } } } diff --git a/TokamakDemo Native/TokamakDemo Native.xcodeproj/project.pbxproj b/TokamakDemo Native/TokamakDemo Native.xcodeproj/project.pbxproj index 5e5aa855e..97c25303b 100644 --- a/TokamakDemo Native/TokamakDemo Native.xcodeproj/project.pbxproj +++ b/TokamakDemo Native/TokamakDemo Native.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ B51F215124B920B400CF2583 /* PathDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F214F24B920B400CF2583 /* PathDemo.swift */; }; B56F22E024BC89FD001738DF /* ColorDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56F22DF24BC89FD001738DF /* ColorDemo.swift */; }; B56F22E124BC89FD001738DF /* ColorDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56F22DF24BC89FD001738DF /* ColorDemo.swift */; }; + B56F22E324BD1C26001738DF /* GridDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56F22E224BD1C26001738DF /* GridDemo.swift */; }; + B56F22E424BD1C26001738DF /* GridDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56F22E224BD1C26001738DF /* GridDemo.swift */; }; D1B4229024B3B9BB00682F74 /* ListDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228E24B3B9BB00682F74 /* ListDemo.swift */; }; D1B4229124B3B9BB00682F74 /* ListDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228E24B3B9BB00682F74 /* ListDemo.swift */; }; D1B4229224B3B9BB00682F74 /* OutlineGroupDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228F24B3B9BB00682F74 /* OutlineGroupDemo.swift */; }; @@ -53,6 +55,7 @@ 85ED18BF24AD464B0085DFA0 /* iOS Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOS Info.plist"; sourceTree = ""; }; B51F214F24B920B400CF2583 /* PathDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PathDemo.swift; sourceTree = ""; }; B56F22DF24BC89FD001738DF /* ColorDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ColorDemo.swift; sourceTree = ""; tabWidth = 2; }; + B56F22E224BD1C26001738DF /* GridDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridDemo.swift; sourceTree = ""; }; D1B4228E24B3B9BB00682F74 /* ListDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListDemo.swift; sourceTree = ""; }; D1B4228F24B3B9BB00682F74 /* OutlineGroupDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutlineGroupDemo.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -112,6 +115,7 @@ 85ED18A024AD425E0085DFA0 /* EnvironmentDemo.swift */, B51F214F24B920B400CF2583 /* PathDemo.swift */, B56F22DF24BC89FD001738DF /* ColorDemo.swift */, + B56F22E224BD1C26001738DF /* GridDemo.swift */, ); name = TokamakDemo; path = ../Sources/TokamakDemo; @@ -217,6 +221,7 @@ buildActionMask = 2147483647; files = ( 85ED186A24AD38F20085DFA0 /* UIAppDelegate.swift in Sources */, + B56F22E324BD1C26001738DF /* GridDemo.swift in Sources */, D1B4229224B3B9BB00682F74 /* OutlineGroupDemo.swift in Sources */, B56F22E024BC89FD001738DF /* ColorDemo.swift in Sources */, B51F215024B920B400CF2583 /* PathDemo.swift in Sources */, @@ -236,6 +241,7 @@ buildActionMask = 2147483647; files = ( 85ED18AA24AD425E0085DFA0 /* TokamakDemo.swift in Sources */, + B56F22E424BD1C26001738DF /* GridDemo.swift in Sources */, D1B4229324B3B9BB00682F74 /* OutlineGroupDemo.swift in Sources */, B56F22E124BC89FD001738DF /* ColorDemo.swift in Sources */, B51F215124B920B400CF2583 /* PathDemo.swift in Sources */, From 882c8f32f4dc33c4ac45ad46256475d6cde4e53c Mon Sep 17 00:00:00 2001 From: Carson Katri Date: Tue, 14 Jul 2020 11:31:24 -0400 Subject: [PATCH 2/2] iOS support & progress doc --- Sources/TokamakDemo/GridDemo.swift | 2 +- Sources/TokamakDemo/TokamakDemo.swift | 4 ++-- docs/progress.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/TokamakDemo/GridDemo.swift b/Sources/TokamakDemo/GridDemo.swift index b15e595f7..90967c13f 100644 --- a/Sources/TokamakDemo/GridDemo.swift +++ b/Sources/TokamakDemo/GridDemo.swift @@ -21,7 +21,7 @@ import SwiftUI import TokamakDOM #endif -@available(OSX 10.16, *) +@available(OSX 10.16, iOS 14.0, *) public struct GridDemo: View { public var body: some View { Group { diff --git a/Sources/TokamakDemo/TokamakDemo.swift b/Sources/TokamakDemo/TokamakDemo.swift index 954a7ff4a..7e9b34230 100644 --- a/Sources/TokamakDemo/TokamakDemo.swift +++ b/Sources/TokamakDemo/TokamakDemo.swift @@ -56,12 +56,12 @@ struct TokamakDemoView: View { #else ListDemo() #endif - if #available(OSX 10.16, *) { + if #available(OSX 10.16, iOS 14.0, *) { OutlineGroupDemo() } ColorDemo() .padding() - if #available(OSX 10.16, *) { + if #available(OSX 10.16, iOS 14.0, *) { GridDemo() } } diff --git a/docs/progress.md b/docs/progress.md index 4fbc0b3da..23771e204 100644 --- a/docs/progress.md +++ b/docs/progress.md @@ -75,9 +75,9 @@ Table columns: | | | | | --- | ------------------------------------------------------------------------ | :-: | -| | [LazyHGrid](https://developer.apple.com/documentation/swiftui/lazyhgrid) | β | -| | [LazyVGrid](https://developer.apple.com/documentation/swiftui/lazyvgrid) | β | -| | [GridItem](https://developer.apple.com/documentation/swiftui/griditem) | β | +|🚧| [LazyHGrid](https://developer.apple.com/documentation/swiftui/lazyhgrid) | β | +|🚧| [LazyVGrid](https://developer.apple.com/documentation/swiftui/lazyvgrid) | β | +|🚧| [GridItem](https://developer.apple.com/documentation/swiftui/griditem) | β | ### Lists and Scroll Views