Cells/cells/ui/src/net/okennedy/cells/sheet/Table.scala

198 lines
6.4 KiB
Scala

package net.okennedy.cells.sheet
import com.raquo.laminar.api.L._
import net.okennedy.cells.Identifier
import net.okennedy.cells.serialized
import com.raquo.airstream.core.Signal
import com.raquo.airstream.combine.MergeEventStream
import play.api.libs.json._
import com.raquo.airstream.core.EventStream
import com.raquo.airstream.flatten.ConcurrentEventStream
import com.raquo.airstream.flatten.FlattenStrategy.ConcurrentStreamStrategy
import com.raquo.airstream.flatten.FlattenStrategy
import net.okennedy.cells.widgets.DragFrame
import com.raquo.airstream.ownership.OneTimeOwner
class Table(val id: Identifier)
{
implicit val tableOwner: Owner =
new OneTimeOwner( () => println(s"Accessing owner of table $id after killed") )
def this(ser: serialized.Table) =
{
this(
ser.id
)
}
val columns =
Var[Seq[serialized.ColSpec]](initial = Seq(
serialized.ColSpec(java.util.UUID.randomUUID(), width = 100),
serialized.ColSpec(java.util.UUID.randomUUID(), width = 100),
serialized.ColSpec(java.util.UUID.randomUUID(), width = 100),
))
val columnGutters: Signal[Seq[(Identifier, Signal[ColumnGutter])]] =
columns.signal
.map {
_.foldLeft( (0, 0, Seq[ColumnGutter]()) ) {
case ((pos, idx, accum), col) =>
(pos+col.width, idx+1, accum :+ new ColumnGutter(col, pos, idx))
}._3
}
.split(_.spec.id)( project = (key, _, signal) => key -> signal)
val rows =
Var[Seq[serialized.RowSpec]](initial = Seq(
serialized.RowSpec(java.util.UUID.randomUUID(), height = 30),
serialized.RowSpec(java.util.UUID.randomUUID(), height = 30),
serialized.RowSpec(java.util.UUID.randomUUID(), height = 30),
serialized.RowSpec(java.util.UUID.randomUUID(), height = 30),
serialized.RowSpec(java.util.UUID.randomUUID(), height = 30),
))
val rowGutters: Signal[Seq[(Identifier, Signal[RowGutter])]] =
rows.signal
.map {
_.foldLeft( (0, 0, Seq[RowGutter]()) ) {
case ((pos, idx, accum), col) =>
(pos+col.height, idx+1, accum :+ new RowGutter(col, pos, idx))
}._3
}
.split(_.spec.id)( project = (key, _, signal) => key -> signal)
val cells = Var[Seq[(Identifier, Seq[(Identifier, Cell)])]](initial =
rows.now().zipWithIndex.map { case (r, ridx) =>
r.id ->
columns.now().zipWithIndex.map { case (c, cidx) =>
c.id -> new Cell("["+cidx+", "+ridx+"]")
}
}
)
val x = Var(initial = 20)
val y = Var(initial = 20)
val width =
columns.signal.map { _.foldLeft(0)( (w, col) => w + col.width) }
.observe
val height =
rows.signal.map { _.foldLeft(0)( (h, row) => h + row.height) }
.observe
def root = div(
className("dataTable"),
// Table position and size information (reactive)
styleAttr <--
Signal.combine(x, y, width, height)
.map { case (x, y, w, h) =>
Seq(
s"left: ${x}px;",
s"top: ${y}px;",
s"width: ${w+Constants.GUTTER_WIDTH}px;",
s"height: ${h+Constants.GUTTER_HEIGHT}px;"
).mkString(" ")
},
// Initiate a Frame Drag session on click
onMouseDown --> {
(evt) =>
DragFrame.start(
x.now(), y.now(),
width.now()+Constants.GUTTER_WIDTH,
height.now()+Constants.GUTTER_HEIGHT,
evt
) { (x, y) => println(s"Move to : $x, $y")}
evt.stopPropagation()
},
// Display column gutters
div(
className("columnGutters"),
styleAttr(s"left: ${Constants.GUTTER_WIDTH}px"),
children <--
columnGutters.map { _.map { case (_, signal) =>
div(
className("columnGutter"),
styleAttr <-- signal.map { case col =>
s"width: ${col.spec.width-Constants.BORDER}px; height: ${Constants.GUTTER_HEIGHT-2*Constants.BORDER}px; left: ${col.position}px"
},
child <-- signal.map { _.label }
)
}},
),
// Display row gutters
div(
className("rowGutters"),
styleAttr(s"top: ${Constants.GUTTER_HEIGHT}px"),
children <--
rowGutters.map { _.map { case (_, signal) =>
div(
className("rowGutter"),
styleAttr <-- signal.map { case row =>
s"width: ${Constants.GUTTER_WIDTH-2*Constants.BORDER}px; height: ${row.spec.height-Constants.BORDER}px; top: ${row.position}px"
},
div(
className("valignHack"),
child <-- signal.map { _.label }
)
)
}},
),
// Display the actual content
div(
className("content"),
styleAttr <--
Signal.combine(width, height)
.map { case (w, h) =>
Seq(
s"width: ${w}px;",
s"height: ${h}px;",
s"left: ${Constants.GUTTER_WIDTH}px;",
s"top: ${Constants.GUTTER_HEIGHT}px",
).mkString(" ")
},
children <--
cells.signal.split( key = _._1 ){
(rowKey, _, rowCellStream) =>
val rowGutterStream: Signal[RowGutter] =
rowGutters.flatMap {
_.find(_._1 == rowKey).get._2:Signal[RowGutter]
}
div(
className("row"),
styleAttr <--
rowGutterStream.map { row =>
s"height: ${row.spec.height}px; top: ${row.position}px"
},
children <--
rowCellStream.map { _._2 }.split( key = _._1 ) {
(colKey, _, cellStream) =>
val cellGutterStream: Signal[ColumnGutter] =
columnGutters.flatMap {
_.find(_._1 == colKey).get._2:Signal[ColumnGutter]
}
div(
className("cell"),
styleAttr <--
cellGutterStream.map { col =>
s"width: ${col.spec.width}px; left: ${col.position}px"
},
child <--
cellStream.map { _._2.tag }
)
}
)
},
)
)
}