Skip to content

Commit

Permalink
feat: add mini-charts for ios
Browse files Browse the repository at this point in the history
  • Loading branch information
vcellu committed Aug 4, 2022
1 parent 46cc021 commit 7f7ee13
Show file tree
Hide file tree
Showing 10 changed files with 494 additions and 24 deletions.
3 changes: 3 additions & 0 deletions ios/ColorParser.swift
Expand Up @@ -26,6 +26,9 @@ class ColorParser {
"teal": .systemTeal]

func fromCSS(cssString: String) -> UIColor {
if(cssString == "none") {
return UIColor.clear
}
let hex = cssString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
if cssString.hasPrefix("#") {
var int = UInt64()
Expand Down
72 changes: 48 additions & 24 deletions ios/DataCellView.swift
Expand Up @@ -33,20 +33,31 @@ class DataCellView: UICollectionViewCell {
let views = contentView.subviews
row.cells.enumerated().forEach {(index, element) in
let col = cols[index]

if let label = views[index] as? PaddedLabel {
let newFrame = CGRect(x: x, y: 0, width: Int(col.width!), height: theme.rowHeight! * numberOfLines)
label.textAlignment = element.qNum == nil ? .left : .right
x += Int(col.width!)
label.frame = newFrame.integral
label.center = CGPoint(x: floor(label.center.x), y: floor(label.center.y))
label.text = element.qText
label.column = index
label.cell = element
label.checkSelected(selectionsEngine)
label.textColor = cellColor!
label.numberOfLines = numberOfLines
let newFrame = CGRect(x: x, y: 0, width: Int(col.width!), height: theme.rowHeight! * numberOfLines)
if let representation = col.representation {
if representation.type == "miniChart" {
if let miniChart = views[index] as? MiniChartView {
miniChart.frame = newFrame.integral
miniChart.setChartData(data: element, representedAs: representation)
}
}
else {
if let label = views[index] as? PaddedLabel {

label.textAlignment = element.qNum == nil ? .left : .right
label.frame = newFrame.integral
label.center = CGPoint(x: floor(label.center.x), y: floor(label.center.y))
label.text = element.qText
label.column = index
label.cell = element
label.checkSelected(selectionsEngine)
label.textColor = cellColor!
label.numberOfLines = numberOfLines
}
}
}
x += Int(col.width!)

}
}

Expand All @@ -56,16 +67,26 @@ class DataCellView: UICollectionViewCell {
var x = 0
clearAllCells()
for col in cols {
let label = PaddedLabel(frame: .zero)
if col.isDim == true {
if let selectionsEngine = selectionsEngine {
label.makeSelectable(selectionsEngine: selectionsEngine)

if let representation = col.representation {
if(representation.type == "miniChart") {
let miniChartView = MiniChartView(frame: .zero)
contentView.addSubview(miniChartView)
}
else {
let label = PaddedLabel(frame: .zero)
if col.isDim == true {
if let selectionsEngine = selectionsEngine {
label.makeSelectable(selectionsEngine: selectionsEngine)
}
}
let sizedFont = UIFont.systemFont(ofSize: 14)
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: sizedFont)
label.adjustsFontForContentSizeCategory = true
contentView.addSubview(label)
}
}
let sizedFont = UIFont.systemFont(ofSize: 14)
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: sizedFont)
label.adjustsFontForContentSizeCategory = true
contentView.addSubview(label)

x += Int(col.width!)
}
}
Expand Down Expand Up @@ -93,19 +114,22 @@ class DataCellView: UICollectionViewCell {
}
let new = CGRect(x: old.origin.x + translation.x, y: 0, width: newNeighbourWidth, height: old.height)
v.frame = new
v.setNeedsDisplay()
}

resizeLabel(view: view, width: newWidth)

resizeContentView(view: view, width: newWidth)
return true
}

fileprivate func resizeLabel(view: UIView, width: CGFloat) {
fileprivate func resizeContentView(view: UIView, width: CGFloat) {
if view.frame.width != width {
let oldFrame = view.frame
let newFrame = CGRect(x: oldFrame.origin.x, y: 0, width: width, height: oldFrame.height)
view.frame = newFrame
view.setNeedsDisplay()
}

}

override func draw(_ rect: CGRect) {
Expand Down
38 changes: 38 additions & 0 deletions ios/DataColumn.swift
Expand Up @@ -6,6 +6,41 @@
//

import Foundation

struct MiniChartColor: Codable {
let color: String?
let index: Int?
}

struct ChartColors: Codable {
let first: MiniChartColor?
let last: MiniChartColor?
let min: MiniChartColor?
let max: MiniChartColor?
let negative: MiniChartColor?
let positive: MiniChartColor?
let main: MiniChartColor?
}

struct YAxis: Codable {
let position: String?
let scale: String?
}

struct MiniChart: Codable {
let type: String?
let colors: ChartColors?
let showDots: Bool?
let yAxis: YAxis?
}

struct Representation: Codable {
let type: String?
let miniChart: MiniChart?
let globalMax: Double?
let globalMin: Double?
}

struct DataColumn: Codable {
var isDim: Bool = false
var active: Bool = false
Expand All @@ -15,6 +50,9 @@ struct DataColumn: Codable {
let align: String?
let sortDirection: String?
let dataColIdx: Double?
let representation: Representation?
let max: Double?
let min: Double?
}

struct TotalsCell: Decodable {
Expand Down
14 changes: 14 additions & 0 deletions ios/DataRow.swift
Expand Up @@ -6,6 +6,16 @@
//

import Foundation
struct MatrixCell: Decodable {
let qNum: Double?
}

struct Matrix: Decodable {
let qMatrix: [[MatrixCell]]?
let qMax: Double?
let qMin: Double?
}

struct DataCell: Decodable {
var qText: String?
var qNum: Double?
Expand All @@ -16,6 +26,8 @@ struct DataCell: Decodable {
var isDim: Bool?
var rawRowIdx: Double?
var rawColIdx: Double?
var qMiniChart: Matrix?

enum CodingKeys: String, CodingKey {
case qText
case qNum
Expand All @@ -26,6 +38,7 @@ struct DataCell: Decodable {
case isDim
case rawRowIdx
case rawColIdx
case qMiniChart
}

init(from decoder: Decoder) throws {
Expand All @@ -38,6 +51,7 @@ struct DataCell: Decodable {
self.rawRowIdx = try container.decodeIfPresent(Double.self, forKey: .rawRowIdx) ?? -1
self.rawColIdx = try container.decodeIfPresent(Double.self, forKey: .rawColIdx) ?? -1
self.isDim = try container.decodeIfPresent(Bool.self, forKey: .isDim) ?? false
self.qMiniChart = try container.decodeIfPresent(Matrix.self, forKey: .qMiniChart) ?? nil

if let temp = try? container.decode(Double.self, forKey: .qNum) {
self.qNum = temp
Expand Down
33 changes: 33 additions & 0 deletions ios/MiniBarChart.swift
@@ -0,0 +1,33 @@
//
// MiniBarChart.swift
// react-native-simple-grid
//
// Created by Vittorio Cellucci on 2022-08-03.
//

import Foundation
class MiniBarChart : MiniChartRenderer {

override func render(_ ctx: CGContext, rect: CGRect) {
guard let data = data else {return}
guard let rows = data.qMatrix else {return}
if (rect.size.height == 0) {return}
ctx.clear(rect)
getBandWidth(rect: rect, data: data)
getScale(rect:rect, data:data)
var x = padding + horizontalPadding / 2;
var index = 0
for row in rows {
mainColor.set()
let value = row[1].qNum ?? 1.0
let height = value * scale;
let y = rect.height - height - verticalPadding / 2
let rect = CGRect(x: x, y: y, width: bandWidth, height:height)
setColor(index, value: value, count: rows.count)
ctx.fill(rect)
x += padding * 2 + bandWidth
index += 1
}
}

}
87 changes: 87 additions & 0 deletions ios/MiniChartRenderer.swift
@@ -0,0 +1,87 @@
//
// MiniChart.swift
// react-native-simple-grid
//
// Created by Vittorio Cellucci on 2022-08-03.
//

import Foundation
class MiniChartRenderer {
var data: Matrix?
var representation: Representation?
var firstColor = UIColor.systemBlue
var lastColor = UIColor.systemBlue
var mainColor = UIColor.systemBlue
var positiveColor = UIColor.systemBlue
var negativeColor = UIColor.systemBlue
var maxColor = UIColor.systemBlue
var minColor = UIColor.systemBlue
var bandWidth = 0.0
var padding = 0.0
var maxValue = Double.infinity
var minValue = -Double.infinity
var globalMaxValue = Double.infinity
var globalMinValue = -Double.infinity
var yScale = Double.infinity
var scale = 0.0
var verticalPadding = 0.0
var horizontalPadding = 0.0
var showDots = false

init() {

}

init(rep: Representation) {
self.representation = rep
mainColor = ColorParser().fromCSS(cssString: rep.miniChart?.colors?.main?.color ?? "black")
firstColor = ColorParser().fromCSS(cssString: rep.miniChart?.colors?.first?.color ?? "black")
lastColor = ColorParser().fromCSS(cssString: rep.miniChart?.colors?.last?.color ?? "black")
positiveColor = ColorParser().fromCSS(cssString: rep.miniChart?.colors?.positive?.color ?? "black")
negativeColor = ColorParser().fromCSS(cssString: rep.miniChart?.colors?.negative?.color ?? "black")
maxColor = ColorParser().fromCSS(cssString: rep.miniChart?.colors?.max?.color ?? "black")
minColor = ColorParser().fromCSS(cssString: rep.miniChart?.colors?.min?.color ?? "black")
showDots = rep.miniChart?.showDots ?? false
}

func setColor(_ index: Int, value: Double, count: Int) {
if(value == maxValue && maxColor != .clear) {
maxColor.set()
return
}

if(value == minValue && minColor != .clear) {
minColor.set()
return
}

if(index == 0 && firstColor != .clear) {
firstColor.set()
return
}

if(index == count - 1 && lastColor != .clear) {
lastColor.set()
return;
}

mainColor.set()
}

func getBandWidth(rect: CGRect, data: Matrix) {
let count = data.qMatrix?.count ?? 1
horizontalPadding = rect.width * 0.05;
let width = rect.width * 0.95;
let totalBandWidth = width / CGFloat(count)
bandWidth = totalBandWidth * 0.8
padding = totalBandWidth * 0.1
}

func getScale(rect: CGRect, data: Matrix) {
let height = rect.height - 8;
verticalPadding = 8
scale = height / yScale
}

func render(_ ctx: CGContext, rect: CGRect){}
}
41 changes: 41 additions & 0 deletions ios/MiniChartView.swift
@@ -0,0 +1,41 @@
//
// MiniChartView.swift
// react-native-simple-grid
//
// Created by Vittorio Cellucci on 2022-08-03.
//

import Foundation
import QuartzCore

class MiniChartView : UIView {
var miniChart = MiniChartRenderer()

func setChartData(data: DataCell, representedAs rep: Representation) {
self.backgroundColor = UIColor.clear
self.layer.contentsScale = UIScreen.main.scale
if rep.miniChart?.type == "bars" {
miniChart = MiniBarChart(rep: rep)
} else if(rep.miniChart?.type == "sparkline") {
miniChart = MiniSparkLineChart(rep: rep)
} else if(rep.miniChart?.type == "dots") {
miniChart = MiniDotGraph(rep: rep)
} else if(rep.miniChart?.type == "posNeg") {
miniChart = PositiveNegativeChart(rep: rep)
}
miniChart.data = data.qMiniChart
miniChart.maxValue = miniChart.data?.qMax ?? Double.infinity
miniChart.minValue = miniChart.data?.qMin ?? -Double.infinity
miniChart.globalMaxValue = rep.globalMax ?? Double.infinity
miniChart.globalMinValue = rep.globalMin ?? -Double.infinity
miniChart.yScale = miniChart.maxValue
if(rep.miniChart?.yAxis?.scale == "global") {
miniChart.yScale = miniChart.globalMaxValue
}
}

override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
miniChart.render(ctx, rect: rect)
}
}

0 comments on commit 7f7ee13

Please sign in to comment.