Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 82 additions & 6 deletions renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/DslBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<CONTENT_CATEGORY : ContentCategory>(
override val dslState: DslState,
) : StatedDsl<CONTENT_CATEGORY>, CssCapable {
/**
* このDSLに適用されるCSSクラス名
*/
override var cssClassName: String? = null
/**
* 保留中のCSSプロパティ
*/
override var pendingCssProperties: Map<String, CssValue>? = null
/**
* 保留中のCSSルールセット
*/
override var pendingCssRuleSet: CssRuleSet? = null
/**
* サブDSLの登録情報を保持するリスト
*/
private val subDsls = mutableListOf<RegisteredDslData>()
/**
* DSLの状態を保持するセット
*/
override val states = mutableSetOf<State<*>>()
/**
* この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 }
Expand All @@ -30,7 +79,19 @@ abstract class DslBase<CONTENT_CATEGORY : ContentCategory>(
)
}
}

/**
* 指定された状態を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を処理
Expand All @@ -44,15 +105,30 @@ abstract class DslBase<CONTENT_CATEGORY : ContentCategory>(
state.setSubDsls(subDsls)
state.applyDsl(this, registeredDslData)
}

/**
* 状態の現在の値を取得し、この状態をDSLの状態セットに追加します
*
* このメソッドは状態の値を取得すると同時に、その状態をDSLの追跡対象として登録します。
* これにより、状態の変更時にDSLの再描画が自動的にトリガーされます。
*
* @param T 状態の値の型
* @return 状態の現在の値
* @see State.currentValue
*/
override fun <T> State<T>.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 ->
Expand Down
18 changes: 18 additions & 0 deletions renlin/src/commonMain/kotlin/net/kigawa/renlin/dsl/EmptyDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<State<*>>()

/**
* EmptyDslの識別情報を含む状態データオブジェクトです。
*
* @see DslStateData
*/
override val dslStateData: DslStateData = DslStateData("empty dsl")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<T : Any>(
/**
* この追加データが関連付けられているコンテキストクラス
*/
val contextClass: KClass<*>,
/**
* この追加データの値の型
*/
val valueType: KType,
/**
* この追加データの一意のキー
*
* `null`の場合、キーは使用されません。
*/
val key: String?,
/**
* 実際に追加される値
*/
val value: T,
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,59 @@ 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<SubBasicDslState>()
/** この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()
).also {
subStates.add(it)
}
}

/**
* 子DSLの状態のリストを設定します。
*
* 指定されたDSLデータのリストに基づいて、現在の子DSL要素の状態を更新します。
*
* 処理の流れ:
* 1. 新しいリストに対応する既存の状態を再利用
* 2. 使用されなくなった状態を削除
* 3. 子DSLの状態リストを更新
*
* @param dsls DSLのデータのリスト
*/
override fun setSubDsls(dsls: List<RegisteredDslData>) {
val newList = mutableListOf<SubBasicDslState>()

Expand All @@ -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) {
Expand All @@ -49,30 +93,64 @@ abstract class BasicDslStateBase(
}
return relativeIndex
}

/**
* 指定されたインデックスに要素を設定します。
*
* @param index 要素を設定するインデックス
* @param elements 設定する要素のリスト
*/
abstract fun setElements(index: Int, elements: List<TagNode>)

/**
* 現在のDSL状態に関連付けられた要素のリストを取得します。
*
* このメソッドは、現在のDSL状態が持つ要素を取得し、
* 子状態の要素も含めて、すべての要素をリストとして返します。
*
* @return 関連付けられた要素のリスト
*/
fun getElements(): List<TagNode> {
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) {
ownElement?.setClassName(dsl.cssClassName!!)
}
throw NotImplementedError("BasicDslState not implemented.")
}

/**
* 新しい要素を作成します。
*
* @param tag 作成する要素のタグ
* @return 作成された要素のノード
*/
abstract fun newElement(tag: Tag<*>): TagNode

/**
* CSS管理インスタンスを初期化します。
*
* まだ作成されていない場合にのみCSS管理インスタンスを作成します
*/
protected fun initializeCssManager() {
if (internalCssManager == null) {
internalCssManager = createCssManager()
Expand Down
Loading
Loading