Skip to content

Commit

Permalink
feat: impl. horizontal flexbox
Browse files Browse the repository at this point in the history
  • Loading branch information
jon4hz committed Jan 30, 2023
1 parent 152af06 commit 5b3916a
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 346 deletions.
20 changes: 10 additions & 10 deletions example/flex-box-horizonal/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"os"

"github.com/76creates/stickers/horizontal"
"github.com/76creates/stickers/flexbox"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
Expand Down Expand Up @@ -33,26 +33,26 @@ var (
)

type model struct {
flexBox *horizontal.HorizontalFlexBox
flexBox *flexbox.HorizontalFlexBox
}

func main() {
m := model{
flexBox: horizontal.NewHorizontalFlexBox(0, 0),
flexBox: flexbox.NewHorizontal(0, 0),
}

columns := []*horizontal.FlexBoxColumn{
columns := []*flexbox.Column{
m.flexBox.NewColumn().AddCells(
horizontal.NewFlexBoxCell(1, 1).SetStyle(style1),
horizontal.NewFlexBoxCell(1, 1).SetStyle(style2),
flexbox.NewCell(1, 1).SetStyle(style1),
flexbox.NewCell(1, 1).SetStyle(style2),
),
m.flexBox.NewColumn().AddCells(
horizontal.NewFlexBoxCell(2, 1).SetStyle(style3),
flexbox.NewCell(2, 1).SetStyle(style3),
),
m.flexBox.NewColumn().AddCells(
horizontal.NewFlexBoxCell(1, 1).SetStyle(style4),
horizontal.NewFlexBoxCell(1, 2).SetStyle(style5),
horizontal.NewFlexBoxCell(1, 1).SetStyle(style6),
flexbox.NewCell(1, 1).SetStyle(style4),
flexbox.NewCell(1, 2).SetStyle(style5),
flexbox.NewCell(1, 1).SetStyle(style6),
),
}

Expand Down
18 changes: 14 additions & 4 deletions flexbox/cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import (
"github.com/charmbracelet/lipgloss"
)

// FlexBoxCell is a building block object of the FlexBox, it represents a single cell within a box
// cells are stacked horizontally
// Cell is a building block object of the FlexBox, it represents a single cell within a box
// A FlexBox stacks cells horizonally.
// A HorizontalFlexBox stacks cells vertically. (controverse, isn't it?)
type Cell struct {
// style of the cell, when rendering it will inherit the style of the parent row
style lipgloss.Style
Expand All @@ -19,7 +20,8 @@ type Cell struct {
ratioY int
// minWidth minimal width of the cell
minWidth int
// TODO: implement minimal height
// minHeigth minimal heigth of the cell
minHeigth int

width int
height int
Expand Down Expand Up @@ -55,12 +57,20 @@ func (r *Cell) GetContent() string {
return r.content
}

// SetMinWidth sets the cells minimum width, this will not disable responsivness
// SetMinWidth sets the cells minimum width, this will not disable responsivness.
// This has only an effect to cells of a normal FlexBox, not a HorizontalFlexBox.
func (r *Cell) SetMinWidth(value int) *Cell {
r.minWidth = value
return r
}

// SetMinHeigth sets the cells minimum height, this will not disable responsivness.
// This has only an effect to cells of a HorizontalFlexBox.
func (r *Cell) SetMinHeigth(value int) *Cell {
r.minHeigth = value
return r
}

// SetStyle replaces the style, it unsets width/height related keys
func (r *Cell) SetStyle(style lipgloss.Style) *Cell {
r.style = style.
Expand Down
75 changes: 37 additions & 38 deletions horizontal/flexBoxColumn.go → flexbox/column.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package horizontal
package flexbox

import (
"strconv"

"github.com/charmbracelet/lipgloss"
)

// FlexBoxColumn is the container for the cells, this object has the least to do with the ratio
// Column is the container for the cells, this object has the least to do with the ratio
// of the construction as it takes all of the needed ratio information from the cell slice
// columns are stacked vertically
type FlexBoxColumn struct {
// columns are stacked horizontally.
type Column struct {
// style of the column
style lipgloss.Style
styleAncestor bool

cells []*FlexBoxCell
cells []*Cell

height int
width int
Expand All @@ -26,7 +26,7 @@ type FlexBoxColumn struct {

// AddCells appends the cells to the column
// if the cell ID is not set it will default to the index of the cell
func (r *FlexBoxColumn) AddCells(cells ...*FlexBoxCell) *FlexBoxColumn {
func (r *Column) AddCells(cells ...*Cell) *Column {
r.cells = append(r.cells, cells...)
for i, cell := range r.cells {
if cell.id == "" {
Expand All @@ -38,26 +38,26 @@ func (r *FlexBoxColumn) AddCells(cells ...*FlexBoxCell) *FlexBoxColumn {
}

// CellsLen returns the len of the cells slice
func (r *FlexBoxColumn) CellsLen() int {
func (r *Column) CellsLen() int {
return len(r.cells)
}

// GetCell returns the FlexBoxCell on the given index if it exists
// GetCell returns the Cell on the given index if it exists
// note: forces the recalculation if found
//
// returns nil if not found
func (r *FlexBoxColumn) GetCell(index int) *FlexBoxCell {
func (r *Column) GetCell(index int) *Cell {
if index >= 0 && index < len(r.cells) {
r.setRecalculate()
return r.cells[index]
}
return nil
}

// GetCellCopy returns a copy of the FlexBoxCell on the given index, if cell
// GetCellCopy returns a copy of the Cell on the given index, if cell
// does not exist it will return nil. This is useful when you need to get
// cells attribute without triggering a recalculation.
func (r *FlexBoxColumn) GetCellCopy(index int) *FlexBoxCell {
func (r *Column) GetCellCopy(index int) *Cell {
if index >= 0 && index < len(r.cells) {
c := r.cells[index].copy()
return &c
Expand All @@ -69,7 +69,7 @@ func (r *FlexBoxColumn) GetCellCopy(index int) *FlexBoxCell {
// note: forces the recalculation if found
//
// returns nil if not found
func (r *FlexBoxColumn) GetCellWithID(id string) *FlexBoxCell {
func (r *Column) GetCellWithID(id string) *Cell {
for _, c := range r.cells {
if c.id == id {
r.setRecalculate()
Expand All @@ -81,15 +81,15 @@ func (r *FlexBoxColumn) GetCellWithID(id string) *FlexBoxCell {

// UpdateCellWithIndex replaces the cell on the given index if it exists
// if its not existing no changes will apply
func (r *FlexBoxColumn) UpdateCellWithIndex(index int, cell *FlexBoxCell) {
func (r *Column) UpdateCellWithIndex(index int, cell *Cell) {
if index >= 0 && len(r.cells) > 0 && index < len(r.cells) {
r.cells[index] = cell
r.setRecalculate()
}
}

// SetStyle replaces the style, it unsets width/height related keys
func (r *FlexBoxColumn) SetStyle(style lipgloss.Style) *FlexBoxColumn {
func (r *Column) SetStyle(style lipgloss.Style) *Column {
r.style = style.
UnsetWidth().
UnsetMaxWidth().
Expand All @@ -100,22 +100,22 @@ func (r *FlexBoxColumn) SetStyle(style lipgloss.Style) *FlexBoxColumn {
}

// StylePassing set whether the style should be passed to the cells
func (r *FlexBoxColumn) StylePassing(value bool) *FlexBoxColumn {
func (r *Column) StylePassing(value bool) *Column {
r.styleAncestor = value
return r
}

func (r *FlexBoxColumn) setHeight(value int) {
func (r *Column) setHeight(value int) {
r.height = value
r.setRecalculate()
}

func (r *FlexBoxColumn) setWidth(value int) {
func (r *Column) setWidth(value int) {
r.width = value
r.setRecalculate()
}

func (r *FlexBoxColumn) render(inherited ...lipgloss.Style) string {
func (r *Column) render(inherited ...lipgloss.Style) string {
var inheritedStyle []lipgloss.Style

for _, style := range inherited {
Expand All @@ -135,20 +135,19 @@ func (r *FlexBoxColumn) render(inherited ...lipgloss.Style) string {
return r.style.
Width(r.getContentWidth()).MaxWidth(r.getMaxWidth()).
Height(r.getContentHeight()).MaxHeight(r.getMaxHeight()).
//Render(lipgloss.JoinHorizontal(lipgloss.Top, renderedCells...))
Render(lipgloss.JoinVertical(lipgloss.Left, renderedCells...))
}

func (r *FlexBoxColumn) setRecalculate() {
func (r *Column) setRecalculate() {
r.recalculateFlag = true
}

func (r *FlexBoxColumn) unsetRecalculate() {
func (r *Column) unsetRecalculate() {
r.recalculateFlag = false
}

// recalculate fetches the cell's height/width distribution slices and sets it on the cells
func (r *FlexBoxColumn) recalculate() {
func (r *Column) recalculate() {
if r.recalculateFlag {
if len(r.cells) > 0 {
r.distributeCellDimensions(r.calculateCellsDimensions())
Expand All @@ -158,24 +157,24 @@ func (r *FlexBoxColumn) recalculate() {
}

// distributeCellDimensions sets width of each column per distribution array
func (r *FlexBoxColumn) distributeCellDimensions(xMatrix, yMatrix []int) {
func (r *Column) distributeCellDimensions(xMatrix, yMatrix []int) {
for index, y := range yMatrix {
r.cells[index].width = xMatrix[index]
r.cells[index].height = y
}
}

// calculateCellsDimensions calculates the height and width of the each cell
func (r *FlexBoxColumn) calculateCellsDimensions() (xMatrix, yMatrix []int) {
func (r *Column) calculateCellsDimensions() (xMatrix, yMatrix []int) {
// calculate the cell height, it uses fixed combined ratio since the height of each cell
// is individual and does not stack, column height will be calculated using the ratio of the
// highest cell in the slice
// is individual and does not stack, column width will be calculated using the ratio of the
// widest cell in the slice
cellXMatrix, cellXMatrixMax := r.getCellWidthMatrix()

// reminder not needed here due to how combined ratio is passed
xMatrix, _ = distributeToMatrix(r.getContentWidth(), cellXMatrixMax, cellXMatrix)

// get the min width matrix of the cells if any
// get the min heigth matrix of the cells if any
withMinHeigth := false
var minHeigthMatrix []int
for _, c := range r.cells {
Expand All @@ -185,7 +184,7 @@ func (r *FlexBoxColumn) calculateCellsDimensions() (xMatrix, yMatrix []int) {
}
}

// calculate the cell width matrix
// calculate the cell heigth matrix
if withMinHeigth {
yMatrix = calculateRatioWithMinimum(r.getContentHeight(), r.getCellHeightMatrix(), minHeigthMatrix)
} else {
Expand All @@ -196,15 +195,15 @@ func (r *FlexBoxColumn) calculateCellsDimensions() (xMatrix, yMatrix []int) {
}

// getCellHeightMatrix return the matrix of the cell height in cells
func (r *FlexBoxColumn) getCellHeightMatrix() (cellHeightMatrix []int) {
func (r *Column) getCellHeightMatrix() (cellHeightMatrix []int) {
for _, cell := range r.cells {
cellHeightMatrix = append(cellHeightMatrix, cell.ratioY)
}
return cellHeightMatrix
}

// getCellWidthMatrix return the matrix of the cell width in cells and the max value in it
func (r *FlexBoxColumn) getCellWidthMatrix() (cellWidthMatrix []int, max int) {
func (r *Column) getCellWidthMatrix() (cellWidthMatrix []int, max int) {
max = 0
for _, cell := range r.cells {
if cell.ratioX > max {
Expand All @@ -215,32 +214,32 @@ func (r *FlexBoxColumn) getCellWidthMatrix() (cellWidthMatrix []int, max int) {
return cellWidthMatrix, max
}

func (r *FlexBoxColumn) getContentWidth() int {
func (r *Column) getContentWidth() int {
return r.getMaxWidth() - r.getExtraWidth()
}

func (r *FlexBoxColumn) getContentHeight() int {
func (r *Column) getContentHeight() int {
return r.getMaxHeight() - r.getExtraHeight()
}

func (r *FlexBoxColumn) getMaxWidth() int {
func (r *Column) getMaxWidth() int {
return r.width
}

func (r *FlexBoxColumn) getMaxHeight() int {
func (r *Column) getMaxHeight() int {
return r.height
}

func (r *FlexBoxColumn) getExtraWidth() int {
func (r *Column) getExtraWidth() int {
return r.style.GetHorizontalMargins() + r.style.GetHorizontalBorderSize()
}

func (r *FlexBoxColumn) getExtraHeight() int {
func (r *Column) getExtraHeight() int {
return r.style.GetVerticalMargins() + r.style.GetVerticalBorderSize()
}

func (r *FlexBoxColumn) copy() FlexBoxColumn {
var cells []*FlexBoxCell
func (r *Column) copy() Column {
var cells []*Cell
for _, cell := range r.cells {
cellCopy := cell.copy()
cells = append(cells, &cellCopy)
Expand Down
Loading

0 comments on commit 5b3916a

Please sign in to comment.