Skip to content

Commit

Permalink
feat: 🎸 [JIRA: HCPSDKFIORIUIKIT-2453]DataTable readonly support (#626)
Browse files Browse the repository at this point in the history
  • Loading branch information
shengxu7 authored Jan 30, 2024
1 parent e1fe82c commit 05b5a86
Show file tree
Hide file tree
Showing 17 changed files with 472 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,16 @@ struct DataTableExample: View {

Section(header: Text("Sticky header/column")) {
NavigationLink("Not sticky header & column, baseline alignment",
destination: DataTableExampleView(model: TestRowData.generateData(row: 10, column: 5, containLeadingAccessory: false, containTrailingAccessory: false, isHeaderSticky: false, isFirstColumnSticky: false, isPinchZoomEnable: true, showListView: false)))
destination: DataTableExampleView(model: TestRowData.generateData(row: 20, column: 15, containLeadingAccessory: false, containTrailingAccessory: false, isHeaderSticky: false, isFirstColumnSticky: false, isPinchZoomEnable: true, showListView: false)))

NavigationLink("Sticky header",
destination: DataTableExampleView(model: TestRowData.generateData(row: 10, column: 5, containLeadingAccessory: false, containTrailingAccessory: true, isHeaderSticky: true, isFirstColumnSticky: false, isPinchZoomEnable: true, showListView: false)))
NavigationLink("Sticky header with readonly rows",
destination: DataTableExampleView(model: TestRowData.generateData(row: 20, column: 15, containLeadingAccessory: false, containTrailingAccessory: true, isHeaderSticky: true, isFirstColumnSticky: false, isPinchZoomEnable: true, showListView: false, rowIndexesReadonly: [1, 3])))

NavigationLink("Sticky column",
destination: DataTableExampleView(model: TestRowData.generateData(row: 10, column: 5, containLeadingAccessory: true, containTrailingAccessory: false, isHeaderSticky: false, isFirstColumnSticky: true, isPinchZoomEnable: true, showListView: false)))
NavigationLink("Sticky column with readonly columns",
destination: DataTableExampleView(model: TestRowData.generateData(row: 20, column: 15, containLeadingAccessory: true, containTrailingAccessory: false, isHeaderSticky: false, isFirstColumnSticky: true, isPinchZoomEnable: true, showListView: false, columnIndexesReadonly: [0, 2, 4])))

NavigationLink("Sticky header & column",
destination: DataTableExampleView(model: TestRowData.generateData(row: 10, column: 5, containLeadingAccessory: false, containTrailingAccessory: false, isHeaderSticky: true, isFirstColumnSticky: true, isPinchZoomEnable: true, showListView: false, isFixedWidth: true)))
NavigationLink("Sticky header & column with readonly cells",
destination: DataTableExampleView(model: TestRowData.generateData(row: 20, column: 15, containLeadingAccessory: false, containTrailingAccessory: false, isHeaderSticky: true, isFirstColumnSticky: true, isPinchZoomEnable: true, showListView: false, isFixedWidth: false, isReadonlyForRandomCell: true)))
}

Section(header: Text("Variant rows/columns")) {
Expand All @@ -155,7 +155,7 @@ struct DataTableExample: View {
destination: DataTableExampleView(model: TestRowData.generateData(row: 5, column: 3, isHeaderSticky: false, isFirstColumnSticky: false, isPinchZoomEnable: true, showListView: false)))

NavigationLink("20 rows 12 columns",
destination: DataTableExampleView(model: TestRowData.generateData(row: 20, column: 12, containLeadingAccessory: false, containTrailingAccessory: false, containIndex: true, isHeaderSticky: true, isFirstColumnSticky: true, isPinchZoomEnable: true, showListView: false), addRowsDuringScrolling: true))
destination: DataTableExampleView(model: TestRowData.generateData(row: 20, column: 12, containLeadingAccessory: false, containTrailingAccessory: false, containIndex: true, isHeaderSticky: true, isFirstColumnSticky: true, isPinchZoomEnable: true, showListView: false, isReadonlyForRandomCell: true), addRowsDuringScrolling: true))

NavigationLink("300 rows 60 columns",
destination: DataTableExampleView(model: TestRowData.generateData(row: 300, column: 60, containIndex: true, isHeaderSticky: true, isFirstColumnSticky: true, isPinchZoomEnable: true, showListView: false)))
Expand Down Expand Up @@ -205,7 +205,8 @@ let row3WithAlignment = TableRowItem(data: [DataImageItem(Image("wheel")), DataT

let row1WithDate = TableRowItem(data: [DataTextItem("Hello", Font.headline, Color.orange), DataImageItem(Image("wheel")), DataDateItem(Date(timeIntervalSince1970: 1), Font.largeTitle, Color.preferredColor(.chart2)), DataTimeItem(Date(timeIntervalSince1970: 1), Font.headline, Color.purple), DataDurationItem(3000, Font.footnote, Color.preferredColor(.secondaryLabel)), DataListItem("San Jose")])
let row2WithDate = TableRowItem(data: [DataImageItem(Image("wheel")), DataTextItem("World"), DataDateItem(Date(timeIntervalSinceReferenceDate: 1), Font.title2), DataTimeItem(Date(timeIntervalSinceReferenceDate: 1000)), DataDurationItem(23000), DataListItem("New York", Font.headline)])
let row3WithDate = TableRowItem(data: [DataTextItem("Leading", Font.largeTitle, Color.purple), DataImageItem(Image("wheel")), DataDateItem(Date(), Font.headline), DataTimeItem(Date()), DataDurationItem(12002), DataListItem("Los Angeles", Font.title3, Color.pink)])
let row3WithDate = TableRowItem(data: [DataTextItem("Leading", Font.largeTitle, Color.purple, isReadonly: true),
DataImageItem(Image("wheel")), DataDateItem(Date(), Font.headline), DataTimeItem(Date()), DataDurationItem(12002), DataListItem("Los Angeles", Font.title3, Color.pink)])

let threeRowThreeColumn = TableModel(headerData: nil,
rowData: [row1WithAlignment, row2WithAlignment, row3WithAlignment],
Expand Down Expand Up @@ -249,7 +250,7 @@ public enum TestRowData {
static let colors = [Color.purple, Color.green, Color.indigo, Color.orange, Color.preferredColor(.primaryLabel)]
static let cities = ["Aberdeen", "Anchorage", "Arvada", "Arvada", "Bakersfield", "Birmingham", "Davenport", "Duluth", "Elkhart", "Hollywood", "Indianapolis", "Knoxville", "Laredo", "San Jose", "New York", "Los Angeles", "Las Vegas", "Tokyo", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Antonio", "Dallas", "Rancho Cucamonga", "Vancouver"]

static func generateRowData(numOfColumns: Int, rowIndex: Int, containLeadingAccessory: Bool = true, containTrailingAccessory: Bool = true, containIndex: Bool = false, newRowHint: Bool = false) -> TableRowItem {
static func generateRowData(numOfColumns: Int, rowIndex: Int, containLeadingAccessory: Bool = true, containTrailingAccessory: Bool = true, containIndex: Bool = false, newRowHint: Bool = false, isReadonly: Bool = false, isReadonlyForRandomCell: Bool = false) -> TableRowItem {
var data: [DataItem] = []
for i in 0 ..< numOfColumns {
let dataType = i % DataItemType.allCases.count
Expand All @@ -272,21 +273,21 @@ public enum TestRowData {
textString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus mattis tristique pretium."
}
let finalText = containIndex ? "(\(rowIndex), \(i)): " + textString : textString
let textItem = DataTextItem(newRowHint ? "New column" : finalText, font, color)
let textItem = DataTextItem(newRowHint ? "New column" : finalText, font, color, isReadonly: isReadonlyForRandomCell ? ((Int.random(in: 0 ... 8) + rowIndex + i) % 5 == 0 ? true : nil) : nil)
data.append(textItem)
case 2:
let ti = rowIndex * 3600 * 24 + (i + 4) * 3600
let dateItem = DataDateItem(Date(timeIntervalSinceReferenceDate: TimeInterval(ti)), font, color)
let dateItem = DataDateItem(Date(timeIntervalSinceReferenceDate: TimeInterval(ti)), font, color, isReadonly: isReadonlyForRandomCell ? ((Int.random(in: 0 ... 8) + rowIndex + i) % 5 == 0 ? true : nil) : nil)
data.append(dateItem)
case 3:
let ti = rowIndex * 3600 + i * 60
let timeItem = DataTimeItem(Date(timeIntervalSinceReferenceDate: TimeInterval(ti)), font, color)
let timeItem = DataTimeItem(Date(timeIntervalSinceReferenceDate: TimeInterval(ti)), font, color, isReadonly: isReadonlyForRandomCell ? ((Int.random(in: 0 ... 8) + rowIndex + i) % 5 == 0 ? true : nil) : nil)
data.append(timeItem)
case 4:
let durationItem = DataDurationItem(TimeInterval(3600 + rowIndex * 600 + i), font, color)
let durationItem = DataDurationItem(TimeInterval(3600 + rowIndex * 600 + i), font, color, isReadonly: isReadonlyForRandomCell ? ((Int.random(in: 0 ... 8) + rowIndex + i) % 5 == 0 ? true : nil) : nil)
data.append(durationItem)
default:
let listItem = DataListItem(cities[(rowIndex + i) % self.cities.count], font, color)
let listItem = DataListItem(cities[(rowIndex + i) % self.cities.count], font, color, isReadonly: isReadonlyForRandomCell ? ((Int.random(in: 0 ... 8) + rowIndex + i) % 5 == 0 ? true : nil) : nil)
data.append(listItem)
}
}
Expand All @@ -296,7 +297,7 @@ public enum TestRowData {
print("trailing accessory tapped: \(rowIndex) tapped")
}))

let output = TableRowItem(leadingAccessories: containLeadingAccessory ? lAccessories : [], trailingAccessory: containTrailingAccessory ? tAccessory : nil, data: data)
let output = TableRowItem(leadingAccessories: containLeadingAccessory ? lAccessories : [], trailingAccessory: containTrailingAccessory ? tAccessory : nil, data: data, isReadonly: isReadonly ? true : nil)

return output
}
Expand All @@ -310,27 +311,27 @@ public enum TestRowData {
return TableRowItem(data: data)
}

static func generateColumnAttributes(column: Int, isFixedWidth: Bool = false) -> [ColumnAttribute] {
static func generateColumnAttributes(column: Int, isFixedWidth: Bool = false, columnIndexesReadonly: [Int] = []) -> [ColumnAttribute] {
var output: [ColumnAttribute] = []
for _ in 0 ..< column {
let att = ColumnAttribute(textAlignment: .leading, width: isFixedWidth ? .fixed(200) : .flexible)
for i in 0 ..< column {
let att = ColumnAttribute(textAlignment: .leading, width: isFixedWidth ? .fixed(200) : .flexible, isReadonly: columnIndexesReadonly.contains(i) ? true : nil)
output.append(att)
}
return output
}

static func generateData(row: Int, column: Int, containLeadingAccessory: Bool = true, containTrailingAccessory: Bool = true, containIndex: Bool = false, isHeaderSticky: Bool = true, isFirstColumnSticky: Bool = true, isPinchZoomEnable: Bool = true, showListView: Bool = false, isFixedWidth: Bool = false) -> TableModel {
static func generateData(row: Int, column: Int, containLeadingAccessory: Bool = true, containTrailingAccessory: Bool = true, containIndex: Bool = false, isHeaderSticky: Bool = true, isFirstColumnSticky: Bool = true, isPinchZoomEnable: Bool = true, showListView: Bool = false, isFixedWidth: Bool = false, rowIndexesReadonly: [Int] = [], columnIndexesReadonly: [Int] = [], isReadonlyForRandomCell: Bool = false) -> TableModel {
var res: [TableRowItem] = []
var titles: [DataTextItem] = []
for k in 0 ..< column {
titles.append(DataTextItem(self.types[k % self.types.count]))
}
for i in 0 ..< row {
res.append(self.generateRowData(numOfColumns: column, rowIndex: i, containLeadingAccessory: containLeadingAccessory, containTrailingAccessory: containTrailingAccessory, containIndex: containIndex))
res.append(self.generateRowData(numOfColumns: column, rowIndex: i, containLeadingAccessory: containLeadingAccessory, containTrailingAccessory: containTrailingAccessory, containIndex: containIndex, isReadonly: rowIndexesReadonly.contains(i) ? true : false, isReadonlyForRandomCell: isReadonlyForRandomCell))
}
let header = TableRowItem(data: titles)
let model = TableModel(headerData: header, rowData: res, isHeaderSticky: isHeaderSticky, isFirstColumnSticky: isFirstColumnSticky, isPinchZoomEnable: isPinchZoomEnable, showListView: showListView)
model.columnAttributes = self.generateColumnAttributes(column: column, isFixedWidth: isFixedWidth)
model.columnAttributes = self.generateColumnAttributes(column: column, isFixedWidth: isFixedWidth, columnIndexesReadonly: columnIndexesReadonly)
model.didSelectRowAt = { rowIndex in
print("Tapped row \(rowIndex)")
}
Expand Down
7 changes: 5 additions & 2 deletions Sources/FioriSwiftUICore/DataTable/ColumnAttribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public struct ColumnAttribute {
public var textAlignment: TextAlignment = .leading
/// Setting the width for each column.
public var width: Width = .flexible

/// Read-only property for all cells in this column. If a cell's `isReadonly` within this column is set, then that value is used instead. `nil` means it is `false`.
public var isReadonly: Bool?
/// used by date or time column
public var dateFormatter: DateFormatter?

Expand Down Expand Up @@ -70,8 +71,10 @@ public struct ColumnAttribute {
/// - Parameters:
/// - textAlignment: Text alignment in each column.
/// - width: Setting the width for each column.
public init(textAlignment: TextAlignment = .leading, width: Width = .flexible) {
/// - isReadonly: Is the column read-only or not for the inline editing mode.
public init(textAlignment: TextAlignment = .leading, width: Width = .flexible, isReadonly: Bool? = nil) {
self.textAlignment = textAlignment
self.width = width
self.isReadonly = isReadonly
}
}
17 changes: 15 additions & 2 deletions Sources/FioriSwiftUICore/DataTable/DataDateItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ public struct DataDateItem: DataItemTextComponent, CheckBinding, Equatable {
/// Binding rule.
public var binding: ObjectViewProperty.Text?

/**
Is the cell read-only or not for the inline editing mode. `nil` means it is `false`.
A cell's `isReadonly` is determined by the value of itself, the row and the column.
If only one of these three value is set then that value is used.
If two or three values are set, then the higher priority of value is used.
The order of priority from high to low is cell, row and column.
*/
public var isReadonly: Bool?

var hasBinding: Bool {
self.binding != nil
}
Expand All @@ -37,12 +46,14 @@ public struct DataDateItem: DataItemTextComponent, CheckBinding, Equatable {
/// - textColor: Foreground color for text Item.
/// - binding: Binding rule.
/// - lineLimit: Line limit for item.
public init(_ date: Date, _ font: Font? = nil, _ textColor: Color? = nil, _ binding: ObjectViewProperty.Text? = nil, lineLimit: Int? = nil) {
/// - isReadonly: Is the cell read-only or not in inline editing mode.
public init(_ date: Date, _ font: Font? = nil, _ textColor: Color? = nil, _ binding: ObjectViewProperty.Text? = nil, lineLimit: Int? = nil, isReadonly: Bool? = nil) {
self.date = date
self.font = font
self.textColor = textColor
self.binding = binding
self.lineLimit = lineLimit
self.isReadonly = isReadonly
}

/// Public initializer for `DataTextItem`
Expand All @@ -52,12 +63,14 @@ public struct DataDateItem: DataItemTextComponent, CheckBinding, Equatable {
/// - textColor: Foreground color for text Item.
/// - binding: Binding rule.
/// - lineLimit: Line limit for item.
public init(date: Date, uifont: UIFont? = nil, textColor: Color? = nil, binding: ObjectViewProperty.Text? = nil, lineLimit: Int? = nil) {
/// - isReadonly: Is the cell read-only or not for the inline editing mode.
public init(date: Date, uifont: UIFont? = nil, textColor: Color? = nil, binding: ObjectViewProperty.Text? = nil, lineLimit: Int? = nil, isReadonly: Bool? = nil) {
self.date = date
self.uifont = uifont
self.textColor = textColor
self.binding = binding
self.lineLimit = lineLimit
self.isReadonly = isReadonly
}

func string(for columnAttribute: ColumnAttribute) -> String {
Expand Down
17 changes: 15 additions & 2 deletions Sources/FioriSwiftUICore/DataTable/DataDurationItem.swift