198 lines
6.4 KiB
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 }
|
|
)
|
|
}
|
|
)
|
|
},
|
|
)
|
|
)
|
|
} |