diff --git a/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/DslBase.kt b/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/DslBase.kt index 91c244b..266016b 100644 --- a/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/DslBase.kt +++ b/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/DslBase.kt @@ -8,16 +8,65 @@ import net.kigawa.renlin.state.DslState import net.kigawa.renlin.state.DslStateData import net.kigawa.renlin.w3c.category.ContentCategory + /** + * `DslBase` は、HTML構造を構築するためのDSLの基底クラスです。 + * このクラスは[StatedDsl]と[CssCapable]の実装を提供し、 + * HTML要素の構築、状態管理、CSS処理、コンポーネント管理、およびサブDSLの管理を行います。 + * + * 主な機能: + * - HTML要素の構築と管理 + * - 状態管理と更新 + * - サブDSLの階層管理とマウント + * - コンポーネントの状態管理と描画 + * - CSSの適用と管理 + * - 状態の自動追跡とバインディング + * + * @param CONTENT_CATEGORY このDSLが生成できるHTMLコンテンツのカテゴリ + * @param dslState このDSLに関連付けられた状態 + * + * @see StatedDsl + * @see CssCapable + * @see DslState + * + */ abstract class DslBase( override val dslState: DslState, ) : StatedDsl, CssCapable { + /** + * このDSLに適用されるCSSクラス名 + */ override var cssClassName: String? = null + /** + * 保留中のCSSプロパティ + */ override var pendingCssProperties: Map? = null + /** + * 保留中のCSSルールセット + */ override var pendingCssRuleSet: CssRuleSet? = null + /** + * サブDSLの登録情報を保持するリスト + */ private val subDsls = mutableListOf() + /** + * DSLの状態を保持するセット + */ override val states = mutableSetOf>() + /** + * このDSLの状態データ。 + */ override val dslStateData: DslStateData? = dslState.dslStateData() + /** + * サブDSLを現在のDSLに登録 + * + * 1. 既存のサブDSLをキーで検索 + * 2. 存在しない場合は追加、存在する場合は更新 + * 3. サブDSLに現在の状態を適用 + * + * @param registeredDslData 登録するDSLのデータ + * @see RegisteredDslData + */ override fun registerSubDsl(registeredDslData: RegisteredDslData) { val i = subDsls.indexOfFirst { it.key == registeredDslData.key } @@ -30,7 +79,19 @@ abstract class DslBase( ) } } - + /** + * 指定された状態をDSLにマウントします + * + * これにより、DSLの状態が更新され、関連するサブDSLも更新されます。 + * + * 1. 保留中のCSS情報の処理 + * 2. すべてのサブDSLへの状態適用 + * 3. 状態へのサブDSL情報の設定 + * 4. 状態へのDSL情報の適用 + * + * @param state マウントする状態 + * @param registeredDslData 関連するDSLデータ + */ override fun applyToDslState(state: DslState, registeredDslData: RegisteredDslData) { // dslStateが設定されたタイミングでpendingCssPropertiesを処理 @@ -44,15 +105,30 @@ abstract class DslBase( state.setSubDsls(subDsls) state.applyDsl(this, registeredDslData) } - + /** + * 状態の現在の値を取得し、この状態をDSLの状態セットに追加します + * + * このメソッドは状態の値を取得すると同時に、その状態をDSLの追跡対象として登録します。 + * これにより、状態の変更時にDSLの再描画が自動的にトリガーされます。 + * + * @param T 状態の値の型 + * @return 状態の現在の値 + * @see State.currentValue + */ override fun State.useValue(): T { states.add(this) return this.currentValue() } - - /** - * 保留中のCSS情報を処理 - */ + /** + * 保留中のCSS情報を処理 + * + * このメソッドは新しい`CssRuleSet`形式のCSSを優先的に処理し、 + * 後方互換性のため古い`Properties`形式もサポートします。 + * + * @see CssRuleSet + * @see pendingCssRuleSet + * @see pendingCssProperties + */ private fun processPendingCss() { // 新しいCssRuleSet形式を優先的に処理 pendingCssRuleSet?.let { ruleSet -> diff --git a/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/EmptyDsl.kt b/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/EmptyDsl.kt index bb5e3ed..b2aa9ed 100644 --- a/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/EmptyDsl.kt +++ b/renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/EmptyDsl.kt @@ -3,7 +3,25 @@ package net.kigawa.renlin.dsl import net.kigawa.hakate.api.state.State import net.kigawa.renlin.state.DslStateData +/** + * `EmptyDsl`は、空のDslクラスです + * + * 主な用途: + * - 空のDSLの表現 + */ class EmptyDsl : Dsl { + /** + * 状態オブジェクトを格納する可変セット + * + * このセットは任意の型の[State]オブジェクトを保持できます。 + * 初期状態では空のセットとして初期化されます。 + */ override val states = mutableSetOf>() + + /** + * EmptyDslの識別情報を含む状態データオブジェクトです。 + * + * @see DslStateData + */ override val dslStateData: DslStateData = DslStateData("empty dsl") } \ No newline at end of file diff --git a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/AdditionalDslStateData.kt b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/AdditionalDslStateData.kt index b1e4726..03d766c 100644 --- a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/AdditionalDslStateData.kt +++ b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/AdditionalDslStateData.kt @@ -3,9 +3,36 @@ package net.kigawa.renlin.state import kotlin.reflect.KClass import kotlin.reflect.KType +/** + * 追加のDSLのデータを表すクラス。 + * + * このクラスは、特定のコンテキストクラスに関連付けられた追加のデータを保持します。 + * 追加データは、キーと値のペアとして格納され、値の型はジェネリック型パラメータ `T` で指定されます。 + * + * @param T この追加データの値の型 + * + * @property contextClass この追加データが関連付けられているコンテキストクラス + * @property valueType この追加データの値の型 + * @property key この追加データの一意のキー + * @property value 実際に追加される値 + */ data class AdditionalDslStateData( + /** + * この追加データが関連付けられているコンテキストクラス + */ val contextClass: KClass<*>, + /** + * この追加データの値の型 + */ val valueType: KType, + /** + * この追加データの一意のキー + * + * `null`の場合、キーは使用されません。 + */ val key: String?, + /** + * 実際に追加される値 + */ val value: T, ) diff --git a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/BasicDslStateBase.kt b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/BasicDslStateBase.kt index 7e4d313..aa2163d 100644 --- a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/BasicDslStateBase.kt +++ b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/BasicDslStateBase.kt @@ -10,15 +10,40 @@ import net.kigawa.renlin.tag.Tag import net.kigawa.renlin.tag.component.Component import net.kigawa.renlin.w3c.element.TagNode +/** + * このクラスは、DSLの状態管理を行う基底クラスです。 + * + * 主な機能 + * - 子DSLの状態を保持し、取得または作成する機能 + * - CSSクラスの管理 + * - 要素の配置 + * + * @param stateContext 状態管理のコンテキスト + */ abstract class BasicDslStateBase( protected val stateContext: StateContext, ) : DslState { + /** 子DSLの状態を保持するリスト */ protected var subStates = mutableListOf() + /** このDSL状態が保有するDOM要素 */ abstract override val ownElement: TagNode? + /** CSSクラス管理インスタンス */ protected var internalCssManager: CssManager? = null + /** CSSクラス管理インスタンスを取得します。 */ override val cssManager: CssManager? get() = internalCssManager - + /** + * 子DSLの状態を取得または作成する + * + * 指定されたキーに対応する子DSL要素の状態を取得します。 + * 存在しない場合は新たに作成します。 + * + * @param key 子DSLの状態のキー + * @param second 子DSL要素のコンポーネント情報 + * @return 子DSLの状態 + * + * + */ override fun getOrCreateSubDslState(key: String, second: Component): DslState { return subStates.firstOrNull { it.key == key } ?: SubBasicDslState( key, this, second, stateContext.newStateContext() @@ -26,7 +51,18 @@ abstract class BasicDslStateBase( subStates.add(it) } } - + /** + * 子DSLの状態のリストを設定します。 + * + * 指定されたDSLデータのリストに基づいて、現在の子DSL要素の状態を更新します。 + * + * 処理の流れ: + * 1. 新しいリストに対応する既存の状態を再利用 + * 2. 使用されなくなった状態を削除 + * 3. 子DSLの状態リストを更新 + * + * @param dsls DSLのデータのリスト + */ override fun setSubDsls(dsls: List) { val newList = mutableListOf() @@ -40,7 +76,15 @@ abstract class BasicDslStateBase( } subStates = newList } - + /** + * 指定された子DSLの状態のインデックスを取得します。 + * + * このメソッドは、子DSLの状態のリスト内で、指定された状態がどの位置にあるかを計算します。 + * 各子DSLの状態が持つ要素の数を考慮して、相対的なインデックスを返します。 + * + * @param basicDslState インデックスを取得する子DSL要素の状態 + * @return 相対的なインデックス + */ fun getIndex(basicDslState: SubBasicDslState): Int { var relativeIndex = 0 for (subState in subStates) { @@ -49,20 +93,45 @@ abstract class BasicDslStateBase( } return relativeIndex } - + /** + * 指定されたインデックスに要素を設定します。 + * + * @param index 要素を設定するインデックス + * @param elements 設定する要素のリスト + */ abstract fun setElements(index: Int, elements: List) + + /** + * 現在のDSL状態に関連付けられた要素のリストを取得します。 + * + * このメソッドは、現在のDSL状態が持つ要素を取得し、 + * 子状態の要素も含めて、すべての要素をリストとして返します。 + * + * @return 関連付けられた要素のリスト + */ fun getElements(): List { return ownElement?.let { if (it.isEmpty) listOf() else listOf(it) } ?: subStates.flatMap { it.getElements() } } - + /** + * このDSL状態とその子要素の状態を削除します。 + * このメソッドは、現在のDSL状態に関連付けられた要素を削除し、 + * すべての子要素の状態も削除します。 + */ fun remove() { ownElement?.remove() subStates.forEach { it.remove() } } - + /** + * DSLを適用します。 + * + * DSLをこの状態に適用し、必要な要素の更新やCSSの適用を行います。 + * + * @param dsl 適用するDSL + * @param registeredDslData 登録されたDSLデータ + */ override fun applyDsl(dsl: StatedDsl<*>, registeredDslData: RegisteredDslData) { // CSS適用処理を追加 if (dsl is CssCapable && dsl.cssClassName != null) { @@ -70,9 +139,18 @@ abstract class BasicDslStateBase( } throw NotImplementedError("BasicDslState not implemented.") } - + /** + * 新しい要素を作成します。 + * + * @param tag 作成する要素のタグ + * @return 作成された要素のノード + */ abstract fun newElement(tag: Tag<*>): TagNode - + /** + * CSS管理インスタンスを初期化します。 + * + * まだ作成されていない場合にのみCSS管理インスタンスを作成します + */ protected fun initializeCssManager() { if (internalCssManager == null) { internalCssManager = createCssManager() diff --git a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/DslStateData.kt b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/DslStateData.kt index dd0ea7f..8d348f4 100644 --- a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/DslStateData.kt +++ b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/DslStateData.kt @@ -4,24 +4,71 @@ import net.kigawa.renlin.w3c.event.WebPointerEvent import kotlin.reflect.KClass import kotlin.reflect.typeOf +/** + * DSLの状態に関するデータとイベントハンドラーを保持するデータクラス + * + * このクラスは、DSL要素の状態管理において、基本的なイベントハンドラー(クリックイベント)と + * 型安全な追加データの管理機能を提供します。 + * 追加データは、コンテキストクラス、型、キーによって識別され、型安全にアクセスできます) + * + * @property key DSLの状態データの一意のキー + * @property onClick クリックイベントのハンドラー + * @property additionalData 追加データのリスト + * + * @see AdditionalDslStateData + */ + data class DslStateData( + /** DSLの状態データの一意の識別子 */ val key: String, + /** クリックイベントのハンドラー */ var onClick: ((WebPointerEvent) -> Unit)? = null, + /** + * 追加データのリスト + * + * 各追加データは、特定のコンテキストクラスに関連付けられ、値の型とキーを持ちます。 + */ var additionalData: List> = listOf() ) { + /** + * 指定されたコンテキストクラスに対して型安全に追加データを設定します。 + * + * 同じコンテキストクラス、型、キーの組み合わせで既存のデータがある場合は置き換えられます。 + * + * @param contextClass 追加データが関連付けられたコンテキストクラス + * @param value 追加する値 + * @param T 追加データの値の型 + * @param key 追加データの一意のキー + */ inline fun setAdditionalData(contextClass: KClass<*>, value: T, key: String? = null) { removeAdditionalData(contextClass) additionalData = additionalData + AdditionalDslStateData( contextClass, typeOf(), key, value ) } - + /** + * 指定されたコンテキストクラス、型、キーに一致する追加データを削除します。 + * + * @param contextClass 追加データが関連付けられたコンテキストクラス + * @param T 追加データの値の型 + * @param key 追加データの一意のキー + */ inline fun removeAdditionalData(contextClass: KClass<*>,key: String? = null) { additionalData = additionalData.filter { it.contextClass != contextClass || it.valueType != typeOf() || it.key != key } } - + /** + * 指定されたコンテキストクラス、型、キーに一致する追加データを取得します。 + * + * このメソッドは、型安全に追加データを取得するために使用されます。 + * + * @param contextClass 追加データが関連付けられたコンテキストクラス + * @param key 追加データの一意のキー + * @param T 追加データの値の型 + * @return 一致するデータが見つかった場合はその値、見つからない場合はnul + * + */ inline fun getAdditionalData(contextClass: KClass<*>, key: String? = null): T? { @Suppress("UNCHECKED_CAST") return additionalData diff --git a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/RootDslStateBase.kt b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/RootDslStateBase.kt index ea9dc8e..cf8f1c9 100644 --- a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/RootDslStateBase.kt +++ b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/RootDslStateBase.kt @@ -4,8 +4,23 @@ import net.kigawa.hakate.api.state.StateContext import net.kigawa.renlin.dsl.RegisteredDslData import net.kigawa.renlin.w3c.element.TagNode import net.kigawa.renlin.tag.Tag - +/** + * ルートDSL状態管理クラス + * + * DSL状態の階層構造における最上位(ルート)の状態を管理するクラスです。 + * このクラスは、アプリケーションの最上位DOM要素を保持し、 + * 全体のCSS管理インスタンスを初期化します。 + * + * 主な特徴: + * - ルートレベルでのCSS管理の初期化 + * - 最上位DOM要素の管理 + * - 子DSL状態の要素配置の起点 + * + * @param ownElement このルート状態が管理する最上位DOM要素 + * @param stateContext 状態管理のコンテキスト + */ class RootDslStateBase( + /** このルート状態が管理する最上位DOM要素 */ override val ownElement: TagNode, stateContext: StateContext, ) : BasicDslStateBase(stateContext) { @@ -13,20 +28,46 @@ class RootDslStateBase( // ルートではCssManagerを初期化 initializeCssManager() } - + /** + * 新しい要素を作成します。 + * + * ルート要素の子要素として新しいDOM要素を作成します。 + * + * @param tag 作成する要素のタグ情報 + * @return 作成された要素のノード + */ override fun newElement(tag: Tag<*>): TagNode { return ownElement.newNode(tag) } - + /** + * 指定されたインデックスに要素を設定します。 + * + * ルート要素の子要素として、指定されたインデックス位置に要素を配置します。 + * + * @param index 要素を設定するインデックス位置 + * @param elements 設定する要素のリスト + */ override fun setElements( index: Int, elements: List, ) { ownElement.setNodes(index, elements) } - + /** + * 最新の登録されたDSLデータを取得します。 + * + * ルート状態では登録されたDSLデータを持たないため、常にnullを返します。 + * + * @return 常にnull + */ override val latestRegisteredDslData: RegisteredDslData? get() = null - + /** + * DSL状態データを取得します。 + * + * ルート状態では独自の状態データを持たないため、常にnullを返します。 + * + * @return 常にnull + */ override fun dslStateData(): DslStateData? { return null } diff --git a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/SubBasicDslState.kt b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/SubBasicDslState.kt index 07b923e..a0817aa 100644 --- a/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/SubBasicDslState.kt +++ b/renlin/src/commonMain/kotlin/net/kigawa/renlin/state/SubBasicDslState.kt @@ -12,22 +12,55 @@ import net.kigawa.renlin.tag.component.Component import net.kigawa.renlin.tag.component.TagComponent import net.kigawa.renlin.w3c.element.TagNode +/** + * 子DSLの状態クラス + * + * 親DSL状態に属する子状態を表すクラスです。 + * タグコンポーネントの場合は独自の要素を持ち、そうでない場合は親の要素を利用します。 + * + * @property key 子DSLの状態を識別するキー + * @property parent 親のDSL + * @property component 子DSLのコンポーネント情報 + * @property stateContext 状態管理のコンテキスト + */ + class SubBasicDslState( + /** 子DSLの状態を識別するキー */ val key: String, + /** 親のDSL状態 */ private val parent: BasicDslStateBase, + /** 子DSLのコンポーネント情報 */ component: Component, + /** 状態管理のコンテキスト */ stateContext: StateContext, ) : BasicDslStateBase(stateContext), DslState { + /** + * この子DSLが所有するDOM要素 + * TagComponentの場合は新しい要素を作成し、そうでない場合はnull + */ override val ownElement = if (component is TagComponent<*>) { parent.newElement(component.tag) } else null + + /** 最新の登録されたDSLデータ */ override var latestRegisteredDslData: RegisteredDslData? = null + /** 最新のDSL状態データ */ private var latestDslStateData: DslStateData? = DslStateData(key) + /** CSS管理インスタンス(親から取得) */ override val cssManager: CssManager? get() = parent. cssManager + /** 最新の状態コンテキスト */ var latestStateContext: StateContext? = null - + /** + * DSLを適用します。 + * + * DSLをこの子状態に適用し、状態の変更を監視します。 + * CSS処理、要素の更新、状態の監視を行います。 + * + * @param dsl 適用するDSL + * @param registeredDslData 登録されたDSLデータ + */ override fun applyDsl(dsl: StatedDsl<*>, registeredDslData: RegisteredDslData) { latestRegisteredDslData = registeredDslData val index = parent.getIndex(this) @@ -66,18 +99,38 @@ class SubBasicDslState( } } } - + /** + * DSL状態データを取得します。 + * + * @return 最新のDSL状態データのコピー + */ override fun dslStateData(): DslStateData? { return latestDslStateData?.copy() } - + /** + * 指定されたインデックスに要素を設定します。 + * + * 独自の要素を持つ場合は子要素として設定し、 + * そうでない場合は親の要素に設定します。 + * + * @param index 要素を設定するインデックス + * @param elements 設定する要素のリスト + */ override fun setElements(index: Int, elements: List) { if (ownElement == null) { val ownIndex = parent.getIndex(this) parent.setElements(index + ownIndex, elements) } else ownElement.setNodes(index, elements) } - + /** + * 新しい要素を作成します。 + * + * 独自の要素を持つ場合はその子要素として作成し、 + * そうでない場合は親を通じて作成します。 + * + * @param tag タグ情報 + * @return 作成された要素 + */ override fun newElement(tag: Tag<*>): TagNode { return ownElement?.newNode(tag) ?: parent.newElement(tag) }