3. Layout

Overview

Since DomKit "widgets" are simply HTML + CSS, they can be placed most anywhere in the DOM tree. However, since DomKit is specifically targeted at building rich user interfaces, it includes some standard layout containers to simplify constructing common UI designs:

Box

Box is the base class for Elems who are intended to layout out their children in a specific way. Box defaults several CSS properties to simplify composition:

display: block;
box-sizing: border-box;
width: 100%;
height: 100%;
position: relative;

GridBox

GridBox lays out children in a table format with a fixed number of columns and rows.

Example

GridBox
{
  it.cellStyle("*",    "*", "padding: 4px")
  it.cellStyle("*", "even", "background: #f8f8f8")
  it.cellStyle("*",     4,  "background: #e8e8e8; border-top: 1px solid #ccc")

  it.addRow([Label {
    it.text = "Enter User Information"
    it.style->fontWeight = "bold"
  }], [2])
  it.addRow([Label { it.text="First Name:"  }, TextField {}])
  it.addRow([Label { it.text="Middle Name:" }, TextField {}])
  it.addRow([Label { it.text="Last Name:"   }, TextField {}])
  it.addRow([null, Button { it.text="Submit" }])
}

GridBox

cellStyle

The cellStyle command may be used to stylize groups of cells. Valid values for col and row:

  • Specific index (0, 1, 2, etc)
  • Range of indexes (0..4, 7..<8, etc)
  • "*": apply to all row or columns
  • "even": apply only to even row or columns indexes
  • "odd": apply only to odd row or column indexes
cellStyle("*",   "*", "padding: 4px")
cellStyle("*", "odd", "padding: 4px")
cellStyle(  0,     1, "padding: 4px")
cellStyle(  3,  2..5, "padding: 4px")

addRow

Children are added to a GridBox a row at a time using addRow. If null is passed for any position on the row, that cell will be left empty:

// leave first cell empty
it.addRow([null, Button { it.text="Submit" }])

Optionally, addRow takes an argument that can specify that child cells should span multiple columns:

// pass a single child, but specify it should span 2 columns
it.addRow([Label {
  it.text = "Enter User Information"
  it.style->fontWeight = "bold"
}], [2])

The colspan argument may be applied to multiple children, and the list values correspond to the child list position:

// Add two children, where child1 spans 2 columns, and child2 3 columns
grid.addRow([child1, child2], [2,3])

removeRow

Rows in a GridBox can be removed by index using removeRow:

grid.removeRow(2)

If the a row does not exist at the given index, this method does nothing.

halign

By default GridBox will use preferred sizes for all child cells. This will often times leave free space around the GridBox. To adjust how the GridBox is positioned in this space use GridBox.halign:

GridBox { it.halign = Align.center }

FlowBox

FlowBox lays out child nodes side-by-side horizontally.

FlowBox
{
  Button { it.text="Alpha" },
  Button { it.text="Beta"  },
  Button { it.text="Gamma" },
}

FlowBox-1

The alignment of children inside a FlowBox can be adjusted using halign:

FlowBox
{
  it.halign = Align.center
  Button { it.text="Alpha" },
  Button { it.text="Beta"  },
  Button { it.text="Gamma" },
}

FlowBox-2

The spacing between each child can be configured use gaps, where each gap is the space between index and index+1 children:

FlowBox
{
  it.gaps = ["4px", "16px"]
  Button { it.text="Alpha" },  // 4px gap after Alpha
  Button { it.text="Beta"  },  // 16px gap after Beta
  Button { it.text="Gamma" },
}

FlowBox-3

If a FlowBox contains more children than gaps, the gaps list will be cycled over the child list:

FlowBox
{
  it.gaps = ["4px", "1px"]
  Button { ... },   // 4px gap after
  Button { ... },   // 1px gap after
  Button { ... },   // 4px gap after
  Button { ... },
}

FlowBox
{
  it.gaps = ["4px"]
  ...  // 4px gap between all children
}

SashBox

SashBox divides a container size in one direction with flexible child sizes:

SashBox
{
  it.sizes = ["25px, "100%", "50px"]
  Box { it.text="Child 1" },
  Box { it.text="Child 2" },
  Box { it.text="Child 3" },
}

+----------+----------------------------------+--------------------+
|          |                                  |                    |
|  Child 1 |            Child 2               |      Child 3       |
|          |                                  |                    |
+----------+----------------------------------+--------------------+

Fixed px sizes can be mixed with % sizes to divide space. When mixing px and %, the pixel sizes are computed first, and the remaining space is divided up per percentage. Sizes are applied using the CSS calc() function.

By default children are laid out left to right. To layout top to bottom, configure the dir field:

SashBox
{
  it.dir = Dir.down
  it.sizes = ["100px", "100%"]
  Box { it.text="Child 1" },
  Box { it.text="Child 2" },
}

By default, sashes are fixed, but you can enable resizing by setting the resizable field:

SashBox
{
  it.sizes = ["40%", "60%"]
  it.resizable = true
  Box { it.text="Child 1" },
  Box { it.text="Child 2" },
}

When resizable is true, the minSize field may be used to limit the minimum size a sash is resized to.

CardBox

CardBox lays out child elements as a stack of cards, where only one card may be visible at a time:

CardBox
{
  Box { it.style->background="#e74c3c"; it.text="Card #0" },
  Box { it.style->background="#2ecc71"; it.text="Card #1" },
  Box { it.style->background="#3498db"; it.text="Card #2" },
}

The visible card is changed using selIndex:

cardBox.selIndex = 2

CardBox

WellBox

WellBox displays content in recessed well:

WellBox { content, }

WellBox

FlexBox

FlexBox lays out children based on the CSS Flexbox layout model. Flexbox can be a little overwhelming to wrap your head around. A great tutorial that manages to explain it can be found here:

https://css-tricks.com/snippets/css/a-guide-to-flexbox/

In practice, only a subset of the flex model is used in domkit, and should be contained to "micro-layouts" for best results. The most common use case is to layout toolbars, such that items are centered vertically and to left/center/right align items.

This example creates a toolbar with several buttons left-aligned:

FlexBox
{
  it.flex = ["1 1 auto"]
  FlowBox {
    it.gaps = ["4px"]
    Button { ... },
    Button { ... },
    Button { ... },
  },
}

To add another button that is right-aligned to the toolbar:

FlexBox
{
  it.flex = ["1 1 auto", "0 0 auto"]
  FlowBox { ... },
  Button { ... },
}

The flex argument specifies how the parents size should be distributed to its children. There should be a flex entry for each child. The "0 0 auto" entry indicates this child should be given its preferred width, and should not grow or shrink as the parent size changes. The "1 1 auto" argument indicates that first our child should be laid out with its preferred size, and then any left over space should be allocated as well. This in effect pushes the second child to be right aligned in the container.