TBSten / mirrorball

0 stars 0 forks source link

ClientSlot #1

Open TBSten opened 2 weeks ago

TBSten commented 2 weeks ago

概要

以下のような記法を可能にし、サーバサイドのレンダリングの一部をクライアントサイドに委譲するClientSlotを実装する。

// server side code (jvmMain)
element.renderComposable {
  Div {
    +"this render on server"
    ClientSlot(HogeSlotId, HogeSlotId(num = 123, str = "hoge"))
  }
}

// shared code (commonMain)
object HogeSlotId : ClientSlotId<Args> {
  @Serializable
  data class Args(
    num: Int,
    str: String,
  )
}

// client side code (jsMain)
object HogeClientSlotImpl : ClientSlotImplementation(HogeSlotId) {
  @Composable
  fun Content() {
    val args: HogeSlotId.Args = getArg()
    // it will be `HogeSlotId.Args(num = 123, str = "hoge")`

    Div {
      +"this render on client"
    }
  }

  @Composable
  fun Loading() {
    CircularProgressIndicator()
  }
}
<!-- result -->
<div>
  this render on server

  <div>
    this render on client
  </div>

</div>

スロットの引数には サーバでレンダリングしたレンダリング結果も含めることができるようにする。 (compositionパターンを実装できるようにするため)

TBSten commented 2 weeks ago

シリアライゼーション機構は #3 で用意する。

TBSten commented 1 week ago

とりあえずプロトタイプはできた

コード ```kt @OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class) fun main() { console.log("slot content table", _MirrorballSlotContentTable_Generated) _MirrorballSlotContentTable_Generated.forEach { val slotElement = document .getElementById("__mirrorball_bow_slot_${it.slotKey}") as HTMLElement val arg = run { val argElement = document.getElementById("__mirrorball_bow_arg_${it.slotKey}")!! val json = Json { allowTrailingComma = true ignoreUnknownKeys = true } json.decodeFromString(it.slotKClass.serializer(), argElement.innerHTML) } slotElement.renderComposable { @Suppress("UnsafeCastFromDynamic") it.slotContent.Content(arg.asDynamic()) } } } @Serializable data object HogeSlot : Slot @MirrorballSlotImplementation object HogeContent : SlotContent { @Composable override fun Content(arg: HogeSlot) { Div { +"ok" } } } @Serializable data class FugaSlot( val fuga1: String, val fuga2: Int, ) : Slot @MirrorballSlotImplementation object FugaContent : SlotContent { @Composable override fun Content(arg: FugaSlot) { Div { Div { +arg.fuga1 } Div { +"${arg.fuga2}" } } } } ```